[update] 兼容各类接口得订单逻辑;升级天勤
This commit is contained in:
parent
55b207f5db
commit
39a231cbdf
@ -10,6 +10,10 @@ github 链接: https://github.com/msincenselee/vnpy
|
||||
gitee 链接: https://gitee.com/vnpy2/vnpy
|
||||
|
||||
###Fork版本主要改进如下
|
||||
19、期权CTA引擎
|
||||
|
||||
-vnpy.app.cta_option
|
||||
+ 支持股票ETF期权的CTA策略
|
||||
|
||||
18、CTA股票选股引擎
|
||||
|
||||
@ -163,11 +167,13 @@ QQ/Wechat:28888502
|
||||
|
||||
系列在线课程
|
||||
|
||||
2022 期货CTA课程: http://www.uquant.org/course/49
|
||||
|
||||
2021 股票CTA实战课程:http://www.uquant.org/course/49
|
||||
|
||||
2021 期货缠论高级课程:http://www.uquant.org/course/48
|
||||
|
||||
2021 数字货币CTA课程:http://www.uquant.org/course/46
|
||||
2021 数字CTA课程:http://www.uquant.org/course/46
|
||||
|
||||
2020 期货套利课程:http://www.uquant.org/course/43
|
||||
|
||||
|
6
prod/Matrix08/win_start.bat
Normal file
6
prod/Matrix08/win_start.bat
Normal file
@ -0,0 +1,6 @@
|
||||
:: 启动脚本
|
||||
:: 进入盘、目录
|
||||
c:
|
||||
cd C:\GitHub\msincenselee_vnpy\prod\Matrix08
|
||||
:: 启动无界面程序
|
||||
C:\Users\incen\AppData\Local\conda\conda\envs\py37\python run_service.py
|
@ -11,7 +11,7 @@ import pandas as pd
|
||||
from contextlib import closing
|
||||
from datetime import datetime, timedelta
|
||||
import argparse
|
||||
from tqsdk import TqApi, TqSim
|
||||
from tqsdk import TqApi, TqSim,TqAuth
|
||||
|
||||
vnpy_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
if vnpy_root not in sys.path:
|
||||
@ -21,7 +21,7 @@ os.environ["VNPY_TESTING"] = "1"
|
||||
|
||||
from vnpy.data.tdx.tdx_future_data import get_future_contracts, Exchange
|
||||
from vnpy.trader.utility import get_csv_last_dt, get_underlying_symbol, extract_vt_symbol
|
||||
from vnpy.data.tq.downloader import DataDownloader
|
||||
from vnpy.data.tq.downloader import DataDownloader,get_account_config
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) <= 1:
|
||||
@ -36,9 +36,9 @@ if __name__ == "__main__":
|
||||
if len(args.symbol) == 0:
|
||||
print('下载合约未设定 参数 -s rb2010')
|
||||
os._exit(0)
|
||||
|
||||
auth_dict = get_account_config()
|
||||
# 开始下载(使用快期的免费行情websocket)
|
||||
api = TqApi(account=TqSim(), url="wss://u.shinnytech.com/t/md/front/mobile")
|
||||
api = TqApi(account=TqSim(), auth=TqAuth(auth_dict['user_name'],auth_dict['password'])) # url="wss://u.shinnytech.com/t/md/front/mobile"
|
||||
download_tasks = {}
|
||||
begin_date = datetime.strptime(args.begin, '%Y%m%d')
|
||||
end_date = datetime.strptime(args.end, '%Y%m%d')
|
||||
|
@ -412,6 +412,10 @@ class CtaOptionEngine(BaseEngine):
|
||||
if not strategy:
|
||||
if order.vt_orderid in self.internal_orderids:
|
||||
self.write_log(f'委托更新 => 内部仓位: {print_dict(order.__dict__)}')
|
||||
if order.sys_orderid and order.sys_orderid != order.orderid and order.sys_orderid not in self.internal_orderids:
|
||||
self.write_log(f'添加系统编号 {order.sys_orderid}=> 内部订单')
|
||||
self.internal_orderids.add(order.sys_orderid)
|
||||
|
||||
# self.write_log(f'当前策略侦听委托单:{list(self.orderid_strategy_map.keys())}')
|
||||
if order.type != OrderType.STOP:
|
||||
if order.status in [Status.ALLTRADED, Status.CANCELLED, Status.REJECTED]:
|
||||
@ -425,6 +429,9 @@ class CtaOptionEngine(BaseEngine):
|
||||
self.write_log(f'委托更新 => 系统账号 => {print_dict(order.__dict__)}')
|
||||
return
|
||||
self.write_log(f'委托更新:{order.vt_orderid} => 策略:{strategy.strategy_name}')
|
||||
if len(order.sys_orderid) > 0 and order.sys_orderid not in self.orderid_strategy_map:
|
||||
self.write_log(f'登记系统委托号 {order.sys_orderid} => 策略:{strategy.strategy_name} 映射')
|
||||
|
||||
# Remove vt_orderid if order is no longer active.
|
||||
vt_orderids = self.strategy_orderid_map[strategy.strategy_name]
|
||||
if order.vt_orderid in vt_orderids and not order.is_active():
|
||||
@ -468,7 +475,7 @@ class CtaOptionEngine(BaseEngine):
|
||||
if not strategy:
|
||||
|
||||
# 属于内部单子
|
||||
if trade.vt_orderid in self.internal_orderids:
|
||||
if trade.vt_orderid in self.internal_orderids or trade.sys_orderid in self.internal_orderids:
|
||||
cur_pos = self.net_pos_holding.get(trade.vt_symbol, 0)
|
||||
if trade.direction == Direction.LONG:
|
||||
new_pos = cur_pos + trade.volume
|
||||
@ -478,13 +485,18 @@ class CtaOptionEngine(BaseEngine):
|
||||
self.write_log(f'成交单:trade:{print_dict(trade.__dict__)}')
|
||||
self.net_pos_holding.update({trade.vt_symbol: new_pos})
|
||||
self.save_internal_data()
|
||||
return
|
||||
|
||||
if trade.sys_orderid and trade.sys_orderid in self.orderid_strategy_map:
|
||||
self.write_log(f'使用系统委托单号{trade.sys_orderid} => 策略')
|
||||
strategy = self.orderid_strategy_map.get(trade.sys_orderid, None)
|
||||
|
||||
# 可能是其他实例得
|
||||
else:
|
||||
if not strategy:
|
||||
self.write_log(f'成交更新 => 没有对应的策略设置:trade:{trade.__dict__}')
|
||||
self.write_log(f'成交更新 => 当前策略侦听委托单:{list(self.orderid_strategy_map.keys())}')
|
||||
self.write_log(f'成交更新 => 当前内部订单清单:{self.internal_orderids}')
|
||||
return
|
||||
return
|
||||
|
||||
self.write_log(f'成交更新 =>:{trade.vt_orderid} => 策略:{strategy.strategy_name}')
|
||||
|
||||
|
@ -353,6 +353,11 @@ class CtaEngine(BaseEngine):
|
||||
self.write_log(f'当前策略侦听委托单:{list(self.orderid_strategy_map.keys())}')
|
||||
return
|
||||
self.write_log(f'委托更新:{order.vt_orderid} => 策略:{strategy.strategy_name}')
|
||||
|
||||
if len(order.sys_orderid) > 0 and order.sys_orderid not in self.orderid_strategy_map:
|
||||
self.write_log(f'登记系统委托号 {order.sys_orderid} => 策略:{strategy.strategy_name} 映射')
|
||||
self.orderid_strategy_map.update({order.sys_orderid: strategy})
|
||||
|
||||
# Remove vt_orderid if order is no longer active.
|
||||
vt_orderids = self.strategy_orderid_map[strategy.strategy_name]
|
||||
if order.vt_orderid in vt_orderids and not order.is_active():
|
||||
@ -388,11 +393,16 @@ class CtaEngine(BaseEngine):
|
||||
|
||||
strategy = self.orderid_strategy_map.get(trade.vt_orderid, None)
|
||||
if not strategy:
|
||||
self.write_log(f'成交单没有对应的策略设置:trade:{trade.__dict__}')
|
||||
self.write_log(f'当前策略侦听委托单:{list(self.orderid_strategy_map.keys())}')
|
||||
return
|
||||
if trade.sys_orderid and trade.sys_orderid in self.orderid_strategy_map:
|
||||
self.write_log(f'使用系统委托单号{trade.sys_orderid} => 策略')
|
||||
strategy = self.orderid_strategy_map.get(trade.sys_orderid, None)
|
||||
|
||||
self.write_log(f'成交更新:{trade.vt_orderid} => 策略:{strategy.strategy_name}')
|
||||
if not strategy:
|
||||
self.write_log(f'成交单没有对应的策略设置:trade:{trade.__dict__}')
|
||||
self.write_log(f'当前策略侦听委托单:{list(self.orderid_strategy_map.keys())}')
|
||||
return
|
||||
|
||||
self.write_log(f'成交更新,本地委托{trade.vt_orderid},系统委托{trade.sys_orderid} => 策略:{strategy.strategy_name}')
|
||||
|
||||
# Update strategy pos before calling on_trade method
|
||||
# 取消外部干预策略pos,由策略自行完成更新
|
||||
|
@ -425,6 +425,14 @@ class BackTestingEngine(object):
|
||||
@lru_cache()
|
||||
def get_size(self, vt_symbol: str):
|
||||
"""查询合约的size"""
|
||||
if vt_symbol not in self.size:
|
||||
symbol, exchange = extract_vt_symbol(vt_symbol)
|
||||
if symbol in self.size:
|
||||
return self.size.get(symbol)
|
||||
else:
|
||||
underly_symbol = get_underlying_symbol(symbol).upper()
|
||||
return self.size.get(f'{underly_symbol}99',10)
|
||||
|
||||
return self.size.get(vt_symbol, 10)
|
||||
|
||||
def set_price(self, vt_symbol: str, price: float):
|
||||
@ -440,11 +448,16 @@ class BackTestingEngine(object):
|
||||
if rate >= 0.1:
|
||||
self.fix_commission.update({vt_symbol: rate})
|
||||
|
||||
@lru_cache()
|
||||
def get_commission_rate(self, vt_symbol: str):
|
||||
""" 获取保证金比例,缺省万分之一"""
|
||||
if vt_symbol not in self.commission_rate:
|
||||
symbol, exchange = extract_vt_symbol(vt_symbol)
|
||||
return self.commission_rate.get(symbol, float(0.0001))
|
||||
if symbol in self.commission_rate:
|
||||
return self.commission_rate.get(symbol)
|
||||
else:
|
||||
underly_symbol = get_underlying_symbol(symbol).upper()
|
||||
return self.commission_rate.get(f'{underly_symbol}99', float(0.0001))
|
||||
return self.commission_rate.get(vt_symbol, float(0.0001))
|
||||
|
||||
def get_fix_commission(self, vt_symbol: str):
|
||||
@ -887,6 +900,16 @@ class BackTestingEngine(object):
|
||||
self.write_log(f"新增订阅指数合约:{setting['idx_symbol']}")
|
||||
self.subscribe_symbol(strategy_name=strategy_name, vt_symbol=setting['idx_symbol'])
|
||||
|
||||
# 如果act_vt_symbol不再列表中,需要订阅
|
||||
if 'act_vt_symbol' in setting.keys() and setting['act_vt_symbol'] not in self.symbol_strategy_map.keys():
|
||||
self.write_log(f"新增订阅act_vt_symbol合约:{setting['act_vt_symbol']}")
|
||||
self.subscribe_symbol(strategy_name=strategy_name, vt_symbol=setting['act_vt_symbol'])
|
||||
|
||||
# 如果pas_vt_symbol不再列表中,需要订阅
|
||||
if 'pas_vt_symbol' in setting.keys() and setting['pas_vt_symbol'] not in self.symbol_strategy_map.keys():
|
||||
self.write_log(f"新增订阅pas_vt_symbol合约:{setting['pas_vt_symbol']}")
|
||||
self.subscribe_symbol(strategy_name=strategy_name, vt_symbol=setting['pas_vt_symbol'])
|
||||
|
||||
if strategy_setting.get('auto_init', False):
|
||||
self.write_log(u'自动初始化策略')
|
||||
strategy.on_init()
|
||||
@ -1264,8 +1287,8 @@ class BackTestingEngine(object):
|
||||
target=self.get_price_tick(vt_symbol)) - self.get_price_tick(
|
||||
vt_symbol) # 在当前时间点前发出的卖出委托可能的最优成交价
|
||||
else:
|
||||
buy_cross_price = tick.last_price
|
||||
sell_cross_price = tick.last_price
|
||||
buy_cross_price = tick.last_price if not tick.ask_price_1 else tick.ask_price_1
|
||||
sell_cross_price = tick.last_price if not tick.bid_price_1 else tick.bid_price_1
|
||||
buy_best_cross_price = tick.last_price
|
||||
sell_best_cross_price = tick.last_price
|
||||
|
||||
@ -2317,6 +2340,8 @@ class BackTestingEngine(object):
|
||||
|
||||
result_info.update({u'Sharpe Ratio': d['sharpe']})
|
||||
self.output(u'Sharpe Ratio:\t%s' % format_number(d['sharpe']))
|
||||
result_file = os.path.abspath(os.path.join(self.get_logs_path(), '{}_result.csv'.format(self.test_name)))
|
||||
self.append_data(result_file, result_info)
|
||||
|
||||
# 保存回测结果/交易记录/日线统计 至数据库
|
||||
self.save_result_to_mongo(result_info)
|
||||
|
@ -419,9 +419,9 @@ class CtaSpreadTemplate(CtaTemplate):
|
||||
if order_info is not None:
|
||||
# 委托单记录 =》 找到 Grid
|
||||
grid = order_info.get('grid')
|
||||
if grid and order_info.get('offset', None) == Offset.OPEN:
|
||||
if grid :
|
||||
# 更新平均开仓/平仓得价格,数量
|
||||
self.update_grid_trade(trade, grid)
|
||||
self.update_grid_trade(order_info.get('offset', None), trade, grid)
|
||||
|
||||
def update_pos(self, price, volume, operation, dt):
|
||||
"""更新持仓组件得pos"""
|
||||
@ -535,14 +535,14 @@ class CtaSpreadTemplate(CtaTemplate):
|
||||
"""修正order被拆单得情况"""
|
||||
order_info = self.active_orders.get(order.vt_orderid, None)
|
||||
if order_info:
|
||||
volume = order_info.get('volume')
|
||||
traded = order_info.get('traded')
|
||||
volume = order_info.get('volume') # 原始委托数量
|
||||
traded = order_info.get('traded') # 原始委托中,已成交的数量
|
||||
if volume != order.volume:
|
||||
self.write_log(f'调整{order.vt_orderid} {order.vt_symbol} 委托:{volume}=>{order.volume}')
|
||||
self.write_log(f'更新未完成订单{order.vt_orderid} {order.vt_symbol} 的委托数量:{volume}=>{order.volume}')
|
||||
order_info.update({'volume': order.volume})
|
||||
if traded != order.traded:
|
||||
self.write_log(f'{order.vt_orderid} {order.vt_symbol} 已成交 :{traded}=>{traded + order.traded}')
|
||||
order_info.update({'volume': traded + order.traded})
|
||||
self.write_log(f'更新未完成订单{order.vt_orderid} {order.vt_symbol} 的已成交数量 :{traded}=>{traded + order.traded}')
|
||||
order_info.update({'traded': traded + order.traded})
|
||||
|
||||
def on_order(self, order: OrderData):
|
||||
"""报单更新"""
|
||||
@ -582,9 +582,9 @@ class CtaSpreadTemplate(CtaTemplate):
|
||||
else:
|
||||
self.write_error(u'委托单{}不在策略的未完成订单列表中:{}'.format(order.vt_orderid, self.active_orders))
|
||||
|
||||
def update_grid_trade(self, trade: TradeData, grid: CtaGrid):
|
||||
def update_grid_trade(self, offset: Offset, trade: TradeData, grid: CtaGrid):
|
||||
"""更新网格内,主动腿/被动腿得开平仓信息"""
|
||||
if trade.offset == Offset.OPEN:
|
||||
if offset == Offset.OPEN:
|
||||
# 更新开仓均价/数量
|
||||
if trade.vt_symbol == self.act_vt_symbol:
|
||||
opened_price = grid.snapshot.get('act_open_price', 0)
|
||||
@ -660,7 +660,7 @@ class CtaSpreadTemplate(CtaTemplate):
|
||||
grid.traded_volume = 0
|
||||
|
||||
# 平仓完毕(cover, sell)
|
||||
if order.offset != Offset.OPEN:
|
||||
if order_info.get("offset", None) != Offset.OPEN:
|
||||
grid.open_status = False
|
||||
grid.close_status = True
|
||||
|
||||
|
@ -2496,7 +2496,10 @@ class CtaLineBar(object):
|
||||
def __count_ama(self):
|
||||
"""计算K线的卡夫曼自适应AMA1
|
||||
如何测量价格变动的速率。
|
||||
采用的方法是,在一定的周期内,计算每个周期价格的变动的累加,用整个周期的总体价格变动除以每个周期价格变动的累加,我们采用这个数字作为价格变化的速率。如果股票持续上涨或下跌,那么变动的速率就是1;如果股票在一定周期内涨跌的幅度为0,那么价格的变动速率就是0。变动速率为1,对应的最快速的均线-2日的EMA;变动速率为0 ,则对应最慢速的均线-30日EMA。
|
||||
采用的方法是,在一定的周期内,计算每个周期价格的变动的累加,用整个周期的总体价格变动除以每个周期价格变动的累加,
|
||||
我们采用这个数字作为价格变化的速率。如果股票持续上涨或下跌,那么变动的速率就是1;
|
||||
如果股票在一定周期内涨跌的幅度为0,那么价格的变动速率就是0。变动速率为1,
|
||||
对应的最快速的均线-2日的EMA;变动速率为0 ,则对应最慢速的均线-30日EMA。
|
||||
以通达信软件的公式为例(其他软件也可以用):
|
||||
每个周期价格变动的累加:=sum(abs(close-ref(close,1)),n);
|
||||
整个周期价格的总体变动:=abs(close-ref(close,n));
|
||||
|
@ -6,15 +6,31 @@
|
||||
# 2. 下载tick时,5档行情都下载
|
||||
# 3. 五档行情变量调整适合vnpy的命名方式
|
||||
|
||||
import os
|
||||
import csv
|
||||
from datetime import date, datetime
|
||||
from typing import Union, List
|
||||
|
||||
import json
|
||||
from tqsdk.api import TqApi
|
||||
from tqsdk.datetime import _get_trading_day_start_time, _get_trading_day_end_time
|
||||
from tqsdk.diff import _get_obj
|
||||
from tqsdk.utils import _generate_uuid
|
||||
|
||||
def get_account_config():
|
||||
"""
|
||||
获取本地账号配置
|
||||
:return:
|
||||
"""
|
||||
config_file_name = os.path.abspath(os.path.join(os.path.dirname(__file__), 'auth_account.json'))
|
||||
if os.path.exists(config_file_name):
|
||||
try:
|
||||
with open(config_file_name, mode="r", encoding="UTF-8") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
except Exception as ex:
|
||||
pass
|
||||
|
||||
return {}
|
||||
|
||||
class DataDownloader:
|
||||
"""
|
||||
|
@ -792,8 +792,6 @@ class LocalOrderManager:
|
||||
Keep an order buf before pushing it to gateway.
|
||||
"""
|
||||
self.orders[order.orderid] = copy(order)
|
||||
|
||||
|
||||
self.gateway.on_order(order)
|
||||
|
||||
def cancel_order(self, req: CancelRequest) -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user