| 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