| Overall Statistics |
|
Total Orders 25 Average Win 6.52% Average Loss -4.40% Compounding Annual Return 16.908% Drawdown 27.200% Expectancy 0.654 Start Equity 10000000 End Equity 16617281.76 Net Profit 66.173% Sharpe Ratio 0.775 Sortino Ratio 0.721 Probabilistic Sharpe Ratio 44.624% Loss Rate 33% Win Rate 67% Profit-Loss Ratio 1.48 Alpha 0.056 Beta 0.584 Annual Standard Deviation 0.124 Annual Variance 0.015 Information Ratio 0.248 Tracking Error 0.109 Treynor Ratio 0.164 Total Fees $3925.01 Estimated Strategy Capacity $12000000.00 Lowest Capacity Asset VXXB WRBPJAJZ2Q91 Portfolio Turnover 0.61% |
# region imports
from AlgorithmImports import *
from QuantConnect.Securities.Option import OptionPriceModels
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy import poly1d, signal
from datetime import datetime, timedelta
import pytz as pytz
from sklearn.metrics import r2_score, mean_absolute_error
from hmmlearn import hmm
from scipy.signal import stft, gaussian, butter, filtfilt, detrend, medfilt, savgol_filter
from scipy.fft import fft
from scipy.interpolate import interp1d
from numpy import quantile
from inspect import signature
from statsmodels.nonparametric.smoothers_lowess import lowess
# endregion
class BasicTemplateAlgorithm(QCAlgorithm):
def Initialize(self):
# Parameters to change
self.SST_resolution = 'Hour'
if self.SST_resolution == 'Hour':
self.tick=1
elif self.SST_resolution == 'Minute':
self.tick=60
elif self.SST_resolution == 'Second':
self.tick=60*60
self.SST_lookback = 6*self.tick*160 ## 6 month trading
self.SetStartDate(2021,1, 1)
self.SetEndDate(2024, 4,1)
#4. Set Initial Cash and Warmup Data
self.SetCash(10000000)
self.SetWarmup(300)
#5. Assign Symbols for SST
self.sst_symbols =[]
if self.SST_resolution == 'Hour':
self.AddEquity('SPY', Resolution.Hour)
elif self.SST_resolution == 'Minute':
self.AddEquity('SPY', Resolution.Minute)
elif self.SST_resolution == 'Second':
self.AddEquity('SPY', Resolution.Second)
self.sst_symbols.append(self.Symbol('SPY'))
if self.SST_resolution == 'Hour':
self.AddEquity('VXX', Resolution.Hour)
elif self.SST_resolution == 'Minute':
self.AddEquity('VXX', Resolution.Minute)
elif self.SST_resolution == 'Second':
self.AddEquity('VXX', Resolution.Second)
#self.sst_symbols.append(self.Symbol('VXX'))
self.sym = self.sst_symbols[0]
self.lookback = self.SST_lookback
self.invested_bool = 0
# If V.X.X. then
self.s = "SHORT "
self.l = "LONG "
def OnData(self, slice: Slice):
#1. Return if warming up
if self.IsWarmingUp:
return
if True:
self.df = self.History(self.sst_symbols, self.lookback)
self.dg = self.df["close"].unstack(level=0)
## realized volatility
## 160 trading hours per week.
RV = self.dg.pct_change().rolling(self.tick*160).std() * (252**0.5)
RV = RV.to_numpy()
RV = RV[~np.isnan(RV)]
RV = RV.reshape((-1,1))
## 6 hour minute medium
short_medium= np.quantile(RV[-self.tick*6:], 0.5)
## 1 week medium
Q_20=np.quantile(RV[-self.tick*160:],0.2)
Q_30=np.quantile(RV[-self.tick*160:],0.3)
Q_40=np.quantile(RV[-self.tick*160:],0.4)
Q_50=np.quantile(RV[-self.tick*160:],0.5)
Q_60=np.quantile(RV[-self.tick*160:],0.6)
Q_70=np.quantile(RV[-self.tick*160:],0.7)
Q_80=np.quantile(RV[-self.tick*160:],0.8)
self.Plot('Quantiles', '30', Q_30)
self.Plot('Quantiles', '40', Q_40)
self.Plot('Quantiles', '50', Q_50)
self.Plot('Quantiles', '60', Q_60)
self.Plot('Quantiles', '70', Q_70)
self.Plot('Quantiles', 'Short Median', short_medium)
# Trend following
if not self.invested_bool:
if short_medium < Q_30:
# Trend following
self.SetHoldings("VXX", -0.3)
self.invested_bool = 1
self.temp = 0
self.priceVXX = self.Portfolio['VXX'].Price
self.quant = Q_40
self.Debug(self.s + 'VXX at ' + str(self.Time) + " (SST: short avg low FOLLOW TREND)")
elif short_medium > Q_70:
self.SetHoldings("VXX", -0.3)
self.invested_bool = 1
self.temp = 2
self.priceVXX = self.Portfolio['VXX'].Price
self.quant = Q_60
self.Debug(self.l + 'VXX at ' + str(self.Time) + " (SST: short avg high FOLLOW TREND)")
else:
if self.temp == 0 and short_medium > self.quant:
self.Liquidate()
self.invested_bool = 0
self.Debug('Liquidate ' + 'VXX at ' + str(self.Time))
elif self.temp == 2 and short_medium < self.quant:
self.Liquidate()
self.invested_bool = 0
self.Debug('Liquidate ' + 'VXX at ' + str(self.Time))
#2. Check stop loss criterion every hour of every day
# if self.invested_bool:
# if (self.temp == 0 or self.temp == 3) and (self.Portfolio['VXX'].Price < self.priceVXX*0.9):
# self.Liquidate()
# self.invested_bool = 0
# self.Debug ('Stop Loss ' + self.l+ 'at ' + str(self.Time))
# elif (self.temp == 1 or self.temp == 2) and (self.Portfolio['VXX'].Price > self.priceVXX*1.1):
# self.Liquidate()
# self.invested_bool = 0
# self.Debug ('Stop Loss ' + self.s+ 'at ' + str(self.Time))