Overall Statistics
Total Orders
7583
Average Win
0.12%
Average Loss
-0.16%
Compounding Annual Return
-6.504%
Drawdown
46.100%
Expectancy
-0.048
Start Equity
1000000
End Equity
714358.18
Net Profit
-28.564%
Sharpe Ratio
-0.614
Sortino Ratio
-0.707
Probabilistic Sharpe Ratio
0.022%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.75
Alpha
-0.06
Beta
-0.31
Annual Standard Deviation
0.126
Annual Variance
0.016
Information Ratio
-0.617
Tracking Error
0.22
Treynor Ratio
0.251
Total Fees
$13590.91
Estimated Strategy Capacity
$160000000.00
Lowest Capacity Asset
BAH UROXQFIWOSX1
Portfolio Turnover
7.36%
Drawdown Recovery
252
from AlgorithmImports import *


class FundamentalFactorAlphaModel(AlphaModel):

    def __init__(self, algorithm, date_rule, assets_per_side: int = 10):
        self._universe = []
        self._assets_per_side = assets_per_side
        # Add a Scheduled Event to emit monthly insights matching the universe.
        self._insights = []
        algorithm.schedule.on(date_rule, algorithm.time_rules.at(8,0), self._create_insights)

    def _create_insights(self):
        securities = [security for security in self._universe if security.price]
        # Rank by fundamental factors.
        by_margin = sorted(securities, key=lambda x: x.fundamentals.operation_ratios.operation_margin.value)
        by_momentum = sorted(securities, key=lambda x: x.fundamentals.valuation_ratios.price_change_1m)
        by_book = sorted(securities, key=lambda x: x.fundamentals.valuation_ratios.book_value_per_share)
        # Calculate a score for each security.
        score_by_security = {
            security: by_margin.index(security) * 0.2 + by_momentum.index(security) * 0.4 + by_book.index(security) * 0.4
            for security in securities
        }
        ranked = sorted(securities, key=lambda security: score_by_security[security])
        # Highest-scored go long, lowest-scored go short.
        self._insights = []
        for security in ranked[-self._assets_per_side:]:
            self._insights.append(Insight.price(security, Expiry.END_OF_MONTH, InsightDirection.UP))
        for security in ranked[:self._assets_per_side]:
            self._insights.append(Insight.price(security, Expiry.END_OF_MONTH, InsightDirection.DOWN))
        
    def update(self, algorithm: QCAlgorithm, data: Slice) -> List[Insight]:
        # Only rebalance once per month.
        insights = self._insights.copy()
        self._insights.clear()
        return insights

    def on_securities_changed(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
        # Add and delete from the universe member as the universe changes.
        for security in changes.added_securities:
            self._universe.append(security)
        for security in changes.removed_securities:
            self._universe.remove(security)
from AlgorithmImports import *
from alpha import FundamentalFactorAlphaModel
from universe import FundamentalFactorUniverseSelectionModel


class VerticalTachyonRegulators(QCAlgorithm):
    
    def initialize(self):
        self.set_start_date(self.end_date - timedelta(5 * 365))
        self.settings.seed_initial_prices = True
        self.set_cash(1_000_000)
        # Add a universe filtering by fundamentals.
        self.universe_settings.resolution = Resolution.DAILY
        date_rule = self.date_rules.month_start("SPY")
        self.universe_settings.schedule.on(date_rule)
        self.add_universe_selection(FundamentalFactorUniverseSelectionModel())
        # Add an Alpha model.
        assets_per_side = 10
        self.add_alpha(FundamentalFactorAlphaModel(self, date_rule, assets_per_side))   
        # Add the long short positions as equal weight.
        self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
        # Add a warmup period for immediate trading.
        self.set_warm_up(timedelta(45))
from AlgorithmImports import *


class FundamentalFactorUniverseSelectionModel(FundamentalUniverseSelectionModel):

    def select(self, algorithm: QCAlgorithm, fundamental: List[Fundamental]) -> List[Symbol]:
        # Filter to profitable companies with positive momentum and book value.
        qualified = [
            f for f in fundamental
            if (f.has_fundamental_data and f.price > 5 and 
                f.operation_ratios.operation_margin.value > 0 and 
                f.valuation_ratios.price_change_1m > 0 and 
                f.valuation_ratios.book_value_per_share > 0)
        ]
        # Take the top 250 by dollar volume.
        return [f.symbol for f in sorted(qualified, key=lambda f: f.dollar_volume)[-250:]]