策略编写文档

关于期魔方

期魔方量化投研平台是由四川赤壁量化科技有限公司自主研发,专为国内期货市场打造的全能量化交易与研究平台。它集成了市场行情分析、量化策略回测、数据分析、风险管理以及机器学习等多项强大功能,具备高度可扩展性,能够满足不同用户的多样化需求。

核心功能:

  1. 专业的K线图表功能:期魔方支持自定义Python指标开发,具备强大的跨周期、跨品种、跨市场数据调用能力,并提供深度DIV可视化界面,帮助用户进行精准的市场分析。
  2. 高效的Python量化与回测:期魔方采用自主研发的核心量化底层,性能相比市场上其他Python策略提升十倍以上。编写简单、快速入门,用户可在十分钟内轻松上手。
  3. 全面的数据支持:期魔方支持超过15年的历史数据回测,且可以调用多维度数据,包括库存、仓单、现货等衍生数据,帮助用户进行深入分析与策略优化。
  4. 盘手训练系统:期魔方提供多周期复盘与动态回放功能,用户可以通过模拟训练不断优化交易策略。同时,系统支持训练报告分析、查看与下载,便于用户进行复盘总结。
  5. 机器学习集成:期魔方深度结合sklearn,初学者也能快速上手模型训练与优化,助力用户实现智能化决策与策略提升。

 

一、策略编写

1. 新手入门 Demo

本案例实现的原理: 当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停止")

 

2. 新建策略

2.1 创建策略文件夹

以下是创建文件夹的步骤:

策略 -> 策略列表文件 -> 新建按钮 -> 新增策略弹窗 -> 填写好策略名称,选择语言为 “Python(已固定)”->点击确定按钮。

2.2 创建策略文件

以下是创建策略文件步骤的文字说明:

a. 策略 -> 策略列表文件 -> 新建按钮 -> 新增策略弹窗 -> 填写好策略名称,选择语言为 “Python(已固定)” -> 点击确定按钮;

b. 如果开发者已下载好编辑器,则可以新增策略成功后直接点击编辑进行代码编写,若是第一次下载编辑器,添加策略成功后会提示下载系统自带的编写工具,用户此时需点击下载新包安装以安装编写工具;

c. 等待编辑器解压进度条100%后,会自动跳转到 vscode 编辑器页;

d. 完成策略代码编写后按 Ctrl + S 键保存,在界面空白处单击鼠标右键,选择python编译,编辑器在终端提示输出[编译成功!]后即可。

 

3. 策略结构

3.1 策略框架

描述‌ 策略框架是策略文件中的一个结构化组成部分,它定义了策略的整体结构和关键要素。这包括策略的目标、执行逻辑、条件判断、决策流程以及可能涉及的外部参数和内部变量等。一个完整策略的整体框架,包含有以下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

 

3.2 编写规范

‌a. 缩进与空格

Python 使用缩进来表示代码块,通常使用4个空格进行缩进,而不是制表符(Tab)。避免混合使用空格和制表符进行缩进,以保持代码格式的一致性和可读性‌。

b. 命名规范

(1)变量、函数和模块名应使用小写字母和下划线分隔(snake_case)。避免使用 Python 关键字作为变量名,以防止潜在的命名冲突;

(2)常用的命名方法包括驼峰命名法(CamelCase),其中类名通常使用大驼峰命名法(UpperCamelCase),而函数和变量则常用小驼峰命名法(lowerCamelCase)。

 

3.3 外置参数

描述

外置参数是策略文件中自定义的变量,用于配置和控制策略的行为。

作用‌

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.策略使用

 

3.4 BASE 全局变量

描述

在策略文件(尤其是涉及编程或脚本的策略文件,如某些自动化策略、交易策略等)中,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

 

4. 功能性函数

描述

策略文件中的功能性函数是指为实现特定策略目标而设计的一系列操作或方法。这些函数在策略文件中起着关键作用,帮助定义、执行和管理策略。以下是对10个功能性函数及其说明和使用方法的,开发者可以根据自己的需求选择性调用:

4.1 get_kline 获取K线数据

说明

