Merge branch 'dev' of https://github.com/JaysonAlbert/vnpy into dev
This commit is contained in:
commit
d2f11eba3b
@ -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
@ -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
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -22,8 +22,8 @@ class ShcifcoApi(object):
|
||||
self.ip = ip
|
||||
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
153
vnpy/pricing/bs.py
Normal 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
181
vnpy/pricing/crr.py
Normal 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
|
@ -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'最大回撤: \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)
|
||||
|
@ -416,20 +416,23 @@ class CtaEngine(object):
|
||||
self.tickStrategyDict[strategy.vtSymbol] = l
|
||||
l.append(strategy)
|
||||
|
||||
# 订阅合约
|
||||
contract = self.mainEngine.getContract(strategy.vtSymbol)
|
||||
if contract:
|
||||
req = VtSubscribeReq()
|
||||
req.symbol = contract.symbol
|
||||
req.exchange = contract.exchange
|
||||
|
||||
# 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取
|
||||
req.currency = strategy.currency
|
||||
req.productClass = strategy.productClass
|
||||
|
||||
self.mainEngine.subscribe(req, contract.gatewayName)
|
||||
else:
|
||||
self.writeCtaLog(u'%s的交易合约%s无法找到' %(name, strategy.vtSymbol))
|
||||
#----------------------------------------------------------------------
|
||||
def subscribeMarketData(self, strategy):
|
||||
"""订阅行情"""
|
||||
# 订阅合约
|
||||
contract = self.mainEngine.getContract(strategy.vtSymbol)
|
||||
if contract:
|
||||
req = VtSubscribeReq()
|
||||
req.symbol = contract.symbol
|
||||
req.exchange = contract.exchange
|
||||
|
||||
# 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取
|
||||
req.currency = strategy.currency
|
||||
req.productClass = strategy.productClass
|
||||
|
||||
self.mainEngine.subscribe(req, contract.gatewayName)
|
||||
else:
|
||||
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:
|
||||
@ -520,8 +525,6 @@ class CtaEngine(object):
|
||||
|
||||
for setting in l:
|
||||
self.loadStrategy(setting)
|
||||
|
||||
self.loadSyncData()
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def getStrategyVar(self, name):
|
||||
@ -594,21 +597,20 @@ 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
|
||||
|
||||
d = syncData[0]
|
||||
|
||||
for key in strategy.syncList:
|
||||
if key in d:
|
||||
strategy.__setattr__(key, d[key])
|
||||
flt = {'name': strategy.name,
|
||||
'vtSymbol': strategy.vtSymbol}
|
||||
syncData = self.mainEngine.dbQuery(POSITION_DB_NAME, strategy.className, flt)
|
||||
|
||||
if not syncData:
|
||||
return
|
||||
|
||||
d = syncData[0]
|
||||
|
||||
for key in strategy.syncList:
|
||||
if key in d:
|
||||
strategy.__setattr__(key, d[key])
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
def roundToPriceTick(self, priceTick, price):
|
||||
|
@ -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模式更新数据,可能存在时间戳重复的情况,需要用户自行清洗
|
||||
self.mainEngine.dbInsert(dbName, collectionName, d)
|
||||
try:
|
||||
self.mainEngine.dbInsert(dbName, collectionName, d)
|
||||
except DuplicateKeyError:
|
||||
self.writeDrLog(u'键值重复插入失败,报错信息:' %traceback.format_exc())
|
||||
except Empty:
|
||||
pass
|
||||
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""错误回报"""
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
||||
@ -772,6 +797,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):
|
||||
|
Loading…
Reference in New Issue
Block a user