Overall Statistics |
Total Trades 74 Average Win 3.91% Average Loss -5.38% Compounding Annual Return 7.132% Drawdown 13.100% Expectancy 0.431 Net Profit 111.041% Sharpe Ratio 0.783 Loss Rate 17% Win Rate 83% Profit-Loss Ratio 0.73 Alpha 0.151 Beta -5.769 Annual Standard Deviation 0.074 Annual Variance 0.006 Information Ratio 0.566 Tracking Error 0.074 Treynor Ratio -0.01 Total Fees $0.00 |
import numpy as np import decimal ### <summary> ### RSI algorithm the buys on overbought and sells on oversold but only with trend. ### </summary> class TrendRSIAlgorithm(QCAlgorithm): '''Trend following: if strong dailt RSI, up trend. If weak RSI: downtrend. Take entry on hourly overbought or oversold.''' def Initialize(self): '''Initializes cash and max trade size. Sets target currencies to run on.''' # What do we want to trade? self.currenciesToUse = {"EURUSD", "AUDUSD", "EURGBP", "USDNZD"} self.SetStartDate(2007,10, 7) #Set Start Date #self.SetEndDate(2016,6,11) #Set End Date # Strategy cash management rules self.SetCash(1000) # Set Strategy Cash self.numlots = 4 # number of mini lots to trade (all positions added up) # Total size of all trades added together: self.maxTradeSize = self.numlots*10000 # 10000 = 1 mini lot # Integer actually equals number of pips when priced this way.... self.profitTarget = self.numlots*40 # profit per mini self.stopLoss = self.numlots*0 # stop per mini self.secondTrade = self.numlots*20 # when to try to get out of trouble if no stop self.criticalDeath = self.secondTrade*3 # when to abandon all hope and close it # Strategy RSI values self.overBought = 80 self.overSold = 20 self.uptrend = 65 self.downtrend = 35 self.SetBrokerageModel(BrokerageName.OandaBrokerage) self.rsiSlow = {} self.rsiFast = {} self.inTrouble = {} # Setup currencies for cur in self.currenciesToUse: # Add symbols to universe self.Debug("Adding symbol: " + cur) self.forex = self.AddForex(cur, Resolution.Hour, Market.Oanda) self.rsiFast[cur] = self.RSI(cur, 30, MovingAverageType.Simple, Resolution.Hour) self.rsiSlow[cur] = self.RSI(cur, 30, MovingAverageType.Simple, Resolution.Daily) self.inTrouble[cur] = 0 # If we don't warmup, we are trading randomly right on day one self.SetWarmUp(30, Resolution.Daily) # Adjust so portfolio total stays within max trade size (not counting getting out of trouble) self.tradesize = round(self.maxTradeSize / len(self.currenciesToUse)) self.profitTarget = round(self.profitTarget / len(self.currenciesToUse)) self.stopLoss = round(self.stopLoss / len(self.currenciesToUse)) self.secondTrade = round(self.secondTrade / len(self.currenciesToUse)) self.criticalDeath = round(self.criticalDeath / len(self.currenciesToUse)) def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' # Probably not necessary, but good to be safe ;) if self.IsWarmingUp: return # Now loop through everyone we want to trade for symbol in self.ActiveSecurities.Keys: holdings = self.Portfolio[symbol.Value].Quantity if holdings == 0: # No current holdings, so initiate new position if appropriate self.InitiatePosition(symbol) else: # We own it, so figure out what to do profit = self.Portfolio[symbol.Value].UnrealizedProfit # Take profit if we have hit profit target if profit > self.profitTarget: self.Liquidate(symbol.Value) self.inTrouble[symbol.Value] = 0 continue # don't bother with anything else once we profit if self.stopLoss == 0: # No stop, we either die or take second trade # Critical death is a last resort stop, so must check for it if profit < -self.criticalDeath: # Things have gone horribly wrong, just throw in the towel self.Debug("ZOMG hit critical death: " + symbol.Value) self.Liquidate(symbol.Value) continue # required since this likely means RSI is extreme so we'd liquide then immediate take a 2nd trade # Didn't stop out or take profit, so deal with 2nd trade situation # Filter to let it move against us enough before adding to the position if profit < -self.secondTrade: self.HandleSecondTrade(symbol, holdings) continue # for completeness or adding code later, not necessary here else: # We have a stop, so use it # (we check every bar instead of setting stops to give us some room to come around) if profit < -self.stopLoss: # get out! self.Liquidate(symbol.Value) def InitiatePosition(self, symbol): # Filter on Trend if self.rsiSlow[symbol.Value].Current.Value > self.uptrend: # Up trend so only take long # Price below oversold RSI, so buy if self.rsiFast[symbol.Value].Current.Value < self.overSold: self.MarketOrder(symbol, self.tradesize) return if self.rsiSlow[symbol.Value].Current.Value < self.downtrend: # Downtrend so only take short # Price above overbought, so sell if self.rsiFast[symbol.Value].Current.Value > self.overBought: self.MarketOrder(symbol, -self.tradesize) return def HandleSecondTrade(self, symbol, holdings): '''Essentially, we take a 2nd trade if the 1st has failed to profit and we hit overbought or oversold again''' # in trouble, try to get out # inTrouble tracks whether we already added to the position if self.inTrouble[symbol.Value] == 0: # haven't initiated fix, so try to do better if holdings < 0: # We are short, so add short if self.rsiFast[symbol.Value].Current.Value > self.overBought: self.MarketOrder(symbol, -self.tradesize) self.inTrouble[symbol.Value] = 1 else: # We are long, so try to add long if self.rsiFast[symbol.Value].Current.Value < self.overSold: self.MarketOrder(symbol, self.tradesize) self.inTrouble[symbol.Value] = 1