| Overall Statistics |
|
Total Trades 688 Average Win 22.79% Average Loss -0.16% Compounding Annual Return 15.420% Drawdown 22.800% Expectancy 8.238 Net Profit 765.726% Sharpe Ratio 0.296 Probabilistic Sharpe Ratio 0.000% Loss Rate 94% Win Rate 6% Profit-Loss Ratio 143.03 Alpha 0.239 Beta -0.06 Annual Standard Deviation 0.793 Annual Variance 0.629 Information Ratio 0.191 Tracking Error 0.813 Treynor Ratio -3.897 Total Fees $69941.00 Estimated Strategy Capacity $55000000.00 Lowest Capacity Asset SPY Y5PEZIKI6JYE|SPY R735QTJ8XC9X |
# region imports
from AlgorithmImports import *
# endregion
from hmmlearn import hmm
from statsmodels.tsa.regime_switching.markov_regression import MarkovRegression
from scipy.stats import multivariate_normal
import numpy as np
class HMMandManualEqFutureOptionPortfolio(QCAlgorithm):
def Initialize(self):
# Option contracts in the portfolio, SPY in this case:
self.option_eq = self.AddEquity("SPY", Resolution.Minute)
self.option_eq.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.option_symbol = self.option_eq.Symbol
self.put_contract = None
self.call_contract = None
self.init_money = 100000
self.SetCash(self.init_money)
self.timetoexpire = 3
# Options take profit
self.tp = 0.2
# backtest period
self.SetStartDate(2008, 1, 12)
self.SetBenchmark("SPY")
self.SetSecurityInitializer(lambda x: x.SetMarketPrice(self.GetLastKnownPrice(x)))
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 60), self.portfolioOptimization)
# checking options
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=60)), self.checkOptions)
def OnData(self, data):
pass
def portfolioOptimization(self):
w_put = 0.002
w_call = 0.002
portfolio = []
if w_put != 0:
if self.put_contract is None or not self.Portfolio[self.put_contract].Invested:
self.put_contract = self.GetContractPut(self.option_symbol, 0.8, 30, 45)
if self.put_contract is not None:
portfolio.append(PortfolioTarget(self.put_contract, w_put))
if w_call != 0:
if self.call_contract is None or not self.Portfolio[self.call_contract].Invested:
self.call_contract = self.GetContractCall(self.option_symbol, 1.2, 30, 45)
if self.call_contract is not None:
portfolio.append(PortfolioTarget(self.call_contract, w_call))
self.SetHoldings(portfolio)
def GetContractPut(self, symbol, percentage=0.8, minExpiry=30, maxExpiry=60):
targetStrike = self.Securities[symbol].Price * percentage
error = self.Securities[symbol].Price * 0.05
contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time)
if not contracts:
return None
puts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put]
puts = sorted(puts, key=lambda x: x.ID.Date)
puts = [x for x in puts if abs(x.ID.StrikePrice - targetStrike) <= error]
puts = [x for x in puts if minExpiry < (x.ID.Date - self.Time).days <= maxExpiry]
if len(puts) == 0:
return None
self.AddOptionContract(puts[0], Resolution.Minute, fillDataForward=False, extendedMarketHours=False)
self.Error(str(self.Time) + " " + str(puts[0].ID.StrikePrice / self.Securities[symbol].Price) + " " + str(
(puts[0].ID.Date - self.Time).days))
return puts[0]
def GetContractCall(self, symbol, percentage=1.2, minExpiry=30, maxExpiry=60):
targetStrike = self.Securities[symbol].Price * percentage
error = self.Securities[symbol].Price * 0.05
contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time)
calls = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
calls = sorted(calls, key=lambda x: x.ID.Date)
calls = [x for x in calls if abs(x.ID.StrikePrice - targetStrike) <= error]
calls = [x for x in calls if minExpiry < (x.ID.Date - self.Time).days <= maxExpiry]
if len(calls) == 0:
return None
self.AddOptionContract(calls[0], Resolution.Minute, fillDataForward=False, extendedMarketHours=False)
self.Error(str(self.Time) + " " + str(calls[0].ID.StrikePrice / self.Securities[symbol].Price) + " " + str(
(calls[0].ID.Date - self.Time).days))
return calls[0]
def checkOptions(self):
now = self.Time
exchange = self.Securities["SPY"].Exchange
if not exchange.ExchangeOpen:
return
slice = self.CurrentSlice
if self.put_contract is not None and self.Portfolio[self.put_contract].Invested:
p = self.Portfolio[self.put_contract].TotalCloseProfit()
q = self.Portfolio[self.put_contract].Quantity
price = self.Portfolio[self.put_contract].Price
strike_price = self.put_contract.ID.StrikePrice
price_underlying = self.Portfolio[self.option_symbol].Price
init_price = self.Portfolio[self.put_contract].AveragePrice
if slice.ContainsKey(self.put_contract) and slice[self.put_contract] is not None and not slice[
self.put_contract].IsFillForward:
if self.Portfolio[
self.put_contract].TotalCloseProfit() / self.Portfolio.TotalPortfolioValue > self.tp:
self.SetHoldings(self.put_contract, 0)
self.put_contract = None
return
if ((self.put_contract.ID.Date - now).days <= self.timetoexpire):
self.SetHoldings(self.put_contract, 0)
self.put_contract = None
if self.call_contract is not None and self.Portfolio[self.call_contract].Invested:
strike_price = self.call_contract.ID.StrikePrice
price_underlying = self.Portfolio[self.option_symbol].Price
if slice.ContainsKey(self.call_contract) and slice[self.call_contract] is not None and not slice[
self.call_contract].IsFillForward:
if self.Portfolio[
self.call_contract].TotalCloseProfit() / self.Portfolio.TotalPortfolioValue > self.tp:
self.SetHoldings(self.call_contract, 0)
self.call_contract = None
return
if ((self.call_contract.ID.Date - now).days <= self.timetoexpire):
self.SetHoldings(self.call_contract, 0)
self.call_contract = None