← back to Zero Code Trading Bot

references/strategy-dsl.md

79 lines

# Strategy DSL — plain English → `strategy.json`

The "zero code" part: the user describes a strategy in plain English, and Claude
compiles it to a declarative `strategy.json`. The bot never runs arbitrary code —
rules are sandboxed expressions over a fixed set of market variables.

## The shape

```jsonc
{
  "name": "longshot-fade",
  "venue": "polymarket",            // polymarket | kalshi
  "mode": "paper",                  // ALWAYS scaffold as paper; live is gated
  "poll_seconds": 300,

  "universe": {                     // which markets are even considered
    "query": "",                    // substring match on market title ("" = all)
    "min_volume_usd": 100000,
    "max_spread": 0.05,
    "resolves_within_days": 45,
    "limit": 200
  },

  "entry": [                        // OR-list: first matching rule fires
    { "when": "price <= 0.08 and volume_usd >= 100000", "side": "YES",
      "reason": "deep longshot with liquidity" }
  ],

  "exit": [                         // evaluated on each open position
    { "when": "price >= 0.15", "action": "close", "reason": "take profit" },
    { "when": "price <= 0.03", "action": "close", "reason": "stop loss" }
  ],

  "sizing": {
    "type": "fixed_usd",
    "amount": 25,                   // $ per new position
    "max_position_usd": 50,
    "max_open_positions": 10
  },

  "risk": {
    "max_daily_loss_usd": 100,      // circuit breaker: halt new entries for the day
    "max_total_exposure_usd": 250,  // hard cap on summed open size
    "kill_switch": true             // honor a data/KILL file
  }
}
```

## Rule variables (the ONLY names a `when` may use)

| variable          | meaning                                   |
|-------------------|-------------------------------------------|
| `price`           | current YES price, 0..1                   |
| `volume_usd`      | lifetime USD volume                       |
| `liquidity_usd`   | order-book liquidity                      |
| `spread`          | best ask − best bid, 0..1                 |
| `days_to_resolve` | days until the market resolves            |

Operators allowed: `and or not  == != < <= > >=  + - * / % **` and numeric
constants. **No function calls, no attribute access, no other names** — enforced
by `safe_eval.py` and re-checked by `scripts/validate_strategy.py`.

## Translating plain English (examples)

| user says | compiles to |
|-----------|-------------|
| "buy longshots under 8 cents with real volume, take profit at 15c, stop at 3c" | entry `price <= 0.08 and volume_usd >= 100000`; exits `price >= 0.15` / `price <= 0.03` |
| "fade near-certain YES over 92c, close if it drops below 85c" | entry `price >= 0.92`, side `NO`; exit `price <= 0.85` |
| "only tight markets resolving this month" | universe `max_spread: 0.03, resolves_within_days: 31` |
| "never risk more than $200 total, $20 a bet, 8 positions max" | sizing `amount:20, max_open_positions:8`; risk `max_total_exposure_usd:200` |

## Workflow

1. Draft `strategy.json` from the user's words.
2. `scripts/validate_strategy.py strategy.json` — must pass.
3. `scripts/new_bot.py <name> --strategy strategy.json` — scaffold the project (paper).
4. `python bot.py --once --dry-run` inside it — confirm the rules fire as intended.
5. Iterate on the rules, re-validate, re-dry-run. Only Steve flips it toward live.