问答交流

【平台使用】初上手问题反馈

由bqfmyxm1创建,最终由bqfmyxm1 被浏览 2 用户

我正在使用标普500的数据写一个简单的动量因子策略,运行时一直打印以下日志:\n

问题一

日志 103 条 ▼
[2026-03-18 15:40:22] INFO: bigtrader.v35 开始运行 ..[2026-03-18 15:40:22] INFO: pybacktest run 2020-12-31 ~ 2024-12-31, , equity, instruments=501[2026-03-18 15:40:22] INFO: bigtrader module V2.2.0[2026-03-18 15:40:22] INFO: bigtrader engine v0.1.0.post9+g6d7300d 2026-02-10[2026-03-18 15:40:23] INFO: pybacktest read history data done, run backtest ...order_target_percent: price is null for 'NVDA.O' at '2020-12-31 15:00:00'
order_target_percent: price is null for 'BBWI.N' at '2020-12-31 15:00:00'
order_target_percent: price is null for 'MRNA.O' at '2020-12-31 15:00:00'
order_target_percent: price is null for 'ETSY.O' at '2020-12-31 15:00:00'
order_target_percent: price is null for 'ENPH.O' at '2020-12-31 15:00:00'
order_target_percent: price is null for 'NVDA.O' at '2021-01-04 15:00:00'
order_target_percent: price is null for 'MRNA.O' at '2021-01-04 15:00:00'
order_target_percent: price is null for 'ETSY.O' at '2021-01-04 15:00:00'
order_target_percent: price is null for 'GNRC.N' at '2021-01-04 15:00:00'
order_target_percent: price is null for 'ENPH.O' at '2021-01-04 15:00:00'
order_target_percent: price is null for 'NVDA.O' at '2021-01-05 15:00:00'
order_target_percent: price is null for 'BBWI.N' at '2021-01-05 15:00:00'
order_target_percent: price is null for 'MRNA.O' at '2021-01-05 15:00:00'
order_target_percent: price is null for 'ETSY.O' at '2021-01-05 15:00:00'
order_target_percent: price is null for 'ENPH.O' at '2021-01-05 15:00:00'
order_target_percent: price is null for 'BBWI.N' at '2021-01-06 15:00:00'
order_target_percent: price is null for 'MRNA.O' at '2021-01-06 15:00:00'
order_target_percent: price is null for 'ETSY.O' at '2021-01-06 15:00:00'
order_target_percent: price is null for 'GNRC.N' at '2021-01-06 15:00:00'
order_target_percent: price is null for 'ENPH.O' at '2021-01-06 15:00:00'

我的代码如下:\n

import os
from bigmodule import M
import dai

# ────────────────────────────────────────────────
# 计算经典 12-1 动量(过去252日收益 - 过去21日收益)
# ────────────────────────────────────────────────
df = dai.query("""
    WITH sp500_components AS (
        -- 历史标普500成分股 + 有效期 carry forward
        SELECT 
            member_code AS instrument,
            date AS start_date,
            LEAD(date, 1, '2099-12-31') OVER (PARTITION BY member_code ORDER BY date) AS end_date
        FROM us_stock_index_component
        WHERE name = '标准普尔500指数'
          AND date <= '2024-12-31'
    ),
    prices AS (
        SELECT 
            b.date,
            b.instrument,
            b.close,
            LAG(b.close, 21)  OVER (PARTITION BY b.instrument ORDER BY b.date) AS close_21d_ago,
            LAG(b.close, 252) OVER (PARTITION BY b.instrument ORDER BY b.date) AS close_252d_ago
        FROM us_stock_bar1d b
        INNER JOIN sp500_components c 
            ON b.instrument = c.instrument 
            AND b.date >= c.start_date 
            AND b.date < c.end_date
        WHERE b.date BETWEEN '2020-01-01' AND '2024-12-31'
    ),
    momentum AS (
        SELECT 
            date,
            instrument,
            CASE 
                WHEN close_21d_ago > 0 AND close_252d_ago > 0 
                     AND close_21d_ago IS NOT NULL 
                     AND close_252d_ago IS NOT NULL
                THEN (close / close_252d_ago - 1) - (close / close_21d_ago - 1)
                ELSE NULL
            END AS score  -- mom_12_1
        FROM prices
    )
    SELECT 
        date,
        instrument,
        score
    FROM momentum
    WHERE score IS NOT NULL
    ORDER BY date, score DESC, instrument
""").df()

