# Kungfu 基础 API 示例
# 普通股票交易策略
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 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)
#启动后
def post_start(context):
pass
#策略退出前,可在此做一些回收工作
def pre_stop(context):
context.log.info("strategy pre_stop.")
#退出后
def post_stop(context):
pass
#行情数据更新回调
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
1
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
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
# 算法交易策略
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 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)
#登录xtp算法交易服务器
#备注:1.context.login_algo_server()函数为算法总线登录函数
# 2.示例中的参数参数"algo_user",代表用户名;参数"algo_password",代表密码;参数具体取值,需要根据实际情况填写
context.login_algo_server(source, account_str, "algo_user", "algo_password")
context.subscribe(source, ["600000", "601988"], exchange_sse)
context.subscribe(source, ["000001", "000002"], exchange_sze)
#启动后
def post_start(context):
pass
#策略退出前,可在此做一些回收工作
def pre_stop(context):
context.log.info("strategy pre_stop.")
#退出后
def post_stop(context):
pass
#行情数据更新回调
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))
#逐笔成交更新回调
def on_transaction(context, transaction):
context.log.info("{} {}".format(transaction.instrument_id, transaction.exchange_id))
pass
#逐笔委托更新回调
def on_entrust(context, entrust):
context.log.info("{} {}".format(entrust.instrument_id, entrust.exchange_id))
pass
#订单信息更新回调
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))
#xtp算法交易服务器登录反馈信息回调
#备注:1.on_login_algo_server(context, success)函数为算法总线登录函数
# 2.参数需要根据实际情况填写
def on_login_algo_server(context, success):
context.log.info('login_algo_server result is: {} '.format(success))
if success:
strategy_param = {}
strategy_param["buyDateTime"] = ["09:30", "09:40"]
strategy_param["stop_selling_while_buy_overflow"] = False
strategy_param["cancel_overflow_order_after_clear_time"] = True
strategy_param["start_time"] = "09:30:00"
strategy_param["end_time"] = "09:40:00"
strategy_param["trade_list"] = []
trade0 = {}
trade0["market"] = "SZ" #深交所
trade0["clientStrategyId"] = "202010200902"
trade0["quantity"] = 20000
trade0["side"] = "BUY"
trade0["ticker"] = "002513"
trade1 = {}
trade1["market"] = "SZ" #深交所
trade1["clientStrategyId"] = "202010200902"
trade1["quantity"] = 20000
trade1["side"] = "BUY"
trade1["ticker"] = "001914"
strategy_param["trade_list"].append(trade0)
strategy_param["trade_list"].append(trade1)
strategy_param["business_type"] = "CASH"
context.create_algo_strategy(account_str, 2001, 202010200902, json.dumps(strategy_param))
#备注:变量strategy_param的数据格式,由context.create_algo_strategy()函数的第二个参数来决定,具体细节需咨询算法交易相关人员
pass
#xtp算法交易创建策略反馈信息回调
def on_create_algo_strategy(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
context.start_algo_strategy(account_xtp, algo_strategy_info.xtp_strategy_id)
pass
#xtp算法交易查询策略反馈信息回调
def on_query_algo_strategy(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
pass
#xtp算法交易开始运行策略反馈信息回调
def on_start_algo_strategy(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
pass
#xtp算法交易停止运行策略反馈信息回调
def on_stop_algo_strategy(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
pass
#xtp算法交易销毁策略反馈信息回调
def on_destroy_algo_strategy(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
pass
#xtp算法交易策略信息更新回调
def on_algo_strategy_info(context, algo_strategy_info):
context.log.info('algo_strategy_info received: {} '.format(algo_strategy_info))
pass
#xtp算法交易运行时策略状态更新回调
def on_algo_strategy_state_report(context, algo_strategy_state_report):
context.log.info('algo_strategy_state_report received: {} '.format(algo_strategy_state_report))
pass
1
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
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
# ETF 相关数据读取
# (一)Python 语言策略 Demo
#启动前
def pre_start(context):
context.log.info("pre run strategy")
# 加载ETF数据,尽量在TD启动三分钟之后再启动策略,防止TD还没有收集齐ETF数据
load_ret = context.load_etf_info()
context.log.info("load etf: {}.".format(load_ret))
if load_ret:
# 只有加载成功后,才能进行以下操作
# 1、获取全市场的ETF清单,返回的map是以“ETF市场_ETF代码”组合成的字符串作为key,比如:SZE_159646
bases = context.get_all_etfs()
basket_prt = False
for etf_code, base in bases.items():
if not basket_prt:
basket_prt = True
# 2、获取指定ETF的所有成分股
baskets = context.get_etf_all_baskets(etf_code)
for etf, basket in baskets.items():
context.log.info("etf code: {} basket code: {}.".format(basket.get_class_key, basket.get_major_key))
context.log.info("etf code: {}, num: {}.".format(etf_code, len(context.get_etf_all_baskets(etf_code))))
# 3、获取单市场的所有ETF清单,市场不能为空,EXCHANGE_SSE:沪市;EXCHANGE_SZE:深市
sh_bases = context.get_etf_base(Exchange.SSE, "")
for sh, sh_base in sh_bases.items():
context.log.info("all shanghai etf code: {}.".format(sh))
context.log.info("all shanghai etf total num: {}.".format(len(sh_bases)))
# 4、获取指定某支ETF清单,市场和ETF代码都传即可;若两个参数都正确传值,返回的map大小为1;否则为空
sh_bases = context.get_etf_base(Exchange.SSE, "588060")
for sh, sh_base in sh_bases.items():
# 5、获取指定ETF的某个市场的所有成分股,市场不能为空,返回的map是以“成分股市场_成分股代码”组合成的字符串作为key,比如:SSE_688772
baskets = context.get_etf_baskets(sh, Exchange.SSE, "")
context.log.info("shanghai etf code: {}, info: {} basket num: {}.".format(sh, sh_base, len(baskets)))
for etf, basket in baskets.items():
context.log.info("etf code: {} shanghai basket code: {}.".format(basket.get_class_key, basket.get_major_key))
# 6、获取指定ETF的某个市场的某支成分股;若三个参数都正确传值,返回的map大小为1;否则为空
baskets = context.get_etf_baskets(sh, Exchange.SSE, "688772")
for etf, basket in baskets.items():
context.log.info("etf code: {} 688772 basket code: {}, info: {}.".format(basket.get_class_key, basket.get_major_key, basket))
1
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
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
# (二)C++ 语言策略 Demo
// 启动前
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("");
// 加载ETF数据,尽量在TD启动三分钟之后再启动策略,防止TD还没有收集齐ETF数据
SPDLOG_INFO("load etf: {}.", load_ret);
if(load_ret)
{
// 只有加载成功后,才能进行以下操作
// 1、获取全市场的ETF清单,返回的map是以“ETF市场_ETF代码”组合成的字符串作为key,比如:SZE_159646
std::unordered_map<std::string, wingchun::msg::data::ETFBase> bases = context->get_all_etfs();
bool basket_prt = false;
std::string etf_code("");
for(const auto& it : bases)
{
etf_code = it.first;
if(!basket_prt)
{
basket_prt = true;
// 2、获取指定ETF的所有成分股
std::unordered_map<std::string, wingchun::msg::data::ETFBasket> baskets = context->get_etf_all_baskets(etf_code);
for(const auto& basket : baskets)
{
SPDLOG_INFO("etf code: {} basket code: {}.", basket.second.get_class_key(), basket.second.get_major_key());
}
}
SPDLOG_INFO("etf code: {}, num: {}.", etf_code, context->get_etf_all_baskets(etf_code).size());
}
// 3、获取单市场的所有ETF清单,市场不能为空,EXCHANGE_SSE:沪市;EXCHANGE_SZE:深市
std::unordered_map<std::string, wingchun::msg::data::ETFBase> sh_bases = context->get_etf_base(EXCHANGE_SSE, "");
for(const auto& it : sh_bases)
{
etf_code = it.first;
SPDLOG_INFO("all shanghai etf code: {}.", etf_code);
}
SPDLOG_INFO("all shanghai etf total num: {}.", sh_bases.size());
// 4、获取指定某支ETF清单,市场和ETF代码都传即可;若两个参数都正确传值,返回的map大小为1;否则为空
sh_bases = context->get_etf_base(EXCHANGE_SSE, "588060");
for(const auto& it : sh_bases)
{
etf_code = it.first;
// 5、获取指定ETF的某个市场的所有成分股,市场不能为空, 返回的map是以“成分股市场_成分股代码”组合成的字符串作为key,比如:SSE_688772
std::unordered_map<std::string, wingchun::msg::data::ETFBasket> baskets = context->get_etf_baskets(etf_code, EXCHANGE_SSE, "");
SPDLOG_INFO("shanghai etf code: {}, basket num: {}.", etf_code, baskets.size());
for(const auto& basket : baskets)
{
SPDLOG_INFO("etf code: {} shanghai basket code: {}.", basket.second.get_class_key(), basket.second.get_major_key());
}
// 6、获取指定ETF的某个市场的某支成分股;若三个参数都正确传值,返回的map大小为1;否则为空
baskets = context->get_etf_baskets(etf_code, EXCHANGE_SSE, "688772");
for(const auto& basket : baskets)
{
SPDLOG_INFO("etf code: {} 688772 basket code: {}.", basket.second.get_class_key(), basket.second.get_major_key());
}
}
}
}
1
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
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
# 静态行情 相关数据读取
# (一)Python 语言策略 Demo
#启动前
def pre_start(context):
context.log.info("pre run strategy")
# 静态行情数量大于零,说明获取成功
global quotes
quotes=context.getAllStaticQuote()
context.log.info('static quotes num: {}.'.format(len(quotes)))
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# (二)C++ 语言策略 Demo
// 启动前
void pre_start(Context_ptr context) override
{
SPDLOG_INFO("cpp demo pre start");
// 静态行情数量大于零,说明获取成功
std::map<std::string, QuoteStaticFullInfo> quotes = context->getAllStaticQuote();
SPDLOG_INFO("load status quote num: {}.", quotes.size());
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 服务端网格交易策略
服务端网格交易git地址:https://github.com/ztsec/smartx_server_python_grid_trading?tab=readme-ov-file
网格交易策略有不同的实现,本例的功能是在excel文件中定义好要交易的股票及初始的一个基准价格,同时定义好买卖的价差,还有价格的上下限,单次最大的委托数量,买入最大数量,卖出最大数量等条件。实现逻辑是在每一次交易完成后,将成交价格作为新的基准价格,在新的基准价格的基础上,如果再浮动相应的价差,又会做相对应的买入或者卖出委托,买入卖出完成后,又会形成一个新的基准价格,又会在这个基准价格上进行买入卖出操作。往复来实现高抛低吸的网格交易,可以参考网格交易策略说明。
- 首先,用户要在excel配置文件中对各参数进行配置;
- 策略开始运行后,根据用户的配置,接收与标的股票对应的行情数据;
- 在收到行情数据的基础上,根据实时行情价格和基准价格,判断满足交易条件。如果当前价格能够触发策略交易,在满足“价格上限”、“价格下限”、“最大买入数量”、“最大卖出数量”、“最大轧差”等约束条件的前提下,进行相应买入或卖出交易;
- 策略会计算最新完成的委托单的成交均价来修改对应标的股票的基准价格,并同时把该价格存放到 excel 配置文件中对应标的股票的基准价格,便于下次启动策略继续使用该基准价格。
import os
import sys
import pyyjj
import kungfu.yijinjing.time as kft
from kungfu.wingchun.constants import *
import math
import kungfu.wingchun.utils as wc_utils
import openpyxl
from threading import Lock
source = Source.XTP
sz_exchange = Exchange.SZE
sh_exchange = Exchange.SSE
class Stock:
def __init__(self,strStockCode,strExchange,fInitBasisPrice,fSellPriceDelta,
fBuyPriceDelta,fPriceUpperBound,fPriceLowerBound,iAmountPerEntrust,
iMaxBuyAmount,iMaxSellAmount,iMaxNettingAmount,index = -1):
self.strStockCode = strStockCode # 股票代码 例 000666
self.strExchange = strExchange # 交易所 例 SSE
self.fInitBasisPrice = fInitBasisPrice # 初始基准价格
self.fSellPriceDelta = fSellPriceDelta # 卖出价差,为百分比,如,0.02代表价差百分比为2%
self.fBuyPriceDelta = fBuyPriceDelta # 买入价差,为百分比
self.fPriceUpperBound = fPriceUpperBound # 价格上限
self.fPriceLowerBound = fPriceLowerBound # 价格下限
self.iAmountPerEntrust = iAmountPerEntrust # 单次委托数量
self.iMaxBuyAmount = iMaxBuyAmount # 最大买入数量
self.iMaxSellAmount = iMaxSellAmount # 最大卖出数量
self.iMaxNettingAmount = iMaxNettingAmount # 最大轧差
self.iBuyAmount = 0 # 委托买入数量
self.iSellAmount = 0 # 委托卖出数量
self.fCurrBasisPrice = fInitBasisPrice # 储存上一次的买卖成交均价
self.isSell = True
self.isBuy = True
self.index = index #用于记录在excel中的索引序号,快速保存该股票的最新交易价格
def read_excel(context, sz , sh ):
wb = openpyxl.load_workbook(context.filename) # 读取xlsx文件
sheet1 = wb.active
context.account = str(sheet1.cell(1,2).value)
#context.log.info("the account is {}, its type is {}".format(context.account,type(context.account)))
nrows = sheet1.max_row + 1
ncols = sheet1.max_column + 1
for i in range(3,nrows): #modified by shizhao on 20191120
instrument_id = sheet1.cell(i,1).value
exchange_id = sheet1.cell(i,2).value
#context.log.info("type of instrument_id is {}; type of exchange_id is {};{}".format(instrument_id, exchange_id, isinstance(instrument_id,str)))
if isinstance(instrument_id,str) and exchange_id == sz_exchange:
sz.append(instrument_id)
elif isinstance(instrument_id,str) and exchange_id == sh_exchange:
sh.append(instrument_id)
else:
#added by shizhao on 20191120
context.log.info("warning: error instrument_id or exchange_id info in the {}th row".format(i))
pass
if isinstance(instrument_id,str) and (exchange_id == sz_exchange or exchange_id == sh_exchange): #modified by shizhao on 20191125
key = instrument_id + exchange_id
context.stock_dict[key] = Stock(sheet1.cell(i,1).value, sheet1.cell(i,2).value, float(sheet1.cell(i,3).value), float(sheet1.cell(i,4).value)/100.0, float(sheet1.cell(i,5).value)/100.0, float(sheet1.cell(i,6).value), float(sheet1.cell(i,7).value), sheet1.cell(i,8).value, sheet1.cell(i,9).value, sheet1.cell(i,10).value ,sheet1.cell(i,11).value ,i)
def convert_time_nano(context , time_str):
time_str = context.trading_day.strftime("%Y-%m-%d") + " " + time_str
return context.strptime(time_str,"%Y-%m-%d %H:%M:%S")
def pre_start(context):
context.log.info("pre run strategy")
context.stock_dict = {}
sz = []
sh = []
context.account = "" #added by shizhao on 20191125
context.customized_trading_time_begin = convert_time_nano(context,"09:30:00") #modified by shizhao on 20191202
context.customized_trading_time_end = convert_time_nano(context,"22:56:59") #modified by shizhao on 20191204
current_path = context.getParamFileDir()
context.filename = os.path.join(current_path, "grid_target.xlsx")
read_excel(context ,sz , sh )
context.log.info("sz:{}.".format(sz))
context.log.info("sh:{}.".format(sh))
context.log.info(context.stock_dict.keys())
context.log.info("customized beginning trading time is {}".format(kft.strftime(context.customized_trading_time_begin)))
context.log.info("customized ending trading time is {}".format(kft.strftime(context.customized_trading_time_end)))
context.add_account(source, context.account, 100000000.0)
context.subscribe_market_data(source, sz , sz_exchange) #modified by shizhao on 20191120
context.subscribe_market_data(source, sh , sh_exchange)
def pre_stop(context):
context.log.info("pre stop strategy")
def on_quote(context, quote):
#added by shizhao on 20191125
if pyyjj.now_in_nano() <= context.customized_trading_time_begin or pyyjj.now_in_nano() >= context.customized_trading_time_end:
context.log.info("warning:当前时间是{},不在自定义交易时间区间内!!!".format(kft.strftime(pyyjj.now_in_nano())))
return
key = quote.instrument_id + quote.exchange_id
#context.log.info("instrument_id:{} last_price:{}".format(quote.instrument_id, quote.last_price))
if key in context.stock_dict:
stock = context.stock_dict[key]
context.log.debug("instrument_id:{}, last_price:{}, bid_price[0]:{}, ask_price[0]:{}, fCurrBasisPrice:{}, upper_limit_price is {},lower_limit_price is {}".format(
quote.instrument_id, quote.last_price, quote.bid_price[0], quote.ask_price[0], stock.fCurrBasisPrice, quote.upper_limit_price, quote.lower_limit_price))
if quote.last_price > stock.fCurrBasisPrice:#卖的可能
# context.log.info("卖:instrument_id:{} last_price:{}".format(quote.instrument_id, quote.last_price ))
# context.log.info(quote.last_price - stock.fCurrBasisPrice)
# context.log.info(stock.fCurrBasisPrice + stock.fSellPriceDelta <= stock.fPriceUpperBound)
# context.log.info(stock.isSell)
# context.log.info(stock.iSellAmount + stock.iAmountPerEntrust <= stock.iMaxSellAmount)
# context.log.info(stock.iSellAmount+stock.iAmountPerEntrust-stock.iBuyAmount <= stock.iMaxNettingAmount)
#added by shizhao on 20191130
rate_of_price_increase = quote.last_price/stock.fCurrBasisPrice - 1.0
multiple = int(rate_of_price_increase/stock.fSellPriceDelta)
if 0 == multiple:
return
new_entrust_price = round(stock.fCurrBasisPrice*(1.0 + stock.fSellPriceDelta*multiple), 2)
if new_entrust_price <= stock.fPriceUpperBound and new_entrust_price <= quote.upper_limit_price and new_entrust_price >= quote.lower_limit_price and stock.isSell and (stock.iSellAmount + stock.iAmountPerEntrust <= stock.iMaxSellAmount) and stock.iSellAmount+stock.iAmountPerEntrust-stock.iBuyAmount <= stock.iMaxNettingAmount:
order_id = context.insert_order(quote.instrument_id, quote.exchange_id, context.account, new_entrust_price, int(stock.iAmountPerEntrust)*multiple , PriceType.Limit, Side.Sell, Offset.Open)
context.log.info("insert_order({},{},{},{},{},{},{},{}), order_id is {}".format(quote.instrument_id,
quote.exchange_id, context.account, new_entrust_price, int(stock.iAmountPerEntrust)*multiple ,
PriceType.Limit, Side.Sell, Offset.Open, order_id)) #modified by shizhao on 20191217
context.log.info("last_price is {}, fCurrBasisPrice is {}, new_entrust_price is {}, fPriceUpperBound is {}, upper_limit_price is {}, lower_limit_price is {}, iSellAmount is {}, iAmountPerEntrust is {}, iMaxSellAmount is {}, iBuyAmount is {}, iMaxNettingAmount is {}".format(
quote.last_price, stock.fCurrBasisPrice, new_entrust_price, stock.fPriceUpperBound, quote.upper_limit_price,
quote.lower_limit_price, stock.iSellAmount, stock.iAmountPerEntrust, stock.iMaxSellAmount,
stock.iBuyAmount, stock.iMaxNettingAmount)) #modified by shizhao on 20191217
if order_id > 0:###已经考虑到报单错误或者交易所拒单的情况
stock.iSellAmount = stock.iSellAmount + stock.iAmountPerEntrust*multiple
stock.isSell = False
elif quote.last_price < stock.fCurrBasisPrice:#买的可能
# context.log.info("买:instrument_id:{} last_price:{}".format(quote.instrument_id, quote.last_price ))
# context.log.info(stock.fCurrBasisPrice -quote.last_price >= stock.fBuyPriceDelta)
# context.log.info(stock.fCurrBasisPrice - stock.fBuyPriceDelta >= stock.fPriceLowerBound)
# context.log.info(stock.isBuy)
# context.log.info(stock.iBuyAmount + stock.iAmountPerEntrust <= stock.iMaxBuyAmount)
# context.log.info(stock.iBuyAmount+stock.iAmountPerEntrust-stock.iSellAmount <= stock.iMaxNettingAmount)
#added by shizhao on 20191130
rate_of_price_decrease = 1.0 - quote.last_price/stock.fCurrBasisPrice
multiple = int(rate_of_price_decrease/stock.fBuyPriceDelta)
if 0 == multiple:
return
new_entrust_price = round(stock.fCurrBasisPrice*(1.0 - stock.fBuyPriceDelta*multiple), 2)
if new_entrust_price >= stock.fPriceLowerBound and new_entrust_price >= quote.lower_limit_price and new_entrust_price <= quote.upper_limit_price and stock.isBuy and (stock.iBuyAmount + stock.iAmountPerEntrust <= stock.iMaxBuyAmount) and stock.iBuyAmount+stock.iAmountPerEntrust-stock.iSellAmount <= stock.iMaxNettingAmount:
order_id = context.insert_order(quote.instrument_id, quote.exchange_id, context.account, new_entrust_price, int(stock.iAmountPerEntrust)*multiple , PriceType.Limit, Side.Buy, Offset.Open)
context.log.info("insert_order({},{},{},{},{},{},{},{}), order_id is {}".format(quote.instrument_id,
quote.exchange_id, context.account, new_entrust_price, int(stock.iAmountPerEntrust)*multiple ,
PriceType.Limit, Side.Buy, Offset.Open, order_id)) #modified by shizhao on 20191217
context.log.info("last_price is {}, fCurrBasisPrice is {}, new_entrust_price is {}, fPriceLowerBound is {}, lower_limit_price is {}, upper_limit_price is {}, iBuyAmount is {}, iAmountPerEntrust is {}, iMaxBuyAmount, iSellAmount is {}, iMaxNettingAmount is {}".format(
quote.last_price, stock.fCurrBasisPrice, new_entrust_price, stock.fPriceLowerBound,
quote.lower_limit_price, quote.upper_limit_price, stock.iBuyAmount, stock.iAmountPerEntrust,
stock.iMaxBuyAmount, stock.iSellAmount, stock.iMaxNettingAmount)) #modified by shizhao on 20191217
if order_id > 0:
stock.iBuyAmount = stock.iBuyAmount + stock.iAmountPerEntrust*multiple
stock.isBuy = False
else:#no need any operation
pass
def on_transaction(context, transaction):
#context.log.info("{} {}".format(transaction.instrument_id, transaction.exchange_id))
pass
def on_entrust(context, entrust):
#context.log.info("{} {}".format(entrust.instrument_id, entrust.exchange_id))
pass
def on_order(context, order):
context.log.info('order received: [instrument_id]{} [volume]{} [price]{}'.format(order.instrument_id, order.volume, order.limit_price)) #added by shizhao on 20191217
key = order.instrument_id + order.exchange_id
if key in context.stock_dict:
stock = context.stock_dict[key]
if wc_utils.is_final_status(order.status):
if order.side == Side.Buy:
stock.iBuyAmount = stock.iBuyAmount - order.volume_left
elif order.side == Side.Sell:
stock.iSellAmount = stock.iSellAmount - order.volume_left
else:
pass
if order.volume_traded > 0:#有成交股数
wb = openpyxl.load_workbook(context.filename) # 读取xlsx文件
ws = wb.active
stock.fCurrBasisPrice = order.amount_traded/order.volume_traded #修改最新成交价
ws.cell(stock.index,3).value = stock.fCurrBasisPrice
wb.save(context.filename)
context.log.info("save new price because the final_status order,order_id is {}".format(order.order_id)) #modified by shizhao on 20191217
stock.isSell = True
stock.isBuy = True
else:#无成交股数
if order.side == Side.Buy:#需要修改下
stock.isBuy = True
else:#取消卖出
stock.isSell = True
1
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# 策略间通信
策略间通信指的是在不同的策略或策略组件之间传递信息和数据的过程,不同交易策略之间的数据传递和协同工作。一个策略可以通过轮询交易接口或监听交易平台的实时通知来传递信息,另一个策略在约定的时间内检查完成订单。
- 一、发送代码
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
#该函数拟调用context.send_msg_to_other_strategy()函数,向名字为“Strategy_B”的策略,发送一条消息
#备注:context.send_msg_to_other_strategy(strategy_id, msg)函数从2.1.2版本开始支持
def test_send_msg_to_other_strategy(context, event):
context.log.info('test send msg to Strategy_B')
test_msg_json = {}
test_msg_json["A"] = "A"
test_msg_json["B"] = "B"
test_msg_json["C"] = "C"
test_msg_str = json.dumps(test_msg_json)
context.send_msg_to_other_strategy("Strategy_B", test_msg_str)
context.log.info('the msg is:{}'.format(test_msg_str))
#启动前
def pre_start(context):
context.log.info("pre run strategy")
#为等待目标策略启动,定于1分钟后调用test_send_msg_to_other_strategy()函数
context.add_timer(pyyjj.now_in_nano()+kft.NANO_PER_MINUTE, partial(test_send_msg_to_other_strategy))
#启动后
def post_start(context):
pass
#策略退出前,可在此做一些回收工作
def pre_stop(context):
context.log.info("strategy pre_stop.")
#退出后
def post_stop(context):
pass
1
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
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
- 二、接收代码
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
#启动前
def pre_start(context):
context.log.info("pre run strategy")
#启动后
def post_start(context):
pass
#策略退出前,可在此做一些回收工作
def pre_stop(context):
context.log.info("strategy pre_stop.")
#退出后
def post_stop(context):
pass
#接收其他策略发送回来的客户自定义消息
#备注:为保证能顺利收到消息,接收策略的策略名必须与发送策略里context.send_msg_to_other_strategy()函数中使用的目标策略名保持一致
#备注:on_other_strategy_msg(context, source_strategy_id, msg)函数从2.1.2版本开始支持
def on_other_strategy_msg(context,source_strategy_id, msg):
context.log.info('received other strategy:{} msg:{}'.format(source_strategy_id , msg))
1
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
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
# 期货交易策略
import sys
import time
import copy
import json
import pyyjj
#import pywingchun
import time
import threading
from functools import partial
import kungfu.yijinjing.journal as kfj
from kungfu.msg.utils import object_as_dict
from functools import partial
import kungfu.yijinjing.time as kft
from kungfu.wingchun.constants import *
import kungfu.wingchun.utils as wc_utils
source = Source.XTP
exchange = Exchange.SSE
account_xtp = "15015336"
account_ctp = "36200014"
def pre_start(context):
context.log.info("pre run strategy")
context.add_account(Source.CTP, account_ctp, 100000000.0)
context.subscribe(Source.CTP, ["IF2407", "IH2407", "IC2407"], Exchange.CFFEX)
context.count = 0
context.log.info("pre_start complete!!!")
def pre_stop(context):
context.log.info("pre stop strategy")
def on_quote(context, quote):
context.log.info("quote received, instrument_id:{}, exchange_id:{}".format(quote.instrument_id, quote.exchange_id))
if context.count >= 3:
context.log.info(" has reached the numlimit for insertorder++++++++++++++!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!+++++++++++++")
return
if (quote.instrument_id == "IF2407" or quote.instrument_id == "IH2407" or quote.instrument_id == "IC2407") :
order_id = context.insert_order(quote.instrument_id.lower(), quote.exchange_id, account_ctp, quote.ask_price[0], 1, PriceType.Limit, Side.Buy, Offset.Open)
if 0 != order_id:
context.log.info("insert_order({}) successfully, order_id is {}".format(quote.instrument_id, order_id))
context.count += 1
else:
context.log.error("insert_order({}) failed, order_id is {}".format(quote.instrument_id, order_id))
pass
def on_order(context, order):
context.log.info('order received: [order_id]{} [instrument_id]{} [volume]{} [price]{} [status]{}'.format(order.order_id, order.instrument_id, order.volume, order.limit_price, int(order.status)))
def on_trade(context, trade):
context.log.info('trade received: {} [trade_id]{} [volume]{} [price]{}'.format(kft.strftime(trade.trade_time), trade.trade_id, trade.volume, trade.price))
1
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
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
# 获取全量静态行情
#启动前
def pre_start(context):
context.log.info("pre run strategy")
#获取沪深交易所全量静态行情
# 静态行情数量大于零,说明获取成功
global quotes
quotes=context.getAllStaticQuote()
context.log.info('static quotes num: {}.'.format(len(quotes)))
quote = quotes['600000.SSE']
context.log.info('quotes[600000.SSE]={}'.format(quote))
context.log.info('instrument_id={}, exchange_id={}, upper_limit_price={}, lower_limit_price={}, '.format(quote.instrument_id, quote.exchange_id, quote.upper_limit_price, quote.lower_limit_price))
#遍历quotes
#for key in quotes:
# quote = quotes[key]
# context.log.info("key={}, instrument_id={}, exchange_id={}".format(key, quote.instrument_id, quote.exchange_id))
#getAllStaticQuote()函数的使用方法,详见“kungfu SDK API 文档”
#获取新三板/北交所全量静态行情
# 静态行情数量大于零,说明获取成功
# 备注:context.getAllNQStaticQuote()函数从 2.1.3 版本开始支持
global nq_quotes
nq_quotes=context.getAllNQStaticQuote()
context.log.info('nq static quotes num: {}.'.format(len(nq_quotes)))
context.log.info("pre_start complete!!!")
1
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
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