用大模型写策略代码的正确提示方式

prompting-llm-for-strategy-code - 智策 SmartQuant
结论先行:把提示词当规格书写:明确的进出场规则、仓位、风控、边界情况。给模板、要代码+测试,永远别信第一稿。把规格写清楚,LLM输出的代码才能直接可用。具体方法与数据详见下文,实操前务必用历史数据回测验证,并结合自身资金与风险承受能力审慎决策。
策略编写 阅读 15 分钟 2026-08-12

让大模型写出可用的策略代码,关键不是措辞技巧,而是给足三样东西:确切的数据结构(列名、dtype、索引类型)、明确的时点约束(禁止未来函数)、以及先写测试的要求。「帮我写个均线策略」这类请求得到的代码几乎一定要重写,因为模型必须靠猜测填补你没说的部分。

本文要点

  • 给出真实的 df.dtypes 输出比用文字描述数据结构有效得多,能消除大部分列名和类型错误。
  • 必须在提示里显式写死时点规则:第 t 根收盘计算信号,第 t+1 根开盘成交。
  • 要求模型先写测试再写实现,测试会把你没说清的假设逼出来。
  • 让模型自查未来函数要给具体清单,问「有问题吗」只会得到风格建议。
  • shift(-1)、全样本 fit、bfill 是三个必须在提示里明令禁止的写法。

模型不是猜不到,是你没说

「帮我写一个双均线策略的回测」——这句话里未定义的信息至少有十二项:数据从哪来、DataFrame 的列名是什么、索引是 DatetimeIndex 还是整数、均线用收盘价还是典型价、周期多少、交叉在哪根 K 线确认、在哪根成交、成交价用开盘还是收盘、手续费多少、滑点怎么算、仓位怎么定、要不要止损。

模型不会追问,它会用训练数据里最常见的组合把这十二项全部填上,然后给你一段语法正确、运行无误、但假设和你完全不同的代码。最糟的是它跑得通,还输出了漂亮的年化数字,你很容易就相信了。

所以提示工程在这里的本质不是话术,而是把规格说明写完整。你把四段式伪代码贴进提示里,模型的自由度就从十二项降到零,输出质量的提升是台阶式的。

有个反直觉的经验:提示越长、约束越多,输出质量越高。这和日常聊天相反。策略代码的提示写到两三百行都很正常,因为其中大部分是数据契约和约束条款,是可以复用的固定模板。

第一要素:把数据契约贴进去

最省事也最有效的做法是直接把真实数据的元信息粘贴进提示,而不是用文字描述。三样东西必贴:df.dtypes 的输出、df.head(3) 的输出、df.index 的类型与时区

为什么重要?因为列名猜错会直接报错(这还算好事,你能发现),而索引类型猜错往往不报错但结果全错。举个真实场景:你的索引是 UTC 的 DatetimeIndex,模型假设是本地时间的整数索引,生成的重采样代码会按错误的边界聚合,回测结果偏差可能达到几个百分点,而代码运行毫无异常。

同样要说明时间戳语义:索引是这根 K 线的开盘时间还是收盘时间。这个区别决定了信号在哪一行、成交在哪一行。交易所之间两种约定都有,模型无法从数据本身判断。

另外贴上数据的时间范围和总行数。这让模型知道样本量级,避免它写出在百万行数据上要跑几小时的逐行 apply。

  • 列名与 dtype:直接粘贴 df.dtypes,不要写「有开高低收量五列」。
  • 索引类型与时区:DatetimeIndex、tz=UTC、单调递增、无重复。
  • 时间戳语义:明确是开盘时间还是收盘时间。
  • 周期与行数:1h 周期,共 26280 行,覆盖 2023-01 至 2026-06。
  • 已知缺陷:是否存在缺口、是否已去重、缺失值如何处理过。

第二要素:把时点约束写成硬规则

未来函数是 AI 生成策略代码时最高频的错误,而且它不会主动避免——训练数据里存在大量带未来函数的教程代码,模型学到的就是那些写法。

