Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 635.027% Drawdown 13.700% Expectancy 0 Net Profit 7.363% Sharpe Ratio 6.603 Probabilistic Sharpe Ratio 61.700% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0.684 Annual Variance 0.468 Information Ratio 6.603 Tracking Error 0.684 Treynor Ratio 0 Total Fees $1.85 Estimated Strategy Capacity $0 Lowest Capacity Asset GC X5NNSA8F63Z1 |
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 = asset #-- 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.ShortEntry = None self.ShortExit = None self.LongEntry = None self.LongExit = None self.PriceIsUnderShort = False self.PriceIsOverLong = False @property def Symbol(self): return self.Security.Mapped @property def PriceIsOverShort(self): return not self.PriceIsUnderShort @property def PriceIsUnderLong(self): return not self.PriceIsOverLong