| Overall Statistics |
|
Total Trades 1195 Average Win 0.71% Average Loss -0.83% Compounding Annual Return 12.284% Drawdown 31.200% Expectancy 0.079 Net Profit 41.565% Sharpe Ratio 0.533 Probabilistic Sharpe Ratio 15.651% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 0.85 Alpha 0 Beta 0 Annual Standard Deviation 0.196 Annual Variance 0.038 Information Ratio 0.533 Tracking Error 0.196 Treynor Ratio 0 Total Fees $1393.72 Estimated Strategy Capacity $670000000.00 Lowest Capacity Asset QQQ RIWIV7K5Z9LX Portfolio Turnover 54.61% |
# region imports
from AlgorithmImports import *
from sklearn.ensemble import ExtraTreesClassifier
# endregion
class MuscularFluorescentPinkSnake(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1) # Set Start Date
self.SetEndDate(2023, 1, 1) # Set End Date
self.SetCash(100000) # Set Strategy Cash
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
# Manual universe of individual tickers
tickers = ["SPY","QQQ"]
self.symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers]
self.AddUniverseSelection(ManualUniverseSelectionModel(self.symbols))
self.resolution = Resolution.Daily
self.UniverseSettings.Resolution = self.resolution
self.models = {}
# Train the model once every week
self.Train(self.TrainModel)
self.Train(self.DateRules.Every(DayOfWeek.Sunday), self.TimeRules.At(8,0), self.TrainModel)
self.SetAlpha(ClassifierAlpha(self))
# Insight weighting Portfolio Construction Model
self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
self.SetRiskManagement(NullRiskManagementModel())
self.SetWarmup(2)
def TrainModel(self):
start = self.Time - timedelta(days=4*365)
end = self.Time
for symbol in self.symbols:
# Train a model per symbol on 4 years of history, use 'volume' as the feature
X, y = self.GetHistoryAndFeatures(symbol, ['volume'], start, end, include_y=True)
self.models[symbol] = ExtraTreesClassifier(random_state=42)
self.models[symbol].fit(X, y)
def GetHistoryAndFeatures(self, symbol, features, start = None, end = None, include_y = False, lookback = None):
X = pd.DataFrame()
y = pd.DataFrame()
if not lookback:
history = self.History(symbol, start, end, self.resolution)
else:
history = self.History(symbol, lookback, self.resolution)
# the target is the one day return
history['returns_1d'] = -history.close.pct_change(-1)
# clean up data and prepare for split into X and y
history = history.reset_index().set_index('time').sort_index()
history.index = history.index.normalize()
history = history.set_index('symbol', append=True)
history = history.dropna(axis=1, how='all')
all_cols = history.columns.tolist()
cols_to_drop = list(set(all_cols) - set(features))
if include_y:
history = history.dropna()
X = history.drop(cols_to_drop, axis=1, inplace=False)
y = history['returns_1d'] > 0
else:
X = history.drop(cols_to_drop, axis=1, inplace=False)
X = X.dropna()
if include_y:
return X, y
else:
return X
class ClassifierAlpha(AlphaModel):
def __init__(self, algo):
self.algo = algo
def Update(self, algo, data):
insights = []
if not self.algo.IsWarmingUp:
for symbol in self.algo.symbols:
symbol = SymbolCache.GetSymbol(symbol)
# In the original algo we need a certain length of history to run several moving averages etc.
# this isn't really needed here as we're only using 'volume' as our dummy feature,
# but we're getting 2 days anyway
X_symbol = self.algo.GetHistoryAndFeatures(symbol, ['volume'], lookback = 2)
if not X_symbol.empty and self.algo.models[symbol]:
# use the most recent row of the feature dataframe
time = X_symbol.index[-1][0]
X_symbol = X_symbol.loc[pd.IndexSlice[time, :], :]
# predict whether the next 1d return is positive
model = self.algo.models[symbol]
result = model.predict(X_symbol)
proba = model.predict_proba(X_symbol)[0][1]
self.algo.Debug("Algorithm Time "+str(self.algo.Time)+" - Last row in features "+str(time))
# create the insights and use the prediction probability as weight
if result:
insights.append(
Insight.Price(symbol, timedelta(days=7), InsightDirection.Up, weight = proba))
else:
insights.append(
Insight.Price(symbol, timedelta(days=7), InsightDirection.Flat, weight = 0))
return insights