| Overall Statistics |
|
Total Trades 2327 Average Win 0.71% Average Loss -1.17% Compounding Annual Return 13.817% Drawdown 21.100% Expectancy 0.153 Net Profit 474.695% Sharpe Ratio 0.966 Probabilistic Sharpe Ratio 35.274% Loss Rate 28% Win Rate 72% Profit-Loss Ratio 0.60 Alpha 0.129 Beta 0.18 Annual Standard Deviation 0.158 Annual Variance 0.025 Information Ratio 0.104 Tracking Error 0.227 Treynor Ratio 0.848 Total Fees $18318.09 Estimated Strategy Capacity $8300000000.00 Lowest Capacity Asset TLT SGNKIKYGE9NP |
# Stock-Bond Trading using Kalman Filter
from math import floor
import numpy as np
# -------------------------------------------------
STOCK = 'QQQ'; BOND = 'TLT'; LEV = 1.00; T = 0.020;
# -------------------------------------------------
class KalmanFilterTrading(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2008, 1, 1)
self.SetEndDate(2021, 7, 3)
self.SetCash(100000)
self.SetBrokerageModel(AlphaStreamsBrokerageModel())
self.stock = self.AddEquity(STOCK, Resolution.Minute).Symbol
self.bond = self.AddEquity(BOND, Resolution.Minute).Symbol
self.kf = KalmanFilter()
self.SetWarmUp(10, Resolution.Daily)
self.Schedule.On(self.DateRules.EveryDay(self.stock), self.TimeRules.BeforeMarketClose(self.stock, 1),
self.Trade)
def Trade(self):
if self.IsWarmingUp: return
stock_price = self.Securities[self.stock].Price
bond_price = self.Securities[self.bond].Price
forecast_error, prediction_std_dev, hedge = self.kf.update(stock_price, bond_price)
if forecast_error < -prediction_std_dev:
self.SetHoldings(self.stock, LEV*hedge)
self.SetHoldings(self.bond, LEV*(1 - hedge))
elif forecast_error > prediction_std_dev:
self.SetHoldings(self.stock, LEV*(1 - hedge))
self.SetHoldings(self.bond, LEV*hedge)
self.Plot("HEDGE", "hedge", float(hedge))
self.Plot("ERROR", "forecast_error", float(forecast_error))
self.Plot("STDEV", "prediction_std_dev", float(prediction_std_dev))
class KalmanFilter:
def __init__(self):
self.delta = 1e-4
self.wt = self.delta / (1 - self.delta) * np.eye(2)
self.vt = 1e-3
self.theta = np.zeros(2)
self.P = np.zeros((2, 2))
self.R = None
self.t = T
def update(self, price_one, price_two):
F = np.asarray([price_one, 1.0]).reshape((1, 2))
y = price_two
if self.R is not None:
self.R = self.C + self.wt
else:
self.R = np.zeros((2, 2))
yhat = F.dot(self.theta)
et = y - yhat
Qt = F.dot(self.R).dot(F.T) + self.vt
sqrt_Qt = np.sqrt(Qt)
At = self.R.dot(F.T) / Qt
self.theta = self.theta + At.flatten() * et
self.C = self.R - At * F.dot(self.R)
hedge = self.t*self.theta[0]
return et, sqrt_Qt, hedge
'''
START
1/1/2008
END
7/2/2021
STOCK = 'QQQ'; BOND = 'TLT'; LEV = 1.00; T = 0.025;
EveryDay
Sharpe Ratio
0.971
Total Trades
2401
Average Win
0.67%
Average Loss
-1.12%
Compounding Annual Return
13.774%
Drawdown
20.900%
Expectancy
0.154
Net Profit
471.759%
PSR
35.955%
Loss Rate
28%
Win Rate
72%
Profit-Loss Ratio
0.60
Alpha
0.128
Beta
0.181
Annual Standard Deviation
0.156
Annual Variance
0.024
Information Ratio
0.102
Tracking Error
0.226
Treynor Ratio
0.837
Total Fees
$18019.24
Estimated Strategy Capacity
$6700000000.00
Lowest Capacity Asset
TLT SGNKIKYGE9NP
'''