| Overall Statistics |
|
Total Trades 261 Average Win 0.80% Average Loss -0.80% Compounding Annual Return -57.854% Drawdown 48.600% Expectancy -0.156 Net Profit -19.188% Sharpe Ratio -0.102 Probabilistic Sharpe Ratio 25.655% Loss Rate 58% Win Rate 42% Profit-Loss Ratio 1.00 Alpha 0 Beta 0 Annual Standard Deviation 0.982 Annual Variance 0.964 Information Ratio -0.102 Tracking Error 0.982 Treynor Ratio 0 Total Fees $2330.49 Estimated Strategy Capacity $150000.00 Lowest Capacity Asset XLMUSDT 18N |
# region imports
from AlgorithmImports import *
from io import StringIO
import pandas as pd
# endregion
class CryptoStrategy01(QCAlgorithm):
def Initialize(self):
#self.SetTimeZone("America/New_York")
self.SetTimeZone("UTC")
self.SetStartDate(2020, 1, 1) # Set Start Date
self.SetEndDate(2020, 3, 30) #Set End Date
self.SetAccountCurrency("USDT")
self.SetCash(10000) # Set Strategy Cash
self.bias = ''
self.rands = {}
self.tickers = []
self.break_percentage = 0.50
self.tpMultiplier = 1.5
self.slMultiplier = 1
self.timeframe = Resolution.Hour
self.SetSecurityInitializer(self.CustomSecurityInitializer)
self.UniverseSettings.Resolution = self.timeframe
self.AddUniverseSelection(ScheduledUniverseSelectionModel(self.DateRules.EveryDay(), self.TimeRules.At(0, 0) ,self.CoarseSelectionFunction))
#self.benchmark = self.AddCrypto("BTCUSDT", self.timeframe, Market.Binance).Symbol
#self.SetBenchmark("BTCUSDT")
self.invested = [] #list of on going invseted coins
self.historyInvested = [] #list of the coins we invested within the day to avoid re-entry
self.indicators = {} # Set dictionary of indicators
self.doneWarmingUp = []
self.marginModel = 0
# start of self-defined functions
def WarmUp(self, symbol):
# customize margin model to allow shorting in crypto Binance
if self.marginModel < len(self.ActiveSecurities.Values):
symbol.BuyingPowerModel = SecurityMarginModel(3.3)
self.marginModel += 1
# Either create a new indicator, or update one we already have
if symbol not in self.indicators:
self.indicators[symbol] = SymbolData(symbol)
history = self.History([symbol.Symbol], 14, self.timeframe)
for index, row in history.loc[symbol.Symbol].iterrows():
tradebar = TradeBar(index, symbol.Symbol, row["open"], row["high"], row["low"], row["close"], row["volume"], timedelta(1))
self.indicators[symbol].update(index, row["close"], tradebar)
self.doneWarmingUp.append(str(symbol))
self.Log(str(symbol) + " : Done warming up...")
def CustomSecurityInitializer(self, security):
'''Initialize the security with raw prices'''
security.SetDataNormalizationMode(DataNormalizationMode.Raw)
def CoarseSelectionFunction(self, coarse):
### EXIT CONDITION ### when tp or sl is not triggered by the end of day
if not len(self.invested) == 0: #checks if the list in not empty
#self.Log(self.invested)
for symbol in self.ActiveSecurities.Values:
Close = symbol.Close #recent bar closing price
if str(symbol) in self.invested:
if self.Portfolio[symbol.Symbol].IsLong:
self.Liquidate(symbol.Symbol, "EOD Long Exit " + str(symbol) + " @ " + str(Close))
self.Log("EOD Long Exit " + str(symbol) + " @ " + str(Close))
self.invested.remove(str(symbol))
elif self.Portfolio[symbol.Symbol].IsShort:
self.Liquidate(symbol.Symbol, "EOD Short Exit " + str(symbol) + " @ " + str(Close))
self.Log("EOD Short Exit " + str(symbol) + " @ " + str(Close))
self.invested.remove(str(symbol))
#self.Log(self.invested)
### END OF EXIT CONDITION ###
self.doneWarmingUp = [] # reset list of symbols warm-up everyday
self.marginModel = 0 # reset marginModel everyday
self.invested = [] #reset list of ongoing investment
self.historyInvested = [] #reset history of invested coins everyday
self.tickers = []
self.rands = {}
symbols = []
csv = self.Download("https://www.dropbox.com/s/97qbgx9x3janqh6/rands.csv?dl=1")
df = pd.read_csv(StringIO(csv))
try:
df1= df.loc[df['datetime']==str(self.Time - timedelta(1))]
symbolsToday = df1["symbol"]
self.bias = df1['bias'].iloc[0]
for ticker in symbolsToday:
x = ticker.upper() + "USDT"
self.tickers.append(x)
resistance = df1.loc[df1['symbol']==ticker, 'resistance']
resistance = resistance.values
support = df1.loc[df1['symbol']==ticker, 'support']
support = support.values
self.rands[x + " 18N"] = {'resistance':resistance[0] , 'support':support[0]}
tickers = self.tickers
for x in tickers:
symbols.append(Symbol.Create(x, SecurityType.Crypto, Market.Binance))
return symbols
except:
self.Log("Error in getting symbols...Date not found.")
# end of self-defined functions
def OnData(self, data):
self.Log(len(self.ActiveSecurities))
self.Log(self.rands)
#self.Log(self.bias)
for symbol in self.ActiveSecurities.Values:
try:
if str(symbol) not in self.doneWarmingUp:
self.WarmUp(symbol)
self.Log(self.doneWarmingUp)
# Either create a new indicator, or update one we already have
if symbol not in self.indicators:
self.indicators[symbol] = SymbolData(symbol)
tradebar = TradeBar(self.Time, symbol.Symbol, symbol.Open, symbol.High, symbol.Low, symbol.Close, symbol.Volume, timedelta(1))
self.indicators[symbol].update(self.Time, symbol.Close, tradebar)
#check if indicators are ready
if not self.indicators[symbol].atrWin.IsReady:
return
# end of adding symbol to indicators dictionary
Close = symbol.Close #recent bar closing price
Open = symbol.Open #recent bar opening price
High = symbol.High #recent bar highest price
Low = symbol.Low #recent bar lowest price
resistance = self.rands[str(symbol)]['resistance'] #previous day resistance
support = self.rands[str(symbol)]['support'] #previous day support
atr = self.indicators[symbol].atrWin[0].Value #recent bar atr value
self.Log(str(symbol) + " Close:" + str(Close) + " Open:" + str(Open) + " High:" + str(High) + " Low:" + str(Low))
self.Log("Resistance:" + str(resistance) + " Support:" + str(support))
#self.Log("ATR :" + str(atr))
balance = self.Portfolio.MarginRemaining / 3 #scaling position size
quantity = (balance * 0.9) /Close
### ENTRY CONDITIONS ###
#"""
if (str(symbol) not in self.invested) and (str(symbol) not in self.historyInvested):
if self.bias == "long":
cl = Open - Close
bl = support - Close
if bl > 0 and cl > 0:
candle_points = bl/cl
if candle_points > self.break_percentage:
self.Buy(symbol.Symbol, quantity)
self.indicators[symbol].EntryPrice = Close
self.indicators[symbol].TP = (atr * self.tpMultiplier) + self.indicators[symbol].EntryPrice
self.indicators[symbol].SL = self.indicators[symbol].EntryPrice - (atr * self.slMultiplier)
self.Log("BUY " + str(symbol) + " @ " + str(self.indicators[symbol].EntryPrice) + " TP:" + str(self.indicators[symbol].TP) + " SL:" + str(self.indicators[symbol].SL))
self.invested.append(str(symbol))
self.historyInvested.append(str(symbol))
else: #if bias is "short"
cl = Close - Open
bl = Close - resistance
if bl > 0 and cl > 0:
candle_points = bl/cl
if candle_points > self.break_percentage:
self.Sell(symbol.Symbol, quantity)
self.indicators[symbol].EntryPrice = Close
self.indicators[symbol].TP = self.indicators[symbol].EntryPrice - (atr * self.tpMultiplier)
self.indicators[symbol].SL = (atr * self.slMultiplier) + self.indicators[symbol].EntryPrice
self.Log("SHORT " + str(symbol) + " @ " + str(self.indicators[symbol].EntryPrice) + " TP:" + str(self.indicators[symbol].TP) + " SL:" + str(self.indicators[symbol].SL))
self.invested.append(str(symbol))
self.historyInvested.append(str(symbol))
### EXIT CONDITIONS ###
elif str(symbol) in self.invested:
if self.Portfolio[symbol.Symbol].IsLong:
#check if SL is hit
if self.indicators[symbol].SL >= Low:
self.StopMarketOrder(symbol.Symbol, -1*quantity, self.indicators[symbol].SL)
self.Log("SL Long Exit " + str(symbol) + " @ " + str(self.indicators[symbol].SL))
self.invested.remove(str(symbol))
#check if TP is hit
elif self.indicators[symbol].TP <= High:
self.LimitOrder(symbol.Symbol, -1*quantity, self.indicators[symbol].TP)
self.Log("TP Long Exit " + str(symbol) + " @ " + str(self.indicators[symbol].TP))
self.invested.remove(str(symbol))
elif self.Portfolio[symbol.Symbol].IsShort:
#check if SL is hit
if self.indicators[symbol].SL <= High:
self.StopMarketOrder(symbol.Symbol, quantity, self.indicators[symbol].SL)
self.Log("SL Short Exit " + str(symbol) + " @ " + str(self.indicators[symbol].SL))
self.invested.remove(str(symbol))
#check if TP is hit
elif self.indicators[symbol].TP >= Low:
self.LimitOrder(symbol.Symbol, quantity, self.indicators[symbol].TP)
self.Log("TP SHORT Exit " + str(symbol) + " @ " + str(self.indicators[symbol].TP))
self.invested.remove(str(symbol))
#"""
except:
self.Log("Symbol is not yet available: " + str(symbol))
def OnOrderEvent(self, orderEvent):
order = self.Transactions.GetOrderById(orderEvent.OrderId)
### Cancel remaining order if limit order or stop loss order is executed
if order.Status == OrderStatus.Filled:
if order.Type == OrderType.Limit or order.Type == OrderType.StopLimit or order.Type == OrderType.StopMarket:
self.Transactions.CancelOpenOrders(order.Symbol)
class SymbolData(object): #holds indicators initialization and update
def __init__(self, symbol):
self.EntryPrice = 0
self.TP = 0
self.SL = 0
# Declare Indicators
self.atr = AverageTrueRange(14)
self.atr.Updated += self.AtrUpdated
self.atrWin = RollingWindow[IndicatorDataPoint](2)
def update(self, time, value, tradebar):
self.atr.Update(tradebar)
def AtrUpdated(self, sender, updated):
self.atrWin.Add(updated)