text: # encoding: utf-8
  
  import json
  import requests
  from Queue import Queue, Empty
  from threading import Thread
  
  
  API_SETTING = {}
  API_SETTING['practice'] = {'rest': 'https://api-fxpractice.oanda.com',
                             'stream': 'https://stream-fxpractice.oanda.com'}
  API_SETTING['trade'] = {'rest': 'https://api-fxtrade.oanda.com',
                          'stream': 'https://stream-fxtrade.oanda.com/'}
  
  
  FUNCTIONCODE_GETINSTRUMENTS = 0
  FUNCTIONCODE_GETPRICES = 1
  FUNCTIONCODE_GETPRICEHISTORY = 2
  FUNCTIONCODE_GETACCOUNTS = 3
  FUNCTIONCODE_GETACCOUNTINFO = 4
  FUNCTIONCODE_GETORDERS = 5
  FUNCTIONCODE_SENDORDER = 6
  FUNCTIONCODE_GETORDERINFO = 7
  FUNCTIONCODE_MODIFYORDER = 8
  FUNCTIONCODE_CANCELORDER = 9
  FUNCTIONCODE_GETTRADES = 10
  FUNCTIONCODE_GETTRADEINFO = 11
  FUNCTIONCODE_MODIFYTRADE= 12
  FUNCTIONCODE_CLOSETRADE = 13
  FUNCTIONCODE_GETPOSITIONS = 14
  FUNCTIONCODE_GETPOSITIONINFO= 15
  FUNCTIONCODE_CLOSEPOSITION = 16
  FUNCTIONCODE_GETTRANSACTIONS = 17
  FUNCTIONCODE_GETTRANSACTIONINFO = 18
  FUNCTIONCODE_GETACCOUNTHISTORY = 19
  FUNCTIONCODE_GETCALENDAR = 20
  FUNCTIONCODE_GETPOSITIONRATIOS = 21
  FUNCTIONCODE_GETSPREADS = 22
  FUNCTIONCODE_GETCOMMIMENTS = 23
  FUNCTIONCODE_GETORDERBOOK = 24
  FUNCTIONCODE_GETAUTOCHARTIST = 25
  FUNCTIONCODE_STREAMPRICES = 26
  FUNCTIONCODE_STREAMEVENTS = 27
  
  
  ########################################################################
  class OandaApi(object):
      """"""
      DEBUG = False
  
      #----------------------------------------------------------------------
      def __init__(self):
          """Constructor"""
          self.token = ''
          self.accountId = ''
          self.headers = {}
          self.restDomain = ''
          self.streamDomain = ''
          self.session = None
          
          self.functionSetting = {}
          
          self.active = False         # API的工作状态
          
          self.reqID = 0              # 请求编号
          self.reqQueue = Queue()     # 请求队列
          self.reqThread = Thread(target=self.processQueue)   # 请求处理线程
          
          self.streamPricesThread = Thread(target=self.processStreamPrices)   # 实时行情线程
          self.streamEventsThread = Thread(target=self.processStreamEvents)   # 实时事件线程（成交等）
          
      #----------------------------------------------------------------------
      def init(self, settingName, token, accountId):
          """初始化接口"""
          self.restDomain = API_SETTING[settingName]['rest']
          self.streamDomain = API_SETTING[settingName]['stream']
          self.session = requests.Session()
          
          self.token = token
          self.accountId = accountId
          
          self.headers['Authorization'] = 'Bearer ' + self.token
          
          self.initFunctionSetting(FUNCTIONCODE_GETINSTRUMENTS, {'path': '/v1/instruments',
                                                            'method': 'GET'})
          
          self.initFunctionSetting(FUNCTIONCODE_GETPRICES, {'path': '/v1/prices',
                                                            'method': 'GET'})        
          
          self.initFunctionSetting(FUNCTIONCODE_GETPRICEHISTORY, {'path': 'v1/candles',
                                                                  'method': 'GET'})
          
          self.initFunctionSetting(FUNCTIONCODE_GETACCOUNTS, {'path': '/v1/accounts',
                                                            'method': 'GET'})  
          
          self.initFunctionSetting(FUNCTIONCODE_GETACCOUNTINFO, {'path': '/v1/accounts/%s' %self.accountId,
                                                            'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETORDERS, {'path': '/v1/accounts/%s/orders' %self.accountId,
                                                            'method': 'GET'})        
          
          self.initFunctionSetting(FUNCTIONCODE_SENDORDER, {'path': '/v1/accounts/%s/orders' %self.accountId,
                                                            'method': 'POST'})     
          
          self.initFunctionSetting(FUNCTIONCODE_GETORDERINFO, {'path': '/v1/accounts/%s/orders' %self.accountId,
                                                            'method': 'GET'})     
          
          self.initFunctionSetting(FUNCTIONCODE_MODIFYORDER, {'path': '/v1/accounts/%s/orders' %self.accountId,
                                                            'method': 'PATCH'})    
          
          self.initFunctionSetting(FUNCTIONCODE_CANCELORDER, {'path': '/v1/accounts/%s/orders' %self.accountId,
                                                            'method': 'DELETE'})             
          
          self.initFunctionSetting(FUNCTIONCODE_GETTRADES, {'path': '/v1/accounts/%s/trades' %self.accountId,
                                                            'method': 'GET'})        
  
          self.initFunctionSetting(FUNCTIONCODE_GETTRADEINFO, {'path': '/v1/accounts/%s/trades' %self.accountId,
                                                               'method': 'GET'})     
          
          self.initFunctionSetting(FUNCTIONCODE_MODIFYTRADE, {'path': '/v1/accounts/%s/trades' %self.accountId,
                                                              'method': 'PATCH'})    
          
          self.initFunctionSetting(FUNCTIONCODE_CLOSETRADE, {'path': '/v1/accounts/%s/trades' %self.accountId,
                                                             'method': 'DELETE'})     
          
          self.initFunctionSetting(FUNCTIONCODE_GETPOSITIONS, {'path': '/v1/accounts/%s/positions' %self.accountId,
                                                               'method': 'GET'})     
          
          self.initFunctionSetting(FUNCTIONCODE_GETPOSITIONINFO, {'path': '/v1/accounts/%s/positions' %self.accountId,
                                                                  'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_CLOSEPOSITION, {'path': '/v1/accounts/%s/positions' %self.accountId,
                                                                'method': 'DELETE'})            
          
          self.initFunctionSetting(FUNCTIONCODE_GETTRANSACTIONS, {'path': '/v1/accounts/%s/transactions' %self.accountId,
                                                                  'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETTRANSACTIONINFO, {'path': '/v1/accounts/%s/transactions' %self.accountId,
                                                                     'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETACCOUNTHISTORY, {'path': '/v1/accounts/%s/alltransactions' %self.accountId,
                                                                    'method': 'GET'})     
          
          self.initFunctionSetting(FUNCTIONCODE_GETCALENDAR, {'path': '/labs/v1/calendar',
                                                              'method': 'GET'})           
          
          self.initFunctionSetting(FUNCTIONCODE_GETPOSITIONRATIOS, {'path': '/labs/v1/historical_position_ratios',
                                                                    'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETSPREADS, {'path': '/labs/v1/spreads',
                                                             'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETCOMMIMENTS, {'path': '/labs/v1/commitments',
                                                                'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETORDERBOOK, {'path': '/labs/v1/orderbook_data',
                                                               'method': 'GET'})    
          
          self.initFunctionSetting(FUNCTIONCODE_GETAUTOCHARTIST, {'path': '/labs/v1/autochartist',
                                                                  'method': 'GET'})  
          
          self.initFunctionSetting(FUNCTIONCODE_GETAUTOCHARTIST, {'path': '/labs/v1/autochartist',
                                                                  'method': 'GET'})  
          
          self.initFunctionSetting(FUNCTIONCODE_STREAMPRICES, {'path': '/v1/prices',
                                                               'method': 'GET'})  
          
          self.initFunctionSetting(FUNCTIONCODE_STREAMEVENTS, {'path': '/v1/events',
                                                               'method': 'GET'})          
          
          
          self.active = True
          self.reqThread.start()
          self.streamEventsThread.start()
          self.streamPricesThread.start()
          
      #----------------------------------------------------------------------
      def exit(self):
          """退出接口"""
          if self.active:
              self.active = False
              self.reqThread.join()
          
      #----------------------------------------------------------------------
      def initFunctionSetting(self, code, setting):
          """初始化API功能字典"""
          self.functionSetting[code] = setting
          
      #----------------------------------------------------------------------
      def processRequest(self, req):
          """发送请求并通过回调函数推送数据结果"""
          url = req['url']
          method = req['method']
          params = req['params']
          
          stream = False
          if 'stream' in req:
              stream = req['stream']
  
          if method in ['GET', 'DELETE']:
              myreq = requests.Request(method, url, headers=self.headers, params=params)
          elif method in ['POST', 'PATCH']:
              myreq = requests.Request(method, url, headers=self.headers, data=params)
          pre = myreq.prepare()
  
          r = None
          error = None
          
          try:
              r = self.session.send(pre, stream=stream)
          except Exception, e:
              error = e
  
          return r, error
      
      #----------------------------------------------------------------------
      def processQueue(self):
          """处理请求队列中的请求"""
          while self.active:
              try:
                  req = self.reqQueue.get(block=True, timeout=1)  # 获取请求的阻塞为一秒
                  callback = req['callback']
                  reqID = req['reqID']
                  
                  r, error = self.processRequest(req)
                  
                  if r:
                      try:
                          data = r.json()
                          if self.DEBUG:
                              print callback.__name__                        
                          callback(data, reqID)    
                      except Exception, e:                  
                          self.onError(str(e), reqID)                      
                  else:                
                      self.onError(error, reqID)
              except Empty:
                  pass
              
      #----------------------------------------------------------------------
      def sendRequest(self, code, params, callback, optional=''):
          """发送请求"""
          setting = self.functionSetting[code]
          
          url = self.restDomain + setting['path']
          if optional:
              url = url + '/' + optional
              
          self.reqID += 1
              
          req = {'url': url,
                 'method': setting['method'],
                 'params': params,
                 'callback': callback,
                 'reqID': self.reqID}
          self.reqQueue.put(req)
          
          return self.reqID
      
      #----------------------------------------------------------------------
      def onError(self, error, reqID):
          """错误信息回调"""
          print error, reqID
              
      #----------------------------------------------------------------------
      def getInstruments(self, params):
          """查询可交易的合约列表"""
          return self.sendRequest(FUNCTIONCODE_GETINSTRUMENTS, params, self.onGetInstruments)
          
      #----------------------------------------------------------------------
      def onGetInstruments(self, data, reqID):
          """回调函数"""
          pass
          
      #----------------------------------------------------------------------
      def getPrices(self, params):
          """查询价格"""
          return self.sendRequest(FUNCTIONCODE_GETPRICES, params, self.onGetPrices)
          
      #----------------------------------------------------------------------
      def onGetPrices(self, data, reqID):
          """回调函数"""
          pass
          
      #----------------------------------------------------------------------
      def getPriceHisory(self, params):
          """查询历史价格数据"""
          return self.sendRequest(FUNCTIONCODE_GETPRICEHISTORY, params, self.onGetPriceHistory)
          
      #----------------------------------------------------------------------
      def onGetPriceHistory(self, data, reqID):
          """回调函数"""
          pass    
          
      #----------------------------------------------------------------------
      def getAccounts(self):
          """查询用户的所有账户"""
          return self.sendRequest(FUNCTIONCODE_GETACCOUNTS, {}, self.onGetAccounts)
      
      #----------------------------------------------------------------------
      def onGetAccounts(self, data, reqID):
          """回调函数"""
          pass   
          
      #----------------------------------------------------------------------
      def getAccountInfo(self):
          """查询账户数据"""
          return self.sendRequest(FUNCTIONCODE_GETACCOUNTINFO, {}, self.onGetAccountInfo)
      
      #----------------------------------------------------------------------
      def onGetAccountInfo(self, data, reqID):
          """回调函数"""
          pass   
          
      #----------------------------------------------------------------------
      def getOrders(self, params):
          """查询所有委托"""
          return self.sendRequest(FUNCTIONCODE_GETORDERS, params, self.onGetOrders)
      
      #----------------------------------------------------------------------
      def onGetOrders(self, data, reqID):
          """回调函数"""
          pass     
  
      #----------------------------------------------------------------------
      def sendOrder(self, params):
          """发送委托"""
          return self.sendRequest(FUNCTIONCODE_SENDORDER, params, self.onSendOrder)
      
      #----------------------------------------------------------------------
      def onSendOrder(self, data, reqID):
          """回调函数"""
          pass         
      
      #----------------------------------------------------------------------
      def getOrderInfo(self, optional):
          """查询委托信息"""
          return self.sendRequest(FUNCTIONCODE_GETORDERINFO, {}, self.onGetOrderInfo, optional)
      
      #----------------------------------------------------------------------
      def onGetOrderInfo(self, data, reqID):
          """回调函数"""
          pass     
          
      #----------------------------------------------------------------------
      def modifyOrder(self, params, optional):
          """修改委托"""
          return self.sendRequest(FUNCTIONCODE_MODIFYORDER, params, self.onModifyOrder, optional)
      
      #----------------------------------------------------------------------
      def onModifyOrder(self, data, reqID):
          """回调函数"""
          pass      
      
      #----------------------------------------------------------------------
      def cancelOrder(self, optional):
          """查询委托信息"""
          return self.sendRequest(FUNCTIONCODE_CANCELORDER, {}, self.onCancelOrder, optional)
      
      #----------------------------------------------------------------------
      def onCancelOrder(self, data, reqID):
          """回调函数"""
          pass     
      
      #----------------------------------------------------------------------
      def getTrades(self, params):
          """查询所有仓位"""
          return self.sendRequest(FUNCTIONCODE_GETTRADES, params, self.onGetTrades)
      
      #----------------------------------------------------------------------
      def onGetTrades(self, data, reqID):
          """回调函数"""
          pass     
      
      #----------------------------------------------------------------------
      def getTradeInfo(self, optional):
          """查询仓位信息"""
          return self.sendRequest(FUNCTIONCODE_GETTRADEINFO, {}, self.onGetTradeInfo, optional)
      
      #----------------------------------------------------------------------
      def onGetTradeInfo(self, data, reqID):
          """回调函数"""
          pass     
          
      #----------------------------------------------------------------------
      def modifyTrade(self, params, optional):
          """修改仓位"""
          return self.sendRequest(FUNCTIONCODE_MODIFYTRADE, params, self.onModifyTrade, optional)
      
      #----------------------------------------------------------------------
      def onModifyTrade(self, data, reqID):
          """回调函数"""
          pass      
      
      #----------------------------------------------------------------------
      def closeTrade(self, optional):
          """平仓"""
          return self.sendRequest(FUNCTIONCODE_CLOSETRADE, {}, self.onCloseTrade, optional)
      
      #----------------------------------------------------------------------
      def onCloseTrade(self, data, reqID):
          """回调函数"""
          pass         
  
      #----------------------------------------------------------------------
      def getPositions(self):
          """查询所有汇总仓位"""
          return self.sendRequest(FUNCTIONCODE_GETPOSITIONS, {}, self.onGetPositions)
      
      #----------------------------------------------------------------------
      def onGetPositions(self, data, reqID):
          """回调函数"""
          pass  
          
      #----------------------------------------------------------------------
      def getPositionInfo(self, optional):
          """查询汇总仓位信息"""
          return self.sendRequest(FUNCTIONCODE_GETPOSITIONINFO, {}, self.onGetPositionInfo, optional)
      
      #----------------------------------------------------------------------
      def onGetPositionInfo(self, data, reqID):
          """回调函数"""
          pass         
  
      #----------------------------------------------------------------------
      def closePosition(self, optional):
          """平仓汇总仓位信息"""
          return self.sendRequest(FUNCTIONCODE_CLOSEPOSITION, {}, self.onClosePosition, optional)
      
      #----------------------------------------------------------------------
      def onClosePosition(self, data, reqID):
          """回调函数"""
          pass      
      
      
      #----------------------------------------------------------------------
      def getTransactions(self, params):
          """查询所有资金变动"""
          return self.sendRequest(FUNCTIONCODE_GETTRANSACTIONS, params, self.onGetTransactions)
      
      #----------------------------------------------------------------------
      def onGetTransactions(self, data, reqID):
          """回调函数"""
          pass  
          
      #----------------------------------------------------------------------
      def getTransactionInfo(self, optional):
          """查询资金变动信息"""
          return self.sendRequest(FUNCTIONCODE_GETTRANSACTIONINFO, {}, self.onGetTransactionInfo, optional)
      
      #----------------------------------------------------------------------
      def onGetTransactionInfo(self, data, reqID):
          """回调函数"""
          pass   
          
      #----------------------------------------------------------------------
      def getAccountHistory(self):
          """查询账户资金变动历史"""
          return self.sendRequest(FUNCTIONCODE_GETACCOUNTHISTORY, {}, self.onGetAccountHistory)
      
      #----------------------------------------------------------------------
      def onGetAccountHistory(self, data, reqID):
          """回调函数"""
          pass      
      
      #----------------------------------------------------------------------
      def getCalendar(self, params):
          """查询日历"""
          return self.sendRequest(FUNCTIONCODE_GETCALENDAR, params, self.onGetCalendar)
      
      #----------------------------------------------------------------------
      def onGetCalendar(self, data, reqID):
          """回调函数"""
          pass       
          
      #----------------------------------------------------------------------
      def getPositionRatios(self, params):
          """查询持仓比例"""
          return self.sendRequest(FUNCTIONCODE_GETPOSITIONRATIOS, params, self.onGetPositionRatios)
      
      #----------------------------------------------------------------------
      def onGetPositionRatios(self, data, reqID):
          """回调函数"""
          pass  
          
      #----------------------------------------------------------------------
      def getSpreads(self, params):
          """查询所有仓位"""
          return self.sendRequest(FUNCTIONCODE_GETSPREADS, params, self.onGetSpreads)
      
      #----------------------------------------------------------------------
      def onGetSpreads(self, data, reqID):
          """回调函数"""
          pass       
          
      #----------------------------------------------------------------------
      def getCommitments(self, params):
          """查询交易商持仓情况"""
          return self.sendRequest(FUNCTIONCODE_GETCOMMIMENTS, params, self.onGetCommitments)
      
      #----------------------------------------------------------------------
      def onGetCommitments(self, data, reqID):
          """回调函数"""
          pass       
      
      #----------------------------------------------------------------------
      def getOrderbook(self, params):
          """查询订单簿"""
          return self.sendRequest(FUNCTIONCODE_GETORDERBOOK, params, self.onGetOrderbook)
      
      #----------------------------------------------------------------------
      def onGetOrderbook(self, data, reqID):
          """回调函数"""
          pass       
          
      #----------------------------------------------------------------------
      def getAutochartist(self, params):
          """查询Autochartist识别的模式"""
          return self.sendRequest(FUNCTIONCODE_GETAUTOCHARTIST, params, self.onGetAutochartist)
      
      #----------------------------------------------------------------------
      def onGetAutochartist(self, data, reqID):
          """回调函数"""
          pass   
          
      #----------------------------------------------------------------------
      def onPrice(self, data):
          """行情推送"""
          print data
          
      #----------------------------------------------------------------------
      def onEvent(self, data):
          """事件推送（成交等）"""
          print data
          
      #----------------------------------------------------------------------
      def processStreamPrices(self):
          """获取价格推送"""
          # 首先获取所有合约的代码
          setting = self.functionSetting[FUNCTIONCODE_GETINSTRUMENTS]
          req = {'url': self.restDomain + setting['path'],
                 'method': setting['method'],
                 'params': {'accountId': self.accountId}}
          r, error = self.processRequest(req)
          if r:
              try:
                  data = r.json()
                  symbols = [d['instrument'] for d in data['instruments']]   
              except Exception, e:
                  self.onError(e, -1)
                  return
          else:
              self.onError(error, -1)
              return
  
          # 然后订阅所有的合约行情
          setting = self.functionSetting[FUNCTIONCODE_STREAMPRICES]
          params = {'accountId': self.accountId,
                    'instruments': ','.join(symbols)}
          req = {'url': self.streamDomain + setting['path'],
                 'method': setting['method'],
                 'params': params,
                 'stream': True}      
          r, error = self.processRequest(req)
          
          if r:
              for line in r.iter_lines():
                  if line:
                      try:
                          msg = json.loads(line)
                          
                          if self.DEBUG:
                              print self.onPrice.__name__
                              
                          self.onPrice(msg)
                      except Exception, e:
                          self.onError(e, -1)
                  
                  if not self.active:
                      break
          else:
              self.onError(error, -1)
      
      #----------------------------------------------------------------------
      def processStreamEvents(self):
          """获取事件推送"""
          setting = self.functionSetting[FUNCTIONCODE_STREAMEVENTS]
          req = {'url': self.streamDomain + setting['path'],
                 'method': setting['method'],
                 'params': {},
                 'stream': True}
          r, error = self.processRequest(req)
          if r:
              for line in r.iter_lines():
                  if line:
                      try:
                          msg = json.loads(line)
                          
                          if self.DEBUG:
                              print self.onEvent.__name__
                              
                          self.onEvent(msg)
                      except Exception, e:
                          self.onError(e, -1)
                  
                  if not self.active:
                      break
          else:
              self.onError(error, -1)