期魔方量化投研平台是由四川赤壁量化科技有限公司自主研发,专为国内期货市场打造的全能量化交易与研究平台。它集成了市场行情分析、量化策略回测、数据分析、风险管理以及机器学习等多项强大功能,具备高度可扩展性,能够满足不同用户的多样化需求。
核心功能:
该 demo 以双均线为例,步骤如下:
第一步是先定义外置参数 Params,第二步在 on_init 中定义输出图形对象,第三步编写 calculate_all 计算均线输出值的代码,第四步编写 calculate_last 计算均线输出值的代码。
# 双均线案例
# 外置参数
from pydantic import BaseModel
class Params(BaseModel, validate_assignment=True):
from pydantic import Field
is_main:bool = Field(default=True, title="是否是主图")
# 初始化图形对象
def on_init(self):
self.fast_line = self.Line('fast_line', 'rgba(255, 0, 0, 1)')
self.slow_line = self.Line('slow_line', 'rgba(0, 255, 0, 1)')
# 第一次全量计算指标
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
# 存储数据
self.length = length
self.datetime_str = datetime_str
self.close = close
for i in range(length):
# 计算快线
if i >= 4:
self.fast_line.set_point(datetime_str[i], sum(close[i-4:i+1]) / 5)
# 计算慢线
if i >= 9:
self.slow_line.set_point(datetime_str[i], sum(close[i-9:i+1]) / 10)
# 实时行情计算增量数据
def calculate_last(self, data):
datetime_str, open, high, low, close, volume = data
# 对比时间
if datetime_str != self.datetime_str[-1]: # 新的k线来了
self.length += 1
self.datetime_str.append(datetime_str)
self.close.append(close)
else: # 更新数据
self.close[-1] = close
# 计算
self.fast_line.set_point(datetime_str, sum(self.close[-5:]) / 5)
self.slow_line.set_point(datetime_str, sum(self.close[-10:]) / 10)
策略 -> 指标列表 -> [新增] -> 输入文件名和注释说明 -> 点击[确定]。
a. 策略 -> 指标列表 -> 新增按钮 -> 新增指标弹窗 -> 填写指标名称,选择语言为 “Python” -> 点击确定;
b. 若是第一次使用编辑器的用户需先根据提示下载编辑器;
c. 等待编辑器自动解压完成后, vscode 编辑器会自动弹出系统自带的基础编程代码框架;
d. 用户根据自己的需求进行代码编写,编写完成后按 Ctrl + S 键保存,在空白处右键,点击[Python编译],提示[编译成功!]即可。
按照指标框架,先导入外置参数,再通过2个主要的结构方法函数定义指标的输出对象并进行相应的计算:
# 外置参数
from pydantic import BaseModel
class Params(BaseModel, validate_assignment=True):
from pydantic import Field
is_main:bool = Field(default=True, title="是否为主图")
# 定义输出图形对象
def on_init(self):
pass
# 第一次全量计算指标代码实现
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
pass
# 实时行情计算增量数据代码实现
def calculate_last(self, data):
length, datetime_str, open, high, low, close, volume = data
pass
a.缩进与空格
Python使用缩进来表示代码块,通常使用4个空格进行缩进,而不是制表符(Tab)。避免混合使用空格和制表符进行缩进,以保持代码格式的一致性和可读性。
b.命名规范
(1)类名通常使用大驼峰命名法(UpperCamelCase),变量、函数和模块名应使用小写字母和下划线分隔(snake_case)。避免使用Python关键字作为变量名,以防止潜在的命名冲突。
指标外置参数配置部分,若没有额外声明 is_main 外置参数,则该指标默认在副图加载:
# 导入参数映射模型
from pydantic import BaseModel
# 设置外置参数
class Params(BaseModel, validate_assignment=True):
from pydantic import Field
# 例:选择指标是否在主图显示 True 则为主图显示 False 则为副图显示
is_main:bool = Field(default=True, title="是否是主图")
目前外置支持4种数据类型,分别为:
不同的类型在指标面板会自动生成对应的输入框,用户可直接在指标面板中修改参数值。
描述
结构性函数为python指标的核心,是用户实现其代码的地方,需要用户在结构性函数中编写具体的指标配置以及计算逻辑,期魔方系统会按照指标加载的步骤调用用户编写的结构性函数。
描述
初始化函数在程序启动时调用一次: 1.可以初始化自定义属性 2.定义订阅数据的方式 3.定义接收数据的类型 4.初始化输出指标的图形对象
说明
接收数据的方式支持2种方式:
接收数据的类型支持4种方式:
输入参数
主输入参数对象
参数 | 中文描述 |
---|---|
self | 指标实例自身 |
输出参数
无
接口案例
def on_init(self):
# 自定义属性
self.name1 = 'value1'
self.name2 = 'value2'
# 定义接收数据方式
self.calculate_type = 'last'
# 定义接收数据的类型
self.calculate_data_type = 'list'
# 定义输出对象
# 画线
"""
1. Line(线条)
2. ArrowLine(箭头)
3. ColorKline(彩色k线)
4. MultiLine(线段)
5. Text(文字)
6. MultiText(多段文字)
7. Point(圆点)
8. MultiPoint(多段圆点)
9. Bar(柱形)
10. Polygon(多边形)
"""
self.day5_line = self.Line('day5', 'rgba(255, 0, 0, 1)', line_width=2, line_dash=[5, 5])
self.day10_line = self.Line('day10', 'rgba(0, 255, 0, 1)', line_width=4, line_dash=[10, 10])
self.arrow1 = self.ArrowLine('arrow1', 'rgba(0, 255, 255, 1)', angle=30, length=10)
数据示例
无
接收k线数据,计算画出图形的具体数值,并设置数据到图形对象中
说明
默认在指标加载时只推送一次全量数据,后续则只推送增量数据,用户除了实现calculate_all
函数外,还需要实现calculate_last
函数。
若在on_init(self)
中定义了self.calculate_type = 'all'
,则每次都推送全量数据,用户则无需实现calculate_last
函数。
输入参数
主输入参数对象
参数 | 中文描述 |
---|---|
self | 指标实例自身 |
data | 接收到的数据内容(元组,请使用解包方式获取) |
输出参数
无
接口案例
# 第一次全量计算指标
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
# 存储数据
self.length = length
self.datetime_str = datetime_str
self.close = close
# 获取外置参数
fast = self.params.fast
slow = self.params.slow
# 列表起点为0,所以起始点为fast-1
fast_start = fast - 1
slow_start = slow - 1
for i in range(length):
# 计算快线
if i >= fast_start:
self.fast_line.set_point(datetime_str[i], sum(close[i-fast_start:i+1]) / fast)
# 计算慢线
if i >= slow_start:
self.slow_line.set_point(datetime_str[i], sum(close[i-slow_start:i+1]) / slow)
# 实时行情计算增量数据
def calculate_last(self, data):
datetime_str, open, high, low, close, volume = data
# 对比时间
if datetime_str != self.datetime_str[-1]: # 新的k线来了
self.length += 1
self.datetime_str.append(datetime_str)
self.close.append(close)
else: # 更新数据
self.close[-1] = close
# 取最新的数据
last_fast = self.close[-self.params.fast:]
last_slow = self.close[-self.params.slow:]
# 计算
self.fast_line.set_point(datetime_str, sum(last_fast) / self.params.fast)
self.slow_line.set_point(datetime_str, sum(last_slow) / self.params.slow)
数据示例
无
描述
用户自定义权限验证
说明
用户可以通过期魔方提供的功能性函数 self.get_userinfo()
获取当前运行指标的客户端的用户信息,以实现自定义权限的效果。
权限验证通过则返回值为 True
,权限未通过验证则返回字符串类型文字提示消息(该消息内容支持自定义)。
返回值
返回值 | 中文描述 | 类型 |
---|---|---|
True | 权限验证通过时返回 | bool |
text | 权限验证未通过返回文字提示消息 | str |
接口案例
def on_auth(self):
# 通过期魔方提供的功能性函数获取用户信息
userinfo = self.get_userinfo()
if userinfo['nickname'] not in ['张三', '李四', '王五']:
return '抱歉,您没有使用该指标的权限'
return True
数据示例
无
描述
订阅其他合约和周期的数据
说明
系统默认推送的为当前加载指标的合约以及当前选中的周期的数据,如果用户指标计算需要其他合约或周期的数据,则需要通过此函数订阅。
用户需要使用期魔方提供的功能性函数 self.subscribe()
去订阅多个合约或周期的数据。
周期参数:
返回值
无
接口案例
def on_subscribe(self):
# 第一个参数为合约名称,后续参数为周期名称,可以订阅多个合约和周期
self.subscribe(self.symbol, 'M15', 'M30')
other_symbol = 'ag2504'
self.subscribe(other_symbol, 'D1')
描述
功能函数是期魔方封装好的函数,会实现某些功能或返回特定的结果,提供给用户使用。
当用户在编写代码时,需要用到这些功能时,可以直接调用。
描述
订阅其他合约和周期的数据
说明
订阅其他品种和周期的数据,以用于计算多品种多周期的指标(非多品种多周期可以不写) 必须调用self.subscribe(symbol, period, period2, period3, ...)
输入参数
参数 | 中文描述 | 类型 | 必填 |
---|---|---|---|
symbol | 合约名称 | str | 是 |
period | 周期 | str | 是 |
输出参数
无
接口案例
见4.4案例代码
数据示例
无
描述
自定义订阅的数据字段
说明
当计算指标不需要所有的k线数据时,可以使用该函数自定义订阅的数据字段,以提高性能。
输入参数
参数 | 中文描述 | 类型 | 必填 |
---|---|---|---|
*field | 字段名称 | str | 是 |
输出参数
无
接口案例
见7.1详细说明
数据示例
无
说明
a. 该函数返回当前登录期魔方用户的信息;
b. 该函数返回的以下 dict 字典中包含用户昵称,用户ID,手机号,VIP等级;
c. 在 on_auth()
函数中调用该函数,来判断该用户是否可以运行该指标文件。
注意,该函数返回的值都为字符串类型
输入参数
无
输出参数
参数 | 中文描述 |
---|---|
dict | 字典,包含字段详见以下 DICT 对象 |
DICT 对象
参数 | 说明 |
---|---|
nickname | 用户昵称,如"张三"、"李四" |
user_id | 用户ID,如"00001" |
phone_number | 手机号码,如"13333331133" |
vip_level | VIP会员等级,"0":普通会员,"1":黄金会员,"2":超级会员 |
接口案例
见4.3案例代码
数据示例
无
描述
发送GET请求
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
url | 请求地址 | str |
params | 参数 | dict、None |
**kwargs | 其他键值对参数 | any |
输出参数
直接返回接口的返回值
接口案例
url = "xxx"
params = {
"key1": 'value1',
"key2": 'value2',
}
re = self.send_get_request(url, params=params)
数据示例
无
描述
发送POST请求
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
url | 请求地址 | str |
data | 数据 | dict、None |
**kwargs | 其他键值对参数 | any |
输出参数
直接返回接口的返回值
接口案例
url = "xxx"
data = {
"key1": data1,
"key2": data2,
}
re = self.send_post_request(url, data=data)
数据示例
无
描述
推送自定义指标预警消息
说明
当触发了某些条件时,可以调用该函数,生成一个消息实例,然后调用实例的 publish()
方法来发送消息。
注意,避免循环中使用,避免造成大量发消息卡死
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
content | 消息内容 | str |
count | 消息次数(默认为1) | int |
输出参数
无
接口案例
def on_init(self):
# 自定义消息字典,记录发送消息状态
self.msg_status = {}
def calculate_last(self, data):
from datetime import datetime
datetime_str, open, high, low, close, volume = data
# 当一些条件达成时,这里比如价格大于100时,触发信号
if (close > 100) and (self.msg_status.get(datetime_str, None) is None):
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
content = f'XXX指标,{self.symbol}.{self.period}周期,于{now},价格在{round(close, 2)},触发XX信号'
# 实例化一个消息实例,并设置内容为content变量中的值
msg = self.Message(content, count=1) # count默认为1,可以不写
msg.publish() # 发送消息
self.msg_status[datetime_str] = 'published' # 更新字典,避免重复发送消息
数据示例
无
描述
一个数据序列
说明
当指标被当作一个函数使用时,需要定义一个数据序列(Buffer),用于存储数据和当作返回值。
如果定义并写入了Buffer,在行情页面左上角,Buffer数据会跟随行情序列(K线)一一对应。
详细说明见 8.1 函数指标案例
输入参数
参数 | 中文描述 | 类型 |
---|---|---|
name | 序列名称 | str |
输出参数
无
接口案例
def on_init(self):
# 定义Buffer
self.fast_buffer = self.Buffer('fast_buffer')
self.slow_buffer = self.Buffer('slow_buffer')
def calculate_all(self, data):
length, klines = data
# 存储数据
self.length = length
self.datetime = klines['datetime']
self.close = klines['close']
# 获取外置参数
fast_start = self.params.fast - 1
slow_start = self.params.slow - 1
for i in range(length):
# 计算快线
if i >= fast_start:
fast_value = sum(self.close[i-fast_start:i+1]) / fast
self.fast_buffer[i] = fast_value # 写入buffer
# 计算慢线
if i >= slow_start:
slow_value = sum(self.close[i-slow_start:i+1]) / slow
self.slow_buffer[i] = slow_value
数据示例
无
描述
一个函数指标
说明
当我们调用函数指标时,需要通过 self.Indicator()
方法来创建一个函数指标对象。
注意:函数指标的强大之处在于支持跨合约、跨周期!
详细说明见 8.3 函数指标案例
输入参数
参数 | 中文描述 | 类型 | 必填 |
---|---|---|---|
name | 指标名称 | str | 是 |
id | 指标ID | str | 否 |
symbol | 合约代码 | str | 否 |
period | 周期 | str | 否 |
**params | 外置参数 | kv | 否 |
输出参数
无
接口案例
def on_init(self):
# 使用当前指标的symbol和period,以及默认外置参数
self.ma_indicator = self.Indicator('ma')
# 使用ag2506和M15,定义fast=5,slow=10
self.ag_indicator = self.Indicator('ma', 'ma_ag', symbol='ag2506', period='M15', fast=5, slow=10)
# 使用au2506和D1,定义fast=10,slow=20
self.au_indicator = self.Indicator('ma', 'ma_au', symbol='au2506', period='D1', fast=10, slow=20)
数据示例
无
描述
一个日志函数,会返回一个日志对象
说明
在开发指标的过程中,我们需要通过日志来记录一些信息,方便调试。
该日志对象提供几个函数用于记录不同等级的日志:
debug()
- 调试信息info()
- 普通信息success()
- 成功信息warning()
- 警告信息error()
- 错误信息critical()
- 严重错误信息输入参数
参数 | 中文描述 | 类型 | 必填 |
---|---|---|---|
file_path | 绝对路径 | str | 是 |
注意:日志的路径必须为绝对路径,格式需要符合Windows的系统格式,如:
C:\\Users\\Admin\\Desktop\\indicator.log
输出参数
无
接口案例
def on_init(self):
# 在on_init中初始化日志对象
self.logger = self.Logger('C:\\Users\\Admin\\Desktop\\indicator.log')
def calculate_all(self, data):
# 在calculate_all中使用日志对象记录信息
self.logger.info('这是一条普通信息')
self.logger.error('这是一条错误信息')
开发者在编辑好主图显示的指标文件代码后按 Ctrl + S 键保存并进行语言编译(Python编译或麦语言编译),编译成功的指标,可在行情 -> 我的指标 -> 自编指标中查看并渲染到K线图上展示效果;
一个行情可以加载多个指标,您只需要继续点击加载别的指标即可,并在[我的加载]或行情左上方查看您添加的具体指标;
区分好指标文件在主/副图显示后,在任务 -> 启动任务 -> 指标标识 -> 指标面板 -> 选择指标,应用指标在任务实时行情K线中。
由于指标的计算并非需要使用所有的K线数据,为了提升指标的性能,期魔方量化交易平台支持自定义接收数据的方式,用户可以根据自己的需求选择订阅不同数据字段。
为了能够实现自定义数据字段,新增了一个 fields
函数,用于订阅数据的字段。
fields
函数有3种使用方法:
length
datetime
open
high
low
close
volume
这些数据字段,既为默认接收方式(主要是为了兼容旧版指标)。*
,则订阅所有数据字段。包含length
datetime
open
high
low
close
volume
total_turnover
open_interest
settlement
这些数据字段。fields('close', 'volume')
,则只订阅 close
和 volume
这两个字段。def on_init(self):
# 1.不声明 fields 函数,订阅和接收默认数据字段
# 2.订阅全部数据字段
self.fields('*')
# 3.订阅指定数据字段
self.fields('close', 'volume')
注意:不同的订阅数据字段方式,需要使用不同的方式接收数据
当使用第1种方式,既不声明fields
函数,接收数据方式代码如下:
def calculate_all(self, data):
# data 是一个元组,里面包含了默认数据字段
# length 是一个值,表示数据长度
# datetime/open/high/low/close/volume 是一个列表,里面包含了对应的数据
length, datetime, open, high, low, close, volume = data
当使用第2种和第3种方式,接收数据方式代码如下:
def on_init(self):
# 假设只订阅了 `close` 字段
self.fields('close')
def calculate_all(self, data):
# data 是一个元组,里面包含了 length 和 klines
# length 是一个值,表示数据长度
# klines 是一个字典,里面包含了对应的数据列表 {'datetime': list, 'close': list}
# 无论是否订阅 datetime,都会默认包含
length, klines = data
self.length = length
self.datetime = klines['datetime'] # datetime 无需订阅,默认包含
self.close = klines['close'] # 获取 close 字段的数据
有时我们需要复用一套算法,我们可以将这套算法封装为一个指标函数,然后在其他指标中被调用。
函数指标是指可以不输出图形的指标,通过对数据的计算,返回一个数据序列或多个数据序列(Buffer)。
所以想让一个指标作为函数来使用,必须定义一个数据序列(Buffer),用于存储数据和当作返回值。
数据序列(Buffer)需要在 on_init
函数中定义,定义方式为:self.xxx = self.Buffer('xxx')
。
我们把之前的双均线指标改造为一个函数,代码如下:
def on_init(self):
self.calculate_type = 'last'
self.calculate_data_type = 'list'
# 订阅字段
self.fields('close')
# 定义快线Buffer
self.fast_buffer = self.Buffer('MA_FAST')
# 定义慢线Buffer
self.slow_buffer = self.Buffer('MA_SLOW')
当一个指标中定义了Buffer,这个指标就能够被作为一个函数来使用。
当指标被当作一个函数使用时,其计算方法和图形指标的函数一样,使用 calculate_all
和 calculate_last
函数来计算数据。
只是我们不需要把计算出的值放入图形,而是把计算出的值放入Buffer中。
以双均线为例,代码如下:
def calculate_all(self, data):
length, klines = data
# 存储数据
self.length = length
self.datetime = klines['datetime']
self.close = klines['close']
# 获取外置参数
fast = self.params.fast
slow = self.params.slow
# 列表起点为0,所以起始点为fast-1
fast_start = fast - 1
slow_start = slow - 1
for i in range(length):
# 计算快线
if i >= fast_start:
self.fast_buffer[i] = sum(self.close[i-fast_start:i+1]) / fast
# 计算慢线
if i >= slow_start:
self.slow_buffer[i] = sum(self.close[i-slow_start:i+1]) / slow
def calculate_last(self, data):
_, kline = data
# 对比时间
if kline['datetime'] != self.datetime[-1]: # 新的k线来了
self.length += 1
self.datetime.append(kline['datetime'])
self.close.append(kline['close'])
else: # 更新数据
self.close[-1] = kline['close']
# 计算
self.fast_buffer[-1] = sum(self.close[-self.params.fast:]) / self.params.fast
self.slow_buffer[-1] = sum(self.close[-self.params.slow:]) / self.params.slow
注意,写入buffer时,不需要像图形指标一样,调用
set_point
函数,而是直接使用buffer[index] = value
即可。 图形指标的数据是一个字典,而Buffer的数据是一个列表。
当我们编写好了一个函数指标后,我们可以在其他指标中调用它。
调用定义的函数指标时,我们需要使用 self.Indicator
方法来创建一个函数指标对象。
该方法有以下参数:
注意:当不需要跨合约、跨周期时,不需要指定symbol period
以下是一些调用案例:
def on_init(self):
self.ma_indicator = self.Indicator('ma')
def on_init(self):
self.ma_indicator1 = self.Indicator('ma', 'ma_1', fast=5, slow=10)
self.ma_indicator2 = self.Indicator('ma', 'ma_2', fast=20, slow=30)
def on_init(self):
self.ag_indicator = self.Indicator('ma', 'ma_ag', symbol='ag2506', period='M15', fast=5, slow=10)
self.au_indicator = self.Indicator('ma', 'ma_au', symbol='au2506', period='D1', fast=10, slow=20)
一共有2种方式来获取函数指标的值:
buffer_all()
和 buffer_last()
,分别用于获取所有数据和最新数据。当使用一个函数指标时,推荐使用这种方式获取值self.gather_all()
和 self.gather_last()
方法来获取所有函数指标的值。当使用了多个函数指标时,推荐使用这种方式获取值以我们刚才的双均线为例,使用第一种方式获取值的代码如下:
def calculate_all(self, data):
length, klines = data
datetime = klines['datetime']
# 调用函数指标的 `buffer_all` 方法,获取所有数据
# 假设在8.1和8.2中,我们已经定义了 `fast_buffer` 和 `slow_buffer` 两个Buffer对象,并且命名为 `MA_FAST` 和 `MA_SLOW`
# ma_buffer 的数据结构为 {'MA_FAST': list, 'MA_SLOW': list}
ma_buffer = self.ma_indicator.buffer_all()
for i in range(length):
self.fast_line.set_point(datetime[i], ma_buffer['MA_FAST'][i])
self.slow_line.set_point(datetime[i], ma_buffer['MA_SLOW'][i])
def calculate_last(self, data):
_, kline = data
# ma_buffer 的数据结构为 {'MA_FAST': value, 'MA_SLOW': value}
ma_buffer = self.ma_indicator.buffer_last()
self.fast_line.set_point(kline['datetime'], ma_buffer['MA_FAST'])
self.slow_line.set_point(kline['datetime'], ma_buffer['MA_FAST'])
对于跨品种、跨周期的情况,不用对每一个函数指标使用 buffer_all
方法,而可以使用 gather_all
一次性获取所有值,代码如下:
# 假设:我们有2个跨品种、跨周期的函数指标,分别为 ag_indicator 和 au_indicator
def calculate_all(self, data):
length, klines = data
datetime = klines['datetime']
# 调用 `self.gather_all` 方法,获取所有函数指标的值
# gather_all 的数据结构为 {'ma_ag': {'MA_FAST': list, 'MA_SLOW': list}, 'ma_au': {'MA_FAST': list, 'MA_SLOW': list}}
gather_buffer = self.gather_all()
# 获取ag2506的MA_FAST和MA_SLOW
ag_fast = gather_buffer['ma_ag']['MA_FAST']
ag_slow = gather_buffer['ma_ag']['MA_SLOW']
# 然后做一些事情。。。
def calculate_last(self, data):
_, kline = data
# gather_buffer 的数据结构为 {'ma_ag': {'MA_FAST': value, 'MA_SLOW': value},'ma_au': {'MA_FAST': value, 'MA_SLOW': value}}
gather_buffer = self.gather_last()
在默认指标代码框架中,输出两条线,一条为5日均线,另一条为10日均线:
# 外置参数
from pydantic import BaseModel
class Params(BaseModel, validate_assignment=True):
from pydantic import Field
is_main:bool = Field(default=True, title="是否是主图")
fast:int = Field(default=5, title="快线")
slow:int = Field(default=10, title="慢线")
# 初始化图形对象
def on_init(self):
self.fast_line = self.Line('fast_line', 'rgba(255, 0, 0, 1)')
self.slow_line = self.Line('slow_line', 'rgba(0, 255, 0, 1)')
# 第一次全量计算指标
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
# 存储数据
self.length = length
self.datetime_str = datetime_str
self.close = close
# 获取外置参数
fast = self.params.fast
slow = self.params.slow
# 列表起点为0,所以起始点为fast-1
fast_start = fast - 1
slow_start = slow - 1
for i in range(length):
# 计算快线
if i >= fast_start:
self.fast_line.set_point(datetime_str[i], sum(close[i-fast_start:i+1]) / fast)
# 计算慢线
if i >= slow_start:
self.slow_line.set_point(datetime_str[i], sum(close[i-slow_start:i+1]) / slow)
# 实时行情计算增量数据
def calculate_last(self, data):
datetime_str, open, high, low, close, volume = data
# 对比时间
if datetime_str != self.datetime_str[-1]: # 新的k线来了
self.length += 1
self.datetime_str.append(datetime_str)
self.close.append(close)
else: # 更新数据
self.close[-1] = close
# 取最新的数据
last_fast = self.close[-self.params.fast:]
last_slow = self.close[-self.params.slow:]
# 计算
self.fast_line.set_point(datetime_str, sum(last_fast) / self.params.fast)
self.slow_line.set_point(datetime_str, sum(last_slow) / self.params.slow)
指标图形对象是指在量化交易中,用于绘制图形的python实例。这些对象通常包括各种线条、柱状图、K线等元素,用以展示价格走势、成交量等信息。
期魔方量化交易平台提供了丰富的指标图形对象,以帮助用户进行技术分析、策略开发等。
图形对象:
说明:
每一个图形对象的使用方式为:
set_point(key, value, **styles)
),写入对应点的值。
当需绘制一条线条时,可以使用线条对象。
线条对象绘制的是完全连续的线条,可以配置颜色、宽度、虚线等属性。
如果需要绘制不连续的线段,可以使用多段线条(MultiLine)对象。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'line1' |
color | 必填 | 线条颜色 | rgba(255, 0, 0, 1) |
line_width | 选填 | 线条宽度 | 5 |
line_dash | 选填 | 虚线间隔 | [3, 3] |
代码案例
def on_init(self):
self.line1 = self.Line('line1', 'rgba(255, 0, 0, 1)')
self.line2 = self.Line('line2', 'rgba(255, 0, 0, 1)', line_width=2, line_dash=[5, 5])
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
代码案例
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
for i in range(length):
self.line1.set_point(datetime_str[i], close[i])
当需要在k线上绘制文字时,可以使用k线文字对象。
文字会显示在设定的k线顶部,或者底部,我们可以配置文字的位置、内容、颜色、字体、居中方式、连线等。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'text1' |
代码案例
def on_init(self):
self.text1 = self.Text('text1')
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
content | 必填 | 文字内容 | "B信号" |
color | 选填 | 文字颜色 | rgb(255,0,0) |
font | 选填 | 字体 | 18px 微软雅黑 |
base_line | 选填 | 上下居中方式 | 0 居中 1 上 2 下 |
y_move | 选填 | 文字Y轴显示位置 | 10 |
代码案例
def calculate_last(self, data):
length, datetime_str, open, high, low, close, volume = data
if close > 100:
self.text1.set_point(datetime_str, close, 'B信号', color='rgb(255,0,0)', font='18px 微软雅黑', base_line=1, y_move=10)
当需要在k线上绘制圆点时,可以使用圆点对象。
圆点会显示在设定的k线顶部,或者底部,我们可以配置圆点的颜色、弧度等属性。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'line1' |
color | 必填 | 圆点颜色 | rgba(255, 0, 0, 1) |
bg_color | 选填 | 背景颜色 | rgba(255, 0, 0, 0.5) |
point_radius | 选填 | 圆点弧度 | 8 |
代码案例
def on_init(self):
self.point1 = self.Point('point1' , 'rgba(255, 0, 0, 1)', bg_color='rgba(255, 0, 0, 1)', point_radius=8)
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
代码案例
def calculate_last(self, data):
length, datetime_str, open, high, low, close, volume = data
if close > 100:
self.point1.set_point(datetime_str, close)
当需要在副图上绘制柱状图时,可以使用柱状图对象。
比如绘制成交量、MACD这样的指标,可以配置柱子的颜色、宽度等属性。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'bar1' |
color | 必填 | 柱子颜色 | rgba(255, 0, 0, 1) |
width | 选填 | 柱子宽度,默认K线柱子宽度 | 8 |
type | 选填 | 柱子类型 | 0 实心 1 空心 |
代码案例
def on_init(self):
self.bar1 = self.Bar('bar1', 'rgba(255, 0, 0, 1)', width=8, type=1)
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value1 | 必填 | 柱子底部值,价格(在Y轴的位置) | 0 |
value2 | 必填 | 柱子顶部值,价格(在Y轴的位置) | 1000 |
代码案例
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
for i in range(length):
self.bar1.set_point(datetime_str[i], 0, volume[i])
多边形由多边形的角构造而成,需要用到角对象CornerSegment
。
多边形绘制需要先实例化角对象,然后使用set_segment()
方法将角对象添加到多边形中,而不是使用set_point()
方法。
角对象仍然以k线时间为键,以价格为值。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'triangle' |
color | 必填 | 多边形颜色 | rgba(255, 0, 0, 1) |
line_width | 选填 | 宽度 | 8 |
line_dash | 选填 | 虚线间隔 | [3, 3] |
bg_color | 选填 | 背景颜色 | rgba(255, 0, 0, 1) |
代码案例
def on_init(self):
self.triangle = self.Polygon('triangle', '#FFCC00')
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'corner1' |
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 选填 | 值,价格(在Y轴的位置) | 5000 |
color | 选填 | 角颜色 | rgba(255, 0, 0, 1) |
radius | 选填 | 半径 | 10 |
line_width | 选填 | 线宽 | 5 |
type | 选填 | 绘制类型 | 0 填充圆 1 只绘制圆边框 |
代码案例
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
corner1 = self.CornerSegment('corner1', datetime_str[-30], high[-1] * 1.02, color='#99CCFF', radius=10, line_width=5, type=0)
corner2 = self.CornerSegment('corner2', datetime_str[-10], high[-1] * 1.02, color='#99CCFF', radius=10, line_width=5, type=0)
corner3 = self.CornerSegment('corner3', datetime_str[-20], high[-1] * 0.98, color='#99CCFF', radius=10, line_width=5, type=0)
self.triangle.set_segment(corner1)
self.triangle.set_segment(corner2)
self.triangle.set_segment(corner3)
箭头用于绘制箭头线,可以配置箭头的颜色、宽度、长度等属性。
绘制箭头请使用set_start_point()
和set_end_point()
方法,而不是使用set_point()
方法。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'arrow1' |
color | 必填 | 箭头颜色 | rgba(255, 0, 0, 1) |
start | 选填 | 是否绘制开始箭头 | false |
end | 选填 | 是否绘制结束箭头 | true |
angle | 选填 | 箭头的角度 | 30 |
length | 选填 | 箭头的长度 | 20 |
line_width | 选填 | 箭头的粗细 | 4 |
代码案例
def on_init(self):
self.arrow1 = self.ArrowLine('a1', '#FFCC00', start=True, end=True, angle=30, length=20, line_width=4)
self.arrow2 = self.ArrowLine('a2', '#33CC66', start=False, end=True, angle=30, length=20, line_width=4)
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
代码案例
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
self.arrow1.set_start_point(datetime_str[-50], close[-1])
self.arrow1.set_end_point(datetime_str[-40], close[-1] * 1.02)
self.arrow2.set_start_point(datetime_str[-30], close[-1] * 1.01)
self.arrow2.set_end_point(datetime_str[-20], close[-1] * 0.98)
彩色k线可以绘制出不同颜色的k线,用于突出显示某些特定的k线,或者可以覆盖现有k线。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'arrow1' |
default_color | 选填 | 默认颜色 | rgba(255, 0, 0, 1) |
代码案例
def on_init(self):
self.color_kline = self.ColorKline('main_kline')
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
color | 选填 | k线颜色 | rgba(255, 0, 0, 1) |
代码案例
def calculate_all(self, data):
length, datetime_str, open, high, low, close, volume = data
for i in range(len(datetime_str)):
self.color_kline.set_point(self.datetime_list[i], color='rgba(0, 255, 255)')
当我们需要绘制不连续的线条时,可以使用多段线条。
多段线条依赖于子对象LineSegment
,我们可以通过set_segment()
方法添加子对象。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'line1' |
line_width | 选填 | 线宽 | 2 |
line_dash | 选填 | 虚线间隔 | [3, 3] |
代码案例
def on_init(self):
self.multi_line = self.MultiLine('multi_line', line_dash=[3, 3])
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'line_segment1' |
color | 必填 | k线颜色 | rgba(255, 0, 0, 1) |
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
代码案例
if k == 1:
self.line_segment = self.LineSegment(f'green_segment', 'rgb(0, 255, 127)')
else:
self.line_segment = self.LineSegment('red_segment', 'rgb(244,55,50)')
self.line_segment.set_point(self.datetime_list[i], bottom)
self.multi_line.set_segment(self.line_segment)
当我们需要绘制不连续的文字时,可以使用多段文字。
多段文字依赖于子对象TextSegment
,我们可以通过set_segment()
方法添加子对象。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'multi_text1' |
代码案例
def on_init(self):
self.atp_multi_text = self.MultiText('atp_multi_text')
self.ttp_multi_text = self.MultiText('ttp_multi_text')
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'multi_text1' |
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 5000 |
content | 必填 | 文字内容 | "B信号" |
color | 选填 | 文字颜色 | rgb(255,0,0) |
font | 选填 | 字体 | 18px 微软雅黑 |
base_line | 选填 | 上下居中方式 | 0 居中 1 上 2 下 |
y_move | 选填 | 文字Y轴显示位置 | 10 |
代码案例
self.atp_segment = self.TextSegment('atp_segment')
self.ttp_segment = self.TextSegment('ttp_segment')
if self.low_list[i] <= atp_price:
self.atp_segment.set_point(self.datetime_list[i], atp_price, atp_str, base_line=2, font=self.my_font)
if self.close_list[i] >= ttp_price:
self.ttp_segment.set_point(self.datetime_list[i], ttp_price, ttp_str, base_line=2, font=self.my_font)
self.atp_multi_text.set_segment(self.atp_segment)
self.ttp_multi_text.set_segment(self.ttp_segment)
当我们需要绘制不连续的圆点时,可以使用多段圆点。
多段圆点依赖于子对象PointSegment
,我们可以通过set_segment()
方法添加子对象。
实例化参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'multi_point1' |
代码案例
def on_init(self):
self.multi_point = self.MultiPoint('multi_point')
绘制参数
参数 | 必须 | 说明 | 示例 |
---|---|---|---|
name | 必填 | 图形名称 | 'point_segment1' |
color | 必填 | 圆点颜色 | rgba(255, 0, 0, 1) |
key | 必填 | 键名,k线时间 | 2025-01-22 09:45:00 |
value | 必填 | 值,价格(在Y轴的位置) | 50 |
bg_color | 选填 | 背景颜色 | rgba(255, 0, 0, 0.5) |
point_radius | 选填 | 圆点弧度 | 8 |
代码案例
self.purple_point_segment = self.PointSegment('purple_segment', 'rgb(255,0,255)', point_radius=6)
self.green_point_segment = self.PointSegment('green_segment', 'rgb(0,255,0)', point_radius=6)
for i in range(self.length):
status = self.generate_status(close[i])
if status == 1:
self.purple_point_segment.set_point(self.datetime_list[i], self.low_list[i] * 0.9995)
else:
self.green_point_segment.set_point(self.datetime_list[i], self.high_list[i] * 1.0005)
self.multi_point.set_segment(self.purple_point_segment)
self.multi_point.set_segment(self.green_point_segment)
❓:为什么代码编写要规范
A:在代码编写时,一定要注意编写格式的规范性问题,变量、函数和类名在使用情景下,最好使用一眼看上去就能通俗易懂的名称。
例:在编写指标源码时,变量名最好不要使用单个字母命名,如:a、b等,最好使用有意义的名称,例如:收盘价 close;
❓:为什么代码编写会出错
A:在编写技术指标源码时,可能会遇到语法错误、逻辑错误等问题。
例:变量未定义、循环未正确结束等,这些问题会导致程序无法正常运行或结果不准确,可在终端 Output 输出中根据报错类型进行相应优化;
❓:为什么指标文件会编译失败
A:出现编译失败时,先排查是否已保存好编辑后的代码,再查看编辑器右下角是否已选择好相应的语言编译方式
例:Python指标代码选择成了麦语言编译,排除以上情况后,如果依旧编译不通过,可在终端 Output 输出中查看报错类型,并进行相应的错误修正;
❓:为什么编译通过的指标在加载时会出错
A: 指标在编译时,会带入测试数据对用户编写的指标代码进行计算并尝试检查输出结果,虽然这样能够保证指标的基本正确性,但是由于在实际加载中,真实的行情数据(不同的合约,不同的周期)更加复杂,所以编译通过的指标代码可能会在真实的行情数据中出现问题,导致指标加载失败。这时,我们可以通过右键期魔方的期货日志查看
按钮,然后进入logs\strategy_indicator_server
文件夹中,查看indicator.log
文件,查看具体的报错信息,从而进行相应的错误修正。
例:测试数据为5分钟周期,包含有time
字段,而日线周期的指标代码中没有time
字段,导致指标通过编译但是无法在日线周期加载,通过查看indicator.log
文件,我们可以看到报错信息,然后修正对应的代码。
❓:为什么指标在主图不显示
A:编译通过后的指标,如果在行情主图指标面板中没有找到该指标,可尝试在副图中指标面板里查看,这可能与外置参数中的 is_main 函数返回的布尔值(0是主图,1是副图)设置有关
例:当没有声明is_main函数则默认显示在副图指标面板中,可以详见3.3外置参数说明。
❓:为什么导入导出文件时候提示失败?
A:(1)本地电脑上保存策略文件的目标路径问题:
例:在本地电脑上保存策略文件的目标路径:导入导出文件时首先要确定好开发者在本地电脑上保存策略文件的目标路径
(2)目标文件类型错误:
例:在策略页面“指标列表”中进行导入时,错误选择成了”策略文件“
(3)指标文件中的结构性函数缺失:
例:缺失关键结构性函数如缺失 on_init() 函数会存在导入失败的提示,此时您应补充完整缺失的结构性函数
(4)导入了外部库:
例:目前平台不支持导入外部库,目前平台集成了:numpy、pandas,可以使用 self.np
和 self.pd
直接进行调用。Python语言自带的库请在函数内部导入,而不是在头部导入
ps:导出可以选择两种文件:源码文件(.py)和加密文件(.pqmf),导出文件为源码文件是能够被用户看到源码的,而加密文件则不行。因此需要用户根据自己的需求去选择导出两者中的哪一种格式文件
❓:为什么使用外部python库会出错
A:使用了期魔方尚未支持的库
B:没有在文件内部导入库文件
正确的使用方法:
# 外置参数
from pydantic import BaseModel
class Params(BaseModel, validate_assignment=True):
from pydantic import Field
is_main:bool = Field(default=True, title="是否是主图")
fast:int = Field(default=5, title="快线")
slow:int = Field(default=10, title="慢线")
目前在期魔方编写指标支持的库文件及版本如下:
Package Version
------------------------- -----------
aiohappyeyeballs 2.4.8
aiohttp 3.10.10
aiosignal 1.3.2
altgraph 0.17.4
annotated-types 0.7.0
anyio 4.6.0
APScheduler 3.10.4
attrs 25.1.0
certifi 2025.1.31
cffi 1.17.1
charset-normalizer 3.4.1
click 8.1.8
colorama 0.4.6
cryptography 43.0.0
Cython 3.0.11
DBUtils 3.1.0
dnspython 2.7.0
email_validator 2.2.0
fastapi 0.115.0
frozenlist 1.5.0
greenlet 3.1.1
h11 0.14.0
idna 3.10
multidict 6.1.0
mysql-connector-python 9.0.0
numpy 2.1.2
packaging 24.2
paho-mqtt 1.6.1
pandas 2.2.2
pefile 2024.8.26
pip 24.0
propcache 0.3.0
pycparser 2.22
pycryptodome 3.20.0
pydantic 2.9.2
pydantic_core 2.23.4
pyinstaller 6.10.0
pyinstaller-hooks-contrib 2025.1
PyMySQL 1.1.1
Pyro4 4.82
python-dateutil 2.9.0.post0
pytz 2025.1
pywin32-ctypes 0.2.3
requests 2.32.3
serpent 1.41
setuptools 75.8.2
six 1.17.0
sniffio 1.3.1
SQLAlchemy 2.0.35
starlette 0.38.6
typing_extensions 4.12.2
tzdata 2025.1
tzlocal 5.3
urllib3 2.3.0
uvicorn 0.32.0
websockets 12.0
win11toast 0.35
winsdk 1.0.0b10
yarl 1.18.3