| Overall Statistics |
|
Total Trades 922 Average Win 4.38% Average Loss -1.25% Compounding Annual Return 20.769% Drawdown 35.700% Expectancy 0.060 Net Profit 26.499% Sharpe Ratio 0.582 Probabilistic Sharpe Ratio 25.956% Loss Rate 77% Win Rate 23% Profit-Loss Ratio 3.51 Alpha 0.251 Beta 0.248 Annual Standard Deviation 0.409 Annual Variance 0.168 Information Ratio 0.672 Tracking Error 0.431 Treynor Ratio 0.959 Total Fees $0.00 Estimated Strategy Capacity $2700000.00 Lowest Capacity Asset EURUSD 8G Portfolio Turnover 665.63% |
# region imports
from AlgorithmImports import *
# endregion
class SmoothRedDogfish(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022, 1, 1) # Set Start Date
self.SetEndDate(2023, 3, 31) # Set End Date
self.SetCash(1000) # Set Strategy Cash
self.SetBenchmark("SPY")
#self.symbol = self.AddEquity("AAPL", Resolution.Daily).Symbol
#self.currency_pair = self.AddForex("EURUSD", Resolution.Minute, Market.Oanda).Symbol
self.currencies = ["EURUSD", "GBPUSD"]
self.Resolution = Resolution.Minute
self.Data = {}
self.BolingerData = {}
self.SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin)
for symbol in self.currencies:
self.currency = self.AddForex(symbol,self.Resolution, Market.Oanda, True, 30)
self.Data[symbol] = self.currency
# Consolidate bars
self.consolidator = QuoteBarConsolidator(timedelta(minutes=30))
self.consolidator.DataConsolidated += self.consolidation_handler
self.SubscriptionManager.AddConsolidator(symbol, self.consolidator)
self.bollinger_bands = self.BB(symbol, 20, 2, MovingAverageType.Simple, self.Resolution)
self.RegisterIndicator(symbol, self.bollinger_bands, timedelta(minutes=30))
self.BolingerData[symbol] = self.bollinger_bands
# Schedule to trade between hour x and y
# create on schedile method
#self.Schedule.On(self.DateRules.EveryDay('GBPUSD'), self.TimeRules.At(6,0), Action(self._sessionOn))
#self.Schedule.On(self.DateRules.EveryDay('GBPUSD'), self.TimeRules.At(21,0), Action(self._sessionOff))
# Add end of day liquidation
self.Schedule.On(self.DateRules.EveryDay('GBPUSD'), self.TimeRules.At(23, 58), Action(self.closePositionsEndOfDay))
# Add end of week liquidation
self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday), self.TimeRules.At(4, 58), Action(self.closePositionsEndOfWeek))
# Risk Management
self.riskPercentage = 0.02
self.stopLossTarget = 40 # Profit target in pips
self.profitTarget = 150 # Profit target in pip
self.lotSize = 0.03
# Initilize order ticket
self.CurrentOrderTicket = {symbol: None for symbol in self.currencies}
self.StopLossTicket = {symbol: None for symbol in self.currencies}
self.ProfitTargetTicket = {symbol: None for symbol in self.currencies}
self.EURCurrentOrderTicket = None
self.EURStopLossTicket = None
self.EURProfitTargetTicket = None
self.GBPCurrentOrderTicket = None
self.GBPStopLossTicket = None
self.GBPProfitTargetTicket = None
# Add a chart
self.chart = Chart('Price and Indicators ')
self.chart.AddSeries(Series("Close",SeriesType.Line))
self.chart.AddSeries(Series("SMA",SeriesType.Line))
self.AddChart(self.chart)
def _sessionOn(self):
self._session = True
def _sessionOff(self):
self._session = False
def consolidation_handler(self, sender: object, consolidated_bar: QuoteBar) -> None:
#self.Log(f"{consolidated.Close} at {consolidated.EndTime}")
if not self.bollinger_bands.IsReady:
return
# Execute trade for each pair
for symbol in self.Data.keys():
invested = self.Portfolio[symbol].Invested
if not invested:
self.Debug(f'Symbol : {symbol}')
# Forex example
#mean = self.moving_average.Current.Value
pip = self.Securities[symbol].SymbolProperties.MinimumPriceVariation
current_price = self.Securities[symbol].Close
accountBalance = self.Portfolio.CashBook["USD"].Amount
#if self.Portfolio.CashBook["USD"].Amount < 1000 :
# accountBalance = self.Portfolio.CashBook["USD"].Amount
#else:
# accountBalance = 1000
#quantity = self.positionSize(current_price, accountBalance,self.stopLossTarget, self.riskPercentage, pip)
quantity = self.lotPositionSize(self.lotSize, pip)
# BB bands
lowerBand = self.BolingerData[symbol].LowerBand.Current.Value
upperBand = self.BolingerData[symbol].UpperBand.Current.Value
self.Debug('I made it here')
# if current price is below or equals to lower band then take long position
if current_price <= lowerBand:
self.Debug('I made it here1')
# Calculate stop loss and profit target
stop_loss = current_price - (self.stopLossTarget * (pip * 10))
profit_target = current_price + (self.profitTarget * (pip * 10))
self.Debug('Long Position:')
self.Debug(f'Position Size: {quantity}')
self.Debug(f'Current Price: {current_price}')
#self.Debug(f'Pip variance: {pip*10}')
#self.Debug(f'Stop Loss Calc: {self.stopLossTarget * pip * 10}')
self.Debug(f'Stop Loss: {stop_loss}')
self.Debug(f'Profit Target: {profit_target}')
#self.Plot(self.chart.Name, "Close", current_price)
#self.Plot(self.chart.Name, "SMA", current_sma_value)
if symbol == 'EURUSD':
marketOrderTicket = self.MarketOrder(symbol, quantity)
stopMarketOrderTicket = self.StopMarketOrder(symbol, -quantity, stop_loss)
limitOrderTicket = self.LimitOrder(symbol, -quantity, profit_target)
self.EURCurrentOrderTicket = marketOrderTicket
self.EURStopLossTicket = stopMarketOrderTicket
self.EURProfitTargetTicket = limitOrderTicket
self.CurrentOrderTicket[symbol] = marketOrderTicket
self.StopLossTicket[symbol] = stopMarketOrderTicket
self.ProfitTargetTicket[symbol] = limitOrderTicket
elif symbol == 'GBPUSD':
marketOrderTicket = self.MarketOrder(symbol, quantity)
stopMarketOrderTicket = self.StopMarketOrder(symbol, -quantity, stop_loss)
limitOrderTicket = self.LimitOrder(symbol, -quantity, profit_target)
self.GBPCurrentOrderTicket = marketOrderTicket
self.GBPStopLossTicket = stopMarketOrderTicket
self.GBPProfitTargetTicket = limitOrderTicket
# if current price is above or equals to upper band then take short position
elif current_price >= upperBand:
self.Debug('I made it here2')
# Calculate stop loss and profit target
stop_loss = current_price + (self.stopLossTarget * (pip * 10))
profit_target = current_price - (self.profitTarget * (pip * 10))
self.Debug('Short Position:')
self.Debug(f'Position Size: {quantity}')
self.Debug(f'Current Price: {current_price}')
#self.Debug(f'Pip variance: {pip*10}')
#self.Debug(f'Stop Loss Calc: {self.stopLossTarget * pip * 10}')
self.Debug(f'Stop Loss: {stop_loss}')
self.Debug(f'Profit Target: {profit_target}')
if symbol == 'EURUSD':
marketOrderTicket = self.MarketOrder(symbol, -quantity)
stopMarketOrderTicket = self.StopMarketOrder(symbol, quantity, stop_loss)
limitOrderTicket = self.LimitOrder(symbol, quantity, profit_target)
self.EURCurrentOrderTicket = marketOrderTicket
self.EURStopLossTicket = stopMarketOrderTicket
self.EURProfitTargetTicket = limitOrderTicket
elif symbol == 'GBPUSD':
marketOrderTicket = self.MarketOrder(symbol, -quantity)
stopMarketOrderTicket = self.StopMarketOrder(symbol, quantity, stop_loss)
limitOrderTicket = self.LimitOrder(symbol, quantity, profit_target)
self.GBPCurrentOrderTicket = marketOrderTicket
self.GBPStopLossTicket = stopMarketOrderTicket
self.GBPProfitTargetTicket = limitOrderTicket
def OnData(self, data: Slice):
pass
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled:
return
self.Debug(f"{self.Time} {orderEvent}")
## python doesn't support null. Instead, check for None
if (self.EURProfitTargetTicket is None) or (self.EURStopLossTicket is None) or (self.GBPProfitTargetTicket is None) or (self.GBPStopLossTicket is None) :
return
filledOrderId = orderEvent.OrderId
symbol = orderEvent.Symbol
if symbol == 'EURUSD':
self.Debug("Cancelling EURUSD Orders")
if (self.EURProfitTargetTicket.OrderId == filledOrderId):
self.EURStopLossTicket.Cancel()
if (self.EURStopLossTicket.OrderId == filledOrderId):
self.EURProfitTargetTicket.Cancel()
elif symbol == 'GBPUSD':
self.Debug("Cancelling GBPUSD Orders")
if (self.GBPProfitTargetTicket.OrderId == filledOrderId):
self.GBPStopLossTicket.Cancel()
if (self.GBPStopLossTicket.OrderId == filledOrderId):
self.GBPProfitTargetTicket.Cancel()
# Close positions at the end of the day if in profit
def closePositionsEndOfDay(self):
for symbol in self.currencies:
if self.Portfolio[symbol].Invested and self.Portfolio[symbol].UnrealizedProfitPercent > 0.02:
unrealisedprofitpercent = self.Portfolio[symbol].UnrealizedProfitPercent
self.Debug(f'Unrealised profit percent: {unrealisedprofitpercent}')
if self.Portfolio[symbol].IsLong:
self.MarketOrder(symbol, -self.Portfolio[symbol].Quantity)
self.Debug(f"Closed {symbol} position at the end of the day")
elif self.Portfolio[symbol].IsShort:
self.MarketOrder(symbol, self.Portfolio[symbol].Quantity)
self.Debug(f"Closed {symbol} position at the end of the day")
# Close positions at the end of the week if in profit
def closePositionsEndOfWeek(self):
for symbol in self.currencies:
if self.Portfolio[symbol].Invested and self.Portfolio[symbol].UnrealizedProfitPercent > 0.02:
if self.Portfolio[symbol].IsLong:
self.MarketOrder(symbol, -self.Portfolio[symbol].Quantity)
self.Debug(f"Closed {symbol} position at the end of the week")
elif self.Portfolio[symbol].IsShort:
self.MarketOrder(symbol, self.Portfolio[symbol].Quantity)
self.Debug(f"Closed {symbol} position at the end of the week")
def positionSize(self,_price,_accountBalance, _stopLossPips, _riskPercentage, _minimumPriceVariation):
# Debug Function Parameters
# Calulate position size
# based on risk percentage and account balance.
###############################################)
_riskAmount = _accountBalance * _riskPercentage
_riskValuePerPip = _riskAmount / _stopLossPips
_positionSize = _riskValuePerPip / (_minimumPriceVariation * 10)
self.Debug(f'Position Size: {_positionSize}')
return _positionSize
def lotPositionSize(self,_lot, _minimumPriceVariation):
# Debug Function Parameters
# Calulate position size using lots
# based on risk percentage and account balance.
###############################################
_positionSize = _lot / _minimumPriceVariation
self.Debug(f'Position Size: {_positionSize}')
self.Debug(f'Variance Size: {_minimumPriceVariation * 10}')
return _positionSize