In [1]:
import configparser
# v1 OANDA API
# pip install git+https://github.com/oanda/oandapy.git
# Note: You can request a v1 instead of v20 account by contacting Oanda chat support
import oandapy as opy

config = configparser.ConfigParser()
config.read('oanda.cfg')

oanda = opy.API(environment='practice',
                access_token=config['oanda']['access_token'])

In [2]:
import pandas as pd

data = oanda.get_history(instrument='EUR_USD',  # our instrument
                         start='2016-12-08',  # start data
                         end='2016-12-10',  # end date
                         granularity='M1')  # minute bars

df = pd.DataFrame(data['candles']).set_index('time')

df.index = pd.DatetimeIndex(df.index)

df.info()


<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 2658 entries, 2016-12-08 00:00:00 to 2016-12-09 21:59:00
Data columns (total 10 columns):
closeAsk    2658 non-null float64
closeBid    2658 non-null float64
complete    2658 non-null bool
highAsk     2658 non-null float64
highBid     2658 non-null float64
lowAsk      2658 non-null float64
lowBid      2658 non-null float64
openAsk     2658 non-null float64
openBid     2658 non-null float64
volume      2658 non-null int64
dtypes: bool(1), float64(8), int64(1)
memory usage: 210.3 KB

In [3]:
import numpy as np

df['returns'] = np.log(df['closeAsk'] / df['closeAsk'].shift(1))

cols = []

for momentum in [15, 30, 60, 120]:
    col = 'position_%s' % momentum
    df[col] = np.sign(df['returns'].rolling(momentum).mean())
    cols.append(col)

In [33]:
%matplotlib inline
import seaborn as sns; sns.set()

strats = ['returns']

for col in cols:
    strat = 'strategy_%s' % col.split('_')[1] 
    df[strat] = df[col].shift(1) * df['returns']
    strats.append(strat)

df[strats].dropna().cumsum().apply(np.exp).plot()


Out[33]:
<matplotlib.axes._subplots.AxesSubplot at 0x111242668>

In [34]:
class MomentumTrader(opy.Streamer): 
    def __init__(self, momentum, *args, **kwargs): 
        opy.Streamer.__init__(self, *args, **kwargs)
        self.ticks = 0 
        self.position = 0
        self.df = pd.DataFrame()
        self.momentum = momentum
        self.units = 100000
    def create_order(self, side, units):
        order = oanda.create_order(config['oanda']['account_id'], 
            instrument='EUR_USD', units=units, side=side,
            type='market') 
        print('\n', order)
    def on_success(self, data):
        self.ticks += 1
        # print(self.ticks, end=', ')
        # appends the new tick data to the DataFrame object
        self.df = self.df.append(pd.DataFrame(data['tick'],
                                 index=[data['tick']['time']]))
        # transforms the time information to a DatetimeIndex object
        self.df.index = pd.DatetimeIndex(self.df['time'])
        # resamples the data set to a new, homogeneous interval
        dfr = self.df.resample('5s').last()
        # calculates the log returns
        dfr['returns'] = np.log(dfr['ask'] / dfr['ask'].shift(1))
        # derives the positioning according to the momentum strategy
        dfr['position'] = np.sign(dfr['returns'].rolling( 
                                      self.momentum).mean())
        if dfr['position'].ix[-1] == 1:
            # go long
            if self.position == 0:
                self.create_order('buy', self.units)
            elif self.position == -1:
                self.create_order('buy', self.units * 2)
            self.position = 1
        elif dfr['position'].ix[-1] == -1:
            # go short
            if self.position == 0:
                self.create_order('sell', self.units)
            elif self.position == 1:
                self.create_order('sell', self.units * 2)
            self.position = -1
        if self.ticks == 250:
            # close out the position
            if self.position == 1:
                self.create_order('sell', self.units)
            elif self.position == -1:
                self.create_order('buy', self.units)
            self.disconnect()

In [31]:
mt = MomentumTrader(momentum=12, environment='practice',
                    access_token=config['oanda']['access_token'])

In [32]:
mt.rates(account_id=config['oanda']['account_id'], instruments=['DE30_EUR'], ignore_heartbeat=True)


---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
<ipython-input-32-ec08ad91d1d6> in <module>()
----> 1 mt.rates(account_id=config['oanda']['account_id'], instruments=['DE30_EUR'], ignore_heartbeat=True)

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/site-packages/oandapy/stream/stream.py in rates(self, account_id, instruments, **params)
     25         params['instruments'] = instruments
     26         endpoint = 'v1/prices'
---> 27         return self.run(endpoint, params=params)
     28 
     29     def events(self, **params):

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/site-packages/oandapy/stream/stream.py in run(self, endpoint, params)
    102 
    103                 if line:
--> 104                     data = json.loads(line.decode("utf-8"))
    105                     if not (ignore_heartbeat and "heartbeat" in data):
    106                         self.on_success(data)

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    352             parse_int is None and parse_float is None and
    353             parse_constant is None and object_pairs_hook is None and not kw):
--> 354         return _default_decoder.decode(s)
    355     if cls is None:
    356         cls = JSONDecoder

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/decoder.py in decode(self, s, _w)
    337 
    338         """
--> 339         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    340         end = _w(s, end).end()
    341         if end != len(s):

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/decoder.py in raw_decode(self, s, idx)
    353         """
    354         try:
--> 355             obj, end = self.scan_once(s, idx)
    356         except StopIteration as err:
    357             raise JSONDecodeError("Expecting value", s, err.value) from None

JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

In [26]:
mystreamer = opy.Streamer(environment='practice',access_token=config['oanda']['access_token'])

In [22]:
mystreamer.rates(account_id="8ba5b3b5e28cd20292383895127b7991-e910b2fd79a67404fb7efcd8132f85e1", instruments="DE30_EUR")


---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
<ipython-input-22-8ff9e3858170> in <module>()
----> 1 mystreamer.rates(account_id="8ba5b3b5e28cd20292383895127b7991-e910b2fd79a67404fb7efcd8132f85e1", instruments="DE30_EUR")

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/site-packages/oandapy/stream/stream.py in rates(self, account_id, instruments, **params)
     25         params['instruments'] = instruments
     26         endpoint = 'v1/prices'
---> 27         return self.run(endpoint, params=params)
     28 
     29     def events(self, **params):

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/site-packages/oandapy/stream/stream.py in run(self, endpoint, params)
    102 
    103                 if line:
--> 104                     data = json.loads(line.decode("utf-8"))
    105                     if not (ignore_heartbeat and "heartbeat" in data):
    106                         self.on_success(data)

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    352             parse_int is None and parse_float is None and
    353             parse_constant is None and object_pairs_hook is None and not kw):
--> 354         return _default_decoder.decode(s)
    355     if cls is None:
    356         cls = JSONDecoder

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/decoder.py in decode(self, s, _w)
    337 
    338         """
--> 339         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    340         end = _w(s, end).end()
    341         if end != len(s):

/Users/benjamin.chodroff/anaconda3/anaconda/lib/python3.6/json/decoder.py in raw_decode(self, s, idx)
    353         """
    354         try:
--> 355             obj, end = self.scan_once(s, idx)
    356         except StopIteration as err:
    357             raise JSONDecodeError("Expecting value", s, err.value) from None

JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

In [ ]: