Suppose from the forex tick data collected, we resample the time series at regular intervals. The average of prices over a reasonably short time period and long time period are calculated. The beta of the price series is taken as the ratio of the short-term average prices to the long-term average prices. In price series, where there is no trend, the ratio is 1—short-term prices are equal to the long-term prices. When prices are on an uptrend, the short-term prices are higher than the long-term average price levels and the beta is more than 1. Conversely, when prices are on a downtrend, the beta is less than 1.
In this section, we will discuss the implementation of a trend-following trading system to buy a position when prices are in an uptrend and sell when prices are going downtrend.
Let's create a class named ForexSystem
that inherits the oandapy.Streamer
class with the following required variables in the constructor:
""" Implementing the trend-following algorithm for trading foreign currencies """ import oandapy from datetime import datetime import pandas as pd class ForexSystem(oandapy.Streamer): def __init__(self, *args, **kwargs): oandapy.Streamer.__init__(self, *args, **kwargs) self.oanda = oandapy.API(kwargs["environment"], kwargs["access_token"]) self.instrument = None self.account_id = None self.qty = 0 self.resample_interval = '10s' self.mean_period_short = 5 self.mean_period_long = 20 self.buy_threshold = 1.0 self.sell_threshold = 1.0 self.prices = pd.DataFrame() self.beta = 0 self.is_position_opened = False self.opening_price = 0 self.executed_price = 0 self.unrealized_pnl = 0 self.realized_pnl = 0 self.position = 0 self.dt_format = "%Y-%m-%dT%H:%M:%S.%fZ"
We will create a method called begin
in the ForexSystem
class as the starting point of the program. Note that invoking self.start
will begin streaming rates data:
def begin(self, **params):
self.instrument = params["instruments"]
self.account_id = params["accountId"]
self.qty = params["qty"]
self.resample_interval = params["resample_interval"]
self.mean_period_short = params["mean_period_short"]
self.mean_period_long = params["mean_period_long"]
self.buy_threshold = params["buy_threshold"]
self.sell_threshold = params["sell_threshold"]
self.start(**params) # Start streaming prices
The on_success
method is inherited from the oandapy.Streamer
class that will handle the incoming rates data into our system. In the following code, we will parse this data into their respective variables for use in the tick_event
method:
def on_success(self, data): time, symbol, bid, ask = self.parse_tick_data( data["tick"]) self.tick_event(time, symbol, bid, ask)
The tick_event
method will process the tick data information by resampling the time series to calculate the beta. Note that we will store the mid-price of the bid and ask price. The beta will be used in the perform_trade_logic
method to determine whether to open or close our position. The status of the system is printed on every method call:
def tick_event(self, time, symbol, bid, ask): midprice = (ask+bid)/2. self.prices.loc[time, symbol] = midprice resampled_prices = self.prices.resample( self.resample_interval, how='last', fill_method="ffill") mean_short = resampled_prices.tail( self.mean_period_short).mean()[0] mean_long = resampled_prices.tail( self.mean_period_long).mean()[0] self.beta = mean_short / mean_long self.perform_trade_logic(self.beta) self.calculate_unrealized_pnl(bid, ask) self.print_status()
In the following perform_trade_logic
method, a buy signal indicates that a new long position is to be opened, or an existing short position is to be closed, by sending a buy market order. Conversely, a sell signal indicates that a new short position is to be opened, or an existing long position is to be closed, by sending a sell market order:
def perform_trade_logic(self, beta): if beta > self.buy_threshold: if not self.is_position_opened or self.position < 0: self.check_and_send_order(True) elif beta < self.sell_threshold: if not self.is_position_opened or self.position > 0: self.check_and_send_order(False)
The full source code for the ForexSystem
class is available at the Packt Publishing website.
The print_status
method displays the time, currency pair, position, beta, and profits and losses of our position during the running lifetime of the system with the following code:
def print_status(self): print "[%s] %s pos=%s beta=%s RPnL=%s UPnL=%s" % ( datetime.now().time(), self.instrument, self.position, round(self.beta, 5), self.realized_pnl, self.unrealized_pnl)
Let's start running our algorithmic trading system with the following code:
if __name__ == "__main__": key = "4c7718c7e03d472c2369abf1cb7ceddb-" "142b7d845d68844e853bb95c63f1c8b91" account_id = 6858884 system = ForexSystem(environment="practice", access_token=key) system.begin(accountId=account_id, instruments="EUR_USD", qty=1000, resample_interval="10s", mean_period_short=5, mean_period_long=20, buy_threshold=1., sell_threshold=1.)
Here, we are specifying the system to trade 1,000 units of EUR/USD each time. The resampling period of the time series is 10-second intervals. The short-term averaging period is defined to be the recent five periods or fifty seconds. The long-term averaging period is defined to be the recent twenty periods or two hundred seconds. When the beta exceeds the buy threshold value of 1, the system will enter into a long position of 1,000 units. Otherwise, when the beta falls below the sell threshold of 1, the system will enter into a short position of 1,000 units.
The first few lines of the output should give us the following result:
[09:31:59.067633] EUR_USD pos=0 beta=1.0 RPnL=0 UPnL=0 [09:31:59.163893] EUR_USD pos=0 beta=1.0 RPnL=0 UPnL=0 [09:32:00.233068] EUR_USD pos=0 beta=1.0 RPnL=0 UPnL=0
Suppose after some time, the value of our beta decreases. Our trading system will follow the trend by placing a sell market order of 1,000 units of EUR/USD. Our output status will update with the following information:
[09:35:42.305521] EUR_USD pos=0 beta=1.0 RPnL=0 UPnL=0 Placed order sell 1000 EUR_USD at market. [09:35:42.765773] EUR_USD pos=-1000 beta=0.99999 RPnL=0 UPnL=-0.14 [09:35:48.434842] EUR_USD pos=-1000 beta=0.99999 RPnL=0 UPnL=-0.11 … [09:38:28.864373] EUR_USD pos=-1000 beta=0.99984 RPnL=0 UPnL=0.32 [09:38:29.096078] EUR_USD pos=-1000 beta=0.99984 RPnL=0 UPnL=0.31
In the fxTrade Practice platform, we should be able to view our trade:
Our trading system will run indefinitely until we terminate the process with a Ctrl + Z or something similar.
Although our trend-following trading system seems to be doing reasonable well, however, as discussed in the previous sections, more improvements are required to develop a profitable and robust trading strategy.
In the next section, we will discuss risk management for our trading systems.