Hello,
I’m still new to QuantConnect and I used the recent time to read a lot in the community in order to get my head around the main concepts. In parallel I tried to practice with code.
I would like to code a basic framework that can handle the most common parts like:
- defining the main parameters (account size, backtesting timeframe, symbol, market, resolution, brokerage, leverage, etc.)
- defining the criteria/rules for the trading setup (including indicators if required)
- defining the risk and order management (position size, trade entry, stop loss, take profit)
- using Debug function
- using RollingWindow function
- using Schedule function
Please find below my example code.
This example strategy is NOT designed to be profitable, but for learning purposes only! :-)
In this example the strategy is long-only and consists of 3 simple criteria:
- if fast EMA > slow EMA then long
- only enter position between 9-10am UTC
- only have one position at a time
The RollingWindow function is used to derive the entry, stop loss and take profit levels from the previous candle:
- stop buy at previous candle high
- stop loss at previous candle low
- take profit at previous candle high plus 10%
The Schedule function is used to handle the required trading time slot (9-10am UTC).
The Risk Management is determined by 2 requirements:
- max risk per trade = 0.5% from portfolio value
- max equity per trade = portfolio value * max leverage * 25%
I would like to use this first example code to identify and solve the main issues and potential misconceptions that I (probably) have and then take it further from there.
Your feedback and comments are highly appreciated!
Cheers,
Thom
class SessionTrend(QCAlgorithm):
def Initialize(self):
# input
self.SetTimeZone('Europe/London')
self.SetCash(100000)
self.SetStartDate(2019,4,1)
self.SetEndDate(2019,4,30)
self._max_risk_per_trade_factor = 0.005
self._max_equity_per_trade_factor = 0.25
self._maxLeverage_factor = 20.0
self._symbol = 'EURUSD'
self._pricePrecision = 4
self._resolution = Resolution.Hour
self._market = Market.Oanda
self._brokerage = BrokerageName.OandaBrokerage
self.SetBrokerageModel(self._brokerage, AccountType.Margin)
# add Price Data to data
self._eurusd = self.AddForex(self._symbol, self._resolution, self._market, leverage=10.0)
# RollingWindow
self._window = RollingWindow[QuoteBar](2)
# indicators
self._ema_fast = self.EMA(self._symbol, 8, self._resolution)
self._ema_slow = self.EMA(self._symbol, 21, self._resolution)
# schedule
self._session = False
self.Schedule.On(self.DateRules.EveryDay(self._symbol), self.TimeRules.At(9, 0), Action(self._sessionOn))
self.Schedule.On(self.DateRules.EveryDay(self._symbol), self.TimeRules.At(10, 0), Action(self._sessionOff))
def _sessionOn(self):
self._session = True
def _sessionOff(self):
self._session = False
def OnData(self, data):
# retrieve current price data
self._currentPrice = self.Securities[self._symbol].Close
# add price data to RollingWindow
self._window.Add(data[self._symbol])
# wait for indicator to be ready.
if not self._ema_slow.IsReady: return
# Setup criteria
_invested = self.Portfolio[self._symbol].Invested
_ema_bullish = (self._ema_fast.Current.Value > self._ema_slow.Current.Value)
_condition = not _invested and self._session and _ema_bullish
# long orders
if _condition:
# trade setup
self._stopBuy = round(self._window[1].High, self._pricePrecision)
self._stopLoss = round(self._window[1].Low, self._pricePrecision)
self._takeProfit = round(self._window[1].High * 1.1, self._pricePrecision)
# risk management
self._max_equity_per_trade = self.Portfolio.TotalPortfolioValue * self._maxLeverage_factor * self._max_equity_per_trade_factor # max allowed equity per trade [currency]
self._max_risk_per_trade = round(self.Portfolio.TotalPortfolioValue * self._max_risk_per_trade_factor, 0) # max allowed risk per trade [currency]
self._risk_per_unit = round(self._stopBuy - self._stopLoss, self._pricePrecision) # risk per unit [currency]
self._max_quantity_equity = round(self._max_equity_per_trade / self._stopBuy, 0) # max quantity per trade [nr. of contracts] based on max equity
self._max_quantity_risk = round(self._max_risk_per_trade / self._risk_per_unit, 0) # max quantity per trade [nr. of contracts] based on max risk
self._quantity = round(min(self._max_quantity_equity, self._max_quantity_risk), 0) # number of contracts to be ordered
# order management
self._marketBuyTicket = self.MarketOrder(self._symbol, self._quantity, False, 'market buy')
self._limitSellTicket = self.LimitOrder(self._symbol, -self._quantity, self._takeProfit, 'target limit sell')
self._stopMarketSellTicket = self.StopMarketOrder(self._symbol, -self._quantity, self._stopLoss, 'stop market sell')
self.Debug(f'order time: {self.Time}, tag: {self._marketBuyTicket.Tag}, quantity: {self._marketBuyTicket.Quantity}, fill price: {self._marketBuyTicket.AverageFillPrice}, status: {self._marketBuyTicket.Status}')
def OnOrderEvent(self, orderEvent):
_orderFromEvent = self.Transactions.GetOrderById(orderEvent.OrderId)
_orderTicketFromEvent = self.Transactions.GetOrderTicket(orderEvent.OrderId)
_openOrderTickets = self.Transactions.GetOrderTickets()
# OCO
if _orderTicketFromEvent.Status == OrderStatus.Filled:
# cancel stop loss order
if _orderTicketFromEvent.Tag == 'target limit sell':
for _ticket in _openOrderTickets:
if _ticket.Tag == 'stop market sell':
self.Debug(f'event time: {orderEvent.UtcTime}, event: {_orderTicketFromEvent.Tag}, cancelled order: {_ticket.Tag}')
self.Transactions.CancelOrder(_ticket.OrderId)
# cancel take profit order
elif _orderTicketFromEvent.Tag == 'stop market sell':
for _ticket in _openOrderTickets:
if _ticket.Tag == 'target limit sell':
self.Debug(f'event time: {orderEvent.UtcTime}, event: {_orderTicketFromEvent.Tag}, cancelled order: {_ticket.Tag}')
self.Transactions.CancelOrder(_ticket.OrderId)