外观
策略
约 26997 字大约 90 分钟
简介
Mine Script® 策略是一种专用脚本,可在历史数据和实时行情上模拟交易,允许用户对其交易系统进行回测和前瞻测试。策略脚本具备与指标脚本相同的多数功能,并提供下达、修改和取消模拟订单以及分析绩效结果的能力。
当脚本使用 strategy() 函数作为声明语句时,即可访问 strategy.* 命名空间。该命名空间包含众多用于模拟订单和获取关键策略信息的函数及变量,同时会在专门的策略测试器选项卡中显示相关信息及模拟绩效结果。
一个简单的策略示例
以下脚本是一个简单策略,当两条移动平均线交叉时模拟建立多头或空头头寸。当fastMA上穿slowMA时,它会下达"买入"市价订单建立多头头寸。当fastMA下穿slowMA时,它会下达"卖出"市价订单建立空头头寸:
Mine Script®
已复制
请注意:
- strategy() 函数声明该脚本是一个名为“Simple strategy demo”的策略,并在主图面板显示可视化内容。
- strategy() 调用中的
margin_long和margin_short参数指定策略必须有100%的多头或空头交易金额才能执行交易。更多信息请参阅相关章节。 - strategy.entry() 函数是脚本用于创建入场订单和反转仓位的命令。“buy”入场订单会平掉所有空头仓位并建立新的多头仓位,“sell”入场订单会平掉所有多头仓位并建立新的空头仓位。
将策略应用于图表
要测试策略,请将其添加到图表中。从“指标、数据与策略”菜单中选择内置或已发布的策略,或在 Mine 编辑器中编写自定义策略并点击右上角的“添加到图表”选项:

该脚本会在主图面板上绘制交易标记,并在策略测试器选项卡中显示模拟交易结果:

策略测试器
策略测试器会可视化策略脚本的模拟表现并显示其属性。要使用它,请将使用 strategy() 函数声明的脚本添加到图表中,然后打开“策略测试器”选项卡。如果图表上有两个或更多策略,请通过左上角选择要分析的策略名称来指定。
选定的脚本在图表数据上执行后,策略测试器将在以下四个选项卡中填充相关策略信息:
概览
“概览”选项卡可快速查看策略在模拟交易序列中的表现。该选项卡显示关键绩效指标和包含三个实用走势图的图表:
- 权益基准线走势图 可视化策略在已平仓交易中的模拟权益变化
- 回撤柱状图 显示策略权益从峰值下跌的幅度
- 买入持有权益走势图 展示在整个测试范围内建立单一多头头寸并持有的策略权益增长

请注意:
- 该图表采用双纵坐标轴显示:
- 左侧坐标轴用于“权益”和“买入持有权益”走势图
- 右侧坐标轴用于“回撤”走势图
- 用户可通过底部选项切换走势图显示,并选择绝对值或百分比刻度
- 交互功能:
- 点击图表数据点时,主图表将自动跳转至对应平仓交易所在的K线
- 同时显示包含平仓时间的工具提示框
绩效摘要
“绩效摘要”选项卡深入展示了策略的关键绩效指标摘要,内容分为不同列。“全部”列显示所有模拟交易的绩效信息,“多头”和“空头”列则分别展示多头和空头交易的相关指标。该视图为策略的整体及方向性交易表现提供了更详细的洞察:

交易列表
“交易列表”选项卡按时间顺序列出策略的模拟交易。列表中的每条记录均显示交易的关键信息,包括进场与离场订单的日期时间、订单名称、订单价格以及合约/股票/手数/单位数量。此外,每条记录还会显示该笔交易的盈亏状况,以及策略的累计利润、浮动盈利和回撤值:

请注意:
- 将鼠标悬停在列表条目的进场或离场信息上时,会显示“滚动至对应K线”按钮。点击该按钮可将主图表定位至该进场或离场发生的K线位置。
- 列表默认按交易编号降序排列(最新交易显示在顶部)。用户可通过点击列表上方的“交易编号”按钮切换排序顺序。
属性
“属性”选项卡详细展示了策略配置及其执行数据集的各项信息,内容分为四个可折叠部分:
- “日期范围”部分显示模拟交易的实际日期区间及完整的可用回测范围。
- “交易品种信息”部分呈现图表品种的名称、时间周期、类型、点值、货币单位和最小价格变动单位,同时包含图表指定的精度设置。
- “策略输入参数”部分列出策略“设置/输入”选项卡中所有参数的名称与数值。仅当脚本包含
input*()函数调用,或在strategy()声明语句中指定了非零calc_bars_count参数时,此部分才会显示。 - “策略属性”部分概述策略的各项属性,包括初始资金、账户货币、订单规模、保证金、加仓规则、佣金、滑点及其他设置。

经纪商模拟器
TradingVue 使用经纪商模拟器在运行策略脚本时模拟交易。与真实交易不同,该模拟器默认仅基于图表数据执行策略订单。因此,它会在历史K线收盘后执行订单;同样,在实时K线中最早也只能在新价格跳动后执行订单。有关此行为的详细信息,请参阅执行模型页面。
由于经纪商模拟器默认仅使用图表价格数据,它在执行订单时会假设K线内部价格波动遵循以下逻辑:
- 若某根K线的开盘价更接近最高价而非最低价,模拟器假定价格波动顺序为:开盘价 → 最高价 → 最低价 → 收盘价。
- 若开盘价更接近最低价而非最高价,则假定顺序为:开盘价 → 最低价 → 最高价 → 收盘价。
- 模拟器认为每根K线内部不存在价格跳空(即K线高低范围内的任何价格均可能触发订单执行)。
- 对于基于价格的订单(除市价单外的所有订单),模拟器假设相邻K线的收盘价与开盘价之间的跳空区间内不存在内部波动。若市场价格在两根K线的跳空区间内穿越订单指定价格,模拟器会以当前K线的开盘价执行订单,而非订单指定价格。

订单与交易
Mine Script策略通过订单进行交易和持仓管理,其逻辑与真实交易类似。在此语境下,订单是策略向经纪商模拟器发出的市场操作指令,而交易则是模拟器执行订单后产生的实际成交结果。
以下示例详细展示策略订单的运作机制及其转化为交易的过程:该脚本每20根K线通过strategy.entry()创建多头市价单并绘制标签,同时在全局范围内调用strategy.close_all(),于每根K线生成平仓市价单以了结任何未平仓头寸:

Mine Script®
已复制
请注意:
- 虽然脚本在每根K线上都调用了strategy.close_all(),但该函数仅在策略有未平仓头寸时才会创建新的平仓订单。如果没有未平仓头寸,函数调用不会产生任何效果。
在上图中,蓝色箭头显示策略建立多头头寸的位置,紫色箭头标记策略平仓的K线。请注意标签绘制出现在进场标记的前一根K线,而进场标记又出现在平仓标记的前一根K线。这个顺序展示了订单创建和执行的实际情况。
默认情况下,经纪商模拟器最早只能在下一个可用价格跳动时执行订单,因为在同一价格跳动上同时创建和执行订单是不现实的。由于策略默认在每根K线收盘后重新计算,模拟器执行已生成订单的下一个可用价格跳动就是下一根K线的开盘价。例如,当longCondition在第20根K线出现时,脚本会创建一个进场订单,该订单将在下一个价格跳动(即第21根K线的开盘价)执行。当策略在第21根K线收盘后重新计算时,它会创建一个在当前价格跳动(即第22根K线的开盘价)平仓的订单。
订单类型
Mine Script策略可模拟多种订单类型以满足特定交易系统需求,主要包括以下重要类型:市价单、限价单、止损单和止损限价单。
市价单
市价单是最基础的订单类型,多数下单指令默认生成此类订单。其本质是要求立即以当前最优价格买入或卖出标的资产的指令,不限定具体成交价格。因此,经纪商模拟器始终会在下一个可用的价格跳动点执行市价单。
以下示例展示每间隔lengthInput根K线交替建立多空市价单的逻辑:当bar_index能被2 * lengthInput整除时,策略生成多头市价单;当bar_index能被lengthInput整除时,则建立空头市价单:

