| Overall Statistics |
|
Total Orders 5 Average Win 1627.50% Average Loss -45.61% Compounding Annual Return 73.108% Drawdown 86.000% Expectancy 23.453 Start Equity 100000 End Equity 519521.87 Net Profit 419.522% Sharpe Ratio 1.209 Sortino Ratio 1.171 Probabilistic Sharpe Ratio 42.852% Loss Rate 33% Win Rate 67% Profit-Loss Ratio 35.68 Alpha 0.578 Beta 1.98 Annual Standard Deviation 0.634 Annual Variance 0.402 Information Ratio 1.234 Tracking Error 0.544 Treynor Ratio 0.387 Total Fees $202.13 Estimated Strategy Capacity $58000000.00 Lowest Capacity Asset QQQ RIWIV7K5Z9LX Portfolio Turnover 2.67% |
from AlgorithmImports import *
import datetime
class TrailingStopLossOptions(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2018, 1, 1)
self.SetEndDate(2021, 1, 1)
self.SetCash(100000)
self.underlying = self.AddEquity("QQQ", Resolution.Hour).Symbol
self.options = self.AddOption("QQQ", Resolution.Hour)
self.options.SetFilter(-2, 2, timedelta(days=30), timedelta(days=90))
self.contract = None
self.expiration_threshold = timedelta(days=30)
self.entryTicket = None
self.stopMarketTicket = None
self.entryTime = datetime.datetime.min
self.stopMarketOrderFillTime = datetime.datetime.min
self.highestPrice = 0
def OnData(self, slice):
if not slice.OptionChains.ContainsKey(self.options.Symbol):
return
chain = slice.OptionChains[self.options.Symbol]
if not chain:
return
if (self.Time - self.stopMarketOrderFillTime).days < 30:
return
if not self.contract or self.contract.Expiry - self.Time < self.expiration_threshold:
self.contract = self.SelectOptionContract(chain)
if not self.contract:
return
option_contract = chain.Contracts.get(self.contract.Symbol)
if not option_contract:
return
option_price = option_contract.LastPrice
self.Debug(f"Option Price: {option_price}")
if not self.Portfolio.Invested and not self.Transactions.GetOpenOrders(self.contract.Symbol):
quantity = self.CalculateOrderQuantity(self.contract.Symbol, 0.9)
self.entryTicket = self.MarketOrder(self.contract.Symbol, quantity)
self.entryTime = self.Time
self.Debug(f"Entered position: {self.contract.Symbol}, Quantity: {quantity}")
if self.stopMarketTicket is not None and self.Portfolio.Invested:
if option_price > self.highestPrice:
self.highestPrice = option_price
updateFields = UpdateOrderFields()
updateFields.StopPrice = option_price * 0.95
self.stopMarketTicket.Update(updateFields)
self.Debug(f"Updated Stop Price to: {updateFields.StopPrice}")
def SelectOptionContract(self, chain):
contracts = [
contract for contract in chain
if contract.Right == OptionRight.Call
and 0.5 <= contract.Greeks.Delta <= 0.8
and contract.Expiry > self.Time + timedelta(days=30)
and contract.Expiry < self.Time + timedelta(days=90)
]
if not contracts:
return None
return min(contracts, key=lambda x: abs(x.Greeks.Delta - 0.7))
def OnOrderEvent(self, orderEvent):
self.Debug(f"Order Event: {orderEvent}")
if orderEvent.Status != OrderStatus.Filled:
return
if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId:
self.highestPrice = orderEvent.FillPrice
self.stopMarketTicket = self.StopMarketOrder(self.contract.Symbol, -self.entryTicket.Quantity, 0.95 * self.highestPrice)
self.Debug(f"Placed Stop Market Order for: {self.contract.Symbol}")
if self.stopMarketTicket is not None and self.stopMarketTicket.OrderId == orderEvent.OrderId:
self.stopMarketOrderFillTime = self.Time
self.highestPrice = 0
self.Debug("Stop market order filled.")