获取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=60int

 

参数中文描述
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]
}

 

 

4.2 get_account 获取CTP帐户信息

说明

获取当下已登录期货公司账户信息。

输入参数

输出参数

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, 
    # 以下展示内容已省略
    ...
}

 

4.3 get_symbolinfo 获取合约信息

说明

从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,
    # 以下内容已省略
    ...
}

 

 

4.4 get_position 获取持仓信息

说明

用于获取用户目前期货交易账户中所有持仓明细详情的函数。

输入参数

输出参数

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大商所持仓成本差值,只有大商所使用
TasPositiontas持仓手数
TasPositionCosttas持仓成本
Symbolinfo持仓细则对象

 

接口案例

def on_init(context):
    # 获取当前持仓的明细
	position_info = get_position()
	print(f"{position_info}")

数据示例

[
    {
        'AbandonFrozen': 0,
        'BrokerID': '9999',
        'CashIn': 0.0,
        'CloseAmount': 0,
        'CloseProfit': 0
    	# 以下展示内容已省略
    	...
    }
]

 

4.5 get_tick 获取合约报价

说明

获取指定合约 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成交金额
UpdateMillisecUpdateMillisec
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
    # 以下内容已省略
    ...
}

 

4.6 send_order 策略报单

说明

做多、做空、平仓等动作都需要通过该方法函数进行报单。

输入参数

参数中文描述
order_info字典对象,如方向、价格等(详见如下对象 ORDER_INFO_DATA

输出参数

参数中文描述类型
code请求成功返回值为 0,请求失败返回值为非0int
data请求成功返回值为空值,请求失败返回的对象是字典any
msg请求成功返回值为 'ok',请求失败返回请求失败的原因str

 

ORDER_INFO_DATA 报单对象明细字段信息

参数中文描述类型
ExchangeID交易所IDstr
Symbol产品IDstr
Direction方向,多:0,空:1str
OrderPriceType报单价格类型,默认为2str
LimitPrice报单价格float
CombOffsetFlag开平标志,开仓:0,平仓/平昨:1,平今:3str
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'}

 

4.7 action_order 策略撤单

说明

涉及撤单时调用。

输入参数

参数中文描述
dict字典对象,包含字段详见如下对象 ACTION_ORDER_INFO

输出参数

参数中文描述类型
code请求成功返回值为 0,请求失败返回值为非0int
data请求成功返回值为空值,请求失败返回的对象是字典any
msg请求成功返回值为 'ok',请求失败返回请求失败的原因str

 

ACTION_ORDER_INFO 撤单对象明细字段信息

参数中文描述类型
ExchangeID交易所IDstr
Symbol产品IDstrstr
Direction方向,多:0,空:1str
OrderPriceType报单价格类型,默认为2str
LimitPrice报单价格float
CombOffsetFlag开平标志,开仓:0,平仓/平昨:1,平今:3str
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'}

 

4.8 remove 策略停止任务

说明

调用该函数会暂停当前的策略运行。

输入参数

输出参数

接口案例

def on_tick(context):
    # 此处运行交易逻辑代码
    ...
    # 需要退出任务运行时调用
    remove()

数据示例

 

4.9 put_log 推送日志

说明

推送日志到前端展示的方法。

输入参数

参数中文描述
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)

数据示例

"这是一条日志消息内容"

 

4.10 get_userinfo 获取期魔方用户信息

说明

返回当前登录期魔方用户的信息。

输入参数

输出参数

参数中文描述
dict字典,包含字段详见以下对象 DICT

 

DICT 对象

参数中文描述
nickname用户昵称,如"张三"、"李四"
user_id用户ID,如"00001"
phone_number手机号码,如13333331133
vip_levelVIP会员等级,0:普通会员,1:黄金会员,2:超级会员

 

接口案例

# 与 on_auth() 函数一起搭配使用
def on_auth(self):
    # 先通过系统接口获取用户信息
    userinfo = self.get_userinfo()
    # 再通过三个返回值字段来判断会员等级
    # 通过会员等级判断 0:普通会员,1:黄金会员,2:超级会员
    return vip_level

数据示例