Mine Script®
已复制
请注意:
- 标签显示的是脚本生成市价单的K线位置,而经纪商模拟器会在下一根K线的开盘价执行这些订单。
- strategy.entry()命令能够自动将现有持仓反向平仓并建立相反方向的新头寸。更多详细信息请参阅下文相关章节。
限价单
限价单是指定价格(或更优价格)买卖证券的指令,对多头订单要求成交价不高于指定价,对空头订单要求成交价不低于指定价。在策略脚本中模拟限价单时,需向订单指令的limit参数传入价格值。
当市场价格达到或优于限价单指定价格时,经纪商模拟器会以该价格或更优价格执行订单。若策略生成的限价单价格劣于当前市价(多头订单限价高于市价,或空头订单限价低于市价),模拟器将立即按市价执行,无需等待价格达到指定水平。
示例脚本在图表倒数第100根K线的收盘价下方800个最小价格单位处创建多头限价单,并通过strategy.entry()指令实现。该脚本绘制标签标记订单创建K线,并绘制水平线显示订单价格:

Mine Script®
已复制
请注意上图中,标签和水平线的起点均早于“Long”进场标记数根K线。当市场价格持续高于limitPrice时,经纪商模拟器无法执行该订单——因为该价格对多头交易而言是劣于市价的条件。直到价格下跌触及limitPrice后,模拟器才在K线内部以该限价执行订单。
若将limitPrice设置为高于(而非低于)K线收盘价,由于收盘价本身已满足多头交易的更优价格条件,经纪商模拟器会直接在下一根K线的开盘价执行订单。以下修改后的脚本将limitPrice设为收盘价上方800个最小变动单位,以演示此现象:

Mine Script®
已复制
止损单和止损限价单
止损单是指当市场价格达到或突破特定价格(对多头订单要求价格高于设定值,对空头订单要求价格低于设定值)时激活新市价单或限价单的指令。要在策略脚本中模拟止损单,请向订单指令的stop参数传递价格值。
当策略生成的止损单价格优于当前市价(即对多头订单止损价低于市价,对空头订单止损价高于市价)时,系统会立即激活后续订单,无需等待市场价格达到该价位。
以下示例使用strategy.entry()在历史图表倒数第100根K线的收盘价上方800个最小价格单位处设置止损单,并在订单创建K线绘制标签,同时用线条标示止损价格。如下图所示,当价格突破止损位后,策略立即建立了多头头寸:

Mine Script®
已复制
请注意:
- 基础止损单的执行逻辑与限价单完全相反(根据市场价格判断方向)。若在此场景中使用限价单替代止损单,订单将在下一根K线立即执行(参见前文示例)。
- 当strategy.entry()或strategy.order()调用同时包含
stop和limit参数时,将创建止损限价单。与基础止损单(触发市价单)不同,止损限价单在价格触及止损位后,会生成以指定限价执行的后续限价订单。
下方我们修改了先前的脚本以模拟止损限价单,新增功能包括:
- 将K线最低价作为strategy.entry()中的限价参数
- 添加标记显示限价单激活位置
- 绘制线条可视化限价水平
在此示例图表中可见:
- 市场价格在订单创建后的下一根K线触及限价水平,但此时策略未建仓(因限价单尚未激活)
- 当价格后续触及止损位后,策略才发出限价单
- 最终经纪商模拟器在价格回落至限价水平时执行该订单

Mine Script®
已复制
订单提交与撤销
交易策略指令strategy.* 命名空间包含以下五个模拟下单操作的函数,称为下单指令:
strategy.entry()、
strategy.order()、
strategy.exit()、
strategy.close() 和
strategy.close_all()。
此外,该命名空间还包含以下两个用于取消挂单的函数,称为取消指令:
strategy.cancel() 和
strategy.cancel_all()。
以下部分将解释这些指令的独特用法及操作方式。
strategy.entry()
strategy.entry()指令用于生成入场订单。其独特功能可简化开仓及持仓管理流程。该下单指令默认生成市价订单,但也可通过limit和stop参数创建限价单、止损单和止损限价单(具体订单类型请参阅前文订单类型章节说明)。
头寸反转
strategy.entry() 指令的一个独特功能是能够自动反转现有持仓。默认情况下,当 strategy.entry() 的订单执行时,若存在相反方向的持仓,该指令会自动将现有持仓数量叠加到新订单的数量上。这种数量叠加机制使得订单能够平掉当前持仓,并在新方向上按照指定数量(合约/手数/股数/单位)建立新仓位。
例如,当策略当前持有 15 股 strategy.long 方向的多头仓位时,若调用 strategy.entry() 下达 strategy.short 方向的新市价订单,最终成交数量将是新订单指定数量加上 15 股。
以下示例演示了该机制的实际运作:当每 100 根 K 线出现一次 buyCondition 时,脚本会调用 strategy.entry(qty = 15) 建立 15 股的多头仓位;而当每 50 根 K 线出现 sellCondition 时,脚本会调用 strategy.entry(qty = 5) 建立 5 股的空头仓位。脚本还在满足 buyCondition 和 sellCondition 的 K 线位置用背景色进行了标记:

Mine Script®
已复制
图表上的交易标记显示的是成交数量,而非最终持仓数量。标记显示每次订单成交数量均为20股,而非多头订单的15股或空头订单的5股。由于strategy.entry()默认会反转相反方向的持仓,每次调用都会将现有持仓数量(例如多头持仓的15股)叠加到新订单数量(例如空头订单的5股)上,导致首次建仓后每次成交数量均为20股。虽然这些交易成交量均为20股,但最终形成的持仓仍为每个空头订单5股、每个多头订单15股。
需注意:
- strategy.risk.allow_entry_in()函数会覆盖strategy.entry()指令允许的交易方向。当脚本使用该风险管理指令指定交易方向时,相反方向的strategy.entry()订单仅会平仓而不允许反转持仓。
加仓机制
strategy.entry()指令的另一特性是与策略的加仓属性相关联。加仓属性规定了策略允许在同一方向上进行连续建仓的最大次数。用户可通过在strategy()声明语句中添加pyramiding参数,或在脚本的“设置/属性”选项卡中调整“加仓次数”输入值来设定该属性。默认值为1,表示策略可以开立新仓位,但无法通过strategy.entry()调用来增加现有仓位。
以下示例使用strategy.entry()在每25根K线出现entryCondition时下达市价订单。订单方向每100根K线切换一次,这意味着每个100根K线的周期内会执行四次相同方向的strategy.entry()调用。为直观显示条件触发情况,脚本在每次出现entryCondition时根据当前方向对图表背景进行着色标记:

Mine Script®
已复制
请注意,虽然脚本在每个100根K线周期内以相同方向调用四次strategy.entry(),但策略并不会在每次调用后都执行订单。由于采用默认加仓值1,策略无法通过strategy.entry()对同一持仓进行多次加仓。
我们在下方修改后的脚本中,通过在strategy()声明语句添加pyramiding = 4参数,允许同一方向最多进行四次连续加仓。现在每次调用strategy.entry()后都会产生订单成交:

Mine Script®
已复制
strategy.order()
strategy.order()指令用于生成基础订单。与其他可能受策略属性和未平仓交易影响的下单指令不同,该指令会忽略加仓等大多数策略属性,仅按指定参数创建订单。该指令默认生成市价订单,但也可通过limit和stop参数创建限价单、止损单和止损限价单。strategy.order()的订单既可开立新仓位,也可调整或平掉现有仓位。当策略执行该指令生成的订单时,最终市场持仓将是现有仓位与成交订单数量的净总和。
以下脚本使用strategy.order()进行建仓和平仓操作。策略每100根K线下达15个单位的多头市价单,在非100倍数的每25根K线处下达5个单位的空头市价单。脚本通过背景高亮标注"买入"和"卖出"订单的执行位置:

Mine Script®
已复制
该策略始终不会建立空头仓位。与strategy.entry()指令不同,strategy.order()不会自动反转现有持仓。当"买入"订单成交后,策略将持有15个单位的多头仓位。随后的三个"卖出"订单每次减少5个单位仓位,最终持仓为15 - 5 * 3 = 0。也就是说,策略在每100根K线建立多头仓位后,通过连续三次空头订单逐步将仓位减至0。若本例改用strategy.entry()指令,策略将会在15个单位多头和5个单位空头之间交替建仓。
strategy.exit()
strategy.exit() 指令用于生成平仓订单。它具有多个与未平仓交易相关联的独特行为,可简化平仓操作并创建包含止盈、止损和追踪止损的多级退出策略。
与其他每次调用只能生成单一订单的下单指令不同,每次调用 strategy.exit() 根据参数不同可产生多种类型的平仓订单。此外,根据指定的 from_entry 值及策略未平仓交易情况,单次调用该指令可为多个建仓点生成平仓订单。
止盈与止损
strategy.exit() 指令最基本的用途是设置限价单(在盈利达到目标时触发平仓)、止损单(在亏损超过阈值时触发平仓)或两者组合(括号订单)。
该指令通过四个参数控制止盈/止损订单的触发价格:
profit和loss参数接收相对值,表示市场价格相对于建仓价需要波动的最小点数才能触发平仓limit和stop参数接收绝对值,表示触发平仓的具体价格水平
当strategy.exit()调用包含用于定义止盈或止损水平的相对和绝对参数(profit和limit或loss和stop)时,它仅会在预期最先触发的水平创建订单。
例如,如果profit距离为19个最小变动单位且limit水平位于入场价格有利方向的20个最小变动单位处,则strategy.exit()命令会在入场价格profit个最小变动单位处设置止盈订单,因为市场价格在达到limit值前会先移动该距离。相反,如果profit距离为20个最小变动单位且limit水平位于入场价格有利方向的19个最小变动单位处,则该命令会在limit水平设置止盈订单,因为价格会先达到该值。
注意
strategy.exit() 指令的 limit 和 stop 参数与 strategy.entry() 及 strategy.order() 的对应参数具有不同行为。当 strategy.entry() 或 strategy.order() 调用包含 limit 和 stop 参数时,会创建单一的止损限价订单;而 strategy.exit() 同时使用这两个参数时,将生成两个独立平仓订单:按 limit 价格设置的止盈订单和按 stop 价格设置的止损订单。
以下示例使用 strategy.exit() 指令创建包含止盈和止损的括号订单。当 buyCondition 触发时,脚本调用 strategy.entry() 下达“买入”市价单,同时调用 strategy.exit() 并传入 limit 和 stop 参数,以在 limitPrice 设置止盈订单、在 stopPrice 设置止损订单。脚本在图表上绘制 limitPrice 和 stopPrice 数值以可视化平仓价格位置:

Mine Script®
已复制
需注意:
- 未在 strategy.exit() 调用中指定
qty或qty_percent参数,意味着将创建平掉“buy”订单100%仓位的平仓单 - strategy.exit() 生成的平仓订单不保证按指定价格成交。根据经纪商模拟器的可用价格范围,限价单可能以更优价格成交,止损单可能以更差价格成交
当 strategy.exit() 调用包含 from_entry 参数时,生成的平仓单仅适用于具有匹配ID的建仓订单。若指定的 from_entry 值与当前持仓中的任何建仓ID都不匹配,该指令不会创建任何平仓单
下方修改后的脚本将 strategy.exit() 的 from_entry 参数改为“buy2”,这意味着仅会为具有“buy2”建仓ID的未平仓交易创建平仓单。由于该版本未创建任何带有“buy2”ID的建仓单,因此不会生成任何平仓单:
Mine Script®
已复制
请注意:
- 当 strategy.exit() 调用未包含
from_entry参数时,该指令会为持仓中所有未平仓交易创建平仓单(无论其建仓ID为何)。具体说明请参阅下文多建仓点的平仓机制章节。
部分与多级平仓
策略可通过多次调用 strategy.exit() 为同一入场ID创建连续的分批平仓订单,从而简化多级退出策略的构建。若需通过多次 strategy.exit() 调用平仓未结头寸,则每次调用需包含 qty(数量)或 qty_percent(百分比数量)参数以指定平仓量。若所有平仓订单的总量超过未平仓头寸,策略将自动按比例缩减订单规模以匹配持仓量。
注意事项:
- 当 strategy.exit() 同时包含
qty和qty_percent参数时,系统将优先采用qty数值作为订单规模,并忽略qty_percent数值。
以下示例展示了一个简单策略:它为某个入场ID创建了两组分批平仓订单。当 buyCondition 触发时,脚本通过 strategy.entry() 下达2股“买入”市价单,并通过两次 strategy.exit() 调用分别创建“exit1”和“exit2”平仓指令。第一次调用使用 qty=1,第二次调用使用 qty=3:

Mine Script®
已复制
从上方图表中的交易标记可见,该策略首先执行“exit1”止盈或止损订单,减少1股未平仓头寸,使持仓剩余1股。然而,我们为“exit2”订单组设定的3股规模超过了剩余持仓。策略并未采用该指定数量,而是自动将“exit2”订单缩减至1股,从而成功平仓。
注意事项:
- 该策略仅会执行“exit1”订单组中的单一平仓订单(而非同时执行两个)。当 strategy.exit() 为同一入场ID生成多个平仓订单类型时,策略仅执行首个触发的订单并自动取消其余订单。
- 策略缩减“exit2”订单规模的原因是:默认情况下,所有通过 strategy.exit() 生成的订单均属于同一 strategy.oca.reduce 组(关于OCA组的详细说明见下文)。
在通过不同 strategy.exit() 调用创建多个平仓订单时,需特别注意:每次调用生成的订单会预留部分未平仓头寸。后一次调用生成的订单无法平仓已被前次调用预留的头寸部分。
例如:以下脚本通过 strategy.entry() 调用生成20股的“买入”入场订单,并在图表最后100根K线前通过两次独立 strategy.exit() 调用分别创建“限价”与“止损”平仓订单。其中“限价”订单指定数量为19股,“止损”订单指定数量为20股:
Mine Script®
已复制
不熟悉 strategy.exit() 命令特殊行为的用户可能会认为:若“止损”订单先于“限价”订单成交,该策略将平掉全部市场头寸。然而下图中的交易标记显示,“止损”订单仅平仓了1股。这是因为代码中针对“限价”订单的 strategy.exit() 调用优先执行,已为该订单预留了19股的平仓头寸。无论“止损”订单何时成交,这种预留机制都使得其仅剩1股可平仓量:

跟踪止损
strategy.exit() 命令的一个关键功能是能够创建跟踪止损,即止损订单会随着市场价格朝有利方向(多头头寸为上涨,空头头寸为下跌)移动至更优值时,以指定距离跟随市场价格。
此类退出订单包含两个组成部分:激活水平和跟踪偏移量。激活水平是市场价格必须突破以触发跟踪止损计算的值,而跟踪偏移量则是激活后的止损单在价格达到更优值时保持的跟随距离。
strategy.exit() 中有三个参数用于确定跟踪止损订单的激活水平和跟踪偏移量:
trail_price参数接受一个绝对价格值作为跟踪止损的激活水平。trail_points参数是另一种指定激活水平的方式,其值表示与入场价之间的最小跳动点数,用于触发跟踪止损。trail_offset参数接受一个值,表示订单的跟踪偏移量(以跳动点数为单位)。
要创建并激活跟踪止损订单,strategy.exit() 调用必须包含 trail_offset 参数,以及 trail_price 或 trail_points 参数之一。如果同时提供 trail_price 和 trail_points 参数,命令将优先使用 trail_price 作为激活水平,并忽略 trail_points 的值。
以下示例展示了跟踪止损订单的工作原理。策略通过 strategy.entry() 命令在图表最后一条柱线前 100 根柱线处下达“多头”市价单,并在下一根柱线调用带 trail_price 和 trail_offset 参数的 strategy.exit() 以创建跟踪止损。脚本使用线条、标签和绘图来可视化跟踪止损的行为。
图表上的绿色线条显示市场价格需达到的跟踪止损激活水平。当价格突破该水平后,脚本通过蓝色绘图显示跟踪止损的价格。每次市场价格在激活跟踪止损后创出新高时,止损价格会随之提高,以保持与最优值相距 trailOffsetInput 跳动点数。若市场价格下跌或未创新高,止损价格则保持不变。最终,当市场价格跌破跟踪止损时,将触发平仓。

Mine Script®
已复制
多笔入场单的平仓
通过单次调用 strategy.exit() 命令,可以为同一持仓中的多笔入场单生成平仓订单,具体取决于 from_entry 参数的设置。
若一个持仓包含两笔或以上相同 ID 的入场单,则调用 strategy.exit() 并指定该 ID 作为 from_entry 参数时,系统会为在该调用发生之前(含当根 K 线)所有对应 ID 的入场单分别生成平仓订单。
例如,以下脚本在连续两根 K 线上周期性调用 strategy.entry() 以开立并加仓多头头寸,两次调用均使用 “buy” 作为 ID 参数。在创建第二笔入场单后,脚本通过单次调用 strategy.exit() 并指定 from_entry="buy",从而为该 ID 下的每笔入场单生成独立的平仓订单。当市场价格触及 takeProfit 或 stopLoss 值时,经纪商模拟器会执行两笔平仓订单并清空该持仓。

