Overall Statistics
Total Trades
12
Average Win
65.52%
Average Loss
-9.67%
Compounding Annual Return
1273.637%
Drawdown
29.500%
Expectancy
2.889
Net Profit
205.784%
Sharpe Ratio
8.71
Probabilistic Sharpe Ratio
88.815%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
6.78
Alpha
0
Beta
0
Annual Standard Deviation
0.965
Annual Variance
0.931
Information Ratio
8.71
Tracking Error
0.965
Treynor Ratio
0
Total Fees
$16.65
Estimated Strategy Capacity
$19000000.00
Lowest Capacity Asset
GC X868WVVY7IQL
import math
from QuantConnect.Statistics import *
from QuantConnect.Python import PythonQuandl
import numpy as np


class Squirtle(QCAlgorithm):

    def Initialize(self):
        
        self.SetStartDate(2019, 5, 16)
        self.SetEndDate(2019, 10, 18)
        self.SetCash(10000)  
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        
        #-- Variables
        self.exitPeriod = 10
        self.entryPeriod = 20
        self.resolution = Resolution.Daily
        self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(Resolution.Daily), self.entryPeriod)
        
        self.Unit = 0
        self.N = 0
        
        self.maxUnits = 4           #-- Maximum units allowed
        self.currentUnits = 0       #-- Current number of units invested
        
        self.losingStreak = 0       #-- Losing streak
        self.winStreak = 0 
        
        self.monthlyLossPercent = 0  #-- Tracks weekly drawdown
        self.maxMonthlyLossPercent = 9
        
        self.newestTrade = 0        #-- Newest trades PNL
        
        #-- Futures to trade. For loop subs each continuous contract
        futuresSymbols = [Futures.Metals.Gold]
        
        self.Data = {}
        for symbol in futuresSymbols:
            future = self.AddFuture(symbol, 
                                    Resolution.Minute, 
                                    dataNormalizationMode = DataNormalizationMode.BackwardsRatio,
                                    dataMappingMode = DataMappingMode.FirstDayMonth,
                                    contractDepthOffset = 0)
        
        #--Scheduled Events
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday), self.TimeRules.At(12, 0), self.EveryFriAtNoon)
        self.Schedule.On(self.DateRules.MonthStart(), self.TimeRules.At(12, 0), self.ResetMonthlyDrawDown)
        
        #-- Debugging
        self.check = False
        self.x = 0
        
        #-- Live trading
        self.DefaultOrderProperties = InteractiveBrokersOrderProperties()
        self.DefaultOrderProperties.TimeInForce = TimeInForce.GoodTilCanceled
        self.DefaultOrderProperties.OutsideRegularTradingHours = False
        
        #-- Trade Builder
        tradeBuilder = TradeBuilder(FillGroupingMethod.FillToFill,FillMatchingMethod.FIFO)    
        self.SetTradeBuilder(tradeBuilder)

        self.SetWarmUp(120, Resolution.Daily)



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

        for symbol, symbolData in self.Data.items():
            
            price = self.Securities[symbolData.Symbol].Close
            
            security = self.Securities[symbolData.Symbol]
            
            breakoutUp = symbolData.LongEntry.Current.Value
            breakoutDown = symbolData.ShortEntry.Current.Value
            
            coverShort = symbolData.ShortExit.Current.Value
            sellLong = symbolData.LongExit.Current.Value
            
            
            if breakoutUp == 0.0 or breakoutDown == 0.0 or price == 0.0:
                return
            
            #-- Maps continuous contract to the current contract to trade
            asset = self.Securities[symbolData.Symbol]
            currentContract = self.Securities[asset.Mapped]
                
                
                
                
                
            #-- Trade Logic    
            
            #-- If not invested in current symbol
            if not self.Portfolio[currentContract.Symbol].Invested:
                
                #-- Makes sure no Trade Flags are set
                if not self.FlagTriggered(currentContract.Symbol, price):
                
                    if price > breakoutUp:
                        self.MarketOrder(currentContract.Symbol, 1)
                        #self.Debug(f"Buy Long: {currentContract.Symbol}")
                        
                    elif price < breakoutDown:
                        self.MarketOrder(currentContract.Symbol, -1)
                        #self.Debug(f"Sell Short: {currentContract.Symbol}")
                #else:
                    #self.Debug("Flag Triggered")
                
            
            #-- If invested in current symbol
            elif self.Portfolio[currentContract.Symbol].Invested:
                
                #if self.Portfolio[currentContract.Symbol].UnrealizedProfitPercent < -0.02:
                    #self.Debug(f"Currently Invested: {self.Portfolio[currentContract.Symbol].UnrealizedProfitPercent}")
                
                if self.Portfolio[currentContract.Symbol].UnrealizedProfitPercent < -0.02:
                    
                    self.Debug(f"Stop Hit: {currentContract.Symbol}, PnL: {self.Portfolio[currentContract.Symbol].UnrealizedProfitPercent}")
                    self.Liquidate(currentContract.Symbol)
                
                
                #-- If Long current symbol
                if self.Portfolio[currentContract.Symbol].IsLong:
                    if price < sellLong:
                        self.Liquidate(currentContract.Symbol)
                        #self.Debug(f"Sell Long: {currentContract.Symbol}")
                
                #-- If Short current symbol
                elif self.Portfolio[currentContract.Symbol].IsShort:
                    if price > coverShort:
                        self.Liquidate(currentContract.Symbol)
                        #self.Debug(f"Cover Short: {currentContract.Symbol}")
                        
                #-- If unrealized profit percent goes below risk target
                
                
                    
                    
                    
    #-- Risk Management Methods
    
    #-- A Flag is anything that should prevent a trade. Size limit hit, risk tolerance hit, etc... 
    def FlagTriggered(self, symbol, price):
        
        if self.MaxExposureFlag(symbol):
            return True
        if self.MonthlyDrawDownFlag():
            return True
        
        #-- If no flags are set, return false
        else: 
            return False
        
        
        
        
    #-- Max position size on any one asset/market
    def MaxExposureFlag(self, symbol):
        
        maxMargin = .45 * self.Portfolio.Cash
        
        #-- Checks if quantity of current symbol is below limit
        if self.Portfolio[symbol].Quantity < 1:
            return False
        elif self.Portfolio.TotalMarginUsed < maxMargin:
            return False
        else:
            return True
    
    #-- Events to happen every friday at noon
    def EveryFriAtNoon(self):
        
        #self.Debug(f"Fri at 12pm: {self.Time}")
        pass
    
    #-- Checks to see if weekly risk is hit
    def MonthlyDrawDownFlag(self):
        
        if self.monthlyLossPercent >= self.maxMonthlyLossPercent:
            return True
        
        else:
            return False
            
    def MonthlyDrawDown(self, newestPNL):
        
        #-- Converts dollar pnl into whole number percentage 
        pnlPercent = (abs(newestPNL) / self.Portfolio.Cash) * 100
        
        self.monthlyLossPercent += pnlPercent
    
    def ResetMonthlyDrawDown(self):
        
        self.Debug(f"This Months Drawdown: {self.monthlyLossPercent}%")
        self.monthlyLossPercent = 0.0
        
    
    #-- Gets Trade size
    def getSize(self, price):
        
        
        #-- Risk Management Targets for Size
        #-- Taking Unit sizing with 30% of N
        #-- Using N number of ticks for stop, no physical stop placed
        #--
        #-- ~10 portfolio exposure on each trade
        #-- ~1% risk target per trade
        
        
        if self.atr.Current.Value is not None:
            #-- 20 day ATR of asset
            
            unroundedUnit = 0
            
            #-- 30% of N, truncated to two decimal places
            self.N = self.atr.Current.Value
            
            #-- Dollar Volatility equals ATR (N) times Dollars per point
            self.dollarVol = self.atr.Current.Value * self.tickPrice
            
            #-- Unit size at full capacity
            if self.losingStreak < 2:
                unroundedUnit = (self.Portfolio.Cash * 0.01) / self.dollarVol
                
            #-- If losing Streak hits 2, reduce size by 25%    
            elif self.losingStreak == 2:
                unroundedUnit = (self.Portfolio.Cash * 0.01) / self.dollarVol
                unroundedUnit = unroundedUnit *.75
                
            #-- If losing Streak hits 3, reduce size by 50%    
            elif self.losingStreak == 3:
                unroundedUnit = (self.Portfolio.Cash * 0.01) / self.dollarVol
                unroundedUnit = unroundedUnit *.5
                
            #-- If losing Streak hits 4, reduce size by 75%    
            elif self.losingStreak == 4:
                unroundedUnit = (self.Portfolio.Cash * 0.01) / self.dollarVol
                unroundedUnit = unroundedUnit *.25
            
            self.Unit = math.floor(unroundedUnit)
            
            
            
            if self.Unit > 1:    
                return self.Unit
            else:
                return 1
                
    def getStop(self):
        
        #-- Risk management targets for stops
        #-- Entry Price - N number of ticks
        
        self.stopPrice = self.buyPrice - (self.N * self.tickSize)
        
        #self.Debug(f"------------------------------")
        return self.stopPrice
        
    #----
    
    
      
    def OnOrderEvent(self, orderevent):
        
        # check if orderevent is a fill
        if orderevent.Status == OrderStatus.Filled:
            
            
            
            symbol = orderevent.Symbol
            self.Debug(f"Order Filled: {symbol}, Time: {self.Time}")
            
            fillPrice = orderevent.FillPrice
            
            #-- Creates trade list to track P/L's for risk management
            tradeList = []
            for trade in self.TradeBuilder.ClosedTrades:
                tradeList.append(trade.ProfitLoss)
            
            #-- Showing in console previous trades P/L   
            #self.Debug(f" ")
            
            if tradeList:
                
                #-- Checks Weekly risk if Order Filled was to close a position 
                #-- by checking if that symbol is still in the portfolio
                if not self.Portfolio[trade.Symbol].Invested:
                    
                    #-- most recent trade PnL
                    self.newestTrade = int(tradeList[-1])
                    
                    #-- Add PnL if newest closed trade was a loss
                    if self.newestTrade < 0:
                        self.MonthlyDrawDown(self.newestTrade)
                        self.Debug(f"Loss: {self.newestTrade}, Time: {self.Time}")
                    
                    
                    '''
                    #-- Print stats for trade and current targets in console
                    #self.Debug(f" ")
                    self.Debug(f"Entry Date: {trade.EntryTime}, Exit Date: {trade.ExitTime}")
                    self.Debug(f"Profit/Loss: {self.newestTrade}, Unit Size: {self.Unit}, N: {self.N}")
                    self.Debug(f"Losing Streak: {self.losingStreak}, Weekly Loss %: {self.weeklyLossPercent}")
                    self.Debug("---------------------------")
                    '''
                    
                #-- If the symbol for last Order Filled is still in the Portfolio, print opened trade
                else:
                    self.Debug(f"Entered Position In {trade.Symbol}")
            else:
                self.Debug("No Closed Trades Yet")
            
    
                 
    def OnSecuritiesChanged(self, changes):
        
            for added in changes.AddedSecurities:
                symbolData = self.Data.get(added.Symbol)
                if symbolData is None:
                    
                    #-- Create high and low indicators
                    symbolData = SymbolData(added)
                    
                    #-- Creating Indicators
                    symbolData.ShortEntry = self.MIN(added.Symbol, self.entryPeriod, Resolution.Daily)
                    symbolData.ShortExit = self.MAX(added.Symbol, self.exitPeriod, Resolution.Daily)
                    symbolData.LongEntry = self.MAX(added.Symbol, self.entryPeriod, Resolution.Daily)
                    symbolData.LongExit = self.MIN(added.Symbol, self.exitPeriod, Resolution.Daily)
                    
                    '''
                    #-- Registering Indicators
                    self.RegisterIndicator(added.Symbol, symbolData.ShortEntry, timedelta(days = self.entryPeriod))
                    self.RegisterIndicator(added.Symbol, symbolData.ShortExit, timedelta(days = self.exitPeriod))
                    self.RegisterIndicator(added.Symbol, symbolData.LongEntry, timedelta(days = self.entryPeriod))
                    self.RegisterIndicator(added.Symbol, symbolData.LongExit, timedelta(days = self.exitPeriod))
                    '''
                    self.Data[added.Symbol] = symbolData
                #else:
                    #-- A security that was already initialized was re-added, reset the indicators
                    
                    symbolData.ShortEntry.Reset()
                    symbolData.ShortExit.Reset()
                    symbolData.LongEntry.Reset()
                    symbolData.LongExit.Reset()
                    
    
   
class SymbolData:
    
    def __init__(self, security):
        self.Security = security
        self.Symbol = security.Symbol

        self.ShortEntry = None
        self.ShortExit = None
        self.LongEntry = None
        self.LongExit = None

        self.PriceIsUnderShort = False
        self.PriceIsOverLong = False
        
        

    @property
    def PriceIsOverShort(self):
        return not self.PriceIsUnderShort
        
    @property
    def PriceIsUnderLong(self):
        return not self.PriceIsOverLong