Skip to main content
Your trading strategy lives in a single YAML file called a StrategySpec. It’s a declarative format: you describe what conditions must be true for your agent to enter or exit a trade, which market conditions should keep it on the sidelines, and how large each position should be. Halyrd’s signal engine reads the same file whether it’s running a backtest or operating live — there’s no separate “live version” that might drift from what you tested.

What the StrategySpec is

A StrategySpec expresses your strategy as:
  • Indicators — measurable market signals like RSI, price deviation from a moving average, or the Fear & Greed index
  • Entry and exit rules — logical combinations of indicator conditions that trigger a buy or a sell back to USDT
  • A regime filter — conditions under which your agent stands aside entirely and skips new entries
  • Position parameters — how much of your available capital to use per trade, and how many trades can be open at once
The same YAML drives both your backtest and the live signal engine. They can never diverge.

Market mode

The first field in every StrategySpec is market_mode. You must set this before writing any rules.
  • spot — long-only. Your agent buys tokens with USDT and sells back to USDT. There is no shorting. This is the only available mode right now.
  • futures — not yet available. The field exists so the parser can reject a futures strategy before it ever reaches a spot executor.
If your spec contains a SHORT signal and market_mode is set to spot, the spec will be rejected with a MARKET_MODE_MISMATCH error before any backtest runs. This is intentional — shorting does not exist in spot mode.

Annotated example

The example below is a mean-reversion strategy: it buys when a token has fallen well below its short-term average and looks oversold, then exits when price reverts. It stays out of the market during strong trending conditions where mean reversion tends to lose money.
StrategySpec:
  market_mode: spot              # REQUIRED first field — spot only for now
  name: "mean-reversion-v1"

  signals:
    entry:
      all_of:                    # ALL of these must be true to trigger a buy
        - { indicator: price_vs_sma, op: "<=", value: -0.05, window: 48 }
          # price is at least 5% below its 48-period SMA
        - { indicator: rsi,          op: "<=", value: 25,    window: 14 }
          # RSI(14) is in oversold territory
        - { indicator: fear_greed,   op: "<=", value: 40 }
          # market sentiment is in the "fear" zone

    exit:
      any_of:                    # ANY ONE of these triggers a sell back to USDT
        - { indicator: price_vs_sma, op: ">=", value: 0.0 }
          # price has reverted to the mean
        - { indicator: rsi,          op: ">=", value: 65 }
          # RSI has climbed into overbought territory

  regime_filter:
    stand_aside_when:            # skip new entries when this condition holds
      - { indicator: funding_rate, op: ">=", value: 0.03 }
        # perpetual funding rates are elevated — market is trending, not mean-reverting

  params:
    position_size_pct: 90        # use 90% of available capital per trade (0–100)
    max_concurrent: 1            # at most 1 open position at a time

Available indicators

IndicatorWhat it measuresNotable parameters
price_vs_smaPercentage deviation of current price from its simple moving average (e.g. -0.05 = 5% below)window — number of periods for the SMA
rsiRelative Strength Index — momentum oscillator, 0–100. Below ~30 = oversold; above ~70 = overboughtwindow — RSI period (typically 14)
fear_greedCMC Fear & Greed Index, 0–100. Lower = more fear; higher = more greed
funding_ratePerpetual futures funding rate as a proxy for market trend intensity

Signal combinators

Rules inside entry, exit, and stand_aside_when are grouped with one of two combinators:
  • all_of — every condition in the list must be true. Use this for entries where you want multiple confirming signals.
  • any_of — at least one condition in the list must be true. Use this for exits where any one signal is sufficient to close the trade.

Operators

You can use the following comparison operators in any indicator condition:
OperatorMeaning
<=less than or equal to
>=greater than or equal to
<less than
>greater than
==equal to

Regime filter

The regime_filter.stand_aside_when block tells your agent when to pause new entries entirely. It doesn’t close existing positions — it simply prevents opening new ones while the condition holds. Mean-reversion strategies in particular need this: when a market is trending strongly, prices keep running instead of reverting, and taking the other side is expensive.
The funding_rate indicator is especially useful in the regime filter even though your agent only trades spot. Elevated funding rates signal that the perpetual futures market is heavily one-directional — a strong hint that the broader market is trending rather than consolidating.

Position parameters

ParameterRangeWhat it controls
position_size_pct0–100Fraction of your available capital used for each trade. 90 means each trade uses 90% of what’s currently in USDT.
max_concurrent1+Maximum number of positions open at the same time. Set to 1 for mean-reversion strategies where you want to be fully in USDT between trades.

Validation rules

When you import or save a StrategySpec, Halyrd validates it before any backtest runs:
1

Schema check

The YAML must be structurally valid: all required fields present, all indicator names and operators recognised.
2

Market mode consistency

Any SHORT signal in a spot spec is rejected immediately with MARKET_MODE_MISMATCH.
3

Look-ahead guard

No indicator may reference future bars. Specs that would produce look-ahead bias are rejected before they can produce misleading backtest results.
4

Fee-blind guard

If the strategy produces negative expectancy after fees over the backtest window, it is rejected. Your agent should not be built on a strategy that loses money once costs are counted.
All four checks run on import. An invalid spec is rejected before any backtest executes — you’ll see a clear error message describing which check failed and why.

How to author a spec

There are two paths in the Strategy Builder:

Generate

Describe what you’re trying to do in plain language and Halyrd’s LLM writes the YAML for you. The generated spec is validated and backtested before you can use it.

Bring your own

Write the YAML directly and paste it into the import field. Useful if you already know exactly what rules you want to express.
Either way, the spec is validated on import and the backtest runs the same code your live agent will use. What you test is what runs.