Skip to main content

As of march 2018 OANDA no longer supports the v1 REST-API. The only pending V20 endpoint was the forexlabs endpoint. Instead of launching forexlabs as a V20-endpoint, OANDA choose to support this endpoint from the v1 REST interface, see: http://developer.oanda.com/rest-live-v20/forexlabs-ep/.

Build Documentation Status Code Health Coverage Pypi Python versions Codacy

Interactive

Jupyter

Using the Jupyter notebook it is easy to play with the oandapyV20 library.

TOC

Install

$ pip install oandapyV20

or the latest development version from github:

$ pip install git+https://github.com/hootnot/oanda-api-v20.git

If you want to run the tests, clone the repository:

$ git clone https://github.com/hootnot/oanda-api-v20
$ cd oanda-api-v20

# install necessary packages for testing
$ grep "\- pip install" .travis.yml |
> while read LNE
> do `echo $LNE| cut -c2-` ; done

$ python setup.py test
$ python setup.py install

Examples are provided in the https://github.com/hootnot/oandapyV20-examples repository.

Design

In the V20-library endpoints are represented as APIRequest objects derived from the APIRequest base class. Each endpoint group (accounts, trades, etc.) is represented by it’s own (abstract) class covering the functionality of all endpoints for that group. Each endpoint within that group is covered by a class derived from the abstract class.

Top

Client

The V20-library has a client class (API) which processes APIRequest objects.

Top

contrib.requests

The contrib.request package offers classes providing an easy way to construct the data for the data parameter of the OrderCreate endpoint or the TradeCRCDO (Create/Replace/Cancel Dependent Orders).

mktOrder = MarketOrderRequest(instrument="EUR_USD",
     units=10000,
     takeProfitOnFill=TakeProfitDetails(price=1.10).data,
     stopLossOnFill=StopLossDetails(price=1.07).data
).data


instead of:
mktOrder = {'order': {
               'timeInForce': 'FOK',
               'instrument': 'EUR_USD',
               'positionFill': 'DEFAULT',
               'units': '10000',
               'type': 'MARKET',
               'takeProfitOnFill': {
                   'timeInForce': 'GTC',
                   'price': '1.10000'}
               }
               'stopLossOnFill': {
                   'timeInForce': 'GTC',
                   'price': '1.07000'}
               }
           }

Top

contrib.factories

The contrib.factories module offers classes providing an easy way generate requests. Downloading historical data is limited to 5000 records per request. This means that you have to make consecutive requests with change of parameters if you want more than 5000 records.

The InstrumentsCandlesFactory solves this by generating the requests for you, example:

import sys
import json

from oandapyV20.contrib.factories import InstrumentsCandlesFactory
from oandapyV20 import API

access_token = "..."

client = API(access_token=access_token)

_from = sys.argv[1]
_to = sys.argv[2]
gran = sys.argv[3]
instr = sys.argv[4]

params = {
    "granularity": gran,
    "from": _from,
    "to": _to
}

def cnv(r, h):
    for candle in r.get('candles'):
        ctime = candle.get('time')[0:19]
        try:
            rec = "{time},{complete},{o},{h},{l},{c},{v}".format(
                time=ctime,
                complete=candle['complete'],
                o=candle['mid']['o'],
                h=candle['mid']['h'],
                l=candle['mid']['l'],
                c=candle['mid']['c'],
                v=candle['volume'],
            )
        except Exception as e:
            print(e, r)
        else:
            h.write(rec+"\n")

with open("/tmp/{}.{}.out".format(instr, gran), "w") as O:
    for r in InstrumentsCandlesFactory(instrument=instr, params=params):
        print("REQUEST: {} {} {}".format(r, r.__class__.__name__, r.params))
        rv = client.request(r)
        cnv(r.response, O)

When running this:

