Home >  > 极智量化踩坑

极智量化踩坑

一、关于k线数据
(1)时间
极智的4小时行情和文华财经一样。但是k线上显示的时间不一样。
比如:7.31 21:00-21:01这一根k线,在文华财经上显示为07312100
在极智显示为07312101
1小时k线也是这样。

和快期行情k线是不相同的。
以甲醇为例,交易时间是这样的:
夜盘‌:21:00—23:00
‌日盘‌:09:00—10:15、10:30—11:30、13:30—15:00(10:15—10:30 为午休休市)
全天交易时间为5小时45分钟。

现在是2026.Aug.01,极智量化4小时图最后一根k显示时间为:2026.8.3 11:15,得知k线显示的时间是结束时间。
前1根显示:7.31 15:00 即:11点15分钟+下午1.5小时,所以这个4h不足4小时,只有1小时45分。(通过文华财经核对,的确是这样,极知这根k线的高低点就是文华11:15-15:00时间段的高低点。)
再前1根显示:7.31 11:15 即晚上2小时+9:00-10:00+10点的45分钟+11点的15分钟。

(2)主连合约
极智的rb2610合约和文华的数据一样,
但是螺纹主连的数据不一样。
比如20260309 这天。

二、关于SetBarInterval(context.contract, 'D', 1, 'A', isTrigger=False)

isTrigger 设置非基准合约是否触发,基准合约设置该参数无效,默认值为 False

由于 isTrigger 对基准合约((即策略主图显示的合约))无效,无论设置为 True 还是 False,日线数据都会正常触发 handle_data()。

所以这个参数主要在多合约策略中有意义。

三、关于多周期
SetBarInterval设定的第一个周期就是handledate的调用周期。
比如下面的代码,显示的结果是:

[2026\7\31 7.34.52] [INFO][15]: 当前触发时间:20260729 150000
[2026\7\31 7.34.52] [INFO][15]: 240分钟线 - 时间: 20260729 0.150000 开盘: 3071.00 收盘: 3062.00
[2026\7\31 7.34.52] [INFO][15]: 日线 - 时间: 20260729 0.000000 开盘: 3060.00 收盘: 3062.00
[2026\7\31 7.34.52] [INFO][15]: 当前触发时间:20260730 111500
[2026\7\31 7.34.52] [INFO][15]: 240分钟线 - 时间: 20260730 0.111500 开盘: 3061.00 收盘: 3021.00
[2026\7\31 7.34.52] [INFO][15]: 日线 - 时间: 20260730 0.000000 开盘: 3061.00 收盘: 3014.00
[2026\7\31 7.34.52] [INFO][15]: 当前触发时间:20260730 150000
[2026\7\31 7.34.52] [INFO][15]: 240分钟线 - 时间: 20260730 0.150000 开盘: 3021.00 收盘: 3014.00
[2026\7\31 7.34.52] [INFO][15]: 日线 - 时间: 20260730 0.000000 开盘: 3061.00 收盘: 3014.00
[2026\7\31 7.34.52] [INFO][15]: 当前触发时间:20260731 111500
[2026\7\31 7.34.52] [INFO][15]: 240分钟线 - 时间: 20260731 0.111500 开盘: 3014.00 收盘: 3010.00
[2026\7\31 7.34.52] [INFO][15]: 日线 - 时间: 20260731 0.000000 开盘: 3014.00 收盘: 3010.00

即每4小时调用一次,但同时会更新日线的k线数据。

测试代码:

import talib

def initialize(context):
    context.contract = Symbol()
    
    SetBarInterval(context.contract, 'M', 240, 'A')
    SetBarInterval(context.contract, 'D', 1, 'A')