解决办法是在提示里写成不可协商的规则条款,用禁令式表述而不是建议式。「注意避免未来函数」这种说法基本无效;「所有特征列必须以 shift(1) 结尾,代码中不得出现 shift 的负数参数」这种就有效,因为它可被机械检查。

有四条禁令覆盖了绝大多数情况。禁止 shift 负参数,这是最直接的未来数据引用。禁止对全样本 fit,StandardScaler、分位数、均值方差都必须用 rolling 或仅用训练段统计量。禁止 bfill 与 interpolate,反向填充等于用未来值补当前值。禁止信号与成交同根 K 线同价格,第 t 根收盘确认的信号只能在第 t+1 根开盘成交。

把这四条写成固定文本块,每次生成代码时都贴上。它们不长,但能挡掉大部分虚高回测。

即使写了约束,模型仍可能违反,尤其在代码较长或多轮修改之后。约束是降低概率,不是保证。生成完必须自己逐行核对时点,或做位移对照实验:把全部特征额外 shift(1) 再跑一次,若收益大幅下降,原代码存在时点泄漏。所有回测数字均为历史数据,不构成收益承诺。

第三要素:要求先写测试

让模型在写实现之前先写测试,是提升代码可靠性性价比最高的一个要求。原因不在测试本身能捕获多少 bug,而在于写测试会强制把模糊假设显式化

当模型被要求为「入场信号」写一个测试时,它必须构造一段输入数据并断言在第几行出现信号。这个断言会暴露它对时点的理解。如果它断言信号出现在交叉那一行、成交也在那一行,你立刻就看到问题了,而不用去读几十行 pandas 代码。

有几类测试值得每次都要求:时点测试(用构造数据验证信号行与成交行相差 1)、边界测试(数据不足窗口长度时应返回 NaN 而不是用部分数据计算)、成本测试(零手续费与真实手续费下收益应有确定差额)、无信号测试(横盘数据下不应产生交易)、未来函数回归测试(同一份数据截断到某个时点,之前的信号序列必须完全不变)。

最后一条尤其有价值,它是未来函数的机械检测手段:如果把数据从第 5000 行截断,前 4999 行的信号应当与用完整数据计算时完全一致。不一致就说明代码用到了后面的数据。这个测试可以自动化,比人工审查可靠得多。

要求模型必写的五类测试
测试验证什么断言写法能挡住的错误
时点测试信号行与成交行相差 1成交 index == 信号 index + 1信号与成交同根 K 线
边界测试窗口不足时不交易行数 < 最长窗口时信号全为 NaN用部分窗口数据算指标
无信号测试横盘不产生交易close 恒定时交易数 == 0条件判断写反或恒真
成本测试成本确实被计入fee=0 的收益严格大于 fee>0硬编码成本为零
截断不变性不存在时点泄漏全量前 5000 行信号 == 截断后信号全样本 fit、shift 负参数、bfill

一个可复用的提示模板

下面是一份完整模板,把你的具体策略填进对应位置即可。它比大多数人写的提示长十倍,但你只需要写一次,之后每次改的只有策略规格那一段。

# ===== ROLE =====
你是一名量化工程师,负责把给定的策略规格实现为可回测的 Python 代码。
严格遵守下面的数据契约与硬约束。规格中未定义的内容,不要自行假设,
列出问题清单让我补充;不要用默认值填空。

# ===== DATA CONTRACT =====
输入是单个 pandas.DataFrame,变量名 df。

df.dtypes:
    open      float64
    high      float64
    low       float64
    close     float64
    volume    float64

df.index:  DatetimeIndex, tz='UTC', 单调递增, 无重复, 无缺口(已预处理)
索引语义:  该行时间戳 = 这根 K 线的【开盘时间】
周期:      1h        行数: 26280        区间: 2023-01-01 ~ 2026-06-30

df.head(2):
                           open     high      low    close     volume
