This commit is contained in:
JaysonAlbert 2018-01-15 09:55:47 +08:00
commit d2f11eba3b
15 changed files with 792 additions and 150 deletions

View File

@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 7,
"metadata": {
"collapsed": false
},
@ -11,7 +11,7 @@
"%matplotlib inline\n",
"\n",
"from vnpy.trader.app.ctaStrategy.ctaBacktesting import BacktestingEngine, OptimizationSetting, MINUTE_DB_NAME\n",
"#from vnpy.trader.app.ctaStrategy.strategy.strategyAtrRsi import AtrRsiStrategy\n",
"from vnpy.trader.app.ctaStrategy.strategy.strategyAtrRsi import AtrRsiStrategy\n",
"#from vnpy.trader.app.ctaStrategy.strategy.strategyMultiTimeframe import MultiTimeframeStrategy\n",
"from vnpy.trader.app.ctaStrategy.strategy.strategyMultiSignal import MultiSignalStrategy"
]
@ -39,14 +39,14 @@
"# 设置回测使用的数据\n",
"engine.setBacktestingMode(engine.BAR_MODE) # 设置引擎的回测模式为K线\n",
"engine.setDatabase(MINUTE_DB_NAME, 'IF0000') # 设置使用的历史数据库\n",
"engine.setStartDate('20100101') # 设置回测用的数据起始日期"
"engine.setStartDate('20130101') # 设置回测用的数据起始日期"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
"collapsed": false
},
"outputs": [],
"source": [
@ -60,7 +60,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 5,
"metadata": {
"collapsed": false
},
@ -75,25 +75,11 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2017-12-15 23:01:54.728000\t开始载入数据\n",
"2017-12-15 23:01:54.765000\t载入完成数据量0\n",
"2017-12-15 23:01:54.765000\t开始回测\n",
"2017-12-15 23:01:54.765000\t策略初始化完成\n",
"2017-12-15 23:01:54.765000\t策略启动完成\n",
"2017-12-15 23:01:54.765000\t开始回放数据\n",
"2017-12-15 23:01:54.766000\t数据回放结束\n"
]
}
],
"outputs": [],
"source": [
"# 运行回测\n",
"engine.runBacktesting() # 运行回测"
@ -139,23 +125,42 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2018-01-08 17:21:35.783000\t------------------------------\n",
"2018-01-08 17:21:35.784000\t优化结果\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 20, 'atrLength': 12}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 25, 'atrLength': 12}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 30, 'atrLength': 12}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 20, 'atrLength': 14}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 25, 'atrLength': 14}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 30, 'atrLength': 14}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 20, 'atrLength': 16}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 25, 'atrLength': 16}目标0\n",
"2018-01-08 17:21:35.784000\t参数{'rsiLength': 5, 'atrMa': 30, 'atrLength': 16}目标0\n",
"耗时214.858999968\n"
]
}
],
"source": [
"# 优化配置\n",
"setting = OptimizationSetting() # 新建一个优化任务设置对象\n",
"setting.setOptimizeTarget('capital') # 设置优化排序的目标是策略净盈利\n",
"setting.addParameter('atrLength', 12, 20, 2) # 增加第一个优化参数atrLength起始12结束20步进2\n",
"setting.setOptimizeTarget('totalNetPnl') # 设置优化排序的目标是策略净盈利\n",
"setting.addParameter('atrLength', 12, 16, 2) # 增加第一个优化参数atrLength起始12结束20步进2\n",
"setting.addParameter('atrMa', 20, 30, 5) # 增加第二个优化参数atrMa起始20结束30步进5\n",
"setting.addParameter('rsiLength', 5) # 增加一个固定数值的参数\n",
"\n",
"# 执行多进程优化\n",
"import time\n",
"start = time.time()\n",
"engine.runParallelOptimization(AtrRsiStrategy, setting)\n",
"resultList = engine.runParallelOptimization(AtrRsiStrategy, setting)\n",
"print u'耗时:%s' %(time.time()-start)"
]
},
@ -166,7 +171,11 @@
"collapsed": true
},
"outputs": [],
"source": []
"source": [
"# 显示优化的所有统计数据\n",
"for result in resultList:\n",
" print u'参数:%s目标%s统计数据%s' %(result[0], result[1], result[2])"
]
}
],
"metadata": {

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,9 @@
# encoding: UTF-8
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import multiprocessing
from time import sleep
from datetime import datetime, time

View File

@ -56,10 +56,7 @@ def generateVtBar(row):
bar.volume = row['volume']
bar.date = str(row['trade_date'])
if row['time'] == 0:
bar.time = '00:00:00'
else:
bar.time = str(row['time'])
bar.time = str(row['time']).rjust(6, '0')
bar.datetime = datetime.strptime(' '.join([bar.date, bar.time]), '%Y%m%d %H%M%S')
return bar

View File

@ -3,7 +3,7 @@
"MONGO_PORT": 27017,
"SHCIFCO_IP": "180.169.126.123",
"SHCIFCO_PORT": "45065",
"SHCIFCO_PORT": "10083",
"SHCIFCO_TOKEN": "请联系上海中期申请",
"SYMBOLS": ["cu1707", "cu1708", "cu1709", "cu1712",

View File

@ -23,7 +23,7 @@ class ShcifcoApi(object):
self.port = port
self.token = token
self.service = 'ShcifcoApi'
self.service = 'shcifco/dataapi'
self.domain = 'http://' + ':'.join([self.ip, self.port])
#----------------------------------------------------------------------
@ -59,7 +59,7 @@ class ShcifcoApi(object):
'askPrice': float(l[4]),
'askVolume': int(l[5]),
'volume': int(l[6]),
'openInterest': int(l[7])
'openInterest': int(float(l[7]))
}
return d
@ -97,14 +97,14 @@ class ShcifcoApi(object):
'low': float(l[4]),
'close': float(l[5]),
'volume': int(l[6]),
'openInterest': int(l[7])
'openInterest': int(float(l[7]))
}
return d
#----------------------------------------------------------------------
def getHisBar(self, symbol, num, date='', period=''):
"""获取历史K线数据"""
path = 'hisbar'
path = 'hisminbar'
# 默认参数
params = {
@ -139,7 +139,7 @@ class ShcifcoApi(object):
'low': float(barData[5]),
'close': float(barData[6]),
'volume': int(barData[7]),
'openInterest': int(barData[8]),
'openInterest': int(float(barData[8])),
'date': barData[9] # natural day
}
barList.append(d)