def handle_data(context):
    contract = context.contract

    # 打印当前触发时间(来自 context)
    ts = context.dateTimeStamp()
    ts_str = str(ts)
    date_str = ts_str[:8]
    time_str = ts_str[8:14]
    LogInfo("当前触发时间:%s %s" % (date_str, time_str))

    # 240分钟线数据
    open_240 = Open(contract, 'M', 240)[-1]
    close_240 = Close(contract, 'M', 240)[-1]
    date_240 = Date(contract, 'M', 240)      # 整数,如 20260731
    time_240 = Time(contract, 'M', 240)      # 浮点数,如 0.093400
    LogInfo("240分钟线 - 时间: %d %.6f  开盘: %.2f  收盘: %.2f" % (date_240, time_240, open_240, close_240))

    # 日线数据
    open_D = Open(contract, 'D', 1)[-1]
    close_D = Close(contract, 'D', 1)[-1]
    date_D = Date(contract, 'D', 1)
    time_D = Time(contract, 'D', 1)
    LogInfo("日线 - 时间: %d %.6f  开盘: %.2f  收盘: %.2f" % (date_D, time_D, open_D, close_D))

如果调换SetBarInterval顺序,则结果就是:

[2026\7\31 8.0.10] [INFO][20]: 当前触发时间:20260729 000000
[2026\7\31 8.0.10] [INFO][20]: 240分钟线 - 时间: 20260728 0.150000 开盘: 3059.00 收盘: 3060.00
[2026\7\31 8.0.10] [INFO][20]: 日线 - 时间: 20260729 0.000000 开盘: 3060.00 收盘: 3062.00
[2026\7\31 8.0.10] [INFO][20]: 当前触发时间:20260730 000000
[2026\7\31 8.0.10] [INFO][20]: 240分钟线 - 时间: 20260729 0.150000 开盘: 3071.00 收盘: 3062.00
[2026\7\31 8.0.10] [INFO][20]: 日线 - 时间: 20260730 0.000000 开盘: 3061.00 收盘: 3014.00
[2026\7\31 8.0.10] [INFO][20]: 当前触发时间:20260731 000000
[2026\7\31 8.0.10] [INFO][20]: 240分钟线 - 时间: 20260730 0.150000 开盘: 3021.00 收盘: 3014.00
[2026\7\31 8.0.10] [INFO][20]: 日线 - 时间: 20260731 0.000000 开盘: 3014.00 收盘: 3010.00

四、关于回测与成交。
极智量化在回测时使用主连合约,但成交(下单)时使用的是该主连当前映射到的具体合约。

五、关于ema

def calc_ema(price, period):
    """计算指数移动平均(EMA)"""
    if len(price) < period:
        return np.array([])
    ema = np.zeros(len(price))
    alpha = 2.0 / (period + 1)
    ema[0] = price[0]
    for i in range(1, len(price)):
        ema[i] = alpha * price[i] + (1 - alpha) * ema[i-1]
    return ema

alpha 的值只和period有关。

简单移动平均(SMA)和指数移动平均(EMA)在算法本质上的核心区别。

简单来说:

MA5(简单移动平均):是“近视眼”,只看最近5天,第6天的数据直接扔掉,没有任何影响。

EMA5(指数移动平均):是“记忆体”,理论上会用到从第1天到今天的全部数据,但每一天的权重不同。

因为 EMA 的公式是递归的(今天的值依赖于昨天的值,昨天的值依赖于前天的值……)。
继续往前推,你会发现第1天的价格(P(第1天))虽然系数已经变得非常非常小(0.667^100),但它永远不等于零,会一直以极微弱的权重存在于今天的EMA值中。

目前市场上主流的趋势跟踪系统(如海龟交易法则的变种、双均线系统)大多采用 EMA 或加权均线,因为它能更好地体现“当前价格热度”。既然你的策略逻辑是基于“趋势证伪”(价格跌破EMA5离场),那么离场触发价格用 EMA 会比 MA 更早一步反映资金出逃的迹象,帮你保住更多利润。除非你在实盘中明显感觉到 EMA 让止损过于频繁(噪音过大),否则不建议换回 MA。

六、ParabolicSAR
根据提供的文档(“极智量化API手册.pdf”第120-121页):

