Forex Trading Diary #1 - Automated Forex Trading with the OANDA API

I previously mentioned in the QuantStart: 2014 In Review article that I would be spending some of 2015 writing about automated forex trading.

Given that I myself usually carry out research in equities and futures markets, I thought it would be fun (and educational!) to write about my experiences of entering the forex market in the style of a diary. Each "diary entry" will attempt to build on all those before, but should also be relatively self-contained.

In this first entry of the diary I'll be describing how to set up a new practice brokerage account with OANDA as well as how to create a basic multithreaded event-driven trading engine that can automatically execute trades in both a practice and live setting.

Last year we spent a lot of time looking at the event-driven backtester, primarily for equities and ETFs. The one I present below is geared towards forex and can be used for either paper trading or live trading.

I have written all of the following instructions for Ubuntu 14.04, but they should easily translate to Windows or Mac OS X, using a Python distribution such as Anaconda. The only additional library used for the Python trading engine is the requests library, which is necessary for HTTP communication to the OANDA API.

Since this is the first post directly about foreign exchange trading, and the code presented below can be straightforwardly adapted to a live trading environment, I would like to present the following disclaimers:

Disclaimer: Trading foreign exchange on margin carries a high level of risk, and may not be suitable for all investors. Past performance is not indicative of future results. The high degree of leverage can work against you as well as for you. Before deciding to invest in foreign exchange you should carefully consider your investment objectives, level of experience, and risk appetite. The possibility exists that you could sustain a loss of some or all of your initial investment and therefore you should not invest money that you cannot afford to lose. You should be aware of all the risks associated with foreign exchange trading, and seek advice from an independent financial advisor if you have any doubts.

This software is provided "as is" and any expressed or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the regents or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any out of the use of this software, even if advised of the possibility of such damage.

Setting Up an Account with OANDA

The first question that comes to mind is "Why choose OANDA?". Simply put, after a bit of Googling around for forex brokers that had APIs, I saw that OANDA had recently released a proper REST API that could easily be communicated with from nearly any language in an extremely straightforward manner. After reading through their developer API documentation, I decided to give them a try, at least with a practice account.

To be clear - I have no prior or existing relationship with OANDA and am only providing this recommendation based on my limited experience playing around with their practice API and some brief usage (for market data download) while employed at a fund previously. If anybody has come across any other forex brokers that also have a similarly modern API then I'd be happy to give them a look as well.

Before utilising the API it is necessary to sign up for a practice account. To do this, head to the sign-up link. You will see the following screen:

OANDA sign-up screen
OANDA sign-up screen

You will then be able to sign in with your login credentials. Make sure to select the "fxTradePractice" tab from the sign-in screen:

OANDA sign-in screen
OANDA sign-in screen

Once in you will need to make a note of your Account ID. It is listed underneath the black "My Funds" header next to "Primary". Mine is a 7-digit number. In addition you will also need to generate a personal API token. To do this click "Manage API Access" underneath the "Other Actions" tab on the lower left:

OANDA dashboard
OANDA dashboard

At this stage you will be able to generate an API token. You will need the key for use later, so make sure to write it down as well.

You will now want to launch the FXTrade Practice application, which will allow us to see the executed orders and our (paper!) profit & loss.

If you are running a Ubuntu system you will need to install a slightly different version of Java. In particular, the Oracle version of Java 8. If you don't do this then the practice simulator will not load from the browser. I ran these commands on my system:

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer

You will now be able to launch the practice trading environment. Return to the OANDA dashboard and click the green highlighted "Launch FXTrade Practice" link. It will bring up a Java dialog asking whether you want to run it. Click "Run" and the fxTrade Practice tool will load. Mine defaulted to a 15-min candle chart of EUR/USD with the Quote Panel on the left:

OANDA fxTrade Practice screen
OANDA fxTrade Practice screen

At this point we are ready to begin designing and coding our automated forex trading system against the OANDA API.

Overview of Trading Architecture

