TradingView Webhook 到 Telegram
一个 alert 只吃一个 URL,你要在哪一层 fan-out
你在 TradingView 上把 alert 设置好了,webhook(也就是把信号 HTTP POST 到你指定 URL 的那条通道)那格填的是自己交易所的 bot;接着发现只能填一个 URL——你想顺便在 Telegram 收一则「进场了、成交价多少」的通知,却找不到第二格可以填。
这件事没有人先跟你讲:TradingView 的 alert 一次只吃一个 webhook URL。想「TradingView Webhook 同时打进 Telegram 与交易所」就得在中间加一层——不管是你自己写个 proxy、把 alert 直接指到 Telegram bot(不下单),还是走一个中间服务把两件事一起做掉。而中间层要不要真的下单,就牵涉到 API key(也就是授权下单/查询用的一组凭证)该由谁来保管。这篇不比较哪个做法比较好,也不评论你的策略。给你三件事:三种 fan-out 位置的取舍、顺序与去重会踩到什么坑,以及只推 Telegram、不真下单的观察期怎么设。
为什么「同时打到 Telegram 与交易所」不是 TradingView 该做的事
TradingView 的 alert 面板长这样:条件、名称、有效期限、通知管道(App、Email、Webhook)。Webhook 那格是一个文本输入框,不是列表——你只能填一个 URL、一份 payload。这是产品设计,不是 bug。TradingView 选择把「发到哪」交给你自己决定的一个端点,之后那个端点要怎么展开,跟它没关系。
所以「下单同时通知我」这件事,得在 webhook URL 那一头自己接。消息里的「进场了、成交价多少、有没有失败」这些字段,都要等到交易所回单之后才存在——alert 刚触发那一刻它们还没有值。fan-out 因此不是「TradingView 一次发两份」的问题,而是「你在 webhook 收到信号之后,怎么在同一段流程里把下单结果同时送到 Telegram」的问题。这个框错了,后面的顺序、去重、rate limit 都会跟着设错。
三个可以放 fan-out 的位置,各自优化什么
同样是「下单同时通知我」,中间那层放哪里差很多。这张表是给你比对用的,不用背——挑一个之后回头看那一列的取舍就好。
| 自己写 proxy | TradingView 直发 Telegram bot(不下单) | 走中间服务同时做两件事 | ||
|---|---|---|---|---|
| 谁握你的交易所 API key | 你自己的服务器 | 没人握(只发消息,不下单) | 中间服务 | |
| 谁扛 uptime | 你自己 | Telegram + 你的 bot server | 中间服务 | |
| 去重谁做 | 要自己写(记住 dedup key) | 没下单所以不会重复下,但消息会来两则 | 中间服务内建 | |
| 顺序保证 | 要自己写(先下单再通知) | 不下单,没有顺序问题 | 中间服务内建(下单结果为准) | |
| 适合谁 | 会写后端、想全掌握 | 只想看信号、还在观察期 | 要真下单但不想维运 |
第二种「TradingView 直发 Telegram bot」是最被低估的一种——你把 alert 的 webhook URL 直接指向https://api.telegram.org/bot<TOKEN>/sendMessage,在 alert message 里把消息内容写成 chat_id 与 text 两个 JSON 字段,就会收到消息但完全不下单。适合还在观察策略准不准、还不敢真下单的阶段;缺点是 TradingView 在 5xx 时会重发、你会收到重复消息(Telegram 的 sendMessage 没有 idempotency key)。
顺序踩雷:先发 Telegram 才下单,就等于误报成功
这是最容易写坏的一段。假设你的中间层写成「先发 Telegram 告诉用户已收到、再去交易所下单」。消息会这样:「已收到信号 BTCUSDT buy 0.01」。然后你的下单撞到余额不足、min notional、或 API key(授权下单/查询用的一组凭证)没开合约权限——任何一个都会让下单根本没发生,但用户已经以为「进场了」。
正确顺序是把 Telegram 通知绑到「下单流程的最后一步」——先跑完 pre-checks、送去交易所 API、拿到 fill 或明确的错误消息之后才推 Telegram。这个顺序是机制上的正确答案,不是凭口味挑:因为 Telegram 消息里的每个字段都需要下单后才存在(成交价、实际数量、剩余余额、失败原因)。「举例来说」,TVSBot 执行端的通知就是在 process_signal 跑完之后才进 _send_notification(backend/app/services/orders.py),消息里的成交价、per-key ✓/✗ 结果、失败时具体错误都是从交易所回单后才有的数据。
去重踩雷:TradingView 会在 5xx 时重发,Telegram 会来两则
TradingView 的官方 webhook 送达规格是接收端 3 秒内没响应就取消、不重试;4xx 也不重试;只有 5xx 会等 5 秒后重发,最多 3 次(见我们的另一篇:TradingView Webhook 延迟有多严重?官方规格、常见原因与能修复的地方)。这代表:若你的中间层在下单当下遇到交易所短暂回 5xx,又把 5xx 直接透传回 TradingView,同一个 alert 会被送 2 到 4 次。结果 Telegram 收到 2 到 4 则「成交」消息,但实际仓位只开了 1 个——其他次可能因为交易所自身的 idempotency 或 dedup 而 no-op。
解法是中间层自己做 dedup,不要把重发往下游传。可用的键至少要包含 strategy、symbol、action、qty,最好加上时间窗(60 秒内同 hash 直接 skip)。同一份设计 TVSBot 执行端在 backend/app/routers/webhook.py 的第 4 步是这样写的:payload hash user_id|strategy|symbol|action|qty,60 秒内重复直接不执行。
Rate limit 踩雷:Telegram Bot API 有秒级上限
Telegram Bot API 对 sendMessage 有秒级的速率限制——单一 chat 大约 1 条/秒,跨 chat 累计还有另一个上限,超过会回 429 并附 retry_after(实际数字请去 Telegram Bot API 官方 FAQ 那页查证,他们保留随时调整的权利)。单人自用大概撞不到,但如果你在跑「一策略多账户 fan-out」或「同时通知社群 100 个订阅者」,就会在某次高频 alert 上一次撞爆。
这件事会安静地坏掉:Telegram 只回 429、不会补发,你不主动去看就不会知道有消息丢了。可行的做法:在中间层加一个消息队列,超过秒级上限就往后排、记录丢过几则。或更务实地:把 Telegram 通知从「每笔订单一则」改成「每小时汇总一则」,散户自用完全够。
只推 Telegram、不真下单的观察期:dry-run 是给这件事用的
一个常被忽略的用法:把 Telegram 当「策略试跑板」。你想看新策略在真实市场上会发多少信号、什么时间、方向对不对——但还不敢接真钱。这时候你不是要「同时通知 + 下单」,你要的是「只通知,不下单」。
两种做法都行。第一种:TradingView alert 直接指向 Telegram Bot API 的 sendMessage,中间不放任何下单逻辑。缺点如上一节写的——重发与 rate limit 都由你自己扛,而且消息里没有「这个信号真的能被交易所吃吗」的验证(min notional、杠杆设置、可用余额都没查)。第二种:走中间服务但设成 dry-run。「举例来说」,TVSBot 的每个策略有 dry_run 开关,开了之后信号会走完整个 pre-check + 假下单流程、把「这笔理论上会下 0.03 BTC,模拟成交价 65,432」写进 Telegram 通知,但完全不打到交易所——订阅守门也会放行让没付费的用户也能试(backend/app/services/orders.py 的 force_dry_run 与策略层的 dry_run 两处都指向同一段逻辑)。
你不用真的自己写 proxy——只要看得懂它在做哪四件事
你要不要自己写这个中间层是取舍题。看清楚它在做什么、再决定要不要接手就好。任何做「TradingView → Telegram + 交易所」fan-out 的中间层都会处理下面这四层。列出来给你当检查清单——你自己写、还是选中间服务,都是看它有没有把这四件事处理完。
- 验身份。Webhook URL 全世界都能打,一定要在 URL 或 payload 带一组长 token 验证。用
hmac.compare_digest这种常数时间比对——直接==会被 timing attack 逐字符反推。 - 限流。per-token 与 per-IP 各一个上限,攻击者知道你的 URL 也不能把中间层打挂。
- 去重。拿
strategy + symbol + action + qty算 hash,60 秒窗口内重复的信号直接 skip。这一步挡掉 TradingView 5xx 重发与人为 replay。 - 先下单、后通知。Telegram 消息一定绑在下单流程的最后一步——这样消息里的成交价、失败原因才会是真的,而不是「我以为」。
诚实的一段:Telegram 不能当你的 kill switch
这一段是我们自己做过会后悔的:把 Telegram 当唯一的通知管道。Telegram 不保证即时、不保证送达、你也不会收到「这则消息丢了」的通知——Telegram 没有这个机制。任何自动化交易架构都保证不了 100% 送达——包括我们自己。如果你打算靠 Telegram 通知在下单失败时「赶快去补救」,那条依赖是脆的:你可能因为手机没信号、Telegram 通知被系统批量静音、或消息本身在中间层 rate-limit 掉,就错过整整一次事件。
真的要有可以在半夜叫醒你的通知,走 SMS 或电话轮询(PagerDuty、Opsgenie 这类),不要押在 IM 上。Telegram 适合「事后对账」与「例行通知」,不适合「异常必须被看到」的用途。反过来,这也代表 kill switch 这种真的要能立刻停单的按钮不能只放在 Telegram bot 里——它一定要有一个 web dashboard 版本,你手机 App 或朋友的电脑都能点得到。
常见问题
为什么还要 fan-out?TradingView alert 不能一次送到多个地方吗?
把 TradingView alert 的 webhook URL 直接指到 Telegram Bot API,这样算作弊吗?
如果我先发 Telegram「已收到信号」再下单,会怎样?
Telegram Bot API 的 rate limit 我怎么知道自己会不会撞到?
我要用 TVSBot 的话,Telegram 通知还要自己设吗?
/start <code>就绑好, 之后下单结果会自动推。也支持 /balance、/positions、/status三个查询指令(见backend/app/services/telegram.py)。 Kill switch 还是要在 web dashboard 上按, 不是靠 Telegram——这是上一节那段诚实话的落地。Get started
把 TradingView alert 指过来,下单同时把成交结果推到你的 Telegram——TVSBot 用你自己的 API key、内建 60 秒去重、先下单再通知、支持 dry-run 观察期。
免费开始使用