AI 幫你生出 Pine Script 策略了
從程式碼到真正能自動交易還差多少
你把想法講給 AI 聽,它丟回一段 Pine Script,貼進去一次就編譯過。這件事現在真的不難。 沒有人先跟你講的是:「程式碼能編譯過」跟「程式碼能自動下單」是兩道完全不同的關卡, Pine Script 有一長串規則在決定你落在哪一邊。
這篇文章不評論你用的是哪個 AI 工具好不好——我們沒做過這種測試,這篇也不會做。 重點在程式碼底下這個語言與平台本身:如果生出來的是 indicator() 而不是 strategy(),如果宣告了版本卻用了另一版的語法,或是不小心在回測裡洩漏了未來資訊, 程式碼一樣會編譯過——只是上線之後不會做你以為它會做的事。這篇就照你實際會踩到的順序, 把這些落差一段段拆開。
先貼這段:要程式碼時附在需求上面的規格
從這裡開始,因為這篇剩下的內容基本上就是在解釋這幾條為什麼要存在。 把下面這段貼在你自己那段白話策略描述的上面。每一條都是你事後可以自己核對的規則, 而要求它逐條編號回覆,就是讓核對變便宜的關鍵:
在你開始寫之前,先把下面每一條都套用進去,並在回覆裡逐條編號確認:
1. 用 strategy() 宣告,不要用 indicator()。我需要成交事件的 alert,
而帶「strategy」前綴的 placeholder 只能用在 strategy 建立的 alert 裡。
2. 第一行放 //@version=6。不要省略版本宣告那一行。
3. 只用 v6 語法:下單函式不要用 "when =" 參數(改成用 "if" 包住呼叫);
任何會回傳值的 switch / if 都必須有 default / else 分支;
需要 bool 的地方要用 bool() 顯式轉型。
4. 明確告訴我你把 margin_long 與 margin_short 設成多少,以及為什麼。
5. 如果你用了 request.security() 搭配 lookahead = barmerge.lookahead_on,
序列必須用 [1] 位移。如果邏輯上根本不需要 lookahead,就完全不要用。
6. 進場與出場條件都用 barstate.isconfirmed 把關。
7. Alert message 必須是單行、合法的 JSON,不能有換行,
欄位就這幾個:[貼上你接收端要求的欄位清單]
策略邏輯:[用白話描述你的進場與出場條件]第 4 條之所以要它「講出來」而不是「設一下就好」,是因為預設值在版本之間變過—— 而一個被講出來的數字,你才有辦法跟它爭論。 接下來幾節,就是這幾條各自的「為什麼」,順序照你實際會踩到的先後排。
落差一:你拿到的是 indicator,還是 strategy?
Pine Script 的腳本分成兩種宣告類型,差異不只是名稱。TradingView 參考手冊寫得很直白: indicator() 是「一個宣告語句,用來標示這支腳本是 indicator」,而 strategy()「將腳本標示為 strategy」,並解鎖下單能力。官方 Strategies 文件講得更明確:strategy 是「用來在歷史與即時 K 棒上模擬交易的腳本」,能「下單、修改、取消假設性訂單」—— 這些能力 indicator 完全沒有。
如果你跟 AI 說「幫我寫一個 RSI 超賣就買進的策略」,這句話有一種合理的理解方式, 會生出一支「畫出 RSI 交叉點」的腳本——語法沒錯、畫面看起來也對,但本質上就是 indicator。 它會乾淨地編譯過,圖表上看起來完全正常,但裡面找不到任何一行 strategy.entry(), 因為 indicator() 腳本根本沒有這個命名空間可以用。
| indicator() | strategy() | |
|---|---|---|
| 能在圖表上畫值 | ||
| 能模擬下單(strategy.entry/exit) | ||
| 有回測 / Strategy Tester 結果 | ||
| alertcondition() alert 有效 | ||
| 成交 alert 帶得出 {{strategy.*}} 資料 |
最後一列才是真正會讓自動化斷掉的地方,值得直接引官方原文。TradingView 官方的 alert placeholder 文件把 placeholder 分成幾組,並直接寫下這句話:
這句話正好解釋了一個很常見的翻車場景:把 AI 生成的程式碼接上 webhook 時, alert 訊息裡寫了像 {{strategy.order.action}} 或 {{strategy.order.contracts}} 這樣的 placeholder,alert 正常觸發、沒有任何錯誤訊息,但這些 placeholder 換回來的是空字串——因為這支腳本是 indicator,根本沒有下單、沒有成交,這些 placeholder 自然沒有資料可以替換。 TradingView 的介面不會阻止你在 indicator 的 alert 訊息裡打這些字,它只是不會把值填進去。
如果你需要這些成交 placeholder,官方 FAQ 直接給了做法:把 indicator() 宣告換成 strategy(),沿用原本畫圖用的觸發條件 (交叉點、突破條件),再加一行下單指令——strategy.entry("Long", strategy.long)——去真正觸發那筆之後 alert 要回報的模擬下單。
alertcondition() 跟 alert() 也容易搞混
跟 indicator/strategy 的分野無關,另一個容易踩的坑是 alertcondition() 只能用在 indicator。官方文件寫的是:把它放進 strategy 腳本「不會造成編譯錯誤,但無法從中建立 alert」——程式碼照跑,只是「Create Alert」 對話框裡永遠不會出現這個選項。相對地,alert() 兩種腳本類型都能用, 而且訊息可以動態組出來。
落差二:你拿到的到底是哪個版本的語法?
Pine Script 目前最新版本是 v6,前面是 v5(2021 年 10 月)跟 v4(2019 年 6 月)。版本宣告不是裝飾用的,而是一個編譯器指令,官方文件對「不寫會怎樣」 講得很清楚:
不會跳警告、不會報錯——少了 //@version= 的腳本,會悄悄用 version 1 的規則去編譯,那個版本舊到會拒絕你以為理所當然能用的語法。就算版本行有寫, v5→v6 的 migration guide 也列出好幾個「舊寫法在新規則下悄悄跑歪,而不是直接報錯」的變更。 挑幾個最常見的:
| 變更項目 | 舊版行為 | v6 行為 |
|---|---|---|
| switch / if 回傳 unique type | 不需要 default/else,沒涵蓋到的情況悄悄回傳 na | 必須有 default/else,否則編譯失敗 |
| 下單函式的 when 參數 | strategy.entry("Long", strategy.long, when = cond) | 已移除,要改寫成 if cond 包住 |
| 預設 margin_long / margin_short | 0——strategy 永遠不會被強平 | 100——資金不足時單子會被拒絕,虧損過大也可能觸發強平 |
| int/float 用在需要 bool 的地方 | 自動隱式轉型成 bool | 必須顯式轉型:bool(x) |
margin_long/margin_short 的預設值是最容易被忽略的一項: 同一段 v5 程式碼,只換版本宣告成 v6、其他都沒改,回測結果可能從「從來不會被強平」 變成「一堆單子被拒絕、強制平倉」——邏輯完全一樣,只是預設值換了。如果 AI 的訓練資料 混雜新舊版範例,生出來的程式碼在這些規則上落在哪一邊都有可能,而且你很可能要等到回測、 甚至上線後 alert 表現跟預期不一樣才會發現。
所以這一關不要靠自己讀程式碼,直接問:「這支腳本的 margin_long 跟 margin_short 設成多少?如果沒有明確設定, 那在它宣告的那個版本底下,預設值是多少?」有把兩個數字跟版本一起講出來的回答,你對照上面那張表十秒就能驗完; 回你「預設值沒問題」的,等於沒回答。
落差三:回測漂亮到不真實嗎?
TradingView 官方文件對 repainting 的定義很直白:「script behavior causing historical vs realtime calculations or plots to behave differently」(腳本在歷史 K 棒與即時 K 棒的計算或繪圖表現不一致)。 下一句話值得停下來看,因為它把整個話題的框架換了一遍:「more than 95% of indicators in existence exhibit some form of repainting behavior」(超過 95% 現存的 indicator 都帶有某種形式的 repainting)。 Repainting 本身不等於有 bug——它是一道光譜,官方文件把成因粗略分成從 「普遍且通常沒問題」到「不可接受」的等級。
「不可接受」這一級,才是你準備把 AI 生成的策略交給真錢下單時真正要在意的地方: 把未來資訊洩漏到過去。最常見的方式是 request.security() 用了 lookahead = barmerge.lookahead_on,卻沒有搭配對應的 [1] 位移。TradingView 文件直接寫出後果:
官方文件甚至附了一段範例程式碼,註解直接寫著 FUTURE LEAK! DO NOT USE!——這個坑常見到 TradingView 特地點名警告:
// FUTURE LEAK! DO NOT USE!
htfClose = request.security(syminfo.tickerid, "D", close, lookahead = barmerge.lookahead_on)
// 正確寫法:用 [1] 位移,確保只用已確認的資料
htfClose = request.security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)這段是給你認形狀用的,不用背。你要找的只有兩樣東西在不在同一行:lookahead_on 跟 [1]。第一個出現、第二個沒出現,就是你該追問的點。 懶得自己看的話,「列出所有 request.security() 呼叫,逐一告訴我有沒有開 lookahead、 序列有沒有用 [1] 位移」可以直接換來一張表。
這個錯誤會讓歷史回測看到「還沒發生」的價格——這正是它能漂亮到不合理的原因。 但實盤當下沒有未來的 K 棒存在,同一段程式碼上線之後行為完全不同。 TradingView 對這件事的態度很明確:「Using lookahead to produce misleading results is not allowed in script publications.」(用 lookahead 產生誤導性結果,不允許出現在已發佈的腳本中)
官方文件給的解法,是接受一個小小的取捨:把 lookahead_on 跟 [1] 搭配使用、在觸發條件加上 and barstate.isconfirmed, 或是把 alert 觸發頻率設成只在 K 棒收盤時觸發。TradingView 自己也坦白這是有代價的:「You can’t have your cake and eat it too」——不 repaint 就代表訊號一定會 比較晚出現。
落差四:把它變成 TradingView 真的會送出的 alert
就算策略回測邏輯正確,還需要正確設定 alert 才能讓任何東西送到 webhook。 兩個設定最關鍵:
| 設定項目 | 意思 | 為什麼在這裡重要 |
|---|---|---|
| Alert 類型 | alertcondition() vs alert() vs strategy 成交事件 | 決定訊息裡哪組 placeholder 有效——用錯組合不會報錯,只會拿到空值 |
| 頻率:Once per bar close | 等 K 棒收盤才觸發 | 是解決前面 repainting 問題最直接的做法 |
alert() 呼叫會自己把訊息組出來, 你不必在對話框裡再打一次。下單函式的 alert_message= 參數只有在成交事件觸發、且訊息裡用了 {{strategy.order.alert_message}} 時 才會被讀出來。「Create Alert」對話框的 Message 欄則是唯一能用 {{strategy.*}} placeholder 的地方,前提是這個 alert 建立在 strategy 上。 介面上要點哪裡(圖表右上時鐘 → Add Alert → Condition 選你的策略 → Notifications 勾 Webhook URL),在 用 Claude 寫 Pine 那篇有逐步走過一遍。在你準備拿這個接真錢之前,有一個結構性細節值得知道:對 strategy 建立 alert 的當下, TradingView 會把整支腳本複製一份到伺服器上。官方文件逐字寫的是,這份副本“runs independently from the chart’s strategy in your browser, and changes to your chart’s strategy will have no effect”(在伺服器上獨立於你瀏覽器裡那份圖表策略執行,你之後改圖表上的策略不會有任何影響)。 也就是說,之後回去改 AI 生成的程式碼,線上那個 alert 還是照舊版邏輯繼續跑, 除非你刪掉重建。
另外值得知道的是,通知只在即時成交時才會觸發——官方逐字是“Notifications are not sent for orders on historical bars.”(歷史棒上的下單不會發送任何通知)——而且單一 alert 有頻率限制, 3 分鐘內最多觸發 15 次,超過 TradingView 會直接停掉這個 alert。
落差五:AI 完全碰不到的那一段——webhook 到你的交易所
以上這一切,全都發生在 Pine Script 裡面,跑在 TradingView 的伺服器上。 沒有一個環節知道你的交易所帳戶存在。策略的權益只是你自己填在 initial_capital 裡的一個數字;TradingView 官方文件把整套模擬叫做「broker emulator」,成交的是「hypothetical orders(假設性訂單)」。 這個語言裡完全沒有提到你真實的 API key、真實的餘額,或是下單失敗時該怎麼辦—— 因為到了鏈路的最後一段,alert 就只是送一則訊息到一個 URL。之後發生的事, 完全在 Pine Script 之外,也完全不是任何 AI 在幫你寫 Pine 的時候能夠生成的東西。
接收 webhook、把它翻譯成一筆有身份驗證的交易所下單、算倉位大小、套用你自己的風控限制、 處理下單失敗的情況——這一段是接收端服務要做的事。
這正是 TVSBot 在處理的部分:每個用戶獨立的 webhook URL(獨立 token + payload secret 雙重驗證)、用 Fernet 加密儲存的交易所 API key(AES-128-CBC + HMAC-SHA256)、 透過你自己的帳戶在支援的 7 家交易所下單,以及你自己設定的風控——單筆上限、最大持倉數、 日虧損熔斷、一鍵 Kill Switch。新策略預設就勾好 dry-run,可以先讓訊號用模擬資金 跑完整條鏈路。
驗收指令:把程式碼貼回去,問它這幾題
下面那份檢查清單裡的每一項,也都可以直接變成問句。 如果你比較想「拿到答案列表」而不是自己去找,就送這一段。 刻意先不讓它改寫,是為了看出它到底有沒有讀過那支腳本:
這是一支我想拿去實盤交易的 Pine Script。先不要改寫,先回答我:
1. 宣告語句是 indicator() 還是 strategy()?把那一行原文引出來。
2. 它宣告的 //@version= 是多少?如果根本沒有,直接說沒有。
3. margin_long 跟 margin_short 設成多少?如果沒有明確設定,
在第 2 題那個版本底下的預設值是多少?
4. 列出所有 request.security() 呼叫,逐一說明有沒有開 lookahead、
序列有沒有用 [1] 位移。
5. 列出我的 alert message 會需要用到的所有 placeholder,
並逐一說明在第 1 題判定的腳本類型底下,它們是不是有效的。
6. Alert message 是不是單行、沒有換行的合法 JSON?
以上都回答完,再給我修正版,並告訴我你改了什麼。
[把你的腳本貼在這裡]第 5 題抓的是最常見的上線翻車:strategy 類的 placeholder 被放進 indicator 的 alert 訊息裡—— 這種錯不會吵,它會安靜地壞掉。
檢查清單:把 AI 生的 Pine 交給真錢之前
- 宣告語句:寫的是
indicator()還是strategy()?要拿到成交 alert 就一定要是strategy()。 - 版本行:有沒有
//@version=, 且版本是你實際要求的那個?沒寫會悄悄退回 v1 規則。 - v5/v6 預設值:檢查
margin_long/margin_short、任何缺 default/else 分支的switch/if,以及殘留在下單函式上的when =參數。 - lookahead 是否成對:任何用了
lookahead = barmerge.lookahead_on的request.security(),都要搭配[1]位移。 - Alert 類型與 placeholder 對得上:確認訊息裡的 placeholder (
{{strategy.*}}或{{plot_0}}這類) 真的對應你建的是 indicator alert 還是 strategy alert。 - 如果你要訊號反映「已確認的 K 棒」而不是進行中的,頻率設成 Once per bar close。
- Alert 訊息是合法的單行 JSON,格式對得上你的 webhook 接收端要解析的內容。
- 先跑 dry-run 再接真錢:完整跑一次「alert → webhook → 模擬下單」的鏈路,確認無誤再指向真實資金。
常見問題
AI 能寫出真的能自動交易的 Pine Script 嗎?
strategy() 而不是 indicator()、版本語法對不對得上你要的版本、 邏輯有沒有避開 repainting。這些都是你可以拿 TradingView 官方文件自己核對的 程式碼屬性,不是在評斷任何特定 AI 工具的好壞。為什麼我的 TradingView alert 訊息裡沒有下單資訊?
indicator(),但 alert 訊息用了 strategy 類的 placeholder,例如 {{strategy.order.price}}。 TradingView 官方 alert placeholder 文件明講:帶「strategy」前綴的 placeholder 只能用在 strategy 建立的 alert——用在 indicator 的 alert 裡, 它們會被替換成空值,而且不會有任何錯誤訊息提示。用 AI 生成的程式碼上線,我需要懂 Pine Script 嗎?
request.security() 呼叫有沒有把 lookahead_on 跟 [1] 位移搭配使用。 上面的檢查清單就是列給你核對用的。為什麼回測看起來很漂亮,實盤卻對不上?
request.security() 把未來的價格洩漏到了歷史 K 棒上。 TradingView 文件提到超過 95% 的 indicator 都有某種形式的 repainting, 而其中「不可接受」的那種(未來洩漏),可以讓回測看起來遠比實盤將來能重現的結果好看。Pine Script 知道我在交易所的真實餘額嗎?
Get started
把 AI 生好的 Pine Script 帶過來,webhook 到交易所這段交給 TVSBot——用你自己的 API key,先 dry-run,風控自己設。
免費開始使用