153
vnpy/pricing/bs.py Normal file
View File

@ -0,0 +1,153 @@
# encoding: UTF-8
'''
Black-Scholes期权定价模型主要用于标的物为股票的欧式期权的定价
变量说明
s标的物股票价格
k行权价
r无风险利率
t剩余到期时间
v隐含波动率
cp期权类型+1/-1对应call/put
price期权价格
出于开发演示的目的本文件中的希腊值计算基于简单数值差分法
运算效率一般实盘中建议使用更高速的算法
本文件中的希腊值计算结果没有采用传统的模型价格数值而是采用
了实盘交易中更为实用的百分比变动数值具体定义如下
delta当f变动1%price的变动
gamma当f变动1%delta的变动
theta当t变动1天时price的变动国内交易日每年240天
vega当v涨跌1个点时price的变动如从16%涨到17%
'''
from __future__ import division
from scipy import stats
from math import (log, pow, sqrt, exp)
cdf = stats.norm.cdf
# 计算希腊值和隐含波动率时用的参数
STEP_CHANGE = 0.001
STEP_UP = 1 + STEP_CHANGE
STEP_DOWN = 1 - STEP_CHANGE
STEP_DIFF = STEP_CHANGE * 2
DX_TARGET = 0.00001
#----------------------------------------------------------------------
def calculatePrice(s, k, r, t, v, cp):
"""计算期权价格"""
# 如果波动率为0则直接返回期权空间价值
if v <= 0:
return max(0, cp * (s - k))
d1 = (log(s / k) + (r + 0.5 * pow(v, 2)) * t) / (v * sqrt(t))
d2 = d1 - v * sqrt(t)
price = cp * (s * cdf(cp * d1) - k * cdf(cp * d2) * exp(-r * t))
return price
#----------------------------------------------------------------------
def calculateDelta(s, k, r, t, v, cp):
"""计算Delta值"""
price1 = calculatePrice(s*STEP_UP, k, r, t, v, cp)
price2 = calculatePrice(s*STEP_DOWN, k, r, t, v, cp)
delta = (price1 - price2) / (s * STEP_DIFF) * (s * 0.01)
return delta
#----------------------------------------------------------------------
def calculateGamma(s, k, r, t, v, cp):
"""计算Gamma值"""
delta1 = calculateDelta(s*STEP_UP, k, r, t, v, cp)
delta2 = calculateDelta(s*STEP_DOWN, k, r, t, v, cp)
gamma = (delta1 - delta2) / (s * STEP_DIFF) * pow(s, 2) * 0.0001
return gamma
#----------------------------------------------------------------------
def calculateTheta(s, k, r, t, v, cp):
"""计算Theta值"""
price1 = calculatePrice(s, k, r, t*STEP_UP, v, cp)
price2 = calculatePrice(s, k, r, t*STEP_DOWN, v, cp)
theta = -(price1 - price2) / (t * STEP_DIFF * 240)
return theta
#----------------------------------------------------------------------
def calculateVega(s, k, r, t, v, cp):
"""计算Vega值"""
vega = calculateOriginalVega(s, k, r, t, v, cp) / 100
return vega
#----------------------------------------------------------------------
def calculateOriginalVega(s, k, r, t, v, cp):
"""计算原始vega值"""
price1 = calculatePrice(s, k, r, t, v*STEP_UP, cp)
price2 = calculatePrice(s, k, r, t, v*STEP_DOWN, cp)
vega = (price1 - price2) / (v * STEP_DIFF)
return vega
#----------------------------------------------------------------------
def calculateGreeks(s, k, r, t, v, cp):
"""计算期权的价格和希腊值"""
price = calculatePrice(s, k, r, t, v, cp)
delta = calculateDelta(s, k, r, t, v, cp)
gamma = calculateGamma(s, k, r, t, v, cp)
theta = calculateTheta(s, k, r, t, v, cp)
vega = calculateVega(s, k, r, t, v, cp)
return price, delta, gamma, theta, vega
#----------------------------------------------------------------------
def calculateImpv(price, s, k, r, t, cp):
"""计算隐含波动率"""
# 检查期权价格必须为正数
if price <= 0:
return 0
# 检查期权价格是否满足最小价值(即到期行权价值)
meet = False
if cp == 1 and (price > (s - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - s):
meet = True
# 若不满足最小价值则直接返回0
if not meet:
return 0
# 采用Newton Raphson方法计算隐含波动率
v = 0.3 # 初始波动率猜测
for i in range(50):
# 计算当前猜测波动率对应的期权价格和vega值
p = calculatePrice(s, k, r, t, v, cp)
vega = calculateOriginalVega(s, k, r, t, v, cp)
# 如果vega过小接近0则直接返回
if not vega:
break
# 计算误差
dx = (price - p) / vega
# 检查误差是否满足要求,若满足则跳出循环
if abs(dx) < DX_TARGET:
break
# 计算新一轮猜测的波动率
v += dx
# 检查波动率计算结果非负
if v <= 0:
return 0
# 保留4位小数
v = round(v, 4)
return v

181
vnpy/pricing/crr.py Normal file
View File

@ -0,0 +1,181 @@
# encoding: UTF-8
'''
Cox-Ross-Rubinstein二叉树期权定价模型主要用于标的物为期货的美式期权的定价
变量说明
f标的物期货价格
k行权价
r无风险利率
t剩余到期时间
v隐含波动率
cp期权类型+1/-1对应call/put
n: 二叉树高度
price期权价格
出于开发演示的目的本文件中的希腊值计算基于简单数值差分法
运算效率一般实盘中建议使用更高速的算法
本文件中的希腊值计算结果没有采用传统的模型价格数值而是采用
了实盘交易中更为实用的百分比变动数值具体定义如下
delta当f变动1%price的变动
gamma当f变动1%delta的变动
theta当t变动1天时price的变动国内交易日每年240天
vega当v涨跌1个点时price的变动如从16%涨到17%
'''
from __future__ import division
import numpy as np
from math import (isnan, exp, sqrt, pow)
# 计算希腊值和隐含波动率时用的参数
STEP_CHANGE = 0.001
STEP_UP = 1 + STEP_CHANGE
STEP_DOWN = 1 - STEP_CHANGE
STEP_DIFF = STEP_CHANGE * 2
DX_TARGET = 0.00001
#----------------------------------------------------------------------
def generateTree(f, k, r, t, v, cp, n):
"""生成二叉树"""
dt = t / n
u = exp(v * sqrt(dt))
d = 1 / u
a = exp(r * dt)
uTree = np.zeros((n+1,n+1))
oTree = np.zeros((n+1,n+1))
# 计算风险平价概率
p = (a - d) / (u - d)
p1 = p / a
p2 = (1 - p) / a
# 计算标的树
uTree[0, 0] = f
for i in range(1, n+1):
uTree[0, i] = uTree[0, i-1] * u
for j in range(1, i+1):
uTree[j, i] = uTree[j-1, i-1] * d
# 计算期权树
for j in range(n+1):
oTree[j, n] = max(0, cp * (uTree[j, n]-k))
for i in range(n-1,-1,-1):
for j in range(i+1):
oTree[j, i] = max((p1 * oTree[j, i+1] + p2 * oTree[j+1, i+1]), # 美式期权存续价值
cp * (uTree[j, i] - k)) # 美式期权行权价值
# 返回期权树和标的物树结果
return oTree, uTree
#----------------------------------------------------------------------
def calculatePrice(f, k, r, t, v, cp, n=15):
"""计算期权价格"""
oTree, uTree = calculatePrice(f, k, r, t, v, cp)
return oTree[0, 0]
#----------------------------------------------------------------------
def calculateDelta(f, k, r, t, v, cp, n=15):
"""计算Delta值"""
price1 = calculatePrice(f*STEP_UP, k, r, t, v, cp, n)
price2 = calculatePrice(f*STEP_DOWN, k, r, t, v, cp, n)
delta = (price1 - price2) / (f * STEP_DIFF) * (f * 0.01)
return delta
#----------------------------------------------------------------------
def calculateGamma(f, k, r, t, v, cp, n=15):
"""计算Gamma值"""
delta1 = calculateDelta(f*STEP_UP, k, r, t, v, cp, n)
delta2 = calculateDelta(f*STEP_DOWN, k, r, t, v, cp, n)
gamma = (delta1 - delta2) / (f * STEP_DIFF) * pow(f, 2) * 0.0001
return gamma
#----------------------------------------------------------------------
def calculateTheta(f, k, r, t, v, cp, n=15):
"""计算Theta值"""
price1 = calculatePrice(f, k, r, t*STEP_UP, v, cp, n)
price2 = calculatePrice(f, k, r, t*STEP_DOWN, v, cp, n)
theta = -(price1 - price2) / (t * STEP_DIFF * 240)
return theta
#----------------------------------------------------------------------
def calculateVega(f, k, r, t, v, cp, n=15):
"""计算Vega值"""
vega = calculateOriginalVega(f, k, r, t, v, cp, n) / 100
return vega
#----------------------------------------------------------------------
def calculateOriginalVega(f, k, r, t, v, cp, n=15):
"""计算原始vega值"""
price1 = calculatePrice(f, k, r, t, v*STEP_UP, cp, n)
price2 = calculatePrice(f, k, r, t, v*STEP_DOWN, cp, n)
vega = (price1 - price2) / (v * STEP_DIFF)
return vega
#----------------------------------------------------------------------
def calculateGreeks(f, k, r, t, v, cp, n=15):
"""计算期权的价格和希腊值"""
price = calculatePrice(f, k, r, t, v, cp, n)
delta = calculateDelta(f, k, r, t, v, cp, n)
gamma = calculateGamma(f, k, r, t, v, cp, n)
theta = calculateTheta(f, k, r, t, v, cp, n)
vega = calculateVega(f, k, r, t, v, cp, n)
return price, delta, gamma, theta, vega
#----------------------------------------------------------------------
def calculateImpv(price, f, k, r, t, cp, n=15):
"""计算隐含波动率"""
# 检查期权价格必须为正数
if price <= 0:
return 0
# 检查期权价格是否满足最小价值(即到期行权价值)
meet = False
if cp == 1 and (price > (f - k) * exp(-r * t)):
meet = True
elif cp == -1 and (price > k * exp(-r * t) - f):
meet = True
# 若不满足最小价值则直接返回0
if not meet:
return 0
# 采用Newton Raphson方法计算隐含波动率
v = 0.3 # 初始波动率猜测
for i in range(50):
# 计算当前猜测波动率对应的期权价格和vega值
p = calculatePrice(f, k, r, t, v, cp, n)
vega = calculateOriginalVega(f, k, r, t, v, cp, n)
# 如果vega过小接近0则直接返回
if not vega:
break
# 计算误差
dx = (price - p) / vega
# 检查误差是否满足要求,若满足则跳出循环
if abs(dx) < DX_TARGET:
break
# 计算新一轮猜测的波动率
v += dx
# 检查波动率计算结果非负
if v <= 0:
return 0
# 保留4位小数
v = round(v, 4)
return v

View File

@ -877,15 +877,15 @@ class BacktestingEngine(object):
targetValue = d[targetName]
except KeyError:
targetValue = 0
resultList.append(([str(setting)], targetValue))
resultList.append(([str(setting)], targetValue, d))
# 显示结果
resultList.sort(reverse=True, key=lambda result:result[1])
self.output('-' * 30)
self.output(u'优化结果:')
for result in resultList:
self.output(u'%s: %s' %(result[0], result[1]))
return result
self.output(u'参数:%s,目标:%s' %(result[0], result[1]))
return resultList
#----------------------------------------------------------------------
def runParallelOptimization(self, strategyClass, optimizationSetting):
@ -917,7 +917,9 @@ class BacktestingEngine(object):
self.output('-' * 30)
self.output(u'优化结果:')
for result in resultList:
self.output(u'%s: %s' %(result[0], result[1]))
self.output(u'参数:%s,目标:%s' %(result[0], result[1]))
return resultList
#----------------------------------------------------------------------
def updateDailyClose(self, dt, price):
@ -970,6 +972,7 @@ class BacktestingEngine(object):
df['return'] = (np.log(df['balance']) - np.log(df['balance'].shift(1))).fillna(0)
df['highlevel'] = df['balance'].rolling(min_periods=1,window=len(df),center=False).max()
df['drawdown'] = df['balance'] - df['highlevel']
df['ddPercent'] = df['drawdown'] / df['highlevel'] * 100
# 计算统计结果
startDate = df.index[0]
@ -981,6 +984,7 @@ class BacktestingEngine(object):
endBalance = df['balance'].iloc[-1]
maxDrawdown = df['drawdown'].min()
maxDdPercent = df['ddPercent'].min()
totalNetPnl = df['netPnl'].sum()
dailyNetPnl = totalNetPnl / totalDays
@ -998,6 +1002,7 @@ class BacktestingEngine(object):
dailyTradeCount = totalTradeCount / totalDays
totalReturn = (endBalance/self.capital - 1) * 100
annualizedReturn = totalReturn / totalDays * 240
dailyReturn = df['return'].mean() * 100
returnStd = df['return'].std() * 100
@ -1015,6 +1020,7 @@ class BacktestingEngine(object):
'lossDays': lossDays,
'endBalance': endBalance,
'maxDrawdown': maxDrawdown,
'maxDdPercent': maxDdPercent,
'totalNetPnl': totalNetPnl,
'dailyNetPnl': dailyNetPnl,
'totalCommission': totalCommission,
@ -1026,6 +1032,7 @@ class BacktestingEngine(object):
'totalTradeCount': totalTradeCount,
'dailyTradeCount': dailyTradeCount,
'totalReturn': totalReturn,
'annualizedReturn': annualizedReturn,
'dailyReturn': dailyReturn,
'returnStd': returnStd,
'sharpeRatio': sharpeRatio
@ -1052,9 +1059,11 @@ class BacktestingEngine(object):
self.output(u'起始资金:\t%s' % self.capital)
self.output(u'结束资金:\t%s' % formatNumber(result['endBalance']))
self.output(u'总收益率:\t%s' % formatNumber(result['totalReturn']))
self.output(u'总收益率:\t%s%%' % formatNumber(result['totalReturn']))
self.output(u'年化收益:\t%s%%' % formatNumber(result['annualizedReturn']))
self.output(u'总盈亏:\t%s' % formatNumber(result['totalNetPnl']))
self.output(u'最大回撤: \t%s' % formatNumber(result['maxDrawdown']))
self.output(u'百分比最大回撤: %s%%' % formatNumber(result['maxDdPercent']))
self.output(u'总手续费:\t%s' % formatNumber(result['totalCommission']))
self.output(u'总滑点:\t%s' % formatNumber(result['totalSlippage']))
@ -1271,5 +1280,5 @@ def optimize(strategyClass, setting, targetName,
targetValue = d[targetName]
except KeyError:
targetValue = 0
return (str(setting), targetValue)
return (str(setting), targetValue, d)

View File

@ -416,6 +416,9 @@ class CtaEngine(object):
self.tickStrategyDict[strategy.vtSymbol] = l
l.append(strategy)
#----------------------------------------------------------------------
def subscribeMarketData(self, strategy):
"""订阅行情"""
# 订阅合约
contract = self.mainEngine.getContract(strategy.vtSymbol)
if contract:
@ -429,7 +432,7 @@ class CtaEngine(object):
self.mainEngine.subscribe(req, contract.gatewayName)
else:
self.writeCtaLog(u'%s的交易合约%s无法找到' %(name, strategy.vtSymbol))
self.writeCtaLog(u'%s的交易合约%s无法找到' %(strategy.name, strategy.vtSymbol))
#----------------------------------------------------------------------
def initStrategy(self, name):
@ -440,6 +443,8 @@ class CtaEngine(object):
if not strategy.inited:
strategy.inited = True
self.callStrategyFunc(strategy, strategy.onInit)
self.loadSyncData(strategy) # 初始化完成后加载同步数据
self.subscribeMarketData(strategy) # 加载同步数据后再订阅行情
else:
self.writeCtaLog(u'请勿重复初始化策略实例:%s' %name)
else:
@ -521,8 +526,6 @@ class CtaEngine(object):
for setting in l:
self.loadStrategy(setting)
self.loadSyncData()
#----------------------------------------------------------------------
def getStrategyVar(self, name):
"""获取策略当前的变量字典"""
@ -594,15 +597,14 @@ class CtaEngine(object):
self.writeCtaLog(content)
#----------------------------------------------------------------------
def loadSyncData(self):
def loadSyncData(self, strategy):
"""从数据库载入策略的持仓情况"""
for strategy in self.strategyDict.values():
flt = {'name': strategy.name,
'vtSymbol': strategy.vtSymbol}
syncData = self.mainEngine.dbQuery(POSITION_DB_NAME, strategy.className, flt)
if not syncData:
continue
return
d = syncData[0]

View File

@ -10,10 +10,12 @@ import json
import csv
import os
import copy
import traceback
from collections import OrderedDict
from datetime import datetime, timedelta
from Queue import Queue, Empty
from threading import Thread
from pymongo.errors import DuplicateKeyError
from vnpy.event import Event
from vnpy.trader.vtEvent import *
@ -241,7 +243,10 @@ class DrEngine(object):
#self.mainEngine.dbUpdate(dbName, collectionName, d, flt, True)
# 使用insert模式更新数据可能存在时间戳重复的情况需要用户自行清洗
try:
self.mainEngine.dbInsert(dbName, collectionName, d)
except DuplicateKeyError:
self.writeDrLog(u'键值重复插入失败,报错信息:' %traceback.format_exc())
except Empty:
pass

View File

@ -224,8 +224,8 @@ class RmEngine(object):
%(orderReq.symbol, self.orderCancelDict[orderReq.symbol], self.orderCancelLimit))
return False
# 检查保证金比例
if gatewayName in self.marginRatioDict and self.marginRatioDict[gatewayName] >= self.marginRatioLimit:
# 检查保证金比例(只针对开仓委托)
if orderReq.offset == OFFSET_OPEN and gatewayName in self.marginRatioDict and self.marginRatioDict[gatewayName] >= self.marginRatioLimit:
self.writeRiskLog(u'%s接口保证金占比%s,超过限制%s'
%(gatewayName, self.marginRatioDict[gatewayName], self.marginRatioLimit))
return False

View File

@ -261,6 +261,16 @@ class XtpMdApi(QuoteApi):
content = (u'行情服务器连接断开,原因:%s' %reason)
self.writeLog(content)
# 重新连接
n = self.login(self.address, self.port, self.userID, self.password, 1)
if not n:
self.connectionStatus = True
self.loginStatus = True
self.gateway.mdConnected = True
self.writeLog(u'行情服务器登录成功')
else:
self.writeLog(u'行情服务器登录失败,原因:%s' %n)
#----------------------------------------------------------------------
def onError(self, error):
"""错误回报"""
@ -418,7 +428,6 @@ class XtpMdApi(QuoteApi):
os.makedirs(path)
self.createQuoteApi(clientID, path)
print address, port, userID, password
n = self.login(address, port, userID, password, 1)
if not n:
self.connectionStatus = True
@ -501,6 +510,18 @@ class XtpTdApi(TraderApi):
content = (u'交易服务器连接断开,原因:%s' %reason)
self.writeLog(content)
# 发起重新连接
n = self.login(self.address, self.port, self.userID, self.password, 1)
if n:
self.sessionID = n
self.connectionStatus = True
self.loginStatus = True
self.gateway.tdConnected = True
self.writeLog(u'交易服务器登录成功,会话编号:%s' %n)
else:
self.writeLog(u'交易服务器登录失败')
#----------------------------------------------------------------------
def onError(self, data):
"""错误回报"""

View File

@ -112,6 +112,10 @@ class MainWindow(QtWidgets.QMainWindow):
appMenu = menubar.addMenu(vtText.APPLICATION)
for appDetail in self.appDetailList:
# 如果没有应用界面,则不添加菜单按钮
if not appDetail['appWidget']:
continue
function = self.createOpenAppFunction(appDetail)
action = self.createAction(appDetail['appDisplayName'], function, loadIconPath(appDetail['appIco']))
appMenu.addAction(action)

View File

@ -1,5 +1,7 @@
# encoding: UTF-8
from __future__ import division
import os
import shelve
import logging
@ -491,7 +493,8 @@ class DataEngine(object):
if vtSymbol in self.detailDict:
detail = self.detailDict[vtSymbol]
else:
detail = PositionDetail(vtSymbol)
contract = self.getContract(vtSymbol)
detail = PositionDetail(vtSymbol, contract)
self.detailDict[vtSymbol] = detail
# 设置持仓细节的委托转换模式
@ -647,9 +650,19 @@ class PositionDetail(object):
MODE_TDPENALTY = 'tdpenalty' # 平今惩罚
#----------------------------------------------------------------------
def __init__(self, vtSymbol):
def __init__(self, vtSymbol, contract=None):
"""Constructor"""
self.vtSymbol = vtSymbol
self.symbol = EMPTY_STRING
self.exchange = EMPTY_STRING
self.name = EMPTY_UNICODE
self.size = 1
if contract:
self.symbol = contract.symbol
self.exchange = contract.exchange
self.name = contract.name
self.size = contract.size
self.longPos = EMPTY_INT
self.longYd = EMPTY_INT
@ -657,6 +670,8 @@ class PositionDetail(object):
self.longPosFrozen = EMPTY_INT
self.longYdFrozen = EMPTY_INT
self.longTdFrozen = EMPTY_INT
self.longPnl = EMPTY_FLOAT
self.longPrice = EMPTY_FLOAT
self.shortPos = EMPTY_INT
self.shortYd = EMPTY_INT
@ -664,6 +679,10 @@ class PositionDetail(object):
self.shortPosFrozen = EMPTY_INT
self.shortYdFrozen = EMPTY_INT
self.shortTdFrozen = EMPTY_INT
self.shortPnl = EMPTY_FLOAT
self.shortPrice = EMPTY_FLOAT
self.lastPrice = EMPTY_FLOAT
self.mode = self.MODE_NORMAL
self.exchange = EMPTY_STRING
@ -720,8 +739,10 @@ class PositionDetail(object):
self.longYd += self.longTd
self.longTd = 0
# 汇总今昨
# 汇总
self.calculatePrice(trade)
self.calculatePosition()
self.calculatePnl()
#----------------------------------------------------------------------
def updateOrder(self, order):
@ -745,10 +766,14 @@ class PositionDetail(object):
self.longPos = pos.position
self.longYd = pos.ydPosition
self.longTd = self.longPos - self.longYd
self.longPnl = pos.positionProfit
self.longPrice = pos.price
elif pos.direction is DIRECTION_SHORT:
self.shortPos = pos.position
self.shortYd = pos.ydPosition
self.shortTd = self.shortPos - self.shortYd
self.shortPnl = pos.positionProfit
self.shortPrice = pos.price
#self.output()
@ -773,6 +798,34 @@ class PositionDetail(object):
# 计算冻结量
self.calculateFrozen()
#----------------------------------------------------------------------
def updateTick(self, tick):
"""行情更新"""
self.lastPrice = tick.lastPrice
self.calculatePnl()
#----------------------------------------------------------------------
def calculatePnl(self):
"""计算持仓盈亏"""
self.longPnl = self.longPos * (self.lastPrice - self.longPrice) * self.size
self.shortPnl = self.shortPos * (self.shortPrice - self.lastPrice) * self.size
#----------------------------------------------------------------------
def calculatePrice(self, trade):
"""计算持仓均价(基于成交数据)"""
# 只有开仓会影响持仓均价
if trade.offset == OFFSET_OPEN:
if trade.direction == DIRECTION_LONG:
cost = self.longPrice * self.longPos
cost += trade.volume * trade.price
newPos = self.longPos + trade.volume
self.longPrice = cost / newPos
else:
cost = self.shortPrice * self.shortPos
cost += trade.volume * trade.price
newPos = self.shortPos + trade.volume
self.shortPrice = cost / newPos
#----------------------------------------------------------------------
def calculatePosition(self):
"""计算持仓情况"""