If you have been following the event-driven backtester series for equities and ETFs that I created last year, you'll be aware of how such an event-driven trading system functions. For those of you who are new to event-driven software, I would strongly suggest reading through the article in order to gain some insight into how they work.

In essence, the entire program is executed in an infinte while loop that only terminates when the trading system is shut off. The central communication mechanism of the program is given via a queue that contains events.

The queue is constantly queried to check for new events. Once an event has been taken off the top of the queue it must be handled by an appropriate component of the program. Hence a market data feed might create TickEvents that are placed onto the queue when a new market price arrives. A signal-generating strategy object might create OrderEvents that are to be sent to a brokerage.

The usefulness of such a system is given by the fact that it doesn't matter what order or types of events are placed on the queue, as they will always be correctly handled by the right component within the program.

In addition different parts of the program can be run in separate threads, meaning that there is never any waiting for any particular component before processing any other. This is extremely useful in algorithmic trading situations where market data feed handlers and strategy signal generators have vastly different performance characteristics.

The main trading loop is given by the following Python pseudo-code:

while True:
    try:
        event = events_queue.get(False)
    except Queue.Empty:
        pass
    else:
        if event is not None:
            if event.type == 'TICK':
                strategy.calculate_signals(event)
            elif event.type == 'ORDER':
                execution.execute_order(event)
    time.sleep(heartbeat)

As we stated above the code runs in an infinite loop. Firstly, the queue is polled to retrieve a new event. If the queue is empty, then the loop simply restarts after a short sleep period known as the "heartbeat". If an event is found its type is assessed and then the relevant module (either the strategy or the execution handler) is called upon to handle the event and possibly generate new ones that go back onto the queue.

The basic components that we will create for our trading system include the following:

  • Streaming Price Handler - This will keep a long-running connection open to OANDAs servers and send tick data (i.e. bid/ask) across the connection for any instruments that we're interested in.
  • Strategy Signal Generator - This will take a sequence of tick events and use them to generate trading orders that will be executed by the execution handler.
  • Execution Handler - Takes a set of order events and then blindly executes them with OANDA.
  • Events - These objects constitute the "messages" that are passed around on the events queue. We only require two for this implementation, namely the TickEvent and the OrderEvent.
  • Main Entry Point - The main entry point also includes the "trade" loop that continuously polls the message queue and dispatches messages to the correct component. This is often known as the "event loop" or "event handler".

We will now discuss the implementation of the code in detail. At the bottom of the article is the complete listing of all source code files. If you place them in the same directory and run python trading.py you will begin generating orders, assuming you have filled in your account ID and authentication token from OANDA.

Python Implementation

It is bad practice to store passwords or authentication keys within a codebase as you can never predict who will eventually be allowed access to a project. In a production system we would store these credentials as environment variables with the system and then query these "envvars" each time the code is redeployed. This ensures that passwords and auth tokens are never stored in a version control system.

However, since we are solely interested in building a "toy" trading system, and are not concerned with production details in this article, we will instead separate these auth tokens into a settings file.

In the following settings.py configuration file we have a dictionary called ENVIRONMENTS which stores the API endpoints for both the OANDA price streaming API and the trading API. Each sub dictionary contains three separate API endpoints: real, practice and sandbox.

The sandbox API is purely for testing code and for checking that there are no errors or bugs. It does not have the uptime guarantees of the real or practice APIs. The practice API, in essence, provides the ability to paper trade. That is, it provides all of the features of the real API on a simulated practice account. The real API is just that - it is live trading! If you use that endpoint in your code, it will trade against your live account balance. BE EXTREMELY CAREFUL!

IMPORTANT: When trading against the practice API remember that an important transaction cost, that of market impact, is not considered. Since no trades are actually being placed into the environment this cost must be accounted for in another way elsewhere using a market impact model if you wish to realistically assess performance.

In the following we are using the practice account as given by the DOMAIN setting. We need two separate dictionaries for the domains, one each for the streaming and trading API components. Finally we have the ACCESS_TOKEN and ACCOUNT_ID. I've filled the two below with dummy IDs so you will need to utilise your own, which can be accessed from the OANDA account page:

