| Overall Statistics |
|
Total Trades 937 Average Win 0.16% Average Loss -0.09% Compounding Annual Return 3.655% Drawdown 2.700% Expectancy 0.409 Net Profit 19.707% Sharpe Ratio 1.593 Probabilistic Sharpe Ratio 90.800% Loss Rate 49% Win Rate 51% Profit-Loss Ratio 1.75 Alpha 0.029 Beta 0.047 Annual Standard Deviation 0.022 Annual Variance 0 Information Ratio -0.506 Tracking Error 0.181 Treynor Ratio 0.747 Total Fees $937.00 |
from decimal import Decimal
class FibonacciOptionStraddle(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 7, 29) # Set Start Date
self.SetCash(30000) # Set Strategy Cash
self.SetBenchmark("SPY")
#Ticker list
self.tickers = ["MSFT", "GS", "V", "CSCO", "PG", "VZ", "TGT", "COST", "PEP", "WMT", "AAPL", "NEE"]
self.maxBySymbol = {}
self.totalPercentInv = 1/len(self.tickers)
self.EnableAutomaticIndicatorWarmUp = True
for ticker in self.tickers:
symbol = self.AddEquity(ticker, Resolution.Hour).Symbol
self.Securities[ticker].SetDataNormalizationMode(DataNormalizationMode.Raw)
self.maxBySymbol[symbol] = self.MAX(symbol, 5, Resolution.Daily)
self.SetBrokerageModel(BrokerageName.AlphaStreams)
#create empy dictionary for purchase price of equity
self.purchasedPrice = {}
self.running = False
#Runs strategy
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.Every(timedelta(hours=2)),
self.runAlg)
def runAlg(self):
self.running = True
def OnData(self, data):
if not self.running:
return
for symbol, max_indicator in self.maxBySymbol.items():
#gets first option contract
contract = self.GetContract(symbol)
#if there is no contract then continue to next symbol
if contract == None:
continue
fib100 = max_indicator.Current.Value
#BufferFib subtracts 30% from the max Fibonacci Retracement to reach a 70% retracement
bufferFib = self.Securities[symbol].Price * 0.3
#checks if equity price is below its maximum retracement. If equity price is above then strategy will automatically buy with no rational
if self.Securities[symbol].Price < fib100:
#if option premium is within a range between 130% and 70% of the Fibonacci Retracement continue
if (fib100+bufferFib) >= self.Securities[contract].BidPrice >= (fib100-bufferFib):
#If portfolio holds equity, move to next symbol
if self.Portfolio[symbol].Invested:
continue
#saves price to priceSec right before purchase
priceSec = self.Securities[symbol].Price
self.SetHoldings(symbol, self.totalPercentInv, False, "Set Holdings")
if self.Portfolio[symbol].Quantity == 0:
continue
self.LimitOrder(symbol, -(self.Portfolio[symbol].Quantity), round(round(priceSec, 2) * 1.03, 2))
#conducts an accurate rounding of priceSec to 2 decimal points and saves the purchase price to a dictionary
numDec = Decimal(str(priceSec))
self.purchasedPrice[symbol] = round(numDec, 2)
else:
#if portfolio is invested and Bid Price and Fibonacci Retracement cross over a second time, sell
if self.Portfolio[symbol].Invested:
numDec2 = Decimal(str(self.Securities[symbol].Price))
#ensure equity is not sold at the same price
if round(numDec2, 2) != self.purchasedPrice[symbol]:
self.Liquidate(symbol)
#stops alg
self.running = False
def GetContract(self, symbol):
#pulls contract data for select equity at current time
contracts = self.OptionChainProvider.GetOptionContractList(symbol, self.Time)
#selects the type of option to be Put contract
calls = [x for x in contracts if x.ID.OptionRight == OptionRight.Call]
if len(calls) == 0:
return None
#sorts contracts by closet expiring date date and closest strike price (sorts in ascending order)
calls = sorted(sorted(calls, key = lambda x: x.ID.Date),
key = lambda x: x.ID.StrikePrice)
#then selects all contracts that meet our expiration criteria
contract = calls[0]
#adds contract
self.AddOptionContract(contract, Resolution.Minute)
#return the call contract
return contract