Custom alert templates let you control exactly what information appears in your Telegram messages, webhook payloads, or email notifications when an arbitrage signal matches your alert criteria. Templates use a simple placeholder syntax mixed with loop and conditional logic — no programming experience required.
How Templates Work
When a signal matches your alert filters, the system takes your saved template text and replaces every placeholder with the actual signal value before sending the notification.
- {variable} — replaced with a single value
- {% for %} — repeats a block for each trading step
- {% if %} — shows different text based on a condition
Signal-level variables
| Placeholder | Description | Example |
|---|---|---|
{signalId} | Unique numeric ID of the signal | 4821 |
{signalType} | Ex1 = all steps on the same exchange · Ex2 = cross-exchange | Ex2 |
{coinType} | Trading cycle: A-B-A (2 pairs) or A-B-C-A (3 pairs) | A-B-C-A |
{profitPercent} | Gross profit in percent | 1.85 |
{feePercent} | Estimated total fee in percent | 0.30 |
{netProfit} | Net profit after fees (profit − fee) | 1.55 |
{volume} | Volume of the first (primary) step | 0.5 |
{lifetime} | Signal age in seconds since creation | 312 |
{createdAt} | Timestamp when the signal was first detected | 2025-03-11 14:02:10 |
{updatedAt} | Timestamp of the last signal update | 2025-03-11 14:07:22 |
{freeSignal} | 1 if this is a free (public) signal, otherwise 0 | 0 |
| Placeholder | Description | Example |
|---|---|---|
{coin_path} | Full coin journey from quote to quote | USDT → BTC → ETH → USDT |
{symbol_steps} | Comma-separated list of all trading pairs | BTCUSDT, ETHBTC, ETHUSDT |
{exchanges_list} | Unique exchanges involved (comma-separated) | Binance, KuCoin |
{signal_cards} | Pre-formatted summary block of the signal | (multi-line) |
| Placeholder | Description | Example |
|---|---|---|
{exchanges[i]} | Exchange name for step i | Binance |
{symbols[i]} | Full trading pair symbol for step i | BTCUSDT |
{baseSymbols[i]} | Base asset of the pair | BTC |
{quoteSymbols[i]} | Quote asset of the pair | USDT |
{directions[i]} | Trade direction: 1 = Buy, -1 = Sell | 1 |
{prices[i]} | Execution price for step i | 42318.57 |
{volumes[i]} | Volume traded at step i | 0.01182 |
{i+1} | Step number starting from 1 (human-readable) | 1, 2, 3 |
| Placeholder | Description |
|---|---|
{symbolStats[exchanges[i]+'_'+symbols[i]].minBid} | Lowest bid price in the order book |
{symbolStats[exchanges[i]+'_'+symbols[i]].maxBid} | Highest bid price in the order book |
{symbolStats[exchanges[i]+'_'+symbols[i]].minAsk} | Lowest ask price in the order book |
{symbolStats[exchanges[i]+'_'+symbols[i]].maxAsk} | Highest ask price in the order book |
{symbolStats[exchanges[i]+'_'+symbols[i]].bidQty} | Total quantity available on the bid side |
{symbolStats[exchanges[i]+'_'+symbols[i]].askQty} | Total quantity available on the ask side |
Template Syntax
Signals have 2 steps (A-B-A) or 3 steps (A-B-C-A). The loop runs once for each step automatically.
{% for i in 0..symbols.length-1 %}
Step {i+1}: {symbols[i]} on {exchanges[i]}
{% endfor %}
Output for a 3-step signal:
Step 1: BTCUSDT on Binance
Step 2: ETHBTC on Binance
Step 3: ETHUSDT on KuCoin
Use {% if %}...{% else %}...{% endif %} to show different text depending on a value. Works both inside and outside loops.
Buy/Sell direction:
{% if directions[i] == 1 %}BUY{% else %}SELL{% endif %}
Exchange type:
{% if signalType == 'Ex2' %}Cross-Exchange{% else %}Same-Exchange{% endif %}
Coin cycle type:
{% if coinType == 'A-B-C-A' %}Triangular{% else %}Simple{% endif %}
Free signal badge:
{% if freeSignal == 1 %}🆓 Free Signal{% endif %}
| Placeholder | Description | Example |
|---|---|---|
== | Equal to | directions[i] == 1 |
!= | Not equal to | signalType != 'Ex1' |
> | Greater than | profitPercent > 2 |
< | Less than | feePercent < 0.5 |
Ready-Made Templates
These are the same templates available in the dropdown inside the alert modal. Use them as-is or as a starting point for your custom template.
🚀 Arbitrage Alert [{signalType}] - {coinType}
📊 Profit: +{profitPercent}% | Fee: {feePercent}% | Net: +{netProfit}%
🔀 Path: {coin_path}
🏦 {exchanges_list}
⏱ Lifetime: {lifetime}
🚀 *Arbitrage Signal* [{signalType} | {coinType}]
━━━━━━━━━━━━━━━━━━━━
💰 *Profit:* +{profitPercent}%
💸 *Fee:* {feePercent}%
📈 *Net:* +{netProfit}%
📦 *Volume:* {volume}
🔀 *Path:* {coin_path}
🏦 *Exchanges:* {exchanges_list}
📋 *Steps:*
{% for i in 0..symbols.length-1 %}
[{i+1}] {exchanges[i]} | {symbols[i]}
↳ {% if directions[i] == 1 %}🟢 Buy{% else %}🔴 Sell{% endif %} @ {prices[i]}
↳ Vol: {volumes[i]}
{% endfor %}
⏱ *Lifetime:* {lifetime}
🕐 *Updated:* {updatedAt}
🚀 *Arbitrage Signal* #️⃣{signalId}
━━━━━━━━━━━━━━━━━━━━
📌 *Type:* {signalType} ({coinType})
{% if signalType == 'Ex2' %}🔁 *Cross-Exchange*{% else %}🔄 *Same-Exchange*{% endif %}
💰 *Profit:* +{profitPercent}%
💸 *Fee:* -{feePercent}%
📈 *Net Profit:* +{netProfit}%
📦 *Volume:* {volume}
🔀 *Coin Path:* {coin_path}
📍 *Symbols:* {symbol_steps}
🏦 *Exchanges:* {exchanges_list}
━━━━━━━━━━━━━━━━━━━━
📋 *STEP DETAILS*
━━━━━━━━━━━━━━━━━━━━
{% for i in 0..symbols.length-1 %}
[STEP {i+1}]
Exchange: {exchanges[i]}
Pair: {symbols[i]} ({baseSymbols[i]}/{quoteSymbols[i]})
Action: {% if directions[i] == 1 %}🟢 BUY{% else %}🔴 SELL{% endif %}
Price: {prices[i]}
Volume: {volumes[i]}
Order Book:
Bid → min: {symbolStats[exchanges[i]+'_'+symbols[i]].minBid} max: {symbolStats[exchanges[i]+'_'+symbols[i]].maxBid} qty: {symbolStats[exchanges[i]+'_'+symbols[i]].bidQty}
Ask → min: {symbolStats[exchanges[i]+'_'+symbols[i]].minAsk} max: {symbolStats[exchanges[i]+'_'+symbols[i]].maxAsk} qty: {symbolStats[exchanges[i]+'_'+symbols[i]].askQty}
{% endfor %}
━━━━━━━━━━━━━━━━━━━━
⏱ Lifetime: {lifetime}
🕐 Created: {createdAt}
🔄 Updated: {updatedAt}
{% if freeSignal == 1 %}🆓 Free Signal{% endif %}
Advanced Examples
Send structured data to your own endpoint or C# application.
{
"id": {signalId},
"type": "{signalType}",
"coinType": "{coinType}",
"profit": {profitPercent},
"fee": {feePercent},
"netProfit": {netProfit},
"volume": {volume},
"path": "{coin_path}",
"exchanges": "{exchanges_list}",
"lifetime": {lifetime},
"createdAt": "{createdAt}",
"steps": [
{% for i in 0..symbols.length-1 %}
{
"step": {i+1},
"exchange": "{exchanges[i]}",
"symbol": "{symbols[i]}",
"base": "{baseSymbols[i]}",
"quote": "{quoteSymbols[i]}",
"direction": {% if directions[i] == 1 %}"buy"{% else %}"sell"{% endif %},
"price": {prices[i]},
"volume": {volumes[i]},
"orderBook": {
"minBid": {symbolStats[exchanges[i]+'_'+symbols[i]].minBid},
"maxBid": {symbolStats[exchanges[i]+'_'+symbols[i]].maxBid},
"minAsk": {symbolStats[exchanges[i]+'_'+symbols[i]].minAsk},
"maxAsk": {symbolStats[exchanges[i]+'_'+symbols[i]].maxAsk},
"bidQty": {symbolStats[exchanges[i]+'_'+symbols[i]].bidQty},
"askQty": {symbolStats[exchanges[i]+'_'+symbols[i]].askQty}
}
}{% endfor %}
]
}
Add a visual marker when net profit exceeds a threshold.
{% if netProfit > 1.5 %}🔥 HIGH PROFIT{% else %}🚀 Signal{% endif %} [{signalType}] {coinType}
Net: +{netProfit}% | Path: {coin_path}
{% if signalType == 'Ex2' %}⚡ Cross-exchange — check transfer fees!{% endif %}
Signal #{signalId} — {coinType} — Net +{netProfit}%
{% for i in 0..symbols.length-1 %}
▸ Step {i+1} · {exchanges[i]} · {symbols[i]}
{% if directions[i] == 1 %}BUY{% else %}SELL{% endif %} @ {prices[i]} (vol: {volumes[i]})
Bid {symbolStats[exchanges[i]+'_'+symbols[i]].minBid} – {symbolStats[exchanges[i]+'_'+symbols[i]].maxBid} qty {symbolStats[exchanges[i]+'_'+symbols[i]].bidQty}
Ask {symbolStats[exchanges[i]+'_'+symbols[i]].minAsk} – {symbolStats[exchanges[i]+'_'+symbols[i]].maxAsk} qty {symbolStats[exchanges[i]+'_'+symbols[i]].askQty}
{% endfor %}
Tips & Common Mistakes
- Always close loops and conditions. Every
{% for %}needs a matching{% endfor %}and every{% if %}needs a matching{% endif %}. - Order book variables only work inside a loop. The key
exchanges[i]+'_'+symbols[i]requiresito be defined by a{% for %}block. - String comparisons need quotes. Use
signalType == 'Ex2'(with single quotes), notsignalType == Ex2. - Numeric comparisons do not need quotes. Use
directions[i] == 1ornetProfit > 1.5. - Lifetime is in seconds. A value of
312means 5 minutes and 12 seconds. Format it in your message if needed. - Telegram Markdown bold. Wrap text with
*asterisks*for bold in Telegram (MarkdownV1 parse mode). - Do not use raw
{}braces for anything other than placeholders — they will be interpreted as template tags and may break rendering.
| Variable | Type | Notes |
|---|---|---|
{signalId} | Integer | Safe to use in JSON without quotes |
{profitPercent}, {feePercent}, {netProfit} | Decimal | Up to 4 decimal places |
{prices[i]}, {volumes[i]} | Decimal | Up to 8 decimal places |
{directions[i]} | Integer | 1 or -1 |
{freeSignal} | Integer | 0 or 1 |
{lifetime} | Integer | Seconds (e.g. 312) |
{createdAt}, {updatedAt} | String | Format: YYYY-MM-DD HH:MM:SS |
| Path & summary variables | String | Wrap in quotes in JSON payloads |