ENVIRONMENTS = {
    "streaming": {
        "real": "stream-fxtrade.oanda.com",
        "practice": "stream-fxpractice.oanda.com",
        "sandbox": "stream-sandbox.oanda.com"
    },
    "api": {
        "real": "api-fxtrade.oanda.com",
        "practice": "api-fxpractice.oanda.com",
        "sandbox": "api-sandbox.oanda.com"
    }
}

DOMAIN = "practice"
STREAM_DOMAIN = ENVIRONMENTS["streaming"][DOMAIN]
API_DOMAIN = ENVIRONMENTS["api"][DOMAIN]
ACCESS_TOKEN = 'abcdef0123456abcdef0123456-abcdef0123456abcdef0123456'
ACCOUNT_ID = '12345678'

The next step is to define the events that the queue will use to help all of the individual components communicate. We need two: TickEvent and OrderEvent. The first stores information about instrument market data such as the (best) bid/ask and the trade time. The second is used to transmit orders to the execution handler and thus contains the instrument, the number of units to trade, the order type ("market" or "limit") and the "side" (i.e. "buy" and "sell").

To future-proof our events code we are going to create a base class called Event and have all events inherit from this. The code is provided below in events.py:

class Event(object):
    pass


class TickEvent(Event):
    def __init__(self, instrument, time, bid, ask):
        self.type = 'TICK'
        self.instrument = instrument
        self.time = time
        self.bid = bid
        self.ask = ask


class OrderEvent(Event):
    def __init__(self, instrument, units, order_type, side):
        self.type = 'ORDER'
        self.instrument = instrument
        self.units = units
        self.order_type = order_type
        self.side = side

The next class we are going to create will handle the trading strategy. In this demo we are going to create a rather nonsensical strategy that simply receives all of the market ticks and on every 5th tick randomly buys or sells 10,000 units of EUR/USD.

Clearly this is a ridiculous "strategy"! However, it is fantastic for testing purposes because it is straightforward to code and understand. In future diary entries we will be replacing this with something significantly more exciting that will (hopefully) turn a profit!

The strategy.py file can be found below. Let's work through it and see what's going on. Firstly we import the random library and the OrderEvent object from events.py. We need the random lib in order to select a random buy or sell order. We need OrderEvent as this is how the strategy object will send orders to the events queue, which will later be executed by the execution handler.

The TestRandomStrategy class simply takes the instrument (in this case EUR/USD), the number of units and the events queue as a set of parameters. It then creates a ticks counter that is used to tell how many TickEvent instances it has seen.

Most of the work occurs in the calculate_signals method, which simply takes an event, determines whether it is a TickEvent (otherwise ignore) and increments the tick counter. It then checks to see if the count is divisible by 5 and then randomly buys or sells, with a market order, the specified number of units. It's certainly not the world's greatest trading strategy, but it will be more than suitable for our OANDA brokerage API testing purposes!

import random

from event import OrderEvent


class TestRandomStrategy(object):
    def __init__(self, instrument, units, events):
        self.instrument = instrument
        self.units = units
        self.events = events
        self.ticks = 0

    def calculate_signals(self, event):
        if event.type == 'TICK':
            self.ticks += 1
            if self.ticks % 5 == 0:
                side = random.choice(["buy", "sell"])
                order = OrderEvent(
                    self.instrument, self.units, "market", side
                )
                self.events.put(order)

The next component is the execution handler. This class is tasked with acting upon OrderEvent instances and making requests to the broker (in this case OANDA) in a "dumb" fashion. That is, there is no risk management or potfolio construction overlay. The execution handler will simply execute any order that it has been given.

We must pass all of the authentication information to the Execution class, including the "domain" (practice, real or sandbox), the access token and account ID. We then create a secure connection with httplib, one of Pythons built in libraries.

