{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://continualexchange.com/protocol/v2/schema.json",
  "title": "Continual Exchange protocol version 2",
  "description": "Engine-to-bot objects and bot-to-engine action objects. Bot responses on the wire are arrays of the action objects defined here.",
  "oneOf": [
    { "$ref": "#/$defs/matchStart" },
    { "$ref": "#/$defs/tick" },
    { "$ref": "#/$defs/matchEnd" },
    { "$ref": "#/$defs/newOrder" },
    { "$ref": "#/$defs/cancel" },
    { "$ref": "#/$defs/cancelAll" }
  ],
  "$defs": {
    "nonNegativeInteger": { "type": "integer", "minimum": 0 },
    "clientId": { "$ref": "#/$defs/nonNegativeInteger" },
    "orderId": { "type": "integer", "minimum": 0 },
    "instrument": { "type": "integer", "minimum": 0 },
    "instrumentIndex": { "type": "integer", "minimum": 0, "maximum": 2 },
    "price": { "type": "integer" },
    "quantity": { "type": "integer" },
    "cash": { "type": "integer", "description": "Centi-units: 100 equals one score unit." },
    "position": {
      "type": "array",
      "prefixItems": [
        { "type": "integer" },
        { "type": "integer" },
        { "type": "integer" }
      ],
      "items": false,
      "minItems": 3,
      "maxItems": 3
    },
    "baseEngineMessage": {
      "type": "object",
      "required": ["type", "protocol_version", "match_id", "t", "seq"],
      "properties": {
        "protocol_version": { "const": 2 },
        "match_id": { "type": "string", "minLength": 1 },
        "t": { "$ref": "#/$defs/nonNegativeInteger" },
        "seq": { "$ref": "#/$defs/nonNegativeInteger" }
      }
    },
    "priors": {
      "type": "object",
      "required": [
        "nominal_ticks", "end_mult_lo", "end_mult_hi", "sigma_f", "sigma_e",
        "beta_a", "beta_b", "lambda_f", "factor_jump_sd", "lambda_e",
        "idio_jump_sd", "bulletin_mag_noise_sd", "earnings_jump_sd",
        "signal_sigma_lo", "signal_sigma_hi", "signal_refresh_rate"
      ],
      "properties": {
        "nominal_ticks": { "$ref": "#/$defs/nonNegativeInteger" },
        "end_mult_lo": { "type": "number" },
        "end_mult_hi": { "type": "number" },
        "sigma_f": { "type": "number" },
        "sigma_e": { "type": "number" },
        "beta_a": { "type": "number" },
        "beta_b": { "type": "number" },
        "lambda_f": { "type": "number" },
        "factor_jump_sd": { "type": "number" },
        "lambda_e": { "type": "number" },
        "idio_jump_sd": { "type": "number" },
        "bulletin_mag_noise_sd": { "type": "number" },
        "earnings_jump_sd": { "type": "number" },
        "signal_sigma_lo": { "type": "number" },
        "signal_sigma_hi": { "type": "number" },
        "signal_refresh_rate": { "type": "number" }
      },
      "additionalProperties": true
    },
    "feeSchedule": {
      "type": "object",
      "required": ["taker_fee_per_lot_centi", "maker_rebate_per_lot_centi", "free_message_budget", "message_overage_fee_centi"],
      "properties": {
        "taker_fee_per_lot_centi": { "$ref": "#/$defs/cash" },
        "maker_rebate_per_lot_centi": { "$ref": "#/$defs/cash" },
        "free_message_budget": { "$ref": "#/$defs/nonNegativeInteger" },
        "message_overage_fee_centi": { "$ref": "#/$defs/cash" }
      },
      "additionalProperties": true
    },
    "earningsEvent": {
      "type": "object",
      "required": ["instrument", "tick"],
      "properties": {
        "instrument": { "$ref": "#/$defs/instrumentIndex" },
        "tick": { "$ref": "#/$defs/nonNegativeInteger" }
      },
      "additionalProperties": true
    },
    "rateLimit": {
      "type": "object",
      "required": ["messages_per_tick", "burst"],
      "properties": {
        "messages_per_tick": { "$ref": "#/$defs/nonNegativeInteger" },
        "burst": { "$ref": "#/$defs/nonNegativeInteger" }
      },
      "additionalProperties": true
    },
    "level": {
      "type": "object",
      "required": ["price", "qty"],
      "properties": {
        "price": { "type": "integer" },
        "qty": { "type": "integer", "minimum": 1 }
      },
      "additionalProperties": true
    },
    "book": {
      "type": "object",
      "required": ["instrument", "bids", "asks"],
      "properties": {
        "instrument": { "$ref": "#/$defs/instrumentIndex" },
        "bids": { "type": "array", "items": { "$ref": "#/$defs/level" } },
        "asks": { "type": "array", "items": { "$ref": "#/$defs/level" } }
      },
      "additionalProperties": true
    },
    "trade": {
      "type": "object",
      "required": ["instrument", "price", "qty", "aggressor_side", "aggressor_seat", "resting_seat"],
      "properties": {
        "instrument": { "$ref": "#/$defs/instrumentIndex" },
        "price": { "type": "integer" },
        "qty": { "type": "integer", "minimum": 1 },
        "aggressor_side": { "enum": ["BUY", "SELL"] },
        "aggressor_seat": { "$ref": "#/$defs/nonNegativeInteger" },
        "resting_seat": { "$ref": "#/$defs/nonNegativeInteger" }
      },
      "additionalProperties": true
    },
    "bulletin": {
      "type": "object",
      "required": ["kind", "asset", "sign", "magnitude_est"],
      "properties": {
        "kind": { "enum": ["FACTOR", "IDIO"] },
        "asset": {
          "oneOf": [
            { "type": "null" },
            { "type": "integer", "minimum": 0, "maximum": 1 }
          ]
        },
        "sign": { "enum": [-1, 1] },
        "magnitude_est": { "type": "number" }
      },
      "additionalProperties": true
    },
    "factorSignal": {
      "type": "object",
      "required": ["type", "s"],
      "properties": {
        "type": { "const": "FACTOR" },
        "s": { "type": "number" }
      },
      "additionalProperties": true
    },
    "idioSignal": {
      "type": "object",
      "required": ["type", "s_a", "s_b"],
      "properties": {
        "type": { "const": "IDIO" },
        "s_a": { "type": "number" },
        "s_b": { "type": "number" }
      },
      "additionalProperties": true
    },
    "signal": {
      "oneOf": [
        { "$ref": "#/$defs/factorSignal" },
        { "$ref": "#/$defs/idioSignal" }
      ]
    },
    "fill": {
      "type": "object",
      "required": ["order_id", "client_id", "instrument", "side", "price", "qty", "is_aggressor", "fee_centi"],
      "properties": {
        "order_id": { "$ref": "#/$defs/orderId" },
        "client_id": { "$ref": "#/$defs/clientId" },
        "instrument": { "$ref": "#/$defs/instrumentIndex" },
        "side": { "enum": ["BUY", "SELL"] },
        "price": { "type": "integer" },
        "qty": { "type": "integer", "minimum": 1 },
        "is_aggressor": { "type": "boolean" },
        "fee_centi": { "$ref": "#/$defs/cash" }
      },
      "additionalProperties": true
    },
    "ack": {
      "type": "object",
      "required": ["client_id", "order_id", "accepted", "reason"],
      "properties": {
        "client_id": { "$ref": "#/$defs/clientId" },
        "order_id": {
          "oneOf": [
            { "type": "null" },
            { "$ref": "#/$defs/orderId" }
          ]
        },
        "accepted": { "type": "boolean" },
        "reason": {
          "oneOf": [
            { "type": "null" },
            { "type": "string" }
          ]
        }
      },
      "additionalProperties": true
    },
    "seatScore": {
      "type": "object",
      "required": ["seat_pseudonym", "score_centi", "placement", "is_house"],
      "properties": {
        "seat_pseudonym": { "type": "string", "minLength": 1 },
        "score_centi": { "$ref": "#/$defs/cash" },
        "placement": {
          "oneOf": [
            { "type": "null" },
            { "type": "integer", "minimum": 1 }
          ]
        },
        "is_house": { "type": "boolean" }
      },
      "additionalProperties": true
    },
    "seatStats": {
      "type": "object",
      "required": [
        "terminal_wealth_centi", "risk_charge_centi", "score_centi",
        "fees_paid_centi", "rebates_centi", "message_count", "fills",
        "final_position", "suspended", "crashed", "overrun_ms"
      ],
      "properties": {
        "terminal_wealth_centi": { "$ref": "#/$defs/cash" },
        "risk_charge_centi": { "$ref": "#/$defs/cash" },
        "score_centi": { "$ref": "#/$defs/cash" },
        "fees_paid_centi": { "$ref": "#/$defs/cash" },
        "rebates_centi": { "$ref": "#/$defs/cash" },
        "message_count": { "$ref": "#/$defs/nonNegativeInteger" },
        "fills": { "$ref": "#/$defs/nonNegativeInteger" },
        "final_position": { "$ref": "#/$defs/position" },
        "suspended": { "type": "boolean" },
        "crashed": { "type": "boolean" },
        "overrun_ms": { "type": "integer", "minimum": 0 }
      },
      "additionalProperties": true
    },
    "matchStart": {
      "allOf": [
        { "$ref": "#/$defs/baseEngineMessage" },
        {
          "type": "object",
          "required": [
            "engine_version", "league", "instruments", "priors", "fee_schedule",
            "seat_pseudonym", "signal_type", "earnings_schedule", "position_limit",
            "message_budget", "think_budget_ms", "think_burst_ms", "rate_limit", "base_price"
          ],
          "properties": {
            "type": { "const": "MATCH_START" },
            "engine_version": { "type": "string", "minLength": 1 },
            "league": { "enum": ["FAST", "STANDARD"] },
            "instruments": {
              "type": "array",
              "prefixItems": [
                { "const": "A" },
                { "const": "B" },
                { "const": "I" }
              ],
              "items": false,
              "minItems": 3,
              "maxItems": 3
            },
            "priors": { "$ref": "#/$defs/priors" },
            "fee_schedule": { "$ref": "#/$defs/feeSchedule" },
            "seat_pseudonym": { "type": "string", "minLength": 1 },
            "signal_type": { "enum": ["FACTOR", "IDIO"] },
            "earnings_schedule": { "type": "array", "items": { "$ref": "#/$defs/earningsEvent" } },
            "position_limit": { "type": "integer", "minimum": 1 },
            "message_budget": { "$ref": "#/$defs/nonNegativeInteger" },
            "think_budget_ms": { "$ref": "#/$defs/nonNegativeInteger" },
            "think_burst_ms": { "$ref": "#/$defs/nonNegativeInteger" },
            "rate_limit": { "$ref": "#/$defs/rateLimit" },
            "base_price": { "type": "integer" }
          }
        }
      ]
    },
    "tick": {
      "allOf": [
        { "$ref": "#/$defs/baseEngineMessage" },
        {
          "type": "object",
          "required": ["books", "trades", "bulletins", "signals", "fills", "acks", "position", "cash", "risk_charge_accrued"],
          "properties": {
            "type": { "const": "TICK" },
            "books": { "type": "array", "items": { "$ref": "#/$defs/book" }, "minItems": 3, "maxItems": 3 },
            "trades": { "type": "array", "items": { "$ref": "#/$defs/trade" } },
            "bulletins": { "type": "array", "items": { "$ref": "#/$defs/bulletin" } },
            "signals": { "$ref": "#/$defs/signal" },
            "fills": { "type": "array", "items": { "$ref": "#/$defs/fill" } },
            "acks": { "type": "array", "items": { "$ref": "#/$defs/ack" } },
            "position": { "$ref": "#/$defs/position" },
            "cash": { "$ref": "#/$defs/cash" },
            "risk_charge_accrued": { "$ref": "#/$defs/cash" }
          }
        }
      ]
    },
    "matchEnd": {
      "allOf": [
        { "$ref": "#/$defs/baseEngineMessage" },
        {
          "type": "object",
          "required": ["t_end", "settlement", "scores", "your_stats"],
          "properties": {
            "type": { "const": "MATCH_END" },
            "t_end": { "$ref": "#/$defs/nonNegativeInteger" },
            "settlement": { "$ref": "#/$defs/position" },
            "scores": { "type": "array", "items": { "$ref": "#/$defs/seatScore" } },
            "your_stats": { "$ref": "#/$defs/seatStats" }
          }
        }
      ]
    },
    "newOrder": {
      "type": "object",
      "required": ["type", "client_id", "instrument", "side", "price", "qty", "tif"],
      "properties": {
        "type": { "const": "NEW_ORDER" },
        "client_id": { "$ref": "#/$defs/clientId" },
        "instrument": { "$ref": "#/$defs/instrument" },
        "side": { "enum": ["BUY", "SELL"] },
        "price": { "$ref": "#/$defs/price" },
        "qty": { "$ref": "#/$defs/quantity" },
        "tif": { "enum": ["GTC", "IOC"] }
      },
      "additionalProperties": true
    },
    "cancel": {
      "type": "object",
      "required": ["type", "client_id", "order_id"],
      "properties": {
        "type": { "const": "CANCEL" },
        "client_id": { "$ref": "#/$defs/clientId" },
        "order_id": { "$ref": "#/$defs/orderId" }
      },
      "additionalProperties": true
    },
    "cancelScope": {
      "oneOf": [
        {
          "type": "object",
          "required": ["kind"],
          "properties": { "kind": { "const": "ALL" } },
          "additionalProperties": true
        },
        {
          "type": "object",
          "required": ["kind", "instrument"],
          "properties": {
            "kind": { "const": "INSTRUMENT" },
            "instrument": { "$ref": "#/$defs/instrument" }
          },
          "additionalProperties": true
        }
      ]
    },
    "cancelAll": {
      "type": "object",
      "required": ["type", "client_id", "scope"],
      "properties": {
        "type": { "const": "CANCEL_ALL" },
        "client_id": { "$ref": "#/$defs/clientId" },
        "scope": { "$ref": "#/$defs/cancelScope" }
      },
      "additionalProperties": true
    }
  }
}
