# 功夫回测功能概述
服务端功夫策略回测是面向SmartX服务端功夫量化策略的回测平台。用户可使用历史多周期行情数据进行回测,通过回测交易记录和绩效分析,判断策略有效性。
- 回测接口与实盘接口保持一致,开发完成的策略可以在回测模式、模拟盘、实盘之间方便切换,无需因为切换环境再修改策略代码。
- 回测引擎使用C++语言,采用云端模式进行回测,回测效率高,可支持股票、ETF全市场快照数据、分钟Bar、日线Bar回测。
- 回测平台由中泰证券自主研发,您的需求可直达开发团队,我们将提供快速响应、全面的支持服务。
# 快速上手
# 编写策略
用户可参照快速上手编写、调试策略程序,正常运行后即可进行回测。
备注:服务端功夫回测功能部署在模拟环境中,用户需联系客户经理开通模拟环境账号。
# 启动回测
点击启动回测按钮进入参数设置页,设置回测的起止时间、初始资金、交易费用、无风险利率、滑点、使用的回测数据周期。页面也会记录最近一次回测的参数设置,便于用户微调回测。
# 查看回测结果
回测运行完成后客户端会展示回测的绩效概览和每日详细数据,方便用户评价量化策略有效性,优化提升量化策略盈利能力、抗风险能力。
绩效概览界面对回测结果进行了统计计算,使用了最大回撤、最大盈利、夏普比率、索提诺比率、卡尔玛比率等常用指标度量量化策略的收益和风险。
详细数据页面展示了每一个回测交易日量化策略的当日收益、持仓、买卖回合、成交等信息。
# 处理异常
当回测过程异常结束时,可以点击启动按钮旁的日志图标
打开策略日志,查看异常日志,排查原因。
# 回测交易规则
# 支持品种
品种 | 回测数据周期 | 特性说明 | 数据更新时间 |
---|---|---|---|
股票、ETF | 日线 | 使用level1 日线数据回测用户策略程序,支持买卖交易,跨日订单不撤销,不支持混合快照数据回测 | 每日盘前更新前前日数据 |
股票、ETF | 1分钟线 | 使用level1 1分钟线数据回测用户策略程序,支持买卖交易,跨日订单撤销,支持混合快照数据回测 | 每日盘前更新前前日数据 |
股票、ETF | 5分钟线 | 使用level1 5分钟线数据回测用户策略程序,支持买卖交易,跨日订单撤销,支持混合快照数据回测 | 每日盘前更新前前日数据 |
股票、ETF | 快照 | 使用level1 快照数据回测用户策略程序,支持买卖交易,跨日订单撤销,支持对手盘、现价撮合模式 | 每日盘前更新前前日数据 |
# 撮合逻辑
日线撮合: 若存在订单,则按照高开低收四个价格分四次撮合订单,当日结束后未成交订单保留 分钟线撮合: 若存在订单,则按照高开低收四个价格分四次撮合订单,当日结束后未成交订单取消 纯tick撮合(对手盘撮合模式): 若存在订单,则按照对手盘价格撮合,例如卖单按照买盘价格撮合,当日结束后未成交订单取消。对手盘撮合存在一个时间差,例如回放10:30:03秒的行情时,对订单应使用10:30:00秒行情的对手盘数据进行撮合。 纯tick撮合(现价撮合模式): 若存在订单,则按照最新价撮合,当日结束后未成交订单取消 分钟线+tick撮合 若存在订单,则按照对手盘价格撮合,当日结束后未成交订单取消
# 滑点处理逻辑
若配置滑点,则模拟撮合时买单成交价格上涨滑点比例,卖单成交价格下跌滑点比例。
# 交易费用
回测交易费用主要包括手续费、最低手续费、印花税、过户费,费率默认使用客户在SmartX配置的费率。
买入订单费用计算 每笔成交手续费 = (手续费 + 过户费) * 成交额 + 印花税 * 成交额 默认买入订单印花税为0。
卖出订单费用计算 每笔成交手续费 = (手续费 + 过户费) * 成交额 + 印花税 * 成交额
最低手续费计算 订单在交易时段全部交易成功则检查该单关联的所有成交手续费之和, 如果该值小于最低手续费 , 则通过调整最后一笔成交成交费用以实施“最小手续费”规则
# 绩效回测指标解析
# 绩效概览页面指标
- 总收益率 = 期末动态权益/初始资金 - 1
- 年化收益率=(期末动态权益/初始资金)^(240/回测天数)-1,其中240是一年的交易日数
- 胜率 = 盈利回合次数 / 总回合次数
- 年化标准差=每日收益率标准差*√240
- 下行标准差=每日亏损收益率标准差*根号下(240)
- 最大回撤=净值曲线的任一高点到其后续最低点的下跌幅度的最大值
- 最大盈利=净值曲线的任一低点到其后续最高点的上涨幅度的最大值
- Sharpe比率 = (年化收益率 - 无风险利率)/ 年化标准差
- Sortino比率 = (年化收益率 - 无风险利率)/ 年化下行标准差
- Calmar比率 = 年化收益率 / 最大回撤
# 数据详情-每日绩效页指标
- 日期为回测的历史交易日
- 平仓盈亏=当日卖出持仓收益 - 对应持仓成本
- 浮动盈亏=当日总持仓市值 - 总持仓成本。持仓成本不包含手续费
- 累计手续费 = 累计至当日的手续费总和
- 动态权益 = 当日持仓市值+ 剩余现金
# 数据详情-持仓明细页指标
- 日期为回测的历史交易日
- 品种字段由
市场.类型.代码
组成,其中深市代码为SZE
,沪市代码为SSE
,类型包括ETF
和STK(股票)
- 持仓数量为当日持仓股数
- 平仓盈亏 = 当日某支票的平仓收益- 对应持仓成本
- 浮动盈亏 = 当日某支票的市值 - 对应持仓成本
# 数据详情-回合明细页指标
- 品种字段由
市场.类型.代码
组成,其中深市代码为SZE
,沪市代码为SSE
,类型包括ETF
和STK(股票)
- 开仓时间为买单成交时间
- 开仓价格为成交价格
- 开仓订单号为买单订单号
- 平仓时间为卖单成交时间
- 平仓价格为卖单成交价格,平仓订单为卖单订单号,开仓订单号与平仓订单号为多对多关系
- 数量为平仓的股数
- 潜在盈利 = (开仓时间至平仓时间内最高价- 开仓价格 )* 数量
- 潜在亏损= (开仓时间至平仓时间内最低价- 开仓价格 )* 数量
# 数据详情-成交页指标
- 品种字段由
市场.类型.代码
组成,其中深市代码为SZE
,沪市代码为SSE
,类型包括ETF
和STK(股票)
- 订单号为回测引擎模拟生成的柜台单号,不是功夫生成的下单编号
- 时间为订单每个成交的时间
- 动作表示买入还是卖出成交
- 成交价格为当次模拟撮合的成交价格
- 成交数量为当次模拟撮合的成交股数
# 实盘兼容接口
接口 | Python接口 | C++接口 |
---|---|---|
添加一次性定时任务 | context.add_timer() | context::add_timer() |
添加周期性定时任务 | context.add_time_interval() | context::add_time_interval() |
订阅快照行情 | context.subscribe_market_data() | context::subscribe_market_data() |
退订快照行情 | context.unsubscribe_market_data() | context::unsubscribe_market_data() |
订阅快照、Bar行情 | context.subscribe() | context::subscribe() |
退订快照、Bar行情 | context.unsubscribe() | context::unsubscribe() |
订阅指定交易所所有证券的某类行情 | context.subscribe_all_market() | context::subscribe_all_market() |
退订指定交易所所有证券的某类行情 | context.unsubscribe_all_market() | context::unsubscribe_all_market() |
添加交易柜台及资金账户 | context.add_account() | context::add_account() |
获取账户投资组合 | context.get_account_book() | |
查询全市场的静态行情 | context.getAllStaticQuote() | context::getAllStaticQuote() |
下单函数 | context.insert_order() | context::insert_order() |
撤单函数 | context.cancel_order() | context::cancel_order() |
快照行情回调 | on_quote() | on_quote() |
Bar行情回调 | on_bar() | on_bar() |
订单信息更新回调 | on_order() | on_order() |
订单成交回报回调 | on_trade() | on_trade() |
撤单失败回调 | on_order_action_error() | on_order_action_error() |
# 复权数据接口
# Python版接口
# 获取历史Bar行情
context.history_bars(symbol, exchange, bar_count) 执行该函数将获取离当前时间最近的一组历史Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 获取历史前复权Bar行情
context.pre_history_bars(symbol, exchange, bar_count) 执行该函数将获取离当前时间最近的一组历史前复权Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 获取历史后复权Bar行情
context.post_history_bars(symbol, exchange, bar_count) 执行该函数将获取离当前时间最近的一组历史后复权Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 前复权行情回调函数
on_prebar(context, bar ) 收到Bar行情时触发该回调。
context
(入参) 策略全局对象,通过点标记(”.”)来调用该对象中函数与变量。bar
(入参)PreBar(类型) 回测起止时间内前复权行情。空
(返回值)
# 后复权行情回调函数
on_postbar(context, bar ) 收到Bar行情时触发该回调。
context
(入参) 策略全局对象,通过点标记(”.”)来调用该对象中函数与变量。bar
(入参)PostBar(类型) 回测起止时间内后复权行情。空
(返回值)
# 全类型行情回调函数
on_barex(context, bar ) 收到Bar行情时触发该回调。
context
(入参) 策略全局对象,通过点标记(”.”)来调用该对象中函数与变量。bar
(入参)BarEx(类型) 回测起止时间内实际行情、前复权行情和后复权行情。空
(返回值)
# C++版接口
# 获取历史Bar行情
// 函数原型
std::vector<msg::data::Bar> history_bars(const std::string &symbol, const std::string &exchange, int64_t bar_count = 0)
2
context::history_bars 执行该函数将获取离当前时间最近的一组历史Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 获取历史前复权Bar行情
// 函数原型
std::vector<msg::data::Bar> pre_history_bars(const std::string &symbol, const std::string &exchange, int64_t bar_count = 0)
2
context::pre_history_bars 执行该函数将获取离当前时间最近的一组历史前复权Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 获取历史后复权Bar行情
// 函数原型
std::vector<msg::data::Bar> post_history_bars(const std::string &symbol, const std::string &exchange, int64_t bar_count = 0)
2
context::post_history_bars 执行该函数将获取离当前时间最近的一组历史后复权Bar行情。
symbol
(入参) 字符串 (类型) 产品代码。exchange
(入参) 字符串 (类型) 市场代码,沪市SSE
,深市SZE
。bar_count
(入参) Int数字类型 需要的Bar行情数量。bar
(返回值) Bar类型数组
# 前复权行情回调函数
virtual void on_prebar(Context_ptr context, const msg::data::PreBar &bar) 收到Bar行情时触发该回调。
context
(入参) 策略全局对象,通过点标记(”.”)来调用该对象中函数与变量。bar
(入参)PreBar(类型) 回测起止时间内前复权行情。空
(返回值)
# 后复权行情回调函数
virtual void on_postbar(Context_ptr context, const msg::data::PostBar &bar) 收到Bar行情时触发该回调。
Context_ptr
(入参) kungfu::wingchun::strategy::Context 类一个实例的智能指针, 在策略进程中这个类只有一个实例。bar
(入参)PostBar(类型) 回测起止时间内后复权行情。空
(返回值)
# 全类型行情回调函数
virtual void on_barex(Context_ptr context, const msg::data::BarEx &bar) 收到Bar行情时触发该回调。
context
(入参) 策略全局对象,通过点标记(”.”)来调用该对象中函数与变量。bar
(入参)BarEx(类型) 回测起止时间内实际行情、前复权行情和后复权行情。空
(返回值)
# 数据结构
# 行情数据结构
Bar 结构体
/**
char trading_day[DATE_LEN]; //交易日
char source_id[SOURCE_ID_LEN]; //柜台ID
char instrument_id[INSTRUMENT_ID_LEN]; //合约代码
char exchange_id[EXCHANGE_ID_LEN]; //交易所代码
char code[CODE_LEN]; // 证券代码 例如: 600000.SH
char type[BAR_TYPE_LEN]; // 类型, 例如:bar_1min
// start_time 和 end_time 格式示例:2023-10-07 09:37:00
char start_time[START_TIME_LEN]; // 开始时间
char end_time[END_TIME_LEN]; // 结束时间
int64_t time_interval; //单位定义为分钟
double open; //开
double close; //收
double low; //低
double high; //高
int64_t volume; //区间交易量
int64_t start_volume; //初始总交易量
double turnover; //区间成交金额
double start_turnover; //初始总成交金额
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 前复权行情数据结构
PreBar 数据结构
/**
char trading_day[DATE_LEN]; //交易日
char source_id[SOURCE_ID_LEN]; //柜台ID
char instrument_id[INSTRUMENT_ID_LEN]; //合约代码
char exchange_id[EXCHANGE_ID_LEN]; //交易所代码
char code[CODE_LEN]; // 证券代码 例如: 600000.SH
char type[BAR_TYPE_LEN]; // 类型, 例如:bar_1min
// int64_t start_time; //开始时间
// int64_t end_time; //结束时间
// start_time 和 end_time 格式示例:2023-10-07 09:37:00
char start_time[START_TIME_LEN]; // 开始时间
char end_time[END_TIME_LEN]; // 结束时间
int64_t time_interval; //单位定义为分钟
double open; //开
double close; //收
double low; //低
double high; //高
int64_t volume; //区间交易量
int64_t start_volume; //初始总交易量
double turnover; //区间成交金额
double start_turnover; //初始总成交金额
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 后复权行情数据结构
PostBar 结构体
/**
char trading_day[DATE_LEN]; //交易日
char source_id[SOURCE_ID_LEN]; //柜台ID
char instrument_id[INSTRUMENT_ID_LEN]; //合约代码
char exchange_id[EXCHANGE_ID_LEN]; //交易所代码
char code[CODE_LEN]; // 证券代码 例如: 600000.SH
char type[BAR_TYPE_LEN]; // 类型, 例如:bar_1min
// int64_t start_time; //开始时间
// int64_t end_time; //结束时间
// start_time 和 end_time 格式示例:2023-10-07 09:37:00
char start_time[START_TIME_LEN]; // 开始时间
char end_time[END_TIME_LEN]; // 结束时间
int64_t time_interval; //单位定义为分钟
double open; //开
double close; //收
double low; //低
double high; //高
int64_t volume; //区间交易量
int64_t start_volume; //初始总交易量
double turnover; //区间成交金额
double start_turnover; //初始总成交金额
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 全类型行情数据结构
BarEx 结构体
/**
char trading_day[DATE_LEN]; //交易日
char source_id[SOURCE_ID_LEN]; //柜台ID
char instrument_id[INSTRUMENT_ID_LEN]; //合约代码
char exchange_id[EXCHANGE_ID_LEN]; //交易所代码
char code[CODE_LEN]; // 证券代码 例如: 600000.SH
char type[BAR_TYPE_LEN]; // 类型, 例如:bar_1min
// int64_t start_time; //开始时间
// int64_t end_time; //结束时间
// start_time 和 end_time 格式示例:2023-10-07 09:37:00
char start_time[START_TIME_LEN]; // 开始时间
char end_time[END_TIME_LEN]; // 结束时间
int64_t time_interval; //单位定义为分钟
double open; //开
double open_pre; //开(前复权)
double open_post; //开(后复权)
double close; //收
double close_pre; //收(前复权)
double close_post; //收(后复权)
double low; //低
double low_pre; //低(前复权)
double low_post; //低(后复权)
double high; //高
double high_pre; //高(前复权)
double high_post; //高(后复权)
int64_t volume; //区间交易量
int64_t start_volume; //初始总交易量
double turnover; //区间成交金额
double start_turnover; //初始总成交金额
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 策略Demo
# Python策略Demo
import sys
import json
from functools import partial
import kungfu.yijinjing.time as kft
import kungfu.wingchun.utils as wc_utils
from kungfu.wingchun.constants import *
import pyyjj
source = Source.XTP
exchange_sse = Exchange.SSE
exchange_sze = Exchange.SZE
account_str = "15003934"
def book_is_ready(context):
if not context.book.ready:
return False
for book in context.books.values():
if not book.ready:
return False
context.book_ready_flag = True
return True
def deal_with_book(context, event):
if book_is_ready(context):
strategy_book = context.book #策略的资金与持仓
context.log.info("strategy available cash:{}".format(strategy_book.avail))
context.log.info("strategy position as follow :")
for position in strategy_book.positions:
context.log.info("account instrument_id:{}, volume:{},account sellable volume:{},account avg_open_price :{}".format(position.instrument_id , position.volume - position.frozen_total , position.yesterday_volume - position.frozen_yesterday ,position.avg_open_price ))
account_book = context.get_account_book(source, account_str ) #某资金账户的资金与持仓
context.log.warning("[account available cash]:{}".format( account_book.avail))
pos_es = account_book.positions
for pos in pos_es:
context.log.info("account instrument_id:{}, volume:{},account sellable volume:{},account avg_open_price :{}".format(pos.instrument_id ,pos.volume - pos.frozen_total , pos.yesterday_volume - pos.frozen_yesterday ,pos.avg_open_price ))
else:
context.add_timer(pyyjj.now_in_nano()+kft.NANO_PER_SECOND, partial(deal_with_book))
context.log.warning("book is not ready,please wait one more second!")
#注意:book和position数据,是异步更新,而非实时更新
#启动前
def pre_start(context):
context.log.info("pre run strategy")
context.book_ready_flag = False #该变量为自定义的全局变量,用于标记策略是否已经拿到策略自己以及所使用账号的持仓;策略在使用资金账户之前要先通过context.add_account添加资金账户。
context.add_account(source, account_str , 100000000.0)
context.subscribe(source, ["600000", "601988"], exchange_sse) #订阅沪市股票快照行情
context.subscribe(source, ["000001", "000002"], exchange_sze) #订阅深市股票快照行情
context.subscribe("bar", ["000001", "000002"], exchange_sze) #订阅深市股票Bar行情
#启动后
def post_start(context):
deal_with_book(context, None)
pass
#策略退出前,可在此做一些回收工作
def pre_stop(context):
context.log.info("strategy pre_stop.")
#退出后
def post_stop(context):
pass
# Bar行情数据更新回调
def on_bar(context, bar):
context.log.info("recv bar({}) succeed,close price is ({})".format(
bar.instrument_id, bar.close))
order_id = context.insert_order(bar.instrument_id, bar.exchange_id,
account_str, bar.high*2, 200, PriceType.Limit, Side.Buy, Offset.Open)
if 0 != order_id:
context.log.info("insert_order({}) succeed,order_id is {}".format(
bar.instrument_id, order_id))
context.count += 1
else:
context.log.error("insert_order({}) failed,order_id is {}".format(
bar.instrument_id, order_id))
#快照行情数据更新回调
def on_quote(context, quote):
if not context.book_ready_flag: #此判断逻辑非必须,如果策略不关心策略或者账户资金与持仓,直接注掉该if语句块即可。
context.logger.warning("book has not been ready")
return
context.log.info("quote received: [time]{} [instrument_id]{} [last_price]{}".format(quote.data_time, quote.instrument_id, quote.last_price))
#一般来说可以在此处进行逻辑判断,符合下单条件下单即可。
order_id = context.insert_order(quote.instrument_id, quote.exchange_id, account_str , quote.ask_price[0], 200, PriceType.Limit, Side.Buy, Offset.Open, HedgeFlag.Speculation)
#订单信息更新回调
def on_order(context, order):
context.log.info('order received: [instrument_id]{} [volume]{} [price]{}'.format(order.instrument_id, order.volume, order.limit_price))
#订单成交回报回调
def on_trade(context, trade):
context.log.info('trade received: {} [trade_id]{} [volume]{} [price]{}'.format(kft.strftime(trade.trade_time), trade.order_id, trade.volume, trade.price))
#订单操作错误回调
def on_order_action_error(context, error):
context.log.info('order_action_error received: [order_id]{} [error_id]{}'.format(error.order_id, error.error_id))
pass
#撤单反馈信息回调
def on_cancelorder_feedback(context, cofeedback):
context.log.info('cancelorder_feedback received: [feedback_type]{} [order_id]{}'.format(cofeedback.feedback_type, cofeedback.order_id))
pass
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# C++ 策略Demo:
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <kungfu/yijinjing/common.h>
#include <kungfu/yijinjing/log/setup.h>
#include <kungfu/wingchun/strategy/context.h>
#include <kungfu/wingchun/strategy/strategy.h>
namespace py = pybind11;
using namespace kungfu;
using namespace kungfu::wingchun;
using namespace kungfu::wingchun::strategy;
#define ACCOUNT_ID "53191001503"
class DemoStrategy : public Strategy
{
public:
DemoStrategy(yijinjing::data::location_ptr home)
{
yijinjing::log::copy_log_settings(home, home->name);
};
void test_timer(yijinjing::event_ptr event, Context_ptr context)
{
return;
}
void test_time_interval(Context_ptr context, yijinjing::event_ptr event)
{
return;
}
void cancel_order(yijinjing::event_ptr event, Context_ptr context, uint64_t order_id)
{
context->cancel_order(order_id);
}
// 启动前
void pre_start(Context_ptr context) override
{
SPDLOG_INFO("cpp demo pre start");
bool load_ret = context->load_etf_info();
const std::string empty_str("");
std::vector<std::string> tickers;
tickers.push_back("600000");
tickers.push_back("601988");
context->add_account(SOURCE_XTP, ACCOUNT_ID, 100000000.0);
context->subscribe(SOURCE_XTP, tickers, EXCHANGE_SSE);
context->subscribe_tick_by_tick(SOURCE_XTP, tickers, EXCHANGE_SSE);
context->subscribe_order_book(SOURCE_XTP, tickers, EXCHANGE_SSE);
auto f1 = std::bind(&DemoStrategy::test_timer, this, std::placeholders::_1, context);
context->add_timer(yijinjing::time::now_in_nano() + yijinjing::time_unit::NANOSECONDS_PER_SECOND * 5, f1);
// 静态行情数量大于零,说明获取成功
quotes = context->getAllStaticQuote();
SPDLOG_INFO("load status quote num: {}.", quotes.size());
auto f4= std::bind(&DemoStrategy::test_time_interval, this, context, std::placeholders::_1);
context->add_time_interval(yijinjing::time_unit::NANOSECONDS_PER_SECOND, f4);//NANOSECONDS_PER_SECOND是一个系统定义的数值常量,为1000000000,单位是纳秒,也即1秒钟;
//客户可根据实际需要,自行对该值进行替换
}
// 启动后
void post_start(Context_ptr context) override
{
SPDLOG_INFO("cpp demo post start");
}
// 策略退出前,可在此做一些回收工作
void pre_stop(Context_ptr context) override
{
SPDLOG_INFO("cpp demo pre stop");
}
// 退出后
void post_stop(Context_ptr context) override
{
SPDLOG_INFO("cpp demo post stop");
}
void on_trading_day(Context_ptr context, int64_t daytime) override
{
SPDLOG_INFO("cpp demo on trading day");
}
// bar 分钟级别行情回调
void on_bar(Context_ptr context, const wingchun::msg::data::Bar &bar) override
{
SPDLOG_INFO("cpp demo on bar");
}
// 行情数据更新回调
void on_quote(Context_ptr context, const wingchun::msg::data::Quote "e) override
{
SPDLOG_INFO("cpp demo on quote");
uint64_t order_id = context->insert_order(quote.instrument_id, quote.exchange_id, ACCOUNT_ID, quote.ask_price[0],
200, PriceType::Limit, Side::Buy, Offset::Open, HedgeFlag::Speculation);
char msg[1024];
sprintf(msg, "insert_order(%s,%s)", quote.instrument_id, quote.exchange_id);
context->send_msg(msg);
if (order_id > 0)
{
auto f = std::bind(&DemoStrategy::cancel_order, this, std::placeholders::_1, context, order_id);
context->add_timer(yijinjing::time::now_in_nano() + yijinjing::time_unit::NANOSECONDS_PER_SECOND, f);
}
SPDLOG_INFO("quote received: [time]{} [instrument_id]{} [last_price]{}, msg: {}.",
yijinjing::time::strftime(quote.data_time), quote.instrument_id, quote.last_price,msg);
}
// 订单信息更新回调
void on_order(Context_ptr context, const wingchun::msg::data::Order &order) override
{
SPDLOG_INFO("cpp demo on order");
}
// 订单成交回报回调
void on_trade(Context_ptr context, const wingchun::msg::data::Trade &trade) override
{
SPDLOG_INFO("cpp demo on trade");
}
// 订单操作错误回调 撤单反馈信息回调
void on_order_action_error(Context_ptr context, const wingchun::msg::data::OrderActionError &error) override
{
SPDLOG_INFO("cpp demo on order action error");
}
private:
// 静态行情整个交易日都不会变,启动策略时取到所有证券的静态数据后,需要时直接使用
// 而不需要用一次查一次,性能很差(因为需要读写文件)
// key的格式是:证券代码.证券市场;比如:600000.SSE和000001.SZE
std::map<std::string, QuoteStaticFullInfo> quotes;
};
PYBIND11_MODULE(PY_MODULE, m)
{
py::class_<DemoStrategy, Strategy, std::shared_ptr<DemoStrategy>>(m, "Strategy")
.def(py::init<yijinjing::data::location_ptr>())
.def("pre_start", &DemoStrategy::pre_start)
.def("post_start", &DemoStrategy::post_start)
.def("pre_stop", &DemoStrategy::pre_stop)
.def("post_stop", &DemoStrategy::post_stop)
.def("on_trading_day", &DemoStrategy::on_trading_day)
.def("on_quote", &DemoStrategy::on_quote)
.def("on_bar", &DemoStrategy::on_bar)
.def("on_order", &DemoStrategy::on_order)
.def("on_order_action_error", &DemoStrategy::on_order_action_error)
.def("on_trade", &DemoStrategy::on_trade)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163