| 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:]]