1 Overview

The forex market is highly liquid and volatile. Automated trading can be helpful because it saves time, it allows traders to react quicker, it eliminates human error, and it eliminates the psychological challenges that traders face. In automated trading, traders can leverage a predefined set of rules into a robot to spot and execute trades.

2 Objective

Build a trading robot that will spot signals, send alerts, and execute trades on the USD/CAD pair during the new york session (8am to 5pm EST) according to the inside bar momentum strategy. The inside bar momentum stategy is detailed here https://www.babypips.com/trading/new-forex-system-inside-bar-momentum-strategy. In a nutshell, a signal occurs when a second candlestick is completely contained be the first candlestick. Then,

  • If the first candlestick is bullish, set a buy stop order at the first candle’s high plus 10% of its range, the stop loss at the first candle’s high minus 20% of its range, and the target at the first candle’s high plus 80% of its range.
  • If the first candlestick is bearish, set a sell stop order at the first candle’s high plus 10% of its range, the stop loss at the first candle’s low plus 20% of its range, and the target at the first candle’s low minus 80% of its range.

Additional rules/indicators can be used to complement and potentially improve this strategy, but we will stick with this as a starting point.

3 Setting the bot and alerts

3.1 Loading the data

Loading the data from OANDA’s rest API using the previously defined HisPrices() function. Only the 2 latest complete candles from the 4-hour timeframe are needed.

candles <- HisPrices(accountType = "practice",
                    instrument = "USD_CAD",
                    granularity = "H4",
                    tzone = "Canada/Eastern",
                    count = 3) # pulling 3 assumming last one is incomplete
# remove incomplete candles
candles_complete <- candles %>% 
  dplyr::filter(complete == "TRUE")
candles_complete
##   complete volume          time_stamp    open   high     low   close
## 1     TRUE  22166 2022-08-22 09:00:00 1.29930 1.3049 1.29928 1.30454
## 2     TRUE  10422 2022-08-22 13:00:00 1.30455 1.3061 1.30424 1.30530

At the time of printing, the date and time is 2022-08-22 19:20:24 EST. The last complete candle closed on 2022-08-22 09:00:00 EST.

3.2 Signal Detection

Determining whether or not the last candle was an inside bar. If so, it signals a buy or sell order depending on the previous candle’s direction.

signal <- candles_complete %>% 
  dplyr::mutate(range = high - low,
                first_candle_dir = dplyr::case_when(
                  open < close ~ "bullish",
                  open > close ~ "bearish",
                  open == close ~ "neither"
                ),
                inside_bar = dplyr::if_else(high < dplyr::lag(high) & low > dplyr::lag(low), 1, 0, 0),
                inside_bar_buy = dplyr::if_else(inside_bar == 1 & lag(first_candle_dir) == "bullish", 1, 0, 0),
                inside_bar_sell = dplyr::if_else(inside_bar == 1 & lag(first_candle_dir) == "bearish", 1, 0, 0))
latest_candle <- signal %>% 
  dplyr::slice_tail(n = 1)
latest_candle
##   complete volume          time_stamp    open   high     low  close   range first_candle_dir inside_bar inside_bar_buy inside_bar_sell
## 1     TRUE  10422 2022-08-22 13:00:00 1.30455 1.3061 1.30424 1.3053 0.00186          bullish          0              0               0

At the time of printing, there were no inside bar signals.

3.3 Setting the bot

Setting up the bot such that, if there were no signals then the script will stop. If there was a signal during the new york session, a trade will be executed according to the inside bar momentum strategy.

if(latest_candle$inside_bar == 0)print("No trade signals")
## [1] "No trade signals"
if(latest_candle$inside_bar == 1 & lubridate::hour(Sys.time()) >= 8 & lubridate::hour(Sys.time()) < 17){
  buy_or_sell <- latest_candle$inside_bar_buy - latest_candle$inside_bar_sell # +1 if buy, -1 if sell
  current_bidask <- ActualPrice(accountType = "practice", instrument = "USD_CAD")
  current_price <- ifelse(buy_or_sell == 1, as.numeric(current_bidask$ask), as.numeric(current_bidask$bid))
  entry_price <- ifelse(buy_or_sell == 1,
                        latest_candle$high + latest_candle$range*0.1,
                        latest_candle$low - latest_candle$range*0.1)
  stoploss_price <- ifelse(buy_or_sell == 1,
                        latest_candle$high - latest_candle$range*0.2,
                        latest_candle$low + latest_candle$range*0.2)
  target_price <- ifelse(buy_or_sell == 1,
                        latest_candle$high + latest_candle$range*0.8,
                        latest_candle$low - latest_candle$range*0.8)
  if(buy_or_sell*(current_price - entry_price) < 0) {orderType = "STOP"; timeInForce = "GTC"}
  if(buy_or_sell*(current_price - entry_price) >= 0) stop("No trade executed. The current price is already past the entry price.")
  order <- NewOrder(accountType = "practice",
                    instrument = "USD_CAD",
                    orderType = orderType,
                    price = round(entry_price, 5),
                    timeInForce = timeInForce,
                    units = buy_or_sell*1000,
                    stopLoss = round(stoploss_price, 5), takeProfit = round(target_price, 5))

  
  alert_message <- paste0("A trade signal was triggered based on the Inside Bar Momentum Strategy. A ",
                   ifelse(buy_or_sell == 1, "buy", "sell"),  " stop order for USD/CAD was executed with order ID ",
                   order$orderCreateTransaction$id, ".")
  }

3.4 Setting the alert

Send an alert if the order was executed. The alert message is sent through gmail.

if(exists("alert_message")){
email <- emayili::envelope() %>%
  emayili::from("R trading bot") %>%
  emayili::to(Sys.getenv("GMAIL_USER")) %>%
  emayili::subject("Trade Updates") %>%
  emayili::text(alert_message)

smtp <- emayili::server(host = "smtp.gmail.com",
               port = 465,
               username = Sys.getenv("GMAIL_USER"),
               password = Sys.getenv("GMAIL_PW"))

smtp(email, verbose = FALSE)
}

4 Concluding Remarks

The trading bot can be executed manually or automatically. I use cronR to set a job that automatically runs the trading bot script at a desired time and frequency. In this case, the script should be executed a few minutes past every four hours. The trading bot can be customized as needed to take on different trading strategies, currency pairs, or schedules.