$ python oandahist.py 2017-01-01T00:00:00Z 2017-06-30T00:00:00Z H4 EUR_USD
REQUEST: v3/instruments/EUR_USD/candles InstrumentsCandles
{'to': '2017-03-25T08:00:00Z',
 'from': '2017-01-01T00:00:00Z', 'granularity': 'H4'}
REQUEST: v3/instruments/EUR_USD/candles InstrumentsCandles
{'to': '2017-06-16T20:00:00Z', 'from': '2017-03-25T12:00:00Z',
 'granularity': 'H4'}
REQUEST: v3/instruments/EUR_USD/candles InstrumentsCandles
{'to': '2017-06-30T00:00:00Z', 'from': '2017-06-17T00:00:00Z',
 'granularity': 'H4'}

The output shows it processed three InstrumentsCandles requests. The data can be found in /tmp/EUR_USD.H4.out:

$ tail /tmp/EUR_USD.H4.out
...
2017-06-28T01:00:0,True,1.13397,1.13557,1.13372,1.13468,1534
2017-06-28T05:00:0,True,1.13465,1.13882,1.13454,1.13603,8486
2017-06-28T09:00:0,True,1.13606,1.13802,1.12918,1.13315,12815
2017-06-28T13:00:0,True,1.13317,1.13909,1.13283,1.13781,13255
2017-06-28T17:00:0,True,1.13783,1.13852,1.13736,1.13771,2104
2017-06-28T21:00:0,True,1.13789,1.13894,1.13747,1.13874,1454

Top

Examples

API-endpoint access

import json
from oandapyV20 import API    # the client
import oandapyV20.endpoints.trades as trades

access_token = "..."
accountID = "..."
client = API(access_token=access_token)

# request trades list
r = trades.TradesList(accountID)
rv = client.request(r)
print("RESPONSE:\n{}".format(json.dumps(rv, indent=2)))

Top

Placing a MarketOrder with TakeProfitOrder and StopLossOrder

import json

from oandapyV20.contrib.requests import MarketOrderRequest
from oandapyV20.contrib.requests import TakeProfitDetails, StopLossDetails

import oandapyV20.endpoints.orders as orders
import oandapyV20

from exampleauth import exampleAuth


accountID, access_token = exampleAuth()
api = oandapyV20.API(access_token=access_token)

# EUR_USD (today 1.0750)
EUR_USD_STOP_LOSS = 1.07
EUR_USD_TAKE_PROFIT = 1.10

mktOrder = MarketOrderRequest(
    instrument="EUR_USD",
    units=10000,
    takeProfitOnFill=TakeProfitDetails(price=EUR_USD_TAKE_PROFIT).data,
    stopLossOnFill=StopLossDetails(price=EUR_USD_STOP_LOSS).data)

# create the OrderCreate request
r = orders.OrderCreate(accountID, data=mktOrder.data)
try:
    # create the OrderCreate request
    rv = api.request(r)
except oandapyV20.exceptions.V20Error as err:
    print(r.status_code, err)
else:
    print(json.dumps(rv, indent=2))

Top

Processing series of requests

Processing series of requests is also possible now by storing different requests in an array or from some ‘request-factory’ class. Below an array example:

import json
from oandapyV20 import API    # the client
from oandapyV20.exceptions import V20Error
import oandapyV20.endpoints.accounts as accounts
import oandapyV20.endpoints.trades as trades
import oandapyV20.endpoints.pricing as pricing

access_token = "..."
accountID = "..."
client = API(access_token=access_token)

# list of requests
lor = []
# request trades list
lor.append(trades.TradesList(accountID))
# request accounts list
lor.append(accounts.AccountList())
# request pricing info
params={"instruments": "DE30_EUR,EUR_GBP"}
lor.append(pricing.PricingInfo(accountID, params=params))

for r in lor:
    try:
        rv = client.request(r)
        # put request and response in 1 JSON structure
        print("{}".format(json.dumps({"request": "{}".format(r),
                                      "response": rv}, indent=2)))
    except V20Error as e:
        print("OOPS: {:d} {:s}".format(e.code, e.msg))