Most of the work occurs in execute_order. The method requires an event as a parameter. It then constructs two dictionaries - the headers and the params. These dictionaries will then be correctly encoded (partially by urllib, another Python library) to be sent as an HTTP POST request to OANDAs API.

We pass the Content-Type and Authorization header parameters, which include our authentication information. In addition we encode the parameters, which include the instrument (EUR/USD), units, order type and side (buy/sell). Finally, we make the request and save the response:

import httplib
import urllib


class Execution(object):
    def __init__(self, domain, access_token, account_id):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.conn = self.obtain_connection()

    def obtain_connection(self):
        return httplib.HTTPSConnection(self.domain)

    def execute_order(self, event):
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": "Bearer " + self.access_token
        }
        params = urllib.urlencode({
            "instrument" : event.instrument,
            "units" : event.units,
            "type" : event.order_type,
            "side" : event.side
        })
        self.conn.request(
            "POST",
            "/v1/accounts/%s/orders" % str(self.account_id),
            params, headers
        )
        response = self.conn.getresponse().read()
        print response

The most complex component of the trading system is the StreamingForexPrices object, which handles the market price updates from OANDA. There are two methods: connect_to_stream and stream_to_queue.

The first method uses the Python requests library to connect to a streaming socket with the appropriate headers and parameters. The parameters include the Account ID and the necessary instrument list that should be listened to for updates (in this case it is only EUR/USD). Note the following line:

resp = s.send(pre, stream=True, verify=False)

This tells the connection to be streamed and thus kept open in a long-running manner.

The second method, stream_to_queue, actually attempts to connect to the stream. If the response is not successful (i.e. the response code is not HTTP 200), then we simply return and exit. If it is successful we try to load the JSON packet returned into a Python dictionary. Finally, we convert the Python dictionary with the instrument, bid/ask and timestamp into a TickEvent that is sent to the events queue:

import requests
import json

from event import TickEvent


class StreamingForexPrices(object):
    def __init__(
        self, domain, access_token,
        account_id, instruments, events_queue
    ):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.instruments = instruments
        self.events_queue = events_queue

    def connect_to_stream(self):
        try:
            s = requests.Session()
            url = "https://" + self.domain + "/v1/prices"
            headers = {'Authorization' : 'Bearer ' + self.access_token}
            params = {'instruments' : self.instruments, 'accountId' : self.account_id}
            req = requests.Request('GET', url, headers=headers, params=params)
            pre = req.prepare()
            resp = s.send(pre, stream=True, verify=False)
            return resp
        except Exception as e:
            s.close()
            print "Caught exception when connecting to stream\n" + str(e)

    def stream_to_queue(self):
        response = self.connect_to_stream()
        if response.status_code != 200:
            return
        for line in response.iter_lines(1):
            if line:
                try:
                    msg = json.loads(line)
                except Exception as e:
                    print "Caught exception when converting message into json\n" + str(e)
                    return
                if msg.has_key("instrument") or msg.has_key("tick"):
                    print msg
                    instrument = msg["tick"]["instrument"]
                    time = msg["tick"]["time"]
                    bid = msg["tick"]["bid"]
                    ask = msg["tick"]["ask"]
                    tev = TickEvent(instrument, time, bid, ask)
                    self.events_queue.put(tev)

We now have all of the major components in place. The final step is to wrap up everything we have written so far into a "main" program. The goal of this file, known as trading.py, is to create two separate threads, one of which runs the pricing handler and the other which runs the trading handler.

Why do we need two separate threads? Put simply, we are executing two "separate" pieces of code, both of which are continuously running. If we were to create a non-threaded program, then the streaming socket used for the pricing updates would never ever "release" back to the main code path and hence we would never actually carry out any trading. Similarly, if we ran the trade loop (see below), we would never actually return the flow path to the price streaming socket. Hence we need multiple threads, one for each component, so that they can be carried out independently. They will both communicate to each other via the events queue.

Let's examine this a bit futher. We create two separate threads with the following lines:

trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
price_thread = threading.Thread(target=prices.stream_to_queue, args=[])

We pass the function or method name to the target keyword argument and then pass an iterable (such as a list or tuple) to the args keyword argument, which then passes those arguments to the actual method/function.

Finally we start both threads with the following lines:

trade_thread.start()
price_thread.start()

Thus we are able to run two, effectively infinite looping, code segments independently, which both communicate through the events queue. Note that the Python threading library does not produce a true multi-core multithreaded environment due to the CPython implementation of Python and the Global Interpreter Lock (GIL). If you would like to read more about multithreading on Python, please take a look at this article.

Let's examine the rest of the code in detail. Firstly we import all of the necessary libraries including Queue, threading and time. We then import all of the above code files. I personally prefer to capitalise any configuration settings, which is a habit I picked up from working with Django!

After that we define the trade function, which was explained in Python-pseudocode above. An infinite while loop is carried out (while True:) that continuously polls from the events queue and only skips the loop if it is found empty. If an event is found then it is either a TickEvent or a OrderEvent and then the appropriate component is called to carry it out. In this case it is either a strategy or execution handler. The loop then simply sleeps for "heartbeat" seconds (in this case 0.5 seconds) and continues.

Finally, we define the main entrypoint of the code in the __main__ function. It is well commented below, but I will summarise here. In essence we instantiate the events queue and define the instruments/units. We then create the StreamingForexPrices price streaming class and then subsequently the Execution execution handler. Both receive the necessary authentication details that are given by OANDA when creating an account.

We then create the TestRandomStrategy instance. Finally we define the two threads and then start them:

import Queue
import threading
import time

from execution import Execution
from settings import STREAM_DOMAIN, API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID
from strategy import TestRandomStrategy
from streaming import StreamingForexPrices


def trade(events, strategy, execution):
    """
    Carries out an infinite while loop that polls the
    events queue and directs each event to either the
    strategy component of the execution handler. The
    loop will then pause for "heartbeat" seconds and
    continue.
    """
    while True:
        try:
            event = events.get(False)
        except Queue.Empty:
            pass
        else:
            if event is not None:
                if event.type == 'TICK':
                    strategy.calculate_signals(event)
                elif event.type == 'ORDER':
                    print "Executing order!"
                    execution.execute_order(event)
        time.sleep(heartbeat)


if __name__ == "__main__":
    heartbeat = 0.5  # Half a second between polling
    events = Queue.Queue()

    # Trade 10000 units of EUR/USD
    instrument = "EUR_USD"
    units = 10000

    # Create the OANDA market price streaming class
    # making sure to provide authentication commands
    prices = StreamingForexPrices(
        STREAM_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID,
        instrument, events
    )

    # Create the execution handler making sure to
    # provide authentication commands
    execution = Execution(API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID)

    # Create the strategy/signal generator, passing the
    # instrument, quantity of units and the events queue
    strategy = TestRandomStrategy(instrument, units, events)

    # Create two separate threads: One for the trading loop
    # and another for the market price streaming class
    trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
    price_thread = threading.Thread(target=prices.stream_to_queue, args=[])

    # Start both threads
    trade_thread.start()
    price_thread.start()

To run the code you simply need to place all the files in the same directory and call the following at the terminal:

python trading.py

Note that to stop the code at this stage requires a hard kill of the Python process, via "Ctrl-Z" or equivalent! I've not added an additional thread to handle looking for the sys.exit() that would be needed to stop the code safely. A potential way to stop the code on a Ubuntu/Linux machine is to type:

pgrep python

And then pass the output of this (a process number) into the following:

kill -9 PROCESS_ID

Where PROCESS_ID must be replaced with the output of pgrep. Note that this is NOT particularly good practice!

In later articles we will be creating a more sophisticated stop/start mechanism that makes use of Ubuntu's process supervision in order to have the trading system running 24/7.

The output after 30 seconds or so, depending upon the time of day relative to the main trading hours for EUR/USD, for the above code, is given below:

{u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2015-01-19T15:28:19.563256Z'}}
{u'tick': {u'ask': 1.16287, u'instrument': u'EUR_USD', u'bid': 1.16274, u'time': u'2015-01-19T15:28:28.021045Z'}}
{u'tick': {u'ask': 1.16287, u'instrument': u'EUR_USD', u'bid': 1.16273, u'time': u'2015-01-19T15:28:30.982725Z'}}
{u'tick': {u'ask': 1.16285, u'instrument': u'EUR_USD', u'bid': 1.16272, u'time': u'2015-01-19T15:28:52.493297Z'}}
{u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.16272, u'time': u'2015-01-19T15:29:12.854066Z'}}
Executing order!
{
    "instrument" : "EUR_USD",
    "time" : "2015-01-19T15:29:14.000000Z",
    "price" : 1.16283,
    "tradeOpened" : {
        "id" : 821102691,
        "units" : 10000,
        "side" : "buy",
        "takeProfit" : 0,
        "stopLoss" : 0,
        "trailingStop" : 0
    },
    "tradesClosed" : [],
    "tradeReduced" : {}
}
{u'tick': {u'ask': 1.16284, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2015-01-19T15:29:17.817401Z'}}
{u'tick': {u'ask': 1.16283, u'instrument': u'EUR_USD', u'bid': 1.1627, u'time': u'2015-01-19T15:29:17.920900Z'}}

The first five lines show the JSON tick data returned from OANDA with bid/ask prices. Subsequently you can see the Executing order! output as well as the JSON response returned from OANDA confirming the opening of a buy trade for 10,000 units of EUR/USD and the price it was achieved at.

This will keep running indefinitely until you kill the program with a "Ctrl-Z" command or similar.

What's Next?

In later articles we are going to carry out some much-needed improvements, including:

  • Real strategies - Proper forex strategies that generate profitable signals.
  • Production infrastructure - Remote server implementation and 24/7 monitored trading system, with stop/start capability.
  • Portfolio and risk management - Portfolio and risk overlays for all suggested orders from the strategy.
  • Multiple strategies - Constructing a portfolio of strategies that integrate into the risk management overlay

As with the equities event-driven backtester, we also need to create a forex backtesting module. That will let us carry out rapid research and make it easier to deploy strategies.

Full Code

settings.py (remember to change ACCOUNT_ID and ACCESS_TOKEN!):

ENVIRONMENTS = {
    "streaming": {
        "real": "stream-fxtrade.oanda.com",
        "practice": "stream-fxpractice.oanda.com",
        "sandbox": "stream-sandbox.oanda.com"
    },
    "api": {
        "real": "api-fxtrade.oanda.com",
        "practice": "api-fxpractice.oanda.com",
        "sandbox": "api-sandbox.oanda.com"
    }
}

DOMAIN = "practice"
STREAM_DOMAIN = ENVIRONMENTS["streaming"][DOMAIN]
API_DOMAIN = ENVIRONMENTS["api"][DOMAIN]
ACCESS_TOKEN = 'abcdef0123456abcdef0123456-abcdef0123456abcdef0123456'
ACCOUNT_ID = '12345678'

event.py:

class Event(object):
    pass


class TickEvent(Event):
    def __init__(self, instrument, time, bid, ask):
        self.type = 'TICK'
        self.instrument = instrument
        self.time = time
        self.bid = bid
        self.ask = ask


class OrderEvent(Event):
    def __init__(self, instrument, units, order_type, side):
        self.type = 'ORDER'
        self.instrument = instrument
        self.units = units
        self.order_type = order_type
        self.side = side

streaming.py:

import requests
import json

from event import TickEvent


class StreamingForexPrices(object):
    def __init__(
        self, domain, access_token,
        account_id, instruments, events_queue
    ):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.instruments = instruments
        self.events_queue = events_queue

    def connect_to_stream(self):
        try:
            s = requests.Session()
            url = "https://" + self.domain + "/v1/prices"
            headers = {'Authorization' : 'Bearer ' + self.access_token}
            params = {'instruments' : self.instruments, 'accountId' : self.account_id}
            req = requests.Request('GET', url, headers=headers, params=params)
            pre = req.prepare()
            resp = s.send(pre, stream=True, verify=False)
            return resp
        except Exception as e:
            s.close()
            print "Caught exception when connecting to stream\n" + str(e)

    def stream_to_queue(self):
        response = self.connect_to_stream()
        if response.status_code != 200:
            return
        for line in response.iter_lines(1):
            if line:
                try:
                    msg = json.loads(line)
                except Exception as e:
                    print "Caught exception when converting message into json\n" + str(e)
                    return
                if msg.has_key("instrument") or msg.has_key("tick"):
                    print msg
                    instrument = msg["tick"]["instrument"]
                    time = msg["tick"]["time"]
                    bid = msg["tick"]["bid"]
                    ask = msg["tick"]["ask"]
                    tev = TickEvent(instrument, time, bid, ask)
                    self.events_queue.put(tev)

strategy.py:

import random

from event import OrderEvent


class TestRandomStrategy(object):
    def __init__(self, instrument, units, events):
        self.instrument = instrument
        self.units = units
        self.events = events
        self.ticks = 0

    def calculate_signals(self, event):
        if event.type == 'TICK':
            self.ticks += 1
            if self.ticks % 5 == 0:
                side = random.choice(["buy", "sell"])
                order = OrderEvent(
                    self.instrument, self.units, "market", side
                )
                self.events.put(order)

execution.py:

import httplib
import urllib


class Execution(object):
    def __init__(self, domain, access_token, account_id):
        self.domain = domain
        self.access_token = access_token
        self.account_id = account_id
        self.conn = self.obtain_connection()

    def obtain_connection(self):
        return httplib.HTTPSConnection(self.domain)

    def execute_order(self, event):
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": "Bearer " + self.access_token
        }
        params = urllib.urlencode({
            "instrument" : event.instrument,
            "units" : event.units,
            "type" : event.order_type,
            "side" : event.side
        })
        self.conn.request(
            "POST",
            "/v1/accounts/%s/orders" % str(self.account_id),
            params, headers
        )
        response = self.conn.getresponse().read()
        print response

trading.py:

import Queue
import threading
import time

from execution import Execution
from settings import STREAM_DOMAIN, API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID
from strategy import TestRandomStrategy
from streaming import StreamingForexPrices


def trade(events, strategy, execution):
    """
    Carries out an infinite while loop that polls the
    events queue and directs each event to either the
    strategy component of the execution handler. The
    loop will then pause for "heartbeat" seconds and
    continue.
    """
    while True:
        try:
            event = events.get(False)
        except Queue.Empty:
            pass
        else:
            if event is not None:
                if event.type == 'TICK':
                    strategy.calculate_signals(event)
                elif event.type == 'ORDER':
                    print "Executing order!"
                    execution.execute_order(event)
        time.sleep(heartbeat)


if __name__ == "__main__":
    heartbeat = 0.5  # Half a second between polling
    events = Queue.Queue()

    # Trade 10000 units of EUR/USD
    instrument = "EUR_USD"
    units = 10000

    # Create the OANDA market price streaming class
    # making sure to provide authentication commands
    prices = StreamingForexPrices(
        STREAM_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID,
        instrument, events
    )

    # Create the execution handler making sure to
    # provide authentication commands
    execution = Execution(API_DOMAIN, ACCESS_TOKEN, ACCOUNT_ID)

    # Create the strategy/signal generator, passing the
    # instrument, quantity of units and the events queue
    strategy = TestRandomStrategy(instrument, units, events)

    # Create two separate threads: One for the trading loop
    # and another for the market price streaming class
    trade_thread = threading.Thread(target=trade, args=(events, strategy, execution))
    price_thread = threading.Thread(target=prices.stream_to_queue, args=[])

    # Start both threads
    trade_thread.start()
    price_thread.start()
comments powered by Disqus