Pine Script 的 Alert 沒有記憶
為什麼天真的觸發邏輯會讓自動化壞掉
策略觸發了 alert,webhook 送出去,結果沒幾秒同一根 K 棒上又冒出一筆幾乎一樣的訂單。 或者反過來:你改了進場條件、存了檔,線上那顆 alert 卻還是照上禮拜的邏輯跑,好像 你什麼都沒改過一樣。這兩個都不是隨機出包。
它們的根源是同一件事:Pine Script 的 alert 不會記得自己已經送出過什麼——而且更容易被忽略的是, 它跑的根本不是你現在圖表上正在看的那份腳本。
如果這支策略是 AI 幫你生的,它不會主動提醒你這些事。因為這不是語法問題——程式碼照樣編譯過、 回測照樣好看,出事的是 TradingView 執行腳本的方式,以及 alert 的儲存機制。 所以這篇不是要教你寫 Pine,而是給你三件事:出事背後的機制、要 AI 生程式碼時該貼給它的指令原文, 以及拿到程式碼後該回頭問它哪幾個問題。
alert() 和 alertcondition() 的觸發頻率設定,只負責節流「同一根 K 棒內這個條件最多可以觸發幾次」——alert() 是靠程式碼裡的 freq 參數,alertcondition() 本身沒有 freq 參數,是在建立 alert 的對話框裡選 Frequency。它們不會確認 webhook 有沒有送達, 不會幫你去重,也不知道上一根 K 棒發生過什麼事——除非你自己用 var/varip 存起來。而且就算存了,這份記憶也只屬於跑在 TradingView 伺服器上的 那一個特定 alert 實例,不是屬於你在圖表編輯器裡看到的那份腳本。腳本為什麼沒有記憶:它每根 K 棒都從頭跑一次
Pine 不是把你的腳本對著整張圖表算一次就結束。TradingView 官方對執行模型的描述是: 腳本會「在歷史 K 棒與即時 tick 的序列上重複執行……針對每一根 K 棒各自計算一次」。 在已經收盤的歷史 K 棒上,這件事很單純:從最早的 K 棒到最新的,依序每根執行一次。
圖表最右邊那根還沒收盤的 K 棒——即時 K 棒——就不一樣了。因為它的 高/低/收盤價都還沒定案,腳本會在每一個新進來的 tick 上重新跑一次,用最新價格重新算過。 每次重跑之前,Pine 都會把這根 K 棒上的變數「重置」(rollback)回這個 tick 發生前的狀態, 確保上一次的暫時性結果不會滲進下一次計算。只有這根 K 棒最終、收盤那一個 tick 的結果, 才會真的寫進永久的歷史資料裡。
Strategy 是部分例外:預設情況下,strategy 跟 indicator 不同,即使是即時 K 棒也只在收盤後執行一次。要開啟 calc_on_every_tick 或 calc_on_order_fills,才會逐 tick 重新計算。
這件事對你下指令的影響是:當你跟 AI 說「同一個型態只准進場一次」, 它必須自己生一個「記憶」出來,因為這個語言本身沒有給它。 它會抓什麼來當記憶——一個旗標、一個計數器、記住某根 K 棒的編號——都是為了繞過這個執行模型。 下一節就是教你認出你拿到的是哪一種繞法。
AI 給的程式碼用了 varip,代表它想解決什麼
有兩個宣告關鍵字決定變數的值會不會跨執行保留下來,差異就正好對應上面講的 rollback 機制。 你看這張表不是為了自己寫得出來,而是為了:這兩個字出現在生成的程式碼裡時, 你知道 AI 當時在試圖解決什麼。
| 宣告方式 | 何時第一次設定 | 會不會跨 K 棒保留 | 即時 K 棒收盤前改動會不會留住 |
|---|---|---|---|
| (不加關鍵字) | 每次執行 | 不會——每次都重新初始化 | 不會 |
var | 該 K 棒收盤 tick 第一次執行時 | 會 | 不會——會被還原回上一次確認收盤的狀態 |
varip | 第一次執行,即使是收盤前 | 會 | 會——不受 rollback 影響 |
這張表是給你比對用的,不用背。
在歷史 K 棒上這個差異完全看不出來,因為每根本來就只執行一次——var 跟 varip 在那裡表現一模一樣。差異只會在即時 K 棒上顯現:你在收盤前用 var 改的旗標,下一個 tick 一進來就悄悄還原,只有收盤那一刻的版本才會真的定下來。 而 strategy 因為預設每根 K 棒(含即時)只執行一次,除非開啟 calc_on_every_tick 或 calc_on_order_fills,否則 varip 的表現會跟 var 一樣——這點很容易讓人誤以為 varip 永遠比 var 更「即時」。
所以你該做什麼?看到生成的程式碼裡有 varip, 通常代表 AI 想跨 tick 記住某個狀態——而在 alert 這個情境下,「跨 tick 記東西」 常常是用錯的方法回答錯的問題。直接問它:「這個狀態在 alert 觸發之後還可靠嗎?我把 alert 刪掉重建之後, 它在第一根 K 棒上的值是多少?」如果回答完全沒提到「會歸零」,那就是你該追問下去的點。 任何拿 var 旗標來當「不要重複觸發」防護的寫法,同樣問這一題。
如果對宣告與執行的基礎還不熟,可以先看我們的 Pine Script 入門教學——不過這篇剩下的內容,不看那篇也讀得完。
直接貼這段指令,多數坑就不會被寫進去
把下面這段當作「需求區塊」,貼在你自己的策略描述上面。它刻意要求 AI 逐條回報, 而不是讓它默默照自己的想法做:
幫我寫一支 TradingView 用的 Pine Script v6 策略。這支策略會接 webhook 去下真實訂單,
所以「可靠」比「漂亮」重要。
策略邏輯:[用白話描述你的進場與出場條件]
以下是硬性要求。全部照做,然後逐條編號回報你怎麼處理的:
1. 用 strategy() 宣告,不要用 indicator()。
2. 所有進場與出場條件,只能在「已確認的 K 棒」上判斷。
用 barstate.isconfirmed 把關。然後列出腳本裡還有哪些條件是在讀
「還沒收盤那根 K 棒」的 close / high / low。
3. 不要用 var 或 varip 來記住「這個訊號我已經送過了」。
如果你認為邏輯上非要這種記憶不可,先停下來跟我說明:
當我把 alert 刪掉、重新建立一個新的之後,那個旗標的值會是多少。
4. Alert message 必須是「單行、合法的 JSON」,不能有換行,
欄位就這幾個:[貼上你接收端要求的欄位清單]
5. 最後列出腳本裡所有「即時 K 棒行為跟歷史 K 棒行為不一樣」的地方。第 3 條是最有價值的一條。好的回答會告訴你「新建 alert 之後那個旗標會從零開始」; 如果它只回你一句「對,var 會保留」——這句話字面沒錯,但實務上會害你, 而且正好告訴你:去重這件事得放到腳本以外的地方做。
一、程式碼裡的
alert() 呼叫——訊息由程式當場組出來, 你不必在對話框裡再打一次。二、下單函式的
alert_message= 參數——這段文字只有在成交事件 觸發、且訊息裡用了 {{strategy.order.alert_message}} 時才會被讀出來。三、TradingView「Create Alert」對話框的 Message 欄——這是唯一能用
{{strategy.*}} placeholder 的地方,前提是這個 alert 建立在 strategy 上。介面上要點哪裡(圖表右上時鐘 → Add Alert → Condition 選你的策略 → Notifications 勾 Webhook URL),在 用 Claude 寫 Pine 那篇有逐步走過一遍。
線上跑的 alert,不是你現在看到的那份程式碼
這是最容易讓人意外的地方——很多人以為「alert 就等於我的腳本」,但其實不是。 當你按下 Create Alert 的那一刻,TradingView 會把腳本、輸入參數、圖表商品/週期做成一份快照, 之後這份快照就在它自己的伺服器上獨立運行。官方文件講得很明白:「之後對腳本輸入或圖表的 修改,不會影響已經根據它們建立的、正在運行的 alert。」FAQ 被問到「改腳本會不會讓正在跑 的 alert 跟著變」時回答更直接:「不會,除非你重新建一個 alert……要讓修改生效,必須刪除 既有的 alert、重新建立一個新的。」
這對任何你用 var/varip 存起來追蹤狀態的東西(例如一個「這個 訊號是不是已經送過」的旗標)有直接影響:這份記憶屬於 alert 正在跑的那份凍結快照, 不屬於你在圖表面板裡持續修改的腳本。你儘管改腳本——線上那顆 alert 內部的計數器還是照舊邏輯跑, 直到你把它刪掉重建。而你重建之後,那就是一份全新的快照:它的 var/varip 狀態從零開始,重新用歷史資料算一遍,完全不會繼承舊 alert 實例累積過的 任何記憶。(這最後一句結論官方文件沒有用單一句子直接講出來——它是把上面「快照機制」跟 下一節「腳本重啟」兩段官方陳述串起來推導出的結果。)
| 你改了什麼 | 正在跑的 alert 會跟著變嗎? | 官方依據 |
|---|---|---|
| 圖表編輯器裡的腳本程式碼 | 不會 | 「Subsequent changes to your script's inputs or the chart will thus not affect running alerts previously created from them.」 |
| 腳本的輸入參數 / 圖表商品或週期 | 不會 | 同一句官方原文——輸入參數與圖表本來就在建立當下被一併快照起來。 |
| 把 alert 刪掉重建 | 會——但那是一份全新的快照 | 「To update an alert after making changes, delete the existing alert and create a new one.」它的 var/varip 狀態會從零開始。 |
引用內容為 TradingView 官方 alerts 文件與 FAQ 的逐字原文。
這一段是最多人跟 AI 耗掉時間的地方。你把腳本貼回去、它找出問題、 你改好了、盯著線上看——結果什麼都沒變。不是因為改錯了, 而是因為那個修改根本沒有送到「正在跑的那個東西」上。再怎麼下指令都到不了一顆已經在跑的 alert。 唯一的路徑是:改腳本 → 把 alert 刪掉 → 重建一個新的。 這件事值得寫在便條紙上,因為它看起來實在太像「AI 沒修好」了。
「觸發了、訊號又不見了」:先檢查這個
TradingView 對 repainting 的定義很直白:「腳本在歷史計算跟即時計算/繪圖之間表現不同的行為」。 這不代表它自動就是壞事——官方把某些 repainting(例如 close 在還沒收盤的 K 棒上會浮動)歸類為「普遍但通常可接受」,但另一些型態則被標為「可能誤導」甚至「不可接受」 (用即時的 K 棒內資料去觸發 alert 或下單,就屬於不可接受的那一類)。
「觸發了、又消失」這個現象背後的機制,官方稱之為 fluid data(浮動資料):歷史 K 棒只會 存最終的 OHLC,但即時 K 棒的高/低/收盤價在收盤前會隨每個 tick 變動。如果你的條件式檢查的是 當下的 close 或 high,它可能在某個 tick 成立、alert 觸發了, 但等 K 棒真正收盤時條件已經不成立——alert 已經送出去了,下次刷新畫面時,條件卻看起來 「根本沒發生過」。
官方給的正式解法是強制等 K 棒收盤才判斷:把 alert 頻率設成Once Per Bar Close,或在程式碼裡用 barstate.isconfirmed 擋住條件。TradingView 也坦白說這是有代價的取捨:「這些方法雖然能避免 repainting,但也會讓 訊號比會 repaint 的腳本更晚觸發……魚與熊掌不可兼得。」這種「收盤 vs 即時」的張力,也是同一個 alert 有時候測起來幾乎瞬間送達、有時候又明顯變慢的部分原因——我們的webhook 延遲拆解文有講清楚哪些是你能調的、哪些是 TradingView 自己基礎設施的部分。
落到實務:Frequency 那個下拉選單是你自己在瀏覽器裡點的,十秒鐘的事。 其他部分則是「要交代給寫程式碼的那一方」的指令——「所有條件都用 barstate.isconfirmed 把關,然後告訴我你改了哪幾條」——這也正是上面那段指令第 2 條的內容。
把程式碼貼回去,問它這四個問題
不管腳本是 AI 生的、論壇抄的,還是你自己幾個月前寫的,這一輪都要跑。 刻意先不讓它改,是為了讓你看出它到底有沒有搞懂狀況,再讓它動手:
這是一支 Pine Script 策略。我要拿它建 TradingView alert 去接 webhook 實盤下單。
先不要改寫,先回答我這四題:
1. 這支腳本裡,有哪些條件可能「K 棒中途成立、收盤時又不成立」?逐行列出來。
2. 這裡面有沒有用 var 或 varip 來避免重複觸發?
如果有:我把 alert 刪掉、重新建一個之後,那個變數在第一根 K 棒上的值是多少?
3. 如果我把 alert 的 Frequency 設成「Once Per Bar Close」,
這支腳本的哪些邏輯行為會改變、哪些不會?
4. 這支腳本裡有沒有任何地方,是假設「我的伺服器已經收到上一則 alert」?
回答有或沒有,並指出是哪一行。
以上都回答完,再給我修正版。
[把你的腳本貼在這裡]第 4 題是故意設的陷阱,正確答案是「沒有」——Pine 腳本根本無從知道這件事。 如果它回答有,或是主動提議在腳本裡加一套「送達確認」機制, 那這則回覆裡的其他內容你都要多留一分懷疑。 這篇最後一節會說明為什麼 Pine 裡沒有東西能回答這個問題。
對照你的症狀,找出對應的機制
把上面幾件事串起來——逐棒執行、var/varip 的作用範圍、被凍結的 alert 快照、repainting—— 多數「我的自動化莫名其妙壞掉」的回報,其實都能對應到下面幾種機制之一。
上線前的自檢清單,以及腳本永遠做不到的那一件事
在你把這套東西指向真錢之前,把下面這張清單走一遍。 每一項都是你自己就能確認的——不是在瀏覽器上點一下,就是直接問 AI 一個問題, 完全不需要你會寫 Pine:
- alert 的 Frequency 設成 Once Per Bar Close,不是只設 Once Per Bar——光是這點就能消掉多數由 repainting 造成的重複觸發。
- 任何會用到
close/high/low的條件式, 都用barstate.isconfirmed擋住。 - 你知道 alert 上次是什麼時候重建的,能拿這個時間點跟你最後一次改腳本的時間比對。
- 用
var/varip旗標做「不要重複觸發」的防護時,清楚知道 alert 重建就會歸零——不是只有圖表刷新才會歸零。 - 你的接收端/執行端自己保留每一筆實際收到的 payload 紀錄,因為 TradingView 不會幫你留。
有一件事 Pine 真的給不了你:確認你送出的 webhook 有沒有被下游收到並處理。 alert() 和 alertcondition() 從設計上就是 fire-and-forget—— TradingView 的文件詳細描述了頻率跟觸發機制,但完全沒有任何一句話說 alert 引擎會追蹤 或確認接收端伺服器對這則訊息做了什麼。同一個訊號在下游是不是被處理了兩次,不是 Pine 執行環境設計上會知道的事。
這其實是執行端的問題,不是 Pine 的問題。TVSBot 的做法是把收到的訊號丟進背景非同步處理, 並把每一筆訊號連同原始 payload 與完整處理紀錄存下來,逐筆可查、可 Replay—— 讓你能實際核對是 TradingView 真的送了兩次,還是重複發生在鏈路的其他環節。
但要講清楚:那是事後查得到,不是自動去重。真正要修 alert 邏輯本身不可靠的問題, 還是得從上面那份 Pine 端 checklist 開始。
常見問題
為什麼 alert 在 K 棒中途觸發了,條件後來又消失了?
close 或 high 這種在 K 棒收盤前還會變動的值——這是一種 repainting。條件在 K 棒中途看起來成立、 觸發了 alert,等最後一個 tick 落地後又不成立了。這種情況下 Once Per Bar 並不會送兩次, 官方文件寫的是「同一根即時 K 棒只有第一次呼叫會觸發 alert」;問題在於那唯一一次送出, 依據的是一個撐不到收盤的數值。把 Frequency 改成 Once Per Bar Close,或在腳本裡用 barstate.isconfirmed 把關,可以強制等收盤確認才判斷。我明明改了 Pine 腳本,為什麼線上的 alert 還是照舊邏輯跑?
var 變數存的計數器,是不是永久安全不會不見?
var 就會針對歷史資料集從頭重新初始化一遍。AI 在我的策略裡用了 varip,需要擔心嗎?
var 的更動只有在 K 棒收盤那個 tick 才會真正生效——在還沒收盤的 即時 K 棒中途做的改動,下一個 tick 一來就會被還原。varip 則是設定當下就立刻保留,逐 tick 都不受 rollback 影響。在歷史 K 棒上兩者沒有可見的差異, 因為那裡每根本來就只執行一次。所以生成的程式碼裡出現 varip, 通常是想跨 tick 保留某個狀態——去問它保留的是什麼狀態、alert 重建之後那個值還剩多少。我能確定 TradingView 只送了一次 webhook 嗎?
Get started
TVSBot 把 TradingView 的 alert 轉成跨 7 家交易所的自動下單——非託管、用你自己的 API key,搭配背景非同步訊號處理與逐筆可查的 Replay 紀錄,讓你清楚知道每一筆訊號實際收到了什麼。
免費開始使用