Mine Script®
已复制
若 strategy.exit() 调用未包含 from_entry 参数,则该命令会为持仓中的所有入场单生成平仓订单,无论其 ID 是否相同。
在此示例中,我们修改了上述脚本的 strategy.entry() 调用,使每次入场均使用不同 ID,并移除了 strategy.exit() 中的 from_entry 参数。由于此版本未指定平仓订单适用的入场单,strategy.exit() 会为持仓中的每笔入场单创建平仓指令:

Mine Script®
已复制
必须注意的是,不带 from_entry 参数的 strategy.exit() 调用会持续生效,并为持仓中的所有未平仓交易创建平仓订单,无论这些交易是何时建立的。这一行为会影响那些管理多笔入场或离场的策略。当策略持有未平仓头寸并在任意一根K线上调用未指定 from_entry ID 的 strategy.exit() 时,它会为该K线及之前建立的所有入场单创建平仓订单,并且会继续为该K线之后新建立的入场单创建平仓订单,直到该头寸完全平仓为止。
让我们通过具体示例来探究这一行为及其运作原理。下面的脚本在用户指定的时间范围内,在每根K线上使用 strategy.entry() 创建多头入场订单,并在该时间范围内的某一根特定K线上调用不带 from_entry 参数的 strategy.exit(),从而为该未平仓头寸中的每笔入场单创建平仓订单。该离场命令使用了 0 作为止损值,这意味着每当市场价格不高于某笔入场订单的价格时,就会执行相应的平仓订单。
脚本在开始计算前会提示用户选择三个关键点:第一个点指定订单创建的起始时间,第二个点决定单次 strategy.exit() 调用的发生时间,第三个点则指定订单创建的结束时间:

Mine Script®
已复制
请注意:
- 我们在 strategy() 声明语句中包含了
pyramiding = 100,这允许该策略通过 strategy.entry() 建立最多 100 笔未平仓的入场交易。 - 脚本使用标签和 bgcolor() 来标记订单开始建立、停止建立以及 strategy.exit() 调用的时间点。
- 脚本在最低入场价位绘制了一条线和标签,用于显示触发完全平仓所需达到的市场价格水平。
通过对比代码本身与脚本的图表输出,我们可以观察到 strategy.exit() 在此案例中的独特行为。虽然脚本仅在带有蓝色标签的那根K线上调用了一次 strategy.exit(),但这次单次调用却:
- 为该K线及之前所有入场单建立了平仓订单
- 并持续为该K线之后的所有新入场单自动建立平仓订单
这一行为的发生是因为当 strategy.exit() 未通过特定ID与入场单关联时,它无法确定何时停止建立平仓订单。在此情况下,该命令只有在持仓完全平仓后才会停止新建平仓订单。
若我们在 strategy.exit() 调用中加入 from_entry 参数,上述脚本将表现出不同行为。当该命令指定了 from_entry ID 时:
- 仅适用于该调用K线及之前建立的、具有该ID的入场单
- 不会为该调用K线之后建立的入场单创建平仓订单(即使这些新入场单具有相同ID)
在本次修改中,我们为 strategy.exit() 添加了 from_entry = "Entry" 参数,这意味着它仅会为具有"Entry" ID 的入场单生成平仓订单。此时仅发生了15次平仓,每次平仓都对应蓝色标签K线及之前建立的入场订单。该调用不会影响策略在该K线之后建立的任何入场单。

Mine Script®
已复制
strategy.close() 和 strategy.close_all()
strategy.close() 和 strategy.close_all() 命令用于生成平仓订单。与 strategy.exit()(创建基于价格的退出订单,如止损单)不同,这些命令会生成市价订单,经纪商模拟器将在下一个可用时点立即执行,无论价格如何。
以下示例展示了一个简单策略:该策略每50根K线使用strategy.entry()建立一个"buy"入场订单,并在25根K线后使用strategy.close()以市价平仓该多头头寸:

Mine Script®
已复制
请注意,本脚本中的 strategy.close() 调用使用“buy”作为必需的 id 参数。与 strategy.exit() 不同,该命令的 id 参数指定的是未平仓交易的入场 ID,并不代表生成的平仓订单 ID。如果一个市场头寸包含多个具有相同入场 ID 的未平仓交易,使用该 ID 作为 id 参数的单次 strategy.close() 调用将生成一个市价订单来平仓所有这些交易。
以下脚本每 25 根 K 线使用 strategy.entry() 创建一个“buy”订单,并每 100 根 K 线使用 strategy.close() 以“buy”作为 id 参数来平仓所有具有该入场 ID 的未平仓交易。在这种情况下,来自 strategy.close() 的市价订单会平掉整个头寸,因为所有未平仓交易都具有相同的“buy”入场 ID:

Mine Script®
已复制
需注意以下几点:
- 我们在 strategy() 声明语句中设置了
pyramiding = 3,这使得脚本通过 strategy.entry() 调用可为每个仓位最多生成 3 笔入场交易;
strategy.close_all() 命令会生成一个不关联任何特定入场ID的市价平仓订单,该命令适用于需要立即平掉包含多个不同入场ID交易的仓位的情况;
下方脚本会根据未平仓交易数量依次建立“A”、“B”和“C”入场订单,随后调用 strategy.close_all() 在下一根K线创建单一平仓订单来清空整个仓位。

Mine Script®
已复制
strategy.cancel() 和 strategy.cancel_all()
strategy.cancel() 和 strategy.cancel_all() 命令允许策略在经纪商模拟器执行前取消未成交的订单。这些订单取消命令在处理基于价格的订单时特别有用,包括:
- 所有来自 strategy.exit() 调用的订单
- 来自使用
limit或stop参数的 strategy.entry() 和 strategy.order() 调用的订单
strategy.cancel() 命令需要一个必填的 id 参数,用于指定要取消的入场或离场订单的 ID。strategy.cancel_all() 命令则不需要此参数,因为它会取消所有未成交订单,无论其 ID 是什么。
下面的策略示例:
- 在图表最后 100 根 K 线处,使用 strategy.entry() 在收盘价下方 500 个最小变动价位处下一个“buy”限价单
- 在下一根 K 线使用 strategy.cancel() 取消该订单
脚本通过背景高亮显示“buy”订单的下单和取消时间,并在订单价格处绘制一条水平线。如下图所示,当市场价格穿过水平线时,我们的示例图表上没有显示入场标记,因为策略在价格达到该水平前(当图表背景为橙色时)就已经取消了该订单。

Mine Script®
已复制
strategy.cancel() 命令会影响所有具有指定 ID 的未成交订单。如果指定的 id 代表一个不存在的订单 ID,则该命令不会执行任何操作。当存在多个具有指定 ID 的未成交订单时,该命令会一次性取消所有这些订单。
下面,我们修改了之前的脚本,在距离最后一根图表柱 100 根柱的位置开始,连续三根柱上分别放置一个“buy”限价订单。在放置完所有三个订单后,策略使用 strategy.cancel() 并以“buy”作为 id 参数取消它们,从而导致当市场价格达到任何订单价格(水平线)时不会发生任何交易:

Mine Script®
已复制
请注意:
- 我们在 strategy() 声明语句中包含了
pyramiding = 3,允许每个仓位通过 strategy.entry() 进行三次连续开仓。如果脚本改用 strategy.order(),即使没有此设置也能达到相同效果,因为pyramiding不会影响该命令生成的订单。
strategy.cancel() 和 strategy.cancel_all() 命令可以取消任何类型的订单,包括市价单。但需要注意的是,这两个命令只有在与订单放置命令同一次脚本执行时调用,才能取消市价单。如果在之后调用,则不会生效,因为经纪商模拟器会在下一个可用 tick 成交市价单。
此示例在距离最后一根图表柱 100 根柱的位置使用 strategy.entry() 放置一个“buy”市价单,随后在下一根柱尝试用 strategy.cancel_all() 取消该订单。由于经纪商模拟器会在下一根柱的开盘 tick 成交该订单(早于脚本执行 strategy.cancel_all() 调用),因此取消命令不会影响该“buy”订单:

