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.")