| Overall Statistics |
|
Total Orders 151 Average Win 10.02% Average Loss -7.75% Compounding Annual Return 100.985% Drawdown 43.200% Expectancy 0.467 Start Equity 100000 End Equity 812912.89 Net Profit 712.913% Sharpe Ratio 1.616 Sortino Ratio 2.001 Probabilistic Sharpe Ratio 69.902% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 1.29 Alpha 0.707 Beta 2.181 Annual Standard Deviation 0.478 Annual Variance 0.228 Information Ratio 1.871 Tracking Error 0.397 Treynor Ratio 0.354 Total Fees $6383.82 Estimated Strategy Capacity $0 Lowest Capacity Asset TQQQ UK280CGTCB51 Portfolio Turnover 13.68% |
from AlgorithmImports import *
class CrazyHolyGrailSimplified(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022, 1, 1) # Set a relevant start date
self.SetEndDate(2024, 12, 31) # Set a relevant end date
self.SetCash(100000)
# Define the symbols used in the Composer strategy
self.tqqq = self.AddEquity("TQQQ", Resolution.Daily).Symbol
self.qqq = self.AddEquity("QQQ", Resolution.Daily).Symbol
self.uvxy = self.AddEquity("UVXY", Resolution.Daily).Symbol
self.tecl = self.AddEquity("TECL", Resolution.Daily).Symbol
self.upro = self.AddEquity("UPRO", Resolution.Daily).Symbol
self.tlt = self.AddEquity("TLT", Resolution.Daily).Symbol
self.sqqq = self.AddEquity("SQQQ", Resolution.Daily).Symbol
# Initialize indicators
self.tqqq_sma200 = self.SMA(self.tqqq, 200, Resolution.Daily)
self.tqqq_sma20 = self.SMA(self.tqqq, 20, Resolution.Daily)
self.qqq_rsi10 = self.RSI(self.qqq, 10, MovingAverageType.Simple, Resolution.Daily)
self.tqqq_rsi10 = self.RSI(self.tqqq, 10, MovingAverageType.Simple, Resolution.Daily)
self.upro_rsi10 = self.RSI(self.upro, 10, MovingAverageType.Simple, Resolution.Daily)
# Set warm-up period. Max warm-up is 200 for SMA200.
self.SetWarmUp(TimeSpan.FromDays(250))
# Schedule the trading logic to run daily, 5 minutes after market open
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.AfterMarketOpen(self.tqqq, 5), # Use one symbol for scheduling
self.TradeLogic)
def TradeLogic(self):
# Ensure all required indicators are ready
if self.IsWarmingUp or \
not self.tqqq_sma200.IsReady or \
not self.tqqq_sma20.IsReady or \
not self.qqq_rsi10.IsReady or \
not self.tqqq_rsi10.IsReady or \
not self.upro_rsi10.IsReady:
self.Debug("Indicators are not ready yet. Skipping trade logic.")
return
# Get current prices for the relevant assets
tqqq_price = self.Securities[self.tqqq].Price
qqq_price = self.Securities[self.qqq].Price
uvxy_price = self.Securities[self.uvxy].Price
tecl_price = self.Securities[self.tecl].Price
upro_price = self.Securities[self.upro].Price
tlt_price = self.Securities[self.tlt].Price
sqqq_price = self.Securities[self.sqqq].Price
# Initialize a list to hold the symbols we want to invest in for this period
target_symbols = []
# Start translating the Composer logic:
# (if (> (current-price "EQUITIES::TQQQ//USD") (moving-average-price "EQUITIES::TQQQ//USD" {:window 200}))
if tqqq_price > self.tqqq_sma200.Current.Value:
# [(if (> (rsi "EQUITIES::QQQ//USD" {:window 10}) 80)
if self.qqq_rsi10.Current.Value > 80:
# [(asset "EQUITIES::UVXY//USD" "ProShares Ultra VIX Short-Term Futures ETF")]
target_symbols.append(self.uvxy)
else:
# [(asset "EQUITIES::TQQQ//USD" "ProShares UltraPro QQQ")])]
target_symbols.append(self.tqqq)
else:
# [(if (< (rsi "EQUITIES::TQQQ//USD" {:window 10}) 31)
if self.tqqq_rsi10.Current.Value < 31:
# [(asset "EQUITIES::TECL//USD" "Direxion Daily Technology Bull 3x Shares")]
target_symbols.append(self.tecl)
else:
# [(if (< (rsi "EQUITIES::UPRO//USD" {:window 10}) 31)
if self.upro_rsi10.Current.Value < 31:
# [(asset "EQUITIES::UPRO//USD" "ProShares UltraPro S&P500")]
target_symbols.append(self.upro)
else:
# [(if (> (current-price "EQUITIES::TQQQ//USD") (moving-average-price "EQUITIES::TQQQ//USD" {:window 20}))
if tqqq_price > self.tqqq_sma20.Current.Value:
# [(asset "EQUITIES::TQQQ//USD" "ProShares UltraPro QQQ")]
target_symbols.append(self.tqqq)
else:
# [(filter (rsi {:window 10}) (select-top 1)
# [(asset "EQUITIES::TLT//USD" "iShares 20+ Year Treasury Bond ETF")
# (asset "EQUITIES::SQQQ//USD" "ProShares UltraPro Short QQQ")])])])])])
# Calculate RSI for TLT and SQQQ to select the one with the lowest RSI (most oversold)
tlt_rsi = self.RSI(self.tlt, 10).Current.Value if self.RSI(self.tlt, 10).IsReady else float('inf')
sqqq_rsi = self.RSI(self.sqqq, 10).Current.Value if self.RSI(self.sqqq, 10).IsReady else float('inf')
if tlt_rsi <= sqqq_rsi and tlt_rsi != float('inf'):
target_symbols.append(self.tlt)
elif sqqq_rsi != float('inf'):
target_symbols.append(self.sqqq)
# If neither RSI is ready, no asset is added from this branch.
# This should ideally be handled by ensuring these RSIs are ready during warm-up.
# For simplicity, we assume they become ready eventually.
# === Execute Trades based on target_symbols ===
# Liquidate positions not in the target_symbols list
for holding in self.Portfolio.Values:
if holding.Invested and holding.Symbol not in target_symbols:
self.Liquidate(holding.Symbol)
self.Debug(f"Liquidating {holding.Symbol.Value}")
# Invest in target_symbols with equal weight
if target_symbols:
allocation_per_asset = 1.0 / len(target_symbols)
for symbol in target_symbols:
# Only set holdings if the asset currently has data
if self.Securities[symbol].HasData and self.Securities[symbol].Price > 0:
self.SetHoldings(symbol, allocation_per_asset)
self.Debug(f"Setting holdings for {symbol.Value} to {allocation_per_asset*100:.2f}%")
else:
self.Debug(f"Skipping SetHoldings for {symbol.Value} due to no data or zero price.")
else:
# If no target symbols are selected by the logic, liquidate everything
if self.Portfolio.Invested:
self.Liquidate()
self.Debug("No assets selected by logic. Liquidating all positions.")