async_v20: Asynchronous OANDA v20 client
========================================
*A foreign exchange client*
.. image:: https://raw.githubusercontent.com/jamespeterschinner/async_v20/master/doc/source/_static/async_v20-icon-128x128.png
:height: 64px
:width: 64px
:alt: async-v20 logo
.. image:: https://travis-ci.org/jamespeterschinner/async_v20.svg?branch=master
:target: https://travis-ci.org/jamespeterschinner/async_v20
:align: right
.. image:: https://codecov.io/gh/jamespeterschinner/async_v20/branch/master/graph/badge.svg
:target: https://codecov.io/gh/jamespeterschinner/async_v20
.. image:: https://readthedocs.org/projects/async-v20/badge/?version=latest
:target: http://async-v20.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
Documentation
-------------
http://async-v20.readthedocs.io/
Disclaimer
----------
- Losses can exceed investment.
- async_v20 and its creator has no affiliation with OANDA. And is not endorsed by OANDA in any manner.
- async_v20 is in Beta stage and has not been tested on a live OANDA account
- **Use at own risk**
Features
---------
- Exposes the entire v20 API
- No `*args, **kwargs` In client methods. Therefore no need to guess what arguments a method takes
- Serialize objects directly into *pandas* **Series** or **DataFrame** objects
- Construct *concurrent* trading algorithms
installation
------------
**REQUIRES:**
python >= 3.6
https://www.python.org/
$ pip install async_v20
`async_v20` is built with `aiohttp <https://github.com/aio-libs/aiohttp>`_.
It is therefore recommended to also install `cchardet` and `aiodns` as per `aiohttp`
`documentation <http://aiohttp.readthedocs.io/en/stable/>`_
.. code-block:: bash
$ pip install cchardet
$ pip install aiodns
Why async_v20?
--------------
There are many OANDA clients for python already available so why create another?
The main driver for creating async_v20 was to facilitate better risk management,
by allowing user's to concurrently monitor account status and trade currency's.
An unintended consequence of async_v20 is the ability to create clear segregation
between implementation ideas.
A simple example might contain a coroutine for the following:
- Monitoring overall account status
- Watching price stream and triggering buy/sell signals
- Monitoring individual trades and closing movements against held positions
A synchronous implementation would require considerable effort to determine which
task communicates with the server next. async_v20 removes this burden by using
`aiohttp <https://github.com/aio-libs/aiohttp>`_
Further goals of async_v20 has been to lower the barrier of entry for algorithmic trading
by providing a complete and simple to use interface.
Getting started
===============
Creating an Account
-------------------
To use `async_v20` you must have an account with *OANDA*
- Create an account
https://www.oanda.com/account/login
- Create an API *token*
https://www.oanda.com/demo-account/tpa/personal_token
Setting up environment
----------------------
- Install `async_v20` as per **installation**
- Create a new *environment variable* with the name 'OANDA_TOKEN' and value as the *token* generated from above
**Note:**
- It is considered best practice use a *virtual environment*
- It is not required to store the token in an *environment variable*. The token can be passed to OandaClient
Using async_v20
---------------
Once an account has been created as per *create-account*
and the environment is configured as per *setting-up-environment*
we are ready to begin.
Lets first take a look at this code example, then go though it line by line.
.. code-block:: python
import asyncio
from async_v20 import OandaClient
async def get_account():
async with OandaClient() as client:
return await client.account()
loop = asyncio.get_event_loop()
account = loop.run_until_complete(get_account())
# pandas Series
print(account.series())
# HTTP response state
print(account)
# JSON data in python dictionary format
print(account.dict())
First we need to import *asyncio* this allows us to run our *coroutine*
.. code-block:: python
import asyncio
We then import *OandaClient* which provides us the means to interact with OANDA
.. code-block:: python
from async_v20 import OandaClient
Because *OandaClient* returns *coroutines* we use *async def*. This allows the use of the *await* syntax
.. code-block:: python
async def get_account():
*OandaClient* is a *context manager*, we use *async with* to instantiate a
client instance. Doing so will automatically close the *http session* when we're done
.. code-block:: python
async with OandaClient() as client:
We then create and *run* the *coroutine* by calling *client*. **account()**
.. code-block:: python
return await client.account()
Now we have defined our *coroutine* we need to execute it.
To do so we need an event loop. Achieved using *asyncio*. **get_event_loop()**
.. code-block:: python
loop = asyncio.get_event_loop()
The value returned by executing the `account()` *coroutine* is accessed through the event loop.
.. code-block:: python
account = loop.run_until_complete(get_account())
`async_v20` objects have a **series()** method that returns a `pandas`. **Series**.
By default `async_v20`. **DateTime**'s will be converted into `pandas`. **Timestamp**'s.
.. code-block:: python
print(account.series())
**Outputs**
.. code-block:: python
alias Primary
balance 97801.9
commission 0
created_by_user_id 1234567
created_time 2017-08-11 15:04:31.639182352+00:00
currency AUD
financing -3.5596
hedging_enabled False
id 123-123-1234567-123
last_margin_call_extension_time None
last_transaction_id 6360
margin_available 97801.9
margin_call_enter_time None
margin_call_extension_count None
margin_call_margin_used 0
margin_call_percent 0
margin_closeout_margin_used 0
margin_closeout_nav 97801.9
margin_closeout_percent 0
margin_closeout_position_value 0
margin_closeout_unrealized_pl 0
margin_rate 0.02
margin_used 0
nav 97801.9
open_position_count 0
open_trade_count 0
orders []
pending_order_count 0
pl -2194.53
position_value 0
positions []
resettable_pl -2194.53
resettabled_pl_time None
trades []
unrealized_pl 0
withdrawal_limit 97801.9
dtype: object
Changelog
=========
8.0.0b0 (01/01/2019)
====================
- Added guaranteedStopLossOrderRestriction to Instrument definition
- Added unixTime attribute to orderBook definition
- Added tags attribute to Instrument definition
- Added guaranteed to StopLossOrder definition
- Replaced ellipsis (...) with sentinel object
- Added fullVWAP to Transaction definition
- UnknownValue Exception is now replaced with UnknownKeywordArgument Warning
in order to make the client more robust to breaking changes from OANDA
- Checked many class definitions against updated OANDA documentation
7.0.1b0
=======
- OandaClient.close is now a coroutine
7.0.0b0
=======
- All OrderRequests now require an `instrument` as there first parameter.
This is to allow correct rounding of decimal numbers to the instruments specification.
- `guaranteed: bool` has been added to Transaction definition.
- Default SinceTransactionID value is now limited to the passed 900 transactions.
- `OandaClient.account()` method will use `get_account_details()` method when default
`SinceTransactionID` has expired.
- `OandaClient.get_pricing` now accepts an `InstrumentName` for `instruments`
6.2.2b0
=======
- `guaranteed: bool` has been added to Order definition.
6.2.1b0
=======
- Array class lazy instantiates objects
- Model objects with same attributes now compare equal (proving equal DateTime format)
- Improved repr of Array objects
- Improved implementation of `get_instrument`/s, `get_id`
- All Array objects have `get_instrument` and `get_instruments`. Returns either the first or all matching objects
- Removed Model class `_template` attribute as it was obsolete
- Added `get(self, name, default=None)` method to Model to allow for dict like key access.
6.2.0b0
=======
- Changed `get_transactions` method to `get_transaction` (API call can only return one transaction)
- Removed default parameter argument for `get_transaction`.(**transaction_id**) it is a required parameter.
- Made OandaClient.hosts a protected member. Now OandaClient._hosts
- `Array`. **get_instruments()** returns an Array of the same type this is when there is a one to many relationship
between the instrument and objects the array contains. `Array`. **get_instrument()** Returns the single object
when there is a one to one relationship. Both methods are mutually exclusive
- All objects now lazy instantiate attribute instances.
- Large refactoring of async_v20.definitions.base
6.1.0b0
=======
- Changed incorrect naming _i_ds to _ids
- Fixed OrderRequest formatting when a OrderRequest instance is passed that defines an instrument.
6.0.2b0
=======
- Fixed InvalidFormatRequest Error when OrderRequest does not define an `instrument` attribute
6.0.1b0
=======
- Fixed incorrect handling of stream response when endpoint returned status 400
6.0.0b0
=======
- Added health API and initialization check
- Fixed stream_transactions. Now returns correct type
- Changed streaming responses keys. Where stream_pricing() returned 'PRICE' or 'HEARTBEAT'
response now contains 'price' or 'heartbeat'. Like wise stream_transactions() now returns
'transaction' or 'heartbeat'. This has been done to standardise access to the transaction
stream response and 'heartbeat' objects. Due to the different types these objects may take.
- Added async_v20.exceptions module
- Added logging to modules
5.0.3b0
=======
- Added default argument to Array.get_id & Array.get_instrument
- Removed default value from get_candles(*count*)
5.0.2b0
=======
- Fixed bug in rest implementation. Positions now update correctly
5.0.1b0
=======
- OandaClient.datetime_format is read only
- OandaClient._instruments is no longer a protected attribute. It is now OandaClient.instruments
5.0.0b0
=======
- DateTime's create pandas.Timestamp's
- Model.dict argument `datetime` is now `datetime_format`. Argument behaviour now
specifies the representation of times. Either `RFC3339` or `UNIX`. Corresponding `json` argument
changes the representation of UNIX times to either a `str` or `numpy.int64`
- *Response* .json() accepts `datetime_format` argument string
4.0.0b0
=======
- Changed get_positions to get_position (as method can only close one position)
- _in_context accepts negative units
3.0.0b0
=======
- Array.get_instrument() works with ArrayInstrument
- OandaClient.initialize() gets account instruments
- OandaClient has `format_order_request` attribute
- async_v20.definitions.primitives.Unit has been removed
- PriceValue and Decimal number has additional method `format(precision, min_, max_)`
2.3.0b0
=======
- Updated limit_replace_order() method to expose all arguments
- TransactionID, TradeID & OrderID get stored as integers and cast to strings when creating JSON
representations
- Added documentation for order API methods
2.2.5b2
=======
- Fixed get_candles default value
2.2.5b1
=======
- RejectTransactions have no required arguments
- API methods now apply default values
- Added undocumented attributes
- Path class has been removed in favour of helper function. Allowing for more useful
error message on failure.
2.2.5b0
=======
- PriceComponent accepts all combinations of 'M' 'A' 'B'
2.2.4b3
=======
Added attributes to TradeSummary:
- margin_used
2.2.4b1
=======
Added attributes to TradeReduce:
- guaranteed_execution_fee
2.2.4b0
=======
Added attributes to Transaction:
- guaranteed_execution_fee
- gain_quote_home_conversion_factor
- loss_quote_home_conversion_factor
Added attributes to TradeOpen:
- price
- guaranteed_execution_fee
2.2.3b0
=======
- Added 'margin_used' to Position object.
(OANDA added new attribute, causing error)
- Added TimeoutError to stream
2.2.2b0
=======
- Added get_position_book and get_order_book API calls
2.2.1b0
=======
- series() method converts both UNIX and RFC3339 time's to pandas.Timestamp 's
2.2.0b0
=======
- Initialization doesn't freeze after failure
- Order methods exposes all arguments
2.1.0b0
=======
- Beta release. At present time client is considered feature full
with 100% test coverage
- _fields attribute stored on instance not class
- RESTful account() method added
- close_all_trades() method added
- Added replace() method to Model
- Simplified Endpoint decorator (No serial requests)
- Changes close_trades to close_trade (Method can only close one trade)
- Response parser checks HTTP status first
- Added tests
2.0.1a0
=======
- `type` argument is set automatically for subclass that define it
- implementation improvements
2.0.0a0
=======
- async_v20 objects are now immutable (greatly reducing complexity)
- Objects now have a repr
- removed inflection as a dependency
- Higher test coverage
1.1.6a0
=======
- Issue with object serialization not working with lists of Type[str, float, int]
1.1.5a4
=======
- Argument passing
1.1.5a3
=======
- Fix long description on PyPI
1.1.5a0
=======
- method signatures were offset buy 1 argument due to handling of
'self' parameter. Methods now displaying correct signature
1.1.4a0
=======
- Fixed incorrect annotation on:
- PUTPositionsInstrumentClose
- GETPositionsInstrument
1.1.3a0
=======
- Fixed incorrect annotation on Interface methods
- Fixed argument passing bug caused by false'y evaluation
1.1.2a5
=======
- Added Travis CI
- Added Codecov
1.1.2a4
=======
- Additional documentation
1.1.2a1
=======
- OandaClient.initialize() method is now exposed
- OandaClient is now also a context manager. To automatically close the http session
- Additional documentation
1.1.1a1
=======
- Floating point numbers are rounded to the correct accuracy required for correct
serialization.
1.1.0a1
=======
- Model.series() returns data in more specific types instead of all 'str'
- OandaClient methods now have correct signature instead of args, kwargs
1.0.1a1
=======
- Fixed code examples in bin directory