2023-01-01 00:00:00+00:00  16541.8  16558.0  16535.2  16552.4   1043.27
2023-01-01 01:00:00+00:00  16552.4  16571.6  16548.9  16560.1    987.55

# ===== HARD CONSTRAINTS(违反任一条即为错误实现) =====
C1  禁止未来函数。特征只能使用第 t 行及更早的数据。
C2  代码中不得出现 shift 的负数参数(禁止 shift(-1) 等价写法)。
    所有由第 t 行数据构成的特征,用于决策前必须已对齐到决策时点。
C3  信号在第 t 行收盘后确认,成交发生在第 t+1 行的 open 价。
    不得用第 t 行 close 同时作为信号来源和成交价。
C4  禁止任何全样本统计量:不得对整个 df 做 fit / mean / std / quantile
    后再用于早期行。需要标准化时使用 rolling 窗口。
C5  禁止 bfill / interpolate / fillna(method='backfill')。
    窗口不足的行应为 NaN,不得参与交易。
C6  手续费与滑点必须计入,参数从函数入参传入,不得硬编码为 0。
C7  不得调用未在 requirements 中列出的库;不得虚构交易所 SDK 接口。

# ===== ALLOWED LIBS =====
python>=3.10, pandas, numpy  (不要用 talib / backtrader / ccxt)

# ===== STRATEGY SPEC =====
<在此粘贴四段式伪代码:FILTER / ENTRY / SIZING / EXIT / COSTS / ASSUMPTIONS>

# ===== DELIVERABLE(按此顺序输出,分三步) =====
STEP 1  先只输出 pytest 测试文件 test_strategy.py,不要写实现。
        必须包含以下测试,且每个测试自行构造最小输入数据:
        t1  test_signal_and_fill_offset
            构造一段确定会触发入场的数据,断言:
            成交所在行 index == 信号确认行 index + 1
        t2  test_insufficient_window_is_nan
            数据行数 < 最长窗口时,信号列全为 NaN 且交易数为 0
        t3  test_no_trade_on_flat_market
            close 恒定的数据不产生任何交易
        t4  test_cost_reduces_return
            fee=0 与 fee=0.0005 两次运行,后者总收益严格更小
        t5  test_truncation_invariance   # 未来函数机械检测
            用 df 全量算出 signal 序列 A;用 df.iloc[:5000] 算出序列 B;
            断言 A.iloc[:5000] 与 B 完全相等(NaN 位置也相同)

STEP 2  等我确认测试无误后,再输出实现文件 strategy.py。
        函数签名固定为:
        def run_backtest(df, fee=0.0005, slippage=0.0005, **params) -> dict
        返回 {"equity": Series, "trades": DataFrame, "stats": dict}

STEP 3  实现给出后,对照 C1-C7 逐条自查,输出一张表格:
        | 约束 | 代码中的对应行 | 是否满足 | 依据 |
        对每一条给出具体行号和判断理由,不要只回答"满足"。

# ===== 现在只做 STEP 1 =====

模板里有几个设计要点。分三步交付避免模型一口气生成几百行代码,中间任何理解偏差都会被放大;分步骤后你在测试阶段就能拦下来。约束编号(C1-C7)让 STEP 3 的自查可以逐条对应,比笼统提问有效得多。明令禁止虚构 SDK,因为模型很容易写出看起来合理但实际不存在的交易所接口方法。要求列出问题清单而不是自行假设,这一条能把你规格里的漏洞反馈回来。

prompt

让模型自查未来函数:问法决定结果

生成完代码后的审查环节,问法差异导致的效果差距非常大。开放式提问(这段代码有什么问题)会得到一堆变量命名、类型注解、异常处理的建议,真正的时点错误往往被淹没。

有效的做法是要求它做变量级的可用时点追踪:对代码里每一个中间变量,回答两个问题——这个变量在第 t 行的值,用到了哪些行的数据;这个值在真实交易中的哪个时刻才能确定。让它输出一张表。这种问法把审查从主观判断变成了机械核对,模型很难糊弄过去。