Mine Script®
已复制
仓位规模
Mine Script策略提供两种控制开仓和管理仓位的订单规模方式:
- 设置订单的默认固定数量类型和数值。程序员可通过在strategy()声明语句中添加
default_qty_type和default_qty_value参数来指定这些属性的默认值。用户可在“设置/属性”选项卡的“订单规模”输入栏中调整这些数值。 - 在strategy.entry()或strategy.order()调用中包含非空的
qty参数。当这两个命令的调用指定了非空的qty值时,该调用将忽略策略的默认数量类型和数值,直接按qty指定的合约数/股数/手数/单位数下单。
以下示例使用strategy.entry()调用为多头和空头交易设置不同的qty值:当K线的最低价等于最低值时,脚本下达“买入”订单建立longAmount单位的多头仓位;当K线的最高价等于最高值时,则下达“卖出”订单建立shortAmount单位的空头仓位。

Mine Script®
已复制
请注意,尽管我们在strategy()声明语句中包含了default_qty_type和default_qty_value参数,但该策略并未使用此默认设置来确定订单规模,因为入场命令中指定的qty参数具有优先权。如果我们想使用默认规模,就必须从strategy.entry()调用中移除qty参数,或将其值设为na。
在此,我们修改了之前的脚本,在两个strategy.entry()调用中为qty参数添加了三元表达式,将输入值为0的情况替换为na。如果我们设置的longAmount或shortAmount为0(这是我们设定的新默认值),相应的入场订单就会转而使用策略的默认订单规模,如下所示:

Mine Script®
已复制
平仓
默认情况下,策略采用先进先出(FIFO)方式平仓,这意味着任何平仓指令都会从最早的开仓交易开始平仓或减仓,即使平仓指令指定了其他开仓交易的入场ID。若要覆盖此默认行为,需在strategy()声明语句中包含close_entries_rule = “ANY”参数。
以下示例依次放置“Buy1”和“Buy2”开仓指令,从图表最新K线前100根开始。当持仓量为0时,调用strategy.entry()下达5个单位的“Buy1”指令。当策略持仓量与该指令量匹配后,再调用strategy.entry()下达10个单位的“Buy2”指令。随后策略通过不带from_entry参数的单个strategy.exit()调用,为两个开仓创建“括号”平仓指令。为便于观察,脚本在独立面板绘制了strategy.position_size数值:

Mine Script®
已复制
请注意:
- 我们在strategy()声明语句中包含了
pyramiding = 2参数,允许每个仓位通过strategy.entry()进行两次连续开仓。
每当市场价格触发平仓指令时,上述脚本会从最早的开仓交易开始平仓。即使我们在代码中明确指定先平仓“Buy2”再平仓“Buy1”,这种FIFO行为仍然适用。
下方脚本版本使用“Buy2”作为strategy.close()的id参数,并在strategy.exit()调用中将“Buy1”作为from_entry参数。strategy.close()生成的市价单会在下一个可用tick执行,这意味着经纪商模拟器会先处理该市价单,再处理来自strategy.exit()的止盈和止损单:
Mine Script®
已复制
脚本中strategy.close()调用产生的市价单数量为10个单位,因为它关联的是带有“Buy2”入场ID的开仓交易。用户可能预期该策略在执行时会完全平掉这笔交易。然而“交易列表”选项卡显示,由于“Buy1”是最早的开仓交易,该指令会优先平掉“Buy1”交易的5个单位,剩余5个单位则平掉“Buy2”交易的一半仓位。此后,由strategy.exit()调用产生的“括号”平仓指令会平掉剩余仓位:

请注意:
- 若在strategy()声明语句中加入
close_entries_rule = "ANY"参数,则strategy.close()产生的市价单会优先平掉带有“Buy2”入场ID的开仓交易,随后由strategy.exit()产生的“括号”平仓指令才会平掉带有“Buy1”入场ID的交易。
OCA分组
一键全撤(OCA)组允许策略在经纪商模拟器执行同组其他订单时,完全或部分取消特定订单。要将订单分配到OCA组,需在订单指令调用中加入oca_name参数。strategy.entry()和strategy.order()指令还允许程序员指定OCA类型,用于定义策略在执行其他订单后是取消、缩减还是不修改该订单。
注意
所有为同一OCA组创建订单的指令必须使用相同的组名和OCA类型。若两条指令使用相同oca_name但不同oca_type值,策略会将其视为两个独立组别。换言之,一个OCA组不可混用strategy.oca.cancel、strategy.oca.reduce和strategy.oca.none这三种OCA类型。
strategy.oca.cancel
当订单指令使用strategy.oca.cancel作为oca_type参数时,若同OCA组的其他订单率先成交,策略将完全取消该订单。
为演示此OCA类型对策略订单的影响,参考以下脚本:当ma1值穿越ma2值时下单。若交叉发生时strategy.position_size为0,策略通过strategy.order()创建两个止损单——首单在K线高点做多,次单在K线低点做空。若交叉发生时已有持仓,则调用strategy.close_all()以市价单平仓。
Mine Script®
已复制
根据价格走势,策略可能在创建平仓市价单之前就已成交两个止损单。此时由于两笔订单规模相同,策略将直接平仓而不会执行strategy.close_all()。如下图所示,该策略多次交替执行“Long”和“Short”订单,但始终未触发strategy.close_all()的平仓指令。

为避免策略在执行strategy.close_all()前同时成交“Long”和“Short”订单的情况,可设置其中一笔订单在另一笔成交后自动取消。我们在两个strategy.order()调用中均加入oca_name=“Entry”参数和oca_type=strategy.oca.cancel参数。现在当策略执行“Long”或“Short”任一订单后,将自动取消另一笔订单,并等待strategy.close_all()执行平仓:

Mine Script®
已复制
strategy.oca.reduce
当订单指令使用strategy.oca.reduce作为OCA类型时,若同OCA组其他订单率先成交,策略不会完全取消该订单,而是按已成交合约/股票/手数/单位数量缩减订单规模,该特性特别适用于自定义平仓策略。
以下示例展示了一个仅做多的策略,该策略为每个新开仓创建1个止损单和2个止盈单。当快速均线上穿慢速均线时,脚本调用qty=6的strategy.entry()创建开仓单,随后使用3个strategy.order()调用分别在止损价创建止损单,在limit1和limit2价位创建限价单。其中“Stop”订单的strategy.order()调用使用qty=6参数,而“Limit 1”和“Limit 2”订单的调用均使用qty=3参数:
Mine Script®
已复制
将该策略添加到图表后,我们发现其运行效果与预期不符。此脚本的问题在于:strategy.order()生成的订单默认不属于任何OCA组(这与strategy.exit()不同,后者生成的订单会自动归入strategy.oca.reduce类型的OCA组)。由于策略未将strategy.order()调用分配到任何OCA组,在任一订单成交后,系统不会缩减未成交的止损单或限价单规模。因此,若经纪商模拟器同时成交止损单和至少一个限价单,实际交易量将超过多头持仓量,最终导致空头仓位产生:

为使仅做多策略按预期运行,必须设置其在任一止损/限价单成交后缩减未成交订单规模,以避免卖出量超过多头持仓量。
我们在所有strategy.order()调用中均加入oca_name=“Bracket”和oca_type=strategy.oca.reduce参数。这些修改指示策略在经纪商模拟器成交任一订单时,自动缩减“Bracket”组内其他订单的规模。此版本策略永远不会产生空头仓位,因为其成交的止损单和限价单总量始终不超过多头持仓量:

Mine Script®
已复制
请注意:
- 我们将“Limit 2”订单的
qty值从3改为6,因为当策略执行“Limit 1”订单时,会将该订单规模缩减3个单位。若保持qty=3,则在第一个限价单成交后,第二个限价单规模将降为0,导致其永远无法成交。
strategy.oca.none
当订单指令使用strategy.oca.none作为oca_type值时,该指令生成的所有订单将独立于任何OCA组执行。此参数是strategy.order()和strategy.entry()指令的默认oca_type值。
修改计算行为
策略脚本会在所有可用历史K线上执行,并随着新数据到来在实时K线上持续运行。但默认情况下,策略仅在K线收盘后重新计算数值(即使对于实时K线也是如此),且经纪商模拟器最早只能在下一根K线开盘时执行策略在当前K线收盘时下达的订单。
用户可通过strategy()声明语句中的calc_on_every_tick、calc_on_order_fills和process_orders_on_close参数,或在脚本“设置/属性”选项卡的“重新计算”和“执行订单”部分修改这些行为。以下章节将说明这些设置如何影响策略计算。
calc_on_every_tick
strategy()函数的calc_on_every_tick参数决定策略在实时K线上的计算频率。当该参数值为true时,脚本会在实时数据流的每个新tick上重新计算;默认值false表示脚本仅在实时K线收盘后执行。用户也可通过脚本“设置/属性”选项卡中的“每次tick计算”选项切换此行为。
启用此设置有助于前瞻测试,因其允许策略在计算中使用实时价格更新。但该设置不影响历史K线的计算,因历史数据流不包含完整tick数据——经纪商模拟器认为每根历史K线仅含四个tick(开盘价、最高价、最低价和收盘价)。因此用户需谨慎理解该设置的局限性:若启用每次tick计算导致策略在历史K线和实时K线上表现不一致,重新加载后策略将出现重绘现象。
以下示例展示每次tick计算如何导致策略重绘:该脚本使用strategy.entry()在收盘价创新高时下达多头订单,创新低时下达空头订单。strategy()声明语句包含calc_on_every_tick=true参数,意味着在实时K线上,策略可在K线收盘前根据新价格更新重新计算并下单:
Mine Script®
已复制
请注意:
- 该脚本设置
pyramiding值为20,允许通过strategy.entry()指令模拟每个仓位最多20次开仓。 - 当barstate.isrealtime为
true时,脚本将图表背景标为橙色以标识实时K线。
将脚本应用于图表并观察若干实时K线后,可见如下输出:

脚本在每个收盘价创新高的tick上都下达了“Buy”订单(这在每根实时K线上可能发生多次)。此外,经纪商模拟器以当前实时价格(而非严格遵循下一根K线开盘价)执行了每个市价单。
重新加载图表后可见策略行为发生变化,对这些K线的计算结果出现重绘。此时策略仅在满足条件的已收盘K线上各生成一个“Buy”订单,且经纪商模拟器统一在下一根K线开盘时执行。由于先前的实时K线已转为历史K线(不含完整tick数据),故每根K线不再产生多次开仓记录:

calc_on_order_fills
strategy()函数的calc_on_order_fills参数使策略能在订单成交后立即重新计算,从而利用更精细的数据并在无需等待K线收盘的情况下下达新订单。其默认值false表示策略不会在每次订单成交后立即重新计算。用户也可通过脚本“设置/属性”选项卡中的“订单成交后计算”选项切换此行为。
启用该设置可为策略脚本提供更及时的数据(例如未收盘K线上模拟仓位的当前平均价格),这些数据通常需等待K线收盘后才能获取。
以下示例展示了一个简单策略:当strategy.position_size为0时,通过strategy.entry()创建“Buy”订单,并使用strategy.position_avg_price计算strategy.exit()的止损和止盈价位。
我们在strategy()声明语句中加入calc_on_order_fills=true参数,这意味着每当经纪商模拟器成交“Buy”或“Exit”订单时,策略都会重新计算。每次“Exit”订单成交后,strategy.position_size恢复为0并触发新的“Buy”订单。经纪商模拟器在下一个tick以该K线的OHLC价格之一执行“Buy”订单,随后策略使用重新计算的strategy.position_avg_price值确定新的“Exit”订单价位:

Mine Script®
已复制
请注意:
- 若不启用订单成交后重新计算,该策略在K线收盘前不会下达新订单。每次平仓后,策略将等待当前K线收盘才下达新“Buy”订单,而经纪商模拟器会在下一
tick(即下根K线开盘时)执行该订单。
需特别说明的是,启用calc_on_order_fills可能导致策略结果失真,因经纪商模拟器假设的订单成交价在实际交易中可能无法实现。因此用户应审慎评估策略逻辑,仔细检查启用该参数后的表现。
例如以下脚本在最近25根历史K线上,会在每次订单成交和K线收盘后下达“Buy”订单。由于经纪商模拟器认为每根历史K线仅有4个tick(开盘、最高、最低和收盘价),策略会在每根K线模拟4次开仓。这种行为在实际交易中并不现实,因为很难精确在K线的最高或最低点成交订单:

Mine Script®
已复制
process_orders_on_close
默认情况下,策略在每根K线收盘时模拟订单,这意味着最早只能在下一根K线开盘时执行订单成交、策略计算和警报触发。程序员可通过在strategy()声明语句中设置process_orders_on_close=true来修改此行为,使订单在每根K线的收盘tick处理。用户也可通过“设置/属性”选项卡中的“执行订单/在K线收盘时”选项调整该设置。
此行为最适用于以下场景:回测手动交易策略(交易者在K线收盘前平仓),或非24x7市场的算法交易者建立盘后交易能力,使得收盘后触发的警报仍有希望在次日开盘前成交。
请注意:
- 对启用
process_orders_on_close的策略发送警报至第三方服务可能导致意外结果。K线收盘触发的警报仍发生在市场收盘后,基于此类警报的真实订单可能需等待市场重新开盘才能成交。 - strategy.close()和strategy.close_all()指令包含
immediately参数,若设为true可使生成的市价单在创建当笔tick成交。该参数为程序员提供了一种选择性应用process_orders_on_close行为的方式(仅影响平仓市价单而不干扰其他下单指令)。
模拟交易成本
当策略绩效报告包含实际交易中的潜在成本时,其参考价值和意义将显著提升。若未计入交易相关成本,交易者可能高估策略的历史盈利能力,进而导致实盘交易决策欠佳。Mine Script策略提供专门用于模拟交易成本的输入参数,可在绩效结果中体现相关费用。
手续费
手续费是经纪商/交易所在执行交易时收取的费用,可分为两种计费方式:按每笔交易/每份合约/每股/每手/每单位收取固定费用,或按交易总金额的百分比收取。用户可通过在strategy()函数中添加commission_type和commission_value参数,或在策略设置“属性”选项卡的“手续费”输入栏中进行配置。
以下示例脚本展示了一个简单策略:当收盘价达到length周期内最高值时,建立2%资金占比的“Long”仓位;当收盘价达到最低值时平仓:

Mine Script®
已复制
策略测试器显示,该策略在测试周期内实现了8.20%的正收益增长。但此次回测结果未计入经纪商/交易所可能收取的费用。现在我们在策略模拟中加入小额手续费,观察结果变化:本例在strategy()声明中添加commission_type=strategy.commission.percent和commission_value=1参数,表示将对所有成交订单模拟1%的手续费:

Mine Script®
已复制
如上例所示,当回测中应用1%手续费后,策略模拟的净利润大幅降至-9.71%,且资金曲线波动加剧,最大回撤升高。这些结果凸显了手续费对策略模拟绩效的显著影响。
滑点与限价单未成交
实际交易中,由于波动性、流动性、订单规模等市场因素,经纪商/交易所的订单成交价可能与预期存在偏差,这种差异称为滑点,其对策略表现影响显著。滑点具有动态不可预测性,虽无法精确模拟,但在回测/前瞻测试中为每笔交易加入小幅滑点可使结果更贴近现实。用户可通过在strategy()声明中添加slippage参数(固定tick数),或在“设置/属性”选项卡设置“滑点”值来模拟该效应。
以下示例展示滑点如何影响市价单成交价:该脚本在价格高于上升的EMA时建立2%资金的“Buy”市价单,当价格跌破下降的EMA时平仓。我们在strategy()函数中加入slippage=20参数,声明每笔模拟订单将沿交易方向滑点20个tick。
脚本通过strategy.opentrades.entry_bar_index()和strategy.closedtrades.exit_bar_index()获取entryIndex和exitIndex,进而得到订单fillPrice。当K线索引等于entryIndex时,fillPrice取首个strategy.opentrades.entry_price()值;等于exitIndex时则取最近平仓交易的strategy.closedtrades.exit_price()值。脚本同步绘制预期成交价与滑点后模拟成交价进行可视化对比:

