| Overall Statistics |
|
Total Trades 16 Average Win 7.80% Average Loss -13.95% Compounding Annual Return -21.008% Drawdown 41.600% Expectancy -0.221 Net Profit -22.474% Sharpe Ratio -0.382 Probabilistic Sharpe Ratio 4.747% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 0.56 Alpha -0.106 Beta -0.718 Annual Standard Deviation 0.345 Annual Variance 0.119 Information Ratio -0.405 Tracking Error 0.415 Treynor Ratio 0.184 Total Fees $31.70 Estimated Strategy Capacity $180000000.00 Lowest Capacity Asset AAPL R735QTJ8XC9X Portfolio Turnover 3.51% |
from AlgorithmImports import *
from EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel, PortfolioBias
class InsightWeightingPortfolioConstructionModel(EqualWeightingPortfolioConstructionModel):
'''Provides an implementation of IPortfolioConstructionModel that generates percent targets based on the
Insight.Weight. The target percent holdings of each Symbol is given by the Insight.Weight from the last
active Insight for that symbol.
For insights of direction InsightDirection.Up, long targets are returned and for insights of direction
InsightDirection.Down, short targets are returned.
If the sum of all the last active Insight per symbol is bigger than 1, it will factor down each target
percent holdings proportionally so the sum is 1.
It will ignore Insight that have no Insight.Weight value.'''
def __init__(self, rebalance = Resolution.Daily, portfolioBias = PortfolioBias.LongShort):
'''Initialize a new instance of InsightWeightingPortfolioConstructionModel
Args:
rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
If None will be ignored.
The function returns the next expected rebalance time for a given algorithm UTC DateTime.
The function returns null if unknown, in which case the function will be called again in the
next loop. Returning current time will trigger rebalance.
portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)'''
super().__init__(rebalance, portfolioBias)
def ShouldCreateTargetForInsight(self, insight):
'''Method that will determine if the portfolio construction model should create a
target for this insight
Args:
insight: The insight to create a target for'''
# Ignore insights that don't have Weight value
return insight.Weight is not None
def DetermineTargetPercent(self, activeInsights):
'''Will determine the target percent for each insight
Args:
activeInsights: The active insights to generate a target for'''
result = {}
# We will adjust weights proportionally in case the sum is > 1 so it sums to 1.
weightSums = sum(self.GetValue(insight) for insight in activeInsights if self.RespectPortfolioBias(insight))
weightFactor = 1.0
if weightSums > 1:
weightFactor = 1 / weightSums
for insight in activeInsights:
result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * self.GetValue(insight) * weightFactor
return result
def GetValue(self, insight):
'''Method that will determine which member will be used to compute the weights and gets its value
Args:
insight: The insight to create a target for
Returns:
The value of the selected insight member'''
return abs(insight.Weight)'''
Bearish Stocks per Option Alpha list - Framework algo
Short stocks with high OI [TSLA, NVDA, AAPL, MSFT, AMD]
Trend Filter: 5 SMA?
Short entry: P X down BB1(40, 1) Pick one w/lowest SD
Exit: Portfolio DD
Rebalance frequency: Daily
10/10 working but insufficient margin errors
10/11 implement EP Chan BB(20) MR strategy using Zscore.
Working but flat insights not liquidating until next month
10/12 MR when BB within 1 SD
'''
from QuantConnect import *
from AlgorithmImports import *
class MyAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022, 9, 1) # Set start date for backtest
self.SetEndDate(2023, 10, 1)
self.SetCash(100000) # Set initial cash balance
#self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
self.Settings.MinimumOrderMarginPortfolioPercentage = 0.01
# Define a manual universe of high OI stocks per OA
tickers = ['TSLA',
'NVDA',
'AAPL',
'MSFT',
'AMD'
]
symbols = [ Symbol.Create(ticker, SecurityType.Equity, Market.USA) for ticker in tickers ]
self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )
self.UniverseSettings.Resolution = Resolution.Daily # Set data resolution for the universe
self.Settings.RebalancePortfolioOnSecurityChanges = False #Stop PCM from rebalancing when universe changes
self.Settings.RebalancePortfolioOnInsightChanges = True
# Use the custom alpha model for selecting stocks and generating insights
self.SetAlpha(BollingerAlphaModel())
# Set the portfolio construction and execution models
#self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None))
#self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: Expiry.EndOfMonth(time), portfolioBias = PortfolioBias.Long))
self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel(lambda time: Expiry.EndOfMonth(time), portfolioBias = PortfolioBias.Short))
#self.SetPortfolioConstruction( InsightWeightingPortfolioConstructionModel(self.RebalanceFunction) )
#self.SetPortfolioConstruction(RiskParityPortfolioConstructionModel(lambda time: Expiry.EndOfMonth(time), portfolioBias = PortfolioBias.Long))
self.SetExecution(ImmediateExecutionModel())
#self.SetRiskManagement(NullRiskManagementModel())
self.SetRiskManagement(MaximumDrawdownPercentPortfolio(0.01))
self.SetWarmup(41)
self.month = -1
def RebalanceFunction(self, time):
if self.month == -1:
self.month = time.month
return time
if self.month != time.month:
self.month = time.month
return time
return None
class BollingerAlphaModel(AlphaModel):
def __init__(self):
self.bb1 = {}
self.month = None
def Update(self, algorithm, data):
insights = []
scores = {}
entryZscore = 1
exitZscore = 0
selected_symbols = [] # Initialize selected_symbols to an empty list
if algorithm.IsWarmingUp:
return []
## If it has already run Update this month, then return nothing
if algorithm.Time.month == self.month:
return []
algorithm.Log('Update() called: ' + str(algorithm.Time))
## Update self.month so that it won't do anything in Update until a month has gone by
self.month = algorithm.Time.month
for symbol in self.bb1.keys():
if data.ContainsKey(symbol) and self.bb1[symbol].StandardDeviation.Current.Value != 0:
#score = self.bb1[symbol].PercentB.Current.Value
score = (self.bb1[symbol].Price.Current.Value - self.bb1[symbol].MiddleBand.Current.Value)/self.bb1[symbol].StandardDeviation.Current.Value
scores[symbol] = score
#algorithm.Debug(f"%B score for {symbol}: {score}")
algorithm.Debug(f"Z score for {symbol}: {score}")
# Select symbols with negative scores
#negative_scores = {k: v for k, v in scores.items() if v < 0}
# Select symbols for short entries (zscore > entryZscore)
shorts_scores = {k: v for k, v in scores.items() if v > entryZscore}
# Select top symbol based on score
selected = max(shorts_scores.items(), key=lambda x: x[1], default=None)
if selected is not None:
selected_symbols = [selected[0]]
algorithm.Debug(f"Selected symbol: {selected_symbols[0]}")
else:
algorithm.Debug("No symbols with zscores > entryZscore")
for symbol in selected_symbols:
insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Down, None, None, None, 1.0))
'''#Emit Flat insights to liquidate shorts
exit_scores = {k: v for k, v in scores.items() if v <= exitZscore}
if exit_scores is not None:
exit_symbols = list(exit_scores.keys())
# Find symbols that are both in exit_symbols and in algorithm.Insights
existing_symbols = [insight.Symbol for insight in algorithm.Insights if insight.Direction == InsightDirection.Down]
common_symbols = set(exit_symbols).intersection(existing_symbols)
for symbol in common_symbols:
insights.append(Insight.Price(symbol, Expiry.EndOfMonth, InsightDirection.Flat))'''
return insights
def OnSecuritiesChanged(self, algorithm, changes):
for added in changes.AddedSecurities:
symbol = added.Symbol
self.bb1[symbol] = algorithm.BB(symbol, 20, 1.0, MovingAverageType.Simple, Resolution.Daily)
for removed in changes.RemovedSecurities:
symbol = removed.Symbol
if symbol in self.mom1:
self.bb1.pop(symbol)