# 会员等级判断 0:普通会员,1:黄金会员,2:超级会员
1

 

5. 结构性函数

描述

策略文件中的结构性函数是指那些为策略文档提供框架、结构和组织的关键要素。这些函数不仅帮助开发者系统地构建策略,还确保策略的全面性、一致性和可读性。以下是7个结构性函数及其说明和使用方法,开发者必须在此框架下编写策略文件:

5.1 on_tick 运算函数

说明

a. 策略的品种合约的TICK变化时调用的方法;

b. 行情报价每进来一次就会调用函数 on_tick 刷新一次。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象

输出参数

接口案例

def on_tick(context):
    pass

数据示例

 

5.2 on_init 初始化函数

说明

a. 该方法每次任务启动只执行一次;

b.用于设置策略交易逻辑中的初始化部分。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象

输出参数

接口案例

def on_init(context):
    pass

数据示例

 

5.3 on_stop 策略停止函数

说明

a. 策略结束时调用,可以执行一些结尾统计操作 ;

b. 保存策略的回测数据或策略回测中途发生错误或者停止都会调用 on_stop,并返回信息。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象

输出参数

接口案例

def on_stop(context):
    pass

数据示例

 

5.4 on_order 接收委托单信息

说明

a. 返回委托单信息的方法,注意此处的 order 报单信息是未成交状态(包含字段详见以下对象 ORDER_DATA,与 on_trade 中 order 对象的值会有一定差异);

b. 用于存储和访问交易过程中的各种状态和信息;

c. 可在此处对订单状态进行分析和调控。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象
order订单信息,包含字段详见以下对象 ORDER_DATA

 

ORDER_DATA 委托单对象明细字段信息

参数中文描述
AccountID投资者帐号
ActiveTime激活时间
ActiveTraderID激活交易ID
ActiveUserID操作用户代码
BranchID营业部编号
BrokerID经纪公司代码
BrokerOrderSeqCTP系统的报单编号
BusinessUnit业务单元
CancelTime撤销时间
ClearingPartID清算会员编号
ClientID交易编码
CombHedgeFlag组合投机套保标志
CombOffsetFlag组合开平标志
ContingentCondition触发条件
CurrencyID币种代码
Direction买卖方向
ExchangeID交易所代码
ExchangeInstID合约在交易所的代码
ForceCloseReason强平原因
FrontIDCTP后台前置编号
GTDDateGTD日期
IPAddressIP地址
InsertDate订单插入日期
InsertTime订单插入时间
InstallID安装编号
InstrumentID合约代码
InvestUnitID投资单元代码
InvestorID投资者代码
IsAutoSuspend自动挂起标志
IsSwapOrder互换单标志
LimitPrice限价
MacAddressMac地址
MinVolume最小成交量
NotifySequence报单提示序号
OrderLocalIDCTP系统分配的唯一标识符
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

数据示例

 

5.5 on_error 接收CTP错误

说明

接收 CTP 端口发送过来的错误信息数据 data(暂只供接收信息,无法输出该信息数据内容)。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象
data报错信息数据

输出参数

接口案例

def on_error(context, data):
    pass

数据示例

 

5.6 on_trade 接收成交单信息

说明

接收成交单信息的方法,注意此处的 order 报单信息是已成交状态,包含字段详见对象 ORDER_DATA(同 on_order 中的 order 对象,但值会有一定差异)。

输入参数

参数中文描述
context空对象,用于存储和传递运行时状态和信息的对象
order订单信息,包含字段详见对象 ORDER_DATA

 

ORDER_DATA 成交单对象明细字段信息

