Package: oandapyV20
As of march 2018 OANDA no longer supports the v1 REST-API. The only pending
V20 endpoint was theforexlabs endpoint. Instead of launchingforexlabsas aV20-endpoint, OANDA choose to support this endpoint from the v1
REST interface, see:http://developer.oanda.com/rest-live-v20/forexlabs-ep/.InteractiveUsing the Jupyternotebookit is easy to play with theoandapyV20library.TOCInstallDesignClientcontrib.requestscontrib.factoriesAPI-endpoint accessPlacing a MarketOrder with TakeProfitOrder and StopLossOrderProcessing series of requestsStreaming endpointsInstall$pipinstalloandapyV20or the latest development version from github:$pipinstallgit+https://github.com/hootnot/oanda-api-v20.gitIf you want to run the tests, clone the repository:$gitclonehttps://github.com/hootnot/oanda-api-v20$cdoanda-api-v20# install necessary packages for testing$grep"\- pip install".travis.yml|>whilereadLNE>do`echo$LNE|cut-c2-`;done$pythonsetup.pytest$pythonsetup.pyinstallExamples are provided in thehttps://github.com/hootnot/oandapyV20-examplesrepository.DesignIn 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.TopClientThe V20-library has a client class (API) which processes APIRequest objects.Topcontrib.requestsThe contrib.request package offers classes providing an easy way
to construct the data for thedataparameter 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).datainsteadof: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'}}}Topcontrib.factoriesThe 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.TheInstrumentsCandlesFactorysolves this by generating the requests for you,
example:importsysimportjsonfromoandapyV20.contrib.factoriesimportInstrumentsCandlesFactoryfromoandapyV20importAPIaccess_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}defcnv(r,h):forcandleinr.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'],)exceptExceptionase:print(e,r)else:h.write(rec+"\n")withopen("/tmp/{}.{}.out".format(instr,gran),"w")asO:forrinInstrumentsCandlesFactory(instrument=instr,params=params):print("REQUEST:{}{}{}".format(r,r.__class__.__name__,r.params))rv=client.request(r)cnv(r.response,O)When running this:$pythonoandahist.py2017-01-01T00:00:00Z2017-06-30T00:00:00ZH4EUR_USDREQUEST:v3/instruments/EUR_USD/candlesInstrumentsCandles{'to':'2017-03-25T08:00:00Z','from':'2017-01-01T00:00:00Z','granularity':'H4'}REQUEST:v3/instruments/EUR_USD/candlesInstrumentsCandles{'to':'2017-06-16T20:00:00Z','from':'2017-03-25T12:00:00Z','granularity':'H4'}REQUEST:v3/instruments/EUR_USD/candlesInstrumentsCandles{'to':'2017-06-30T00:00:00Z','from':'2017-06-17T00:00:00Z','granularity':'H4'}The output shows it processed threeInstrumentsCandlesrequests. 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,15342017-06-28T05:00:0,True,1.13465,1.13882,1.13454,1.13603,84862017-06-28T09:00:0,True,1.13606,1.13802,1.12918,1.13315,128152017-06-28T13:00:0,True,1.13317,1.13909,1.13283,1.13781,132552017-06-28T17:00:0,True,1.13783,1.13852,1.13736,1.13771,21042017-06-28T21:00:0,True,1.13789,1.13894,1.13747,1.13874,1454TopExamplesAPI-endpoint accessimportjsonfromoandapyV20importAPI# the clientimportoandapyV20.endpoints.tradesastradesaccess_token="..."accountID="..."client=API(access_token=access_token)# request trades listr=trades.TradesList(accountID)rv=client.request(r)print("RESPONSE:\n{}".format(json.dumps(rv,indent=2)))TopPlacing aMarketOrderwithTakeProfitOrderandStopLossOrderimportjsonfromoandapyV20.contrib.requestsimportMarketOrderRequestfromoandapyV20.contrib.requestsimportTakeProfitDetails,StopLossDetailsimportoandapyV20.endpoints.ordersasordersimportoandapyV20fromexampleauthimportexampleAuthaccountID,access_token=exampleAuth()api=oandapyV20.API(access_token=access_token)# EUR_USD (today 1.0750)EUR_USD_STOP_LOSS=1.07EUR_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 requestr=orders.OrderCreate(accountID,data=mktOrder.data)try:# create the OrderCreate requestrv=api.request(r)exceptoandapyV20.exceptions.V20Erroraserr:print(r.status_code,err)else:print(json.dumps(rv,indent=2))TopProcessing series of requestsProcessing series of requests is also possible now by storing different requests in
an array or from some ‘request-factory’ class. Below an array example:importjsonfromoandapyV20importAPI# the clientfromoandapyV20.exceptionsimportV20ErrorimportoandapyV20.endpoints.accountsasaccountsimportoandapyV20.endpoints.tradesastradesimportoandapyV20.endpoints.pricingaspricingaccess_token="..."accountID="..."client=API(access_token=access_token)# list of requestslor=[]# request trades listlor.append(trades.TradesList(accountID))# request accounts listlor.append(accounts.AccountList())# request pricing infoparams={"instruments":"DE30_EUR,EUR_GBP"}lor.append(pricing.PricingInfo(accountID,params=params))forrinlor:try:rv=client.request(r)# put request and response in 1 JSON structureprint("{}".format(json.dumps({"request":"{}".format(r),"response":rv},indent=2)))exceptV20Errorase: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"}]}}TopStreaming endpointsStreaming quotes: use pricing.PricingStream.
Streaming transactions: use transactions.TransactionsEvents.To fetch streaming data from a stream use the following pattern:importjsonfromoandapyV20importAPIfromoandapyV20.exceptionsimportV20ErrorfromoandapyV20.endpoints.pricingimportPricingStreamaccountID="..."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=0forRinapi.request(s):print(json.dumps(R,indent=2))n+=1ifn>10:s.terminate("maxrecs received:{}".format(MAXREC))exceptV20Errorase: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"}TopAbout this softwareTheoanda-api-v20software 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=latestOANDA developer docs :http://developer.oanda.comexamples :https://github.com/hootnot/oandapyV20-examplesGithub:https://github.com/hootnot/oanda-api-v20check the open and closed issuesIf you still have questions/issues you can open anissueon Gitub:https://github.com/hootnot/oanda-api-v20