Mine Script®
已复制
请注意:
- 由于策略对所有订单成交应用固定滑点,模拟中可能出现成交价超出K线范围的情况。需谨慎设置该参数,过度滑点会导致测试结果失真。
部分交易者认为使用限价单可避免滑点,因其成交价不会劣于指定价格。但即便市场价格触及限价单价格,实际市场中仍可能因流动性不足而未能成交。为在回测中模拟该情况,用户可在声明语句设置backtest_fill_limits_assumption参数,或通过“设置/属性”选项卡的“限价单验证价位”选项,指定市场价格需超越限价单价格多少个tick才会成交。
以下示例在最高价创length周期新高且无挂单时,于hlcc4价位下达2%资金的限价单。当最低价创新低时平仓并撤销所有订单。每次触发订单时,脚本在limitPrice位置绘制水平线,该线持续更新直至平仓或撤单:

Mine Script®
已复制
默认情况下,脚本假设所有限价单在市场价格触及其价位时必定成交,但这与实际交易情况往往不符。现在我们在限价单中加入价格验证机制以模拟可能的未成交情况。本例在strategy()函数调用中添加backtest_fill_limits_assumption=3参数。如图所示,启用限价验证后,部分模拟订单因需价格超越限价3个tick才可成交而被忽略,其余订单的成交时点也相应改变:

注意
限价验证机制会改变部分订单的成交时点,但策略仍按原定价格执行已验证的限价单。这种“时间错位”效应在保留限价单价格的同时,可能导致成交时点偏离实际市场情况。因此用户分析策略结果时需审慎评估该设置的局限性。
风险管理
设计一个表现优异(尤其是在多种市场环境下)的策略具有挑战性。大多数策略针对特定市场模式/条件设计,当应用于其他数据时可能产生失控亏损。因此策略的风险管理行为对其表现至关重要。程序员可使用strategy.risk.*()系列指令在策略脚本中设置风险管理标准。
策略可组合使用任意数量的风险管理标准。所有风险管理指令会在每个tick和订单执行事件触发,不受策略计算行为变更影响。无法在特定脚本执行时禁用这些指令。无论风险管理指令在代码中的位置如何,除非程序员主动移除调用,否则其始终对策略生效。
strategy.risk.allow_entry_in()
- 该指令用于覆盖脚本中所有strategy.entry()指令允许的交易方向。当用户通过该函数指定交易方向(如做多)后,策略仅在该方向建仓。若脚本在已有持仓时反向建仓,策略将模拟市价单平仓。
strategy.risk.max_cons_loss_days()
- 当策略模拟连续亏损达到指定交易日数后,该指令将撤销所有挂单、平掉现有持仓并停止后续交易操作。
- 当策略回撤幅度达到函数设定的阈值时,该指令将撤销所有挂单、平掉现有持仓并停止后续交易操作。
strategy.risk.max_intraday_filled_orders()
- 该指令设定每个交易日(若时间框架高于日线则按每根K线计算)的最大成交订单数。若订单数超限,将撤销所有挂单、平掉现有持仓并停止当日交易。
strategy.risk.max_intraday_loss()
- 该指令控制策略每个交易日(若时间框架高于日线则按每根K线计算)可承受的最大亏损。达到亏损阈值时将撤销所有挂单、平掉现有持仓并停止当日交易。
strategy.risk.max_position_size()
- 该指令限定通过strategy.entry()建仓时的最大持仓规模。若建仓指令导致持仓超限,策略将自动缩减订单数量以确保持仓不超阈值。
保证金
保证金是交易者为获得经纪商杠杆融资所需维持的最低抵押资金比例。通过strategy()声明语句的margin_long和margin_short参数,或脚本设置“属性”选项卡中的“多/空头保证金”选项可分别设置多空仓位保证金比例。例如:当多头保证金设为25%时,交易者需持有相当于开仓价值25%的资金,这也意味着其理论上可动用400%账户资金进行交易。
若策略模拟资金无法覆盖保证金交易的亏损,经纪商模拟器将触发强制平仓。具体平仓量为覆盖亏损所需量的四倍(以避免后续K线持续触发平仓),其计算逻辑如下:
- 计算持仓占用资金:占用资金 = 持仓量 × 开仓价
- 计算当前市值(MVS):MVS = 持仓量 × 现价
- 计算浮动盈亏:多头仓位为(MVS - 占用资金),空头仓位为(占用资金 - MVS)
- 计算账户权益:权益 = 初始资金 + 净盈亏 + 浮动盈亏
- 计算保证金比例:保证金比例 = 保证金参数值 / 100
- 计算保证金金额:保证金 = MVS × 保证金比例
- 计算可用资金:可用资金 = 权益 - 保证金
- 计算亏损总额:亏损额 = 可用资金 / 保证金比例
- 计算理论平仓量(精确至当前品种最小交易单位):平仓基数 = 取整(亏损额 / 现价)
- 确定实际平仓量:强制平仓量 = 平仓基数 × 4”
请注意:
- strategy.margin_liquidation_price变量的数值代表触发强制平仓的价位阈值。
在脚本中使用策略信息
strategy.*命名空间及其子命名空间中的大量内置函数为程序员提供了便捷方案,使其能直接在代码逻辑和计算中使用策略的交易和绩效信息(包括策略测试器中显示的数据)。
多个strategy.*变量存储着策略的基础信息,包括起始资金、当前权益、盈亏、浮动收益与回撤以及未平仓头寸:
- strategy.account_currency - 返回账户货币单位
- strategy.initial_capital - 策略初始资金
- strategy.equity - 当前账户净值
- strategy.netprofit 和 strategy.netprofit_percent - 净盈亏(金额/百分比)
- strategy.grossprofit 和 strategy.grossprofit_percent - 毛利润(金额/百分比)
- strategy.grossloss 和 strategy.grossloss_percent - 毛亏损(金额/百分比)
- strategy.openprofit 和 strategy.openprofit_percent - 浮动盈亏(金额/百分比)
- strategy.max_runup 和 strategy.max_runup_percent - 最大盈利回撤(金额/百分比)
- strategy.max_drawdown 和 strategy.max_drawdown_percent - 最大资金回撤(金额/百分比)
- strategy.position_size - 当前持仓数量
- strategy.position_avg_price - 持仓平均价格
- strategy.position_entry_name - 当前持仓的入场订单名称
此外,该命名空间还包含多个记录常规交易信息的变量,例如未平仓与已平仓交易数量、盈利与亏损交易数量、平均交易利润以及最大交易规模:
- strategy.opentrades - 当前未平仓交易数量
- strategy.closedtrades - 已平仓交易总数
- strategy.wintrades - 盈利交易数量
- strategy.losstrades - 亏损交易数量
- strategy.eventrades - 盈亏平衡交易数量
- strategy.avg_trade 和 strategy.avg_trade_percent - 平均每笔交易盈亏(金额/百分比)
- strategy.avg_winning_trade 和 strategy.avg_winning_trade_percent - 平均盈利交易盈亏(金额/百分比)
- strategy.avg_losing_trade 和 strategy.avg_losing_trade_percent - 平均亏损交易盈亏(金额/百分比)
- strategy.max_contracts_held_all - 历史最大持仓合约总数
- strategy.max_contracts_held_long - 历史最大多头持仓合约数
- strategy.max_contracts_held_short - 历史最大空头持仓合约数
程序员可利用这些变量在图表上显示相关策略信息、基于策略数据创建定制交易逻辑、计算自定义绩效指标等。
以下示例展示了这些strategy.*变量的几个简单应用场景。脚本在订单下达和显示计算中使用这些变量:当计算得出的rank值突破10且strategy.opentrades为0时,调用strategy.entry()下达“Buy”市价单;在下一根K线订单成交后,调用strategy.exit()以strategy.position_avg_price下方用户指定百分比设置止损单。若持仓期间rank值突破80,则通过strategy.close()在下一根K线平仓。
脚本在主图区域绘制表格,显示包含以下格式化字符串的策略数据:净利润及净利润百分比、账户货币单位、盈利交易次数及胜率、平均盈利与平均亏损比率、以及盈利因子(总盈利与总亏损比率)。同时在独立面板绘制总权益曲线,并根据策略浮动盈亏对面板背景进行高亮标注:

