| Overall Statistics |
|
Total Orders 1756 Average Win 2.89% Average Loss -0.30% Compounding Annual Return 41.339% Drawdown 36.300% Expectancy 0.873 Start Equity 100000 End Equity 651848.44 Net Profit 551.848% Sharpe Ratio 1.02 Sortino Ratio 1.055 Probabilistic Sharpe Ratio 44.827% Loss Rate 83% Win Rate 17% Profit-Loss Ratio 9.78 Alpha 0.224 Beta 0.771 Annual Standard Deviation 0.298 Annual Variance 0.089 Information Ratio 0.739 Tracking Error 0.27 Treynor Ratio 0.394 Total Fees $19371.67 Estimated Strategy Capacity $430000.00 Lowest Capacity Asset BIL TT1EBZ21QWKL Portfolio Turnover 34.43% Drawdown Recovery 494 |
from AlgorithmImports import *
import numpy as np
class TheKineticTitan(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 1, 1)
self.set_end_date(2024, 6, 1)
self.set_cash(100000)
self.settings.rebalance_portfolio_on_insight_changes = False
self.tqqq = self.add_equity("TQQQ", Resolution.MINUTE).symbol
self.erx = self.add_equity("ERX", Resolution.MINUTE).symbol
self.safe = self.add_equity("BIL", Resolution.MINUTE).symbol
self.vol_lookback = 20
self.mom_lookback = 21
self.target_vol = 0.40
self.buffer = 0.05
self.current_target = None
self.schedule.on(self.date_rules.every_day(self.tqqq),
self.time_rules.before_market_close(self.tqqq, 10),
self.rebalance)
self.set_warm_up(60)
def on_data(self, data):
pass
def rebalance(self):
if self.is_warming_up: return
assets = [self.tqqq, self.erx]
history = self.history(assets, self.mom_lookback + 5, Resolution.DAILY)
if history.empty: return
scores = {}
for symbol in assets:
if symbol not in history.index: continue
closes = history.loc[symbol].close
if len(closes) < self.mom_lookback: continue
start_price = closes[-(self.mom_lookback)]
end_price = closes[-1]
mom = (end_price / start_price) - 1
returns = closes.pct_change().dropna()
vol = np.std(returns)
if vol > 0:
score = mom / vol
else:
score = -999
scores[symbol] = score
if not scores: return
best_asset = max(scores, key=scores.get)
best_score = scores[best_asset]
target_asset = self.current_target
if self.current_target is None:
if best_score > 0:
target_asset = best_asset
else:
target_asset = self.safe
else:
current_score = scores.get(self.current_target, -999)
if self.current_target == self.safe:
if best_score > 0:
target_asset = best_asset
else:
if best_score < 0 and current_score < 0:
target_asset = self.safe
elif best_asset != self.current_target:
if best_score > (current_score + self.buffer):
target_asset = best_asset
self.current_target = target_asset
target_weight = 0.0
if target_asset and target_asset != self.safe:
hist_vol = self.history(target_asset, self.vol_lookback + 1, Resolution.DAILY)
if not hist_vol.empty:
rets = hist_vol.close.pct_change().dropna()
current_vol = np.std(rets) * np.sqrt(252)
if current_vol > 0:
vol_weight = self.target_vol / current_vol
else:
vol_weight = 1.0
target_weight = min(1.0, vol_weight)
else:
target_weight = 0.5
elif target_asset == self.safe:
target_weight = 1.0
for s in [self.tqqq, self.erx, self.safe]:
if s != target_asset:
if self.portfolio[s].invested:
self.liquidate(s)
if target_asset:
if target_asset == self.safe:
self.set_holdings(target_asset, 1.0)
else:
current_holding_weight = self.portfolio[target_asset].quantity * self.portfolio[target_asset].price / self.portfolio.total_portfolio_value
if abs(current_holding_weight - target_weight) > 0.05:
self.set_holdings(target_asset, target_weight)
remaining_cash = 1.0 - target_weight
if remaining_cash > 0.1:
self.set_holdings(self.safe, remaining_cash)
state = 0
if target_asset == self.tqqq: state = 1
elif target_asset == self.erx: state = 2
self.plot("Titan", "Asset", state)