Created with Highcharts 12.1.2Equity20152016201720182019202020212022202320242025600k3,000k-32000.0401011507501206000100M01,000k060
Overall Statistics
Total Orders
67
Average Win
8.55%
Average Loss
-2.79%
Compounding Annual Return
10.838%
Drawdown
25.100%
Expectancy
-0.137
Start Equity
1000000
End Equity
2784559.38
Net Profit
178.456%
Sharpe Ratio
0.584
Sortino Ratio
0.661
Probabilistic Sharpe Ratio
20.158%
Loss Rate
79%
Win Rate
21%
Profit-Loss Ratio
3.07
Alpha
0.013
Beta
0.538
Annual Standard Deviation
0.097
Annual Variance
0.009
Information Ratio
-0.271
Tracking Error
0.089
Treynor Ratio
0.106
Total Fees
$1905.75
Estimated Strategy Capacity
$68000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
0.06%
# region imports
from AlgorithmImports import *
from datetime import timedelta
# endregion

class HipsterOrangeCrocodile(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2015, 1, 1)
        self.set_cash(1000000)
        self.equity = self.add_equity("SPY", Resolution.Minute)
        self.equity.set_data_normalization_mode(DataNormalizationMode.Raw)
        self.symbol = self.equity.symbol

        self.set_benchmark("SPY")

        self.vix = self.add_index("VIX").symbol
        self.rank = 0
        self.contract = str()
        self.contracts_added = set() 
        self.days_before_exp = 2
        self.dte = 25
        self.otm = 0.01 
        self.lookback_iv = 150 
        self.iv_lvl = 0.5 
        self.percentage = 0.9 
        self.options_alloc = 90 

        self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.after_market_open(self.symbol, 30), self.plotting)
        self.schedule.on(self.date_rules.every_day(self.symbol), self.time_rules.after_market_open(self.symbol, 30), self.vix_rank)
        self.set_warm_up(timedelta(self.lookback_iv))

    def vix_rank(self):
        history = self.history(TradeBar, self.vix, self.lookback_iv, Resolution.Daily)
        self.rank = ((self.Securities[self.vix].Price - min(history["low"])) / (max(history["high"]) - min(history["low"])))
        

    def on_data(self, data: Slice):
        if self.is_warming_up:
            return

        if not self.portfolio[self.symbol].invested:
            self.set_holdings(self.symbol, self.percentage)
        
        if self.rank > self.iv_lvl:
            self.buy_put(data)
        
        if self.contract:
            if self.contract.ID.date - self.time <= timedelta(self.days_before_exp):
                self.liquidate(self.contract)
                self.log("Closed: too close to expiration")
                self.contract = str()
    
    def buy_put(self, data: Slice):
        if self.contract == str():
            self.contract = self.options_filter(data)
            self.log(f"Option filter done. contract={self.contract}")
            return
        elif not self.portfolio[self.contract].invested and data.contains_key(self.contract):
            qty = round(self.portfolio[self.symbol].quantity / self.options_alloc)
            self.log(f"Buying option contract {self.contract} - qty={qty}")
            self.buy(self.contract, qty)
        else:
            self.log(f"Got contract and get to buy option {self.contract}")

    def options_filter(self, data: Slice):
        contracts = self.option_chain_provider.get_option_contract_list(self.symbol, data.time)
        self.underlying_price = self.securities[self.symbol].price
        otm_puts = [i for i in contracts if i.ID.option_right == OptionRight.PUT 
                            and self.underlying_price - i.ID.strike_price > self.otm * self.underlying_price
                            and self.dte - 8 < (i.ID.date - data.time).days < self.dte + 8
                            ]
        if len(otm_puts) > 0:
            contract = sorted(
                            sorted(otm_puts, key = lambda x: abs((x.ID.date - self.time).days - self.dte)),
                            key= lambda x:self.underlying_price - x.ID.strike_price
                            )[0]
            if contract not in self.contracts_added:
                self.contracts_added.add(contract)
                self.add_option_contract(contract, Resolution.MINUTE)
            return contract
        return str()
    
    def plotting(self):
        self.plot("Vol Chart", "Rank", self.rank)
        self.plot("Vol Chart", "lvl", self.iv_lvl)
        self.plot("Data chart", self.symbol, self.securities[self.symbol].close)

        option_invested = [x.key for x in self.portfolio if x.value.invested and x.value.type == SecurityType.OPTION]
        if option_invested:
            self.plot("Data Chart", "Strike", option_invested[0].ID.strike_price)

    def on_order_event(self, order_event):
        self.log(str(order_event))