使用手册
功能在哪 · 是什么 · 怎么调出来一、Pine Script 是什么
Pine Script 是 TradingView 专有的脚本语言,特点:
- 纯云端脚本,不用装开发环境,网页里写,浏览器里编译运行
- 写
indicator画在图上,写strategy可以回测 - 回测由 TradingView 自带的"券商模拟器"(Broker Emulator)撮合
- 要实盘自动交易,需要配合 Webhook + 中间服务 + 券商 API
白话注解:Pine Script 就是 TradingView 里的"Excel 公式"——你在网页上写几行代码,它自动帮你画均线、发信号。但它本身不能帮你下单,最多帮你发通知。
1、和 MT4/MT5 的 EA 有什么区别
| 对比项 | Pine Script(TradingView) | MQL4/MQL5(MetaTrader) |
|---|---|---|
| 运行位置 | TradingView 云端服务器 | 你自己的电脑/VPS |
| 数据源 | TradingView 提供的数据 | 券商服务器数据 |
| 自动下单 | 不能直接下,要走 Webhook | 能直接下 |
| 最小执行粒度 | 一根K线(Bar) | Tick 级 |
| 学习曲线 | 平缓,几小时上手 | 陡峭,要学几周 |
白话注解:Pine 适合"想清楚策略逻辑 + 看历史成绩",MQL 适合"真正挂在服务器上自动跑"。很多成熟做法是:Pine 里研究信号,MQL 或第三方跟单工具里做执行。
二、strategy.exit:止损止盈与移动止损
1、完整签名(官方参考手册核实)
strategy.exit(id, from_entry, qty, qty_percent, profit, limit, loss, stop,
trail_price, trail_points, trail_offset, oca_name,
comment, comment_profit, comment_loss, comment_trailing,
alert_message, alert_profit, alert_loss, alert_trailing)
2、六种出场参数,两种单位
| 参数 | 单位 | 含义 |
|---|---|---|
limit | 价格 | 限价单到指定价位止盈 |
stop | 价格 | 止损单到指定价位止损 |
profit | tick 数 | 盈利 N 个 tick 挂限价止盈 |
loss | tick 数 | 亏损 N 个 tick 挂止损 |
trail_price | 价格 | 移动止损的触发价位 |
trail_points | tick 数 | 移动止损的触发盈利(tick) |
trail_offset | tick 数 | 触发后,止损挂在触发价多少个 tick 之外 |
最容易算错的地方:profit/loss/trail_points/trail_offset的单位是 tick(最小变动价位),不是价格、不是点、不是百分比!黄金 mintick=0.01 时,止损 5 美元 = 500 个 tick。换算公式:ticks = 价格距离 / syminfo.mintick。价格单位则直接用stop=和limit=传具体价格。
3、三种典型用法
//@version=5
strategy("exit 三种用法", overlay=true)
// 用法一:价格单位,开多时直接挂 2% 止损 + 4% 止盈
if ta.crossover(ta.sma(close, 10), ta.sma(close, 30))
strategy.entry("L", strategy.long)
strategy.exit("SLTP", from_entry="L", stop=close * 0.98, limit=close * 1.04)
// 用法二:tick 单位,用 mintick 换算(推荐写法,换品种不用改)
// 止损 200 tick、止盈 400 tick:
// strategy.exit("SLTP2", from_entry="L", loss=200, profit=400)
// 用法三:ATR 移动止损,浮盈超过 2 倍 ATR 后启动,距离 2 倍 ATR
atrVal = ta.atr(14)
if strategy.position_size > 0
strategy.exit("Trail", from_entry="L",
trail_points=(atrVal * 2) / syminfo.mintick,
trail_offset=(atrVal * 2) / syminfo.mintick)
4、使用规则(易踩的坑)
strategy.exit每调用一次只出场一次,要多次分批出场就传不同的 id 多次调用- 若
profit、limit、loss、stop、trail_price、trail_points全是 na,该命令直接失败(什么都不挂) - 止损+移动止损同时给时,同属 stop 类订单,只会挂先被触发的那一个
- 入场单还没成交时,对应的 exit 单会排队等入场成交后再生效
- 想市价信号出场用
strategy.close;strategy.exit是预挂价格单,二者可同时用,先到先平 - exit 单会一直挂着,直到成交、仓位被别的指令平掉、或被
strategy.cancel撤销
白话注解:记住一句话——"条件出场找 close,价格出场找 exit"。有人写了 stop 参数却放在 strategy.close 里,止损永远不生效,回测看起来没亏过钱,一上实盘全露馅。
操作教程
一步一步 · 跟着做完一件事三、开发环境与第一个脚本
- 打开 TradingView 任意图表
- 图表底部点 Pine Editor(Pine 编辑器)
- 左侧写代码,点 Save 保存
- 点 Add to chart(添加到图表)加载运行
- 报错会在编辑器下方红字提示,点错误可跳到出错行
白话注解:整个开发流程都在浏览器里完成。保存的脚本在"Pine 编辑器 → 管理脚本"里能找回。别在别人发布的图表脚本上直接抄来改——先看懂每一行再动手。
1、第一个脚本:Hello Pine
//@version=5
indicator("我的第一个指标", overlay=true)
plot(close, title="收盘价", color=color.blue)
保存并添加到图表,主图上会画出收盘价曲线。//@version=5 必须写在第一行,声明脚本用的语言版本。
四、脚本的三种类型
| 声明函数 | 用途 | 能模拟下单吗 |
|---|---|---|
indicator() | 画指标、标记信号、发告警 | 不能 |
strategy() | 回测策略,模拟买卖,生成绩效报告 | 能(模拟盘) |
library() | 封装可复用的函数库,供其他脚本引用 | 不能 |
白话注解:indicator = 只画图不交易;strategy = 模拟回测,告诉你历史上这么做赚不赚钱;library = 把常用函数打包,别的脚本 import 来用。一个脚本只能声明一次类型,不能既当指标又当策略。
五、版本声明与语法基础
1、缩进敏感
Pine 用缩进表示代码块(类似 Python),if 内的语句必须缩进 4 个空格或 1 个 Tab:
//@version=5
indicator("缩进演示")
if close > open
plot(close, color=color.green)
2、声明与重新赋值
//@version=5
indicator("赋值演示")
myVar = 10 // 首次声明,用 =(类型自动推断)
myVar := myVar + 1 // 已声明变量重新赋值,必须用 :=
var int counter = 0 // var 声明:只初始化一次,之后每根K线保留上次值
counter := counter + 1
最常见的编译错误之一:=和:=混用。=只在首次声明时用,之后每根K线上更新变量一律用:=。
3、var 与普通变量的区别
- 普通变量:每根新K线都重新初始化,值不跨K线保留
var变量:只在第一根K线初始化一次,之后一直保留(做计数器、记录最高价、状态机必备)varip变量:在实时K线的每个 tick 之间也保留(仅实时有效,历史K线上行为同var)
六、数据类型与变量
1、基本类型
| 类型 | 说明 | 示例 |
|---|---|---|
int | 整数 | length = 14 |
float | 浮点数 | price = close |
bool | 布尔 | isUp = close > open |
string | 字符串 | msg = "金叉" |
color | 颜色 | myColor = color.red |
2、series 修饰符
Pine 里变量还带"时间维度":series float 表示"每根K线上都有一个值的历史序列"。内置的 close、指标输出都是 series。规则:大部分函数参数要求 series 类型,但有些地方(如 request.security 的周期参数在 v5 中)要求"简单类型"(simple string),不能用动态计算的值——这是新手报错高发区。
3、na 处理
//@version=5
indicator("na 演示")
m = ta.sma(close, 20) // 前19根K线上没有足够数据,值为 na
isReady = not na(m) // 用 na() 判断,not na() 即"数据就绪"
safeValue = nz(m, close) // nz():如果是 na 就用第二个参数兜底
白话注解:na 就是"这个位置没数据"。均线前 19 根K线、会话外的K线,都可能是 na。判断不用== na,要用na(m)函数。
七、input 输入参数家族
input.* 让脚本的参数变成图表上的"设置项",不用改代码就能调:
//@version=5
indicator("input 演示", overlay=true)
fastLen = input.int(10, title="快线周期", minval=1, maxval=200)
slowLen = input.int(30, title="慢线周期", minval=1, maxval=400)
useFilter = input.bool(true, title="启用RSI过滤")
src = input.source(close, title="数据源")
sess = input.session("0930-1600", title="交易时段")
tf = input.timeframe("D", title="高周期")
symbol = input.symbol("BINANCE:BTCUSDT", title="对比品种")
group示例 = input.int(14, title="RSI长度", group="过滤参数", tooltip="RSI 的计算周期")
| 函数 | 控件类型 |
|---|---|
input.int / input.float | 数字框(可加 minval/maxval/step) |
input.bool | 开关 |
input.string | 文本框(可加 options 下拉列表) |
input.source | 数据源选择(收盘价/最高价/其他指标) |
input.session | 时段选择器(配合 time() 使用,见第十七章) |
input.timeframe | 周期选择器(配合 request.security 使用) |
input.symbol | 品种选择器 |
input.color | 取色器 |
白话注解:group参数把设置项分组显示,tooltip是鼠标悬停提示。参数一多,加分组让设置面板不乱。
八、内置变量与K线数据
1、当前K线数据
close // 当前K线收盘价
open // 开盘价
high // 最高价
low // 最低价
volume // 成交量
bar_index // 当前是第几根K线(从0开始)
time // 当前K线开盘时间的Unix时间戳
time_close // 当前K线收盘时间戳
2、历史引用运算符 []
close[1] // 上一根K线的收盘价
close[2] // 上上根
high[1] - low[1] // 上一根K线的振幅
close[1]在第一根K线上没有上一根,返回 na。用到[n]的地方要考虑 na 情况。回看太远(如close[5000])可能触发max_bars_back缓冲区报错,可在声明函数里显式设置max_bars_back=5000解决。
3、品种与周期信息
syminfo.tickerid // 完整品种ID(交易所:代码),request.security 常用
syminfo.ticker // 品种代码
syminfo.mintick // 最小变动价位(tick 大小),算止损距离常用
syminfo.timezone // 交易所时区
syminfo.type // 品种类型(stock/futures/forex/crypto 等)
timeframe.period // 当前图表周期字符串(如 "5"、"D")
timeframe.isintraday // 是否日内周期
timeframe.isdaily // 是否日线
白话注解:syminfo.mintick 特别有用——比如黄金 mintick 是 0.01,那"跌 50 个 tick"就是 0.50 美元。strategy.exit 的 profit/loss 参数用的就是 tick 数(见第十三章),不搞清 mintick 必算错。
九、strategy() 声明参数全表
以下参数经 TradingView 官方《Strategy properties》帮助文档与官方参考手册核实,均为真实存在的 v5 参数:
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
title / shorttitle | string | — | 脚本名称 / 图表上显示的短名 |
overlay | bool | false | true = 叠加在主图上 |
initial_capital | int/float | 1000000 | 初始资金 |
currency | string | currency.NONE | 结算币种,如 currency.USD |
default_qty_type | string | strategy.fixed | 下单量单位:strategy.fixed(合约数)/ strategy.cash(金额)/ strategy.percent_of_equity(权益百分比) |
default_qty_value | int/float | 1 | 默认下单量 |
pyramiding | int | 0 | 同方向最多连续加仓次数。只对 strategy.entry() 生效,对 strategy.order() 无效 |
commission_type | string | strategy.commission.percent | 手续费类型:strategy.commission.percent(百分比)/ strategy.commission.cash_per_contract(每合约)/ strategy.commission.cash_per_order(每单) |
commission_value | int/float | 0 | 手续费数值。注意是双边收取(开仓平仓都收) |
slippage | int | 0 | 滑点,单位是 tick,市价单和止损单成交价会向不利方向偏移 |
margin_long / margin_short | int/float | 0 | 多/空保证金比例(%),模拟保证金不足强制平仓 |
calc_on_order_fills | bool | false | 订单成交后立即再计算一次(可盘中动作) |
calc_on_every_tick | bool | false | 实时K线每个 tick 都重算(历史K线无 tick 数据,会造成回测/实盘不一致,慎用) |
process_orders_on_close | bool | false | true = 本根K线收盘就撮合订单(默认是下一根开盘撮合) |
close_entries_rule | string | "FIFO" | 平仓顺序:"FIFO" 先进先出 / "ANY" 任意(美股期货外汇受 NFA 规则限制只能 FIFO) |
use_bar_magnifier | bool | false | 放大镜功能:回测时用更小周期数据撮合,更真实(付费套餐功能) |
backtest_fill_limits_assumption | int | 0 | 限价单成交严格度:为 0 时价格碰到限价就算成交;设为 n 则要求穿越 n 个 tick 才成交,回测更保守 |
max_bars_back | int | 0 | 历史引用缓冲区长度,[] 回看报错时手动调大 |
max_labels_count / max_lines_count / max_boxes_count | int | 50 | 图上最多保留的标签/线/框数量(上限 500) |
白话注解:默认 initial_capital 是 100 万,手续费滑点全是 0——这种默认设置回测出来的成绩基本是"理想国"。写正式策略前,先把这几个数字改成接近你真实账户和券商的水平。
十、下单指令族:entry / order / close / close_all
1、四个指令一览
// 开仓(受 pyramiding 限制,自动处理反手)
strategy.entry("MyLong", strategy.long, qty=100)
strategy.entry("MyShort", strategy.short, qty=100)
// 底层挂单(不受 pyramiding 限制,不自动反手)
strategy.order("Add", strategy.long, qty=10, limit=close * 0.99)
// 平指定 id 的仓位(市价平仓)
strategy.close("MyLong", comment="信号平多")
// 平掉所有仓位(市价)
strategy.close_all(comment="清仓")
// 撤销未成交的挂单
strategy.cancel("MyLimitOrder")
strategy.cancel_all()
2、strategy.entry 完整签名(v5,官方参考手册核实)
strategy.entry(id, direction, qty, limit, stop, oca_name, oca_type,
comment, alert_message)
| 参数 | 说明 |
|---|---|
id | 订单标识(字符串)。同一 id 的未成交订单会被修改而不是重复下 |
direction | strategy.long 或 strategy.short |
qty | 数量(合约/股/手)。不填用 default_qty 设置 |
limit | 限价。填了就是限价单 |
stop | 触发价。只填 stop 是止损单;stop+limit 都填是止损限价单 |
oca_name / oca_type | OCA 组:strategy.oca.none / strategy.oca.cancel(一组里成交一单撤其余)/ strategy.oca.reduce(成交一单其余减量) |
comment | 备注,显示在交易明细和图上 |
alert_message | 自定义消息,通过 {{strategy.order.alert_message}} 占位符进 Webhook(见第十八章) |
v5 的下单函数已不再使用 v4 的when参数。条件下单一律用if 条件把下单语句包起来,别照抄老代码。
3、entry 的自动反手机制
持多仓时调用 strategy.entry("S", strategy.short),模拟器会自动先平多再开空,一笔订单完成反手。这是 strategy.entry 和 strategy.order 的核心区别:
| 对比项 | strategy.entry | strategy.order |
|---|---|---|
| 受 pyramiding 限制 | 是 | 否 |
| 自动反手 | 是 | 否(只会往同方向堆仓位) |
| 典型用途 | 常规策略进出场 | 精细控制加仓/对冲/网格 |
白话注解:反手是双刃剑。你以为只是"平仓",结果 id 写错方向变成了反手开空,仓位直接翻倍。写策略时先用 strategy.position_size(当前仓位,多头为正、空头为负)判断清楚现在的状态再下单。
十一、挂单管理与订单执行模型
1、默认执行时序(最关键的一张图)
K线A收盘 → 策略计算 → 产生信号/挂单
K线B开盘 → 模拟器撮合上一根K线产生的市价单(以B开盘价成交)
K线B运行中 → 限价单/止损单按B内价格触发
K线B收盘 → 再计算 → 下一轮
白话注解:默认模式下,你"本根K线收盘看到金叉",实际成交在"下一根开盘价"。这个一根K线的延迟是回测和实盘对不上的第一大原因。想让收盘价立刻成交,加 process_orders_on_close=true——但实盘里你也得真的能在收盘那一瞬间下单,否则是自欺欺人。
2、撤单
strategy.cancel("MyLong") // 撤销指定 id 的未成交挂单
strategy.cancel_all() // 撤销全部未成交挂单(不动持仓)
注意:strategy.cancel 撤的是"挂单",strategy.close/close_all 平的是"持仓",两回事。收盘清场标准写法是两个都调。
3、收盘前强制清仓
//@version=5
strategy("日内清仓", overlay=true)
// 15:50 后平掉所有仓位并撤单(交易所时区)
isLate = hour == 15 and minute >= 50
if isLate and strategy.position_size != 0
strategy.close_all(comment="收盘前清仓")
strategy.cancel_all()
十二、多周期与跨品种数据:request.security
1、基本用法
//@version=5
indicator("多周期均线", overlay=true)
// 当前品种的日线 20 均线(画在小时图上)
dailyMA = request.security(syminfo.tickerid, "D", ta.sma(close, 20), lookahead=barmerge.lookahead_off)
plot(dailyMA, color=color.purple, title="日线MA20")
// 跨品种:拿 BTC 的收盘价对比
btcClose = request.security("BINANCE:BTCUSDT", timeframe.period, close, lookahead=barmerge.lookahead_off)
签名:request.security(symbol, timeframe, expression, gaps, lookahead)。gaps 控制请求周期的K线没对齐时返回 na(barmerge.gaps_on)还是沿用上一值(barmerge.gaps_off,默认)。
2、未来函数(lookahead)——回测虚高的头号来源
lookahead=barmerge.lookahead_on会让回测"提前知道"高周期的收盘数据——相当于作弊看答案。v5 默认barmerge.lookahead_off,但建议显式写出来,防止复制老代码时把lookahead_on带进来。判断脚本有没有未来函数最简单的办法:把lookahead_on全部改成lookahead_off再跑一次,如果绩效大幅变差,原脚本就有未来数据泄漏。
3、方向限制与调用上限
request.security只能取等于或高于当前图表周期的数据;要取更低周期数据用request.security_lower_tf()(v5 新增)- 每个脚本最多 40 个不重复的 request 调用(Ultimate 套餐 64 个);相同参数的重复调用不重复计数
- 多个值一次请求:元组写法
[ma, rsi] = request.security(syminfo.tickerid, "D", [ta.sma(close, 20), ta.rsi(close, 14)])
4、高周期未收盘数据的重绘问题
小时图上取日线数据时,"当天的日线"还没走完,它的 close 全天都在变——这叫重绘(repaint)。两个稳妥做法:
// 做法1:取上一根已收盘的高周期K线,杜绝重绘
dailyCloseConfirmed = request.security(syminfo.tickerid, "D", close[1], lookahead=barmerge.lookahead_off)
// 做法2:只在实时K线上取当前值,历史K线取上一根(官方文档标准方案)
f_secure(_tf, _expr) => request.security(syminfo.tickerid, _tf, _expr[barstate.isrealtime ? 1 : 0])
十三、时间与会话过滤
1、time() 函数与会话字符串
签名(官方文档核实):time(timeframe, session) 和 time(timeframe, session, timezone)。当前K线在会话内返回K线时间戳,不在则返回 na——所以判断写法是 not na(...):
//@version=5
strategy("时段过滤", overlay=true)
// 会话字符串格式 "HHMM-HHMM:日期",日期数字 1=周日 ... 7=周六
// 0930-1600 且只要工作日(2~6):
sessInput = input.session("0930-1600:23456", title="交易时段")
inSession = not na(time(timeframe.period, sessInput, "America/New_York"))
longCond = ta.crossover(ta.sma(close, 10), ta.sma(close, 30))
if longCond and inSession
strategy.entry("L", strategy.long)
// 背景染色直观查看时段
bgcolor(inSession ? color.new(color.blue, 92) : na)
2、时区与跨零点会话(高频踩坑点)
会话字符串默认按交易所时区解释,不是你电脑的时区!美盘品种在东八区看会差十几个小时。第三个参数传 IANA 时区名(如"Asia/Shanghai"、"America/New_York")或 GMT 偏移(如"GMT+8")显式指定。另外:星期数字 1 是周日、2 才是周一,与直觉相反;结束时间早于开始时间表示跨零点时段(如2200-0600);日线及以上周期用会话过滤没有意义(一根K线已经覆盖全天)。
3、其他时间函数
year(time) / month(time) / dayofmonth(time) / dayofweek(time) / hour(time) / minute(time)
timestamp("2026-01-01T00:00:00") // 构造指定时刻的时间戳
timenow // 当前真实时间(每tick变)
barstate.isconfirmed // 实时K线是否已收盘
barstate.isrealtime // 当前是否实时K线(历史为false)
barstate.islast // 是否最后一根K线
白话注解:实盘告警防重绘的保险丝是 barstate.isconfirmed——只在K线收盘确认后动作。代价是反应慢一拍,但换来"信号不会反悔"。
十四、告警体系:alertcondition 与 alert
1、alertcondition(指标常用)
alertcondition(crossUp, title="金叉报警", message="出现金叉信号")
- 只能在
indicator里用(strategy 里写了也用不了) - 消息是编译时写死的字符串,不能动态拼变量
- 用户要手动在图表上创建 Alert,Condition 选这个指标,再选对应 alertcondition
2、alert(动态消息,指标和策略都能用)
if crossUp
alert("BUY " + syminfo.ticker + " @ " + str.tostring(close), alert.freq_once_per_bar_close)
频率常量(官方参考手册核实):
| 常量 | 触发时机 |
|---|---|
alert.freq_all | 条件满足的每次计算都触发(实时K线内每个tick都可能触发) |
alert.freq_once_per_bar | 每根K线最多触发一次(首次满足时) |
alert.freq_once_per_bar_close | 只在K线收盘确认时触发一次(实盘推荐,信号不反悔) |
3、策略的订单成交告警
strategy 里创建 Alert 时,条件选"Order fills only(仅订单成交)":每笔模拟订单成交时发通知,消息里可用 alert_message 参数 + {{strategy.order.alert_message}} 占位符传动态内容:
strategy.entry("L", strategy.long,
alert_message='{"action":"buy","symbol":"' + syminfo.ticker + '","price":' + str.tostring(close) + '}')
白话注解:alertcondition 是"固定话术的路牌",alert 是"现场播报",订单成交告警是"每笔交易自动回执"。做 Webhook 自动交易主要靠后两种。
十五、Webhook 实盘架构
1、信号流向
TradingView云端策略触发 → Webhook URL(HTTP POST) → 你的中间服务器/第三方跟单服务 → 券商API → 实盘下单
白话注解:TradingView 不直接帮你下单,它只发一个"我要买"的 HTTP POST 通知。你需要一个中间服务器收到通知后调券商 API 真正下单。中间件可以自己用 Python/Node 写,也可以用现成的第三方跟单桥服务。
2、官方硬性规则(创建 Webhook Alert 前必知)
- 套餐要求:免费版没有 Webhook 选项,至少需要付费套餐(Essential 及以上)
- 两步验证:账户必须开启 2FA,否则无法填 Webhook URL
- 端口:目标 URL 只接受 80 和 443 端口
- 超时:接收方 3 秒内没响应,TradingView 就放弃投递(中间件要做异步处理,收到先回 200 再慢慢干活)
- 熔断:同一个策略告警 3 分钟内触发超过 15 次会被自动停止
- 服务端副本:策略告警跑的是 TradingView 服务器上的脚本副本,改了代码后必须删除并重建 Alert 才生效
3、消息占位符全表(Alert 的 Message 框里用)
| 占位符 | 返回值 |
|---|---|
{{ticker}} / {{exchange}} | 品种代码 / 交易所 |
{{close}} {{open}} {{high}} {{low}} {{volume}} | 触发K线的 OHLCV |
{{time}} / {{timenow}} | K线时间 / 实际触发时间(UTC) |
{{interval}} | 图表周期 |
{{plot_0}} ~ {{plot_19}} / {{plot("名称")}} | 指标输出(只对指标告警有意义) |
{{strategy.order.action}} | buy / sell(仅策略告警有效) |
{{strategy.order.contracts}} / {{strategy.order.price}} | 订单数量 / 订单价格 |
{{strategy.position_size}} | 下单后的仓位 |
{{strategy.order.alert_message}} | 下单函数里写的 alert_message |
{{strategy.*}} 占位符只在策略的告警里生效;用在指标告警里会原样发出字符串,JSON 直接报废。
4、标准中间件 Payload 示例
{
"secret": "REPLACE_WITH_LONG_RANDOM_TOKEN",
"symbol": "{{ticker}}",
"action": "{{strategy.order.action}}",
"qty": "{{strategy.order.contracts}}",
"price": {{close}},
"position_after": {{strategy.position_size}},
"fired_at": "{{timenow}}"
}
中间件安全三件事:① 校验 secret 字段(共享密钥),拒绝不明来源的 POST;② 绝不要把券商 API 密钥写在告警消息里(TradingView 官方文档明确警告);③ 对同一品种加节流/幂等处理,防重复信号重复下单。
十六、绘图进阶与信息面板
1、label / line / box(动态绘图对象)
//@version=5
indicator("绘图对象", overlay=true, max_labels_count=500)
if barstate.islastconfirmedhistory
// 标签
label.new(bar_index, high, "最新价 " + str.tostring(close),
style=label.style_label_down, color=color.blue, textcolor=color.white)
// 直线
line.new(bar_index - 20, ta.highest(high, 20), bar_index, high, color=color.red)
// 方框
box.new(bar_index - 10, high, bar_index, low, bgcolor=color.new(color.green, 85))
2、table 信息面板
//@version=5
indicator("状态面板", overlay=true)
var table t = table.new(position.top_right, 2, 3, bgcolor=color.white, border_width=1)
if barstate.islastconfirmedhistory
table.cell(t, 0, 0, "指标")
table.cell(t, 1, 0, "数值")
table.cell(t, 0, 1, "RSI")
table.cell(t, 1, 1, str.tostring(ta.rsi(close, 14), "#.0"))
table.cell(t, 0, 2, "收盘")
table.cell(t, 1, 2, str.tostring(close, "#.00"))
白话注解:table做实时状态面板(仓位、信号、风控状态一目了然),配合var table+barstate.islastconfirmedhistory只在最后一根K线刷新,不拖慢历史计算。注意声明里加max_labels_count等,否则默认只显示最近 50 个对象。
十七、参数优化与过拟合陷阱
1、给参数划边界
fastLen = input.int(10, title="快线", minval=2, maxval=60, step=2)
限定范围后,用图表上方的"策略测试器 → 参数"做步进优化。TradingView 付费套餐还有 Deep Backtesting(深度回测)可拉长历史。
2、回测可信度自检清单
- 手续费设了吗?默认是 0,双边收费品种必须设置
- 滑点设了吗?
slippage至少给 1-2 个 tick - 用了
calc_on_every_tick吗?历史K线没有 tick 数据,回测结果和实盘必然不一致 request.security有没有lookahead_on?有没有引用未收盘的高周期数据?- 信号用的是
close还是实时价?收盘确认了吗? - 交易笔数够不够?30 笔以下的回测没有统计意义
- 换了品种、换了周期,结论还成立吗?(参数敏感性检验)
风险口径:参数调到回测最漂亮的那个组合,大概率是对历史噪音的拟合,不是未来会重演的规律。回测业绩不代表未来收益,上线前先在小仓位或模拟盘上跑一段(前向验证)。
十八、Pine Script v6 新版要点
v6 于 2024 年 12 月发布(官方博客核实),v5 脚本继续可用,新旧不互通:新功能只在 v6 开放。主要变化:
| 变化 | v5 行为 | v6 行为 |
|---|---|---|
| bool 类型 | 可取 true / false / na 三态 | 严格二值,不能再是 na;na() 不再接受 bool |
| 数字转 bool | 0=false、非0=true 隐式转换 | 必须显式 bool() 转换,否则编译报错 |
| and / or 求值 | 两边都算(eager) | 短路求值(lazy),左侧已定则不算右侧 |
when 参数 | 下单函数已废弃 | 正式移除 |
| request.\*() 调用 | 必须全局作用域 + 静态字符串 | 动态请求:品种/周期可用 series string,可在循环、条件内调用 |
| 整数除法 | 1/2 结果为 0 | 1/2 结果为 0.5 |
| 负数组索引 | 不支持 | array.get(arr, -2) 直接取倒数第二个 |
| 9000 笔交易上限 | 超出报错停止计算 | 自动裁剪最旧交易继续算,新增 strategy.closedtrades.first_index 定位最早未裁剪交易 |
| 枚举 / 日志 | 无 | 新增 enum、log.info()/log.warning()/log.error() |
白话注解:本教程用 v5 写,因为 v5 资料最多、存量脚本最大。新写脚本可以上 v6(编辑器"管理脚本 → 转换代码为 v6"有官方迁移工具,但不是所有脚本都能全自动转,转换后要人工核对)。v5→v6 最容易翻车的点:三态 bool 和整数除法,老代码里靠 na 判断状态的逻辑要重写。
💬 用户评价 (0)
立即登录