def initialize(context):
    from bigtrader.finance.commission import PerOrder
    
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    
    context.hold_count = 5
    context.weight = 1.0 / context.hold_count

    context.df = df


def handle_data(context, data):
    # 当前日期字符串
    dt_str = data.current_dt.strftime("%Y-%m-%d")
    
    # 取出当天所有股票的 score,按降序已排好
    current_df = context.df[context.df["date"] == dt_str]
    
    if current_df.empty:
        return
    
    # 取前 hold_count 只(已按 score DESC 排序)
    to_hold_df = current_df.head(context.hold_count)
    to_hold_instruments = set(to_hold_df["instrument"])
    
    # -----------------------
    # 卖出:不在新列表里的全部清仓
    # -----------------------
    current_positions = context.get_account_positions()
    for instrument in list(current_positions.keys()):
        if instrument not in to_hold_instruments:
            context.order_target(instrument, 0)
    
    # -----------------------
    # 买入/调整:目标等权
    # -----------------------
    for instrument in to_hold_instruments:
        context.order_target_percent(instrument, context.weight)

# 回测引擎调用
m5 = M.bigtrader.v35(
    data=df,                    # 传入预计算好的 df
    initialize=initialize,
    handle_data=handle_data,
    capital_base=1000000,
    frequency="daily",
    order_price_field_buy="open",
    order_price_field_sell="close",
    m_cached=True,            
)

我理解order_target_percent这个函数就是按照order_price_field_buy设置的价格买入context.weight比例的指定标的。不太明白为啥会出现这个问题。


问题二

我搞了个可视化版本的,但是又出现了新的错误

from bigmodule import M, I

# <aistudiograph>

# @param(id="m3", name="initialize")
# 交易引擎:初始化函数,只执行一次
def m3_initialize_bigquant_run(context):
    from bigtraderv1.finance.commission import PerOrder
 
    # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0008, min_cost=5))
    
    context.target_hold_count = 5
    
    context.weight = 1.0 / context.target_hold_count
# @param(id="m3", name="before_trading_start")
# 交易引擎:每个单位时间开盘前调用一次。
def m3_before_trading_start_bigquant_run(context, data):
    # 盘前处理,订阅行情等
    pass

# @param(id="m3", name="handle_tick")
# 交易引擎:tick数据处理函数,每个tick执行一次
def m3_handle_tick_bigquant_run(context, tick):
    pass

# @param(id="m3", name="handle_data")
def m3_handle_data_bigquant_run(context, data):
    import pandas as pd
 
    # 下一个交易日不是调仓日,则不生成信号
    if not context.rebalance_period.is_signal_date(data.current_dt.date()):
        return
 
    # 从传入的数据 context.data 中读取今天的信号数据这个
    today_df = context.data[context.data["date"] == str(data.current_dt.date())]
 
    # 卖出不在目标持有列表中的股票
    for instrument in sorted(set(context.get_account_positions().keys()) - set(today_df["instrument"])):
        context.order_target_percent(instrument, 0)
    # 买入目标持有列表中的股票
    for i, x in today_df.iterrows():
        context.order_target_percent(x.instrument, context.weight)

# @param(id="m3", name="handle_trade")
# 交易引擎:成交回报处理函数,每个成交发生时执行一次
def m3_handle_trade_bigquant_run(context, trade):
    pass

# @param(id="m3", name="handle_order")
# 交易引擎:委托回报处理函数,每个委托变化时执行一次
def m3_handle_order_bigquant_run(context, order):
    pass

# @param(id="m3", name="after_trading")
# 交易引擎:盘后处理函数,每日盘后执行一次
def m3_after_trading_bigquant_run(context, data):
    pass

# @module(position="-251,-76", comment="""""", comment_collapsed=True)
m1 = M.dai_sql.v1(
    sql="""WITH sp500_components AS (
    -- 标普500成分股 + 生效区间
    SELECT 
        member_code AS instrument,
        date AS start_date,
        LEAD(date, 1, '2099-12-31') OVER (
            PARTITION BY member_code 
            ORDER BY date
        ) AS end_date
    FROM us_stock_index_component
    WHERE name = '标准普尔500指数'
),

prices AS (
    -- 价格 + 21日 / 252日滞后
    SELECT 
        b.date,
        b.instrument,
        b.close,
        LAG(b.close, 21)  OVER (
            PARTITION BY b.instrument 
            ORDER BY b.date
        ) AS close_21d_ago,
        LAG(b.close, 252) OVER (
            PARTITION BY b.instrument 
            ORDER BY b.date
        ) AS close_252d_ago
    FROM us_stock_bar1d b
    INNER JOIN sp500_components c 
        ON b.instrument = c.instrument 
        AND b.date >= c.start_date 
        AND b.date < c.end_date
),

momentum AS (
    -- 12-1 动量(排除最近1个月)
    SELECT 
        date,
        instrument,
        CASE 
            WHEN close_21d_ago > 0 
             AND close_252d_ago > 0 
             AND close_21d_ago IS NOT NULL 
             AND close_252d_ago IS NOT NULL
            THEN (close / close_252d_ago - 1) 
               - (close / close_21d_ago - 1)
            ELSE NULL
        END AS score
    FROM prices
),

ranked AS (
    -- 每天按动量排序
    SELECT 
        date,
        instrument,
        score,
        ROW_NUMBER() OVER (
            PARTITION BY date 
            ORDER BY score DESC
        ) AS rank
    FROM momentum
    WHERE score IS NOT NULL
)

-- 只取前5
SELECT 
    date,
    instrument,
    score
FROM ranked
WHERE rank <= 5
ORDER BY date, rank""",
    extract_data=False,
    m_name="""m1"""
)

# @module(position="-259,54", comment="""""", comment_collapsed=True)
m2 = M.extract_data_dai.v20(
    sql=m1.data,
    start_date="""2020-01-01""",
    start_date_bound_to_trading_date=True,
    end_date="""2026-03-01""",
    end_date_bound_to_trading_date=True,
    before_start_days=300,
    keep_before=False,
    debug=False,
    m_name="""m2"""
)

# @module(position="-327,164", comment="""""", comment_collapsed=True)
m3 = M.bigtrader.v58(
    options_data=m2.data,
    start_date="""2020-01-01""",
    end_date="""2026-03-01""",
    initialize=m3_initialize_bigquant_run,
    before_trading_start=m3_before_trading_start_bigquant_run,
    handle_tick=m3_handle_tick_bigquant_run,
    handle_data=m3_handle_data_bigquant_run,
    handle_trade=m3_handle_trade_bigquant_run,
    handle_order=m3_handle_order_bigquant_run,
    after_trading=m3_after_trading_bigquant_run,
    capital_base=1000000,
    frequency="""daily""",
    product_type="""股票""",
    rebalance_period_type="""交易日""",
    rebalance_period_days="""1""",
    rebalance_period_roll_forward=True,
    backtest_engine_mode="""标准模式""",
    before_start_days=0,
    volume_limit=1,
    order_price_field_buy="""open""",
    order_price_field_sell="""close""",
    benchmark="""沪深300指数""",
    plot_charts="""全部显示""",
    debug=False,
    backtest_only=False,
    m_name="""m3"""
)
# </aistudiograph>

出现以下错误:\n

[2026-03-18 18:16:24] INFO: dai_sql.v1 开始运行 ..[2026-03-18 18:16:24] INFO: dai_sql.v1 命中缓存[2026-03-18 18:16:24] INFO: dai_sql.v1 运行完成 [0.035s].[2026-03-18 18:16:24] INFO: extract_data_dai.v20 开始运行 ..[2026-03-18 18:16:25] INFO: extract_data_dai.v20 命中缓存[2026-03-18 18:16:25] INFO: extract_data_dai.v20 运行完成 [0.028s].[2026-03-18 18:16:25] INFO: bigtrader.v58 开始运行 ..[2026-03-18 18:16:25] INFO: pybacktest run  ~ , , equity, instruments=0
您可以去社区论坛问答交流板块反馈咨询 去发帖>>
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/modulecache.py:106, in get_source(***failed resolving arguments***)

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/modulecache.py:84, in get_source_lines(obj, source_provider)

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/modulecache.py:25, in find_source(obj, loader)

