| Overall Statistics |
|
Total Orders 14710 Average Win 0.08% Average Loss -0.09% Compounding Annual Return 7.626% Drawdown 29.100% Expectancy 0.052 Start Equity 100000 End Equity 144421.76 Net Profit 44.422% Sharpe Ratio 0.16 Sortino Ratio 0.189 Probabilistic Sharpe Ratio 5.217% Loss Rate 45% Win Rate 55% Profit-Loss Ratio 0.92 Alpha -0.031 Beta 0.943 Annual Standard Deviation 0.156 Annual Variance 0.024 Information Ratio -0.424 Tracking Error 0.082 Treynor Ratio 0.027 Total Fees $10656.19 Estimated Strategy Capacity $260000000.00 Lowest Capacity Asset RIVN XTDCDCDRI6HX Portfolio Turnover 7.19% Drawdown Recovery 969 |
############################################
# Researched and Implemented by: Ethan Huang
# Mentored by: Rudy Osuna
# Triton Quantitative Trading @ UC San Diego
############################################
# region imports
from AlgorithmImports import *
# endregion
class EMAOnlyCrossoverBenchmark(QCAlgorithm):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5 * 365))
self.set_cash(100_000)
self.settings.seed_initial_prices = True
self.universe_settings.resolution = Resolution.DAILY
self.universe_settings.schedule.on(self.date_rules.week_start())
self._universe = self.add_universe(self._select)
self.set_warm_up(250)
def on_warmup_finished(self):
time_rule = self.time_rules.at(8, 0)
# Rebalance the portfolio weekly after warmup.
self.schedule.on(self.date_rules.week_start("SPY"), time_rule, self._rebalance)
# Rebalance the portfolio today too.
if self.live_mode:
self._rebalance()
else:
self.schedule.on(self.date_rules.today, time_rule, self._rebalance)
def _select(self, fundamental):
# Select the top 100 most liquid stocks by dollar volume with price above $10.
return [f.symbol for f in sorted(
[f for f in fundamental if f.has_fundamental_data and f.price > 10],
key=lambda f: f.dollar_volume
)[-100:]]
def on_securities_changed(self, changes):
# Once a security leaves the universe, liquidate and remove indicators.
for security in changes.removed_securities:
if security.invested:
self.liquidate(security.symbol)
self.deregister_indicator(security.fast_ema)
self.deregister_indicator(security.slow_ema)
# Initialize EMA indicators for newly added securities.
for security in changes.added_securities:
security.fast_ema = self.ema(security.symbol, 50, Resolution.DAILY)
security.slow_ema = self.ema(security.symbol, 200, Resolution.DAILY)
self.warm_up_indicator(security.symbol, security.fast_ema, Resolution.DAILY)
self.warm_up_indicator(security.symbol, security.slow_ema, Resolution.DAILY)
def _rebalance(self):
if self.is_warming_up:
return
selected_symbols = []
for symbol in self._universe.selected:
security = self.securities[symbol]
if not (security.fast_ema.is_ready and security.slow_ema.is_ready):
continue
if security.fast_ema.current.value > security.slow_ema.current.value:
selected_symbols.append(security.symbol)
if len(selected_symbols) == 0:
for symbol in self._universe.selected:
security = self.securities[symbol]
if security.invested:
self.liquidate(security.symbol)
return
weight = 1.0 / len(selected_symbols)
targets = [PortfolioTarget(symbol, weight) for symbol in selected_symbols]
self.set_holdings(targets, True)