语法:ParabolicSAR(numpy.array high, numpy.array low, float afstep, float aflimit)

参数:高、低、加速因子步长、加速因子限制。

返回值:四个值,均为数值型numpy.array。

第一个值:oParClose - 当前bar的停损值。

第二个值:oParOpen - 下一Bar的停损值。

第三个值:oPosition - 输出建议的持仓状态,1 - 买仓,-1 - 卖仓。

第四个值:oTransition - 输出当前Bar的状态是否发生反转,1或-1为反转,0为保持不变。

另外,回想一下标准SAR(抛物线转向)指标在技术分析中的工作原理:它跟踪趋势,停损点随价格移动(加速因子增加),当价格穿越SAR值时趋势反转。

七、关于实盘和回测
1.极智量化的实盘和回测严重割裂。
比如AI写的成交价用的是open_h[-1],我以为要用close_h[-1]。

但是实盘时,T2(下午3点):你看到信号成立,立刻以 close_h[-1] 的价格挂出限价买单。

但是,交易所已经收盘了(或者这根4小时K线刚刚收线)。你的买单只能挂到下一根K线(K线C)的开盘时段去成交。

2.我建议你采取的行动

考虑到你当前策略的复杂度,我强烈建议你同时做两件事:

把 SetOrderWay(2) 改为 SetOrderWay(1)(实时发单),这是根本解决“信号延迟”问题的最佳手段。

在代码中增加 pending_signal 状态存储,作为“万一因网络或不可抗力没成交”的二次保险。双重保障下,你的策略几乎不可能出现仓差。

3.如何保存订单状态
极智量化中,正确的“跨重启持久化”方案

既然你需要把“今天收盘产生的信号”存下来,等到“明天开盘”再执行,那你就必须把数据存到内存以外的地方。极智量化提供了两种官方途径:
方案A(推荐):使用 SetGlobalVar / GetGlobalVar(极智量化原生函数)

这是极智量化专门为“跨K线、跨重启、甚至跨策略”保存数据设计的函数。数据会被保存在客户端的本地文件系统中,重启策略也不会丢失。

# ----- 在 initialize 中初始化(但不要重置已有数据) -----
def initialize(context):
    # 读取之前存储的信号,检查是否还存在
    pending_direction = GetGlobalVar("pending_direction")
    pending_price = GetGlobalVar("pending_price")
    
    if pending_direction is not None and pending_price is not None:
        LogInfo("检测到上次未执行的信号,方向:{},价格:{}".format(pending_direction, pending_price))
        # 你可以在这里立即执行补单逻辑,或者将其赋值给 context 继续使用
        context.pending_signal = {'direction': pending_direction, 'price': pending_price}
    else:
        context.pending_signal = None

# ----- 在 handle_data 中存储信号 -----
if 信号成立 and context.position == 0:
    # 存储到本地持久化存储
    SetGlobalVar("pending_direction", 1)   # 1为多,-1为空
    SetGlobalVar("pending_price", open_h[-1] + slip)
    
    LogInfo("信号已持久化存储,即使重启策略也不会丢失!")

# ----- 执行完信号后,必须清理 -----
if context.pending_signal is not None:
    # ... 执行下单逻辑 ...
    # 下单成功后,清空持久化存储,防止重复执行
    SetGlobalVar("pending_direction", None)
    SetGlobalVar("pending_price", None)
    context.pending_signal = None

方案B(备选):读写本地文件(JSON格式)

如果你想更灵活地控制数据格式,也可以直接用 Python 的 json 库写文件。

import json
import os

def save_pending_signal(direction, price):
    data = {'direction': direction, 'price': price}
    with open('pending_signal.json', 'w') as f:
        json.dump(data, f)

def load_pending_signal():
    if os.path.exists('pending_signal.json'):
        with open('pending_signal.json', 'r') as f:
            return json.load(f)
    return None

暧昧帖

本文暂无标签