参数中文描述
AccountID投资者帐号
ActiveTime激活时间
ActiveTraderID激活交易ID
ActiveUserID操作用户代码
BranchID营业部编号
BrokerID经纪公司代码
BrokerOrderSeqCTP系统的报单编号
BusinessUnit业务单元
CancelTime撤销时间
ClearingPartID清算会员编号
ClientID交易编码
CombHedgeFlag组合投机套保标志
CombOffsetFlag组合开平标志
ContingentCondition触发条件
CurrencyID币种代码
Direction买卖方向
ExchangeID交易所代码
ExchangeInstID合约在交易所的代码
ForceCloseReason强平原因
FrontIDCTP后台前置编号
GTDDateGTD日期
IPAddressIP地址
InsertDate订单插入日期
InsertTime订单插入时间
InstallID安装编号
InstrumentID合约代码
InvestUnitID投资单元代码
InvestorID投资者代码
IsAutoSuspend自动挂起标志
IsSwapOrder互换单标志
LimitPrice限价
MacAddressMac地址
MinVolume最小成交量
NotifySequence报单提示序号
OrderLocalIDCTP系统分配的唯一标识符
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

数据示例

 

6. 策略使用

6.1 回测模块的调试与应用

在策略页面创建好策略文件后会自动进入编辑器,此时开发者应在策略框架下完成代码编写,保存并进行Python编译;

然后在回测 -> 添加回测页面 -> 测试模型中选择策略模型,填好基础参数;

image-20241205155818073

若策略中带有策略外置参数的,还需补充策略参数

image-20241107144625089

添加成功后,点击[回测]按钮,随后会在回测页面下方打印日志内容,开发者可在此处返回的日志内容检查代码的正确性并进行调试。

image-20241205164530849

调试好策略并等待回测完成100%后,点击[详情]

img

查看该策略的回测策略数据。

image-20241107145350059

6.2 任务模块的调试与应用

任务页面双击选中策略进入添加任务窗口,设置默认参数;

若策略中带有策略外置参数,还需补充策略外置参数,点击提交启动任务。

启动任务后展示实时 k 线图,并在任务页面右下角打印日志内容,开发者可在此处返回的日志内容检查代码的正确性并进行调试。

 

7. 策略多产品运算形式

运行多产品策略时,运算过程中会存在各标的合约运算频率不一致的情况,为避免此等情况,多产品标的合约均统一以“运行品种”为主频率进行运算,但会存在所选择“运行品种”的频率运算会比多产品策略中的部分品种低,因此在回测页面或任务页面运行多产品策略时,建议开发者在选择“运行品种”时设置交易量活跃的高频率主力品种。

而在多产品策略下,要想实现一个策略同时订阅多个标的合约 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停止")

 

三、常见问题

1. 策略编译步骤及编译失败问题

策略文件代码在编辑器中编写完成后需先按 Ctrl + S 键保存,在界面空白处单击鼠标右键,选择 Python编译,编辑器在终端提示输出[编译成功!]便视为成功;而当提示策略编译失败时,先查看鼠标右键是否选择的是 Python 语言编译方式,如果依旧编译报错,可在终端的 Output 输出中查看报错类型,并进行相应的错误修正;

2. 文件路径选择与命名问题

在新增策略时先确定好在哪个文件夹下,尽量使用不同的文件名字以便于区分不同的策略,否则可能会覆盖掉旧的策略文件;

3. 导入导出文件选择问题

策略列表中使用导入导出功能按钮时,需要确定好文件路径,否则可能无法搜索到目标策略文件。其中导出可以选择两种文件:源码文件(.py)和加密文件(.pqmf);

4. 回测数据的监听

假设仅使用策略框架而没有丰富明确的逻辑执行交易代码,在回测过程中虽然能支持回测,但是详情页中没有统计出相应的交易数据,因此可能会导致无法查看详情页并强制要求重新回测,此时您应重新返回编辑器中重新对策略文件中的代码进行调优和完善;

5. 策略执行效率问题

编写的策略可能因为代码效率问题导致执行速度慢,影响实时交易。例如,策略中包含复杂的计算或大量的数据查询操作,会增加策略的执行时间;

6. 对以涨跌停价报单情况的处理

以本文档案例为例,若策略核心交易逻辑中是通过获取涨跌停价保证以当前市价成交,以实现市价成交的效果,成交价为市价。那么当策略涉及的某个标的接近涨停货跌停的时候,通过设置“获取涨跌停价保证以当前市价成交”这种方法可能会导致该单子因交易规则而成为废单;若通过市价报单的方式报单,而此时需要注意盘口撮合机制: