Overall Statistics
Total Trades
394
Average Win
14.11%
Average Loss
-4.79%
Compounding Annual Return
192.780%
Drawdown
96.000%
Expectancy
1.003
Net Profit
2980.860%
Sharpe Ratio
2.513
Sortino Ratio
4.023
Probabilistic Sharpe Ratio
66.355%
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
2.95
Alpha
2.785
Beta
1.898
Annual Standard Deviation
1.155
Annual Variance
1.335
Information Ratio
2.574
Tracking Error
1.104
Treynor Ratio
1.529
Total Fees
$221625.49
Estimated Strategy Capacity
$0
Lowest Capacity Asset
TSLA Y6URRGCB7VXI|TSLA UNU3P8Y3WFAD
Portfolio Turnover
2.82%
# region imports
from AlgorithmImports import *
# endregion

class VAlgorithm(QCAlgorithmFramework):

    def Initialize(self):
        self.SetStartDate(2020, 1, 1)
        self.SetEndDate(2023,3,11)
        self.SetCash(1000000)

        #set Tesla equity
        equity = self.AddEquity("TSLA",Resolution.Minute)
        equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.tesla = equity.Symbol

        option = self.AddOption("TSLA", Resolution.Minute)
        option.SetFilter(-3,3,timedelta(50), timedelta(70))
        self.call = None


    def OnData(self,data):

        option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type == SecurityType.Option]
        if option_invested:
             if self.Time + timedelta(30) > option_invested[0].ID.Date:
                self.Liquidate(option_invested[0],"too close to expiration")
                self.call = None

        else: #not invested in an option
            for i in data.OptionChains:
                chains = i.Value
                self.BuyCall(chains)
                self.Log(f"Call symbol : {self.call.Symbol}")
                self.HedgeDelta(chains)

        if self.Time.hour == 10 and self.Time.minute == 0:
            for i in data.OptionChains:
                chains = i.Value
                self.HedgeDelta(chains)


    def HedgeDelta(self,chains):
        # Get the option position
        if self.call:
            option_position = self.Portfolio[self.call.Symbol]

            if option_position is not None:
                # Calculate the delta of the option position
                # Extract the delta from the Greeks
               
                call = next((x for x in chains if x.Symbol == self.call.Symbol), None)

                if call:
                    self.call = call
                    # Calculate the delta of the option position using the updated data
                    option_delta = self.call.Greeks.Delta    
                    
                    # Calculate the quantity of the underlying asset needed to offset the delta
                    underlying_quantity = -option_delta * option_position.Quantity

                    underlying_price = self.Securities[self.tesla].Price

                    # Calculate the percentage of the portfolio value to set as holdings
                    portfolio_value = self.Portfolio.TotalPortfolioValue

                    target_percentage = (underlying_quantity * underlying_price) / portfolio_value

                    # Place an order to hedge the delta
                    self.SetHoldings(self.tesla, target_percentage)
        return

    def BuyCall(self,chains):
        #we buy calls that are near at the money 60 days till expiration
        expiry = sorted(chains, key = lambda x: abs((x.Expiry-self.Time).days-60))[0].Expiry#60 DTE
        calls = [i for i in chains if i.Right == OptionRight.Call and i.Expiry == expiry ]
        call_contracts = sorted(calls, key = lambda x: abs(x.Strike - x.UnderlyingLastPrice))
        if len(call_contracts) == 0:
            return

        self.call = call_contracts[0]
        quantity = self.Portfolio.TotalPortfolioValue / self.call.AskPrice
        quantity = int(quantity*0.4/100)
        self.Buy(self.call.Symbol, quantity)

        return