策略 -> 策略列表 -> 我的策略 -> 新增策略 -> 填写策略名称,选择语言为 “Python”(已固定),添加注释说明 -> 确定
如果系统提示下载编写工具,就点击下载安装包,编辑器解压进度条100%后,会自动跳转到 vscode 编辑器页面
以下是双均线策略代码举例: 当5日均线上穿20日均线时,平空买多; 当5日均线下穿20日均线时,平多卖空;
from pydantic import BaseModel, Field
import numpy as np
# 判断当前是否持有指定方向的仓位
def is_holding(context,direction):
try:
if context.base_instrument_id in context.positions:
# 把成交单传到全局变量 position里面
position = context.positions.get(context.base_instrument_id)
position_type = position.get("Direction")
if str(position_type) == str(direction):
return True
except Exception as e:
put_log(f"[isHolding] => error ==> {e}", 'ERROR')
return False
# 获取K线数据
def get_k_line_data(context):
len1 = context.len1
len2 = context.len2
context.klines = get_kline(context.base_instrument_id, context.base_period, max(len1, len2))
context.dt_array = context.klines.get('datetime')
context.close_array = context.klines.get("close")
# 计算均线指标数据
def calc_sma(context):
import talib as ta
len1 = context.len1
len2 = context.len2
context.close_array = context.klines.get("close")
ma1_array = ta.SMA(np.asarray(context.close_array),len1)
ma2_array = ta.SMA(np.asarray(context.close_array),len2)
context.ma1_array = np.roll(context.ma1_array, -1)
context.ma2_array = np.roll(context.ma2_array, -1)
context.ma1_array[-1] = ma1_array[-1]
context.ma2_array[-1] = ma2_array[-1]
# 根据SMA指标产生开仓信号
def get_open_signal_by_ma(context):
if len(context.ma2_array) < 3 or len(context.ma1_array) < 3:
return 0 # 数值不足
sig_open = 0
if context.ma1_array[-3] <= context.ma2_array[-3] \
and context.ma1_array[-2] > context.ma2_array[-2]:
sig_open = 1
elif context.ma1_array[-3] >= context.ma2_array[-3] \
and context.ma1_array[-2] < context.ma2_array[-2]:
sig_open = -1
return sig_open
# 根据指标计算平仓信号
def get_close_signal_by_ma(context):
if len(context.ma2_array) < 3 or len(context.ma1_array) < 3:
return 0 # 数值不足
sig_close = 0
if context.ma1_array[-3] <= context.ma2_array[-3] \
and context.ma1_array[-2] > context.ma2_array[-2]:
sig_close = 1
elif context.ma1_array[-3] >= context.ma2_array[-3] \
and context.ma1_array[-2] < context.ma2_array[-2]:
sig_close = -1
return sig_close
# 根据信号开仓
def open_position_by_signal(context, sig_open):
if sig_open == 1: # 开多信号
if not is_holding(context,"0"): # 没有持多仓
buy_open(context.volume, context.base_instrument_id)
elif sig_open == -1: # 开空信号
if not is_holding(context,"1"): # 没有持空仓
sell_open(context.volume, context.base_instrument_id)
# 根据信号平仓
def close_position_by_signal(context, sig_close):
if sig_close == 1: # 平空信号
if is_holding(context,"1"): # 持有空仓
buy_close(context.volume, context.base_instrument_id)
elif sig_close == -1: # 平多信号
if is_holding(context,"0"): # 持有多仓
sell_close(context.volume, context.base_instrument_id)
# k线收盘运行逻辑
def on_bar(context):
get_k_line_data(context)
calc_sma(context)
sig_open = get_open_signal_by_ma(context)
sig_close = get_close_signal_by_ma(context)
close_position_by_signal(context, sig_close)
open_position_by_signal(context, sig_open)
######################## main programm ##############################
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
len1:int = Field(default=5,title="均线1周期")
len2:int = Field(default=20,title="均线2周期")
volume:int = Field(default=1,title="手数")
# 初始化一些参数
def on_init(context):
import talib
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.base_period = BASE_SETTING.get("bPeriod")
context.positions = {}
context.close_array = np.zeros(context.len2)
context.ma1_array = np.zeros(context.len1)
context.ma2_array = np.zeros(context.len2)
# 每次有行情进来,便会调用 on_tick 函数刷新一次
def on_tick(context):
# 注册K线处理回调函数
on_bar_run(on_bar, context)
# 返回委托单信息 注意此处的报单信息是未成交状态
def on_order(context,order):
pass
# 返回成交单信息 注意此处的报单信息是已成交状态
def on_trade(context,trade):
InstrumentID = trade.get("InstrumentID")
OffsetFlag = trade.get("OffsetFlag")
if OffsetFlag == "0":
context.positions[InstrumentID] = trade
else:
context.positions.pop(InstrumentID)
# 返回报错
def on_error(context,order):
pass
# 策略回测中途发生错误 or 回撤完毕时调用
def on_stop(context):
pass
编辑好策略文件后,保存文件,在文件中单击右键,选择python编译,编译输出中显示编译成功即可。
【注意】在编辑器中不要运行代码,否则会报错。
回测栏 -> 添加回测单元 -> 在参数设置页面填写基础参数和策略参数后保存 -> 点击回测
回测完成后点击详情查看报告,综合报告,收益回撤,图表分析,明细详情,参数信息等,回测日志区内容在软件退出后会保存到日志目录,如用户目录的AppData\Local\qmfquant\logs\backtest_server下面
任务栏 -> 双击策略 -> 填写任务参数 -> 提交任务 -> 启动
可以在任务界面查看实时K线和日志内容,日志内容在客户端退出后会保存到日志目录,如用户目录的AppData\Local\qmfquant\logs\task_server下面
一个完整的策略,一般包含以下6个结构性函数:on_tick,on_init,on_stop,on_order,on_error,on_trade
# 外置参数
# 参数映射模型
from pydantic import BaseModel
class Params(BaseModel,validate_assignment=True):
pass
# 行情报价每进来一次就会 onTick 刷新一次
def on_tick(context):
# 调用 获取K线 功能性函数举例:
klines = get_kline("ru2501","M1",3)
# 举例省略展示打印结果
print(f"{klines}")
# 初始化默认参数
def on_init(context):
# 调用 获取账户信息 功能性函数举例:
account_info = get_account()
# 举例省略展示打印结果
print(f"{account_info}")
# 在程序停止时调用
def on_stop(context):
pass
# 订单挂单后(订单未成交状态的报单信息) 返回报单信息
def on_order(context,order):
pass
# 用于返回报错类型的方法,可在此对报错的类型进行调试与分析
def on_error(context,order):
pass
# 订单实际成交后的成交单信息
def on_trade(context,trade):
pass
说明
a. 策略的品种合约的TICK变化时调用的方法;
b. 行情报价每进来一次就会调用函数 on_tick 刷新一次。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
输出参数
无
接口案例
def on_tick(context):
pass
数据示例
无
说明
a. 该方法每次任务启动只执行一次;
b.用于设置策略交易逻辑中的初始化部分。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
输出参数
无
接口案例
def on_init(context):
pass
数据示例
无
说明
a. 策略结束时调用,可以执行一些结尾统计操作 ;
b. 保存策略的回测数据或策略回测中途发生错误或者停止都会调用 on_stop,并返回信息。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
输出参数
无
接口案例
def on_stop(context):
pass
数据示例
无
说明
a. 返回委托单信息的方法,注意此处的 order 报单信息是未成交状态(包含字段详见以下对象 ORDER_DATA,与 on_trade 中 order 对象的值会有一定差异);
b. 用于存储和访问交易过程中的各种状态和信息;
c. 可在此处对订单状态进行分析和调控。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
order | 订单信息,包含字段详见以下对象 ORDER_DATA |
ORDER_DATA 委托单对象明细字段信息
参数 | 中文描述 | 取值 |
---|---|---|
AccountID | 投资者帐号 | |
ActiveTime | 激活时间 | |
ActiveTraderID | 激活交易ID | |
ActiveUserID | 操作用户代码 | |
BranchID | 营业部编号 | |
BrokerID | 经纪公司代码 | |
BrokerOrderSeq | CTP系统的报单编号 | |
BusinessUnit | 业务单元 | |
CancelTime | 撤销时间 | |
ClearingPartID | 清算会员编号 | |
ClientID | 交易编码 | |
CombHedgeFlag | 组合投机套保标志 | |
CombOffsetFlag | 组合开平标志 | |
ContingentCondition | 触发条件 | |
CurrencyID | 币种代码 | |
Direction | 买卖方向 | |
ExchangeID | 交易所代码 | |
ExchangeInstID | 合约在交易所的代码 | |
ForceCloseReason | 强平原因 | |
FrontID | CTP后台前置编号 | |
GTDDate | GTD日期 | |
IPAddress | IP地址 | |
InsertDate | 订单插入日期 | |
InsertTime | 订单插入时间 | |
InstallID | 安装编号 | |
InstrumentID | 合约代码 | |
InvestUnitID | 投资单元代码 | |
InvestorID | 投资者代码 | |
IsAutoSuspend | 自动挂起标志 | True:自动挂起;False:非自动挂起 |
IsSwapOrder | 互换单标志 | |
LimitPrice | 限价 | |
MacAddress | Mac地址 | |
MinVolume | 最小成交量 | |
NotifySequence | 报单提示序号 | |
OrderLocalID | CTP系统分配的唯一标识符 | |
OrderPriceType | 报单价格条件 | |
OrderRef | 报单引用 | |
OrderSource | 标识订单的来源 | '0':来自参与者;'1':来自管理员; |
OrderStatus | 订单的状态 | '0':全部成交;'1':部分成交还在队列中;'2':部分成交不在队列中;'3':未成交还在队列中;'4':未成交不在队列中;'5':撤单;'a':未知;'b':尚未触发;'c':已触发; |
OrderSubmitStatus | 订单的提交状态 | '0':已经提交;'1':撤单已经提交;'2':修改已经提交;'3':已经接受;'4':报单已经被拒绝;'5':撤单已经被拒绝;'6':改单已经被拒绝; |
OrderSysID | 订单在交易所系统的唯一标识 | |
OrderType | 订单类型 | '0':正常;'1':报价衍生;'2':组合衍生;'3':组合报单;'4':条件单;'5':互换单;'6':大宗交易成交衍生;'7':期转现成交衍生; |
ParticipantID | 参与者的唯一标识 | |
RelativeOrderSysID | 关联订单的系统编号 | |
RequestID | 请求编号 | |
SequenceNo | 序号 | |
SessionID | 会话编号 | |
SettlementID | 结算编号 | |
StatusMsg | 状态信息 | |
StopPrice | 止损价 | |
SuspendTime | 顺序消费过程中消费失败后的延时时间 | |
TimeCondition | 报单有效期类型 | |
TraderID | 交易所交易员代码 | |
TradingDay | 交易系统日期 | |
UpdateTime | 行情交易数据的更新时间 | |
UserForceClose | 强制平仓标志 | |
UserID | 用户代码 | |
UserProductInfo | 用户端产品信息 | |
VolumeCondition | 成交量类型 | |
VolumeTotal | 总成交量 | |
VolumeTotalOriginal | 数量 | |
VolumeTraded | 已成交数量 | |
ZCETotalTradedVolume | 郑商所某一期货品种的总成交量 | |
reserve1 | (保留字段) | |
reserve2 | (保留字段) | |
reserve3 | (保留字段) | |
HedgeFlag | 投机套保标志 | |
OffsetFlag | 开平标志 | |
Price | 价格 | |
PriceSource | 价格来源 | |
TradeDate | 交易日期 | |
TradeID | 成交编号 | |
TradeSource | 成交来源 | |
TradeTime | 交易时间 | |
TradeType | 成交类型 | |
TradingRole | 交易角色 | |
Volume | 数量 |
输出参数
无
接口案例
def on_order(context, order):
# 获取订单状态
order_status = order.get("OrderStatus")
CombOffsetFlag = order.get("CombOffsetFlag")
StatusMsg = order.get("StatusMsg")
if order_status == "5":
if CombOffsetFlag == "0":
# 清除开启任务等待序列
context.open_task.clear()
else:
# 清除关闭任务等待序列
context.close_task.clear()
数据示例
无
说明
接收 CTP 端口发送过来的错误信息数据 data(暂只供接收信息,无法输出该信息数据内容)。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
data | 报错信息数据 |
输出参数
无
接口案例
def on_error(context, data):
pass
数据示例
无
说明
接收成交单信息的方法,注意此处的 order 报单信息是已成交状态,包含字段详见对象 ORDER_DATA(同 on_order 中的 order 对象,但值会有一定差异)。
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
order | 订单信息,包含字段详见对象 ORDER_DATA |
ORDER_DATA 成交单对象明细字段信息
参数 | 中文描述 | 取值 |
---|---|---|
AccountID | 投资者帐号 | |
ActiveTime | 激活时间 | |
ActiveTraderID | 激活交易ID | |
ActiveUserID | 操作用户代码 | |
BranchID | 营业部编号 | |
BrokerID | 经纪公司代码 | |
BrokerOrderSeq | CTP系统的报单编号 | |
BusinessUnit | 业务单元 | |
CancelTime | 撤销时间 | |
ClearingPartID | 清算会员编号 | |
ClientID | 交易编码 | |
CombHedgeFlag | 组合投机套保标志 | |
CombOffsetFlag | 组合开平标志 | |
ContingentCondition | 触发条件 | |
CurrencyID | 币种代码 | |
Direction | 买卖方向 | '0':买;'1':卖; |
ExchangeID | 交易所代码 | |
ExchangeInstID | 合约在交易所的代码 | |
ForceCloseReason | 强平原因 | |
FrontID | CTP后台前置编号 | |
GTDDate | GTD日期 | |
IPAddress | IP地址 | |
InsertDate | 订单插入日期 | |
InsertTime | 订单插入时间 | |
InstallID | 安装编号 | |
InstrumentID | 合约代码 | |
InvestUnitID | 投资单元代码 | |
InvestorID | 投资者代码 | |
IsAutoSuspend | 自动挂起标志 | |
IsSwapOrder | 互换单标志 | |
LimitPrice | 限价 | |
MacAddress | Mac地址 | |
MinVolume | 最小成交量 | |
NotifySequence | 报单提示序号 | |
OrderLocalID | CTP系统分配的唯一标识符 | |
OrderPriceType | 报单价格条件 | |
OrderRef | 报单引用 | |
OrderSource | 标识订单的来源 | |
OrderStatus | 订单的状态 | |
OrderSubmitStatus | 订单的提交状态 | |
OrderSysID | 订单在交易所系统的唯一标识 | |
OrderType | 订单类型 | |
ParticipantID | 参与者的唯一标识 | |
RelativeOrderSysID | 关联订单的系统编号 | |
RequestID | 请求编号 | |
SequenceNo | 序号 | |
SessionID | 会话编号 | |
SettlementID | 结算编号 | |
StatusMsg | 状态信息 | |
StopPrice | 止损价 | |
SuspendTime | 顺序消费过程中消费失败后的延时时间 | |
TimeCondition | 报单有效期类型 | |
TraderID | 交易所交易员代码 | |
TradingDay | 交易系统日期 | |
UpdateTime | 行情交易数据的更新时间 | |
UserForceClose | 强制平仓标志 | |
UserID | 用户代码 | |
UserProductInfo | 用户端产品信息 | |
VolumeCondition | 成交量类型 | |
VolumeTotal | 总成交量 | |
VolumeTotalOriginal | 数量 | |
VolumeTraded | 已成交数量 | |
ZCETotalTradedVolume | 郑商所某一期货品种的总成交量 | |
reserve1 | (保留字段) | |
reserve2 | (保留字段) | |
reserve3 | (保留字段) | |
HedgeFlag | 投机套保标志 | |
OffsetFlag | 开平标志 | '0':开仓;'1':平仓;'2':强平;'3':平今;'4':平昨;'5':强减;'6':本地强平; |
Price | 价格 | |
PriceSource | 价格来源 | |
TradeDate | 交易日期 | |
TradeID | 成交编号 | |
TradeSource | 成交来源 | |
TradeTime | 交易时间 | |
TradeType | 成交类型 | '#':组合持仓拆分为单一持仓,初始化不应包含该类型的持仓;'0':普通成交;'1':期权执行;'2':OTC成交;'3':期转现衍生成交;'4':组合衍生成交;'5':大宗交易成交; |
TradingRole | 交易角色 | |
Volume | 数量 |
输出参数
无
接口案例
def on_trade(context, order):
pass
数据示例
无
说明
获取K线的开高低收、时间周期、成交量的序列。
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
symbol | 期货品种合约,如 ru2501(橡胶2501) | str |
period | 时间周期,支持的周期:1分钟 "M1"、3分钟 "M3"、5分钟 "M5"、10分钟 "M10"、 15分钟 "M15"、30分钟 "M30"、45分钟 "M45"、1小时 "H1"、2小时 "H2"、4小时 "H4"、"1天 "D1"、1周 "W1" | str |
len | 输出参数对获取K线数据的长度做一个限制,默认长度为30;也可自行设置长度,如 len=60 | int |
返回值
返回一个字典
key | 中文描述 |
---|---|
close | 收盘价 |
open | 开盘价 |
high | 最高价 |
low | 最低价 |
datetime | 日期时间 |
volume | 成交量 |
接口案例
klines = get_kline("ru2501","M1",3)
print(f"{klines}")
数据示例
{
'close': [7687.0, 7686.0, 7647.0],
'open': [7687.0, 7687.0, 7686.0],
'high': [7687.0, 7688.0, 7686.0],
'low': [7687.0, 7686.0, 7647.0],
'datetime': [Timestamp('2024-11-15 14:57:00'),
Timestamp('2024-11-15 14:58:00'),
Timestamp('2024-11-15 14:59:00')]
'volume': [0.0, 3.0, 3966.0]
}
a) 衍生数据接口有使用频率限制,在策略回测模式中,建议在on_init函数中一次性把需要的数据都取出来,不要在on_tick函数中频繁调用接口;
b) 所有衍生数据都是通过市场公开数据收集而来,数据准确性期魔方不做保证,仅做参考
1、说明
返回指定 symbol 的具体品种大宗商品库存数据
2、输入参数
参数 | 中文描述 | 参数类型 | 是否必填 | 描述 |
---|---|---|---|---|
symbol | 品种 | 字符 | 是 | 外盘品种字典见下方库存品种字典 |
begin_date | 开始日期 | 字符 | 否 | 格式:yyyy-MM-dd |
end_date | 结束日期 | 字符 | 否 | 格式:yyyy-MM-dd |
序号 | 品种代码 | 品种名称 |
---|---|---|
1 | cu | 铜 |
2 | bc | 铜(BC) |
3 | al | 铝 |
4 | zn | 锌 |
5 | pb | 铅 |
6 | ni | 镍 |
7 | sn | 锡 |
8 | ao | 氧化铝 |
9 | au | 黄金 |
10 | ag | 白银 |
11 | rb | 螺纹钢 |
12 | wr | 线材 |
13 | hc | 热轧卷板 |
14 | ss | 不锈钢 |
15 | sc | 原油 |
16 | lu | 低硫燃料油 |
17 | fu | 燃料油 |
18 | bu | 石油沥青 |
19 | br | 丁二烯橡胶 |
20 | ru | 天然橡胶 |
21 | nr | 20号胶 |
22 | sp | 纸浆 |
23 | WH | 强麦 |
24 | PM | 普麦 |
25 | CF | 棉花 |
26 | SR | 白糖 |
27 | OI | 菜籽油 |
28 | RI | 早籼稻 |
29 | RS | 油菜籽 |
30 | RM | 菜籽粕 |
31 | JR | 粳稻 |
32 | LR | 晚籼稻 |
33 | PR | 瓶片 |
34 | CY | 棉纱 |
35 | AP | 苹果 |
36 | CJ | 红枣 |
37 | PK | 花生 |
38 | TA | PTA |
39 | MA | 甲醇 |
40 | FG | 玻璃 |
41 | ZC | 动力煤 |
42 | SF | 硅铁 |
43 | SM | 锰硅 |
44 | UR | 尿素 |
45 | SA | 纯碱 |
46 | PF | 短纤 |
47 | PX | 对二甲苯 |
48 | SH | 烧碱 |
49 | c | 玉米 |
50 | lg | 原木 |
51 | cs | 玉米淀粉 |
52 | a | 豆一 |
53 | b | 豆二 |
54 | m | 豆粕 |
55 | y | 豆油 |
56 | p | 棕榈油 |
57 | fb | 纤维板 |
58 | bb | 胶合板 |
59 | jd | 鸡蛋 |
60 | rr | 粳米 |
61 | lh | 生猪 |
62 | l | 聚乙烯 |
63 | v | 聚氯乙烯 |
64 | pp | 聚丙烯 |
65 | j | 焦炭 |
66 | jm | 焦煤 |
67 | i | 铁矿石 |
68 | eg | 乙二醇 |
69 | eb | 苯乙烯 |
70 | pg | 液化石油气 |
71 | LC | 碳酸锂 |
72 | SI | 工业硅 |
73 | PS | 多晶硅 |
3、输出参数
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
code | 接口查询状态 | int | 0:表示获取数据成功; -1:表述获取数据失败 |
data | 库存数据 | json | 库存数据JSON对象,可直接转pandas 有columns和data两列:columns是列标题,data是数据 |
msg | 接口查询结果中文描述 | str | 接口执行后返回的成功/失败消息描述 |
库存数据JSON对象的data对象
参数 | 参数类型 |
---|---|
日期 | str |
收盘价 | float64 |
库存 | int64 |
4、接口调用案例
调用接口代码
from futures_data_client import fetch_futures_inventory_99
result1 = fetch_futures_inventory_99('ag', '2025-02-1', '2025-02-27')
print(result1)
数据返回案例
{
'code': 0,
'data': {
'columns': ['日期', '收盘价', '库存'],
'data': [
['2025-02-07', 8055.0, 120081],
['2025-02-21', 8102.0, 122764]
]
},
'msg': '获取数据成功'
}
1、说明
获取仓单日报的库存数据,目前是近 60 个交易日的数据
2、输入参数
参数 | 中文描述 | 参数类型 | 是否必填 | 描述 |
---|---|---|---|---|
symbol* | 品种代码 | 字符 | 是 | 外盘品种字典见下方仓单日报库存品种字典字典数据 |
begin_date | 开始日期 | 字符 | 否 | 格式:yyyy-MM-dd |
end_date | 结束日期 | 字符 | 否 | 格式:yyyy-MM-dd |
序号 | 品种代码 | 品种名称 |
---|---|---|
1 | a | 豆一 |
2 | ag | 沪银 |
3 | al | 沪铝 |
4 | ao | 氧化铝 |
5 | AP | 苹果 |
6 | au | 沪金 |
7 | b | 豆二 |
8 | br | BR橡胶 |
9 | bu | 沥青 |
10 | c | 玉米 |
11 | CF | 棉花 |
12 | CJ | 红枣 |
13 | cs | 淀粉 |
14 | cu | 沪铜 |
15 | CY | 棉纱 |
16 | eb | 苯乙烯 |
17 | ec | 集运欧线 |
18 | eg | 乙二醇 |
19 | FG | 玻璃 |
20 | fu | 燃料油 |
21 | hc | 热卷 |
22 | i | 铁矿石 |
23 | IC | 中证500指数 |
24 | IF | 沪深300指数 |
25 | IH | 上证50指数 |
26 | IM | 中证1000指数 |
27 | j | 焦炭 |
28 | jd | 鸡蛋 |
29 | jm | 焦煤 |
30 | l | 塑料 |
31 | lc | 碳酸锂 |
32 | lh | 生猪 |
33 | lu | LU燃油 |
34 | m | 豆粕 |
35 | MA | 甲醇 |
36 | ni | 沪镍 |
37 | nr | 20号胶 |
38 | OI | 菜籽油 |
39 | p | 棕榈油 |
40 | pb | 沪铅 |
41 | PF | 短纤 |
42 | pg | 液化气 |
43 | PK | 花生 |
44 | pp | 聚丙烯 |
45 | PX | 对二甲苯 |
46 | rb | 螺纹钢 |
47 | RM | 菜籽粕 |
48 | RS | 油菜籽 |
49 | ru | 橡胶 |
50 | SA | 纯碱 |
51 | SF | 硅铁 |
52 | SH | 烧碱 |
53 | si | 工业硅 |
54 | SM | 锰硅 |
55 | sn | 沪锡 |
56 | sp | 纸浆 |
57 | SR | 白糖 |
58 | ss | 不锈钢 |
59 | T | 十年国债 |
60 | TA | PTA |
61 | TF | 五年国债 |
62 | TL | 三十年国债 |
63 | TF | 五年国债 |
64 | TS | 二年国债 |
65 | UR | 尿素 |
66 | v | PVC |
67 | y | 豆油 |
68 | zn | 沪锌 |
3、输出参数
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
code | 接口查询状态 | int | 0:表示获取数据成功; -1:表述获取数据失败 |
data | 库存数据 | json | 仓单日报库存数据JSON对象,可直接转pandas 数据: columns和data两列:columns是列标题,data是数据 |
msg | 接口查询结果中文描述 | str | 接口执行后返回的成功/失败消息描述 |
仓单日报库存数据JSON对象data对象
参数 | 参数类型 | 描述 |
---|---|---|
日期 | str | 日期 |
库存 | int64 | 库存数据 |
增减 | float64 | 相对前一个交易日的增减 |
4、接口调用案例
调用接口代码
from futures_data_client import fetch_futures_inventory_em
result1 = fetch_futures_inventory_em('ag', '2025-02-27', '2025-02-28')
print(result1)
数据返回案例
{
'code': 0,
'data': {
'columns': ['日期', '库存', '增减'],
'data': [
['2025-02-27', 1293568, -7845.0],
['2025-02-28', 1277102, -16466.0]
]
},
'msg': '获取数据成功'
}
1、说明
获取期货外盘历史行情数据
2、输入参数
参数 | 中文描述 | 参数类型 | 是否必填 | 描述 |
---|---|---|---|---|
outer_symbol | 外盘品种代码 | 字符 | 是 | 外盘品种字典见下方外盘品种字典数据 |
begin_date | 开始日期 | 字符 | 否 | 格式:yyyy-MM-dd |
end_date | 结束日期 | 字符 | 否 | 格式:yyyy-MM-dd |
序号 | 外盘品种代码 | 外盘品种名称 |
---|---|---|
1 | FEF | 新加坡铁矿石 |
2 | FCPO | 马棕油 |
3 | RSS3 | 日橡胶 |
4 | RS | 美国原糖 |
5 | BTC | CME比特币期货 |
6 | CT | NYBOT-棉花 |
7 | NID | LME镍3个月 |
8 | PBD | LME铅3个月 |
9 | SND | LME锡3个月 |
10 | ZSD | LME锌3个月 |
11 | AHD | LME铝3个月 |
12 | CAD | LME铜3个月 |
13 | S | CBOT-黄豆 |
14 | W | CBOT-小麦 |
15 | C | CBOT-玉米 |
16 | BO | CBOT-黄豆油 |
17 | SM | CBOT-黄豆粉 |
18 | TRB | 日本橡胶 |
19 | HG | COMEX铜 |
20 | NG | NYMEX天然气 |
21 | CL | NYMEX原油 |
22 | SI | COMEX白银 |
23 | GC | COMEX黄金 |
24 | LHC | CME-瘦肉猪 |
25 | OIL | 布伦特原油 |
26 | XAU | 伦敦金 |
27 | XAG | 伦敦银 |
28 | XPT | 伦敦铂金 |
29 | XPD | 伦敦钯金 |
30 | EUA | 欧洲碳排放 |
3、输出参数
查询接口返回对象
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
code | 接口返回查询状态 | int | 0:表示获取数据成功;-1:表述获取数据失败 |
data | 外盘数据 | json | 外盘数据的JSON对象,可直接转pandas 有columns和data两列:columns是列标题,data是数据 |
msg | 接口查询结果中文描述 | str | 接口执行后返回的成功/失败消息描述 |
外盘数据JSON对象的data对象
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
日期 | 交易日 | str | |
open | 开盘价 | float64 | |
high | 最高价 | float64 | |
low | 最低价 | float64 | |
close | 收盘价 | float64 | |
volume | 成交量 | int64 |
4、接口调用案例
调用接口代码
from futures_data_client import fetch_futures_foreign_hist
result1 = fetch_futures_foreign_hist('XAU', '2025-03-06', '2025-03-07')
print(result1)
数据返回案例
{
'code': 0,
'data': {
'columns': ['日期', 'open', 'high', 'low', 'close', 'volume', 'position', 's'],
'data': [
['2025-03-06', 2918.54, 2926.44, 2891.19, 2910.76, 0, 0, 0],
['2025-03-07', 2911.04, 2930.12, 2896.56, 2912.0, 0, 0, 0]
]
},
'msg': '获取数据成功'
}
1、说明
查询黄金/白银的所有库存数据
2、输入参数
参数 | 中文描述 | 参数类型 | 是否必填 | 描述 |
---|---|---|---|---|
symbol_name | 品种名称 | 字符 | 是 | 外品名称,字典值:黄金,白银 |
begin_date | 开始日期 | 字符 | 否 | 格式:yyyy-MM-dd |
end_date | 结束日期 | 字符 | 否 | 格式:yyyy-MM-dd |
3、输出参数**
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
code | 接口查询状态 | int | 0:表示获取数据成功; -1:表述获取数据失败 |
data | 库存数据 | json | 库存数据SON对象,可直接转pandas 数据: columns和data两列:columns是列标题,data是数据 |
msg | 接口查询结果中文描述 | str | 接口执行后返回的成功/失败消息描述 |
库存数据JSON对象data对象
参数 | 参数类型 |
---|---|
序号 | str |
日期 | str |
COMEX白银库存量-吨 | int64 |
COMEX白银库存量-盎司 | float64 |
4、接口调用案例
调用接口代码
from futures_data_client import fetch_futures_comex_inventory
result1 = fetch_futures_comex_inventory('白银', '2025-3-6', '2025-3-7')
print(result1)
数据返回案例
{
'code': 0,
'data': {
'columns': ['序号', '日期', 'COMEX白银库存量-吨', 'COMEX白银库存量-盎司'],
'data': [
[1242, '2025-03-06', 13072.104555172, 420277607.188]
]
},
'msg': '获取数据成功'
}
1、说明
查询品种在指定日期现货和基差数据
2、输入参数
参数 | 中文描述 | 参数类型 | 是否必填 | 描述 |
---|---|---|---|---|
symbol_no | 品种代码 | 字符 | 是 | 数据字典,见现货和基差品种字典 |
begin_date | 开始日期 | 字符 | 否 | 格式:yyyy-MM-dd |
end_date | 结束日期 | 字符 | 否 | 格式:yyyy-MM-dd |
序号 | 品种代码 | 品种名称 |
---|---|---|
1 | a | 豆一 |
2 | ag | 沪银 |
3 | al | 沪铝 |
4 | ao | 氧化铝 |
5 | AP | 苹果 |
6 | au | 沪金 |
7 | b | 豆二 |
8 | br | BR橡胶 |
9 | bu | 沥青 |
10 | c | 玉米 |
11 | CF | 棉花 |
12 | CJ | 红枣 |
13 | cs | 淀粉 |
14 | cu | 沪铜 |
15 | CY | 棉纱 |
16 | eb | 苯乙烯 |
17 | ec | 集运欧线 |
18 | eg | 乙二醇 |
19 | FG | 玻璃 |
20 | fu | 燃料油 |
21 | hc | 热卷 |
22 | i | 铁矿石 |
23 | IC | 中证500指数 |
24 | IF | 沪深300指数 |
25 | IH | 上证50指数 |
26 | IM | 中证1000指数 |
27 | j | 焦炭 |
28 | jd | 鸡蛋 |
29 | jm | 焦煤 |
30 | l | 塑料 |
31 | lc | 碳酸锂 |
32 | lh | 生猪 |
33 | lu | LU燃油 |
34 | m | 豆粕 |
35 | MA | 甲醇 |
36 | ni | 沪镍 |
37 | nr | 20号胶 |
38 | OI | 菜籽油 |
39 | p | 棕榈油 |
40 | pb | 沪铅 |
41 | PF | 短纤 |
42 | pg | 液化气 |
43 | PK | 花生 |
44 | pp | 聚丙烯 |
45 | PX | 对二甲苯 |
46 | rb | 螺纹钢 |
47 | RM | 菜籽粕 |
48 | RS | 油菜籽 |
49 | ru | 橡胶 |
50 | SA | 纯碱 |
51 | SF | 硅铁 |
52 | SH | 烧碱 |
53 | si | 工业硅 |
54 | SM | 锰硅 |
55 | sn | 沪锡 |
56 | sp | 纸浆 |
57 | SR | 白糖 |
58 | ss | 不锈钢 |
59 | T | 十年国债 |
60 | TA | PTA |
61 | TF | 五年国债 |
62 | TL | 三十年国债 |
63 | TF | 五年国债 |
64 | TS | 二年国债 |
65 | UR | 尿素 |
66 | v | PVC |
67 | y | 豆油 |
68 | zn | 沪锌 |
3、输出参数
参数 | 参数名称 | 参数类型 | 描述 |
---|---|---|---|
code | 接口查询状态 | int | 0:表示获取数据成功; -1:表述获取数据失败 |
data | 现货和基差数据JSON对象 | json | 库存数据SON对象,可直接转pandas 数据: columns和data两列:columns是列标题,data是数据 |
msg | 接口查询结果中文描述 | str | 接口执行后返回的成功/失败消息描述 |
现货和基差数据JSON对象
参数 | 参数类型 | 参数描述 |
---|---|---|
日期 | str | |
symbol | str | 品种 |
spot_price | float64 | 现货价格 |
near_contract | str | 最近交割的合约 |
near_contract_price | float64 | 最近交割合约价格 |
dominant_contract | str | 主力合约 |
dominant_contract_price | float64 | 主力合约价格 |
near_month | str | 最近月交割合约 |
dominant_month | str | 主力合约 |
near_basis | float64 | 最近合约基差值 |
dom_basis | float64 | 主力合约基差值 |
near_basis_rate | float64 | 最近合约基差率 |
dom_basis_rate | float64 | 主力合约基差率 |
4、接口调用案例
调用接口代码
from futures_data_client import fetch_futures_spot_price
result1 = fetch_futures_spot_price('ag', '2025-3-6', '2025-3-7')
print(result1)
数据返回案例
{
'code': 0,
'data': {
'columns': ['日期', 'symbol', 'spot_price', 'near_contract', 'near_contract_price', 'dominant_contract', 'dominant_contract_price', 'near_month', 'dominant_month', 'near_basis', 'dom_basis', 'near_basis_rate', 'dom_basis_rate'],
'data': [
['2025-03-06', 'A', 4180.0, 'a2503', 4100.0, 'a2505', 4175.0, '2503', '2505', -80.0, -5.0, -0.019138756, -0.0011961722]
]
},
'msg': '获取数据成功'
}
说明
获取当下已登录期货公司账户信息
输入参数
无
返回值
ACCOUNT_DATA 账户对象
参数 | 中文描述 |
---|---|
BrokerID | 经纪公司代码 |
AccountID | 投资者账号 |
PreMortgage | 上次质押金额 |
PreCredit | 上次信用额度 |
PreDeposit | 上次存款额 |
PreBalance | 上次结算准备金 |
PreMargin | 上次占用的保证金 |
InterestBase | 利息基数 |
Interest | 利息收入 |
Deposit | 入金金额 |
Withdraw | 出金金额 |
FrozenMargin | 冻结的保证金 |
FrozenCash | 冻结的资金 |
FrozenCommission | 冻结的手续费 |
CurrMargin | 当前保证金总额 |
CashIn | 资金差额 |
Commission | 手续费 |
CloseProfit | 平仓盈亏 |
PositionProfit | 持仓盈亏 |
Balance | 期货结算准备金 |
Available | 可用资金 |
WithdrawQuota | 可取资金 |
Reserve | 基本准备金 |
TradingDay | 交易日 |
SettlementID | 结算编号 |
Credit | 信用额度 |
Mortgage | 质押金额 |
ExchangeMargin | 交易所保证金 |
DeliveryMargin | 投资者交割保证金 |
ExchangeDeliveryMargin | 交易所交割保证金 |
ReserveBalance | 保底期货结算准备金 |
CurrencyID | 币种代码 |
PreFundMortgageIn | 上次货币质入金额 |
PreFundMortgageOut | 上次货币质出金额 |
FundMortgageIn | 货币质入金额 |
FundMortgageOut | 货币质出金额 |
FundMortgageAvailable | 货币质押余额 |
MortgageableFund | 可质押货币金额 |
SpecProductMargin | 特殊产品占用保证金 |
SpecProductFrozenMargin | 特殊产品冻结保证金 |
SpecProductCommission | 特殊产品手续费 |
SpecProductFrozenCommission | 特殊产品冻结手续费 |
SpecProductPositionProfit | 特殊产品持仓盈亏 |
SpecProductCloseProfit | 特殊产品平仓盈亏 |
SpecProductPositionProfitByAlg | 根据持仓盈亏算法计算的特殊产品持仓盈亏 |
SpecProcudtExchangeMargin | 特殊产品交易所保证金 |
BizType | 业务类型 |
ForzenSwap | 延时换汇冻结金额 |
RemainSwap | 剩余换汇额度 |
接口案例
def on_init(context):
account_info = get_account()
print(f"{account_info}")
数据示例
{
'AccountID': '182958',
'Available': 20833162.898450002,
'Balance': 20940449.198450003,
'BizType': '',
'BrokerID': '9999',
'CashIn': 0.0,
# 以下展示内容已省略
...
}
说明
返回一个dict,从CTP获取指定品种代码或合约代码产品细则数据
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
symbol | 期货合约标识,如 ru2501(橡胶2501),详见以下 SYMBOLINFO_DATA 对象 | str |
输出参数
SYMBOLINFO_DATA 产品对象明细字段信息
参数 | 中文描述 | 类型 |
---|---|---|
id | 合约的编号 | int |
exchange_id | 交易所符号 | str |
product_class | 合约前缀字母 | str |
product_name | 合约名称 | str |
volume_multiple | 合约乘数 | int |
price_tick | 一跳的价格 | float |
margin_ratio | 保证金比例 | float |
commission_type | 手续费类型 | int |
commission_value | 手续费类型 | float |
limit_d1 | 手续费1档 | float |
limit_d2 | 手续费2档 | float |
limit_d3 | 手续费3档 | float |
接口案例
def on_tick(context):
# 获取沪银产品信息
ag_symbolinfo = get_symbolinfo("ag")
print(f"{ag_symbolinfo}")
数据示例
{
'id': 1,
'exchange_id': 'SHFE',
'product_class': 'ag',
'product_name': '沪银',
'volume_multiple': 15,
# 以下内容已省略
...
}
说明
返回一个list,用于获取用户目前期货交易账户中所有持仓明细详情的函数。
输入参数
无
输出参数
POSITION_DATA 持仓对象明细字段信息
参数 | 类型 | 中文描述 | 取值 |
---|---|---|---|
AbandonFrozen | int | 放弃执行冻结 | |
BrokerID | str | 经纪公司代码 | |
CashIn | int | 入金金额 | |
CloseAmount | int | 平仓金额 | |
CloseProfit | int | 平仓盈亏 | |
CloseProfitByDate | int | 平仓逐日盈亏 | |
CloseProfitByTrade | int | 平仓逐步盈亏 | |
CloseVolume | int | 平仓手数 | |
CombLongFrozen | int | 组合多头冻结 | |
CombPosition | int | 组合持仓 | |
CombShortFrozen | int | 组合空头冻结 | |
Commission | int | 手续费 | |
ExchangeID | str | 交易所ID | |
ExchangeMargin | int | 交易所保证金 | |
FrozenCash | int | 冻结资金 | |
FrozenCommission | int | 冻结手续费 | |
FrozenMargin | int | 冻结保证金 | |
HedgeFlag | str | 投机套保标志 | |
InstrumentID | str | 合约代码 | |
InvestUnitID | str | ||
InvestorID | str | 投资者代码 | |
LongFrozen | int | 多头冻结 | |
LongFrozenAmount | int | 多头冻结金额 | |
MarginRateByMoney | int | 保证金比例 | |
MarginRateByVolume | int | 每手保证金 | |
OpenAmount | int | 开仓金额 | |
OpenCost | float | 开仓价值 | |
OpenVolume | int | 开仓手数 | |
PosiDirection | str | 持仓多空方向 | '2':多头;'3':空头;'1':其它 |
Position | int | 当前持仓 | |
PositionCost | int | 持仓价值 | |
PositionCostOffset | int | 持仓价值平移 | |
PositionDate | str | 持仓日期 | |
PositionProfit | int | 持仓盈亏 | |
PreMargin | int | 前保证金 | |
PreSettlementPrice | int | 前结算价 | |
SettlementID | int | 结算ID | |
SettlementPrice | int | 结算价 | |
ShortFrozen | int | 空头冻结 | |
ShortFrozenAmount | int | 空头冻结金额 | |
StrikeFrozen | int | 执行冻结 | |
StrikeFrozenAmount | int | 执行冻结金额 | |
TasPositionCost | int | tas持仓价值 | |
TodayAvgPrice | float | 今均价 | |
TodayPosition | int | 今持仓 | |
TradingDay | str | 交易日 | |
UsedMargin | float | 已用保证金 | |
YdPosition | int | 昨仓持仓量 | |
YdStrikeFrozen | int | 执行昨仓冻结 |
接口案例
def on_init(context):
# 获取当前持仓的明细
position_info = get_position()
print(f"{position_info}")
数据示例
[
{
'AbandonFrozen': 0,
'BrokerID': '9999',
'CashIn': 0.0,
'CloseAmount': 0,
'CloseProfit': 0
# 以下展示内容已省略
...
}
]
说明
获取指定合约 id 的当前最新报价
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
symbol_code | 指定合约代码 | str |
输出参数
参数 | 中文描述 |
---|---|
AskPrice1 | 申卖价一 |
AskPrice2 | 申卖价二 |
AskPrice3 | 申卖价三 |
AskPrice4 | 申卖价四 |
AskPrice5 | 申卖价五 |
AskVolume1 | 申卖量一 |
AskVolume2 | 申卖量二 |
AskVolume3 | 申卖量三 |
AskVolume4 | 申卖量四 |
AskVolume5 | 申卖量五 |
AveragePrice | 当日均价 |
BandingLowerPrice | 设定更低价格区间 |
BandingUpperPrice | 设定更高价格区间 |
BidPrice1 | 申买价一 |
BidPrice2 | 申买价二 |
BidPrice3 | 申买价三 |
BidPrice4 | 申买价四 |
BidPrice5 | 申买价五 |
BidVolume1 | 申买量一 |
BidVolume2 | 申买量二 |
BidVolume3 | 申买量三 |
BidVolume4 | 申买量四 |
BidVolume5 | 申买量五 |
ClosePrice | 今收盘 |
CurrDelta | 今虚实度 |
ExchangeID | 交易所代码 |
ExchangeInstID | 合约在交易所的代码 |
HighestPrice | 最高价 |
InstrumentID | 合约代码 |
LastPrice | 最新价 |
LowerLimitPrice | 跌停板价 |
LowestPrice | 最低价 |
OpenInterest | 持仓量 |
OpenPrice | 今开盘 |
PreClosePrice | 昨收盘 |
PreDelta | 昨虚实度 |
PreOpenInterest | 昨持仓量 |
PreSettlementPrice | 上次结算价 |
SettlementPrice | 本次结算价 |
TradingDay | 交易日 |
Turnover | 成交金额 |
UpdateMillisec | 更新毫秒数 |
UpdateTime | 最后修改时间 |
UpperLimitPrice | 涨停板价 |
Volume | 数量 |
reserve1 | (保留字段) |
reserve2 | (保留字段) |
接口案例
def on_tick(context):
# 获取合约 ag2412 当前的最新报价
ag2412_tick = get_tick("ag2412")
数据示例
{
'ActionDay': "",
'AskPrice1': '0',
'AskPrice2': 0,
'AskPrice3': 0,
'AskPrice4': 0,
'AskPrice5': 0,
'AskVolume1': 0
# 以下内容已省略
...
}
说明
建立一个多头仓位,默认采用市价单发送
输入参数
参数 | 中文描述 | 必填 |
---|---|---|
volume | 手数 | 是 |
symbol | 产品ID,不填写时,自动选择加载产品 | 否 |
price | 报单价格:不填写时,自动根据方向选择涨跌停价 | 否 |
返回值
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
接口案例
def on_tick(context):
buy_open(1) #开单1手多单
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
建立一个空头仓位,默认采用市价单发送
输入参数**
参数 | 必填 | 中文描述 |
---|---|---|
volume | 是 | 手数 |
symbol | 否 | 产品ID;不填写时,自动选择加载产品 |
price | 否 | 报单价格:不填写时,自动根据方向选择涨跌停价 |
输出参数
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
接口案例
def on_tick(context):
sell_open(1) #开单1手空单
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
结束一个空头仓位,默认采用市价单发送,如果存在今昨仓的持仓,平仓时会先平今
输入参数
参数 | 必填 | 中文描述 |
---|---|---|
volume | 是 | 手数 |
symbol | 否 | 产品ID;不填写时,自动选择加载产品 |
price | 否 | 报单价格:不填写时,自动根据方向选择涨跌停价 |
输出参数
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
接口案例
def on_tick(context):
buy_close(1) #平仓1手空单
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
结束一个多头仓位,默认采用市价单发送,如果存在今昨仓的持仓,平仓时会先平今;
输入参数
参数 | 必填 | 中文描述 |
---|---|---|
volume | 是 | 手数 |
symbol | 否 | 产品ID;不填写时,自动选择加载产品 |
price | 否 | 报单价格:不填写时,自动根据方向选择涨跌停价 |
输出参数
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
接口案例
def on_tick(context):
sell_close(1) #平仓1手多单
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
做多、做空、平仓等动作都可以通过该方法函数进行报单,用户自己封装订单
输入参数
参数 | 中文描述 |
---|---|
order_info | 字典对象,如方向、价格等(详见如下对象 ORDER_INFO_DATA) |
输出参数
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
ORDER_INFO_DATA 报单对象明细字段信息
参数 | 中文描述 | 类型 |
---|---|---|
ExchangeID | 交易所ID | str |
Symbol | 产品ID | str |
Direction | 方向,多:0,空:1 | str |
OrderPriceType | 报单价格类型,默认为2 | str |
LimitPrice | 报单价格 | float |
CombOffsetFlag | 开平标志,开仓:0,平仓/平昨:1,平今:3 | str |
Volume | 手数 | int |
接口案例
def on_tick(context):
# 假设这是一个单子完整的信息
dict={
"Direction":"0",
"OrderPriceType":"2",
"Comboffsetflag":"0",
"Volumn":"1",
"Symbol":"ag2412",
"ExchangeID":"SHFE",
"LimitPrice":66666,
}
# 执行报单
result = send_order(dict)
# 输出报单请求类型
print(result)
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
涉及撤单时调用。
输入参数
参数 | 中文描述 |
---|---|
dict | 字典对象,包含字段详见如下对象 ACTION_ORDER_INFO |
输出参数
参数 | 中文描述 | 类型 |
---|---|---|
code | 请求成功返回值为 0,请求失败返回值为非0 | int |
data | 请求成功返回值为空值,请求失败返回的对象是字典 | any |
msg | 请求成功返回值为 'ok',请求失败返回请求失败的原因 | str |
ACTION_ORDER_INFO 撤单对象明细字段信息
参数 | 中文描述 | 类型 |
---|---|---|
ExchangeID | 交易所ID | str |
Symbol | 产品IDstr | str |
Direction | 方向,多:0,空:1 | str |
OrderPriceType | 报单价格类型,默认为2 | str |
LimitPrice | 报单价格 | float |
CombOffsetFlag | 开平标志,开仓:0,平仓/平昨:1,平今:3 | str |
Volume | 手数 | int |
OrderSysID | 订单号 | str |
接口案例
def on_tick(context):
# 单子具体信息
dict={"Direction":"0",
"OrderPriceType":"2",
...
# 单子中 key 必须包含 Symbol、ExchangeID、OrderSysID,否则会存在撤单失败的情况,同 send_order 方法函数的用法举例,订单号 OrderSysID 为共12位右置的字符串,该字符串存在于 on_order 接口返回的订单信息
"OrderSysID":" 1863"
"ExchangeID":"SHFE"
"Symbol":"rb2501"
}
# 执行撤单
result = action_order(dict)
print(result)
数据示例
# 以下是请求成功返回的结果。若请求失败:code 的值为非零 且 msg 会标记请求失败的原因)
{'code': 0, 'data': None, 'msg': 'ok'}
说明
用来注册K线处理回调函数,此回调函数只在每根K线收线时运行一次
输入参数
参数 | 中文描述 |
---|---|
func | K线数据处理回调函数 |
arg | func的参数 |
返回值
无
接口案例
def on_tick(context):
...
# tick数据处理逻辑
tick_data_process(context)
...
# on_bar函数中处理K线数据
on_bar_run(on_bar, context, other_params)
def on_bar(context,other_params):
"""需要被逐根K线运行的策略代码"""
...
执行流程图解:
行情Tick到达
↓
执行on_tick()
├─ 实时平仓逻辑(每个Tick都执行)
└─ on_bar_run() # 检查K线闭合
↓
if 新K线生成:
执行on_bar()中的策略逻辑
else:
跳过
通过这种设计,既能保证实时性的平仓操作,又能确保开仓信号在K线闭合时准确触发,避免在未成形K线上做出错误决策。
数据示例
无
说明
调用该函数会暂停当前的策略运行
输入参数
无
输出参数
无
接口案例
def on_tick(context):
# 此处运行交易逻辑代码
...
# 需要退出任务运行时调用
remove()
数据示例
无
说明
推送日志到前端展示的方法
输入参数
参数 | 中文描述 |
---|---|
text_data | 消息内容 |
level | 日志级别,有INFO,ERROR,USER_LOG三种级别(默认为 'USER_LOG' 级别) |
输出参数
无
接口案例
# 以 INFO 级别示例:
def on_tick(context):
text_data="这是一条日志消息内容"
# 推送日志
put_log(text_data,level="INFO")
print(text_data)
数据示例
"这是一条日志消息内容"
说明
返回当前登录期魔方用户的信息
输入参数
无
输出参数
参数 | 中文描述 |
---|---|
dict | 字典,包含字段详见以下对象 DICT |
DICT 对象
参数 | 中文描述 |
---|---|
nickname | 用户昵称,如"张三"、"李四" |
user_id | 用户ID,如"00001" |
phone_number | 手机号码,如13333331133 |
vip_level | VIP会员等级,0:普通会员,1:黄金会员,2:超级会员 |
接口案例
# 与 on_auth() 函数一起搭配使用
def on_auth(self):
# 先通过系统接口获取用户信息
userinfo = self.get_userinfo()
# 再通过三个返回值字段来判断会员等级
# 通过会员等级判断 0:普通会员,1:黄金会员,2:超级会员
return vip_level
数据示例
# 会员等级判断 0:普通会员,1:黄金会员,2:超级会员
1
说明
获取当前时间,在回测中返回当前行情报价时间,在实际任务中返回当前的系统时间
输入参数
无
输出参数
参数 | 中文描述 |
---|---|
datetime | 返回当前时间 |
接口案例
def on_tick(context):
...
# 获取当前时间
current_dt = time_current()
数据示例
无
说明
获取当前主连合约映射的具体合约
输入参数
参数 | 中文描述 |
---|---|
symbol | 需要查询当前主力合约的品种,可以带后缀,例如 'ag2505','ag','rb240' |
返回值
返回值 | 中文描述 |
---|---|
symbol | 返回当前主力合约,例如 'c2505' |
接口案例
def on_tick(context):
...
# 获取玉米当前的主力合约
hot_symbol = get_current_main_symbol('c888')
数据示例
无
说明
on_order只能收到本任务发送请求的回复,on_allorder会接收任务挂在账户的所有的订单回复,仅在任务中使用,回测中不可用
输入参数
参数 | 中文描述 |
---|---|
context | 空对象,用于存储和传递运行时状态和信息的对象 |
order | 订单信息,包含字段详见1.2.4 on_order中的对象 ORDER_DATA |
返回值
无
接口案例
def on_init(context):
pass
def on_tick(context):
pass
def on_allorder(context,order):
put_log("收到order信息","INFO")
put_log(f"{order}","INFO")
数据示例
无
说明
调用该函数可以加载指定机器学习模型,并返回模型
输入参数
模型地址 model_file_path
参数 | 中文描述 |
---|---|
model_file_path | 输入模型的地址 |
输出参数
参数 | 中文描述 |
---|---|
object | 返回模型对象 |
接口案例
def on_tick(context):
# 此处运行交易逻辑代码
...
# 需要退出任务运行时调用
model = load_model(model_file_path)
数据示例
无
参见 1.3.14 用户可以使用 put_log 函数打印日志,如果没有显示出来,可以将level参数提高级别到‘ERROR’级别;
基于回测的日志信息在软件退出后会写入到用户目录的 AppData\Local\qmfquant\logs\backtest_server 下面
基于任务的日志信息在软件退出后会写入到用户目录的 AppData\Local\qmfquant\logs\task_server 下面
在回测功能视图右上角有综合报表按钮,综合报表结构与单策略类似
描述
外置参数是策略文件中自定义的变量,用于配置和控制策略的行为
作用
a. 允许用户根据需求动态调整策略;
b. 提供灵活性,使策略能适应不同环境和需求;
c. 通过修改参数,可以控制策略在特定条件下的表现。
配置步骤
a. 导入参数映射模型;
b. 设置外置参数。
以下是策略外置参数目前可支持5种类型:
类型 | 说明 |
---|---|
int | 整数 |
float | 浮点数 |
str | 字符串 |
bool | 布尔值 |
dict | 下拉条 |
接口案例
# a.导入参数映射模型
from pydantic import BaseModel
class Params(BaseModel,validate_assignment=True):
# b.设置外置参数
# 配置外置参数可支持类型:
INT_TEST:int=Field(default=1,title="整数")
FLOAT_TEST:float=Field(default=1.0,title="浮点数")
STR_TEST:str=Field(default="test",title ="字符串")
DICT_TEST:dict=Field(default={"options":["选项1","选项2","选项3"],"value":"选项1"},title ="下拉条")
BOOL_TEST:bool=Field(default=True,title = "布尔")
# 完成外置参数配置后的具体效果,可在期魔方添加任务和回测时的弹窗查看
描述
在策略文件(尤其是涉及编程或脚本的策略文件,如某些自动化策略、交易策略等)中,BASE
全局变量通常指的是一个基础或核心的变量,它在整个策略文件或程序中都是可访问和可修改的。BASE
变量的具体含义和用途可能因策略的不同而有所差异。
作用
全局变量在程序中具有广泛的作用,主要体现在以下几个方面:
a. 数据共享:全局变量能够在不同的函数或代码块中共享数据,实现多个函数对相同数据的访问和修改,从而加强函数之间的联系;
b. 配置信息存储:全局变量可用于存储程序的配置信息,如数据库连接信息、API密钥等,便于在整个程序中轻松访问这些信息;
c. 扩大作用域:全局变量的作用域是整个程序,可以在程序的任何地方访问,这增加了变量的可用性和灵活性。
然而,全局变量的使用也需谨慎,因其作用范围广,容易被不同函数修改,可能导致代码的可读性和可维护性下降,还可能引发命名冲突和命名空间污染等问题。
用户可以在编写策略文件中根据需求自行调用以下的BASE
全局变量:
"""执行模式"""
BASE_MODE="REAL"
"""期货交易账户id"""
BASE_USERID="1111111"
"""每个回测任务后端唯一的标识id,不在前端展示"""
BASE_ID="135DAWQE654DAQWE321D5XAS654"
"""基础产品交易所"""
BASE_EXCHANGEID="simnow"
"""基础产品"""
BASE_SYMBOLS=['c2503', 'c2505']
"""基础设置"""
BASE_SETTING={}
"""日志"""
BASE_LOG_DATA=[]
"""写日志对象"""
BASE_LOG=None
"""产品细则对象"""
BASE_SYMBOLS_INFO={}
"""获取产品tick"""
SYMBOLS_TICKS={}
"""基础产品运行tick 该对象可获取当前基础产品的最新tick对象"""
BASE_SYMBOL_TICK={}
"""多产品调用申明"""
"""格式: 合约id1:周期1&合约id2:周期2
例如: ag2512:D1&rb2510:D1&ag2512:M15
例子定义了3个子产品数据;
这里的定义不会影响BASE产品的数据获取;
"""
QMF_SUBSCRIBE_SYMBOLS = ""
接口案例
# 导入参数映射模型
from pydantic import BaseModel
class Params(BaseModel,validate_assignment=True):
# 设置外置参数,此处仅以 int 举例...
INT_TEST:int=Field(default=1,title="整数")
...
# 调用全局变量以获取 BASE_USERID 账户id
BASE_USERID="12121212"
# 打印 BASE_USERID 账户id
print(f"BASE_USERID:{BASE_USERID}")
# BASE_USERID 账户id 的打印结果:
BASE_USERID:12121212
参见 1.3.10 send_order 策略报单函数,用户可以自定义订单字段,如平今/平昨,交易所ID,报价方式等
运行多产品策略时,运算过程中会存在各标的合约运算频率不一致的情况,为避免此等情况,多产品标的合约均统一以“运行品种”为主频率进行运算,但会存在所选择“运行品种”的频率运算会比多产品策略中的部分品种低,因此在回测页面或任务页面运行多产品策略时,建议开发者在选择“运行品种”时设置交易量活跃的高频率主力品种。
而在多产品策略下,要想实现一个策略同时订阅多个标的合约 tick 时,需在策略参数中申明 QMF 全局变量。
申明方式
QMF_SUBSCRIBE_SYMBOLS = "First_symbol:First_period&second_symbol:second_period&....."
接口案例
from pydantic import BaseModel
# 外置参数
# 参数映射模型
class Params(BaseModel, validate_assignment=True):
volume:int = Field(default=1,title="手数")
QMF_SUBSCRIBE_SYMBOLS:str = Field(default='j2505:M5&jm2505:M5',title="订阅合约和周期")
...
def on_tick(context):
...
套利策略涉及多品种或周期的合约数据订阅,订阅方法参见2.5中描述,通过传入的合约列表解析出具体合约,即可进行后续开仓信号的计算,一个基于j2505和jm2505合约的跨品种套利的案例参见策略案例3.3。
对于订阅主连合约进行交易的策略,当主力合约映射发生改变后,可以通过get_current_main_symbol函数检测到这种改变,从而进行必要的处理,一个自动移仓换月的demo见策略案例3.4。需要注意在回测模式,主力连续合约作为一个独立合约存在,不存在移仓换月,不需处理,只在任务模式才需要这个处理。
在跑自动交易途中,如果突然不小心关了电脑或客户端,重启后不做处理的情况下,之前的订单和任务就丢了。希望接之前跑的任务和持仓继续跑,其中一种解决方法是,把订单保存到本地一个指定目录,当策略启动时,从本地磁盘读取历史订单信息,加载到内存中,方便用户基于历史订单信息继续执行之前的交易逻辑,参见策略案例3.5。
大交易量用户在实际使用中,为了降低冲击成本,最大程度减小滑点损失,可以使用twap或vwap方式拆分订单,下面是一个twap拆单算法的例子:
for _ in range(num_orders - context.twap_order_count_bull):
if context.twap_executed_volume_bull >= context.volume:
print("TWAP 多头交易完成,总下单量已达到目标")
del context.twap_start_time_bull
del context.twap_order_count_bull
del context.twap_executed_volume_bull
return
# 记录交易时间
next_trade_time = context.twap_start_time_bull + timedelta(minutes=context.twap_order_count_bull)
if current_time >= next_trade_time:
current_price = context.close_array[-1]
# 计算实际下单量
actual_order_size = min(order_size, context.volume - context.twap_executed_volume_bull)
print(f"TWAP 多单 {context.twap_order_count_bull+1}/{num_orders}: 价格 {current_price}, 手数 {actual_order_size}")
buy_open(actual_order_size, context.base_instrument_id, price=current_price)
# 累加已下单量
context.twap_order_count_bull += 1
context.twap_executed_volume_bull += actual_order_size
这是一个网格策略,其原理是确定网格的大小和价格区间。网格的大小是指相邻两个价格之间的距离,而价格区间则是指每个网格的买入和卖出价格。这些参数的设定往往需要一定的经验和技巧,通常根据市场的波动性和投资者的风险承受能力来确定网格大小,而价格区间则可以根据自己的预期收益率来设定。
下面是一个只做多的网格策略:
# 整体止损清仓
def clear_position(context):
for order in context.order_list:
sell_close(order['volume'])
context.quit_flag = True
# 整体止损处理
def over_all_stop_loss(context):
total_vol = 0
weighted_open_price = 0
for order in context.order_list:
total_vol += order['volume']
weighted_open_price += order['open_price'] * order['volume']
avg_open_price = weighted_open_price / total_vol
if context.last_price <= avg_open_price - context.overall_stop_loss_point:
clear_position(context)
return True
else:
return False
# 判断网格是否发生改变,并进行处理
def wait_grid_change(context):
if context.last_price >= context.min_open_price + context.plus_tick * context.price_tick:
sell_close(context.volume) # 向上突破网格,分笔止盈
elif context.last_price <= context.min_open_price - context.plus_tick * context.price_tick:
if context.total_position < context.max_volume:
buy_open(context.volume) # 向下突破网格,加仓
# 更新最小开仓价格等变量
def update_variables(context):
index = 0
total_position = 0
context.min_open_price = -1
context.min_open_price_index = -1
for order in context.order_list:
if context.min_open_price == -1:
context.min_open_price = order['open_price']
context.min_open_price_index = index
elif order['open_price'] < context.min_open_price:
context.min_open_price = order['open_price']
context.min_open_price_index = index
index += 1
total_position += order['volume']
context.total_position = total_position
# 订单成交处理回调
def on_trade_callback(context, trade):
offset = trade.get("OffsetFlag")
price = trade.get("Price")
volume = trade.get("Volume")
if offset == '0': # 开仓就增加订单列表
order = {
'open_price' : price,
'volume' : volume
}
context.order_list.append(order)
else: # 平仓单就删除订单列表
context.order_list.pop(context.min_open_price_index)
update_variables(context)
context.weit_trade = False # 首笔订单已经成交
#################################### main program ##################################
from pydantic import BaseModel, Field
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
"""
参数1:每笔开仓手数:1手
参数2:加仓距离:10个点
参数3:最大持仓手数:3手
参数4:分笔止盈:10个点
参数5:止损风控:30个点(相对于均价)
"""
volume:int = Field(default=1,title = "每笔开仓手数")
plus_tick:int = Field(default=10,title = "加仓距离(跳)")
max_volume:int = Field(default=3,title = "最大持仓手数")
take_tick:int = Field(default=10,title = "分笔止盈(跳)")
overall_stop_loss_point:int = Field(default=30,title = "整体止损(跳)")
def on_init(context):
context.order_list = [] # 保存开仓订单
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.last_price = -1 # 保存最新价格
context.price_tick = get_symbolinfo(context.base_instrument_id)['price_tick']
context.quit_flag = False
context.min_open_price = -1
context.min_open_price_index = 0
context.total_position = 0
context.weit_trade = False
def on_tick(context):
if context.quit_flag == True: # 已经清仓止损过了
return
if context.weit_trade == True: # 首笔订单已经发送了,还未成交
return
context.last_price = float(BASE_SYMBOL_TICK.get("LastPrice"))
if len(context.order_list) == 0:
buy_open(context.volume, context.base_instrument_id)
context.weit_trade = True
else:
ret = over_all_stop_loss(context)
if ret == False: # 没有整体止损,就监控网格变化
wait_grid_change(context)
def on_order(context, order):
pass
def on_trade(context, trade):
on_trade_callback(context, trade)
def on_error(context, data):
pass
def on_stop(context):
pass
这是一个日内清仓策略,其核心是在预设的时间段内对所有持仓进行平仓操作,以避免持仓过夜可能带来的风险或根据特定的交易策略需求结束当日的交易活动。
详细代码如下:
from pydantic import BaseModel, Field
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
day_clear_mode:bool = Field(default=False,title="日内清盘")
day_clear_time:str = Field(default="14:58",title="日内清盘开始时间")
day_over_time:str = Field(default="22:00",title="日内清盘结束时间")
def on_init(context):
print("demo start")
def on_tick(context):
"""获取账户信息"""
day_over_close(context)
def isSHFEoINE(context,exchangeid):
return exchangeid in "SHFE INE"
def close_order(context,symbol,exchangid,direction,volume,comboffsetflag):
try:
tick = get_tick(symbol)
if not tick.get("ActionDay"):
# tick获取失败 返回等待下一次运行
return
upper_price = float(tick.get("UpperLimitPrice"))
lower_price = float(tick.get("LowerLimitPrice"))
price =upper_price if direction=="0" else lower_price
order = {
"symbol":symbol,
"exchangeid":exchangid,
"limitprice":price,
"direction":direction,
"orderpricetype":"2",
"comboffsetflag":comboffsetflag,
"volumn":volume
}
send_order(order)
except Exception as e:
print(f"开单出现错误=>请查看GridTrade.close_order[{e}]")
print(e)
return 0
return 1
def clear_position(context):
position = get_position()
CLOSE_DIRECTION_MAP = {"2":"1","3":"0"}
for item in position:
PosiDirection = item.get("PosiDirection")
direction = CLOSE_DIRECTION_MAP[PosiDirection]
YdStrikeFrozen = item.get("YdStrikeFrozen")
LongFrozen = item.get("LongFrozen")
ShortFrozen = item.get("ShortFrozen")
Frozen = LongFrozen if direction == "0" else ShortFrozen
todayFrozen = Frozen - YdStrikeFrozen
today_volume = item.get("TodayPosition") - todayFrozen
yestoday_volume = item.get("YdPosition") - YdStrikeFrozen
exchangeid = item.get("ExchangeID")
InstrumentID = item.get("InstrumentID")
today_position = 0
if isSHFEoINE(context,exchangeid):
today_position = today_volume
yestoday_position = yestoday_volume
else:
yestoday_position = today_volume + yestoday_volume
"""暂定一组订单同时报两次"""
"""不做回复确认 => 重获取持仓时再做重新分配 直到分配结束"""
if today_position > 0:
close_order(context,InstrumentID,exchangeid,direction,today_position,"3")
if yestoday_position > 0:
close_order(context,InstrumentID,exchangeid,direction,yestoday_position,"1")
def day_over_close(context):
"""到达时间执行平仓"""
from datetime import datetime, time
if not context.day_clear_mode: return
now = datetime.now().time()
# 定义时间段
start_time = datetime.strptime(context.day_clear_time, "%H:%M").time() # 开始时间为14:58
end_time = datetime.strptime(context.day_over_time, "%H:%M").time() # 结束时间为15:00
# 判断当前时间是否在指定的时间段内
if start_time <= now < end_time:
clear_position(context)
def on_stop(context):
print("demo停止")
基于j2505和jm2505的跨品种套利,监控两种合约比值的变化,动态计算其均值和标准差,当比值偏离均值超过一倍标准差时,就进行反向开仓,预测比值回归到一倍标准差之内。
详细代码如下:
from pydantic import BaseModel, Field
import pandas as pd
import numpy as np
# 判断当前是否持有指定方向的仓位
def isHolding(positions, instrument_id, direction):
try:
if instrument_id in positions:
# 把成交单传到全局变量 position里面
position = positions.get(instrument_id)
position_type = position.get("Direction")
if str(position_type) == str(direction):
return True
except Exception as e:
put_log(f"[isHolding] => error ==> {e}", 'ERROR')
return False
# 获取K线数据
def get_k_line_data(context):
context.klines = get_kline(context.base_instrument_id, context.base_period, context.calc_len)
context.close_array = context.klines.get("close")
context.klines2 = get_kline(context.instrument_id2, context.base_period, context.calc_len)
context.close_array2 = context.klines2.get("close")
# 计算比例数据
def calc_indicator(context):
import talib as ta
if (not context.close_array) or (not context.close_array2):
return -1
if len(context.close_array) < context.calc_len or len(context.close_array2) < context.calc_len:
return
ratio_array = np.asarray(context.close_array) / np.asarray(context.close_array2)
series = pd.Series(ratio_array)
rolling_mean = series.rolling(window=context.calc_len).mean().values
rolling_std = series.rolling(window=context.calc_len).std().values
context.ratio_array = np.roll(context.ratio_array, -1)
context.mean_array = np.roll(context.mean_array, -1)
context.std_array = np.roll(context.std_array, -1)
context.ratio_array[-1] = ratio_array[-1]
context.mean_array[-1] = rolling_mean[-1]
context.std_array[-1] = rolling_std[-1]
# 根据偏离度产生开仓信号
def get_open_signal(context):
if len(context.ratio_array) < 3 or len(context.mean_array) < 3 or len(context.std_array) < 3:
return 0 # 数值不足
sig_open = 0
if context.ratio_array[-3] <= context.mean_array[-3] + context.deviation * context.std_array[-3] \
and context.ratio_array[-2] > context.mean_array[-2] + context.deviation * context.std_array[-2]:
sig_open = -1
elif context.ratio_array[-3] >= context.mean_array[-3] - context.deviation * context.std_array[-3] \
and context.ratio_array[-2] < context.mean_array[-2] - context.deviation * context.std_array[-2]:
sig_open = 1
upper = context.mean_array[-2] + context.deviation * context.std_array[-2]
lower = context.mean_array[-2] - context.deviation * context.std_array[-2]
return sig_open
# 根据指标计算平仓信号
def get_close_signal(context):
if len(context.ratio_array) < 3 or len(context.mean_array) < 3 or len(context.std_array) < 3:
return 0 # 数值不足
sig_close = 0
if context.ratio_array[-3] >= context.mean_array[-3] + context.deviation * context.std_array[-3] \
and context.ratio_array[-2] < context.mean_array[-2] + context.deviation * context.std_array[-2]:
sig_close = 1
elif context.ratio_array[-3] <= context.mean_array[-3] - context.deviation * context.std_array[-3] \
and context.ratio_array[-2] > context.mean_array[-2] - context.deviation * context.std_array[-2]:
sig_close = -1
return sig_close
def isHoldingPosition(context):
if isHolding(context.positions, context.base_instrument_id, '0') or \
isHolding(context.positions, context.base_instrument_id, '1') or \
isHolding(context.positions, context.instrument_id2, '0') or \
isHolding(context.positions, context.instrument_id2, '1'):
return True
else:
return False
# 开一对正向套利仓位
def open_long_position(context):
buy_open(context.volume, context.base_instrument_id)
sell_open(context.volume, context.instrument_id2)
# 开一对反向套利仓位
def open_short_position(context):
sell_open(context.volume, context.base_instrument_id)
buy_open(context.volume, context.instrument_id2)
# 根据信号开仓
def open_position_by_signal(context, sig_open):
if sig_open == 1: # 开多信号
if not isHoldingPosition(context): # 没有持仓
open_long_position(context)
elif sig_open == -1: # 开空信号
if not isHoldingPosition(context): # 没有持仓
open_short_position(context)
# 结束一对正向套利仓位
def close_long_position(context):
if isHolding(context.positions, context.base_instrument_id, '0'): # 持有多仓
sell_close(context.volume, context.base_instrument_id)
if isHolding(context.positions, context.instrument_id2, '1'): # 持有空仓
buy_close(context.volume, context.instrument_id2)
# 结束一对反向套利仓位
def close_short_position(context):
if isHolding(context.positions, context.base_instrument_id, '1'): # 持有空仓
buy_close(context.volume, context.base_instrument_id)
if isHolding(context.positions, context.instrument_id2, '0'): # 持有多仓
sell_close(context.volume, context.instrument_id2)
# 根据信号平仓
def close_position_by_signal(context, sig_close):
if sig_close == 1: # 平空信号
close_short_position(context)
elif sig_close == -1: # 平多信号
close_long_position(context)
# k线收盘运行逻辑
def on_bar(context):
get_k_line_data(context)
calc_indicator(context)
sig_open = get_open_signal(context)
sig_close = get_close_signal(context)
close_position_by_signal(context, sig_close)
open_position_by_signal(context, sig_open)
######################## main programm ##############################
class Params(BaseModel, validate_assignment=True):
calc_len:int = Field(default=100,title="统计周期")
deviation:float = Field(default=1.0,title="触发交易的偏离度(标准差倍数)")
volume:int = Field(default=1,title="手数")
QMF_SUBSCRIBE_SYMBOLS:str = Field(default='j2505:M5&jm2505:M5',title="订阅合约和周期")
# 初始化一些参数
def on_init(context):
import talib
symbols = QMF_SUBSCRIBE_SYMBOLS.split("&")
context.base_instrument_id = symbols[0].split(":")[0]
context.instrument_id2 = symbols[1].split(":")[0]
context.base_period = BASE_SETTING.get("bPeriod")
context.positions = {}
context.close_array = np.zeros(context.calc_len)
context.ratio_array = np.zeros(context.calc_len)
context.mean_array = np.zeros(context.calc_len)
context.std_array = np.zeros(context.calc_len)
context.close_array2 = np.ones(context.calc_len)
context.ratio_array2 = np.zeros(context.calc_len)
context.mean_array2 = np.zeros(context.calc_len)
context.std_array2 = np.zeros(context.calc_len)
# 每次有行情进来,便会调用 on_tick 函数刷新一次
def on_tick(context):
on_bar_run(on_bar, context)
# 返回委托单信息 注意此处的报单信息是未成交状态
def on_order(context,order):
pass
# 返回成交单信息 注意此处的报单信息是已成交状态
def on_trade(context,trade):
InstrumentID = trade.get("InstrumentID")
OffsetFlag = trade.get("OffsetFlag")
if OffsetFlag == "0":
context.positions[InstrumentID] = trade
else:
context.positions.pop(InstrumentID)
# 返回报错
def on_error(context,order):
pass
# 策略回测中途发生错误 or 回撤完毕时调用
def on_stop(context):
pass
当有基于主连合约(后缀888)进行交易并发生主力映射改变,如下案例是自动关闭原主力合约,在新主力合约开同样数量的仓位。
详细代码如下:
from pydantic import BaseModel, Field
# K线交易逻辑
def custom_trade_on_k_line(context):
pass
# 是否在指定合约上持有仓位
def is_holding_on_symbol(context,direction, symbol):
try:
if symbol in context.positions:
# 把成交单传到全局变量 position里面
position = context.positions.get(symbol)
position_type = position.get("Direction")
if str(position_type) == str(direction):
return True
except Exception as e:
put_log(f"[isHolding] => error ==> {e}", 'ERROR')
return False
# 是否需要移仓换月
def need_deal_hot_change(context):
if ('888' in context.hot_symbol) and context.auto_hot_change == True:
cur_hot = get_current_main_symbol(context.base_instrument_id)
if (context.hot_symbol != cur_hot): # 基于主连合约交易,并且发生了主力换月
return True
else:
return False
# 关闭原主力合约的仓位
def close_position_on_prev_hot(context):
if is_holding_on_symbol(context, '0', context.hot_symbol):
sell_close(context.volume, context.hot_symbol)
if is_holding_on_symbol(context, '1', context.hot_symbol):
buy_close(context.volume, context.hot_symbol)
# 在新主力合约建立仓位
def open_position_on_new_hot(context):
cur_hot = get_current_main_symbol(context.base_instrument_id)
if not is_holding_on_symbol(context, '0', cur_hot):
buy_open(context.volume, cur_hot)
if not is_holding_on_symbol(context, '1', cur_hot):
sell_open(context.volume, cur_hot)
# 如果原主力合约有仓位,就移到新主力
def move_position(context):
holding_short = is_holding_on_symbol(context, '1', context.hot_symbol)
holding_long = is_holding_on_symbol(context, '0', context.hot_symbol)
if holding_short or holding_long:
close_position_on_prev_hot(context)
open_position_on_new_hot(context)
# k线收盘运行逻辑
def on_bar(context):
if need_deal_hot_change(context):
move_position(context)
# 更新记录最新的主力映射
context.hot_symbol = get_current_main_symbol(context.base_instrument_id)
else:
custom_trade_on_k_line(context)
######################## main programm ##############################
class Params(BaseModel, validate_assignment=True):
volume:int = Field(default=1,title="手数")
auto_hot_change:bool = Field(default=False, title='是否自动移仓换月')
# 初始化一些参数
def on_init(context):
import talib
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.base_period = BASE_SETTING.get("bPeriod")
context.positions = {}
context.hot_symbol = get_current_main_symbol(context.base_instrument_id)
context.cur_symbol = get_current_main_symbol(context.base_instrument_id)
# 每次有行情进来,便会调用 on_tick 函数刷新一次
def on_tick(context):
# 注册K线处理回调函数
on_bar_run(on_bar, context)
# 返回委托单信息 注意此处的报单信息是未成交状态
def on_order(context,order):
pass
# 返回成交单信息 更新仓位
def on_trade(context,trade):
InstrumentID = trade.get("InstrumentID")
OffsetFlag = trade.get("OffsetFlag")
if OffsetFlag == "0":
context.positions[InstrumentID] = trade
else:
context.positions.pop(InstrumentID)
# 返回报错
def on_error(context,order):
pass
# 策略回测中途发生错误 or 回撤完毕时调用
def on_stop(context):
pass
把订单保存到本地一个指定目录,当策略启动时,从本地磁盘读取历史订单信息,加载到内存中,方便用户基于历史订单信息执行不同的交易逻辑。
详细代码如下:
from pydantic import BaseModel, Field
import pandas as pd
import numpy as np
# 判断当前是否持有指定方向的仓位
def isHolding(context,direction):
try:
if context.base_instrument_id in context.positions:
# 把成交单传到全局变量 position里面
position = context.positions.get(context.base_instrument_id)
position_type = position.get("Direction")
if str(position_type) == str(direction):
return True
except Exception as e:
put_log(f"[isHolding] => error ==> {e}", 'ERROR')
return False
# 获取K线数据
def get_k_line_data(context):
len1 = context.len1
len2 = context.len2
context.klines = get_kline(context.base_instrument_id, context.base_period, max(len1, len2))
context.dt_array = context.klines.get('datetime')
context.close_array = context.klines.get("close")
# 计算均线指标数据
def calc_sma(context):
import talib as ta
len1 = context.len1
len2 = context.len2
context.close_array = context.klines.get("close")
ma1_array = ta.SMA(np.asarray(context.close_array),len1)
ma2_array = ta.SMA(np.asarray(context.close_array),len2)
context.ma1_array = np.roll(context.ma1_array, -1)
context.ma2_array = np.roll(context.ma2_array, -1)
context.ma1_array[-1] = ma1_array[-1]
context.ma2_array[-1] = ma2_array[-1]
# 根据SMA指标产生开仓信号
def get_open_signal_by_ma(context):
if len(context.ma2_array) < 3 or len(context.ma1_array) < 3:
return 0 # 数值不足
sig_open = 0
if context.ma1_array[-3] <= context.ma2_array[-3] \
and context.ma1_array[-2] > context.ma2_array[-2]:
sig_open = 1
elif context.ma1_array[-3] >= context.ma2_array[-3] \
and context.ma1_array[-2] < context.ma2_array[-2]:
sig_open = -1
return sig_open
# 根据指标计算平仓信号
def get_close_signal_by_ma(context):
if len(context.ma2_array) < 3 or len(context.ma1_array) < 3:
return 0 # 数值不足
sig_close = 0
if context.ma1_array[-3] <= context.ma2_array[-3] \
and context.ma1_array[-2] > context.ma2_array[-2]:
sig_close = 1
elif context.ma1_array[-3] >= context.ma2_array[-3] \
and context.ma1_array[-2] < context.ma2_array[-2]:
sig_close = -1
return sig_close
# 根据信号开仓
def open_position_by_signal(context, sig_open):
if sig_open == 1: # 开多信号
if not isHolding(context,"0"): # 没有持多仓
ret = buy_open(context.volume, context.base_instrument_id)
elif sig_open == -1: # 开空信号
if not isHolding(context,"1"): # 没有持空仓
sell_open(context.volume, context.base_instrument_id)
# 根据信号平仓
def close_position_by_signal(context, sig_close):
if sig_close == 1: # 平空信号
if isHolding(context,"1"): # 持有空仓
buy_close(context.volume, context.base_instrument_id)
elif sig_close == -1: # 平多信号
if isHolding(context,"0"): # 持有多仓
sell_close(context.volume, context.base_instrument_id)
# k线收盘运行逻辑
def on_bar(context):
get_k_line_data(context)
calc_sma(context)
sig_open = get_open_signal_by_ma(context)
sig_close = get_close_signal_by_ma(context)
close_position_by_signal(context, sig_close)
open_position_by_signal(context, sig_open)
# 记录订单到磁盘文件
def record_order(context, order):
import json
if len(context.order_list) >= context.max_order_num:
context.order_list = context.order_list[1-context.max_order_num:]
context.order_list.append(order)
with open(context.order_dir, "w") as f:
json.dump(context.order_list, f)
# 从磁盘文件读取订单到内存
def restore_orders(context):
import json
import os
if os.path.exists(context.order_dir):
with open(context.order_dir, "r") as f:
if os.path.getsize(context.order_dir) != 0:
order_dicts = json.load(f)
context.order_list = order_dicts
# put_log(f'restored history orders : \n{context.order_list}', 'INFO')
############################### main programm ###############################
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
len1:int = Field(default=5,title="均线1周期")
len2:int = Field(default=20,title="均线2周期")
volume:int = Field(default=1,title="手数")
order_dir:str = Field(default=r'E:\order.json', title='订单保存地址') # 这里要改成本机地址
max_order_num:int = Field(default=10, title='最大保存订单个数')
# 初始化一些参数
def on_init(context):
import talib
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.base_period = BASE_SETTING.get("bPeriod")
context.positions = {}
context.close_array = np.zeros(context.len2)
context.ma1_array = np.zeros(context.len1)
context.ma2_array = np.zeros(context.len2)
context.order_list = []
restore_orders(context)
# 每次有行情进来,便会调用 on_tick 函数刷新一次
def on_tick(context):
# 注册K线处理回调
on_bar_run(on_bar, context)
# 返回委托单信息 注意此处的报单信息是未成交状态
def on_order(context,order):
pass
# 返回成交单信息 记录仓位变化
def on_trade(context,trade):
InstrumentID = trade.get("InstrumentID")
OffsetFlag = trade.get("OffsetFlag")
if OffsetFlag == "0":
context.positions[InstrumentID] = trade
else:
context.positions.pop(InstrumentID)
record_order(context, trade)
# 返回报错
def on_error(context,order):
pass
# 策略回测中途发生错误 or 回撤完毕时调用
def on_stop(context):
pass
平台默认支持akshare库调用,如果需要引用akshare库所支持数据,比如A股或美股数据做参考的用户,可以调用其接口来获取数据。
具体对应数据参考akshare官网:https://akfamily.xyz/
获取个股历史行情:
import akshare as ak
stock_zh_a_hist_df = ak.stock_zh_a_hist(symbol="000001",period="daily",start_date='2025-03-15',end_date='2025-03-20',adjust="")
put_log(f'\n{stock_zh_a_hist_df}', 'INFO')
'''
股票000001历史行情:
日期 股票代码 开盘 收盘 最高 最低 成交量 成交额 振幅 涨跌幅 涨跌额 换手率
0 2025-03-17 000001 11.63 11.50 11.67 11.46 4603612 5.315523e+09 1.75 -3.93 -0.47 2.37
1 2025-03-18 000001 11.52 11.49 11.54 11.48 1605290 1.846500e+09 0.52 -0.09 -0.01 0.83
2 2025-03-19 000001 11.48 11.52 11.53 11.46 1362455 1.566452e+09 0.61 0.26 0.03 0.70
3 2025-03-20 000001 11.51 11.49 11.61 11.49 1101157 1.268849e+09 1.04 -0.26 -0.03 0.57
获取科创板实时行情:
stock_kc_a_spot_em_df = ak.stock_kc_a_spot_em()
put_log(f'科创板实时行情:\n{stock_kc_a_spot_em_df}', 'INFO')
'''
科创板实时行情:
序号 代码 名称 最新价 涨跌幅 涨跌额 成交量 成交额 振幅 最高 最低 今开 昨收 量比 换手率 市盈率-动态 市净率 总市值 流通市值 涨速 5分钟涨跌 60日涨跌幅 年初至 今涨跌幅
0 1 688793 倍轻松 35.90 6.65 2.24 68981.0 2.509844e+08 13.93 38.57 33.88 33.88 33.66 1.89 8.03 327.04 8.29 3.085441e+09 3.085441e+09 -0.39 -0.58 21.20 20.47
1 2 688118 普元信息 26.60 5.14 1.30 76647.0 2.010637e+08 10.28 27.20 24.60 24.99 25.30 2.74 8.35 382.49 2.85 2.442349e+09 2.442349e+09 0.26 -0.11 23.38 26.49
2 3 688776 国光电气 72.80 5.13 3.55 41949.0 3.035973e+08 8.58 74.18 68.24 68.29 69.25 1.47 3.87 163.91 4.21 7.890313e+09 7.890313e+09 0.17 0.12 33.07 54.40
.. ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
获取美股历史行情
stock_us = ak.stock_us_hist(symbol='106.TTE', period="daily", start_date="20250301", end_date="20250310", adjust="qfq")
put_log(f'{stock_us}', 'INFO')
'''
日期 开盘 收盘 最高 最低 成交量 成交额 振幅 涨跌幅 涨跌额 换手率
0 2025-03-03 61.57 59.83 61.670 59.360 1607167 96877978.0 3.84 -0.66 -0.40 0.07
1 2025-03-04 59.22 59.87 60.550 58.966 2320214 138720830.0 2.65 0.07 0.04 0.10
2 2025-03-05 60.97 60.94 61.230 60.100 2092218 127068015.0 1.89 1.79 1.07 0.09
3 2025-03-06 60.64 60.78 61.508 60.540 1556678 94709372.0 1.59 -0.26 -0.16 0.07
4 2025-03-07 61.39 61.39 62.050 60.570 2835416 174262938.0 2.44 1.00 0.61 0.13
5 2025-03-10 61.67 61.07 61.950 60.585 1961626 120370695.0 2.22 -0.52 -0.32 0.09
获取美股实时行情
stock_us_spot_em_df = ak.stock_us_spot_em()
put_log(f'{stock_us_spot_em_df}', 'INFO')
'''
序号 名称 最新价 涨跌额 涨跌幅 开盘价 最高价 最低价 昨收价 总市值 市盈率 成交量 成交额 振幅 换手率 代码
0 1 Impact BioMedical Inc 2.08 1.550 292.45 0.530 2.480 0.530 0.530 23928226.0 20.54 30679574.0 54278090.0 367.92 266.69 107.IBO
1 2 Innovative Eyewear Inc Wt-A 0.14 0.098 229.41 0.042 0.191 0.042 0.043 NaN NaN 41062.0 4693.0 350.12 NaN 105.LUCYW
2 3 Plus Therapeutics Inc 1.38 0.869 170.06 0.570 2.080 0.550 0.511 8136940.0 -0.63 357747872.0 454892368.0 299.37 6067.29 105.PSTV
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
可能的原因:
a) 编辑代码后没有保存
b) 不符合python语法,例:将def on_stop写成了deff on-stop
c) 期魔方平台函数接口使用错误,例:get_kline("ru2501","M1"),少了一个参数
解决方法:
借助终端的output选择“python编译输出”,在输出结果查看原因,分析处理
修改策略后,在策略回测页面点击编辑参数,点击保存,即可重新回测
可能的原因:
a) 策略文件的命名格式问题,比如在同一文件夹路径下按前后顺序添加了两个命名都为MACD策略文件导致后面添加的策略文件会把前面已添加的文件覆盖掉
b) 系统本地保存的策略目录缓存问题,比如当把本地缓存的路径C:\期魔方\strategy下策略目录文件夹清空后,重启软件会把原先在软件上导入后的策略文件给清空而无法找回
如果在回测进度100%后,点开详情没有数据,可能的原因:
a) 错误地调用了功能函数以获取目标合约信息,比如没有正确使用功能函数get_symbolinfo,自然无法获取到数据去进行计算或查询工作
b) 策略文件只有策略原生的结构性框架,比如一个策略文件的代码中仅仅保留使用策略原生的结构性函数框架,此时保存策略文件代码后右键点击 Python编译依旧能够显示编译通过的提示,而后在回测过程中使用该策略文件依旧可以支持回测,但回测进度完成至100%后,会存在未统计出相应的交易数据的情况,而导致点击详情时无法正常打开详情页并强制清除该回测进度
c) 使用的策略不支持回测,比如”风控模型“策略的逻辑目前暂时只能在任务模块中运行,因此在回测模块中应用该策略会导致回测过程中无法产生交易数据
如果回测时候策略的进度太慢,可能是因为:
a) 策略文件中涉及使用多种复杂的计算或大量的数据查询的模块
b) 运行周期选择很小,比如当策略在回测过程中选择“1分钟”的运行时间周期上时,回测效率会很慢。当选择分钟运行周期时,信号点增加了,因此产生的交易数据量暴增便会导致策略执行效率慢的问题,建议选择“1天”为运行周期
可能的原因:
a) 错误使用功能方法函数,比如在使用方法函数send_order时缺乏必需的order_info参数,在传递过程中这张单子可能就无法正确报单成功
b) 系统在处理报单任务时可能存在无法即时成交而出现单子堆积的问题,系统增加了报单超过3秒不成交会自动撤单的机制,比如当你的系统的时间为“11:00:50”本地时间为“11:00:55”时,误差超过了3秒,单子就被自动撤单。此时校准本地时间即可
可能原因:对于收盘后收到的溢出数据,没有判断时间,仍执行发单逻辑,导致失败
解决办法:判断当前时间,如果不在交易时间内,则忽略处理,不执行发单逻辑
可能原因:基于K线触发的开仓逻辑,写在了on_tick函数中,没有判断是否生成新K线,导致开仓逻辑多次执行
解决办法:K线的处理逻辑调用on_bar_run处理,就能保证只在新K线产生时才执行开仓逻辑运算
可能原因:引用K线数据时,访问了当前K线的数据。例如:
if context.ma1_array[-2] <= context.ma2_array[-2] \
and context.ma1_array[-1] > context.ma2_array[-1]:
sig_open = 1
解决办法:当前最新的K线是实时变动的,需要改为访问已经收线的K线数据
if context.ma1_array[-3] <= context.ma2_array[-3] \
and context.ma1_array[-2] > context.ma2_array[-2]:
sig_open = 1
a) 平仓失败的可能原因是没有持仓
b) 重复开仓的可能原因是策略出现了非预期情况,已经持有仓位
解决办法:开仓前判断是否已持有仓位,平仓前判断是否并没持有仓位
可能原因:
a) 使用了期魔方尚未支持的库
b) 没有在函数内部导入库文件
正确的使用方法如下:
# 计算均线指标数据
def calc_sma(context):
import talib as ta
len1 = context.len1
len2 = context.len2
context.close_array = context.klines.get("close")
ma1_array = ta.SMA(np.asarray(context.close_array),len1)
ma2_array = ta.SMA(np.asarray(context.close_array),len2)
目前在期魔方平台编写策略支持的库文件及版本如下:
aiohttp 3.9.5
aiosignal 1.3.1
akshare 1.16.9
altgraph 0.17.4
annotated-types 0.6.0
anyio 4.3.0
APScheduler 3.10.4
arch 7.2.0
astropy 7.0.1
astropy-iers-data 0.2025.2.17.0.34.13
asttokens 2.4.1
attrs 23.2.0
beautifulsoup4 4.12.3
certifi 2024.7.4
cffi 1.17.1
charset-normalizer 3.3.2
clarabel 0.10.0
click 8.1.7
colorama 0.4.6
contourpy 1.3.0
cryptography 43.0.1
cvxpy 1.6.1
cycler 0.12.1
DBUtils 3.1.0
decorator 5.1.1
dnspython 2.6.1
email_validator 2.1.1
et_xmlfile 2.0.0
executing 2.1.0
fastapi 0.110.1
fonttools 4.54.1
frozendict 2.4.6
frozenlist 1.4.1
h11 0.14.0
html5lib 1.1
idna 3.7
ipython 8.29.0
jedi 0.19.1
joblib 1.4.2
jsonpath 0.82.2
kiwisolver 1.4.7
loguru 0.7.2
lxml 5.3.0
matplotlib 3.9.2
matplotlib-inline 0.1.7
mini-racer 0.12.4
multidict 6.0.5
multitasking 0.0.11
mysql-connector-python 8.3.0
networkx 3.4.2
ntplib 0.4.0
numpy 1.26.4
objprint 0.2.3
openpyxl 3.1.5
osqp 0.6.7.post3
packaging 24.1
paho-mqtt 1.6.1
pandas 2.2.2
parso 0.8.4
patsy 1.0.1
peewee 3.17.7
pefile 2023.2.7
pillow 11.0.0
pip 25.0.1
platformdirs 4.3.6
prompt_toolkit 3.0.48
pure_eval 0.2.3
py-mini-racer 0.6.0
pybind11 2.13.6
pycparser 2.22
pydantic 2.6.4
pydantic_core 2.16.3
pyerfa 2.0.1.5
Pygments 2.18.0
pyinstaller 6.10.0
pyinstaller-hooks-contrib 2024.9
PyMySQL 1.1.0
pyparsing 3.2.0
python-dateutil 2.9.0.post0
pytz 2024.1
pywin32-ctypes 0.2.2
PyYAML 6.0.2
qdldl 0.1.7.post5
QuantStats 0.0.64
requests 2.31.0
Riskfolio-Lib 7.0.0
scikit-learn 1.6.1
scipy 1.14.1
scs 3.2.7.post2
seaborn 0.13.2
setuptools 72.1.0
six 1.16.0
sniffio 1.3.1
soupsieve 2.6
stack-data 0.6.3
starlette 0.37.2
statsmodels 0.14.4
TA-Lib 0.4.28
tabulate 0.9.0
threadpoolctl 3.5.0
tqdm 4.67.1
traitlets 5.14.3
typing_extensions 4.12.2
tzdata 2024.1
uvicorn 0.29.0
wcwidth 0.2.13
webencodings 0.5.1
websockets 12.0
win32-setctime 1.1.0
xlrd 2.0.1
XlsxWriter 3.2.2
yarl 1.9.4
yfinance 0.2.48