├── .gitignore ├── README.md ├── get_trading_account_details.py ├── historical_data.py ├── instrument_candlestick_ohlc.py ├── order_management.py ├── orderbook.py ├── position_management.py ├── setup.py ├── streaming_prices.py ├── trade_management.py └── transaction_history.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | config/config.ini 3 | .vscode -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Oanda Python API "pretty" v20 API documentation 2 | > https://oanda-api-v20.readthedocs.io/en/latest/installation.html 3 | 4 | ## Understanding Oanda Python v20 API documentation 5 | > https://github.com/anthonyng2/FX-Trading-with-Python-and-Oanda/blob/master/Oanda%20v20%20REST-oandapyV20/01.02%20Understanding%20the%20Documentations.ipynb 6 | 7 | ## OrderBook info 8 | > OANDA OrderBook: 9 | > http://oanda-api-v20.readthedocs.io/en/latest/endpoints/instruments/instrumentorderbook.html 10 | 11 | ## Jupyter Notebook references 12 | > https://nbviewer.jupyter.org/github/anthonyng2/ 13 | 14 | ## OANDA REST API v20 specifications 15 | > developer.oanda.com/rest-live-v20/introduction/ 16 | 17 | ## FOREX Labs 18 | > Spread - not supported yet under V20. http://developer.oanda.com/rest-live-v20/forexlabs-ep/ 19 | > Commitment of Traders - not supported yet under V20. http://developer.oanda.com/rest-live-v20/forexlabs-ep/ 20 | > OrderBook - now supported under V20http://oanda-api-v20.readthedocs.io/en/latest/endpoints/instruments/instrumentorderbook.html 21 | 22 | ## API access token 23 | > should be contained in a ../config/config.ini file and accessed by importing the configparser library 24 | > A copy could be kept in a private gist. 25 | 26 | ## Backtesting ideas 27 | > https://www.quantstart.com/qstrader 28 | > QSTrader is a freely available backtesting and live trading engine written in Python. 29 | > It has been developed for both retail quant traders and institutional quant hedge funds to aid strategy, 30 | > development and deployment. It is currently under heavy development from a team of 31 | > volunteer software developers. Bugs are fixed and features are added each week, 32 | > leading to a robust, optimised institutional-grade algorithmic trading infrastructure. -------------------------------------------------------------------------------- /get_trading_account_details.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import oandapyV20 3 | import oandapyV20.endpoints.accounts as accounts 4 | import configparser 5 | 6 | # get account details 7 | config = configparser.ConfigParser() 8 | config.read('./config/config.ini') 9 | accountID = config['oanda']['account_id'] 10 | access_token = config['oanda']['api_key'] 11 | 12 | client = oandapyV20.API(access_token=access_token) 13 | 14 | # Get Basic Account Details 15 | r = accounts.AccountDetails(accountID) 16 | 17 | client.request(r) 18 | account_details = r.response 19 | print("\n raw account details: \n", account_details) 20 | # or 21 | account_table_format = pd.Series(r.response['account']) 22 | print(account_table_format) 23 | 24 | # Get Account List (for tracking multiple aub accounts) 25 | r = accounts.AccountList() 26 | all_acc_under_mgt = client.request(r) 27 | print("\n All Accounts under management: \n", all_acc_under_mgt) 28 | 29 | # Get Account Status Summary (are there open trades?... etc) 30 | r = accounts.AccountSummary(accountID) 31 | client.request(r) 32 | account_status_summary = pd.Series(r.response['account']) 33 | print("\n Summary of Account Status: \n", account_status_summary) 34 | 35 | # Instruments that can be traded with the specified account, 36 | # minimum Trailing Stop Distance, 37 | r = accounts.AccountInstruments(accountID=accountID, params="EUR_USD") 38 | client.request(r) 39 | account_instruments = pd.DataFrame(r.response['instruments']) 40 | print("\n List of tradable Account Instruments: \n", account_instruments) 41 | -------------------------------------------------------------------------------- /historical_data.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import oandapyV20 3 | import oandapyV20.endpoints.instruments as instruments 4 | import configparser 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config_v20.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | client = oandapyV20.API(access_token=access_token) 12 | 13 | """ 14 | extract candlestick data: 15 | http://developer.oanda.com/rest-live-v20/instrument-df/#CandlestickGranularity 16 | """ 17 | 18 | 19 | params = { 20 | "count": 250, 21 | "granularity": "D" 22 | } 23 | r = instruments.InstrumentsCandles(instrument="EUR_USD", params=params) 24 | 25 | client.request(r) # ouputs a large JSON object 26 | 27 | r.response['candles'][0]['mid'] # outputs an object with open, high, low, close information when on print() 28 | r.response['candles'][0]['time'] # outputs a time stamp on print() 29 | r.response['candles'][0]['volume'] # outputs the current volume value on print() 30 | 31 | dat = [] 32 | for oo in r.response['candles']: 33 | dat.append([oo['time'], oo['volume'], oo['mid']['o'], oo['mid']['h'], oo['mid']['l'], oo['mid']['c']]) 34 | 35 | # output time, open, high, low, close in a table 36 | df = pd.DataFrame(dat) 37 | df.columns = ['Time', 'Volume', 'Open', 'High', 'Low', 'Close'] 38 | df = df.set_index('Time') 39 | df.head() -------------------------------------------------------------------------------- /instrument_candlestick_ohlc.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import oandapyV20 3 | import oandapyV20.endpoints.instruments as instruments 4 | import configparser 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | client = oandapyV20.API(access_token=access_token) 12 | 13 | params = { 14 | "count": 5, 15 | "granularity": "M5" 16 | } 17 | 18 | r = instruments.InstrumentsCandles(instrument="DE30_EUR", params=params) 19 | 20 | client.request(r) 21 | print(r.response) # displays flat JSON object of OHLC, time, and volume 22 | 23 | candlestick_attrib = r.response['candles'] 24 | print(candlestick_attrib) # displays JSON object of OHLC, time, and volume data -------------------------------------------------------------------------------- /order_management.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import configparser 3 | import oandapyV20 4 | import oandapyV20.endpoints.orders as orders 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | client = oandapyV20.API(access_token=access_token) 12 | 13 | """ 14 | # 1 Create an order for an Account 15 | """ 16 | 17 | data = { 18 | "order": { 19 | "price": "1.2", 20 | "stopLossOnFill": { 21 | "timeInForce": "GTC", 22 | "price": "1.22" 23 | }, 24 | "timeInForce": "GTC", 25 | "instrument": "EUR_USD", 26 | "units": "-100", 27 | "type": "LIMIT", 28 | "positionFill": "DEFAULT" 29 | } 30 | } 31 | # r = orders.OrderCreate(accountID, data=data) 32 | # client.request(r) 33 | # create_order = pd.Series(r.response['orderCreateTransaction']) 34 | # print(create_order) 35 | 36 | 37 | """ 38 | # 2 Get a list of open orders for an account 39 | """ 40 | r = orders.OrderList(accountID) 41 | client.request(r) 42 | account_orders_list = pd.Series(r.response['orders'][0]) 43 | print(account_orders_list) 44 | 45 | 46 | """ 47 | # 3 List all Pending Orders in an Account, before you cancel an order (pending order == open order) 48 | use OrdersPending(), in lieu of the above OrderList() to cancel open orders 49 | """ 50 | r = orders.OrdersPending(accountID) 51 | client.request(r) 52 | res = r.response['orders'] 53 | last_order_id = res[0]['id'] # also used directly below to get details for a single order 54 | # print(res) 55 | 56 | display_all_pending_orders = pd.Series(r.response['orders'][0]) # or pd.Series(res['orders'][0]) 57 | print(display_all_pending_orders) 58 | 59 | """ 60 | # 4 Get Details for a Single Order in an Account 61 | """ 62 | r = orders.OrderDetails(accountID=accountID, orderID=last_order_id) 63 | client.request(r) 64 | single_order_details = r.response 65 | print(single_order_details) 66 | 67 | """ 68 | # 5 Replace an Order in an Account by simultaneously cancelling it and creating a replacement Order. 69 | """ 70 | replacement_order_data = { 71 | "order": { 72 | "units": "-500000", 73 | "instrument": "EUR_USD", 74 | "price": "1.25000", 75 | "type": "LIMIT" 76 | } 77 | } 78 | 79 | r = orders.OrderReplace(accountID=accountID, orderID=last_order_id, data=replacement_order_data) 80 | client.request(r) 81 | print(r.response) 82 | 83 | # store lastTransactionID here because it will be used below for cancellation 84 | last_order_id = r.response['lastTransactionID'] 85 | 86 | 87 | """ 88 | # 6 Cancel a pending Order in an Account. 89 | """ 90 | client.request(r) 91 | r = orders.OrderCancel(accountID=accountID, orderID=last_order_id) # taken from above 92 | print(r.response) 93 | 94 | """ 95 | # 7 Execute A Market Order 96 | """ 97 | market_order_data = {"order": 98 | {"units": "100", 99 | "instrument": "GBP_USD", 100 | "timeInForce": "FOK", 101 | "type": "MARKET", 102 | "positionFill": "DEFAULT" 103 | }, 104 | } 105 | 106 | r = orders.OrderCreate(accountID, data=market_order_data) 107 | client.request(r) 108 | print(r.response) 109 | 110 | """ 111 | # 8 order confirmation output 112 | """ 113 | print(r.response) 114 | orderCreateTransaction = pd.Series(r.response['orderCreateTransaction']) 115 | orderFillTransaction = pd.Series(r.response['orderFillTransaction']) 116 | print(orderCreateTransaction) 117 | print(orderFillTransaction) 118 | 119 | -------------------------------------------------------------------------------- /orderbook.py: -------------------------------------------------------------------------------- 1 | import oandapyV20 2 | import oandapyV20.endpoints.instruments as instruments 3 | import configparser 4 | 5 | config = configparser.ConfigParser() 6 | config.read('./config/config_v20.ini') 7 | accountID = config['oanda']['account_id'] 8 | access_token = config['oanda']['api_key'] 9 | 10 | client = oandapyV20.API(access_token=access_token) 11 | """ 12 | # r = instruments.InstrumentsOrderBook(instrument="EUR_USD", params=params) 13 | params are an optional request query parameters, check developer.oanda.com for details 14 | """ 15 | params = { 16 | # optional values 17 | } 18 | 19 | r = instruments.InstrumentsOrderBook(instrument="EUR_USD") 20 | client.request(r) -------------------------------------------------------------------------------- /position_management.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import oandapyV20 3 | import oandapyV20.endpoints.positions as positions 4 | import configparser 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config_v20.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | client = oandapyV20.API(access_token=access_token) 12 | 13 | 14 | """ 15 | # 1 List all Positions included pending limit orders AND currently active market orders to the account and their P/L status 16 | """ 17 | r = positions.PositionList(accountID=accountID) 18 | client.request(r) 19 | # print(client.request(r)) 20 | 21 | """ 22 | # 2 List all current OPEN market order positions with current trades and their P/L status. 23 | """ 24 | r = positions.OpenPositions(accountID=accountID) 25 | client.request(r) 26 | 27 | """ 28 | # 3 Get the details of a SINGLE instrument’s current open position in an Account (excellent for automating/assessing risk-management strategies) 29 | """ 30 | instrument = "AUD_USD" 31 | r = positions.PositionDetails(accountID=accountID, instrument=instrument) 32 | client.request(r) 33 | 34 | """ 35 | # 4 Closeout ALL open positions for an instrument in an Account. 36 | """ 37 | data = { 38 | "longUnits": "ALL", 39 | "shortUnits": "ALL" 40 | } 41 | r = positions.PositionClose(accountID=accountID, instrument=instrument, data=data) 42 | client.request(r) 43 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import configparser 3 | import oandapyV20 4 | from oandapyV20 import API 5 | import oandapyV20.endpoints.pricing as pricing 6 | 7 | config = configparser.ConfigParser() 8 | config.read('./config/config.ini') 9 | accountID = config['oanda']['account_id'] 10 | access_token = config['oanda']['api_key'] 11 | api = API(access_token=access_token) 12 | 13 | # Get rates information 14 | params = {"instruments": "EUR_USD,USD_JPY"} 15 | 16 | r = pricing.PricingInfo(accountID=accountID, params=params) 17 | 18 | rv = api.request(r) 19 | 20 | print(r.response) 21 | 22 | prices = pd.DataFrame(r.response['prices']) 23 | 24 | print(prices) 25 | 26 | -------------------------------------------------------------------------------- /streaming_prices.py: -------------------------------------------------------------------------------- 1 | import json 2 | from oandapyV20 import API 3 | from oandapyV20.exceptions import V20Error 4 | from oandapyV20.endpoints.pricing import PricingStream 5 | import configparser 6 | 7 | 8 | config = configparser.ConfigParser() 9 | config.read('./config/config.ini') 10 | accountID = config['oanda']['account_id'] 11 | access_token = config['oanda']['api_key'] 12 | 13 | api = API(access_token=access_token, environment="practice") 14 | 15 | instruments = "EUR_USD,USD_JPY" 16 | s = PricingStream(accountID=accountID, params={"instruments": instruments}) 17 | 18 | # format output to JSON 19 | try: 20 | n = 0 21 | for R in api.request(s): 22 | print(json.dumps(R, indent=2)) 23 | n += 1 24 | if n > 5: 25 | print("done") 26 | break 27 | 28 | except V20Error as e: 29 | print("Error: {}".format(e)) -------------------------------------------------------------------------------- /trade_management.py: -------------------------------------------------------------------------------- 1 | import pandas as py 2 | import oandapyV20 3 | import oandapyV20.endpoints.trades as trades 4 | import configparser 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config_v20.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | """ 12 | # 1 Get list of current trades for an account 13 | """ 14 | client = oandapyV20.API(access_token=access_token) 15 | 16 | params = { 17 | "instrument": "DE30_EUR,EUR_USD" 18 | } 19 | 20 | # params are optional. use to retrieve trades for specific currency pair(s) 21 | r = trades.TradesList(accountID=accountID, params=params) 22 | client.request(r) 23 | print(r.response) 24 | 25 | 26 | """ 27 | # 2 Get the list of open Trades for an Account.¶ 28 | """ 29 | r = trades.OpenTrades(accountID) 30 | client.request(r) 31 | trade_id = r.response['trades'][0]['id'] 32 | print(trade_id) 33 | 34 | 35 | """ 36 | # 3 Get the details of a specific Trade in an Account. 37 | """ 38 | r = trades.TradeDetails(accountID, tradeID=trade_id) 39 | client.request(r) 40 | print(r.response) 41 | 42 | 43 | """ 44 | # 4 Close (partially or fully) a specific open Trade in an Account. 45 | """ 46 | # close JUST PART of position 47 | data = { 48 | "units": 100 # this specifies the size of position to close, assuming the size is greater than 100 units 49 | } 50 | r = trades.TradeClose(accountID, tradeID=trade_id, data=data) 51 | client.request(r) 52 | 53 | # close ENTIRE position 54 | r = trades.TradeClose(accountID, tradeID=trade_id) 55 | client.request(r) -------------------------------------------------------------------------------- /transaction_history.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import oandapyV20 3 | import oandapyV20.endpoints.transactions as trans 4 | import configparser 5 | 6 | config = configparser.ConfigParser() 7 | config.read('./config/config_v20.ini') 8 | accountID = config['oanda']['account_id'] 9 | access_token = config['oanda']['api_key'] 10 | 11 | client = oandapyV20.API(access_token=access_token) 12 | 13 | 14 | """ 15 | # 1 Get transaction list 16 | """ 17 | r = trans.TransactionList(accountID) 18 | client.request(r) 19 | 20 | params = { 21 | "type": "MARKET_ORDER" 22 | } 23 | r = trans.TransactionList(accountID, params=params) 24 | client.request(r) 25 | 26 | """ 27 | # 2 Get the details of a single transaction (they can be extracted individually as a pandas dataframe and stored in a database) 28 | """ 29 | r = trans.TransactionDetails(accountID=accountID, transactionID=49) 30 | client.request(r) 31 | 32 | # iterate through output of transactions 33 | for oo in range(10, 20): 34 | r = trans.TransactionDetails(accountID=accountID, transactionID=oo) 35 | print(client.request(r)) 36 | 37 | --------------------------------------------------------------------------------