| Overall Statistics |
|
Total Orders 52 Average Win 2.18% Average Loss -2.92% Compounding Annual Return -7.088% Drawdown 20.800% Expectancy -0.127 Start Equity 100000 End Equity 91175.96 Net Profit -8.824% Sharpe Ratio -1.043 Sortino Ratio -0.788 Probabilistic Sharpe Ratio 2.186% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 0.75 Alpha -0.114 Beta 0.129 Annual Standard Deviation 0.096 Annual Variance 0.009 Information Ratio -1.59 Tracking Error 0.13 Treynor Ratio -0.772 Total Fees $115.77 Estimated Strategy Capacity $54000000.00 Lowest Capacity Asset MPC UXVGRK71ZB6T Portfolio Turnover 7.67% |
# region imports
from AlgorithmImports import *
# endregion
class wrapper(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 6, 10)
self.SetEndDate(2024, 9, 10)
self.SetCash(100000)
# Create instances of the SymbolData class for different stock pairs
self.model = [
meanReversion(self, "CVX", "JNJ"),
meanReversion(self, "DIS", "MRK"),
meanReversion(self, "AMZN", "MPC"),
]
def OnData(self, data):
for model in self.model:
model.bb.Update(self.Time, model.series.Current.Value)
model.OnData(data)
class meanReversion:
def __init__(self, algorithm, tickr1, tickr2):
self.algorithm = algorithm
self.ticker_1 = algorithm.AddEquity(tickr1, Resolution.Daily).Symbol
self.ticker_2 = algorithm.AddEquity(tickr2, Resolution.Daily).Symbol
# Create two identity indicators (a indicator that repeats the value without any processing)
self.ticker_1_identity = Identity(tickr1)
self.ticker_2_identity = Identity(tickr2)
# Set these indicators to receive the data from ticker_1 and ticker_2
algorithm.RegisterIndicator(
self.ticker_1, self.ticker_1_identity, Resolution.Daily)
algorithm.RegisterIndicator(
self.ticker_2, self.ticker_2_identity, Resolution.Daily)
# Create the portfolio as a new indicator using slope of linear regression in research.ipynb
# Will need to re-compute slope for each pair
self.series = IndicatorExtensions.Minus(
self.ticker_1_identity, IndicatorExtensions.Times(self.ticker_2_identity, 0.356))
# We then create a bollinger band with 120 steps for lookback period
# Will need to play around with band's std deviation
self.bb = BollingerBands(120, 0.6, MovingAverageType.Exponential)
# Define the objectives when going long or going short
# Can play around with divergent thresholds
self.long_targets = [PortfolioTarget(
self.ticker_1, 0.8), PortfolioTarget(self.ticker_2, -0.8)]
self.short_targets = [PortfolioTarget(
self.ticker_1, -0.8), PortfolioTarget(self.ticker_2, 0.8)]
self.is_invested = None
def OnData(self, data):
# For daily bars data is delivered at 00:00 of the day containing the closing price of the previous day (23:59:59)
if (not data.Bars.ContainsKey(self.ticker_1)) or (not data.Bars.ContainsKey(self.ticker_2)):
return
# Check if the bolllinger band indicator is ready (filled with 120 steps)
if not self.bb.IsReady:
return
serie = self.series.Current.Value
self.algorithm.Plot("ticker_2 Prices", "Open",
self.algorithm.Securities[self.ticker_2].Open)
self.algorithm.Plot("ticker_2 Prices", "Close",
self.algorithm.Securities[self.ticker_2].Close)
self.algorithm.Plot("Indicators", "Serie", serie)
self.algorithm.Plot("Indicators", "Middle",
self.bb.MiddleBand.Current.Value) # moving average
self.algorithm.Plot("Indicators", "Upper",
self.bb.UpperBand.Current.Value) # upper band
self.algorithm.Plot("Indicators", "Lower",
self.bb.LowerBand.Current.Value) # lower bank
# if it is not invested, see if there is an entry point
if not self.is_invested:
# if our portfolio is bellow the lower band, enter long
if serie < self.bb.LowerBand.Current.Value:
self.algorithm.SetHoldings(self.long_targets)
self.algorithm.Debug('Entering Long')
self.is_invested = 'long'
# if our portfolio is above the upper band, go short
if serie > self.bb.UpperBand.Current.Value:
self.algorithm.SetHoldings(self.short_targets)
self.algorithm.Debug('Entering Short')
self.is_invested = 'short'
# if it is invested in something, check the exiting signal (when it crosses the mean)
elif self.is_invested == 'long':
if serie > self.bb.MiddleBand.Current.Value:
self.algorithm.Liquidate()
self.algorithm.Debug('Exiting Long')
self.is_invested = None
elif self.is_invested == 'short':
if serie < self.bb.MiddleBand.Current.Value:
self.algorithm.Liquidate()
self.algorithm.Debug('Exiting Short')
self.is_invested = None