| Overall Statistics |
|
Total Orders 2300 Average Win 0.09% Average Loss -0.08% Compounding Annual Return 3.336% Drawdown 22.900% Expectancy 0.043 Start Equity 1000000 End Equity 1178440.85 Net Profit 17.844% Sharpe Ratio -0.088 Sortino Ratio -0.11 Probabilistic Sharpe Ratio 2.596% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.17 Alpha -0.053 Beta 0.654 Annual Standard Deviation 0.104 Annual Variance 0.011 Information Ratio -1.123 Tracking Error 0.068 Treynor Ratio -0.014 Total Fees $2292.00 Estimated Strategy Capacity $35000000.00 Lowest Capacity Asset CYBR VU6BPZ7ECBQD Portfolio Turnover 1.23% Drawdown Recovery 1037 |
# region imports
from AlgorithmImports import *
# endregion
class WellDressedSkyBlueSardine(QCAlgorithm):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5*365))
self.set_cash(1_000_000)
self.settings.automatic_indicator_warm_up = True
self.settings.seed_initial_prices = True
# Define some parameters.
self._percentage_buy = 0.01
# Add a universe of US Equities.
self.universe_settings.resolution = Resolution.DAILY
self._universe = self.add_universe(self._select_assets)
# Add a Scheduled Event to rebalance the portfolio.
self.schedule.on(self.date_rules.every_day('SPY'), self.time_rules.at(8, 0), self._rebalance)
def _select_assets(self, fundamentals):
# Select the most liquid stocks that are trading above $10.
filtered = [f for f in fundamentals if f.price > 10 and f.has_fundamental_data]
filtered = sorted(filtered, key=lambda x: x.dollar_volume)[-1000:]
# Further filter the stocks to those with a market cap above $10 billion and take the smallest 500.
filtered = sorted([f for f in filtered if f.market_cap > 10e9], key=lambda f: f.market_cap)[:500]
return [f.symbol for f in filtered]
def on_securities_changed(self, changes):
# As assets enter the universe, add indicators to them.
for security in changes.added_securities:
security.ema_200 = self.ema(security, 200)
security.ema_50 = self.ema(security, 50)
security.roc_125 = self.roc(security, 125)
security.roc_20 = self.roc(security, 20)
security.rsi_14 = self.rsi(security, 14, MovingAverageType.SIMPLE)
security.sma_200 = self.sma(security, 200)
security.slow_stoch_5 = self.sto(security, 5)
security.ppo = self.ppo(security, 12, 26, MovingAverageType.EXPONENTIAL)
# Populate the PPO indicator with historical data to ensure it's ready for use.
security.ppo.window[2]
for bar in self.history[TradeBar](security, security.ppo.samples + security.ppo.window.size):
security.ppo.update(bar.end_time, bar.close)
# As assets leave the universe, liquidate them.
for security in changes.removed_securities:
self.liquidate(security)
def _rebalance(self):
# Calculate the SCTR score for each security in the universe.
sctr_by_security = {}
for symbol in self._universe.selected:
security = self.securities[symbol]
if not security.sma_200.is_ready:
continue
sctr_by_security[security] = (
security.ema_200.current.value * 0.30
+ security.ema_50.current.value * 0.15
+ security.roc_125.current.value * 0.30
+ security.roc_20.current.value * 0.15
+ (security.ppo.window[0].value - security.ppo.window[2].value) / (3 * security.ppo.window[2].value) * 0.05
+ security.rsi_14.current.value * 0.05
)
# Select the top 15% of securities based on their SCTR score.
sctr_universe = sorted(
sctr_by_security,
key=lambda security: sctr_by_security[security]
)[-int(len(sctr_by_security)*0.15):]
# Scan for trade entries.
for security in sctr_universe:
if security.invested:
continue
if self.portfolio.margin_remaining <= 0.9 * self._percentage_buy * self.portfolio.total_portfolio_value:
continue
if security.close > security.sma_200.current.value and security.slow_stoch_5.current.value <= 30:
self.set_holdings(security, self._percentage_buy)
# Liquidate any securities that are no longer in the SCTR universe.
for symbol in self._universe.selected:
security = self.securities[symbol]
if security not in sctr_universe:
self.liquidate(security)