Output

{
  "request": "v3/accounts/101-004-1435156-001/trades",
  "response": {
    "lastTransactionID": "1109",
    "trades": [
      {
        "unrealizedPL": "23.0000",
        "financing": "-0.5556",
        "state": "OPEN",
        "price": "10159.4",
        "realizedPL": "0.0000",
        "currentUnits": "-10",
        "openTime": "2016-07-22T16:47:04.315211198Z",
        "initialUnits": "-10",
        "instrument": "DE30_EUR",
        "id": "1105"
      },
      {
        "unrealizedPL": "23.0000",
        "financing": "-0.5556",
        "state": "OPEN",
        "price": "10159.4",
        "realizedPL": "0.0000",
        "currentUnits": "-10",
        "openTime": "2016-07-22T16:47:04.141436468Z",
        "initialUnits": "-10",
        "instrument": "DE30_EUR",
        "id": "1103"
      }
    ]
  }
}

{
  "request": "v3/accounts",
  "response": {
    "accounts": [
      {
        "tags": [],
        "id": "101-004-1435156-002"
      },
      {
        "tags": [],
        "id": "101-004-1435156-001"
      }
    ]
  }
}

{
  "request": "v3/accounts/101-004-1435156-001/pricing",
  "response": {
    "prices": [
      {
        "status": "tradeable",
        "quoteHomeConversionFactors": {
          "negativeUnits": "1.00000000",
          "positiveUnits": "1.00000000"
        },
        "asks": [
          {
            "price": "10295.1",
            "liquidity": 25
          },
          {
            "price": "10295.3",
            "liquidity": 75
          },
          {
            "price": "10295.5",
            "liquidity": 150
          }
        ],
        "unitsAvailable": {
          "default": {
            "short": "60",
            "long": "100"
          },
          "reduceOnly": {
            "short": "0",
            "long": "20"
          },
          "openOnly": {
            "short": "60",
            "long": "0"
          },
          "reduceFirst": {
            "short": "60",
            "long": "100"
          }
        },
        "closeoutBid": "10293.5",
        "bids": [
          {
            "price": "10293.9",
            "liquidity": 25
          },
          {
            "price": "10293.7",
            "liquidity": 75
          },
          {
            "price": "10293.5",
            "liquidity": 150
          }
        ],
        "instrument": "DE30_EUR",
        "time": "2016-09-29T17:07:19.598030528Z",
        "closeoutAsk": "10295.5"
      },
      {
        "status": "tradeable",
        "quoteHomeConversionFactors": {
          "negativeUnits": "1.15679152",
          "positiveUnits": "1.15659083"
        },
        "asks": [
          {
            "price": "0.86461",
            "liquidity": 1000000
          },
          {
            "price": "0.86462",
            "liquidity": 2000000
          },
          {
            "price": "0.86463",
            "liquidity": 5000000
          },
          {
            "price": "0.86465",
            "liquidity": 10000000
          }
        ],
        "unitsAvailable": {
          "default": {
            "short": "624261",
            "long": "624045"
          },
          "reduceOnly": {
            "short": "0",
            "long": "0"
          },
          "openOnly": {
            "short": "624261",
            "long": "624045"
          },
          "reduceFirst": {
            "short": "624261",
            "long": "624045"
          }
        },
        "closeoutBid": "0.86442",
        "bids": [
          {
            "price": "0.86446",
            "liquidity": 1000000
          },
          {
            "price": "0.86445",
            "liquidity": 2000000
          },
          {
            "price": "0.86444",
            "liquidity": 5000000
          },
          {
            "price": "0.86442",
            "liquidity": 10000000
          }
        ],
        "instrument": "EUR_GBP",
        "time": "2016-09-29T17:07:19.994271769Z",
        "closeoutAsk": "0.86465",
        "type": "PRICE"
      }
    ]
  }
}

