| Overall Statistics |
|
Total Trades 334 Average Win 0.01% Average Loss -0.02% Compounding Annual Return -0.254% Drawdown 0.300% Expectancy -0.039 Net Profit -0.156% Sharpe Ratio -0.949 Probabilistic Sharpe Ratio 6.075% Loss Rate 49% Win Rate 51% Profit-Loss Ratio 0.90 Alpha -0.003 Beta 0.001 Annual Standard Deviation 0.003 Annual Variance 0 Information Ratio -2.129 Tracking Error 0.123 Treynor Ratio -2.856 Total Fees $2073.36 |
from pykalman import KalmanFilter
import numpy as np
from System.Drawing import Color
class VerticalCalibratedCompensator(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2019, 5, 20) # Set Start Date
self.SetEndDate(2019, 12, 30)
self.ticker1="MSFT"
self.ticker2="SPY"
self.SetCash(5000000) # Set Strategy Cash
self.AddEquity(self.ticker1, Resolution.Hour)
self.AddEquity(self.ticker2, Resolution.Hour)
self.delt=.05
self.Delta=0.45
self.TargetXQty=0
self.TargetYQty=0
self.current_state="A"
self.next_state="A"
self.counter=0
self.state_mean = np.zeros(2)
self.state_cov=np.ones((2, 2))
self.delta = 1/2
self.trans_cov = self.delta / (1 - self.delta) * np.eye(2)
self.prevAvgXfillPrice=0
self.prevAvgYfillPrice=0
self.kf = KalmanFilter(n_dim_obs=1, n_dim_state=2,
initial_state_mean=self.state_mean,
initial_state_covariance=self.state_cov,
transition_matrices=np.eye(2),
observation_covariance=1.0,
transition_covariance=self.trans_cov)
stockPlot2 = Chart("Beta")
stockPlot2.AddSeries(Series('beta', SeriesType.Line,0))
self.AddChart(stockPlot2)
stockPlot3 = Chart("Intercept")
stockPlot3.AddSeries(Series('intercept', SeriesType.Line, '$', Color.Green))
self.AddChart(stockPlot3)
stockPlot1 = Chart('Trade Plot')
# On the Trade Plotter Chart we want 3 series: trades and price:
stockPlot1.AddSeries(Series('Spread', SeriesType.Line, '$', Color.Green))
stockPlot1.AddSeries(Series('Buy', SeriesType.Scatter, '$', Color.Red, ScatterMarkerSymbol.Triangle))
stockPlot1.AddSeries(Series('Sell', SeriesType.Scatter, '$', Color.Blue, ScatterMarkerSymbol.TriangleDown))
self.AddChart(stockPlot1)
def OnData(self, data):
if not self.IsMarketOpen(self.ticker1):
return
if( not(data.ContainsKey(self.ticker1)) or not(data.ContainsKey(self.ticker2)) ):
return
x=data.Bars[self.ticker1].Close
y=data.Bars[self.ticker2].Close
self.state_mean, self.state_cov= (self.kf.filter_update(
filtered_state_mean =self.state_mean,
filtered_state_covariance =self.state_cov,
observation = np.array([y]),
observation_matrix = np.array([[x,1]])
))
beta=round(self.state_mean[0],2)
intercept=round(self.state_mean[1],2)
y_hat=round( (x * beta) + intercept , 2)
res=round(y-y_hat,2)
self.Plot('Trade Plot' , 'Spread', 100 * res)
self.Plot('Beta', 'beta', 100 * beta)
self.Plot('Intercept', 'intercept', 100 * intercept)
#self.Log(f"beta : {beta}")
#self.Log(f"intercept : {intercept}")
#self.Log(f"state mean {self.state_mean}")
#self.Log(f"residue {res}")
#self.Log(f"x {x}\ny {y}\n\n")
if(-self.delt <= res and res < self.delt):
self.next_state="A"
if(self.delt<= res and res <self.Delta ):
self.next_state="B+"
if(self.Delta<= res and res < (2 * self.Delta) ):
self.next_state="C+"
if( (2 * self.Delta) <= res and res < (3 * self.Delta) ):
self.next_state="D+"
if( (3 * self.Delta) <= res and res < (4 * self.Delta) ):
self.next_state="E+"
if( - self.delt >= res and res > -self.Delta ):
self.next_state="B-"
if(- self.Delta >= res and res > (-2 * self.Delta) ):
self.next_state="C-"
if( (-2 * self.Delta) >= res and res > (-3 * self.Delta) ):
self.next_state="D-"
if( (-3 * self.Delta) >= res and res > (-4 * self.Delta) ):
self.next_state="E-"
if(self.current_state != self.next_state):
mf=-400
if(self.next_state=="A"):
self.TargetXQty=self.Portfolio[self.ticker1].Quantity
self.TargetYQty=self.Portfolio[self.ticker2].Quantity
if(self.next_state=="C+"):
self.TargetXQty=round(beta*mf,0)
self.TargetYQty=-mf
if(self.next_state=="D+"):
self.TargetXQty=round(beta*3*mf,0)
self.TargetYQty=-3*mf
if(self.next_state=="E+"):
self.TargetXQty=round(beta*7*mf,0)
self.TargetYQty=-7*mf
if(self.next_state=="C-"):
self.TargetXQty=-round(beta*1*mf,0)
self.TargetYQty=mf
if(self.next_state=="D-"):
self.TargetXQty=-round(beta*3*mf,0)
self.TargetYQty=3*mf
if(self.next_state=="E-"):
self.TargetXQty=-round(beta*7*mf,0)
self.TargetYQty=7*mf
self.TargetXQty=self.TargetXQty
self.TargetYQty=self.TargetYQty
oldXqty=self.Portfolio[self.ticker1].Quantity
oldYqty=self.Portfolio[self.ticker2].Quantity
self.xTradeQty=-(oldXqty - self.TargetXQty)
self.yTradeQty=-(oldYqty - self.TargetYQty)
if(abs(self.yTradeQty)>10):
unrealizedProfit=self.Portfolio.TotalUnrealizedProfit
orderTicketX=self.MarketOrder(self.ticker1 , self.xTradeQty, True, '')
orderTicketY=self.MarketOrder(self.ticker2 , self.yTradeQty, True, '')
avgXfillPrice=orderTicketX.AverageFillPrice
avgYfillPrice=orderTicketY.AverageFillPrice
#Do logging
if orderTicketX.Status == OrderStatus.Filled and orderTicketY.Status == OrderStatus.Filled:
self.Log(f",{oldXqty},{oldYqty},{self.prevAvgXfillPrice},{self.prevAvgYfillPrice},{x},{y},{res},{beta},{intercept},{unrealizedProfit},{avgXfillPrice},{avgYfillPrice},{self.current_state},{self.next_state}")
#update prevAvgXfillPrice prevAvgYfillPrice
self.prevAvgXfillPrice=avgXfillPrice
self.prevAvgYfillPrice=avgYfillPrice
if(self.yTradeQty>0):
self.Plot('Trade Plot' , 'Buy', 100 * res)
if(self.yTradeQty<0):
self.Plot('Trade Plot' , 'Sell', 100 * res)
#self.Log(f"moving from {self.current_state} state to state{self.next_state}")
self.current_state=self.next_statefrom Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
class BankingIndustryStocks(FundamentalUniverseSelectionModel):
'''
This module selects the most liquid stocks listed on the Nasdaq Stock Exchange.
'''
def __init__(self, filterFineData = True, universeSettings = None, securityInitializer = None):
'''Initializes a new default instance of the TechnologyUniverseModule'''
super().__init__(filterFineData, universeSettings, securityInitializer)
self.numberOfSymbolsCoarse = 1000
self.numberOfSymbolsFine = 100
self.dollarVolumeBySymbol = {}
self.lastMonth = -1
def SelectCoarse(self, algorithm, coarse):
'''
Performs a coarse selection:
-The stock must have fundamental data
-The stock must have positive previous-day close price
-The stock must have positive volume on the previous trading day
'''
if algorithm.Time.month == self.lastMonth:
return Universe.Unchanged
sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData and x.Volume > 0 and x.Price > 0],
key = lambda x: x.DollarVolume, reverse=True)[:self.numberOfSymbolsCoarse]
self.dollarVolumeBySymbol = {x.Symbol:x.DollarVolume for x in sortedByDollarVolume}
# If no security has met the QC500 criteria, the universe is unchanged.
if len(self.dollarVolumeBySymbol) == 0:
return Universe.Unchanged
return list(self.dollarVolumeBySymbol.keys())
def SelectFine(self, algorithm, fine):
'''
Performs a fine selection for companies in the Morningstar Banking Sector
'''
# Filter stocks and sort on dollar volume
sortedByDollarVolume = sorted([x for x in fine if (\
x.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.FinancialServices ) ],
key = lambda x: self.dollarVolumeBySymbol[x.Symbol], reverse=True)
if len(sortedByDollarVolume) == 0:
return Universe.Unchanged
self.lastMonth = algorithm.Time.month
return [x.Symbol for x in sortedByDollarVolume[:self.numberOfSymbolsFine]]