Skip to content

Triggers ​

Triggers let strategies communicate with each other without knowing about each other. One strategy fires a signal, and other strategies react to it — opening trades, blocking trades, or force-closing positions.

This is how you build multi-strategy setups: a paper-trading "emulator" strategy proves a signal is good, fires a trigger, and a real-money strategy acts on it.

How It Works ​

The trigger system has three roles a strategy can play:

  • Producer — fires trigger keys when positions close at profit or loss
  • Consumer — only trades when specific trigger keys are active
  • Blacklisted — gets blocked (and optionally force-closed) when specific keys fire

A single strategy can play multiple roles at the same time.

Strategy A (producer + emulator)
  position closes at profit → activates key "emu_profit" for 120s
  position closes at loss   → activates key "emu_loss" for 120s

Strategy B (consumer + blacklisted)
  sees "emu_profit" active  → allowed to trade
  sees "emu_loss" fire      → blacklisted for 1 hour, positions force-closed
  no triggers active        → blocked (waits for signal)

Trigger Keys ​

A trigger key is a string identifier — any name you choose. Keys are global: when one strategy activates "emu_profit", every other strategy in the bot can see it.

Keys have a duration. When activated, a key stays active for the configured number of seconds, then expires automatically. Activating the same key again resets its timer.

Key "emu_profit" activated at 14:00 (duration: 120s)
  → active from 14:00 to 14:02
  → re-activated at 14:01 → now active until 14:03 (timer restarted)
  → expires at 14:03 → no longer active

Enabling Triggers ​

Add a triggers object to your strategy parameters:

json
{
  "triggers": {
    "triggerKeyProfit": "emu_profit",
    "triggerKeyLoss": "emu_loss",
    "triggerSeconds": 120,
    "triggerScope": "global",
    "triggerByKeys": ["emu_profit"],
    "blacklistKeys": ["emu_loss"],
    "blacklistSeconds": 3600,
    "forceCloseKeys": ["emu_loss"],
    "forceCancelKeys": ["emu_loss"]
  }
}

If no triggers key is present in the strategy params, triggers are disabled for that strategy.

Producing Triggers ​

A producer strategy fires trigger keys when its positions close.

ParameterTypeDefaultDescription
triggerKeyProfitstringnoneKey to activate when a position closes at profit
triggerKeyLossstringnoneKey to activate when a position closes at loss
triggerSecondsseconds60How long the activated key stays active
triggerScopestring"global""global" or "symbol" — stored metadata, see Scope below

A strategy is a producer if it has triggerKeyProfit or triggerKeyLoss set.

Example: Emulator Producer ​

json
{
  "triggers": {
    "triggerKeyProfit": "emu_profit",
    "triggerKeyLoss": "emu_loss",
    "triggerSeconds": 120,
    "triggerScope": "global"
  }
}

This strategy fires "emu_profit" (active for 2 minutes) when it closes a profitable position, and "emu_loss" when it closes a losing position.

Consuming Triggers ​

A consumer strategy only trades when specific trigger keys are active. If none of its required keys are active, can_trade() returns false and the strategy holds.

ParameterTypeDefaultDescription
triggerByKeysstring[][]Keys that allow trading. OR logic: any one active = trading allowed

Empty triggerByKeys means the strategy always trades (no gating).

OR Logic ​

If you list multiple keys in triggerByKeys, any single key being active is enough to trade. You don't need all of them.

triggerByKeys: ["emu_profit", "manual_go"]

  "emu_profit" active, "manual_go" expired  →  CAN trade (one is enough)
  "emu_profit" expired, "manual_go" active  →  CAN trade
  both active                               →  CAN trade
  both expired                              →  CANNOT trade

Example: Gated Consumer ​

json
{
  "triggers": {
    "triggerByKeys": ["emu_profit"]
  }
}

This strategy only opens positions when the "emu_profit" key is active. When the key expires, the strategy goes back to holding.

Blacklisting ​

A blacklisted strategy gets temporarily blocked when specific trigger keys fire. Unlike consumers (which need triggers to trade), blacklisted strategies are blocked by triggers.

ParameterTypeDefaultDescription
blacklistKeysstring[][]Keys that block this strategy when they fire
blacklistSecondsseconds3600How long the strategy stays blocked

When a key in blacklistKeys fires, the strategy is blocked on all symbols it trades for blacklistSeconds. The block is per strategy+symbol — other strategies are not affected.

Strategy B has blacklistKeys: ["emu_loss"]
Strategy B trades: BTCUSDT, ETHUSDT, SOLUSDT

"emu_loss" fires →
  Strategy B blocked on BTCUSDT for 1 hour
  Strategy B blocked on ETHUSDT for 1 hour
  Strategy B blocked on SOLUSDT for 1 hour
  Strategy A (different strategy) → NOT blocked

Force-Close and Force-Cancel ​

When blacklisted, you can optionally force-close positions and cancel pending orders:

ParameterTypeDefaultDescription
forceCloseKeysstring[][]Keys that force-close all positions when they fire
forceCancelKeysstring[][]Keys that cancel all pending orders when they fire

These are one-time actions — they execute once when the blacklist activates. If the strategy opens a new position later (after the blacklist expires), it won't be auto-closed.

forceCloseKeys: ["emu_loss"]

"emu_loss" fires →
  1. Strategy blacklisted on all symbols
  2. All open positions force-closed (once)
  3. All pending orders cancelled (if forceCancelKeys also set)
  4. Strategy blocked for blacklistSeconds

Example: Full Consumer + Blacklist ​

json
{
  "triggers": {
    "triggerByKeys": ["emu_profit"],
    "blacklistKeys": ["emu_loss"],
    "blacklistSeconds": 3600,
    "forceCloseKeys": ["emu_loss"],
    "forceCancelKeys": ["emu_loss"]
  }
}

This strategy:

  • Only trades when "emu_profit" is active
  • Gets blocked for 1 hour when "emu_loss" fires
  • Force-closes all positions and cancels all orders on blacklist

Clearing Keys ​

Producers can clear other trigger keys when they fire. This lets one strategy cancel another strategy's signal.

ParameterTypeDefaultDescription
clearOnProfitKeysstring[][]Keys to deactivate when this strategy fires a profit trigger
clearOnLossKeysstring[][]Keys to deactivate when this strategy fires a loss trigger
Strategy A config:
  triggerKeyProfit: "a_profit"
  clearOnProfitKeys: ["b_signal"]

Strategy A closes at profit →
  1. Activates "a_profit"
  2. Deactivates "b_signal" (cleared)

Scope ​

Each trigger has a scope that records whether it was fired globally or for a specific symbol:

  • "global" — the trigger applies to all symbols
  • "symbol" — the trigger was fired for the specific symbol where the position closed

Scope is stored as metadata on the trigger. When checking if a key is active (is_key_active, are_keys_satisfied), scope is not checked. Once a key is active, it gates all symbols regardless of scope. Scope is informational — useful for logging and monitoring.

ParameterTypeDefaultDescription
triggerScopestring"global""global" or "symbol"

Trade Gating Flow ​

On every tick, the runner checks whether a strategy is allowed to trade:

tick arrives →
  1. Is strategy blacklisted on this symbol?
     YES → Action::Hold (blocked)
  2. Does strategy have triggerByKeys configured?
     NO  → allowed (no gating)
     YES → is ANY key in triggerByKeys active?
           YES → allowed
           NO  → Action::Hold (waiting for signal)
  3. Check session (if sessions enabled)
  4. Run strategy logic

Blacklist is checked first and takes priority over everything else.

Cleanup ​

Expired triggers and blacklist entries are automatically cleaned up every 5 seconds by the runner. You don't need to manage expiration — it happens in the background.

Multi-Strategy Example ​

A common pattern: emulator strategy proves signals on paper, real strategy only trades on confirmed signals.

Strategy 1: Emulator (paper trading, produces triggers) ​

json
{
  "triggers": {
    "triggerKeyProfit": "emu_profit",
    "triggerKeyLoss": "emu_loss",
    "triggerSeconds": 120,
    "triggerScope": "global"
  }
}

Strategy 2: Real trader (consumes triggers, blacklisted by losses) ​

json
{
  "triggers": {
    "triggerByKeys": ["emu_profit"],
    "blacklistKeys": ["emu_loss"],
    "blacklistSeconds": 3600,
    "forceCloseKeys": ["emu_loss"],
    "forceCancelKeys": ["emu_loss"],
    "clearOnProfitKeys": [],
    "clearOnLossKeys": []
  }
}

How this plays out:

  1. Emulator trades on paper. No real money involved.
  2. Emulator closes at profit → fires "emu_profit" (active for 2 minutes)
  3. Real trader sees "emu_profit" → starts trading with real money
  4. 2 minutes pass, "emu_profit" expires → real trader goes back to holding
  5. Emulator closes at loss → fires "emu_loss"
  6. Real trader gets blacklisted for 1 hour, all positions force-closed
  7. After 1 hour, blacklist expires → real trader waits for next "emu_profit"

All Parameters ​

ParameterTypeDefaultDescription
triggerKeyProfitstringnoneKey activated on profitable close
triggerKeyLossstringnoneKey activated on losing close
triggerSecondsseconds60Duration keys stay active
triggerScopestring"global"Scope metadata: "global" or "symbol"
triggerByKeysstring[][]Keys required to trade (OR logic)
blacklistKeysstring[][]Keys that block this strategy
blacklistSecondsseconds3600Blacklist duration
forceCloseKeysstring[][]Keys that force-close positions
forceCancelKeysstring[][]Keys that cancel pending orders
clearOnProfitKeysstring[][]Keys to clear on profit trigger
clearOnLossKeysstring[][]Keys to clear on loss trigger