另一种有效手段是对抗式提问:假设这段代码存在未来函数,请找出最可能的位置并说明理由。给定「存在问题」的前提,模型不会敷衍地回答「看起来没问题」,会真的去找。这个技巧对代码审查普遍有效。

第三种是要求它自己构造反例:写一段能证明该代码存在时点泄漏的最小测试数据。如果它写不出反例,代码正确的概率就高一些;如果它写出来了,你直接就拿到了 bug 复现。

差 prompt 与好 prompt 对照
场景差写法好写法差异原因
描述数据有一份 BTC 的 K 线数据粘贴 df.dtypes + head(2) + 索引 tz 与语义消除列名、dtype、时区、时间戳语义四类猜测
描述策略写个双均线策略粘贴完整四段式伪代码,含成交时点与成本把十余项未定义参数降为零
防未来函数注意不要用到未来数据列 C1-C7 编号禁令,明确禁止 shift 负参数与全样本 fit禁令可被机械检查,建议无法验证
交付方式把完整回测代码给我分三步:先测试、再实现、最后逐条自查早期拦截理解偏差,避免长代码整体重写
代码审查这段代码有问题吗逐变量追踪可用时点并输出表格把主观判断转为机械核对
缺信息时(不说)规格未定义的列成问题清单,不要自行假设阻止模型用训练集常见值静默填空
库依赖用合适的库实现明确列出允许的库,禁止虚构 SDK 方法避免不存在的接口与不必要依赖
修改代码改一下止损逻辑给出当前代码 + 只改 EXIT 段 + 保持其余不变并复跑测试防止模型顺手重写无关部分引入回归
多轮修改是隐藏风险点。模型在第五轮修改时,很可能已经忘了第一轮的约束条款。可靠做法是每轮都重贴约束块,并在改完后重跑全部测试,特别是那条截断不变性测试。把它接到 CI 里比靠记性可靠。相关的实现层陷阱见向量化 vs 逐 K 线,完整流程见量化学院

什么是常见问题?

提示写这么长,模型能记住全部约束吗?

长上下文里靠前和靠后的内容遵守率较高,中间部分容易被忽略。实践上把硬约束放在开头、把「现在只做 STEP 1」这类当前指令放在结尾效果较好。更可靠的是不依赖记忆——用测试和自查表做机械验证,尤其是截断不变性测试。

要不要让模型直接接数据源自己跑回测?

不建议一步到底。让它写纯函数(输入 DataFrame,输出结果字典),数据获取和结果检查由你控制。这样测试更容易写,也避免它虚构交易所接口。数据获取部分见交易所 API 与数据获取基础一篇。

模型说它已经检查过没有未来函数,可信吗?

不能直接采信。它的检查是对文本的推理,无法执行代码验证。把结论落到可运行的检查上:截断不变性测试、全特征额外位移一格的对照回测、以及逐行核对成交时点。三项都通过才算过关。

同一个提示为什么两次输出不一样?

生成本身带随机性,这也是必须有测试的原因——测试把「输出是否可接受」变成确定判断,而不是每次都靠人读代码。约束越具体、规格越完整,两次输出的行为差异就越小,尽管代码写法可能不同。

风险提示:量化交易同样存在亏损风险。本站所有策略、信号、收益数据均为历史回测数据,不构成收益承诺,也不构成投资建议。加密货币波动剧烈,请先用小资金验证,并遵守所在地法律法规。

📺 相关视频

TradingView Automated Trading with AI
TradingView Automated Trading with AI
AI自动化交易演示(OctoBot)
📚 参考来源
Google AI 优化指南(2026-06更新)

developers.google.com/search/docs/fundamentals/ai-optimization-guide — Google官方对生成式AI搜索优化的指引。

SE Ranking AI引用研究

seranking.com/blog/ai-search-research/ — AI答案引用来源研究(44%引用来自页面前30%)。

量化分析维基百科

en.wikipedia.org/wiki/Quantitative_analysis_(finance) — 量化分析的基础定义与学术脉络。