| Overall Statistics |
|
Total Trades 181 Average Win 0.97% Average Loss -0.59% Compounding Annual Return 15.243% Drawdown 9.500% Expectancy 0.006 Net Profit 1.725% Sharpe Ratio 0.57 Probabilistic Sharpe Ratio 42.712% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.66 Alpha -0.463 Beta 0.403 Annual Standard Deviation 0.324 Annual Variance 0.105 Information Ratio -4.24 Tracking Error 0.335 Treynor Ratio 0.458 Total Fees $450.00 |
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Common")
from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Securities.Option import OptionPriceModels
import numpy as np
from datetime import timedelta
### <summary>
### Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve
### data for and manually subscribing to them.
### </summary>
### <meta name="tag" content="strategy example" />
### <meta name="tag" content="options" />
### <meta name="tag" content="using data" />
### <meta name="tag" content="selecting options" />
### <meta name="tag" content="manual selection" />
class OptionChainProviderAlgorithm(QCAlgorithm):
highestPrice = 0
trailing_stop_pct = .93
trailing_stop_price = 0
def Initialize(self):
self.SetStartDate(2020, 4, 21)
#self.SetEndDate(2016, 6, 24)
self.SetCash(20000)
# add the underlying asset
#self.equity = self.AddEquity("SPY", Resolution.Minute)
self.equity = self.AddEquity("SPY R735QTJ8XC9X", Resolution.Minute)
#self.equity = self.Symbol("SPY R735QTJ8XC9X", Resolution.Minute)
#spy = self.Symbol("SPY R735QTJ8XC9X")
#SPY R735QTJ8XC9X
self.syl = "SPY"
self.spy = "SPY"
self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
# create a XX-period exponential moving average
self.fast = self.EMA("SPY", 30, Resolution.Minute)
# create a XX-period exponential moving average since this is minute, may have to multiply by 2 if want 2 min chart
#self.slow = self.EMA("SPY", 50, Resolution.Minute)
slow_ema_lookback = 100
self.slow = self.EMA("SPY", slow_ema_lookback, Resolution.Minute);
self.SetWarmUp(slow_ema_lookback)
# initialize the option contract with empty string
self.contract_put = str()
self.contract_call = str ()
self.contractsAdded = set()
equity = self.AddEquity(self.syl, Resolution.Minute)
self.underlyingsymbol = equity.Symbol
def OnData(self, data):
if self.IsWarmingUp:
return
#if not self.Portfolio[self.equity.Symbol].Invested:
# self.MarketOrder(self.equity.Symbol, 100)
#put entry
if not (self.Portfolio.Invested) and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)):
# worked self.equity.Price > 0:
#(self.fast.Current.Value < self.slow.Current.Value):
#and (self.equity ["GOOG"] >= (self.fast.Current.Value - 0.03)):
#if not (self.Portfolio[self.contract_put].Invested)
#(self.Securities.ContainsKey(self.contract_put) and ):
#(self.fast.Current.Value < self.slow.Current.Value) and (slice["SPY"].Price >= (self.fast.Current.Value - 0.03)
self.contract_put = self.OptionsFilter(data)
self.MarketOrder(self.contract_put, +10)
self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_put].Close
#call entry
if not (self.Portfolio.Invested) and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)):
#(self.Securities.ContainsKey(self.contract_call) and self.Portfolio[self.contract_call].Invested):
self.contract_call = self.OptionsFilter(data)
self.MarketOrder(self.contract_call, +10)
self.trailing_stop_price = self.trailing_stop_pct * self.Securities[self.contract_call].Close
#exit
if (self.Portfolio.Invested):
contract_price_call = self.Securities[self.contract_call].Close
contract_price_put = self.Securities[self.contract_put].Close
self.Log(f"Stop price: {self.trailing_stop_price}")
#near 20% exit
if (self.Securities[self.contract_call].Close >= (self.trailing_stop_price * 1.3)):
self.Log(f"20% profit call contract price: {contract_price_call}")
self.MarketOrder(self.contract_call, -10)
self.Log("Liquidated")
elif (self.Securities[self.contract_put].Close >= (self.trailing_stop_price * 1.3)):
self.MarketOrder(self.contract_put, -10)
self.Log(f"20% profit put contract price: {contract_price_put}")
self.Log("Liquidated")
#set trailing stop for calls
elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call < self.trailing_stop_price):
self.Log(f"Call contract price: {contract_price_call}")
self.MarketOrder(self.contract_call, -10)
self.Log("Liquidated")
elif (self.Portfolio[self.contract_call].Invested) and (contract_price_call > self.highestPrice):
self.Log(f"Call contract price: {contract_price_call}")
self.highestPrice = contract_price_call
self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct
#take 20% off puts
#set trailing stop for puts
elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put < self.trailing_stop_price):
self.Log(f"Put contract price: {contract_price_put}")
self.MarketOrder(self.contract_put, -10)
self.trailing_stop_price = 0
self.highestPrice = 0
self.Log("Liquidated")
elif (self.Portfolio[self.contract_put].Invested) and (contract_price_put > self.highestPrice):
self.Log(f"Put contract price: {contract_price_put}")
self.highestPrice = contract_price_put
self.trailing_stop_price = self.highestPrice * self.trailing_stop_pct
#if not (self.Securities.ContainsKey(self.contract) and self.Portfolio[self.contract].Invested):
# self.contract = self.OptionsFilter(data)
#if self.Securities.ContainsKey(self.contract) and not self.Portfolio[self.contract].Invested:
# self.MarketOrder(self.contract, -1)
#def contract_put(self):
# contracts = self.OptionChainProvider.GetOptionContractList(self.security.symbol, self.Time.date())
# if len(contracts) == 0: return
# filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14)
# put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put]
# if len(put) == 0:
# return
# sorted the contracts according to their expiration dates and choose the ATM options
# contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)),
# key = lambda x: x.ID.Date, reverse=False)
# self.contract = contracts[0]
# self.AddOptionContract(self.contract, Resolution.Minute)
# self.Buy(self.contract, 5)
def OptionsFilter(self, data):
''' OptionChainProvider gets a list of option contracts for an underlying symbol at requested date.
Then you can manually filter the contract list returned by GetOptionContractList.
The manual filtering will be limited to the information included in the Symbol
(strike, expiration, type, style) and/or prices from a History call '''
contracts = self.OptionChainProvider.GetOptionContractList(self.equity.Symbol, data.Time)
self.underlyingPrice = self.Securities[self.equity.Symbol].Price
# filter the out-of-money call options from the contract list which expire in 0 to 7 days from now on
otm_calls = [i for i in contracts if i.ID.OptionRight == OptionRight.Call] #and
#i.ID.StrikePrice - self.underlyingPrice > 0 and
#0 < (i.ID.Date - data.Time).days < 3]
#filter the out-of-money put options fromt the contract list which expire in 0 to 7 days from now
otm_puts = [i for i in contracts if i.ID.OptionRight == OptionRight.Put] #and
#i.ID.StrikePrice - self.underlyingPrice > 0 and
#0 < (i.ID.Date - data.Time).days < 3]
#this should help select the soonest exp.
#if len(otm_puts) == 0:
# return
#this should help select the soonest exp.
#if len(otm_calls) == 0:
# return
if len(otm_calls) > 0 and (self.fast.Current.Value >= self.slow.Current.Value) and (self.fast.Current.Value <= self.equity.Price <= (self.fast.Current.Value + 0.03)):
contract_call = sorted(sorted(otm_calls, key = lambda x: x.ID.Date, reverse=False),
key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0]
if contract_call not in self.contractsAdded:
self.contractsAdded.add(contract_call)
# use AddOptionContract() to subscribe the data for specified contract
self.AddOptionContract(contract_call, Resolution.Minute)
return contract_call
if len(otm_puts) > 0 and (self.fast.Current.Value <= self.slow.Current.Value) and (self.fast.Current.Value >= self.equity.Price >= (self.fast.Current.Value - 0.03)):
contract_put = sorted(sorted(otm_puts, key = lambda x: x.ID.Date, reverse=False),
key = lambda x: abs(self.underlyingPrice - x.ID.StrikePrice))[0]
if contract_put not in self.contractsAdded:
self.contractsAdded.add(contract_put)
# use AddOptionContract() to subscribe the data for specified contract
self.AddOptionContract(contract_put, Resolution.Minute)
return contract_put
else:
return str()
#attempt to select closest expiry.
#def contract_put(self):
# ontracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
# if len(contracts) == 0: return
# filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -1, 1, 0, 14)
# put = [x for x in filtered_contracts if x.ID.OptionRight == OptionRight.Put]
# if len(put) == 0:
# return
# sorted the contracts according to their expiration dates and choose the ATM options
# contracts = sorted(sorted(put, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)),
# key = lambda x: x.ID.Date, reverse=False)
# self.contract = contracts[0]
# self.AddOptionContract(self.contract, Resolution.Minute)
# self.Buy(self.contract_put, 1)
# def BuyCall(self):
# contracts = self.OptionChainProvider.GetOptionContractList(self.underlyingsymbol, self.Time.date())
# if len(contracts) == 0: return
# filtered_contracts = self.InitialFilter(self.underlyingsymbol, contracts, -3, 3, 0, 30)
# call = [x for x in filtered_contracts if x.ID.OptionRight == 1]
# sorted the contracts according to their expiration dates and choose the ATM options
# contracts = sorted(sorted(call, key = lambda x: abs(self.Securities[self.syl].Price - x.ID.StrikePrice)),
# key = lambda x: x.ID.Date, reverse=True)
# self.contract = contracts[0]
# self.AddOptionContract(self.contract, Resolution.Minute)
# self.Buy(self.contract, 1)
#def InitialFilter(self, underlyingsymbol, symbol_list, min_strike_rank, max_strike_rank, min_expiry, max_expiry):
# ''' This method is an initial filter of option contracts
# according to the range of strike price and the expiration date '''
# if len(symbol_list) == 0 : return
# fitler the contracts based on the expiry range
# contract_list = [i for i in symbol_list if min_expiry < (i.ID.Date.date() - self.Time.date()).days < max_expiry]
# if len(contract_list) == 0:
# return []
# find the strike price of ATM option
# atm_strike = sorted(contract_list,
# key = lambda x: abs(x.ID.StrikePrice - self.Securities[underlyingsymbol].Price))[0].ID.StrikePrice
# strike_list = sorted(set([i.ID.StrikePrice for i in contract_list]))
# find the index of ATM strike in the sorted strike list
# atm_strike_rank = strike_list.index(atm_strike)
# try:
# min_strike = strike_list[atm_strike_rank + min_strike_rank]
# max_strike = strike_list[atm_strike_rank + max_strike_rank]
# except:
# min_strike = strike_list[0]
# max_strike = strike_list[-1]
# filtered_contracts = [i for i in contract_list if i.ID.StrikePrice >= min_strike and i.ID.StrikePrice <= max_strike]
# return filtered_contracts