Overall Statistics
Total Trades
2
Average Win
17.20%
Average Loss
0%
Compounding Annual Return
2.024%
Drawdown
4.600%
Expectancy
0
Net Profit
17.190%
Sharpe Ratio
0.451
Probabilistic Sharpe Ratio
1.329%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0.032
Annual Variance
0.001
Information Ratio
0.451
Tracking Error
0.032
Treynor Ratio
0
Total Fees
$10.00
Estimated Strategy Capacity
$5000.00
Lowest Capacity Asset
UVXY 2ZWLGYQK5YDEU|UVXY V0H08FY38ZFP
# region imports
from AlgorithmImports import *
from scipy import stats
# endregion

class WellDressedTanLemur(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)  # Set Start Date
        self.SetEndDate(datetime.now())
        self.SetCash(100000)  # Set Strategy Cash
        self.SetWarmup(timedelta(50))
        self.equity = self.AddEquity("UVXY", Resolution.Hour)
        self.VIX = self.AddIndex("VIX", Resolution.Hour).Symbol
        self.symbol = self.equity.Symbol
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.contractsAdded = set()

        self.percentileThreshold = 50
        self.lookback = 50
        self.DTEput = 20
        self.DaysBeforeExp = 3
        self.putcontract = str()
        self.current_VIX_percentile = 0

        self.VIXwindow = RollingWindow[Tick](self.lookback)

    def OnData(self, data):
        if(self.IsWarmingUp):
            return

            self.VIXwindow.Add(data["VIX"])


            current_VIX = self.Securities[self.VIX].Price
            self.current_VIX_percentile = stats.percentileofscore(self.VIXwindow, current_VIX)
            self.Log(self.current_VIX_percentile)

            option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type==SecurityType.Option]
            for option in option_invested:

                if option.IsShort:
                    if (self.putcontract.ID.Date - self.Time) <= timedelta(self.DaysBeforeExp):
                        self.Liquidate(self.putcontract)
                        self.Log("Closed: too close to expiration")
                        self.putcontract = str()

                    if self.Portfolio.UnrealizedProfitPercent < -1:
                        self.Liquidate(self.putcontract)
                        self.putcontract = str()

                    if self.Portfolio.UnrealizedProfitPercent >= 0.75:
                        self.Liquidate(self.putcontract)
                        self.putcontract = str()
                

        if self.current_VIX_percentile < self.percentileThreshold:
            self.SellPut(data)

    def SellPut(self, data):
        if self.putcontract == str():
            self.putcontract = self.OptionsFilter(data)
            return

        elif not self.Portfolio[self.putcontract].Invested and data.ContainsKey(self.putcontract):
            self.underlyingPrice = self.Securities[self.symbol].Price
            positionSize = self.Portfolio.Cash / (self.putcontract.ID.StrikePrice * 100)
            self.Sell(self.putcontract, positionSize)
            # Allocation sizing goes here


    def OptionsFilter(self, data):
        contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, data.Time)
        self.underlyingPrice = self.Securities[self.symbol].Price

        # self.underlyingPrice can later be replaced with projected UVXY price
        otm_puts = [i for i in contracts if i.ID.OptionRight == OptionRight.Put and i.ID.StrikePrice < self.underlyingPrice]

        if len(otm_puts) > 0:
            putcontract = sorted(sorted(otm_puts, key = lambda x: abs((x.ID.Date - self.Time).days - self.DTEput)), key = lambda x: self.underlyingPrice - x.ID.StrikePrice)[0]
            if putcontract not in self.contractsAdded:
                self.contractsAdded.add(putcontract)
                self.AddOptionContract(putcontract, Resolution.Hour)
            return putcontract
        else:
            return str()

    def OnOrderEvent(self, orderEvent):
        # log order events
        self.Log(str(orderEvent))
        

                # QC appears to support daily resolution for options now