Top

Streaming endpoints

Streaming quotes: use pricing.PricingStream. Streaming transactions: use transactions.TransactionsEvents.

To fetch streaming data from a stream use the following pattern:

import json
from oandapyV20 import API
from oandapyV20.exceptions import V20Error
from oandapyV20.endpoints.pricing import PricingStream

accountID = "..."
access_token="..."

api = API(access_token=access_token, environment="practice")

instruments = "DE30_EUR,EUR_USD,EUR_JPY"
s = PricingStream(accountID=accountID, params={"instruments":instruments})
try:
    n = 0
    for R in api.request(s):
        print(json.dumps(R, indent=2))
        n += 1
        if n > 10:
            s.terminate("maxrecs received: {}".format(MAXREC))

except V20Error as e:
    print("Error: {}".format(e))

Check the ‘examples’ directory for more detailed examples.

Output

{
  "status": "tradeable",
  "asks": [
    {
      "price": "10547.0",
      "liquidity": 25
    },
    {
      "price": "10547.2",
      "liquidity": 75
    },
    {
      "price": "10547.4",
      "liquidity": 150
    }
  ],
  "closeoutBid": "10546.6",
  "bids": [
    {
      "price": "10547.0",
      "liquidity": 25
    },
    {
      "price": "10546.8",
      "liquidity": 75
    },
    {
      "price": "10546.6",
      "liquidity": 150
    }
  ],
  "instrument": "DE30_EUR",
  "time": "2016-10-17T12:25:28.158741026Z",
  "closeoutAsk": "10547.4",
  "type": "PRICE",
}
{
  "type": "HEARTBEAT",
  "time": "2016-10-17T12:25:37.447397298Z"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "114.490",
      "liquidity": 1000000
    },
    {
      "price": "114.491",
      "liquidity": 2000000
    },
    {
      "price": "114.492",
      "liquidity": 5000000
    },
    {
      "price": "114.494",
      "liquidity": 10000000
    }
  ],
  "closeoutBid": "114.469",
  "bids": [
    {
      "price": "114.473",
      "liquidity": 1000000
    },
    {
      "price": "114.472",
      "liquidity": 2000000
    },
    {
      "price": "114.471",
      "liquidity": 5000000
    },
    {
      "price": "114.469",
      "liquidity": 10000000
    }
  ],
  "instrument": "EUR_JPY",
  "time": "2016-10-17T12:25:40.837289374Z",
  "closeoutAsk": "114.494",
  "type": "PRICE",
}
{
  "type": "HEARTBEAT",
  "time": "2016-10-17T12:25:42.447922336Z"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "1.09966",
      "liquidity": 10000000
    },
    {
      "price": "1.09968",
      "liquidity": 10000000
    }
  ],
  "closeoutBid": "1.09949",
  "bids": [
    {
      "price": "1.09953",
      "liquidity": 10000000
    },
    {
      "price": "1.09951",
      "liquidity": 10000000
    }
  ],
  "instrument": "EUR_USD",
  "time": "2016-10-17T12:25:43.689619691Z",
  "closeoutAsk": "1.09970",
  "type": "PRICE"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "114.486",
      "liquidity": 1000000
    },
    {
      "price": "114.487",
      "liquidity": 2000000
    },
    {
      "price": "114.488",
      "liquidity": 5000000
    },
    {
      "price": "114.490",
      "liquidity": 10000000
    }
  ],
  "closeoutBid": "114.466",
  "bids": [
    {
      "price": "114.470",
      "liquidity": 1000000
    },
    {
      "price": "114.469",
      "liquidity": 2000000
    },
    {
      "price": "114.468",
      "liquidity": 5000000
    },
    {
      "price": "114.466",
      "liquidity": 10000000
    }
  ],
  "instrument": "EUR_JPY",
  "time": "2016-10-17T12:25:43.635964725Z",
  "closeoutAsk": "114.490",
  "type": "PRICE"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "10547.3",
      "liquidity": 25
    },
    {
      "price": "10547.5",
      "liquidity": 75
    },
    {
      "price": "10547.7",
      "liquidity": 150
    }
  ],
  "closeoutBid": "10546.9",
  "bids": [
    {
      "price": "10547.3",
      "liquidity": 25
    },
    {
      "price": "10547.1",
      "liquidity": 75
    },
    {
      "price": "10546.9",
      "liquidity": 150
    }
  ],
  "instrument": "DE30_EUR",
  "time": "2016-10-17T12:25:44.900162113Z",
  "closeoutAsk": "10547.7",
  "type": "PRICE"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "10547.0",
      "liquidity": 25
    },
    {
      "price": "10547.2",
      "liquidity": 75
    },
    {
      "price": "10547.4",
      "liquidity": 150
    }
  ],
  "closeoutBid": "10546.6",
  "bids": [
    {
      "price": "10547.0",
      "liquidity": 25
    },
    {
      "price": "10546.8",
      "liquidity": 75
    },
    {
      "price": "10546.6",
      "liquidity": 150
    }
  ],
  "instrument": "DE30_EUR",
  "time": "2016-10-17T12:25:44.963539084Z",
  "closeoutAsk": "10547.4",
  "type": "PRICE"
}
{
  "status": "tradeable",
  "asks": [
    {
      "price": "114.491",
      "liquidity": 1000000
    },
    {
      "price": "114.492",
      "liquidity": 2000000
    },
    {
      "price": "114.493",
      "liquidity": 5000000
    },
    {
      "price": "114.495",
      "liquidity": 10000000
    }
  ],
  "closeoutBid": "114.471",
  "bids": [
    {
      "price": "114.475",
      "liquidity": 1000000
    },
    {
      "price": "114.474",
      "liquidity": 2000000
    },
    {
      "price": "114.473",
      "liquidity": 5000000
    },
    {
      "price": "114.471",
      "liquidity": 10000000
    }
  ],
  "instrument": "EUR_JPY",
  "time": "2016-10-17T12:25:45.586100087Z",
  "closeoutAsk": "114.495",
  "type": "PRICE"
}

