Ora
All documentation

Docs / Questions & answers

Many symbols in one request

Send state.positions instead of state.symbol. Every question is answered once per position, and each position's other fields can be read by name in its rules.

{
  "model": "ora-fin",
  "state": {
    "positions": [
      {
        "symbol": "AAPL",
        "shares": 100,
        "entry": 200
      },
      {
        "symbol": "msft",
        "shares": 50,
        "entry": 300
      },
      {
        "symbol": "NVDA",
        "shares": 10,
        "entry": 100
      }
    ]
  },
  "questions": {
    "pnl": "=(C - entry) * shares",
    "above_entry": {
      "type": "bool",
      "ask": "=C > entry"
    }
  },
  "aggregate": [
    "pnl"
  ]
}

A real response (other fields left out):

{
  "answers_by_symbol": {
    "AAPL": {
      "pnl": {
        "type": "value",
        "value": 13832,
        "rule": "(C - entry) * shares",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 200,
          "shares": 100
        }
      },
      "above_entry": {
        "type": "bool",
        "bool": true,
        "rule": "C > entry",
        "basis": "rule",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 200
        }
      }
    },
    "MSFT": {
      "pnl": {
        "type": "value",
        "value": 10356,
        "rule": "(C - entry) * shares",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 300,
          "shares": 50
        }
      },
      "above_entry": {
        "type": "bool",
        "bool": true,
        "rule": "C > entry",
        "basis": "rule",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 300
        }
      }
    },
    "NVDA": {
      "pnl": {
        "type": "value",
        "value": 1020.6,
        "rule": "(C - entry) * shares",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 100,
          "shares": 10
        }
      },
      "above_entry": {
        "type": "bool",
        "bool": true,
        "rule": "C > entry",
        "basis": "rule",
        "as_of": "2026-11-02",
        "facts": {
          "entry": 100
        }
      }
    }
  },
  "aggregate": {
    "pnl": {
      "sum": 25208.6,
      "count": 3,
      "missing": []
    }
  },
  "usage": {
    "input_tokens": 4300,
    "output_tokens": 54,
    "data_tokens": 3000
  }
}

How it works

  • Fields. Other fields in state are shared by all positions. A position's own field wins over a shared one.
  • One bad symbol. It goes in errors_by_symbol and the others still answer. When none can answer, the request is a 422 naming each one.
  • Other results. context_by_symbol gives each session. An output template fills in once per position, under output_by_symbol. Actions run once per position, under actions_by_symbol.
  • Totals. "aggregate": ["pnl"] adds up a number question. missing names the positions with no number, which are not counted as 0.
{
  "errors_by_symbol": {
    "ZZZNOPE": {
      "status": 422,
      "detail": [
        {
          "loc": [
            "body",
            "state",
            "symbol"
          ],
          "type": "value_error",
          "msg": "No daily prices for `ZZZNOPE`. Use a symbol as Ora lists it (AAPL, BRK-B, 7203.T, BTCUSD, EURUSD), or send `state.context` or `state.bars` yourself."
        }
      ]
    }
  },
  "aggregate": {
    "v": {
      "sum": 2366.8,
      "count": 2,
      "missing": [
        "ZZZNOPE",
        "$P123",
        "$S77"
      ]
    }
  }
}

Limits and cost

At most 50 positions in one request, and positions times questions at most 500. Answers are keyed by symbol, so a symbol can be listed once. A position can't carry its own bars or context.

Each position is metered and rate limited as its own call, with its own market data tokens, and is listed as its own line in your usage. It costs what the same calls would cost one at a time. usage adds them up.

{
  "detail": [
    {
      "loc": [
        "body",
        "state",
        "positions"
      ],
      "msg": "At most 50 positions per request, got 51. Split it into several requests.",
      "type": "too_long"
    }
  ]
}

It needs at least one rule in the request, and a state with no symbol, bars or context. Otherwise positions is an ordinary field, as it always was.

Create an account Open the Playground Everything here also lives in the console, next to your keys and usage.