[Del]移除OANDA接口
This commit is contained in:
parent
5cfb62fd31
commit
672353f369
@ -17,8 +17,6 @@ Using the vn.py project, institutional investors and professional traders, such
|
||||
|
||||
- Interactive Brokers(vn.ib)
|
||||
|
||||
- OANDA(vn.oanda)
|
||||
|
||||
- Shanghai Zhida Futures(vn.shzd)
|
||||
|
||||
**Chinese Futures Market**
|
||||
|
33
README.md
33
README.md
@ -37,8 +37,6 @@ vn.py是基于Python的开源量化交易程序开发框架,起源于国内私
|
||||
|
||||
- Interactive Brokers(ib)
|
||||
|
||||
- OANDA(oanda)
|
||||
|
||||
- 福汇(fxcm)
|
||||
|
||||
- OKCOIN(okcoin)
|
||||
@ -125,14 +123,8 @@ sudo /home/vnpy/anaconda2/bin/conda install -c quantopian ta-lib=0.4.9
|
||||
```
|
||||
# encoding: UTF-8
|
||||
|
||||
# 重载sys模块,设置默认字符串编码方式为utf8
|
||||
import sys
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf8')
|
||||
|
||||
# 判断操作系统
|
||||
import platform
|
||||
system = platform.system()
|
||||
|
||||
# vn.trader模块
|
||||
from vnpy.event import EventEngine
|
||||
@ -141,18 +133,11 @@ from vnpy.trader.uiQt import createQApp
|
||||
from vnpy.trader.uiMainWindow import MainWindow
|
||||
|
||||
# 加载底层接口
|
||||
from vnpy.trader.gateway import (ctpGateway, oandaGateway, ibGateway,
|
||||
tkproGateway)
|
||||
|
||||
if system == 'Windows':
|
||||
from vnpy.trader.gateway import (femasGateway, xspeedGateway,
|
||||
futuGateway, secGateway)
|
||||
|
||||
if system == 'Linux':
|
||||
from vnpy.trader.gateway import xtpGateway
|
||||
from vnpy.trader.gateway import ctpGateway, ibGateway
|
||||
|
||||
# 加载上层应用
|
||||
from vnpy.trader.app import (riskManager, ctaStrategy, spreadTrading)
|
||||
from vnpy.trader.app import (riskManager, ctaStrategy,
|
||||
spreadTrading, algoTrading)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
@ -169,23 +154,13 @@ def main():
|
||||
|
||||
# 添加交易接口
|
||||
me.addGateway(ctpGateway)
|
||||
me.addGateway(tkproGateway)
|
||||
me.addGateway(oandaGateway)
|
||||
me.addGateway(ibGateway)
|
||||
|
||||
if system == 'Windows':
|
||||
me.addGateway(femasGateway)
|
||||
me.addGateway(xspeedGateway)
|
||||
me.addGateway(secGateway)
|
||||
me.addGateway(futuGateway)
|
||||
|
||||
if system == 'Linux':
|
||||
me.addGateway(xtpGateway)
|
||||
|
||||
# 添加上层应用
|
||||
me.addApp(riskManager)
|
||||
me.addApp(ctaStrategy)
|
||||
me.addApp(spreadTrading)
|
||||
me.addApp(algoTrading)
|
||||
|
||||
# 创建主窗口
|
||||
mw = MainWindow(me, ee)
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"token": "请在OANDA网站申请",
|
||||
"accountId": "请在OANDA网站申请",
|
||||
"settingName": "practice"
|
||||
}
|
@ -5,9 +5,14 @@ try:
|
||||
reload # Python 2
|
||||
except NameError: # Python 3
|
||||
from importlib import reload
|
||||
|
||||
import sys
|
||||
reload(sys)
|
||||
|
||||
try:
|
||||
sys.setdefaultencoding('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# 判断操作系统
|
||||
import platform
|
||||
@ -20,8 +25,7 @@ from vnpy.trader.uiQt import createQApp
|
||||
from vnpy.trader.uiMainWindow import MainWindow
|
||||
|
||||
# 加载底层接口
|
||||
from vnpy.trader.gateway import (ctpGateway, oandaGateway,
|
||||
ibGateway)
|
||||
from vnpy.trader.gateway import (ctpGateway, ibGateway)
|
||||
|
||||
if system == 'Linux':
|
||||
from vnpy.trader.gateway import xtpGateway
|
||||
@ -48,7 +52,6 @@ def main():
|
||||
|
||||
# 添加交易接口
|
||||
me.addGateway(ctpGateway)
|
||||
me.addGateway(oandaGateway)
|
||||
me.addGateway(ibGateway)
|
||||
|
||||
if system == 'Windows':
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
### 外盘
|
||||
* vn.ib:Interactive Brokers接口
|
||||
* vn.oanda:OANDA接口
|
||||
* vn.shzd:直达期货接口
|
||||
|
||||
### 比特币
|
||||
|
@ -1,42 +0,0 @@
|
||||
# vn.oanda
|
||||
|
||||
### 简介
|
||||
OANDA外汇交易接口,基于REST API开发,实现了以下功能:
|
||||
|
||||
1. 发送、修改、撤销委托
|
||||
|
||||
2. 查询委托、持仓(按照每笔成交算)、汇总持仓(按照单一货币对算)、资金、成交历史
|
||||
|
||||
3. 实时行情和成交推送
|
||||
|
||||
4. 获取Forex Lab中的日历、订单簿、历史持仓比、价差、交易商持仓、Autochartist
|
||||
|
||||
目前该API尚处于测试阶段,如果发现bug或者需要改进的地方请在github上开issue给我。
|
||||
|
||||
### 特点
|
||||
相比较于[OANDA官网](http://developer.oanda.com/rest-live/sample-code/)上贴出的一些Python API(如pyoanda、oanda-trading-environment等),vn.oanda的一些不同:
|
||||
|
||||
1. 面向对象的API设计,接近CTP API的结构,对于国内用户而言更容易上手
|
||||
|
||||
2. 三个独立的工作线程,分别处理:用户请求(如发送委托等)、行情推送、事件推送(如成交事件等),提供更高的性能
|
||||
|
||||
3. 参考CTP API的设计,主动函数调用的结果通过异步(回调函数)的方式推送到程序中,适用于开发真正可靠的实盘交易程序(pyoanda里使用的同步阻塞工作模式在实盘应用中的风险:想象你的交易程序发送委托请求后,因为网络问题不能立即返回,因此主线程阻塞导致界面卡死或者背后的策略引擎线程卡死,对新的行情事件完全失去响应)
|
||||
|
||||
### Quick Start
|
||||
1. 安装Anaconda 2.7 32位
|
||||
|
||||
2. 前往[OANDA](http://www.oanda.com)注册一个fxTrade practice测试账户(注意国家不要选中国,会无法申请API token,作者测试英国可以)
|
||||
|
||||
3. 在网站登陆后,进入Manage Funds,记录下自己的Account Number
|
||||
|
||||
4. 回到上一个界面,左侧有个Manage API Access(在Recent Logins上方,没有的就是第一步国家选错了),进入后生成token
|
||||
|
||||
5. 下载vn.oanda到本地后,打开test.py,修改token和accountId为你的信息
|
||||
|
||||
6. 将test.py中想要测试的功能取消注释,开始使用吧!
|
||||
|
||||
### API版本
|
||||
日期:2016-02-27
|
||||
|
||||
链接:[http://developer.oanda.com/rest-live/introduction/](http://developer.oanda.com/rest-live/introduction/)
|
||||
|
@ -1,4 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .vnoanda import OandaApi
|
@ -1,106 +0,0 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from __future__ import absolute_import
|
||||
from .vnoanda import OandaApi
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
token = ''
|
||||
accountId = ''
|
||||
|
||||
api = OandaApi()
|
||||
api.DEBUG = True
|
||||
|
||||
api.init('practice', token, accountId)
|
||||
|
||||
# 获取交易合约列表,通过
|
||||
#api.getInstruments({'accountId': accountId})
|
||||
|
||||
# 获取价格,通过
|
||||
#api.getPrices({'instruments': 'EUR_USD'})
|
||||
|
||||
# 获取历史数据,失败
|
||||
#api.getPriceHisory({'instrument': 'EUR_USD',
|
||||
#'granularity': 'D',
|
||||
#'candleFormat': 'midpoint',
|
||||
#'count': '50'})
|
||||
|
||||
# 查询用户的所有账户,通过
|
||||
#api.getAccounts()
|
||||
|
||||
# 查询账户信息,通过
|
||||
#api.getAccountInfo()
|
||||
|
||||
# 查询委托数据,通过
|
||||
#api.getOrders({})
|
||||
|
||||
# 发送委托,通过
|
||||
#api.sendOrder({'instrument': 'EUR_USD',
|
||||
#'units': '10000',
|
||||
#'side': 'buy',
|
||||
#'type': 'market'})
|
||||
|
||||
# 查询委托数据,通过
|
||||
#api.getOrderInfo('123')
|
||||
|
||||
# 修改委托,通过
|
||||
#api.modifyOrder({'units': '10000',
|
||||
#'side': 'buy',
|
||||
#'type': 'market'}, '123')
|
||||
|
||||
# 撤销委托,通过
|
||||
#api.cancelOrder('123')
|
||||
|
||||
# 查询所有持仓,通过
|
||||
#api.getTrades({})
|
||||
|
||||
# 查询持仓数据,通过
|
||||
#api.getTradeInfo('10125150909')
|
||||
|
||||
# 修改持仓,通过
|
||||
#api.modifyTrade({'trailingStop': '150'}, '10125150909')
|
||||
|
||||
# 平仓,通过
|
||||
#api.closeTrade('10125150909')
|
||||
|
||||
# 查询汇总持仓,通过
|
||||
#api.getPositions()
|
||||
|
||||
# 查询汇总持仓细节,通过
|
||||
#api.getPositionInfo('EUR_USD')
|
||||
|
||||
# 平仓汇总持仓,通过
|
||||
#api.closePosition('EUR_USD')
|
||||
|
||||
# 查询账户资金变动,通过
|
||||
#api.getTransactions({})
|
||||
|
||||
# 查询资金变动信息,通过
|
||||
#api.getTransactionInfo('10135713982')
|
||||
|
||||
# 查询账户变动历史,部分通过,某些情况下可能触发JSONDecodeError
|
||||
#api.getAccountHistory()
|
||||
|
||||
# 查询财经日历,通过
|
||||
#api.getCalendar({'period': '604800'})
|
||||
|
||||
# 查询历史持仓比,通过
|
||||
#api.getPositionRatios({'instrument': 'EUR_USD',
|
||||
#'period': '604800'})
|
||||
|
||||
# 查询历史价差,通过
|
||||
#api.getSpreads({'instrument': 'EUR_USD',
|
||||
#'period': '604800'})
|
||||
|
||||
# 查询交易商持仓,通过
|
||||
#api.getCommitments({'instrument': 'EUR_USD'})
|
||||
|
||||
# 查询订单簿,通过
|
||||
#api.getOrderbook({'instrument': 'EUR_USD',
|
||||
#'period': '604800'})
|
||||
|
||||
# 查询Autochartist,失败,OANDA服务器报错
|
||||
#api.getAutochartist({'instrument': 'EUR_USD'})
|
||||
|
||||
# 阻塞
|
||||
input()
|
@ -1,610 +0,0 @@
|
||||
# encoding: utf-8
|
||||
|
||||
from __future__ import print_function
|
||||
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 as 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 as 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):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPrices(self, params):
|
||||
"""查询价格"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETPRICES, params, self.onGetPrices)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPrices(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPriceHisory(self, params):
|
||||
"""查询历史价格数据"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETPRICEHISTORY, params, self.onGetPriceHistory)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPriceHistory(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAccounts(self):
|
||||
"""查询用户的所有账户"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETACCOUNTS, {}, self.onGetAccounts)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetAccounts(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAccountInfo(self):
|
||||
"""查询账户数据"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETACCOUNTINFO, {}, self.onGetAccountInfo)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetAccountInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getOrders(self, params):
|
||||
"""查询所有委托"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETORDERS, params, self.onGetOrders)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetOrders(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, params):
|
||||
"""发送委托"""
|
||||
return self.sendRequest(FUNCTIONCODE_SENDORDER, params, self.onSendOrder)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onSendOrder(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getOrderInfo(self, optional):
|
||||
"""查询委托信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETORDERINFO, {}, self.onGetOrderInfo, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetOrderInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def modifyOrder(self, params, optional):
|
||||
"""修改委托"""
|
||||
return self.sendRequest(FUNCTIONCODE_MODIFYORDER, params, self.onModifyOrder, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onModifyOrder(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, optional):
|
||||
"""查询委托信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_CANCELORDER, {}, self.onCancelOrder, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCancelOrder(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTrades(self, params):
|
||||
"""查询所有仓位"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETTRADES, params, self.onGetTrades)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetTrades(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTradeInfo(self, optional):
|
||||
"""查询仓位信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETTRADEINFO, {}, self.onGetTradeInfo, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetTradeInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def modifyTrade(self, params, optional):
|
||||
"""修改仓位"""
|
||||
return self.sendRequest(FUNCTIONCODE_MODIFYTRADE, params, self.onModifyTrade, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onModifyTrade(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def closeTrade(self, optional):
|
||||
"""平仓"""
|
||||
return self.sendRequest(FUNCTIONCODE_CLOSETRADE, {}, self.onCloseTrade, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onCloseTrade(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPositions(self):
|
||||
"""查询所有汇总仓位"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETPOSITIONS, {}, self.onGetPositions)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPositions(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPositionInfo(self, optional):
|
||||
"""查询汇总仓位信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETPOSITIONINFO, {}, self.onGetPositionInfo, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPositionInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def closePosition(self, optional):
|
||||
"""平仓汇总仓位信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_CLOSEPOSITION, {}, self.onClosePosition, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onClosePosition(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTransactions(self, params):
|
||||
"""查询所有资金变动"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETTRANSACTIONS, params, self.onGetTransactions)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetTransactions(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTransactionInfo(self, optional):
|
||||
"""查询资金变动信息"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETTRANSACTIONINFO, {}, self.onGetTransactionInfo, optional)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetTransactionInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAccountHistory(self):
|
||||
"""查询账户资金变动历史"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETACCOUNTHISTORY, {}, self.onGetAccountHistory)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetAccountHistory(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getCalendar(self, params):
|
||||
"""查询日历"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETCALENDAR, params, self.onGetCalendar)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetCalendar(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getPositionRatios(self, params):
|
||||
"""查询持仓比例"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETPOSITIONRATIOS, params, self.onGetPositionRatios)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPositionRatios(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getSpreads(self, params):
|
||||
"""查询所有仓位"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETSPREADS, params, self.onGetSpreads)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetSpreads(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getCommitments(self, params):
|
||||
"""查询交易商持仓情况"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETCOMMIMENTS, params, self.onGetCommitments)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetCommitments(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getOrderbook(self, params):
|
||||
"""查询订单簿"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETORDERBOOK, params, self.onGetOrderbook)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetOrderbook(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getAutochartist(self, params):
|
||||
"""查询Autochartist识别的模式"""
|
||||
return self.sendRequest(FUNCTIONCODE_GETAUTOCHARTIST, params, self.onGetAutochartist)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetAutochartist(self, data, reqID):
|
||||
"""回调函数"""
|
||||
print(data, reqID)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
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 as 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 as 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 as e:
|
||||
self.onError(e, -1)
|
||||
|
||||
if not self.active:
|
||||
break
|
||||
else:
|
||||
self.onError(error, -1)
|
@ -554,7 +554,7 @@ class Api(FxcmApi):
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTime(s):
|
||||
"""把OANDA返回的时间格式转化为简单的时间字符串"""
|
||||
"""把时间格式转化为简单的时间字符串"""
|
||||
month = s[:2]
|
||||
day = s[2:4]
|
||||
year = s[4:8]
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"token": "请在OANDA网站申请",
|
||||
"accountId": "请在OANDA网站申请",
|
||||
"settingName": "practice"
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
from __future__ import absolute_import
|
||||
from vnpy.trader import vtConstant
|
||||
from .oandaGateway import OandaGateway
|
||||
|
||||
gatewayClass = OandaGateway
|
||||
gatewayName = 'OANDA'
|
||||
gatewayDisplayName = gatewayName
|
||||
gatewayType = vtConstant.GATEWAYTYPE_INTERNATIONAL
|
||||
gatewayQryEnabled = True
|
@ -1,458 +0,0 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
'''
|
||||
vn.oanda的gateway接入
|
||||
|
||||
由于OANDA采用的是外汇做市商的交易模式,因此和国内接口方面有若干区别,具体如下:
|
||||
|
||||
* 行情数据反映的是OANDA的报价变化,因此只有买卖价,而没有成交价
|
||||
|
||||
* OANDA的持仓管理分为单笔成交持仓(Trade数据,国内没有)
|
||||
和单一资产汇总持仓(Position数据)
|
||||
|
||||
* OANDA系统中的所有时间都采用UTC时间(世界协调时,中国是UTC+8)
|
||||
|
||||
* 由于采用的是外汇做市商的模式,用户的限价委托当价格被触及时就会立即全部成交,
|
||||
不会出现部分成交的情况,因此委托状态只有已报、成交、撤销三种
|
||||
|
||||
* 外汇市场采用24小时交易,因此OANDA的委托不像国内收盘后自动失效,需要用户指定
|
||||
失效时间,本接口中默认设置为24个小时候失效
|
||||
'''
|
||||
|
||||
|
||||
import os
|
||||
import json
|
||||
import datetime
|
||||
|
||||
from vnpy.api.oanda import OandaApi
|
||||
from vnpy.trader.vtGateway import *
|
||||
from vnpy.trader.vtFunction import getJsonPath
|
||||
|
||||
# 价格类型映射
|
||||
priceTypeMap = {}
|
||||
priceTypeMap[PRICETYPE_LIMITPRICE] = 'limit'
|
||||
priceTypeMap[PRICETYPE_MARKETPRICE] = 'market'
|
||||
priceTypeMapReverse = {v: k for k, v in priceTypeMap.items()}
|
||||
|
||||
# 方向类型映射
|
||||
directionMap = {}
|
||||
directionMap[DIRECTION_LONG] = 'buy'
|
||||
directionMap[DIRECTION_SHORT] = 'sell'
|
||||
directionMapReverse = {v: k for k, v in directionMap.items()}
|
||||
|
||||
|
||||
########################################################################
|
||||
class OandaGateway(VtGateway):
|
||||
"""OANDA接口"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, eventEngine, gatewayName='OANDA'):
|
||||
"""Constructor"""
|
||||
super(OandaGateway, self).__init__(eventEngine, gatewayName)
|
||||
|
||||
self.api = Api(self)
|
||||
|
||||
self.qryEnabled = False # 是否要启动循环查询
|
||||
|
||||
self.fileName = self.gatewayName + '_connect.json'
|
||||
self.filePath = getJsonPath(self.fileName, __file__)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def connect(self):
|
||||
"""连接"""
|
||||
# 载入json文件
|
||||
try:
|
||||
f = file(self.filePath)
|
||||
except IOError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'读取连接配置出错,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 解析json文件
|
||||
setting = json.load(f)
|
||||
try:
|
||||
token = str(setting['token'])
|
||||
accountId = str(setting['accountId'])
|
||||
settingName = str(setting['settingName'])
|
||||
except KeyError:
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'连接配置缺少字段,请检查'
|
||||
self.onLog(log)
|
||||
return
|
||||
|
||||
# 初始化接口
|
||||
self.api.init(settingName, token, accountId)
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = u'接口初始化成功'
|
||||
self.onLog(log)
|
||||
|
||||
# 查询信息
|
||||
self.api.qryInstruments()
|
||||
self.api.qryOrders()
|
||||
self.api.qryTrades()
|
||||
|
||||
# 初始化并启动查询
|
||||
self.initQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def subscribe(self, subscribeReq):
|
||||
"""订阅行情"""
|
||||
pass
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder(self, orderReq):
|
||||
"""发单"""
|
||||
return self.api.sendOrder_(orderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder(self, cancelOrderReq):
|
||||
"""撤单"""
|
||||
self.api.cancelOrder_(cancelOrderReq)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryAccount(self):
|
||||
"""查询账户资金"""
|
||||
self.api.getAccountInfo()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryPosition(self):
|
||||
"""查询持仓"""
|
||||
self.api.getPositions()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def close(self):
|
||||
"""关闭"""
|
||||
self.api.exit()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def initQuery(self):
|
||||
"""初始化连续查询"""
|
||||
if self.qryEnabled:
|
||||
# 需要循环的查询函数列表
|
||||
self.qryFunctionList = [self.qryAccount, self.qryPosition]
|
||||
|
||||
self.qryCount = 0 # 查询触发倒计时
|
||||
self.qryTrigger = 2 # 查询触发点
|
||||
self.qryNextFunction = 0 # 上次运行的查询函数索引
|
||||
|
||||
self.startQuery()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def query(self, event):
|
||||
"""注册到事件处理引擎上的查询函数"""
|
||||
self.qryCount += 1
|
||||
|
||||
if self.qryCount > self.qryTrigger:
|
||||
# 清空倒计时
|
||||
self.qryCount = 0
|
||||
|
||||
# 执行查询函数
|
||||
function = self.qryFunctionList[self.qryNextFunction]
|
||||
function()
|
||||
|
||||
# 计算下次查询函数的索引,如果超过了列表长度,则重新设为0
|
||||
self.qryNextFunction += 1
|
||||
if self.qryNextFunction == len(self.qryFunctionList):
|
||||
self.qryNextFunction = 0
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def startQuery(self):
|
||||
"""启动连续查询"""
|
||||
self.eventEngine.register(EVENT_TIMER, self.query)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def setQryEnabled(self, qryEnabled):
|
||||
"""设置是否要启动循环查询"""
|
||||
self.qryEnabled = qryEnabled
|
||||
|
||||
|
||||
|
||||
########################################################################
|
||||
class Api(OandaApi):
|
||||
"""OANDA的API实现"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def __init__(self, gateway):
|
||||
"""Constructor"""
|
||||
super(Api, self).__init__()
|
||||
|
||||
self.gateway = gateway # gateway对象
|
||||
self.gatewayName = gateway.gatewayName # gateway对象名称
|
||||
|
||||
self.orderDict = {} # 缓存委托数据
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onError(self, error, reqID):
|
||||
"""错误信息回调"""
|
||||
err = VtErrorData()
|
||||
err.gatewayName = self.gatewayName
|
||||
err.errorMsg = error
|
||||
self.gateway.onError(err)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetInstruments(self, data, reqID):
|
||||
"""回调函数"""
|
||||
if not 'instruments' in data:
|
||||
return
|
||||
l = data['instruments']
|
||||
for d in l:
|
||||
contract = VtContractData()
|
||||
contract.gatewayName = self.gatewayName
|
||||
|
||||
contract.symbol = d['instrument']
|
||||
contract.name = d['displayName']
|
||||
contract.exchange = EXCHANGE_OANDA
|
||||
contract.vtSymbol = '.'.join([contract.symbol, contract.exchange])
|
||||
contract.priceTick = float(d['pip'])
|
||||
contract.size = 1
|
||||
contract.productClass = PRODUCT_FOREX
|
||||
self.gateway.onContract(contract)
|
||||
|
||||
self.writeLog(u'交易合约信息查询完成')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetAccountInfo(self, data, reqID):
|
||||
"""回调函数"""
|
||||
account = VtAccountData()
|
||||
account.gatewayName = self.gatewayName
|
||||
|
||||
account.accountID = str(data['accountId'])
|
||||
account.vtAccountID = '.'.join([self.gatewayName, account.accountID])
|
||||
|
||||
account.available = data['marginAvail']
|
||||
account.margin = data['marginUsed']
|
||||
account.closeProfit = data['realizedPl']
|
||||
account.positionProfit = data['unrealizedPl']
|
||||
account.balance = data['balance']
|
||||
|
||||
self.gateway.onAccount(account)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetOrders(self, data, reqID):
|
||||
"""回调函数"""
|
||||
if not 'orders' in data:
|
||||
return
|
||||
l = data['orders']
|
||||
|
||||
for d in l:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = d['instrument']
|
||||
order.exchange = EXCHANGE_OANDA
|
||||
order.vtSymbol = '.'.join([order.symbol, order.exchange])
|
||||
order.orderID = str(d['id'])
|
||||
|
||||
order.direction = directionMapReverse.get(d['side'], DIRECTION_UNKNOWN)
|
||||
order.offset = OFFSET_NONE
|
||||
order.status = STATUS_NOTTRADED # OANDA查询到的订单都是活动委托
|
||||
|
||||
order.price = d['price']
|
||||
order.totalVolume = d['units']
|
||||
order.orderTime = getTime(d['time'])
|
||||
|
||||
order.vtOrderID = '.'.join([self.gatewayName , order.orderID])
|
||||
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
self.orderDict[order.orderID] = order
|
||||
|
||||
self.writeLog(u'委托信息查询完成')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetPositions(self, data, reqID):
|
||||
"""回调函数"""
|
||||
if not 'positions' in data:
|
||||
return
|
||||
l = data['positions']
|
||||
|
||||
for d in l:
|
||||
pos = VtPositionData()
|
||||
pos.gatewayName = self.gatewayName
|
||||
|
||||
pos.symbol = d['instrument']
|
||||
pos.exchange = EXCHANGE_OANDA
|
||||
pos.vtSymbol = '.'.join([pos.symbol, pos.exchange])
|
||||
pos.direction = directionMapReverse.get(d['side'], DIRECTION_UNKNOWN)
|
||||
pos.position = d['units']
|
||||
pos.price = d['avgPrice']
|
||||
pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction])
|
||||
|
||||
self.gateway.onPosition(pos)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onGetTransactions(self, data, reqID):
|
||||
"""回调函数"""
|
||||
if not 'transactions' in data:
|
||||
return
|
||||
l = data['transactions']
|
||||
|
||||
for d in l:
|
||||
# 这里我们只关心委托成交
|
||||
if d['type'] == 'ORDER_FILLED':
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = d['instrument']
|
||||
trade.exchange = EXCHANGE_OANDA
|
||||
trade.vtSymbol = '.'.join([trade.symbol, trade.exchange])
|
||||
trade.tradeID = str(d['id'])
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
trade.orderID = str(d['orderId'])
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
trade.direction = directionMapReverse.get(d['side'], DIRECTION_UNKNOWN)
|
||||
trade.offset = OFFSET_NONE
|
||||
trade.price = d['price']
|
||||
trade.volume = d['units']
|
||||
trade.tradeTime = getTime(d['time'])
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
self.writeLog(u'成交信息查询完成')
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onPrice(self, data):
|
||||
"""行情推送"""
|
||||
if 'tick' not in data:
|
||||
return
|
||||
d = data['tick']
|
||||
|
||||
tick = VtTickData()
|
||||
tick.gatewayName = self.gatewayName
|
||||
|
||||
tick.symbol = d['instrument']
|
||||
tick.exchange = EXCHANGE_OANDA
|
||||
tick.vtSymbol = '.'.join([tick.symbol, tick.exchange])
|
||||
tick.bidPrice1 = d['bid']
|
||||
tick.askPrice1 = d['ask']
|
||||
tick.time = getTime(d['time']) + '.0' # 补齐毫秒部分
|
||||
tick.date = datetime.datetime.utcnow().strftime('%Y%m%d') # OANDA的时间是UTC标准时
|
||||
|
||||
# 做市商的TICK数据只有买卖的报价,因此最新价格选用中间价代替
|
||||
tick.lastPrice = (tick.bidPrice1 + tick.askPrice1)/2
|
||||
|
||||
self.gateway.onTick(tick)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def onEvent(self, data):
|
||||
"""事件推送(成交等)"""
|
||||
if 'transaction' not in data:
|
||||
return
|
||||
|
||||
d = data['transaction']
|
||||
|
||||
# 委托成交
|
||||
if d['type'] == 'ORDER_FILLED':
|
||||
# 推送成交事件
|
||||
trade = VtTradeData()
|
||||
trade.gatewayName = self.gatewayName
|
||||
|
||||
trade.symbol = d['instrument']
|
||||
trade.exchange = EXCHANGE_OANDA
|
||||
trade.vtSymbol = '.'.join([trade.symbol, trade.exchange])
|
||||
|
||||
trade.tradeID = str(d['id'])
|
||||
trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
|
||||
|
||||
trade.orderID = str(d['orderId'])
|
||||
trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
|
||||
|
||||
trade.direction = directionMapReverse.get(d['side'], DIRECTION_UNKNOWN)
|
||||
trade.offset = OFFSET_NONE
|
||||
|
||||
trade.price = d['price']
|
||||
trade.volume = d['units']
|
||||
trade.tradeTime = getTime(d['time'])
|
||||
|
||||
self.gateway.onTrade(trade)
|
||||
|
||||
# 推送委托事件
|
||||
order = self.orderDict.get(str(d['orderId']), None)
|
||||
if not order:
|
||||
return
|
||||
order.status = STATUS_ALLTRADED
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
# 委托下达
|
||||
elif d['type'] in ['MARKET_ORDER_CREATE', 'LIMIT_ORDER_CREATE']:
|
||||
order = VtOrderData()
|
||||
order.gatewayName = self.gatewayName
|
||||
|
||||
order.symbol = d['instrument']
|
||||
order.exchange = EXCHANGE_OANDA
|
||||
order.vtSymbol = '.'.join([order.symbol, order.exchange])
|
||||
order.orderID = str(d['id'])
|
||||
order.direction = directionMapReverse.get(d['side'], DIRECTION_UNKNOWN)
|
||||
order.offset = OFFSET_NONE
|
||||
order.status = STATUS_NOTTRADED
|
||||
order.price = d['price']
|
||||
order.totalVolume = d['units']
|
||||
order.orderTime = getTime(d['time'])
|
||||
order.vtOrderID = '.'.join([self.gatewayName , order.orderID])
|
||||
|
||||
self.gateway.onOrder(order)
|
||||
self.orderDict[order.orderID] = order
|
||||
|
||||
# 委托撤销
|
||||
elif d['type'] == 'ORDER_CANCEL':
|
||||
order = self.orderDict.get(str(d['orderId']), None)
|
||||
if not order:
|
||||
return
|
||||
order.status = STATUS_CANCELLED
|
||||
self.gateway.onOrder(order)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def writeLog(self, logContent):
|
||||
"""发出日志"""
|
||||
log = VtLogData()
|
||||
log.gatewayName = self.gatewayName
|
||||
log.logContent = logContent
|
||||
self.gateway.onLog(log)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryInstruments(self):
|
||||
"""查询合约"""
|
||||
params = {'accountId': self.accountId}
|
||||
self.getInstruments(params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryOrders(self):
|
||||
"""查询委托"""
|
||||
self.getOrders({})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def qryTrades(self):
|
||||
"""查询成交"""
|
||||
# 最多查询100条记录
|
||||
self.getTransactions({'count': 100})
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def sendOrder_(self, orderReq):
|
||||
"""发送委托"""
|
||||
params = {}
|
||||
params['instrument'] = orderReq.symbol
|
||||
params['units'] = orderReq.volume
|
||||
params['side'] = directionMap.get(orderReq.direction, '')
|
||||
params['price'] = orderReq.price
|
||||
params['type'] = priceTypeMap.get(orderReq.priceType, '')
|
||||
|
||||
# 委托有效期24小时
|
||||
expire = datetime.datetime.now() + datetime.timedelta(days=1)
|
||||
params['expiry'] = expire.isoformat('T') + 'Z'
|
||||
|
||||
self.sendOrder(params)
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def cancelOrder_(self, cancelOrderReq):
|
||||
"""撤销委托"""
|
||||
self.cancelOrder(cancelOrderReq.orderID)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getTime(t):
|
||||
"""把OANDA返回的时间格式转化为简单的时间字符串"""
|
||||
return t[11:19]
|
@ -80,7 +80,6 @@ EXCHANGE_CME = 'CME' # CME交易所
|
||||
EXCHANGE_ICE = 'ICE' # ICE交易所
|
||||
EXCHANGE_LME = 'LME' # LME交易所
|
||||
|
||||
EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
|
||||
EXCHANGE_FXCM = 'FXCM' # FXCM外汇做市商
|
||||
|
||||
EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
|
||||
|
@ -76,7 +76,6 @@ EXCHANGE_CME = 'CME' # CME交易所
|
||||
EXCHANGE_ICE = 'ICE' # ICE交易所
|
||||
EXCHANGE_LME = 'LME' # LME交易所
|
||||
|
||||
EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
|
||||
EXCHANGE_FXCM = 'FXCM' # FXCM外汇做市商
|
||||
|
||||
EXCHANGE_OKCOIN = 'OKCOIN' # OKCOIN比特币交易所
|
||||
|
Loading…
Reference in New Issue
Block a user