Top

About this software

The oanda-api-v20 software is a personal project. I have no prior or existing relationship with OANDA.

If you have any questions regarding this software, please take a look at the documentation first:

If you still have questions/issues you can open an issue on Gitub: https://github.com/hootnot/oanda-api-v20

Release files for oandapyV20 0.7.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for oandapyV20 0.7.2
File Size Uploaded
oandapyV20-0.7.2.tar.gz 51.6 kB Details

Release files / oandapyV20-0.7.2.tar.gz

Download URL oandapyV20-0.7.2.tar.gz
Size 51.6 kB
Tags Source
SHA-256 checksum
How to use checksums
5ca3990f824d0b16afd49d8e08f7403862d2b3c5a583470f777db7f76f04d073
BLAKE2b-256 checksum
How to use checksums
10f704f0ee293c979829cad5d8ee8d55df19f1dedf5cb4ebd35fb1e6b039fff4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.4.2 importlib_metadata/4.7.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.2 CPython/3.7.1

Release history Release notifications | RSS feed

This release

0.7.2 This release

1 release file

0.7.1

2 release files

0.7.0

1 release file

0.6.3

1 release file

0.6.2

1 release file

0.6.1

1 release file

0.6.0

1 release file

0.5.0

1 release file

0.4.5

1 release file

0.4.4

1 release file

0.4.3

1 release file

0.4.2

1 release file

0.4.1

1 release file

0.4.0

1 release file

0.3.0

1 release file

0.2.4

1 release file

0.2.3

1 release file

0.2.2

1 release file

0.2.1

1 release file

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page