Skip to main content
Create custom routines to extend agent capabilities with deterministic Python code.

Create a Custom Routine

1. Create the File

In your agent’s routines/ folder:
# trading_agents/sol_scalper/routines/momentum_scanner.py

async def scan_momentum(
    connector_name: str = "binance_perpetual",
    trading_pair: str = "SOL-USDT",
    lookback: int = 20
) -> dict:
    """
    Scan for momentum breakouts.

    Args:
        connector_name: Exchange connector
        trading_pair: Market to analyze
        lookback: Number of candles

    Returns:
        Momentum analysis with signal
    """
    from hummingbot_api_client import get_candles

    candles = await get_candles(connector_name, trading_pair, lookback)

    # Calculate momentum
    closes = [c.close for c in candles]
    momentum = (closes[-1] - closes[0]) / closes[0] * 100

    # Volume confirmation
    volumes = [c.volume for c in candles]
    avg_volume = sum(volumes) / len(volumes)
    volume_surge = volumes[-1] > avg_volume * 1.5

    # Generate signal
    if momentum > 2 and volume_surge:
        signal = "strong_bullish"
    elif momentum > 1:
        signal = "bullish"
    elif momentum < -2 and volume_surge:
        signal = "strong_bearish"
    elif momentum < -1:
        signal = "bearish"
    else:
        signal = "neutral"

    return {
        "momentum_pct": round(momentum, 2),
        "volume_surge": volume_surge,
        "signal": signal,
        "price": closes[-1]
    }

2. Register the Routine

Add to your agent’s config or the routine will be auto-discovered.

3. Use in Agent

Update your agent.md:
## Entry Rules

1. Run momentum_scanner routine
2. If signal is "strong_bullish" → Open long
3. If signal is "strong_bearish" → Open short
4. Otherwise → Wait

4. Test

Run a dry run to verify:
/agent → SOL Scalper → Dry Run

Dry Run Result:
- Ran momentum_scanner
- Result: {signal: "strong_bullish", momentum: 2.5%}
- Decision: Would open LONG

Creating with Agent Builder

When you create an agent via /agent → Agent Builder, the system can generate custom routines for your strategy. For example, if you specify:
“I want to use one-minute candles and look for support and resistance levels plus EMAs of 7, 25, and 99”
The Agent Builder creates a routine like:
# routines/support_resistance_ema_levels.py
async def analyze_levels(
    trading_pair: str,
    connector_name: str,
    candle_count: int = 100,
    lookback: int = 20,
    tolerance: float = 0.02
):
    """Identify support/resistance levels and EMA alignment."""
    candles = await get_candles(connector_name, trading_pair, candle_count)

    # Calculate EMAs
    ema_7 = calculate_ema(candles, 7)
    ema_25 = calculate_ema(candles, 25)
    ema_99 = calculate_ema(candles, 99)

    # Determine trend from EMA alignment
    if ema_7 > ema_25 > ema_99:
        trend = "bullish"
    elif ema_7 < ema_25 < ema_99:
        trend = "bearish"
    else:
        trend = "mixed"

    # Find support/resistance levels
    supports, resistances = find_sr_levels(candles, lookback, tolerance)

    return {
        "price": candles[-1].close,
        "trend": trend,
        "ema_7": ema_7,
        "ema_25": ema_25,
        "ema_99": ema_99,
        "supports": supports,
        "resistances": resistances
    }
The routine is configurable—you can adjust parameters without modifying code.

Routine Best Practices

Same input should always produce same output. Avoid randomness or time-based logic.
Return dictionaries with clear field names. The agent will interpret the results.
Return error states rather than raising exceptions:
if not candles:
    return {"error": "No candle data available"}
The agent reads docstrings to understand what the routine does.

Global vs Agent-Specific

LocationScope
~/condor/routines/Available to all agents
trading_agents/{slug}/routines/Specific to one agent
Agent-specific routines override global ones with the same name.