| Overall Statistics |
|
Total Orders 23 Average Win 3.65% Average Loss -9.76% Compounding Annual Return 21.724% Drawdown 17.100% Expectancy 0.145 Net Profit 8.390% Sharpe Ratio 0.695 Sortino Ratio 0.458 Probabilistic Sharpe Ratio 39.981% Loss Rate 17% Win Rate 83% Profit-Loss Ratio 0.37 Alpha 0.106 Beta 0.879 Annual Standard Deviation 0.252 Annual Variance 0.064 Information Ratio 0.403 Tracking Error 0.238 Treynor Ratio 0.199 Total Fees $22.00 Estimated Strategy Capacity $3800000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 24.94% |
# region imports
from datetime import datetime, date, time, timedelta
from AlgorithmImports import *
# endregion
class LearningProject(QCAlgorithm):
UnderlyingTicker = "SPY"
def Initialize(self):
self.SetStartDate(2014, 1, 1)
self.SetEndDate(2014, 6, 1)
self.SetCash(1000)
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.SetTimeZone(TimeZones.Chicago)
equity = self.AddEquity(self.UnderlyingTicker, Resolution.Minute)
option = self.AddOption(self.UnderlyingTicker)
self.option_symbol = option.Symbol
self.smaFast = self.SMA(equity.Symbol, 13) # prev: 13
self.WarmUpIndicator(equity.Symbol, self.smaFast)
self.smaSlow = self.SMA(equity.Symbol, 31) # prev: 31
self.WarmUpIndicator(equity.Symbol, self.smaSlow)
self.last_profit = 0
self.prev_smaFast = 0
self.prev_smaSlow = 0
self.expiry = None
# set our strike/expiry filter for this option chain
option.SetFilter(lambda u: (u.IncludeWeeklys()
.Strikes(-2, +2)
# Expiration method accepts TimeSpan objects or integer for days.
# The following statements yield the same filtering criteria
.Expiration(2, 3)))
#.Expiration(TimeSpan.Zero, TimeSpan.FromDays(180))))
# use the underlying equity as the benchmark
self.SetBenchmark(equity.Symbol)
def HandleInvestment(self,slice) -> None:
profit = self.Portfolio.TotalUnrealizedProfit
last_profit = self.last_profit
entry_plus_day = datetime(
year = self.placed_time.year,
month = self.placed_time.month,
day = self.placed_time.day + 1,
hour = 9
)
four_hours = 60 * 60 * 4
expiry_delta = self.expiry.timestamp() - self.Time.timestamp()
if expiry_delta < four_hours:
self.MarketOrder(self.active_symbol, -self.qty)
return
if self.Time.date().__lt__(entry_plus_day):
return
if profit > self.Portfolio.Cash * 0.2:
self.MarketOrder(self.active_symbol, -self.qty)
return
if profit > 0 and profit > last_profit + 5:
return
if profit > 0 and profit < last_profit - 5:
self.MarketOrder(self.active_symbol, -self.qty)
elif profit < last_profit - 10:
self.MarketOrder(self.active_symbol, -self.qty)
def OnData(self,slice) -> None:
if not self.IsMarketOpen(self.option_symbol): return
if self.Portfolio.Invested and self.IsMarketOpen(self.option_symbol):
self.HandleInvestment(slice)
self.last_profit = self.Portfolio.TotalUnrealizedProfit
return
history_day_close = self.History(["SPY"], 2, Resolution.Daily)
prev_close2 = history_day_close.loc["SPY"]['close'][0]
prev_close1 = history_day_close.loc["SPY"]['close'][1]
smaFast = self.smaFast.Current.Value
smaSlow = self.smaSlow.Current.Value
is_trending = prev_close1 > prev_close2 + 1.0
sma_crossover = False
if self.prev_smaFast < self.prev_smaSlow and smaFast > smaSlow:
sma_crossover = True
self.prev_smaFast = smaFast
self.prev_smaSlow = smaSlow
if not (is_trending and sma_crossover):
return
chain = slice.OptionChains.GetValue(self.option_symbol)
if chain is None:
return
# we sort the contracts to find at the money (ATM) contract with farthest expiration
callContracts = sorted(sorted(sorted(chain, \
# key = lambda x: abs(chain.Underlying.Price - x.Strike)), \
key = lambda x: x.Strike), \
key = lambda x: x.Expiry, reverse=True), \
key = lambda x: x.Right == OptionRight.Call, reverse=True)
# if found, trade it
if len(callContracts) == 0: return
ask_cost = callContracts[0].AskPrice * 100
max_cost = self.Portfolio.Cash * 0.15
qty = Math.Floor(max_cost / ask_cost)
self.qty = qty if qty > 0 else 1
self.expiry = callContracts[0].Expiry
self.active_symbol = callContracts[0].Symbol
self.placed_time = self.Time
self.order = self.MarketOrder(self.active_symbol, self.qty)
def OnOrderEvent(self, orderEvent):
self.Log(str(orderEvent))