Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/hootnot/oanda-api-v20
OANDA REST-V20 API wrapper. Easy access to OANDA's REST v20 API with oandapyV20 package. Checkout the Jupyter notebooks!
https://github.com/hootnot/oanda-api-v20
finance forex oanda rest-api trading trading-bot v20 v20-python
Last synced: 1 day ago
JSON representation
OANDA REST-V20 API wrapper. Easy access to OANDA's REST v20 API with oandapyV20 package. Checkout the Jupyter notebooks!
- Host: GitHub
- URL: https://github.com/hootnot/oanda-api-v20
- Owner: hootnot
- License: mit
- Created: 2016-07-09T09:15:33.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2023-12-11T21:52:49.000Z (about 1 year ago)
- Last Synced: 2024-12-14T12:16:23.321Z (8 days ago)
- Topics: finance, forex, oanda, rest-api, trading, trading-bot, v20, v20-python
- Language: Python
- Homepage:
- Size: 442 KB
- Stars: 405
- Watchers: 47
- Forks: 107
- Open Issues: 6
-
Metadata Files:
- Readme: README.rst
- Changelog: CHANGELOG.rst
- License: LICENSE.md
Awesome Lists containing this project
README
OANDA REST-V20 API wrapper
==========================.. _Top:
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/... image:: https://travis-ci.org/hootnot/oanda-api-v20.svg?branch=master
:target: https://travis-ci.org/hootnot/oanda-api-v20
:alt: Build.. image:: https://readthedocs.org/projects/oanda-api-v20/badge/?version=latest
:target: http://oanda-api-v20.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status.. image:: https://landscape.io/github/hootnot/oanda-api-v20/master/landscape.svg?style=flat
:target: https://landscape.io/github/hootnot/oanda-api-v20/master
:alt: Code Health.. image:: https://coveralls.io/repos/github/hootnot/oanda-api-v20/badge.svg?branch=master
:target: https://coveralls.io/github/hootnot/oanda-api-v20?branch=master
:alt: Coverage.. image:: https://badge.fury.io/py/oandapyV20.svg
:target: https://badge.fury.io/py/oandapyV20
:alt: Pypi.. image:: https://img.shields.io/pypi/pyversions/oandapyV20.svg
:target: https://pypi.org/project/oandapyV20
:alt: Python versions.. image:: https://api.codacy.com/project/badge/Grade/5946514e3a7c407291f76e630ce3553b
:target: https://www.codacy.com/app/hootnot/oandaapiv20utm_source=github.com&utm_medium=referral&utm_content=hootnot/oanda-api-v20&utm_campaign=Badge_Grade
:alt: CodacyInteractive
-----------.. image:: https://jupyter.readthedocs.io/en/latest/_static/_images/jupyter.svg
:target: ./jupyter
:alt: JupyterUsing the Jupyter `notebook`_ it is easy to play with the
*oandapyV20* library... _notebook: ./jupyter/index.ipynb
TOC
---+ `Install`_
+ `Design`_
+ `Client`_
- `contrib.requests`_
- `contrib.factories`_
- `API-endpoint access`_
- `Placing a MarketOrder with TakeProfitOrder and StopLossOrder`_
- `Processing series of requests`_
- `Streaming endpoints`_Install
-------.. code-block:: bash
$ pip install oandapyV20
or the latest development version from github:
.. code-block:: bash
$ pip install git+https://github.com/hootnot/oanda-api-v20.git
If you want to run the tests, clone the repository:
.. code-block:: bash
$ 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 installExamples 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)... code-block:: python
mktOrder = MarketOrderRequest(instrument="EUR_USD",
units=10000,
takeProfitOnFill=TakeProfitDetails(price=1.10).data,
stopLossOnFill=StopLossDetails(price=1.07).data
).datainstead of:
.. code-block:: python
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:.. code-block:: python
import sys
import jsonfrom oandapyV20.contrib.factories import InstrumentsCandlesFactory
from oandapyV20 import APIaccess_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:
.. code-block:: shell
$ 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*:.. code-block:: shell
$ 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,1454Top_
Examples
--------API-endpoint access
~~~~~~~~~~~~~~~~~~~.. code-block:: python
import json
from oandapyV20 import API # the client
import oandapyV20.endpoints.trades as tradesaccess_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*
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.. code-block:: python
import json
from oandapyV20.contrib.requests import MarketOrderRequest
from oandapyV20.contrib.requests import TakeProfitDetails, StopLossDetailsimport oandapyV20.endpoints.orders as orders
import oandapyV20from 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.10mktOrder = 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:.. code-block:: python
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 pricingaccess_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
``````.. code-block:: json
{
"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:
.. code-block:: python
import json
from oandapyV20 import API
from oandapyV20.exceptions import V20Error
from oandapyV20.endpoints.pricing import PricingStreamaccountID = "..."
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
``````.. code-block:: json
{
"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:* oandapyV20 : http://oanda-api-v20.readthedocs.io/en/latest/?badge=latest
* OANDA developer docs : http://developer.oanda.com
* examples : https://github.com/hootnot/oandapyV20-examples
* Github: https://github.com/hootnot/oanda-api-v20 check the open and closed issuesIf you still have questions/issues you can open an *issue* on Gitub: https://github.com/hootnot/oanda-api-v20