Overall Statistics
Total Trades
18
Average Win
15.11%
Average Loss
0%
Compounding Annual Return
130.090%
Drawdown
41.100%
Expectancy
0
Net Profit
249.914%
Sharpe Ratio
1.882
Probabilistic Sharpe Ratio
64.976%
Loss Rate
0%
Win Rate
100%
Profit-Loss Ratio
0
Alpha
-0.15
Beta
0.706
Annual Standard Deviation
0.601
Annual Variance
0.361
Information Ratio
-1.75
Tracking Error
0.39
Treynor Ratio
1.602
Total Fees
$3998.92
Estimated Strategy Capacity
$33000000.00
Lowest Capacity Asset
SOXL UKTSIYPJHFMT
# Purpose: SIMPLIFIED ALGORITH FOR SUPPORT PURPOSES

class BuyLowSellHighDrawdowns(QCAlgorithm):
    
    # SOXL splits: 05/20/2015    4 for 1 | 03/02/2021    15 for 1
    
    def Initialize(self): # PREPARATORY ITEMS BEFORE SIMULATION
        self.SetStartDate(2020, 7, 1) # Ignored in live (will cover split date)
        self.SetEndDate(2022, 1, 1) # Ignored in live
        #self.DURATION = self.SetEndDate(2021, 12, 7) - self.SetStartDate(2020, 12, 7) # Doesn't work yet
        self.DURATION = "LIVE"
        self.INVESTMENT_AMOUNT = 1000000
        self.SetCash(self.INVESTMENT_AMOUNT)   # IB uses default of $1,000,000 for paper trading.
        self.debug = False # This is the manual method to do safe logging only when live to avoid huge logs.
        
        #STOCKS TO TRADE
        
        self.STOCK_ONE = "SOXL"
        
        # BUY THRESHOLDS
        self.BUY_THRESHOLD_ONE = -10            # Percent below HH (Drawdown) to buy at initially 
        
        # BUY QUANTITY THRESHOLDS
        self.BUY_QUANTITY_PERC_ONE = 1.0         # Percent of cash to use in first buy event

        # SELL THRESHOLDS
        self.SELL_THRESHOLD_ONE = -1            # Percent below HH (Drawdown) to sell at, currently only supports one 
        
        
        # OTHER VARIABLES
        self.CYCLE_LIMIT = 1                    # Track number of sequential buys and limit it (includes zero)
        self.SPLIT_DIFF = 1.0                   # Track the split differential for us in calculating/adjusting drawdown (52-wk-high/HH)
        self.Settings.FreePortfolioValuePercentage = 0.03 # Amount of portfolio to keep in cash for operating expenses
        self.BUY_CYCLE_CNT = 0                  # Track the number of buy cycles
        self.BUY_SELL_CYCLE_CNT = 0             # Track the number of full buy-sell cycles (closed investments)
        self.SPENT = 0                          # Local variable to track how much is spent during each buy
        self.EARNED = 0                         # Local variable to track how much is earned (gross) in each sell
        self.STOCK_PRICE = 0                    # Adjusted stock price (used by default)
        self.ORIG_STOCK_PRICE = 0               # Original (raw) stock price (used for bank transactions)
        self.MANUAL_HH = 0                      # Since QC doesn't support slit-adjusted HH, track this and adjust after splits
        self.SPLIT_OCCURRED = 0                 # Track if a split happened
        self.CURRENT_DRAWDOWN = 0
        
        
        # GET PORTFOLIO DETAILS
        self.Portfolio.Invested                 # Hold at least one stock
        self.Portfolio.Cash                     # Sum of all currencies in account (only settled cash)
        self.Portfolio.UnsettledCash            # Sum of all currencies in account (only unsettled cash)
        self.Portfolio.TotalFees                # Fees incurred since backtest start
        self.Portfolio.TotalHoldingsValue       # Absolute sum portfolio items
        self.Portfolio.MarginRemaining          # Remaining margin on the account
        self.Portfolio.TotalMarginUsed          # Sum of margin used across all securities
        self.Portfolio.TotalPortfolioValue      # Portfolio equity
        self.Portfolio.TotalProfit              # Sum of all gross profit
        self.Portfolio.TotalUnrealizedProfit    # Holdings profit/loss
        
        # PREP THE STOCK
        self.CURRENT_STOCK = self.AddEquity(self.STOCK_ONE, Resolution.Daily)                      # Day (good for debugging)
        self.CURRENT_STOCK.SetDataNormalizationMode(DataNormalizationMode.Adjusted)                 # Use adjusted data
        
        self.HISTORICAL_HIGH = self.MAX(self.STOCK_ONE, 253, Resolution.Daily, Field.High)          # Day: Get 52-wk-high (adjusted, raw)
        self.HH_DAY = self.MAX(self.STOCK_ONE, 1, Resolution.Daily, Field.High)                     # Day: Trying to get the high price from the last day
        
        self.SetBenchmark(self.STOCK_ONE)  
        self.SetWarmUp(timedelta(days=253))
        
        # DAILY LOG REPORT - hour after market open
        self.Schedule.On(self.DateRules.EveryDay(self.STOCK_ONE), self.TimeRules.AfterMarketOpen(self.STOCK_ONE, 60), self.EveryDayAfterMarketOpen)
            
        
    def OnData(self, data): # PROCESSES REPEATEDLY THROUGHOUT SIMULATION
        # Don't place trades until our indicators are warmed up (note this delays the alg from starting trading for 4 months due to warmup time.)
        if not self.HISTORICAL_HIGH.IsReady:
            self.Log("I failed to run today because HH is not ready yet.")
            return
        #if self.IsWarmingUp:
        #    return
    
        # Local variables for the sim run
        self.STOCK_PRICE = self.Securities[self.STOCK_ONE].Price            # Get current price (should auto-adjust to splits)
        self.STOCK_PRICE_HIGH = self.HH_DAY.Current.Value                   # Get HH for today
        self.MANUAL_HH = self.HISTORICAL_HIGH.Current.Value                                         # Update the historical high in case a split occurred today
        
        #INITIAL VARS
        self.CASH_REMAINING = self.Portfolio.Cash                           # Get cash remaining
        self.STOCK_POSITION = self.Portfolio[self.STOCK_ONE].Quantity       # Get number of shares currently held for this stock
        self.Time                                                           # Current simulation time
        
        # Determine if there was a split
        """if data.Splits.ContainsKey(self.STOCK_ONE):
            ## Log split information
            stockSplit = data.Splits[self.STOCK_ONE] 
            if stockSplit.Type == 0:
                self.Log('Stock will split next trading day')
            if stockSplit.Type == 1:
                self.SPLIT_DIFF = stockSplit.SplitFactor                    # Save the new split differential if one occurred
                self.SPLIT_OCCURRED += 1                                    # Record that a split has occurred
                # Adjust the previous HH by the split diff and use that as the new HH
                self.MANUAL_HH = self.MANUAL_HH * self.SPLIT_DIFF           # Adjust the HH based on the new split (this is manual way to handle it)
                self.Log("Split type: {0}, Split factor: {1}, Reference price: {2}".format(stockSplit.Type, stockSplit.SplitFactor, stockSplit.ReferencePrice))
        """
        # Get current drawdown amount in the form of a negative full percent
        if (self.MANUAL_HH == 0): # During the beginning of the simulation there is no value set for this yet
            self.CURRENT_DRAWDOWN = 0
        else:
            self.CURRENT_DRAWDOWN = -100 * (self.MANUAL_HH - self.STOCK_PRICE)/self.MANUAL_HH
        
        
        # TRADING BEHAVIOR
       
        #--BUY #1: If stock price dips below Drawdown Buy Threshold 1, and more than X shares being purchased, then buy
        if (self.CURRENT_DRAWDOWN < self.BUY_THRESHOLD_ONE) and (self.BUY_CYCLE_CNT == 0):
            # DO THE BUY
            self.SetHoldings(self.STOCK_ONE, self.BUY_QUANTITY_PERC_ONE) # Buy the stock, according to the assigned percentage
            
            #self.SPENT = self.NUM_SHARES_CAN_PURCHASE * self.STOCK_PRICE
            self.Log(f"BUY #1: Cycle: {self.BUY_SELL_CYCLE_CNT} | {self.CURRENT_STOCK} at: ${self.STOCK_PRICE:.2f} use {self.BUY_QUANTITY_PERC_ONE}% of ${self.CASH_REMAINING} | DD: {self.CURRENT_DRAWDOWN:.1f}% < {self.BUY_THRESHOLD_ONE:.1f}%")
            
            self.BUY_CYCLE_CNT += 1 #Start or encrement buy cycle
            
            # --PRINT BUY DIAGNOSTICS
            self.STOCK_POSITION = self.Portfolio[self.STOCK_ONE].Quantity
        
        #--SELL: If stock price goes above Drawdown Sell Threshold, and position > 0,  liquidate holdings
        elif (self.CURRENT_DRAWDOWN > self.SELL_THRESHOLD_ONE) and (self.STOCK_POSITION > 0):
            # DO THE SELL
            self.Liquidate(self.STOCK_ONE) # Sell all holdings of this ticker
            
            #self.EARNED = self.STOCK_POSITION * self.STOCK_PRICE
            self.Log(f"SELL: Cycle: {self.BUY_SELL_CYCLE_CNT} | {self.STOCK_POSITION} {self.CURRENT_STOCK} at: ${self.STOCK_PRICE:.2f} | DD: C:{self.CURRENT_DRAWDOWN:.1f}% < {self.SELL_THRESHOLD_ONE:.1f}%")
            
            self.BUY_CYCLE_CNT = 0 # Reset buy cycle
            self.BUY_SELL_CYCLE_CNT += 1 # Count number of buy-sell cycles (investments)
            
            # --PRINT SELL DIAGNOSTICS
            self.STOCK_POSITION = self.Portfolio[self.STOCK_ONE].Quantity
            

    # DEBUG: Can use this to only produce error messages when doing paper/live trading    
    def SafeLog(self, message):
        if self.LiveMode or self.debug:
            self.Log(message)
            
    def EveryDayAfterMarketOpen(self):
        #if self.HISTORICAL_HIGH.IsReady:
        self.Log(f"-----{self.STOCK_ONE} D: {self.DURATION} | ADJ_PRICE: ${self.STOCK_PRICE:.2f} | HH: ${self.HISTORICAL_HIGH.Current.Value:.2f} | DD: {self.CURRENT_DRAWDOWN:.1f}% | CASH: ${self.Portfolio.Cash:.2f} | PROFIT: ${self.Portfolio.TotalProfit:.2f} ** B1: {self.BUY_THRESHOLD_ONE}% (Using: {self.BUY_QUANTITY_PERC_ONE*100}%) | SELL: {self.SELL_THRESHOLD_ONE}%") 
            
    def OnOrderEvent(self, orderEvent):
        self.Log("{} {}".format(self.Time, orderEvent.ToString()))

    def OnEndOfAlgorithm(self):
        self.PROFIT_PERC = (self.Portfolio.TotalProfit / self.INVESTMENT_AMOUNT) * 100
        self.Log(f"__END SUMMARY_______________________________________________________")
        self.Log(f"PROFIT: ${self.Portfolio.TotalProfit:.2f} | PROFIT: {self.PROFIT_PERC:.1f}% | COMPLETED CYCLES: {self.BUY_SELL_CYCLE_CNT} | CASH: ${self.Portfolio.Cash:.2f}") 
        self.Log(f"B1: {self.BUY_THRESHOLD_ONE}% (Using: {self.BUY_QUANTITY_PERC_ONE*100}%) | SELL: {self.SELL_THRESHOLD_ONE}%") 
            
        self.Log("{} - TotalPortfolioValue: {}".format(self.Time, self.Portfolio.TotalPortfolioValue))
        self.Log("{} - CashBook: {}".format(self.Time, self.Portfolio.CashBook))