AttributeError: 'NoneType' object has no attribute 'get_source'

During handling of the above exception, another exception occurred:

InvalidInputException                     Traceback (most recent call last)
Cell In[5], line 153
    140 m2 = M.extract_data_dai.v20(
    141     sql=m1.data,
    142     start_date="""2020-01-01""",
   (...)
    149     m_name="""m2"""
    150 )
    152 # @module(position="-327,164", comment="""""", comment_collapsed=True)
--> 153 m3 = M.bigtrader.v58(
    154     options_data=m2.data,
    155     start_date="""2020-01-01""",
    156     end_date="""2026-03-01""",
    157     initialize=m3_initialize_bigquant_run,
    158     before_trading_start=m3_before_trading_start_bigquant_run,
    159     handle_tick=m3_handle_tick_bigquant_run,
    160     handle_data=m3_handle_data_bigquant_run,
    161     handle_trade=m3_handle_trade_bigquant_run,
    162     handle_order=m3_handle_order_bigquant_run,
    163     after_trading=m3_after_trading_bigquant_run,
    164     capital_base=1000000,
    165     frequency="""daily""",
    166     product_type="""股票""",
    167     rebalance_period_type="""交易日""",
    168     rebalance_period_days="""1""",
    169     rebalance_period_roll_forward=True,
    170     backtest_engine_mode="""标准模式""",
    171     before_start_days=0,
    172     volume_limit=1,
    173     order_price_field_buy="""open""",
    174     order_price_field_sell="""close""",
    175     benchmark="""沪深300指数""",
    176     plot_charts="""全部显示""",
    177     debug=False,
    178     backtest_only=False,
    179     m_name="""m3"""
    180 )
    181 # </aistudiograph>

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/modules.py:28, in __call__(self, **kwargs)

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/moduleinvoker.py:218, in module_invoke(name, version, kwargs)

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/moduleinvoker.py:181, in _module_invoke(name, version, kwargs)

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/bigmodule/moduleinvoker.py:44, in _module_run(module, kwargs)

File dist/build/bigtrader/v58/__init__.py:444, in v58.run()

File dist/build/bigtrader/v58/__init__.py:222, in v58._run()

File dist/build/bigtrader/v58/__init__.py:208, in v58._run_backtest()

File dist/build/bigtrader/v58/core/pybacktest/__init__.py:176, in v58.core.pybacktest.BigQuantModule.__init__()

File /opt/pyenv/versions/3.11.8/lib/python3.11/site-packages/vectortrader/rebalanceperiod.py:48, in build(self, start_date, end_date, extra_days)

File /var/app/enabled/dai/_telemetry.py:128, in wrapper(*args, **kwargs)

File /var/app/enabled/dai/_functions.py:126, in df(self)

InvalidInputException: Invalid Input Error: Attempting to execute an unsuccessful or closed pending query result
Error: Invalid Input Error: Failed to cast value: Could not convert string '' to INT64

这个我也是搞不明白,哪里需要类型转换而且转换失败了

\

问题三

可视化编辑的使用,以我这个可视化代码为例:

  1. m2数据抽取的用处是啥,我直接在m1 DAI SQL的SQL中过滤不是最高效的吗?我看到DAI SQL的帮助文档里写的输出端是“data:data,输出(SQL文件)”,这到底是输出了数据本身,还是只是把SQL传给了下游,这两种情况对于m2的处理逻辑就不一样了。
  2. m2和m3都要填写开始时间和结束时间,重复填写的意义是啥?如果只写m2的,那么m3会自动取m2的吗?
  3. 节点直接传递数据的方式不明:现在看起来的方式是数据抽取DAI会输出一个data变量,然后在context中直接使用这个变量?

\

问题四

调试功能似乎完全没法用。

{link}