Mine Script®
已复制
请注意:
- 该脚本在入场订单后一根K线才创建止损单,因其使用strategy.position_avg_price确定价格水平。该变量仅在策略持有未平仓头寸时才会返回非
na值。 - 脚本仅在最后一根历史K线和所有实时K线上绘制表格,因历史状态的表格不可见。
- 我们在table.new()调用中包含
force_overlay=true参数,使表格显示在主图区域。
单个交易信息
strategy.*命名空间包含两个提供单个交易信息的子命名空间:strategy.opentrades.*和strategy.closedtrades.*。strategy.opentrades.*内置函数返回未完成(未平仓)交易的数据,而strategy.closedtrades.*内置函数返回已完成(已平仓)交易的数据。通过这些内置函数,程序员可以在脚本中使用细粒度的交易数据,从而进行更详细的策略分析和高级计算。
这两个子命名空间都包含多个类似的函数,用于返回关于交易订单、模拟成本和盈亏的信息,包括:
- strategy.opentrades.entry_id() / strategy.closedtrades.entry_id() - 获取交易入场订单ID
- strategy.opentrades.entry_comment() / strategy.closedtrades.entry_comment() - 获取交易入场备注
- strategy.opentrades.entry_price() / strategy.closedtrades.entry_price() - 获取交易入场价格
- strategy.opentrades.entry_bar_index() / strategy.closedtrades.entry_bar_index() - 获取入场K线索引
- strategy.opentrades.entry_time() / strategy.closedtrades.entry_time() - 获取交易入场时间
- strategy.opentrades.size() / strategy.closedtrades.size() - 获取交易仓位大小
- strategy.opentrades.profit() / strategy.closedtrades.profit() - 获取交易盈亏金额
- strategy.opentrades.profit_percent() / strategy.closedtrades.profit_percent() - 获取交易盈亏百分比
- strategy.opentrades.commission() / strategy.closedtrades.commission() - 获取交易手续费
- strategy.opentrades.max_runup() / strategy.closedtrades.max_runup() - 获取交易最大盈利金额
- strategy.opentrades.max_runup_percent() / strategy.closedtrades.max_runup_percent() - 获取交易最大盈利百分比
- strategy.opentrades.max_drawdown() / strategy.closedtrades.max_drawdown() - 获取交易最大回撤金额
- strategy.opentrades.max_drawdown_percent() / strategy.closedtrades.max_drawdown_percent() - 获取交易最大回撤百分比
- strategy.closedtrades.exit_id() - 获取平仓订单ID
- strategy.closedtrades.exit_price() - 获取平仓价格
- strategy.closedtrades.exit_time() - 获取平仓时间
- strategy.closedtrades.exit_bar_index() - 获取平仓K线索引
- strategy.closedtrades.exit_comment() - 获取平仓备注
请注意:
- 这些命名空间中的大多数内置函数都是函数形式。但
strategy.opentrades.*命名空间还包含一个特殊变量:strategy.opentrades.capital_held,其值表示所有未平仓交易占用的资金总额。 - 只有
strategy.closedtrades.*命名空间包含.exit_*()函数,用于获取平仓订单信息。 - 所有
strategy.opentrades.*()和strategy.closedtrades.*()函数都包含trade_num参数,接受表示未平仓/已平仓交易索引的整数值。首个交易索引为0,最后一个交易索引比strategy.opentrades/strategy.closedtrades变量值小1。
以下示例演示了每个仓位最多下达5个不同ID的多头建仓订单,并计算特定已平仓交易的指标:
当收盘价上穿中位数但未达最高值时,若未平仓交易数少于5个,策略会下达新建仓订单。通过strategy.exit()的止损单或strategy.close_all()的市价单平仓。每个连续建仓订单的ID取决于未平仓交易数量,首个订单ID为“Buy0”,最后一个可能为“Buy4”。
脚本在for循环中调用strategy.closedtrades.*()函数获取已平仓交易的入场ID、盈亏、建仓K线索引和平仓K线索引,据此计算:
- 指定入场ID的已平仓交易总数
- 盈利交易数量
- 每笔交易平均持仓K线数
- 所有交易总盈利
最终将这些信息组织成格式化字符串,在单单元格表格中显示:

Mine Script®
已复制
请注意:
- 该策略每个仓位最多可建立5笔多头交易,因我们在strategy()声明语句中包含
pyramiding=5参数。更多信息请参阅金字塔加仓章节。 - 本脚本中的strategy.exit()实例会持续存在并为未平仓头寸中的每个建仓生成平仓订单,因我们未指定
from_entry参数。了解此行为的更多信息,请参阅多建仓平仓章节。
策略测试注意事项
在历史和实时市场条件下测试和优化策略,有助于了解策略特征、潜在弱点及未来潜力。但交易者需始终注意模拟结果的偏差和局限性,特别是在依据这些结果做出实盘交易决策时。本节将说明策略验证和优化过程中需注意的事项,以及可能的解决方案。
注意
尽管在现有数据上测试策略能为交易者提供有价值的参考信息,但必须注意:过去和现在的表现都不能保证未来结果。金融市场可能快速且不可预测地变化,这可能导致策略产生无法控制的亏损。此外,模拟结果可能无法完全反映影响实际交易表现的其他现实因素。因此,我们建议交易者充分理解回测和前瞻测试的局限性与风险,将其视为验证过程中的“组成部分”而非唯一决策依据。
回测与前瞻测试
回测是通过在历史市场数据上模拟和分析交易策略或模型的过往表现,来评估其历史绩效的技术。该技术假设策略在历史数据上的表现能够反映其优势和劣势。许多交易者在回测过程中会调整策略参数以优化结果。对历史结果的分析和优化有助于交易者更深入地理解策略特性,但需充分认识基于优化后回测结果做决策的风险和局限性。
同时,建议采用实时分析作为前瞻性评估交易系统的工具。前瞻测试旨在评估策略在实时市场条件下的表现,其中交易成本、滑点和流动性等因素可能显著影响绩效。虽然前瞻测试具有不受前视偏差(如未来数据泄露)影响的独特优势,但其测试数据量有限的缺点也不容忽视。因此,尽管前瞻测试能有效反映策略在当前市场环境中的表现,但通常不会单独使用。
前视偏差
回测策略时若请求非当前时间框架数据、使用timenow等重绘变量,或修改计算行为以实现K线内订单成交,常会出现未来数据在评估时泄漏到过去的情况,即前视偏差。这种偏差不仅会导致策略结果失真(因未来信息实际不可预知),也是造成策略重绘的典型原因之一。
交易者可通过在实时数据上前瞻测试来验证策略是否存在前视偏差——实时环境下最新K线之后不存在已知数据。若策略在历史K线和实时K线上表现不一致,则表明其存在前视偏差。
消除策略前视偏差的方法:
- 避免在订单逻辑中使用会导致未来值泄漏的重绘变量
- 调用
request.*()时不包含barmerge.lookahead_on参数(除非如重绘页面所述对数据序列进行偏移校正) - 采用符合实际的策略计算行为
选择偏差
当交易者仅分析特定品种或时间框架的结果而忽略其他数据时,就会出现选择偏差。这种偏差会扭曲对策略鲁棒性的判断,进而影响交易决策和绩效优化。交易者可通过以下方式降低选择偏差的影响:
- 在多样化品种和时间框架上评估策略表现
- 确保不忽略表现不佳的结果
- 避免“选择性”选取测试区间
过拟合问题
基于回测结果优化策略时常见的问题是“过拟合”(又称“曲线拟合”),即策略过度适配特定数据集。过拟合策略在新数据上往往表现不佳。为降低过拟合风险并提升泛化能力,广泛采用的方法是将品种数据分为两部分:用于优化的“样本内”(IS)数据和用于验证的“样本外”(OOS)数据。
该方法要求交易者在IS数据上优化策略参数,而后在未经调整的OOS数据上测试优化配置。尽管这种方法(以及其他更稳健的方案)能初步评估策略优化后的表现,但交易者仍需保持谨慎。由于未来具有不可预知性,任何交易策略都无法保证未来绩效——无论采用何种数据进行优化和测试。
订单限制
策略最多可跟踪9000笔订单。如果策略创建的订单超过9000笔,最早的订单将被裁剪,因此策略仅存储最近订单的信息。
被裁剪的订单不会显示在策略测试器中。使用strategy.closedtrades.*函数引用被裁剪的订单ID将返回na。
strategy.closedtrades.first_index变量保存最旧的未裁剪交易的索引,该索引对应交易列表中列出的第一笔交易。如果策略创建的订单少于9000笔,则不存在被裁剪的订单,该变量的值为0。