扩展函数开发
当 talib 和平台内置的 EsTalib 都满足不了你的需求时,就需要自己写扩展函数。极智量化的扩展函数开发核心只有一句话:把 NumericSeries 当作指标的「记忆」,逐 Bar 递推。本章讲清封装思路、导入方式和一个完整范例。
什么时候要写扩展函数
- 指标需要中间状态(上一根的 EMA、SAR 的加速因子等),不能用一次公式算完。
- 你有一套自己的交易逻辑想复用到多个策略。
- 想把一段重复的计算抽成函数,让策略主逻辑更清爽。
自定义函数的基本写法
Python 自定义函数遵循标准语法。用 def 定义、圆括号列参数、冒号起函数体、return 返回值:
def my_func(parameters):
"""文档字符串,可选"""
# 函数体
return result
无状态的函数最简单,比如一个「阳线实体占整根 K 线的比例」:
def body_ratio(o, c, h, l):
total = h - l
if total == 0:
return 0
return abs(c - o) / total
在 handle_data 里直接调用即可。
封装有状态指标
真正的扩展函数往往有状态。秘诀就是用 NumericSeries 保存上一次的值,每根 Bar 调用一次、写回一次。下面把一个自定义的「自适应均线」封装成函数:
from EsSeries import NumericSeries
def U_MyAdaptive(X, data, period):
"""
自适应均线示例:趋势强时贴近价格,震荡时更平滑
X —— 保存历史值的 NumericSeries(外部维护)
data—— 价格序列,如 Close()
period —— 平滑周期
"""
factor = 2 / (period + 1)
if len(data) == 1:
ret = data[-1]
else:
ret = X[-1] + factor * (data[-1] - X[-1])
return ret
在策略里这样用它:
from EsSeries import NumericSeries
# 假设上面的函数已放在同目录 mylib.py
from mylib import U_MyAdaptive
ama = NumericSeries()
period = 15
def handle_data(context):
if len(Close()) == 0:
return
ama[-1] = U_MyAdaptive(ama, Close(), period)
PlotNumeric('ama', ama[-1], RGB_Purple())
这是极智量化扩展函数的通用范式:函数本身无状态,状态全靠外部传入的
NumericSeries承载。EsTalib里的U_EMA / U_SMA都是这么写的。
封装成类:多输出指标
如果指标有多个输出(比如 SAR 同时给出价格和方向),像 EsTaClass.UC_SAR 那样封装成类更清晰:在 __init__ 里为每个输出建一条 NumericSeries,方法内部维护它们并返回结果。详见 EsTalib技术指标库 的 UC_SAR 示例。
导入个人库
把写好的函数文件(如 mylib.py)放到用户策略目录下,在策略里用相对路径导入。假设 mylib.py 和你的策略 test.py 在同级目录:
from mylib import U_MyAdaptive # 同级目录,直接导入
如果个人库在子目录或上层目录,按相对量化工作路径补全,例如:
from Quant.Strategy.用户策略.ALib import ATalib # 下级目录
from Quant.Strategy import CTalib # 上层模块
经验:在用户策略下新建文件夹单独存放个人库,便于区分和管理。
自定义函数的使用要点
- 先判长度:函数开头用
len(data)检查数据是否就绪,避免回测初期算出异常值。 - 状态写回:每根 Bar 一定要把递推结果写回
X[-1],否则下一根读不到。 - 越界安全:
NumericSeries已做越界保护,但传入的data(如Close())取下标前最好确认长度。 - 参数优化兼容:若想让参数参与平台参数优化,把可调参数写进
g_params字典,而不是写成普通全局变量——后者在参数优化时不会随新值更新。
小结
扩展函数开发的核心是「函数无状态 + 外部 NumericSeries 承载状态」;有多个输出就封装成类;个人库放用户策略目录按相对路径导入。写好自己的指标库后,下一步进入实盘核心:账户与下单示例。