期魔方量化投研平台是由四川赤壁量化科技有限公司自主研发,专为国内期货市场打造的全能量化交易与研究平台。它集成了市场行情分析、量化策略回测、数据分析、风险管理以及机器学习等多项强大功能,具备高度可扩展性,能够满足不同用户的多样化需求。
核心功能:
本案例实现的原理: 当5日均线上穿10日均线时,打印信息显示:{金叉 平空买多}; 当5日均线上穿10日均线时,打印信息显示:{死叉 平多卖空}。
详细代码如下:
def on_init(context):
print("[on_init] => 初始化...")
# 获取基础产品的产品code及周期并保存在变量中,方便后续访问使用
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.period = BASE_SETTING.get("bPeriod")
# 设定一个全局的变量用于区分是否为新K线的计算使用
context.nBars_time = ""
print("[on_init] => 初始化完成.")
# 当生成了新的K线时,就返回真"""
def is_changing(context,time_array):
if context.nBars_time != time_array[-1]:
context.nBars_time = time_array[-1]
return True
return False
# 每当新的行情进来就调用 on_tick() 函数刷新一次,以判断5日均线和10日均线金死叉情况
def on_tick(context):
kline_data = get_kline(context.base_instrument_id,context.period,20)
if is_changing(context,kline_data.get("datetime")):
close_array = kline_data.get("close")
if len(close_array)>=3:
fast_ma = sum(close_array[-5-1:-1])
slow_ma = sum(close_array[-10-1:-1])
if fast_ma > slow_ma:
print("金叉 平空买多")
elif fast_ma < slow_ma:
print("死叉 平多买空")
def on_stop(context):
print("demo停止")
以下是创建文件夹的步骤:
策略 -> 策略列表文件 -> 新建按钮 -> 新增策略弹窗 -> 填写好策略名称,选择语言为 “Python(已固定)”->点击确定按钮。
以下是创建策略文件步骤的文字说明:
a. 策略 -> 策略列表文件 -> 新建按钮 -> 新增策略弹窗 -> 填写好策略名称,选择语言为 “Python(已固定)” -> 点击确定按钮;
b. 如果开发者已下载好编辑器,则可以新增策略成功后直接点击编辑进行代码编写,若是第一次下载编辑器,添加策略成功后会提示下载系统自带的编写工具,用户此时需点击下载新包安装以安装编写工具;
c. 等待编辑器解压进度条100%后,会自动跳转到 vscode 编辑器页;
d. 完成策略代码编写后按 Ctrl + S 键保存,在界面空白处单击鼠标右键,选择python编译,编辑器在终端提示输出[编译成功!]后即可。
描述 策略框架是策略文件中的一个结构化组成部分,它定义了策略的整体结构和关键要素。这包括策略的目标、执行逻辑、条件判断、决策流程以及可能涉及的外部参数和内部变量等。一个完整策略的整体框架,包含有以下6个结构性函数(必填,否则可能会出现编译失败的情况);
而在结构性函数中用户可以根据自己的需求调用功能性函数(选填,用户选择性调用):
# 外置参数
# 参数映射模型
from pydantic import BaseModel
class Params(BaseModel,validate_assignment=True):
pass
# 6个结构性函数: on_tick,on_init,on_stop,on_order,on_error,on_trade
# 功能性函数,如:get_kline(),get_account()...
# 行情报价每进来一次就会 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. 缩进与空格
Python 使用缩进来表示代码块,通常使用4个空格进行缩进,而不是制表符(Tab)。避免混合使用空格和制表符进行缩进,以保持代码格式的一致性和可读性。
b. 命名规范
(1)变量、函数和模块名应使用小写字母和下划线分隔(snake_case)。避免使用 Python 关键字作为变量名,以防止潜在的命名冲突;
(2)常用的命名方法包括驼峰命名法(CamelCase),其中类名通常使用大驼峰命名法(UpperCamelCase),而函数和变量则常用小驼峰命名法(lowerCamelCase)。
描述
外置参数是策略文件中自定义的变量,用于配置和控制策略的行为。
作用
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 = "布尔")
# 完成外置参数配置后的具体效果,可在期魔方添加任务和回测时的弹窗查看,参考:6.策略使用
描述
在策略文件(尤其是涉及编程或脚本的策略文件,如某些自动化策略、交易策略等)中,BASE
全局变量通常指的是一个基础或核心的变量,它在整个策略文件或程序中都是可访问和可修改的。BASE
变量的具体含义和用途可能因策略的不同而有所差异。
作用
全局变量在程序中具有广泛的作用,主要体现在以下几个方面:
a. 数据共享:全局变量能够在不同的函数或代码块中共享数据,实现多个函数对相同数据的访问和修改,从而加强函数之间的联系;
b. 配置信息存储:全局变量可用于存储程序的配置信息,如数据库连接信息、API密钥等,便于在整个程序中轻松访问这些信息;
c. 扩大作用域:全局变量的作用域是整个程序,可以在程序的任何地方访问,这增加了变量的可用性和灵活性。
然而,全局变量的使用也需谨慎,因其作用范围广,容易被不同函数修改,可能导致代码的可读性和可维护性下降,还可能引发命名冲突和命名空间污染等问题。
用户可以在编写策略文件中根据需求自行调用以下的BASE
全局变量:
"""执行模式,目前系统默认只有 REAL 这一种执行模式"""
BASE_MODE="REAL"
"""期货交易账户id"""
BASE_USERID="1111111"
"""每个回测任务后端唯一的标识id,不在前端展示"""
BASE_ID="135DAWQE654DAQWE321D5XAS654"
"""基础产品交易所"""
BASE_EXCHANGEID="simnow"
"""基础产品"""
BASE_SYMBOLS=[]
"""基础设置"""
BASE_SETTING={}
"""日志"""
BASE_LOG_DATA=[]
"""写日志对象"""
BASE_LOG=None
"""产品细则对象"""
BASE_SYMBOLS_INFO={}
"""获取产品tick"""
SYMBOLS_TICKS={}
"""基础产品运行tick 该对象可获取当前基础产品的最新tick对象"""
BASE_SYMBOL_TICK={}
接口案例
# 导入参数映射模型
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
描述
策略文件中的功能性函数是指为实现特定策略目标而设计的一系列操作或方法。这些函数在策略文件中起着关键作用,帮助定义、执行和管理策略。以下是对10个功能性函数及其说明和使用方法的,开发者可以根据自己的需求选择性调用:
说明
获取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 |
参数 | 中文描述 |
---|---|
list | 列表,包含字段详见以下 LIST 对象 |
LIST 对象
参数 | 中文描述 |
---|---|
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]
}
说明
获取当下已登录期货公司账户信息。
输入参数
无
输出参数
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,
# 以下展示内容已省略
...
}
说明
从CTP获取指定品种代码或合约代码产品细则数据。
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
symbol | 期货合约标识,如 ru2501(橡胶2501),详见以下 SYMBOLINFO_DATA 对象 | str |
输出参数
SYMBOLINFO_DATA 产品对象明细字段信息
参数 | 中文描述 |
---|---|
InstrumentID | 合约代码 |
ExchangeID | 交易所代码 |
InstrumentName | 合约名称 |
ExchangeInstID | 合约在交易所的代码 |
ProductID | 产品代码 |
ProductClass | 产品类型 |
DeliveryYear | 交割年份 |
DeliveryMonth | 交割月 |
MaxMarketOrderVolume | 市价单最大下单量 |
MinMarketOrderVolume | 市价单最小下单量 |
MaxLimitOrderVolume | 限价单最大下单量 |
MinLimitOrderVolume | 限价单最小下单量 |
VolumeMultiple | 合约数量乘数 |
PriceTick | 最小变动价位 |
CreateDate | 创建日 |
OpenDate | 上市日 |
ExpireDate | 到期日 |
StartDelivDate | 开始交割日 |
EndDelivDate | 结束交割日 |
InstLifePhase | 合约生命周期状态 |
IsTrading | 当前是否交易 |
PositionType | 持仓类型 |
PositionDateType | 持仓日期类型 |
LongMarginRatio | 多头保证金率 |
ShortMarginRatio | 空头保证金率 |
MaxMarginSideAlgorithm | 是否使用大额单边保证金算法 |
UnderlyingInstrID | 基础商品代码 |
StrikePrice | 执行价 |
OptionsType | 期权类型 |
UnderlyingMultiple | 合约基础商品乘数 |
CombinationType | 组合类型 |
接口案例
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,
# 以下内容已省略
...
}
说明
用于获取用户目前期货交易账户中所有持仓明细详情的函数。
输入参数
无
输出参数
POSITION_DATA 持仓对象明细字段信息
参数 | 中文描述 |
---|---|
InstrumentID | 合约代码 |
BrokerID | 经纪公司代码 |
InvestorID | 投资者代码 |
PosiDirection | 持仓多空方向 |
HedgeFlag | 投机套保标志 |
PositionDate | 持仓多空方向 |
YdPosition | 上日持仓 |
Position | 今日持仓 |
LongFrozen | 多头冻结 |
ShortFrozen | 空头冻结 |
LongFrozenAmount | 开仓冻结金额 |
OpenVolume | 开仓量 |
CloseVolume | 平仓量 |
OpenAmount | 开仓金额 |
PositionCost | 持仓成本 |
PreMargin | 上次占用的保证金 |
UseMargin | 占用的保证金 |
FrozenMargin | 冻结的保证金 |
FrozenCash | 冻结的资金 |
FrozenCommission | 冻结的手续费 |
CashIn | 资金差额 |
Commission | 手续费 |
CloseProfit | 平仓盈亏 |
PositionProfit | 持仓盈亏 |
PreSettlementPrice | 上次结算价 |
SettlementPrice | 本次结算价 |
TradingDay | 交易日 |
SettlementID | 结算编号 |
OpenCost | 开仓成本 |
ExchangeMargin | 交易所保证金 |
CombPosition | 组合成交形成的持仓 |
CombLongFrozen | 组合多头冻结 |
CombShortFrozen | 组合空头冻结 |
CloseProfitByDate | 逐日盯市平仓盈亏 |
CloseProfitByTrade | 逐笔对冲平仓盈亏 |
TodayPosition | 今日持仓 |
MarginRateByMoney | 保证金率 |
StrikeFrozen | 执行冻结 |
StrikeFrozenAmount | 执行冻结金额 |
AbandonFrozen | 放弃执行冻结 |
ExchangeID | 交易所代码 |
YdStrikeFrozen | 执行冻结的昨仓 |
InvestUnitID | 投资单元代码 |
PositionCostOffset | 大商所持仓成本差值,只有大商所使用 |
TasPosition | tas持仓手数 |
TasPositionCost | tas持仓成本 |
Symbolinfo | 持仓细则对象 |
接口案例
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 | 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
# 以下内容已省略
...
}
说明
做多、做空、平仓等动作都需要通过该方法函数进行报单。
输入参数
参数 | 中文描述 |
---|---|
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'}
说明
调用该函数会暂停当前的策略运行。
输入参数
无
输出参数
无
接口案例
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
描述
策略文件中的结构性函数是指那些为策略文档提供框架、结构和组织的关键要素。这些函数不仅帮助开发者系统地构建策略,还确保策略的全面性、一致性和可读性。以下是7个结构性函数及其说明和使用方法,开发者必须在此框架下编写策略文件:
说明
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 | 自动挂起标志 |
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 | 开平标志 |
Price | 价格 |
PriceSource | 价格来源 |
TradeDate | 交易日期 |
TradeID | 成交编号 |
TradeSource | 成交来源 |
TradeTime | 交易时间 |
TradeType | 成交类型 |
TradingRole | 交易角色 |
Volume | 数量 |
输出参数
无
接口案例
def on_order(context, order):
pass
数据示例
无
说明
接收 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 | 买卖方向 |
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 | 开平标志 |
Price | 价格 |
PriceSource | 价格来源 |
TradeDate | 交易日期 |
TradeID | 成交编号 |
TradeSource | 成交来源 |
TradeTime | 交易时间 |
TradeType | 成交类型 |
TradingRole | 交易角色 |
Volume | 数量 |
输出参数
无
接口案例
def on_trade(context, order):
pass
数据示例
无
在策略页面创建好策略文件后会自动进入编辑器,此时开发者应在策略框架下完成代码编写,保存并进行Python编译;
然后在回测 -> 添加回测页面 -> 测试模型中选择策略模型,填好基础参数;
若策略中带有策略外置参数的,还需补充策略参数。
添加成功后,点击[回测]按钮,随后会在回测页面下方打印日志内容,开发者可在此处返回的日志内容检查代码的正确性并进行调试。
调试好策略并等待回测完成100%后,点击[详情];
查看该策略的回测策略数据。
在任务页面双击选中策略进入添加任务窗口,设置默认参数;
若策略中带有策略外置参数,还需补充策略外置参数,点击提交并启动任务。
启动任务后展示实时 k 线图,并在任务页面右下角打印日志内容,开发者可在此处返回的日志内容检查代码的正确性并进行调试。
运行多产品策略时,运算过程中会存在各标的合约运算频率不一致的情况,为避免此等情况,多产品标的合约均统一以“运行品种”为主频率进行运算,但会存在所选择“运行品种”的频率运算会比多产品策略中的部分品种低,因此在回测页面或任务页面运行多产品策略时,建议开发者在选择“运行品种”时设置交易量活跃的高频率主力品种。
而在多产品策略下,要想实现一个策略同时订阅多个标的合约 tick 时,需在策略中申明 QMF 全局变量。
申明方式
QMF_SUBSCRIBE_SYMBOLS = "First_symbol: First_period; second_symbol: second_symbol;....."
接口案例
# 外置参数
# 参数映射模型
from pydantic import BaseModel
class Params(BaseModel,validate_assignment=True):
pass
# 多产品需申明 QMF 全局变量
QMF_SUBSCRIBE_SYMBOLS = "rb2412:M15;ru2409:M15;TA409:M15"
def on_tick(context):
...
这是一个双均线策略,其原理是根据定义的“快”“慢”两条均线形成金叉、死叉的交易信号点,针对性地进行方向性开平仓
快线上穿慢线,金叉:开多,平空;
快线下穿慢线,死叉:开空,平多。
详细代码如下:
# 外部参数
from pydantic import BaseModel, Field
import time
class Params(BaseModel, validate_assignment=True):
"""参数映射模型"""
set_symbol_mode:dict = Field(default={"options":["全局","指定"],\
"value":"全局"},title="跟踪对象")
fast:int = Field(default=5,title="快线")
slow:int = Field(default=10,title="慢线")
volume:int = Field(default=1,title="手数")
exchangeid:str = Field(default="SHFE",title="交易所")
# 设置全局变量方便调用
position = {}
# 初始化一些参数
def on_init(context):
print("[on_init] => 初始化...")
context.base_instrument_id = BASE_SETTING.get("Instrument")
context.base_period = BASE_SETTING.get("bPeriod")
context.positions = {}
context.open_task = []
context.close_task = []
print("[on_init] => 初始化完成.")
context.ticks = 0
# 每次有行情进来,便会调用 on_tick 函数刷新一次
def on_tick(context):
context.ticks += 1
if context.ticks > 5:
print(f"{context.ticks = } {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}已获得5个tick")
context.ticks = 0
# 执行策略核心逻辑
custom_trade(context)
# 策略核心交易逻辑
def custom_trade(context):
sig = ma_signal(context)
# 当信号为1时,平空且开多
if sig == 1:
"""平空 开多"""
# 获取涨停价保证以当前市价成交,实现以市价成交的效果
price = BASE_SYMBOL_TICK.get("UpperLimitPrice")
# 订单信息
order = {
"symbol":context.base_instrument_id,
"exchangeid":context.exchangeid,
"limitprice":price
}
# 判断手中持仓是否有空仓 空单价格标识为 1
if isHolding(context,"1"):
# 如果关闭任务列表为空
if len(context.close_task)==0:
# 判断是今仓则平今空
if isToday(context):
closeTodaySell(context,order)
# 判断不是今仓,则平昨
else:
closeYestodaySell(context,order)
# 关闭任务加入等待序列,表示有一个关闭任务正在进行
context.close_task.append("wait")
# 如果打开任务列表为空 并且 不持有多仓 则执行开多仓操作
if len(context.open_task)==0 and \
not isHolding(context,"0"):
openBuy(context,order)
# 打开任务加入等待序列,表示有一个打开任务正在进行
context.open_task.append("wait")
# 当信号为-1,平多且开空
elif sig == -1:
"""平多 开空"""
# 获取跌停价保证以当前市价成交,实现以市价成交的效果
price = BASE_SYMBOL_TICK.get("LowerLimitPrice")
# 订单信息
order = {
"symbol":context.base_instrument_id,
"exchangeid":context.exchangeid,
"limitprice":price
}
# 判断手中持仓是否有多仓 多单价格标识为 0
if isHolding(context,"0"):
# 如果关闭任务列表为空
if len(context.close_task)==0:
# 判断是今仓则平今多
if isToday(context):
closeTodayBuy(context,order)
# 判断是昨仓则平昨多
else:
closeYestodayBuy(context,order)
# 关闭任务加入等待序列,表示有一个关闭任务正在进行
context.close_task.append("wait")
# 如果开启任务列表为空 并且 不持有多仓 则执行开空仓操作
if len(context.open_task)==0 and \
not isHolding(context,"1"):
openSell(context,order)
# 开启任务加入等待序列,表示有一个打开任务正在进行
context.open_task.append("wait")
# 判断当前是否持有指定方向的仓位
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 postion_type == direction:
return True
except Exception as e:
print(f"[isHolding] => error ==> {e}")
return False
# 判断持仓是否属于今天的交易
def isToday(context):
try:
position = context.positions.get(context.base_instrument_id)
position_date = position.get("TradingDay")
current_tick_date = BASE_SYMBOL_TICK.get("TradingDay")
exchangeid = position.get("ExchangeID")
# 比较持仓的交易日期和当前行情的交易日期是否一致 是上海期货交易所(SHFE)还是大连商品交易所(INE)
if current_tick_date == position_date \
and isSHFEoINE(context,exchangeid):
# 两者为 是 则为今仓;#否 则为昨仓
return True
except Exception as e:
print(f"[isToday] => error ==> {e}")
return False
# 判断持仓所在的交易所是上海期货交易所(SHFE)或大连商品交易所(INE)
def isSHFEoINE(context,exchangeid):
return exchangeid in "SHFE INE"
# 更新订单信息数据
def cook_order_data(context,order:dict,direction,comboffsetflag):
order.update({
"direction":direction,
"orderpricetype":"2",
"comboffsetflag":comboffsetflag,
"volumn":context.volume
})
return order
# 开多仓 提交最新订单信息数据
def openBuy(context,order:dict):
print(f"开启多单=>{order}")
send_order(cook_order_data(context,order,"0","0"))
# 开空仓 提交最新订单信息数据
def openSell(context,order:dict):
print(f"开启空单=>{order}")
send_order(cook_order_data(context,order,"1","0"))
# 平昨空 提交最新订单信息数据
def closeYestodaySell(context,order:dict):
print(f"平昨=>{order}")
send_order(cook_order_data(context,order,"0","1"))
# 平今空 提交最新订单信息数据
def closeTodaySell(context,order:dict):
print(f"平今=>{order}")
send_order(cook_order_data(context,order,"0","3"))
# 平昨多 提交最新订单信息数据
def closeYestodayBuy(context,order:dict):
print(f"平昨=>{order}")
send_order(cook_order_data(context,order,"1","1"))
#平今多 提交最新订单信息数据
def closeTodayBuy(context,order:dict):
print(f"平今=>{order}")
send_order(cook_order_data(context,order,"1","3"))
# 定义MA交易信号
# 根据金、死叉信号定义响应开仓动作 金叉返回数值 1 (开多仓信号) 死叉返回数值 -1 (开空仓信号) 返回数值 0 提示取值不够
def ma_signal(context):
fast = context.fast
slow = context.slow
context.klines = get_kline(context.base_instrument_id, context.base_period, max(fast,slow) + 2)
context.close_array = context.klines.get("close")
context.fast_array = simple_moving_average(context.close_array,fast)
context.slow_array = simple_moving_average(context.close_array,slow)
if len(context.slow_array) < 3:
return 0 # 数值不足
if context.ticks == 3:
print(f"检查均线数据=>{context.fast_array = } {context.slow_array = } {context.close_array = }")
if context.fast_array[-3] < context.slow_array[-3] \
and context.fast_array[-2] > context.slow_array[-2]:
return 1
if context.fast_array[-3] > context.slow_array[-3] \
and context.fast_array[-2] < context.slow_array[-2]:
return -1
return 0
# 定义ma均线的计算方式
def simple_moving_average(prices, n):
# 初始化一个空列表来保存每一个时间点的SMA值
sma_values = []
# 对于列表中的每一个价格,计算SMA
for i in range(len(prices)):
# 如果已经有足够的数据来计算SMA
if i >= n - 1:
# 计算从i-n+1到i的平均值
sma = sum(prices[i-n+1:i+1]) / n
sma_values.append(sma)
else:
# 如果还没有足够的数据,则使用可用的数据点的平均值
sma = sum(prices[:i+1]) / (i + 1)
sma_values.append(sma)
return sma_values
# 返回委托单信息 注意此处的报单信息是未成交状态 可在此处对订单状态进行分析和调控
def on_order(context,order):
"""需要用户自定义的回调函数 仅作参考"""
# 获取订单状态
order_status = order.get("OrderStatus")
CombOffsetFlag = order.get("CombOffsetFlag")
StatusMsg = order.get("StatusMsg")
# 5 是订单状态的撤单标识,只有订单返回 5 后才返回报单目前处在 a 3 0 三个状态中的哪个状态"""
# a 是报单成功了状态 3 是报单但未成交状态 0 是报单且成交状态
if order_status == "5":
if CombOffsetFlag == "0":
# 清除开启任务等待序列
context.open_task.clear()
print(f"open:{StatusMsg}")
else:
# 清除关闭任务等待序列
context.close_task.clear()
print(f"close:{StatusMsg}")
# 返回成交单信息 注意此处的报单信息是已成交状态
def on_trade(context,trade):
"""需要用户自定义的回调函数 仅作参考"""
InstrumentID = trade.get("InstrumentID")
OffsetFlag = trade.get("OffsetFlag")
if OffsetFlag == "0":
context.positions[InstrumentID] = trade
context.open_task.clear()
else:
context.positions.pop(InstrumentID)
context.close_task.clear()
# 返回报错
def on_error(context,order):
print(f"error=>{order}")
# 策略回测中途发生错误 or 回撤完毕时调用
def on_stop(context):
print("demo停止")
策略文件代码在编辑器中编写完成后需先按 Ctrl + S 键保存,在界面空白处单击鼠标右键,选择 Python编译,编辑器在终端提示输出[编译成功!]便视为成功;而当提示策略编译失败时,先查看鼠标右键是否选择的是 Python 语言编译方式,如果依旧编译报错,可在终端的 Output 输出中查看报错类型,并进行相应的错误修正;
在新增策略时先确定好在哪个文件夹下,尽量使用不同的文件名字以便于区分不同的策略,否则可能会覆盖掉旧的策略文件;
策略列表中使用导入导出功能按钮时,需要确定好文件路径,否则可能无法搜索到目标策略文件。其中导出可以选择两种文件:源码文件(.py)和加密文件(.pqmf);
假设仅使用策略框架而没有丰富明确的逻辑执行交易代码,在回测过程中虽然能支持回测,但是详情页中没有统计出相应的交易数据,因此可能会导致无法查看详情页并强制要求重新回测,此时您应重新返回编辑器中重新对策略文件中的代码进行调优和完善;
编写的策略可能因为代码效率问题导致执行速度慢,影响实时交易。例如,策略中包含复杂的计算或大量的数据查询操作,会增加策略的执行时间;
以本文档案例为例,若策略核心交易逻辑中是通过获取涨跌停价保证以当前市价成交,以实现市价成交的效果,成交价为市价。那么当策略涉及的某个标的接近涨停货跌停的时候,通过设置“获取涨跌停价保证以当前市价成交”这种方法可能会导致该单子因交易规则而成为废单;若通过市价报单的方式报单,而此时需要注意盘口撮合机制: