{
  "$defs": {
    "ADXIndicatorValueRef": {
      "additionalProperties": false,
      "description": "ADX with access to +DI / -DI directional indicators.\n\nThe ADX indicator computes three series internally: the composite ADX\n(trend strength) plus the Plus Directional Indicator (+DI) and Minus\nDirectional Indicator (-DI). Use ``output`` to select which series:\n\n  - ``adx`` (default): composite Wilder ADX, 0-100 trend strength.\n  - ``plus_di``: +DI, upward directional movement strength.\n  - ``minus_di``: -DI, downward directional movement strength.\n\nAll outputs first exist after 2N bars seen (zero-based index 2N-1).\nPublic DI access intentionally waits for composite ADX readiness; TA-Lib\nPLUS_DI/MINUS_DI instead start at index N (DEC-018).\n\n``plus_di > minus_di`` indicates an up-trending directional bias;\n``plus_di < minus_di`` indicates a down-trending bias.",
      "properties": {
        "indicator": {
          "const": "adx_di",
          "default": "adx_di",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "output": {
          "default": "adx",
          "enum": [
            "adx",
            "plus_di",
            "minus_di"
          ],
          "title": "Output",
          "type": "string"
        },
        "period": {
          "default": 14,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "ADXIndicatorValueRef",
      "type": "object"
    },
    "AVWAPIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Anchored VWAP \u2014 VWAP anchored to a specific session/week/month/custom point.\n\nMirrors the IR ``AnchoredVWAP`` model: ``anchor`` selects the canonical\nanchor boundary; ``anchor_datetime`` (ISO 8601 string) supplies the custom\nanchor instant when ``anchor=\"custom\"``.\n\nUse ``vwap`` for the simple session-anchored case; use ``avwap`` when you\nneed week/month/ytd/custom anchors or the ``std_dev`` band output.",
      "properties": {
        "anchor": {
          "default": "session",
          "enum": [
            "session",
            "session_open",
            "week",
            "week_open",
            "month",
            "month_open",
            "month_start",
            "ytd",
            "custom"
          ],
          "title": "Anchor",
          "type": "string"
        },
        "anchor_datetime": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Anchor Datetime"
        },
        "indicator": {
          "const": "avwap",
          "default": "avwap",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "output": {
          "default": "value",
          "enum": [
            "value",
            "std_dev"
          ],
          "title": "Output",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "AVWAPIndicatorValueRef",
      "type": "object"
    },
    "AdIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Accumulation/Distribution line \u2014 running sum of close-location-value times volume.\n\nCumulative and unbounded from the first bar of loaded history (warmup bars\nincluded), so its absolute level is an artifact of where the data starts:\ncompare it against its own moving average or slope, never against a fixed\nnumber. A bar whose high equals its low contributes zero rather than\ndividing by zero.",
      "properties": {
        "indicator": {
          "const": "ad",
          "default": "ad",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "AdIndicatorValueRef",
      "type": "object"
    },
    "AllOfCondition": {
      "additionalProperties": false,
      "description": "Logical AND -- all child conditions must be true.",
      "properties": {
        "child_names": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Child Names"
        },
        "conditions": {
          "items": {
            "$ref": "#/$defs/Condition"
          },
          "minItems": 1,
          "title": "Conditions",
          "type": "array"
        },
        "kind": {
          "const": "all_of",
          "default": "all_of",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "conditions"
      ],
      "title": "AllOfCondition",
      "type": "object"
    },
    "AlwaysTrueCondition": {
      "additionalProperties": false,
      "description": "Always true -- fires on every bar. Use as a no-op placeholder or unconditional trigger.",
      "properties": {
        "kind": {
          "const": "always_true",
          "default": "always_true",
          "title": "Kind",
          "type": "string"
        }
      },
      "title": "AlwaysTrueCondition",
      "type": "object"
    },
    "AnchorPeriod": {
      "enum": [
        "session",
        "week",
        "month"
      ],
      "type": "string"
    },
    "AnchorValueRef": {
      "additionalProperties": false,
      "description": "A field of a named anchor \u2014 a single bar captured earlier and then held.\n\nThe anchor is declared in the strategy's ``anchors`` (captured by\ntimestamp, bar index, or the first false-to-true flip of a condition);\nreferencing an ``anchor_id`` that is not declared fails at compile time.\nUntil the anchor actually fires the value is typed absence. A comparison\nover that absent operand is UNKNOWN; the action boundary takes no action.\n``mid`` and ``range`` are derived from the captured bar's high and low.\nDoes not accept ``resolution``.",
      "properties": {
        "anchor_field": {
          "enum": [
            "open",
            "high",
            "low",
            "close",
            "mid",
            "range"
          ],
          "title": "Anchor Field",
          "type": "string"
        },
        "anchor_id": {
          "title": "Anchor Id",
          "type": "string"
        },
        "kind": {
          "const": "anchor",
          "default": "anchor",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "anchor_id",
        "anchor_field"
      ],
      "title": "AnchorValueRef",
      "type": "object"
    },
    "AnyOfCondition": {
      "additionalProperties": false,
      "description": "Logical OR -- at least one child condition must be true.",
      "properties": {
        "child_names": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Child Names"
        },
        "conditions": {
          "items": {
            "$ref": "#/$defs/Condition"
          },
          "minItems": 1,
          "title": "Conditions",
          "type": "array"
        },
        "kind": {
          "const": "any_of",
          "default": "any_of",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "conditions"
      ],
      "title": "AnyOfCondition",
      "type": "object"
    },
    "AroonIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Aroon \u2014 how recently the window's extreme printed, scaled 0-100 (`line` picks up/down).\n\nMeasured over ``period + 1`` bars including the current one: 100 means the\nhighest high (or lowest low) is the current bar, 0 means it is the oldest\nbar in the window. Ties resolve to the **oldest** matching bar, so a level\nthat has been revisited reads lower than 100 even when the current bar\nequals the extreme. This ref exposes only the up and down lines; there is\nno oscillator (up - down) output.",
      "properties": {
        "indicator": {
          "const": "aroon",
          "default": "aroon",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "line": {
          "enum": [
            "up",
            "down"
          ],
          "title": "Line",
          "type": "string"
        },
        "period": {
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "period",
        "line"
      ],
      "title": "AroonIndicatorValueRef",
      "type": "object"
    },
    "AtLeastCondition": {
      "additionalProperties": false,
      "description": "At-least-N -- true when min_true or more child conditions are true.",
      "properties": {
        "child_names": {
          "anyOf": [
            {
              "items": {
                "anyOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Child Names"
        },
        "conditions": {
          "items": {
            "$ref": "#/$defs/Condition"
          },
          "minItems": 1,
          "title": "Conditions",
          "type": "array"
        },
        "kind": {
          "const": "at_least",
          "default": "at_least",
          "title": "Kind",
          "type": "string"
        },
        "min_true": {
          "maximum": 100,
          "minimum": 1,
          "title": "Min True",
          "type": "integer"
        }
      },
      "required": [
        "conditions",
        "min_true"
      ],
      "title": "AtLeastCondition",
      "type": "object"
    },
    "AtSessionCondition": {
      "additionalProperties": false,
      "description": "Execute a number of minutes after the session open or before the session close.\n\nSchedules the rule at that instant on every trading session, the general form of\n\"five minutes before the close\" or \"thirty minutes after the open\". The offset is\nresolved against each date's own open and close, so ``close`` with -5 is 12:55 on a\n13:00 half-day, and the order fills from the 1-minute execution print there. Works on\nANY timeframe, daily included; a daily strategy decides from the last completed daily\nbar (yesterday's).\n\n``open`` takes an offset of zero or more and ``close`` an offset of zero or less\n(negative is before the anchor): the opposite signs fall outside every session and are\nrefused when written. It needs a market with a session, so it is refused for crypto\n(24/7), which has no open or close; use ``at_time`` there. The rule is evaluated ONLY\nat these instants and places a market order, so other conditions alongside it are\nsampled then and a protective exit must not be scheduled. An entry at exactly the\nclose is refused by the simulator (its closing auction is reduce-only).\n\nBecause it names when to ACT, it can only be an entry or an exit trigger, alone or\ninside ``all_of`` with other conditions, one per rule. It cannot sit on a gate or\ninside any_of/not/sequence/consecutive/at_least/latch.\n\nExample, five minutes before the close: {\"kind\": \"at_session\", \"anchor\": \"close\", \"offset_minutes\": -5}",
      "properties": {
        "anchor": {
          "description": "The session boundary the offset is measured from: 'open' or 'close' of each trading session.",
          "enum": [
            "open",
            "close"
          ],
          "title": "Anchor",
          "type": "string"
        },
        "kind": {
          "const": "at_session",
          "default": "at_session",
          "title": "Kind",
          "type": "string"
        },
        "offset_minutes": {
          "description": "Whole minutes from the anchor; negative is before it. Zero or more after the 'open', zero or less before the 'close' (-5 is five minutes before the close). At most a day either way.",
          "maximum": 1440,
          "minimum": -1440,
          "title": "Offset Minutes",
          "type": "integer"
        }
      },
      "required": [
        "anchor",
        "offset_minutes"
      ],
      "title": "AtSessionCondition",
      "type": "object"
    },
    "AtTimeCondition": {
      "additionalProperties": false,
      "description": "Execute at a specific clock time (e.g. \"buy at 10:16\").\n\nSchedules the rule at that instant. The order is submitted at the clock time\nand fills from the 1-minute execution print opening exactly then \u2014 the time\nis honoured EXACTLY and is never snapped to the strategy's bar grid, because\nexecution resolution is independent of strategy resolution. Works on ANY\ntimeframe, daily included: a daily strategy scheduled at 10:31 takes its\ndecision from the last completed daily bar (yesterday's) and fills at\ntoday's 10:31 print.\n\nThe rule is evaluated ONLY at these moments and is removed from bar-close\nevaluation entirely, so any other conditions attached alongside it are\nsampled at the scheduled instant rather than checked every bar. That makes\nit the wrong place for a stop-loss or other protective exit, which must keep\nchecking every bar; the compiler refuses that combination by name.\n\nThe time is interpreted in the exchange session timezone (US equities:\nAmerica/New_York; crypto: UTC). A time that falls outside the session on a\ngiven date \u2014 15:30 on a 13:00 half-day \u2014 simply does not occur that day. A\ntime equal to that date's actual open or close routes to the opening or\nclosing auction, which backtests simulate and paper/live refuse.\n\nBecause it names when to ACT, it can only be an entry or an exit. It cannot\nsit on a gate or inside any_of/not/sequence; use predicates over ``time`` operands to restrict\nwhich bars qualify.",
      "properties": {
        "kind": {
          "const": "at_time",
          "default": "at_time",
          "title": "Kind",
          "type": "string"
        },
        "time": {
          "description": "Clock time in HH:MM 24-hour format, interpreted in the exchange session timezone. Schedules the decision at exactly this instant \u2014 it is never snapped to the strategy's bar grid, and works on any timeframe including daily.",
          "pattern": "^\\d{2}:\\d{2}$",
          "title": "Time",
          "type": "string"
        }
      },
      "required": [
        "time"
      ],
      "title": "AtTimeCondition",
      "type": "object"
    },
    "BandOutput": {
      "enum": [
        "upper",
        "middle",
        "lower"
      ],
      "type": "string"
    },
    "BinaryExprValueRef": {
      "additionalProperties": false,
      "description": "Binary expression over two ValueRef operands.\n\nProvided alongside UnaryExprValueRef so tests / flat-Condition authors can\nbuild composite expressions like ``abs(close - 100)``. Compiles to a binary\nIRExpression.",
      "properties": {
        "kind": {
          "const": "binary_expr",
          "default": "binary_expr",
          "title": "Kind",
          "type": "string"
        },
        "left": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ]
        },
        "op": {
          "enum": [
            "+",
            "-",
            "*",
            "/",
            "max",
            "min"
          ],
          "title": "Op",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "right": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ]
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "op",
        "left",
        "right"
      ],
      "title": "BinaryExprValueRef",
      "type": "object"
    },
    "BollingerValueRef": {
      "additionalProperties": false,
      "description": "Bollinger Bands \u2014 select band edge or derived property.\n\nOutputs:\n  - upper / middle / lower: price-level bands\n  - bandwidth: (upper - lower) / middle * 100\n  - stddev: population standard deviation over the window",
      "properties": {
        "indicator": {
          "const": "bollinger",
          "default": "bollinger",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "multiplier": {
          "default": 2.0,
          "ge": 0.1,
          "le": 10.0,
          "title": "Multiplier",
          "type": "number"
        },
        "output": {
          "enum": [
            "upper",
            "middle",
            "lower",
            "bandwidth",
            "stddev"
          ],
          "title": "Output",
          "type": "string"
        },
        "period": {
          "default": 20,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "output"
      ],
      "title": "BollingerValueRef",
      "type": "object"
    },
    "BreakoutRetestCondition": {
      "additionalProperties": false,
      "description": "Detect a breakout followed by a pullback retest within N bars.\n\nCompiles to a sequence: Donchian channel breakout, then ATR-based pullback.\nUse for confirmed breakout entries that wait for a retest of the breakout level.",
      "properties": {
        "direction": {
          "description": "long entry on an upside breakout; short entry on a downside breakout.",
          "enum": [
            "long",
            "short"
          ],
          "title": "Direction",
          "type": "string"
        },
        "kind": {
          "const": "breakout_retest",
          "default": "breakout_retest",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "default": 20,
          "description": "Donchian breakout lookback in bars.",
          "maximum": 500,
          "minimum": 5,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "pullback_depth_atr": {
          "default": 1.0,
          "description": "Pullback depth from the breakout, in ATR units.",
          "ge": 0.1,
          "le": 10.0,
          "title": "Pullback Depth Atr",
          "type": "number"
        },
        "within_bars": {
          "default": 20,
          "description": "Maximum bars after the breakout in which the retest can occur.",
          "maximum": 100,
          "minimum": 1,
          "title": "Within Bars",
          "type": "integer"
        }
      },
      "required": [
        "direction"
      ],
      "title": "BreakoutRetestCondition",
      "type": "object"
    },
    "CalendarScheduleCondition": {
      "additionalProperties": false,
      "description": "Fire on a periodic calendar schedule (daily, weekly, biweekly, monthly, quarterly).\n\nUse for DCA / recurring-buy entries that trigger at regular intervals\nregardless of price action.\n\nSchedules the rule: it is evaluated ONLY at its occurrences and is removed\nfrom bar-close evaluation, so any other conditions attached alongside it are\nsampled at the scheduled moment rather than checked every bar. With ``time``\nset, the occurrence is that clock time and the order fills from the\nexecution print there; with ``time`` unset it is the session's own open\n(the opening auction), or midnight on a 24/7 market.\n\n``time`` is read in the exchange session timezone (US equities:\nAmerica/New_York; crypto: UTC) \u2014 exactly as ``at_time`` reads its own \u2014\nunless ``tz`` names another zone. A time that falls outside the session on\na given date simply does not occur that day.\n\nOccurrences landing on a non-trading day are moved or dropped by\n``holiday_policy``; the boundary's own date stays the occurrence's identity,\nso a rolled occurrence fires once, not twice.\n\nBecause it names when to ACT, it can only be an entry or an exit \u2014 not a\ngate, and not inside any_of/not/sequence. Use predicates over ``time`` operands to restrict\nwhich bars qualify instead.",
      "properties": {
        "day": {
          "anyOf": [
            {
              "$ref": "#/$defs/Weekday"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Day of week (required for '1w' and '2w' schedules)"
        },
        "every": {
          "description": "Calendar interval. '2w' fires on the target weekday of even ISO weeks (an absolute epoch, never chained off the last firing).",
          "enum": [
            "1d",
            "1w",
            "2w",
            "1m",
            "3m"
          ],
          "title": "Every",
          "type": "string"
        },
        "holiday_policy": {
          "default": "next_session",
          "description": "What happens when an occurrence lands on a non-trading day (exchange holiday or weekend) for calendar-backed (equity) markets. 'next_session' (default): fire on the first bar of the next trading session. 'skip': drop that occurrence entirely. 'previous_session': fire on the last trading session before the scheduled day. Ignored for 24/7 (crypto) markets, where every day trades.",
          "enum": [
            "next_session",
            "skip",
            "previous_session"
          ],
          "title": "Holiday Policy",
          "type": "string"
        },
        "kind": {
          "const": "calendar_schedule",
          "default": "calendar_schedule",
          "title": "Kind",
          "type": "string"
        },
        "time": {
          "anyOf": [
            {
              "pattern": "^\\d{2}:\\d{2}$",
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Time of day in HH:MM 24-hour format, interpreted in the exchange session timezone unless 'tz' names another one \u2014 the same rule at_time follows. Leave unset to fire at the session's own open.",
          "title": "Time"
        },
        "tz": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "IANA timezone name for 'time'. Omit (the default) to use the exchange session timezone (US equities: America/New_York; crypto: UTC), which is what at_time does. Set it only to pin a time to a zone other than the one the symbol trades in.",
          "title": "Tz"
        }
      },
      "required": [
        "every"
      ],
      "title": "CalendarScheduleCondition",
      "type": "object"
    },
    "CandlePatternCondition": {
      "additionalProperties": false,
      "description": "Detect candlestick patterns (hammer, engulfing, doji, morning star, etc.).\n\nFires on the bar where the pattern completes. Supports single-bar, two-bar,\nand three-bar patterns with optional quality filters. Use for reversal or continuation entries.",
      "properties": {
        "atr_period": {
          "default": 14,
          "description": "ATR period for min_range_atr filter",
          "maximum": 100,
          "minimum": 5,
          "title": "Atr Period",
          "type": "integer"
        },
        "kind": {
          "const": "candle_pattern",
          "default": "candle_pattern",
          "title": "Kind",
          "type": "string"
        },
        "max_body_frac": {
          "anyOf": [
            {
              "ge": 0.0,
              "le": 1.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max body as fraction of total range. Hammer default: 0.33, Doji default: 0.1",
          "title": "Max Body Frac"
        },
        "max_opposite_wick_frac": {
          "anyOf": [
            {
              "ge": 0.0,
              "le": 1.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Max wick on the opposite side as fraction of range. Default: 0.1. Only applies to hammer/shooting_star/inverted_hammer.",
          "title": "Max Opposite Wick Frac"
        },
        "min_body_frac": {
          "anyOf": [
            {
              "ge": 0.0,
              "le": 1.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Min body as fraction of total range. Engulfing default: 0.5",
          "title": "Min Body Frac"
        },
        "min_range_atr": {
          "anyOf": [
            {
              "ge": 0.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Min candle range as multiple of ATR. Filters noise candles. Default: pattern-specific; no filter for inside_bar.",
          "title": "Min Range Atr"
        },
        "min_wick_ratio": {
          "anyOf": [
            {
              "ge": 0.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Min lower (hammer) or upper (shooting_star) wick as multiple of body. Default: 2.0. Only applies to hammer/shooting_star/inverted_hammer.",
          "title": "Min Wick Ratio"
        },
        "pattern": {
          "$ref": "#/$defs/PatternName",
          "description": "Candlestick pattern to detect. Single-bar: hammer, inverted_hammer, shooting_star, doji, dragonfly_doji, gravestone_doji, spinning_top, marubozu. Two-bar: bullish_engulfing, bearish_engulfing, harami, harami_cross, inside_bar. Three-bar: morning_star, evening_star, piercing_line, dark_cloud_cover, three_white_soldiers, three_black_crows."
        }
      },
      "required": [
        "pattern"
      ],
      "title": "CandlePatternCondition",
      "type": "object"
    },
    "CandlestickValueRef": {
      "additionalProperties": false,
      "description": "Candlestick pattern recognition as a numeric signal value (VIB-1316).\n\nEmits a per-bar value in ``{-1.0, 0.0, +1.0}`` indicating a bearish\npattern hit, no hit, or a bullish pattern hit respectively. Resolves the\npattern name to the same :class:`CandlePattern` class as\n``CandlePatternSpec`` and registers the same indicator, so the two paths\nare literally one value series, not two that agree.\n\nA name that reads as one side of a two-sided shape means that side only:\n``bearish_engulfing`` is ``-1`` on a bearish engulfing and ``0`` on a\nbullish one, never ``+1``.\n\nUse this ValueRef when you need the pattern value as an operand in a\n``compare`` or ``cross`` condition (e.g. ``candlestick(doji) == 1``).\nFor role-shaped entry/exit semantics with quality filters, prefer\n``CandlePatternSpec`` as the top-level ConditionSpec.",
      "properties": {
        "indicator": {
          "const": "candlestick",
          "default": "candlestick",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "pattern": {
          "$ref": "#/$defs/PatternName"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "pattern"
      ],
      "title": "CandlestickValueRef",
      "type": "object"
    },
    "CompareCondition": {
      "additionalProperties": false,
      "description": "Compare two signals with a relational operator (>, >=, <, <=, ==, !=).\n\nTrue on every bar where the comparison holds. Use for threshold checks like\n\"close > 100\", \"RSI > 70\", or \"EMA(20) > EMA(50)\".",
      "properties": {
        "kind": {
          "const": "compare",
          "default": "compare",
          "title": "Kind",
          "type": "string"
        },
        "left": {
          "$ref": "#/$defs/ValueRef",
          "description": "Left-hand value (e.g., close, ema(20))"
        },
        "op": {
          "$ref": "#/$defs/CompareOp",
          "description": "Comparison operator"
        },
        "right": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Right-hand value or constant",
          "title": "Right"
        }
      },
      "required": [
        "left",
        "op",
        "right"
      ],
      "title": "CompareCondition",
      "type": "object"
    },
    "CompareOp": {
      "description": "Comparison operators.",
      "enum": [
        "<",
        "<=",
        ">",
        ">=",
        "==",
        "!="
      ],
      "title": "CompareOp",
      "type": "string"
    },
    "Condition": {
      "discriminator": {
        "mapping": {
          "all_of": "#/$defs/AllOfCondition",
          "always_true": "#/$defs/AlwaysTrueCondition",
          "any_of": "#/$defs/AnyOfCondition",
          "at_least": "#/$defs/AtLeastCondition",
          "at_session": "#/$defs/AtSessionCondition",
          "at_time": "#/$defs/AtTimeCondition",
          "breakout_retest": "#/$defs/BreakoutRetestCondition",
          "calendar_schedule": "#/$defs/CalendarScheduleCondition",
          "candle_pattern": "#/$defs/CandlePatternCondition",
          "compare": "#/$defs/CompareCondition",
          "condition_ref": "#/$defs/ConditionReference",
          "consecutive": "#/$defs/ConsecutiveCondition",
          "cross": "#/$defs/CrossSignalCondition",
          "eod_flatten": "#/$defs/EodFlattenCondition",
          "fixed_targets": "#/$defs/FixedTargetsCondition",
          "flag_pattern": "#/$defs/FlagPatternCondition",
          "fvg": "#/$defs/FairValueGapCondition",
          "in_set": "#/$defs/InSetCondition",
          "latch": "#/$defs/LatchCondition",
          "liquidity_sweep": "#/$defs/LiquiditySweepCondition",
          "not": "#/$defs/NotCondition",
          "order_block": "#/$defs/OrderBlockCondition",
          "ote": "#/$defs/OTECondition",
          "pennant_pattern": "#/$defs/PennantPatternCondition",
          "range": "#/$defs/RangeCondition",
          "sequence": "#/$defs/SequenceCondition",
          "spread": "#/$defs/SpreadCondition",
          "trailing_stop": "#/$defs/TrailingStopCondition",
          "transition": "#/$defs/TransitionCondition"
        },
        "propertyName": "kind"
      },
      "oneOf": [
        {
          "$ref": "#/$defs/LiquiditySweepCondition"
        },
        {
          "$ref": "#/$defs/FairValueGapCondition"
        },
        {
          "$ref": "#/$defs/OrderBlockCondition"
        },
        {
          "$ref": "#/$defs/OTECondition"
        },
        {
          "$ref": "#/$defs/FlagPatternCondition"
        },
        {
          "$ref": "#/$defs/PennantPatternCondition"
        },
        {
          "$ref": "#/$defs/BreakoutRetestCondition"
        },
        {
          "$ref": "#/$defs/CandlePatternCondition"
        },
        {
          "$ref": "#/$defs/EodFlattenCondition"
        },
        {
          "$ref": "#/$defs/AtTimeCondition"
        },
        {
          "$ref": "#/$defs/CrossSignalCondition"
        },
        {
          "$ref": "#/$defs/TransitionCondition"
        },
        {
          "$ref": "#/$defs/InSetCondition"
        },
        {
          "$ref": "#/$defs/RangeCondition"
        },
        {
          "$ref": "#/$defs/CompareCondition"
        },
        {
          "$ref": "#/$defs/SpreadCondition"
        },
        {
          "$ref": "#/$defs/FixedTargetsCondition"
        },
        {
          "$ref": "#/$defs/TrailingStopCondition"
        },
        {
          "$ref": "#/$defs/CalendarScheduleCondition"
        },
        {
          "$ref": "#/$defs/AlwaysTrueCondition"
        },
        {
          "$ref": "#/$defs/AllOfCondition"
        },
        {
          "$ref": "#/$defs/AnyOfCondition"
        },
        {
          "$ref": "#/$defs/NotCondition"
        },
        {
          "$ref": "#/$defs/AtLeastCondition"
        },
        {
          "$ref": "#/$defs/SequenceCondition"
        },
        {
          "$ref": "#/$defs/ConsecutiveCondition"
        },
        {
          "$ref": "#/$defs/ConditionReference"
        },
        {
          "$ref": "#/$defs/LatchCondition"
        },
        {
          "$ref": "#/$defs/AtSessionCondition"
        }
      ],
      "required": [
        "kind"
      ]
    },
    "ConditionReference": {
      "additionalProperties": false,
      "description": "Binding-only source node. Resolve before Blueprint compilation.",
      "properties": {
        "definition_id": {
          "minLength": 1,
          "title": "Definition Id",
          "type": "string"
        },
        "kind": {
          "const": "condition_ref",
          "default": "condition_ref",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "definition_id"
      ],
      "title": "ConditionReference",
      "type": "object"
    },
    "ConditionSequenceStep": {
      "additionalProperties": false,
      "properties": {
        "condition": {
          "$ref": "#/$defs/Condition"
        },
        "hold_bars": {
          "anyOf": [
            {
              "maximum": 5000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Hold Bars"
        },
        "id": {
          "anyOf": [
            {
              "minLength": 1,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Explicit step id for SequenceStepValueRef lookup. Defaults to 'step_{index}' (e.g. step_0, step_1) when absent.",
          "title": "Id"
        },
        "within_bars": {
          "anyOf": [
            {
              "maximum": 5000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Within Bars"
        }
      },
      "required": [
        "condition"
      ],
      "title": "ConditionSequenceStep",
      "type": "object"
    },
    "ConsecutiveCondition": {
      "additionalProperties": false,
      "description": "N-of-M bar counter -- true when inner condition holds for min_true bars in a window.\n\nA bar on which the inner condition cannot be evaluated (an operand has no\nvalue yet) counts as a miss, so counting starts from the first bar the data\nexists; on that bar the count is UNKNOWN rather than false unless the window\nalready holds min_true truths -- so ``not(consecutive(...))`` takes no\naction until the data exists.",
      "properties": {
        "child_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Child Name"
        },
        "condition": {
          "$ref": "#/$defs/Condition"
        },
        "kind": {
          "const": "consecutive",
          "default": "consecutive",
          "title": "Kind",
          "type": "string"
        },
        "min_true": {
          "maximum": 100,
          "minimum": 1,
          "title": "Min True",
          "type": "integer"
        },
        "strict": {
          "default": true,
          "title": "Strict",
          "type": "boolean"
        },
        "window_bars": {
          "maximum": 100,
          "minimum": 1,
          "title": "Window Bars",
          "type": "integer"
        }
      },
      "required": [
        "condition",
        "min_true",
        "window_bars"
      ],
      "title": "ConsecutiveCondition",
      "type": "object"
    },
    "ConstantValueRef": {
      "additionalProperties": false,
      "description": "A fixed numeric literal, usable anywhere a value ref is accepted.\n\nThe one ref that is NOT a ``ValueRefBaseModel``: it has no ``symbol`` or\n``resolution`` qualifier. A bare int/float written into ``binary_expr``/\n``unary_expr`` operands or ``compare.right`` is coerced to this type\nautomatically, so writing it out explicitly is rarely necessary.",
      "properties": {
        "kind": {
          "const": "constant",
          "default": "constant",
          "title": "Kind",
          "type": "string"
        },
        "value": {
          "title": "Value",
          "type": "number"
        }
      },
      "required": [
        "value"
      ],
      "title": "ConstantValueRef",
      "type": "object"
    },
    "CrossSignalCondition": {
      "additionalProperties": false,
      "description": "Fires once on the bar where one signal crosses another (edge-based, not state).\n\nSupports any signal type on left and right \u2014 not just indicator-vs-indicator:\n- EMA/RSI/price crossing an indicator level (e.g., EMA 12 cross_above EMA 26)\n- Close crossing a swing high/low (e.g., edge-based breakout at prior swing high)\n- Close crossing a percentile level (e.g., price crosses 20-bar high)\n- Close crossing a rolling max/min (e.g., close cross_above rolling_max(high, 20))\n\nFor level-based (persistent) conditions use compare. For N-bar high/low breakouts\nthat fire every qualifying bar, compare against rolling_max/rolling_min with\nthe desired historical offset. Use cross+swing for edge-based breakouts.\n\nTo ask \"did this cross at any point in the last N bars\", wrap it in consecutive\nwith min_true=1, window_bars=N, strict=False \u2014 cross itself only reports the bar\nit fired on. For intrabar detection, cross against a high or low price operand\nrather than close; fills still follow the bar model of the order type you use.",
      "properties": {
        "direction": {
          "$ref": "#/$defs/LevelDirection"
        },
        "kind": {
          "const": "cross",
          "default": "cross",
          "title": "Kind",
          "type": "string"
        },
        "left": {
          "$ref": "#/$defs/ValueRef",
          "description": "Left-hand value (e.g., ema(20), close)"
        },
        "right": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Right-hand value or constant",
          "title": "Right"
        }
      },
      "required": [
        "left",
        "right",
        "direction"
      ],
      "title": "CrossSignalCondition",
      "type": "object"
    },
    "DispersionValueRef": {
      "additionalProperties": false,
      "description": "Spread of ``source`` across the WHOLE universe on the current bar.\n\nUnlike ``rank`` and ``xs_zscore`` it does not say where this symbol sits; every\nsymbol reads the same number, so it does not degrade while this symbol's own\nvalue is still warming up. ``stat`` chooses the standard deviation or the range.\n``source`` is evaluated once per symbol; it cannot carry a ``symbol`` and cannot\ncontain another cross-sectional operand. Requires a universe or portfolio strategy\nwith two or more symbols.\n\nExample, how widely 20-bar returns are spread across the universe:\n  {\"kind\": \"xs_dispersion\", \"stat\": \"stdev\",\n   \"source\": {\"kind\": \"indicator\", \"indicator\": \"roc\", \"period\": 20}}",
      "properties": {
        "kind": {
          "const": "xs_dispersion",
          "default": "xs_dispersion",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Per-symbol value whose spread across the universe is measured on the current bar. It is evaluated for each symbol on its own data."
        },
        "stat": {
          "description": "'stdev' = sample standard deviation (ddof=1) of the symbols' values. 'range' = highest minus lowest.",
          "enum": [
            "stdev",
            "range"
          ],
          "title": "Stat",
          "type": "string"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "source",
        "stat"
      ],
      "title": "DispersionValueRef",
      "type": "object"
    },
    "DonchianValueRef": {
      "additionalProperties": false,
      "description": "Donchian Channel \u2014 rolling max/min over `period` bars; **excludes the current bar** (differs from most charting platforms which include it).\n\nNote: this engine excludes the current bar so that a close-exceeds-upper\nbreakout signal is meaningful (the current bar cannot contaminate its own\nband). Most charting platforms plot Donchian bands *including* the current\nbar \u2014 strategies ported from those platforms will see the breakout-bar\nupper/lower differ by one bar of high/low data. See\n``docs/indicator-conventions.md``.",
      "properties": {
        "indicator": {
          "const": "donchian",
          "default": "donchian",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "output": {
          "$ref": "#/$defs/BandOutput"
        },
        "period": {
          "default": 20,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "output"
      ],
      "title": "DonchianValueRef",
      "type": "object"
    },
    "EntryBarField": {
      "enum": [
        "open",
        "high",
        "low",
        "close",
        "range",
        "body_high",
        "body_low"
      ],
      "type": "string"
    },
    "EntryBarValueRef": {
      "additionalProperties": false,
      "description": "Reference to a field of the signal bar for the currently-open position.\n\nAuto-captured by the engine when a position opens. Returns NaN when no\nposition is open. Clears on position close. Use for ORB-style \"stop out\nbelow the signal-bar low\" without AnchorDefinition's silent-latching bug.\n\nFields:\n  - open / high / low / close: signal-bar OHLC\n  - range: high - low\n  - body_high: max(open, close)\n  - body_low: min(open, close)",
      "properties": {
        "entry_bar_field": {
          "$ref": "#/$defs/EntryBarField"
        },
        "kind": {
          "const": "entry_bar",
          "default": "entry_bar",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "entry_bar_field"
      ],
      "title": "EntryBarValueRef",
      "type": "object"
    },
    "EodFlattenCondition": {
      "additionalProperties": false,
      "description": "Session end-of-day flatten exit: flat from close - N until the close.\n\nAt ``flatten_minutes_before_close`` minutes before the session close,\nresolved against THAT DATE's actual close \u2014 so a 13:00 half-day flattens at\n12:55, not at a 15:55 instant the session never reaches \u2014 it closes the\nposition (filled from the execution print at that instant) and cancels any\nentry still waiting to fill; until the close every entry signal is skipped\nas ``policy:eod_flat_window``. It must be its own, whole exit: combined with\nother conditions or attached with a size_frac it is refused at compile, as\nis a scheduled entry at or after close - N.\n\nEquity (session-bound) instruments only. A 24/7 crypto session has no close\nto anchor to, so the condition is refused at attach and at Blueprint\nconstruction for a crypto strategy (it used to compile to a\n``minutes_to_close`` comparison that is NaN on every bar and silently never\nfired). For a 24/7 market exit at a clock time with ``at_time`` instead.",
      "properties": {
        "flatten_minutes_before_close": {
          "default": 5,
          "description": "Minutes before session close at which to flatten, and from which the position stays flat until the close. Compiles to a scheduled exit at AnchorTrigger(close, -N) holding the flat window. Use at least 1 minute; 0 would require an official closing-auction print for market-on-close.",
          "maximum": 390,
          "minimum": 1,
          "title": "Flatten Minutes Before Close",
          "type": "integer"
        },
        "kind": {
          "const": "eod_flatten",
          "default": "eod_flatten",
          "title": "Kind",
          "type": "string"
        }
      },
      "title": "EodFlattenCondition",
      "type": "object"
    },
    "EquityValueRef": {
      "additionalProperties": false,
      "description": "The strategy's running equity-curve value.\n\n``field`` selects: ``current`` (equity right now), ``peak`` (the running\nhigh-water mark), or ``drawdown_pct`` (percent below that peak \u2014 0 at a\nnew high, positive while underwater). Use ``drawdown_pct`` directly for a\ncircuit-breaker gate rather than deriving it from ``current``/``peak``.\n\nVIB-1370 / VIB-1319: the sub-metric selector is named ``field``\nacross every ValueRef-style type. The legacy name ``equity_metric`` was\nremoved.",
      "properties": {
        "field": {
          "enum": [
            "drawdown_pct",
            "current",
            "peak"
          ],
          "title": "Field",
          "type": "string"
        },
        "kind": {
          "const": "equity",
          "default": "equity",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "field"
      ],
      "title": "EquityValueRef",
      "type": "object"
    },
    "EventDistanceValueRef": {
      "additionalProperties": false,
      "description": "Distance from the current bar to an event type's next or previous occurrence.\n\n``direction=\"previous\"`` counts back to the latest occurrence at or before the\nbar: 0 on the bar the occurrence is assigned to. ``direction=\"next\"`` counts\nforward to the earliest occurrence strictly after the bar, so it is never 0\nin minutes. ``unit`` is wall-clock minutes or bars.\n\nWhen no such occurrence is known the value is infinity (present, not absent), so\n``distance <= N`` is false and ``not (distance <= N)`` is true: that is how a\n\"stay away from events\" gate is written.\n\nA ``next`` distance needs the event's date to be known in advance. Among system\nevents only those with a forward release calendar (macro.cpi,\nmacro.nonfarm_payrolls, macro.unemployment) qualify; any other system event is\nrefused with ``direction=\"next\"``, because the platform cannot anticipate it.\nCustom events are unrestricted. Use ``direction=\"previous\"`` to react after a\nsystem event.\n\nExample, within 5 bars after an event:\n  {\"kind\": \"event_distance\", \"event_type_id\": \"my-signal\", \"direction\": \"previous\",\n   \"unit\": \"bars\"}",
      "properties": {
        "direction": {
          "description": "'previous' = distance back to the latest occurrence at or before this bar. 'next' = distance forward to the earliest occurrence after this bar; needs a custom event or a system event with a forward release calendar.",
          "enum": [
            "next",
            "previous"
          ],
          "title": "Direction",
          "type": "string"
        },
        "event_type_id": {
          "description": "Event type ID from list_event_types().",
          "title": "Event Type Id",
          "type": "string"
        },
        "kind": {
          "const": "event_distance",
          "default": "event_distance",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        },
        "unit": {
          "anyOf": [
            {
              "enum": [
                "minutes",
                "bars"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Measure the distance in wall-clock minutes or in bars; minutes when omitted.",
          "title": "Unit"
        }
      },
      "required": [
        "event_type_id",
        "direction"
      ],
      "title": "EventDistanceValueRef",
      "type": "object"
    },
    "EventFieldValueRef": {
      "additionalProperties": false,
      "description": "Field from latest event payload.\n\nAuthoring flow:\n  1. Call list_event_types().\n  2. Pick event_type_id from a returned event type.\n  3. Pick event_field from payload_schema.fields.\n\nExample:\n  {\"kind\": \"event_field\", \"event_type_id\": \"macro.cpi\",\n   \"event_field\": \"value\"}\n\nTo compare a release against a previous one, set occurrences_back on a\nsecond ref and subtract:\n  {\"kind\": \"binary_expr\", \"op\": \"-\",\n   \"left\":  {\"kind\": \"event_field\", \"event_type_id\": \"macro.cpi\",\n             \"event_field\": \"value\"},\n   \"right\": {\"kind\": \"event_field\", \"event_type_id\": \"macro.cpi\",\n             \"event_field\": \"value\", \"occurrences_back\": 1}}",
      "properties": {
        "event_field": {
          "description": "Payload field from payload_schema.fields. A field the event type's schema does not declare is refused, naming the declared fields.",
          "title": "Event Field",
          "type": "string"
        },
        "event_type_id": {
          "description": "Event type ID from list_event_types().",
          "title": "Event Type Id",
          "type": "string"
        },
        "fill": {
          "default": "previous",
          "description": "'previous' carries forward; 'none' resolves only on the event bar.",
          "enum": [
            "none",
            "previous"
          ],
          "title": "Fill",
          "type": "string"
        },
        "kind": {
          "const": "event_field",
          "default": "event_field",
          "title": "Kind",
          "type": "string"
        },
        "max_age_seconds": {
          "anyOf": [
            {
              "minimum": 0,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Staleness limit for carried-forward values, in seconds.",
          "title": "Max Age Seconds"
        },
        "occurrences_back": {
          "default": 0,
          "description": "0 = latest release; 1 = the release before it, and so on. Counted by distinct data period, so a later revision of an earlier period does not consume a step. Requires an event type whose payload declares a 'period' (the macro.*/treasury.*/market.*/energy.* release families) and fill='previous'. Resolves as missing when fewer than occurrences_back+1 releases are known.",
          "minimum": 0,
          "title": "Occurrences Back",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "event_type_id",
        "event_field"
      ],
      "title": "EventFieldValueRef",
      "type": "object"
    },
    "FairValueGapCondition": {
      "additionalProperties": false,
      "description": "Detect Fair Value Gap (FVG) zones -- 3-candle imbalance patterns.\n\nCreates zones in the zone registry when FVGs form. Entry triggers\nwhen price returns to an unfilled FVG zone.",
      "properties": {
        "direction": {
          "$ref": "#/$defs/PatternBias",
          "default": "both"
        },
        "expires_after_bars": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": 200,
          "description": "Zone expiry in bars",
          "title": "Expires After Bars"
        },
        "fill_frac": {
          "default": 0.5,
          "description": "Fill percentage (0-1) at which zone is considered filled",
          "ge": 0.0,
          "le": 1.0,
          "title": "Fill Frac",
          "type": "number"
        },
        "kind": {
          "const": "fvg",
          "default": "fvg",
          "title": "Kind",
          "type": "string"
        },
        "min_gap_pct": {
          "default": 0.1,
          "description": "Minimum gap size as % of price",
          "ge": 0.0,
          "title": "Min Gap Pct",
          "type": "number"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this zone's lifecycle. Set to e.g. '4h' to create, fill, expire, and invalidate this zone on 4H bar closes. None = strategy primary timeframe."
        },
        "select": {
          "$ref": "#/$defs/ZoneSelection",
          "default": "any",
          "description": "How to pick among matching zones at trigger time. 'any' (default) fires when any zone overlaps the bar. 'nearest'/'most_recent'/'oldest' deterministically pick one zone \u2014 required when binding execution price via TriggeringZonePriceSpec (VIB-591)."
        }
      },
      "title": "FairValueGapCondition",
      "type": "object"
    },
    "FixedTargetsCondition": {
      "additionalProperties": false,
      "description": "Fixed take-profit, stop-loss, and time-stop exits based on entry price.\n\nFires when price hits a percentage-based TP/SL level or a bar count is reached.\nUse as an exit condition for simple bracket-style position management.",
      "properties": {
        "breakeven_buffer_pct": {
          "default": 0.0,
          "ge": 0.0,
          "le": 10.0,
          "title": "Breakeven Buffer Pct",
          "type": "number"
        },
        "breakeven_trigger_pct": {
          "anyOf": [
            {
              "gt": 0.0,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Breakeven Trigger Pct"
        },
        "kind": {
          "const": "fixed_targets",
          "default": "fixed_targets",
          "title": "Kind",
          "type": "string"
        },
        "stop_loss_pct": {
          "anyOf": [
            {
              "ge": 0.1,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Stop Loss Pct"
        },
        "stop_loss_price_ref": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Structural stop price (absolute) resolved from a ValueRef at fill time. Mutually exclusive with stop_loss_pct."
        },
        "stop_order_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "ID of the stop order to use as the R-distance anchor for take_profit_r_multiple. Defaults to 'sl' (the stop emitted by this spec). Only valid when take_profit_r_multiple is set.",
          "title": "Stop Order Id"
        },
        "take_profit_pct": {
          "anyOf": [
            {
              "ge": 0.1,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Take Profit Pct"
        },
        "take_profit_r_multiple": {
          "anyOf": [
            {
              "gt": 0.0,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Take-profit at N x R from the SL's risk distance. Requires a stop (stop_loss_pct or stop_loss_price_ref). Mutually exclusive with take_profit_pct and take_profit_tiers. Resolved at fill time.",
          "title": "Take Profit R Multiple"
        },
        "take_profit_tiers": {
          "items": {
            "$ref": "#/$defs/TpTierSpec"
          },
          "title": "Take Profit Tiers",
          "type": "array"
        },
        "time_stop_bars": {
          "anyOf": [
            {
              "description": "Hold the position for exactly this many bars, counting the bar the entry filled in as bar 1: the exit is decided at the close of the Nth held bar and fills at the next bar's open, so exit_bar - entry_bar is N.",
              "maximum": 10000,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Time Stop Bars"
        }
      },
      "title": "FixedTargetsCondition",
      "type": "object"
    },
    "FlagPatternCondition": {
      "additionalProperties": false,
      "description": "Flag chart pattern detection.\n\nA flag is a strong directional move (pole) followed by a shallow counter-trend\nconsolidation (flag). Breakout from the flag continues the pole direction.",
      "properties": {
        "consolidation_bars": {
          "maximum": 100,
          "minimum": 1,
          "title": "Consolidation Bars",
          "type": "integer"
        },
        "direction": {
          "enum": [
            "up",
            "down",
            "same"
          ],
          "title": "Direction",
          "type": "string"
        },
        "kind": {
          "const": "flag_pattern",
          "default": "flag_pattern",
          "title": "Kind",
          "type": "string"
        },
        "min_momentum_pct": {
          "default": 2.0,
          "ge": 0.5,
          "le": 20.0,
          "title": "Min Momentum Pct",
          "type": "number"
        },
        "momentum_bars": {
          "maximum": 100,
          "minimum": 1,
          "title": "Momentum Bars",
          "type": "integer"
        }
      },
      "required": [
        "momentum_bars",
        "consolidation_bars",
        "direction"
      ],
      "title": "FlagPatternCondition",
      "type": "object"
    },
    "GapIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Session-open gap, in percent: (session open - prior session close) / prior session close * 100.\n\nThe value exists ONLY on the first bar of the chosen session, and only once the\nprior session's close is known; on every other bar it is absent, so a comparison\nover it is unknown. Compare it with a threshold to state a market fact: a gap up\nis ``gap > 0.5``, a gap down is ``gap < -0.5``. The operand names no trade side.\n\nThe source candles must preserve both session boundaries. US equity candles do so\nat every supported resolution; crypto candles (UTC-aligned) do so for all three\nsessions through 30m and for Asia at 1h. Other region and timeframe combinations\nare refused when the strategy is compiled. Unlike ``gap_pct`` (every bar, open\nagainst the previous close), this is session-aware.\n\nExample: {\"kind\": \"indicator\", \"indicator\": \"gap\", \"session\": \"us\"}",
      "properties": {
        "indicator": {
          "const": "gap",
          "default": "gap",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "session": {
          "anyOf": [
            {
              "enum": [
                "us",
                "eu",
                "asia"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Session whose opening gap is measured: 'us', 'eu' or 'asia'; 'us' when omitted. Requires candles aligned to that session's open and close (see the operand description).",
          "title": "Session"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "GapIndicatorValueRef",
      "type": "object"
    },
    "InSetCondition": {
      "additionalProperties": false,
      "description": "Membership test for discrete-valued refs: ``value \u2208 members`` (or its negation).\n\nIntended for categorical/discrete signals like ``supertrend.direction``\n(values in ``{-1, +1}``). Equivalent to ``any_of(compare(value == m) ...)``\nbut with cleaner ergonomics and a single capture node.\n\nSemantics:\n  - Evaluates once per bar from the resolved value of ``value``.\n  - If ``value`` has no value on the bar, the condition is unknown and\n    takes no action regardless of ``negate`` (undefined is neither in nor\n    out of the set).\n  - Numeric members are compared with tolerance 1e-9.\n  - ``negate=True`` flips in-set to not-in-set.\n\nExamples:\n    Direction is +1 or -1 (trend is engaged either way):\n        {\"kind\": \"in_set\", \"value\": {...supertrend.direction ref...},\n         \"members\": [-1, 1]}\n\n    Direction is NOT sideways (not zero):\n        {\"kind\": \"in_set\", \"value\": {...}, \"members\": [0], \"negate\": true}\n\nFor onset semantics on categorical values (fires once when a discrete ref's value\nchanges), use ``TransitionCondition`` \u2014 the discrete-valued counterpart to onset.",
      "properties": {
        "kind": {
          "const": "in_set",
          "default": "in_set",
          "title": "Kind",
          "type": "string"
        },
        "members": {
          "description": "Allowed values (numeric literals, compared with tolerance 1e-9).",
          "items": {
            "type": "number"
          },
          "minItems": 1,
          "title": "Members",
          "type": "array"
        },
        "negate": {
          "default": false,
          "description": "If True, fires when value is NOT in members.",
          "title": "Negate",
          "type": "boolean"
        },
        "value": {
          "$ref": "#/$defs/ValueRef",
          "description": "Value to test for set membership"
        }
      },
      "required": [
        "value",
        "members"
      ],
      "title": "InSetCondition",
      "type": "object"
    },
    "IndicatorValueRef": {
      "discriminator": {
        "mapping": {
          "ad": "#/$defs/AdIndicatorValueRef",
          "adx_di": "#/$defs/ADXIndicatorValueRef",
          "aroon": "#/$defs/AroonIndicatorValueRef",
          "atr": "#/$defs/PeriodIndicatorValueRef",
          "avwap": "#/$defs/AVWAPIndicatorValueRef",
          "bollinger": "#/$defs/BollingerValueRef",
          "candlestick": "#/$defs/CandlestickValueRef",
          "cci": "#/$defs/PeriodIndicatorValueRef",
          "cmf": "#/$defs/PeriodIndicatorValueRef",
          "donchian": "#/$defs/DonchianValueRef",
          "ema": "#/$defs/MovingAverageValueRef",
          "gap": "#/$defs/GapIndicatorValueRef",
          "keltner": "#/$defs/KeltnerValueRef",
          "macd": "#/$defs/MacdIndicatorValueRef",
          "macd_histogram": "#/$defs/MacdIndicatorValueRef",
          "macd_signal": "#/$defs/MacdIndicatorValueRef",
          "median": "#/$defs/PeriodIndicatorValueRef",
          "mfi": "#/$defs/PeriodIndicatorValueRef",
          "obv": "#/$defs/OBVIndicatorValueRef",
          "pivot_p": "#/$defs/PivotIndicatorValueRef",
          "pivot_r1": "#/$defs/PivotIndicatorValueRef",
          "pivot_r2": "#/$defs/PivotIndicatorValueRef",
          "pivot_r3": "#/$defs/PivotIndicatorValueRef",
          "pivot_s1": "#/$defs/PivotIndicatorValueRef",
          "pivot_s2": "#/$defs/PivotIndicatorValueRef",
          "pivot_s3": "#/$defs/PivotIndicatorValueRef",
          "roc": "#/$defs/PeriodIndicatorValueRef",
          "rsi": "#/$defs/PeriodIndicatorValueRef",
          "sma": "#/$defs/MovingAverageValueRef",
          "srsi_d": "#/$defs/SrsiIndicatorValueRef",
          "srsi_k": "#/$defs/SrsiIndicatorValueRef",
          "std": "#/$defs/PeriodIndicatorValueRef",
          "stochastic_d": "#/$defs/StochasticIndicatorValueRef",
          "stochastic_k": "#/$defs/StochasticIndicatorValueRef",
          "supertrend": "#/$defs/SupertrendIndicatorValueRef",
          "var": "#/$defs/PeriodIndicatorValueRef",
          "volume_sma": "#/$defs/PeriodIndicatorValueRef",
          "vwap": "#/$defs/VWAPIndicatorValueRef",
          "vwap_bands": "#/$defs/VWAPBandsValueRef",
          "vwma": "#/$defs/MovingAverageValueRef",
          "wma": "#/$defs/MovingAverageValueRef"
        },
        "propertyName": "indicator"
      },
      "oneOf": [
        {
          "$ref": "#/$defs/MovingAverageValueRef"
        },
        {
          "$ref": "#/$defs/PeriodIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/MacdIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/StochasticIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/SrsiIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/AdIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/OBVIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/AroonIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/SupertrendIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/BollingerValueRef"
        },
        {
          "$ref": "#/$defs/DonchianValueRef"
        },
        {
          "$ref": "#/$defs/KeltnerValueRef"
        },
        {
          "$ref": "#/$defs/VWAPBandsValueRef"
        },
        {
          "$ref": "#/$defs/VWAPIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/AVWAPIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/ADXIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/PivotIndicatorValueRef"
        },
        {
          "$ref": "#/$defs/CandlestickValueRef"
        },
        {
          "$ref": "#/$defs/GapIndicatorValueRef"
        }
      ],
      "required": [
        "indicator"
      ]
    },
    "KeltnerValueRef": {
      "additionalProperties": false,
      "description": "Keltner Channel \u2014 EMA middle with ATR-based upper/lower bands.\n\nMirrors BollingerValueRef/DonchianValueRef. Exposes typed access to the\nthree KC band edges. ``period`` drives the EMA middle band. Set\n``atr_period`` to use a different lookback for the ATR that shapes the\nupper/lower bands (common practice is a shorter ATR than the EMA, e.g.\n``period=20, atr_period=10``). ``atr_period=None`` (default) reuses\n``period`` for ATR \u2014 historical behavior.",
      "properties": {
        "atr_period": {
          "anyOf": [
            {
              "maximum": 500,
              "minimum": 2,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Atr Period"
        },
        "indicator": {
          "const": "keltner",
          "default": "keltner",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "multiplier": {
          "default": 2.0,
          "ge": 0.1,
          "le": 10.0,
          "title": "Multiplier",
          "type": "number"
        },
        "output": {
          "$ref": "#/$defs/BandOutput"
        },
        "period": {
          "default": 20,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "output"
      ],
      "title": "KeltnerValueRef",
      "type": "object"
    },
    "LatchCondition": {
      "additionalProperties": false,
      "description": "Edge detector with memory -- true on the bar where ``fire`` holds after ``arm`` did.\n\nObserved at every bar close, warm-up bars included. A close where ``arm`` is true\narms it and the result is false; ``arm`` wins if both are true. While armed, a close\nwhere ``fire`` is true returns true once and disarms it. If either side has no value on\na bar the result has no value and the armed state does not change. Position events never\nreset it, only firing does, and two identical latches share one state.\n\nThis is the form of \"price re-enters the band after leaving it\" (``arm`` = outside,\n``fire`` = inside) and of a rising edge (``arm`` = not(c), ``fire`` = c). Under a\nscheduled rule only the outcome of the frame bar's close is seen: a fire on an earlier\nbar is consumed on that bar and is not seen at the next occurrence, so a latch fire\nbetween two occurrences is lost while an armed latch stays armed.\n\nBoth children are ordinary conditions or ``condition_ref``s. A protective policy\n(stop, target, trailing stop) or a schedule cannot sit inside either, as under any\nother combinator.\n\nExample, price closes back above a level after closing below it:\n{\"kind\": \"latch\",\n \"arm\": {\"kind\": \"compare\", \"left\": {\"kind\": \"price\", \"field\": \"close\"}, \"op\": \"<\", \"right\": 100},\n \"fire\": {\"kind\": \"compare\", \"left\": {\"kind\": \"price\", \"field\": \"close\"}, \"op\": \">=\", \"right\": 100}}",
      "properties": {
        "arm": {
          "$ref": "#/$defs/Condition",
          "description": "Condition that arms the latch on any close where it is true. Arming wins if `fire` is true on the same close."
        },
        "fire": {
          "$ref": "#/$defs/Condition",
          "description": "Condition that, while the latch is armed, makes it true for that one close and disarms it."
        },
        "kind": {
          "const": "latch",
          "default": "latch",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "arm",
        "fire"
      ],
      "title": "LatchCondition",
      "type": "object"
    },
    "LevelDirection": {
      "enum": [
        "above",
        "below"
      ],
      "type": "string"
    },
    "LiquiditySweepCondition": {
      "additionalProperties": false,
      "description": "Detect a liquidity sweep pattern (price breaks a level then reclaims it).\n\nPrice sweeps beyond a recent high/low and then reverses back within\nwithin_bars. Common stop-hunt pattern used for reversal entries.\n\ndirection:\n  \"low\"  \u2014 sweep below recent low, reclaim above it (bullish reversal)\n  \"high\" \u2014 sweep above recent high, reclaim below it (bearish reversal)\n  \"both\" \u2014 detect either sweep type",
      "properties": {
        "direction": {
          "default": "low",
          "enum": [
            "low",
            "high",
            "both"
          ],
          "title": "Direction",
          "type": "string"
        },
        "kind": {
          "const": "liquidity_sweep",
          "default": "liquidity_sweep",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "default": 20,
          "maximum": 5000,
          "minimum": 1,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "within_bars": {
          "default": 5,
          "maximum": 100,
          "minimum": 1,
          "title": "Within Bars",
          "type": "integer"
        }
      },
      "title": "LiquiditySweepCondition",
      "type": "object"
    },
    "LivePositionValueRef": {
      "additionalProperties": false,
      "description": "A metric of the CURRENTLY OPEN position, read live every bar (not a\nfrozen snapshot from entry \u2014 contrast ``entry_bar``/``sequence_step``,\nwhich capture a value once and hold it).\n\n``field`` selects: ``entry_price``, ``bars_in_position``,\n``unrealized_pnl_pct`` / ``unrealized_pnl_usd``, or\n``max_excursion_pct`` / ``min_excursion_pct`` (the best/worst unrealized\nPnL% reached since entry \u2014 MAE/MFE). All are NaN while flat.\n\n``bars_in_position`` counts the bars the position has been open in,\nINCLUDING the bar it filled in: it is 1 at the close of the fill bar, not\n0. An exit on ``bars_in_position >= N`` therefore decides at the close of\nthe Nth held bar and fills at the next bar's open, so the trade's\n``exit_bar - entry_bar`` is exactly N.\n\nVIB-1398 / VIB-1370 / VIB-1319: the sub-metric selector is named\n``field`` across every ValueRef-style type. The legacy name\n``position_metric`` was removed (mirrors the IR-side\n``LivePositionRef.metric \u2192 field`` rename that VIB-1394 shipped).",
      "properties": {
        "field": {
          "enum": [
            "entry_price",
            "bars_in_position",
            "unrealized_pnl_pct",
            "unrealized_pnl_usd",
            "max_excursion_pct",
            "min_excursion_pct"
          ],
          "title": "Field",
          "type": "string"
        },
        "kind": {
          "const": "live_position",
          "default": "live_position",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "field"
      ],
      "title": "LivePositionValueRef",
      "type": "object"
    },
    "MacdIndicatorValueRef": {
      "additionalProperties": false,
      "description": "MACD \u2014 `indicator` selects the macd line, its signal line, or the histogram.\n\n``macd`` is ``EMA(fast) - EMA(slow)`` on close; ``macd_signal`` is an\nSMA-seeded EMA **of the MACD line**, not of price; ``macd_histogram`` is\n``macd - macd_signal``. The MACD line first exists after ``slow_period``\nbars have been seen, at zero-based index ``slow_period - 1``. The signal\nline and histogram first exist after ``slow_period + signal_period - 1``\nbars have been seen, at zero-based index ``slow_period + signal_period - 2``.\nEach line is NaN before its readiness point.",
      "properties": {
        "fast_period": {
          "maximum": 500,
          "minimum": 1,
          "title": "Fast Period",
          "type": "integer"
        },
        "indicator": {
          "enum": [
            "macd",
            "macd_signal",
            "macd_histogram"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "signal_period": {
          "maximum": 500,
          "minimum": 1,
          "title": "Signal Period",
          "type": "integer"
        },
        "slow_period": {
          "maximum": 500,
          "minimum": 2,
          "title": "Slow Period",
          "type": "integer"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator",
        "fast_period",
        "slow_period",
        "signal_period"
      ],
      "title": "MacdIndicatorValueRef",
      "type": "object"
    },
    "MovingAverageValueRef": {
      "additionalProperties": false,
      "description": "Moving-average series use the same shape with the authored MA period range.",
      "properties": {
        "indicator": {
          "enum": [
            "ema",
            "sma",
            "wma",
            "vwma"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "default": 0,
          "maximum": 500,
          "minimum": 0,
          "title": "Offset",
          "type": "integer"
        },
        "period": {
          "maximum": 800,
          "minimum": 1,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator",
        "period"
      ],
      "title": "MovingAverageValueRef",
      "type": "object"
    },
    "NamedZoneValueRef": {
      "additionalProperties": false,
      "description": "A boundary of a named strategy-level zone: its `upper`, `lower`, or derived `mid`.\n\n``mid`` is computed as ``(upper + lower) / 2`` at read time and is not a\nstored field. Zone boundaries may themselves be anchor refs, in which case\nthe whole zone reads NaN until that anchor fires. Referencing an undeclared\n``zone_id`` fails at compile time. Does not accept ``resolution``.",
      "properties": {
        "kind": {
          "const": "named_zone",
          "default": "named_zone",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        },
        "zone_field": {
          "$ref": "#/$defs/ZoneLevel"
        },
        "zone_id": {
          "title": "Zone Id",
          "type": "string"
        }
      },
      "required": [
        "zone_id",
        "zone_field"
      ],
      "title": "NamedZoneValueRef",
      "type": "object"
    },
    "NotCondition": {
      "additionalProperties": false,
      "description": "Logical NOT -- inverts the inner condition.\n\nOver a reference that has no value on the bar (an indicator still warming\nup, an opening range before its window) the inner condition is unknown, and\nso is its negation: ``not`` takes no action there rather than firing.",
      "properties": {
        "child_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Child Name"
        },
        "condition": {
          "$ref": "#/$defs/Condition"
        },
        "kind": {
          "const": "not",
          "default": "not",
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "condition"
      ],
      "title": "NotCondition",
      "type": "object"
    },
    "OBVIndicatorValueRef": {
      "additionalProperties": false,
      "description": "On-balance volume \u2014 running sum of volume signed by the close-to-close direction.\n\nAn unchanged close contributes nothing (neither added nor subtracted). NaN\non the very first bar, since there is no prior close. Like ``ad`` this is\ncumulative from the start of loaded history, so its absolute level is\nmeaningless \u2014 compare it to its own average or slope, not to a constant.",
      "properties": {
        "indicator": {
          "const": "obv",
          "default": "obv",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "OBVIndicatorValueRef",
      "type": "object"
    },
    "OTECondition": {
      "additionalProperties": false,
      "description": "Optimal Trade Entry -- Fibonacci retracement zone between swing points.\n\nCreates a zone between two Fibonacci levels (default 61.8%-78.6%) of the\nmost recent swing high to swing low range. Entry triggers when price\nretraces into this zone.",
      "properties": {
        "direction": {
          "$ref": "#/$defs/PatternBias",
          "default": "both"
        },
        "expires_after_bars": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": 100,
          "description": "Zone expiry in bars",
          "title": "Expires After Bars"
        },
        "fib_lower": {
          "default": 0.786,
          "description": "Lower fib level (deeper retracement)",
          "ge": 0.0,
          "le": 1.0,
          "title": "Fib Lower",
          "type": "number"
        },
        "fib_upper": {
          "default": 0.618,
          "description": "Upper fib level (closer to origin, shallower retracement)",
          "ge": 0.0,
          "le": 1.0,
          "title": "Fib Upper",
          "type": "number"
        },
        "fill_frac": {
          "default": 0.5,
          "description": "Fill percentage (0-1) at which zone is considered filled",
          "ge": 0.0,
          "le": 1.0,
          "title": "Fill Frac",
          "type": "number"
        },
        "kind": {
          "const": "ote",
          "default": "ote",
          "title": "Kind",
          "type": "string"
        },
        "select": {
          "$ref": "#/$defs/ZoneSelection",
          "default": "any",
          "description": "How to pick among matching zones at trigger time. 'any' (default) fires when any zone overlaps the bar. 'nearest'/'most_recent'/'oldest' deterministically pick one zone \u2014 required when binding execution price via TriggeringZonePriceSpec (VIB-591)."
        },
        "swing_strength": {
          "default": 5,
          "description": "Bars on each side to confirm swing point",
          "maximum": 50,
          "minimum": 2,
          "title": "Swing Strength",
          "type": "integer"
        }
      },
      "title": "OTECondition",
      "type": "object"
    },
    "OrderBlockCondition": {
      "additionalProperties": false,
      "description": "Detect Order Block zones -- last opposing candle before a displacement move.\n\nTracks the most recent opposing candle (bearish before a bullish move, or vice\nversa) and, when a displacement candle fires, creates a zone from it. Entry\ntriggers when price returns to the zone.\n\n**What the DEFAULTS actually do -- weaker than textbook SMC.** With no optional\nfield set, \"displacement\" is literally ``close > open`` (bullish) or\n``close < open`` (bearish): ANY up/down bar qualifies, with no size, body or\nwick test. The zone spans the opposing candle's FULL high/low range including\nwicks, not its body. And the opposing-candle tracker is never consumed, so every\nlater up-bar re-arms a new zone from the same -- possibly arbitrarily old --\ncandle.\n\nTo get the canonical SMC reading, set any of ``use_wicks=False`` (body bounds),\n``min_body_frac`` (body as a fraction of range) or ``min_displacement_atr`` (body\nin ATR(14) multiples). Setting ANY of the three also switches on\nconsume-after-fire, so one setup yields one zone.",
      "properties": {
        "direction": {
          "$ref": "#/$defs/PatternBias",
          "default": "both"
        },
        "expires_after_bars": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": 200,
          "description": "Zone expiry in bars",
          "title": "Expires After Bars"
        },
        "fill_frac": {
          "default": 0.5,
          "description": "Fill percentage (0-1) at which zone is considered filled",
          "ge": 0.0,
          "le": 1.0,
          "title": "Fill Frac",
          "type": "number"
        },
        "kind": {
          "const": "order_block",
          "default": "order_block",
          "title": "Kind",
          "type": "string"
        },
        "min_body_frac": {
          "anyOf": [
            {
              "ge": 0.0,
              "le": 1.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional displacement-body filter. Require the displacement candle's body |close-open| to be at least this fraction of its full range (high-low). None disables the filter (default-preserving). Setting this also activates canonical-mode consume-after-fire.",
          "title": "Min Body Frac"
        },
        "min_displacement_atr": {
          "anyOf": [
            {
              "gt": 0.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional displacement strength filter, in ATR(14) multiples. The displacement candle body must satisfy |close-open| >= mult * ATR(14). None disables the filter (default-preserving). Setting this also activates canonical-mode consume-after-fire.",
          "title": "Min Displacement Atr"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Higher-timeframe override for zone lifecycle. Set to '4h' to detect OBs on 4H bars. None = strategy timeframe."
        },
        "select": {
          "$ref": "#/$defs/ZoneSelection",
          "default": "any",
          "description": "How to pick among matching zones at trigger time. 'any' (default) fires when any zone overlaps the bar. 'nearest'/'most_recent'/'oldest' deterministically pick one zone \u2014 required when binding execution price via TriggeringZonePriceSpec (VIB-591)."
        },
        "use_wicks": {
          "default": true,
          "description": "Zone bounds source for the opposing candle. Default True preserves current behavior (full high/low including wicks). False switches to canonical SMC body bounds (max(open,close)/min(open,close)) and activates consume-after-fire so only one zone fires per setup.",
          "title": "Use Wicks",
          "type": "boolean"
        }
      },
      "title": "OrderBlockCondition",
      "type": "object"
    },
    "PatternBias": {
      "enum": [
        "bullish",
        "bearish",
        "both"
      ],
      "type": "string"
    },
    "PatternName": {
      "enum": [
        "hammer",
        "inverted_hammer",
        "shooting_star",
        "doji",
        "dragonfly_doji",
        "gravestone_doji",
        "spinning_top",
        "marubozu",
        "bullish_engulfing",
        "bearish_engulfing",
        "harami",
        "harami_cross",
        "inside_bar",
        "morning_star",
        "evening_star",
        "piercing_line",
        "dark_cloud_cover",
        "three_white_soldiers",
        "three_black_crows"
      ],
      "type": "string"
    },
    "PennantPatternCondition": {
      "additionalProperties": false,
      "description": "Pennant chart pattern detection.\n\nA pennant is a strong directional move (pole) followed by converging trendlines\n(pennant). Breakout from the pennant continues the pole direction.",
      "properties": {
        "consolidation_bars": {
          "maximum": 100,
          "minimum": 2,
          "title": "Consolidation Bars",
          "type": "integer"
        },
        "direction": {
          "enum": [
            "up",
            "down",
            "same"
          ],
          "title": "Direction",
          "type": "string"
        },
        "kind": {
          "const": "pennant_pattern",
          "default": "pennant_pattern",
          "title": "Kind",
          "type": "string"
        },
        "min_momentum_pct": {
          "default": 2.0,
          "ge": 0.5,
          "le": 20.0,
          "title": "Min Momentum Pct",
          "type": "number"
        },
        "momentum_bars": {
          "maximum": 100,
          "minimum": 1,
          "title": "Momentum Bars",
          "type": "integer"
        }
      },
      "required": [
        "momentum_bars",
        "consolidation_bars",
        "direction"
      ],
      "title": "PennantPatternCondition",
      "type": "object"
    },
    "PercentileValueRef": {
      "additionalProperties": false,
      "description": "Percentile RANK of the current bar's `field` (or of any `source` value) within the trailing `lookback_bars` window.\n\nReturns ``100 * (values strictly below the current one) / (lookback_bars - 1)``\nover a window that **includes the current bar**: a new window high reads\n100, a new window low reads 0. Ties are not counted, so a perfectly flat\nwindow reads 0.\n\n**This is the inverse of** ``rolling_stats(stat=\"percentile\")``, which\nreturns the *value* sitting at a given percentile level. Use this ref to\nask \"how extreme is price right now\", and ``rolling_stats`` to ask \"what\nprice is the 90th percentile\". NaN propagates: any missing value in the\nwindow returns NaN.\n\n``source`` ranks any value ref rather than a price field, so \"is the volume\nunusually high\" or \"is the band width unusually narrow\" is one operand:\n``lookback_bars`` (2 to 500) is the window the current value is ranked in.\nEvery bar in the window is evaluated, so a long window over a nested\nexpression costs more than a price field does.\n\nExample, the percentile of the Bollinger band width over 100 bars:\n  {\"kind\": \"percentile\", \"lookback_bars\": 100,\n   \"source\": {\"kind\": \"binary_expr\", \"op\": \"-\",\n              \"left\": {\"kind\": \"indicator\", \"indicator\": \"bollinger\", \"output\": \"upper\"},\n              \"right\": {\"kind\": \"indicator\", \"indicator\": \"bollinger\", \"output\": \"lower\"}}}",
      "properties": {
        "field": {
          "anyOf": [
            {
              "$ref": "#/$defs/PriceField"
            },
            {
              "type": "null"
            }
          ],
          "default": "close",
          "description": "Price field to rank (open, high, low or close). Mutually exclusive with `source`: when `source` is given there is no price field. Close when neither is given."
        },
        "kind": {
          "const": "percentile",
          "default": "percentile",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "default": 100,
          "maximum": 500,
          "minimum": 2,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Any value ref to rank instead of a price field: an indicator, a volume, or an expression. Mutually exclusive with `field`. Its own resolution and symbol are honoured."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "PercentileValueRef",
      "type": "object"
    },
    "PeriodIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Entry point for the single-period indicator family (ema, sma, rsi, atr, roc, ...).\n\n``period`` means whatever that indicator's textbook definition means, and\nthat differs across the family: ``sma``/``std``/``median`` use a strict\nN-bar window, while ``ema``, ``rsi`` and ``atr`` are SMA-seeded\nand then smoothed exponentially (Wilder for rsi/atr), keeping a\nresponse to data older than N bars. SMA-seeded EMA first exists after N\nbars seen (zero-based index N-1); RSI and ATR at index N.\n\n``roc`` returns a **ratio, not a percentage** \u2014 a 5% move reads ``0.05``,\nso a threshold of ``5`` means 500%. ``offset`` reads the indicator's own\nvalue N bars back, which is how you express \"this indicator is rising\".",
      "properties": {
        "indicator": {
          "enum": [
            "rsi",
            "atr",
            "roc",
            "cci",
            "volume_sma",
            "mfi",
            "cmf",
            "std",
            "var",
            "median"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "default": 0,
          "maximum": 500,
          "minimum": 0,
          "title": "Offset",
          "type": "integer"
        },
        "period": {
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator",
        "period"
      ],
      "title": "PeriodIndicatorValueRef",
      "type": "object"
    },
    "PivotIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Classic floor-trader pivot levels (P, S1-S3, R1-R3) from the PRIOR day's H/L/C.\n\nLevels are computed once per day and stay **constant for every bar of the\ncurrent day**, rolling forward only at the boundary \u2014 they are not\nrecomputed intraday. The boundary is the **UTC** calendar day, not the\nexchange session, which matters for crypto and for non-US venues. NaN until\none full prior day has completed.",
      "properties": {
        "indicator": {
          "enum": [
            "pivot_p",
            "pivot_s1",
            "pivot_s2",
            "pivot_s3",
            "pivot_r1",
            "pivot_r2",
            "pivot_r3"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator"
      ],
      "title": "PivotIndicatorValueRef",
      "type": "object"
    },
    "PriceField": {
      "description": "Price bar fields.",
      "enum": [
        "open",
        "high",
        "low",
        "close"
      ],
      "title": "PriceField",
      "type": "string"
    },
    "PriceValueRef": {
      "additionalProperties": false,
      "description": "A single OHLC field of the current bar, or of a bar `offset` bars back.\n\n``offset=0`` (the default) is the bar being evaluated. Reading past the\nstart of loaded history returns NaN rather than clamping to the first bar,\nand a NaN operand makes the enclosing compare false.",
      "properties": {
        "field": {
          "$ref": "#/$defs/PriceField",
          "default": "close"
        },
        "kind": {
          "const": "price",
          "default": "price",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "default": 0,
          "maximum": 500,
          "minimum": 0,
          "title": "Offset",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "PriceValueRef",
      "type": "object"
    },
    "RangeCondition": {
      "additionalProperties": false,
      "description": "Interval test: ``subject`` is between ``low`` and ``high``, each bound's inclusivity stated separately.\n\nTrue on every bar the interval contains ``subject`` \u2014 a LEVEL condition, not\nan edge. Two bounds are what make this its own type rather than another\n``compare`` operator.\n\nPer-bound inclusivity is not decoration. ``[30, 70)`` \u2014 enter when RSI is at\nor above 30 but strictly below 70 \u2014 cannot be said with one shared flag, and\nthe two interval tests already inside the runtime disagree: a band's\n``inside`` level is strictly exclusive on both edges, a zone membership test\nis inclusive on both. Both are expressible here.\n\nSemantics:\n  - Bounds are resolved every bar, so they may be moving series (a band\n    edge, a moving average) as well as literals.\n  - If ``subject``, ``low`` or ``high`` has no value (indicator warmup,\n    undefined prior bar) the condition is unknown and takes no action --\n    the same rule ``compare`` uses, so ``not(range)`` does not fire either.\n  - Literal bounds are checked at construction: ``low`` above ``high``, or\n    ``low`` equal to ``high`` with either bound exclusive, is an empty\n    interval that could never fire, and is refused rather than shipped as a\n    zero-trade strategy.\n\nExamples:\n    RSI in the mid-band, high end excluded (``[30, 70)``):\n        {\"kind\": \"range\", \"subject\": {...rsi ref...}, \"low\": 30, \"high\": 70,\n         \"low_inclusive\": true, \"high_inclusive\": false}\n\n    Close inside a Bollinger band, both edges excluded:\n        {\"kind\": \"range\", \"subject\": {...close...},\n         \"low\": {...bollinger lower...}, \"high\": {...bollinger upper...},\n         \"low_inclusive\": false, \"high_inclusive\": false}\n\nFor a one-sided threshold use ``compare``; for membership in a discrete set\nuse ``in_set``; to fire once on entering the interval rather than on every\nbar inside it, wrap this in ``transition`` semantics via ``cross`` on the\nbound you care about.",
      "properties": {
        "high": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Upper bound: a value ref or a constant",
          "title": "High"
        },
        "high_inclusive": {
          "description": "True tests `subject <= high`; False tests `subject < high`. Required for the same reason as `low_inclusive`.",
          "title": "High Inclusive",
          "type": "boolean"
        },
        "kind": {
          "const": "range",
          "default": "range",
          "title": "Kind",
          "type": "string"
        },
        "low": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Lower bound: a value ref or a constant",
          "title": "Low"
        },
        "low_inclusive": {
          "description": "True tests `subject >= low`; False tests `subject > low`. Required: the two two-bound sites this replaces DISAGREE (`band_level: inside` is exclusive on both edges, zone membership is inclusive on both), so there is no default that is not a silent choice between them.",
          "title": "Low Inclusive",
          "type": "boolean"
        },
        "subject": {
          "$ref": "#/$defs/ValueRef",
          "description": "Value tested against the interval"
        }
      },
      "required": [
        "subject",
        "low",
        "high",
        "low_inclusive",
        "high_inclusive"
      ],
      "title": "RangeCondition",
      "type": "object"
    },
    "RankValueRef": {
      "additionalProperties": false,
      "description": "Cross-sectional rank of ``source`` among the universe's symbols on the current bar, 0 to 1.\n\nThe lowest symbol reads 0, the highest 1 (mid-rank for ties). With fewer than 2\nsymbols holding a value there is no rank: it is absent, so a comparison over it\nis unknown (not 0.5). ``source`` is evaluated once per symbol; it cannot carry a\n``symbol`` and cannot contain another cross-sectional operand. Requires a universe\nor portfolio strategy with two or more symbols.\n\nExample, the top tercile of the universe by 20-bar rate of change:\n  {\"kind\": \"rank\", \"source\": {\"kind\": \"indicator\", \"indicator\": \"roc\", \"period\": 20}}\ncompared with ``> 0.66``.",
      "properties": {
        "kind": {
          "const": "rank",
          "default": "rank",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Per-symbol value to rank across the universe on the current bar, e.g. an indicator or a price. It is evaluated for each symbol on its own data."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "source"
      ],
      "title": "RankValueRef",
      "type": "object"
    },
    "RollingMaxValueRef": {
      "additionalProperties": false,
      "description": "Highest value of `source` over the prior `lookback_bars` \u2014 **excludes the current bar**.\n\n``source`` accepts ANY value ref, not just a price field: an indicator, a\nspread, or a nested expression. That is what makes normalisations\ncomposable \u2014 e.g. a stochastic of the MACD line is\n``(macd - rolling_min(macd)) / (rolling_max(macd) - rolling_min(macd))``\nbuilt with ``binary_expr``, with no dedicated indicator type.\n\nTwo behaviours to design around, both deliberate:\n\n- **The current bar is excluded** (same convention as ``donchian``), so a\n  close-exceeds-max breakout is meaningful \u2014 the current bar cannot\n  contaminate its own threshold. For a stochastic you want an INCLUSIVE\n  window, so rebuild one with ``binary_expr``:\n  ``{\"kind\": \"binary_expr\", \"op\": \"max\", \"left\": <source>,\n     \"right\": {\"kind\": \"rolling_max\", \"source\": <source>,\n               \"lookback_bars\": N - 1}}``\n- **NaN propagates**: one missing value anywhere in the window returns NaN\n  (no silent zero-substitution while `source` is still warming up), and a\n  NaN operand makes the enclosing compare false. Indicators with their own\n  NaN-compacting buffers \u2014 ``srsi_k``, ``stochastic_k`` \u2014 do NOT behave\n  this way, which is why they are not redundant with this ref.",
      "properties": {
        "field": {
          "anyOf": [
            {
              "$ref": "#/$defs/PriceField"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "kind": {
          "const": "rolling_max",
          "default": "rolling_max",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "default": 20,
          "maximum": 500,
          "minimum": 2,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Any value ref (indicator, price, spread, or a nested binary_expr) as a raw dict \u2014 typed `dict` only to avoid a circular reference in the ref union, and validated against the full union at parse time."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "RollingMaxValueRef",
      "type": "object"
    },
    "RollingMinValueRef": {
      "additionalProperties": false,
      "description": "Lowest value of `source` over the prior `lookback_bars` \u2014 **excludes the current bar**.\n\nThe mirror of ``rolling_max``; see that class for the composition pattern,\nthe inclusive-window workaround via ``binary_expr`` ``min``, and the\nNaN-propagation rule. ``source`` accepts any value ref, not just a price\nfield.",
      "properties": {
        "field": {
          "anyOf": [
            {
              "$ref": "#/$defs/PriceField"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        },
        "kind": {
          "const": "rolling_min",
          "default": "rolling_min",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "default": 20,
          "maximum": 500,
          "minimum": 2,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Any value ref (indicator, price, spread, or a nested binary_expr) as a raw dict \u2014 typed `dict` only to avoid a circular reference in the ref union, and validated against the full union at parse time."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "RollingMinValueRef",
      "type": "object"
    },
    "RollingStatsValueRef": {
      "additionalProperties": false,
      "description": "A rolling statistic of `source` over `lookback_bars` \u2014 mean, std, zscore, sum, count, rank, percentile or ema.\n\n``source`` accepts ANY value ref, including a nested ``binary_expr``, so\nthis is the general smoothing and normalisation primitive: an SMA of an\narbitrary expression is ``stat=\"mean\"``, and a rolling z-score of a spread\nis ``stat=\"zscore\"``. Combined with ``rolling_max``/``rolling_min`` it\nexpresses whole indicator families that have no dedicated type \u2014 the %K of\na stochastic over any series is a 3-bar ``\"mean\"`` of the normalised ratio.\n\n**NaN propagates**: one missing value in the window returns NaN, and a NaN\noperand makes the enclosing compare false. Purpose-built indicators that\ncompact their buffers to finite samples (``srsi_k``, ``stochastic_k``)\nsurvive undefined stretches that this ref does not, so they are not\nredundant with it.\n\n``percentile_level`` is required when ``stat=\"percentile\"`` and must be\nomitted otherwise.",
      "properties": {
        "kind": {
          "const": "rolling_stats",
          "default": "rolling_stats",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "maximum": 1000,
          "minimum": 2,
          "title": "Lookback Bars",
          "type": "integer"
        },
        "percentile_level": {
          "anyOf": [
            {
              "ge": 0.0,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Percentile Level"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Value or expression to summarize."
        },
        "stat": {
          "enum": [
            "mean",
            "std",
            "zscore",
            "sum",
            "count",
            "rank",
            "percentile",
            "ema"
          ],
          "title": "Stat",
          "type": "string"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "source",
        "lookback_bars",
        "stat"
      ],
      "title": "RollingStatsValueRef",
      "type": "object"
    },
    "SequenceCondition": {
      "additionalProperties": false,
      "description": "Ordered sequence -- steps must fire in order, each optionally within N bars.\n\nSet ``id`` to use this sequence with ``SequenceStepValueRef`` (VIB-792):\nthe id plus each step's id addresses the matched bars so downstream\nrefs can read the retest bar low, breakout bar high, etc.\n\nA bar on which the active step cannot be evaluated (an operand has no value\nyet, e.g. an opening range before its window) counts as a miss for the\nsequence's progress, and the sequence is UNKNOWN on that bar rather than\nfalse -- so ``not(sequence(...))`` takes no action until the data exists.",
      "properties": {
        "id": {
          "anyOf": [
            {
              "minLength": 1,
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Explicit sequence id for SequenceStepValueRef lookup. Must be unique within a strategy. Auto-generated when absent.",
          "title": "Id"
        },
        "kind": {
          "const": "sequence",
          "default": "sequence",
          "title": "Kind",
          "type": "string"
        },
        "steps": {
          "items": {
            "$ref": "#/$defs/ConditionSequenceStep"
          },
          "minItems": 1,
          "title": "Steps",
          "type": "array"
        }
      },
      "required": [
        "steps"
      ],
      "title": "SequenceCondition",
      "type": "object"
    },
    "SequenceStepValueRef": {
      "additionalProperties": false,
      "description": "Reference to a field of the bar that matched a named sequence step.\n\nAddress the bar that satisfied a specific step inside a specific\nSequenceCondition. Useful for \"stop at the retest bar's low\" in\nbreak-retest setups.\n\nLifecycle mirrors EntryBarValueRef: the snapshot is captured at step\nfire, preserved for the lifetime of the open position, cleared on\nposition close. NaN when no position is open or the step has not yet\nfired on the current match.\n\nFields:\n  - open / high / low / close: step-bar OHLC\n  - range: high - low\n  - body_high: max(open, close)\n  - body_low: min(open, close)",
      "properties": {
        "kind": {
          "const": "sequence_step",
          "default": "sequence_step",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "sequence_id": {
          "description": "Id of the target SequenceCondition.",
          "minLength": 1,
          "title": "Sequence Id",
          "type": "string"
        },
        "step_field": {
          "$ref": "#/$defs/EntryBarField"
        },
        "step_id": {
          "description": "Id of the step within the sequence.",
          "minLength": 1,
          "title": "Step Id",
          "type": "string"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "sequence_id",
        "step_id",
        "step_field"
      ],
      "title": "SequenceStepValueRef",
      "type": "object"
    },
    "SessionValueRef": {
      "additionalProperties": false,
      "description": "Session-level value reference.\n\nDay-scoped mode (default): resolves against the full trading day in the\nsession timezone. field in {open, high, low, close, prior_close,\nbars_into_session, range, mid}.\n\nIntraday window mode: set ``window_start`` (HH:MM wall-clock in the\nsession timezone) and ``window_duration_minutes`` to aggregate OHLC\nacross a fixed intraday slot (e.g., Asian session 00:00+480, ORB\n09:30+30). Before the window opens each day: NaN. During the window:\nevolving aggregate. After close: frozen aggregate for the rest of the\nday; resets at the next day's window_start. Fields prior_close and\nbars_into_session are day-scope only (not valid with a window).",
      "properties": {
        "kind": {
          "const": "session",
          "default": "session",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "anyOf": [
            {
              "maximum": 500,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Bars back. Absent is the session extreme through the current bar; k >= 1 is the extreme through bar i-k, with no value on a session's first k bars. Only the day-scoped 'high' and 'low' accept it. A level the current bar can still move needs offset 1 to be crossed.",
          "title": "Offset"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "session_field": {
          "enum": [
            "open",
            "high",
            "low",
            "close",
            "prior_close",
            "bars_into_session",
            "range",
            "mid",
            "in_session"
          ],
          "title": "Session Field",
          "type": "string"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        },
        "window_duration_minutes": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Window Duration Minutes"
        },
        "window_start": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Intraday window start as HH:MM wall-clock in the session timezone (e.g., '09:30'). Requires window_duration_minutes.",
          "title": "Window Start"
        }
      },
      "required": [
        "session_field"
      ],
      "title": "SessionValueRef",
      "type": "object"
    },
    "SharedSignalValueRef": {
      "additionalProperties": false,
      "description": "Reference to a portfolio-level shared signal by name (VIB-2102).\n\nResolves only inside a multi-member portfolio that declares a matching\n``shared_signals`` entry; bound to a member-local metric at portfolio\ncompile time. Illegal in a standalone strategy.",
      "properties": {
        "kind": {
          "const": "shared_signal",
          "default": "shared_signal",
          "title": "Kind",
          "type": "string"
        },
        "name": {
          "minLength": 1,
          "title": "Name",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "name"
      ],
      "title": "SharedSignalValueRef",
      "type": "object"
    },
    "SiblingValueRef": {
      "additionalProperties": false,
      "description": "The value of ``source`` on another symbol of the universe, on the current bar.\n\n``symbol`` must be a member of the strategy's universe; one outside it never has\na value, so such a strategy is refused when it is compiled. ``source`` is\nevaluated on that symbol's own data, not this one's; it cannot carry a ``symbol``\nand cannot contain another cross-sectional operand. Requires a universe or\nportfolio strategy with two or more symbols.\n\nExample, QQQ's close, to compare this symbol's close with:\n  {\"kind\": \"sibling\", \"symbol\": \"QQQ\", \"source\": {\"kind\": \"price\", \"field\": \"close\"}}",
      "properties": {
        "kind": {
          "const": "sibling",
          "default": "sibling",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Value read on the sibling's own bars and indicators, independent of this symbol's data."
        },
        "symbol": {
          "description": "Symbol of the sibling in the strategy's universe whose value is read, e.g. 'QQQ' when the universe is ['SPY', 'QQQ', 'IWM'].",
          "minLength": 1,
          "title": "Symbol",
          "type": "string"
        }
      },
      "required": [
        "symbol",
        "source"
      ],
      "title": "SiblingValueRef",
      "type": "object"
    },
    "SpreadCondition": {
      "additionalProperties": false,
      "description": "Trigger on spread or ratio deviation between two symbols.\n\nCalculates ratio, difference, log-ratio, or z-score between two assets.\nUse for pairs trading, intermarket mean-reversion, or relative-value entries.",
      "properties": {
        "calc_type": {
          "default": "zscore",
          "description": "How to calculate the spread value",
          "enum": [
            "ratio",
            "difference",
            "log_ratio",
            "zscore"
          ],
          "title": "Calc Type",
          "type": "string"
        },
        "hedge_ratio": {
          "anyOf": [
            {
              "ge": 0.1,
              "le": 10.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Hedge ratio (default: 1.0 or rolling estimate)",
          "title": "Hedge Ratio"
        },
        "kind": {
          "const": "spread",
          "default": "spread",
          "title": "Kind",
          "type": "string"
        },
        "symbol_a": {
          "description": "First symbol in spread (numerator)",
          "title": "Symbol A",
          "type": "string"
        },
        "symbol_b": {
          "description": "Second symbol in spread (denominator)",
          "title": "Symbol B",
          "type": "string"
        },
        "threshold": {
          "default": 2.0,
          "description": "Threshold value for trigger",
          "title": "Threshold",
          "type": "number"
        },
        "trigger_op": {
          "default": "above",
          "description": "Trigger when spread is above/below threshold",
          "enum": [
            "above",
            "below",
            "crosses_above",
            "crosses_below"
          ],
          "title": "Trigger Op",
          "type": "string"
        },
        "window_bars": {
          "default": 100,
          "description": "Lookback for rolling calculations",
          "maximum": 1000,
          "minimum": 10,
          "title": "Window Bars",
          "type": "integer"
        }
      },
      "required": [
        "symbol_a",
        "symbol_b"
      ],
      "title": "SpreadCondition",
      "type": "object"
    },
    "SrsiIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Stochastic RSI \u2014 the stochastic formula applied to RSI values, not to price.\n\n``period`` is used twice: it is both the RSI period and the lookback of the\nstochastic applied over it. ``srsi_k`` is the raw %K smoothed by\n``k_period``; ``srsi_d`` smooths that again by ``d_period``.\n\n**Range is 0-100, not 0-1** \u2014 ``100 * (rsi - lowest_rsi) / rsi_range``,\nthe pandas-ta/TA-Lib convention and the same scale as this platform's own\n``rsi`` and ``stochastic_k``. So \"deeply oversold\" is ``<= 5``, NOT\n``<= 0.05``; a 0-1 threshold makes the condition silently almost-always or\nalmost-never true instead of erroring. Measured on SPY 1m Q1 2025:\n``>= 50`` fires 50.8% of bars, ``>= 95`` fires 10.2%, ``>= 0.95`` fires\n95.5%.\n\n**Not equivalent to the same shape built from `rolling_stats`/`rolling_max`\nover an `rsi` ref**, for two reasons. The scale is the big one: the generic\ncomposition returns 0-1, this returns 0-100, so identical thresholds mean\ndifferent things. Second, the lookback here is the last ``period``\n**finite** RSI samples \u2014 undefined RSI bars are skipped rather than\nadmitted to the window \u2014 so it survives flat stretches where the generic\ncomposition propagates NaN.",
      "properties": {
        "d_period": {
          "default": 3,
          "maximum": 100,
          "minimum": 1,
          "title": "D Period",
          "type": "integer"
        },
        "indicator": {
          "enum": [
            "srsi_k",
            "srsi_d"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "k_period": {
          "default": 3,
          "maximum": 100,
          "minimum": 1,
          "title": "K Period",
          "type": "integer"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "period": {
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator",
        "period"
      ],
      "title": "SrsiIndicatorValueRef",
      "type": "object"
    },
    "StochasticIndicatorValueRef": {
      "additionalProperties": false,
      "description": "Stochastic oscillator \u2014 `stochastic_k` (smoothed %K) or `stochastic_d` (%D).\n\nRaw %K is ``100 * (close - lowest_low) / (highest_high - lowest_low)`` over\n``period`` bars **including the current bar** \u2014 the opposite convention to\n``rolling_max``/``rolling_min``, which exclude it. ``stochastic_k`` is\nalready SMA-smoothed over ``k_period`` (i.e. the slow %K, not raw %K), and\n``stochastic_d`` smooths that again over ``d_period``, so %D needs a\nfurther ``d_period`` bars before it is finite.\n\nA flat window (zero high-low range) yields ``0.0``, not NaN. This is a\npurpose-built indicator, not a shorthand for a ``rolling_max``/\n``rolling_min`` composition, which would use an exclusive window and\npropagate NaN instead.",
      "properties": {
        "d_period": {
          "default": 3,
          "maximum": 100,
          "minimum": 1,
          "title": "D Period",
          "type": "integer"
        },
        "indicator": {
          "enum": [
            "stochastic_k",
            "stochastic_d"
          ],
          "title": "Indicator",
          "type": "string"
        },
        "k_period": {
          "default": 3,
          "maximum": 100,
          "minimum": 1,
          "title": "K Period",
          "type": "integer"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "period": {
          "default": 14,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "indicator"
      ],
      "title": "StochasticIndicatorValueRef",
      "type": "object"
    },
    "SupertrendIndicatorValueRef": {
      "additionalProperties": false,
      "description": "SuperTrend \u2014 `output=\"value\"` gives the active band price, `\"direction\"` gives +1/-1.\n\n``value`` is whichever band is currently active (lower band in an uptrend,\nupper band in a downtrend), so it **jumps discontinuously across the price\nevery time the trend flips** \u2014 it is a trailing stop level, not a smooth\nline, and a price-vs-value comparison changes meaning on each flip. Use\n``direction`` when you want trend state. Both are NaN until ``period``\nbars have been seen (Wilder ATR, SMA-seeded).",
      "properties": {
        "indicator": {
          "const": "supertrend",
          "default": "supertrend",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "multiplier": {
          "default": 3.0,
          "ge": 0.1,
          "le": 10.0,
          "title": "Multiplier",
          "type": "number"
        },
        "output": {
          "default": "value",
          "enum": [
            "value",
            "direction"
          ],
          "title": "Output",
          "type": "string"
        },
        "period": {
          "default": 10,
          "maximum": 500,
          "minimum": 2,
          "title": "Period",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "SupertrendIndicatorValueRef",
      "type": "object"
    },
    "SwingValueRef": {
      "additionalProperties": false,
      "description": "Price of a confirmed swing high/low \u2014 a bar whose extreme beats `strength` bars each side.\n\nConfirmation is **lagging by design**: a swing at bar N is not visible\nuntil bar ``N + strength``, because the bars to its right have to print\nfirst. That is what makes it lookahead-free, and it means the newest\nreadable swing is always at least ``strength`` bars old.\n\nThe comparison is strict, so a level matched exactly by a neighbouring bar\nnever forms a swing. ``offset`` counts back through confirmed swings\n(0 = most recent); typed absence if that many have not been confirmed yet.\nA comparison over that absent operand is UNKNOWN; the action boundary\ntakes no action.",
      "properties": {
        "kind": {
          "const": "swing",
          "default": "swing",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "default": 0,
          "maximum": 20,
          "minimum": 0,
          "title": "Offset",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "strength": {
          "default": 5,
          "maximum": 50,
          "minimum": 1,
          "title": "Strength",
          "type": "integer"
        },
        "swing_type": {
          "enum": [
            "high",
            "low"
          ],
          "title": "Swing Type",
          "type": "string"
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        },
        "value_at_swing": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "The value to read at the swing bar. The swing itself is always found on PRICE (the bar high for a swing high, the bar low for a swing low); this chooses what is read there. Omitted means the swing's own price. Any value ref is accepted, and its own resolution and symbol are honoured."
        }
      },
      "required": [
        "swing_type"
      ],
      "title": "SwingValueRef",
      "type": "object"
    },
    "TimeValueRef": {
      "additionalProperties": false,
      "description": "Reference to the current bar's time field.\n\nVIB-1370 / VIB-1319: the sub-metric selector is named ``field`` across\nevery ValueRef-style type. The legacy name ``component`` was removed.\n\nCalendar/intraday fields: ``hour``, ``minute``, ``day_of_week`` (0=Mon),\n``day_of_month``, ``month`` (1=Jan), ``minutes_since_midnight``.\n\nSession-aware fields (use the strategy's session metadata):\n``minutes_to_close``, ``minutes_since_open``. Have no value for crypto (24h)\nand pre/post-market bars. A comparison over a missing value is unknown and\ntakes no action, negated or not: ``not(minutes_to_close <= 5)`` does not\nfire outside the session.",
      "properties": {
        "field": {
          "enum": [
            "hour",
            "minute",
            "day_of_week",
            "day_of_month",
            "month",
            "minutes_since_midnight",
            "minutes_to_close",
            "minutes_since_open"
          ],
          "title": "Field",
          "type": "string"
        },
        "kind": {
          "const": "time",
          "default": "time",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        },
        "timezone": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "IANA timezone (e.g. 'America/New_York'). None or 'UTC' = no conversion.",
          "title": "Timezone"
        }
      },
      "required": [
        "field"
      ],
      "title": "TimeValueRef",
      "type": "object"
    },
    "Timeframe": {
      "description": "Bar duration. These seven are the only granularities the data layer serves.",
      "enum": [
        "1m",
        "5m",
        "15m",
        "30m",
        "1h",
        "4h",
        "1d"
      ],
      "title": "Timeframe",
      "type": "string"
    },
    "TpTierSpec": {
      "additionalProperties": false,
      "description": "One tier in a multi-target take-profit ladder (VIB-1239).\n\npct = take-profit threshold (% from entry, ascending across tiers).\nsize_frac = fraction of the initial filled position closed at this tier.\nMulti-tier sum must equal 1.0; single-tier may carry size_frac<1.0 to\ncompose with attached trailing/SL exits.",
      "properties": {
        "pct": {
          "ge": 0.1,
          "le": 100.0,
          "title": "Pct",
          "type": "number"
        },
        "size_frac": {
          "gt": 0.0,
          "le": 1.0,
          "title": "Size Frac",
          "type": "number"
        }
      },
      "required": [
        "pct",
        "size_frac"
      ],
      "title": "TpTierSpec",
      "type": "object"
    },
    "TrackedValueRef": {
      "additionalProperties": false,
      "description": "Running highest or lowest value of ``source`` since the start of the run or since entry.\n\n``extreme`` chooses the maximum or the minimum and ``since`` the starting point.\nWith ``since: \"start\"`` (the default) it updates at the end of every bar, warm-up\nincluded, so a rule deciding at the close of bar t sees the extreme through bar t-1; a\nscheduled rule sees its frame bar included. With ``since: \"entry\"`` it is unset while\nflat, resets when a position opens from flat (not on a scale-in or a partial exit; a\nreversal opens from flat) and updates before decisions on each bar while invested, so a\nrule sees the extreme through the current bar. Absent values of ``source`` are skipped,\nand an unset value makes a comparison over it unknown. It keeps its state across a\npaper session's restarts. ``since: \"entry\"`` is refused in a signal scan, as every\noperation on the open position is.\n\nKnown gaps it inherits: a raw trailing ratchet built from it does not reset after it\nfires, ``since: \"entry\"`` folds in the whole bar of a mid-bar scheduled fill, and the\nreset runs only for the entry rule that filled. A ``since: \"entry\"`` value is refused\nanywhere inside a gate: a gate is evaluated while flat as well as invested, so a value\nthat resets at entry cannot be kept correct there. Use ``since: \"start\"``, or put the\ncondition on an exit. It returns the unit of ``source``.\n\nExample, the highest high since entry, to place a trailing exit under:\n  {\"kind\": \"tracked\", \"source\": {\"kind\": \"price\", \"field\": \"high\"}, \"extreme\": \"max\",\n   \"since\": \"entry\"}\ncompared with ``close`` using ``<``.",
      "properties": {
        "extreme": {
          "description": "'max' keeps the highest value of `source`, 'min' the lowest.",
          "enum": [
            "max",
            "min"
          ],
          "title": "Extreme",
          "type": "string"
        },
        "kind": {
          "const": "tracked",
          "default": "tracked",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "since": {
          "anyOf": [
            {
              "enum": [
                "start",
                "entry"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Where the extreme starts. 'start' (also when omitted) runs from the first delivered bar of the run. 'entry' is unset while flat, resets when a position opens from flat and runs while invested; it is refused in a signal scan and inside a gate (a gate is evaluated while flat too).",
          "title": "Since"
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Any value ref whose running extreme is kept, e.g. a price field, an indicator or an expression. It is evaluated every bar with its own resolution and symbol."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "source",
        "extreme"
      ],
      "title": "TrackedValueRef",
      "type": "object"
    },
    "TradeHistoryValueRef": {
      "additionalProperties": false,
      "description": "A statistic computed from the strategy's own closed-trade history.\n\n``field`` selects the metric: ``trade_count`` (closed trades so far),\n``consecutive_wins`` / ``consecutive_losses`` (current streak length),\n``last_pnl`` / ``last_pnl_pct`` (the most recently closed trade's PnL),\n``rolling_win_rate`` / ``rolling_avg_pnl_pct`` (win rate / average PnL%\nover the last ``lookback_bars`` closed trades \u2014 ``lookback_bars`` is REQUIRED for these\ntwo and forbidden for every other field, since it counts TRADES, not\nbars), or ``bars_since_last_exit``.\n\nBefore the first trade closes: ``trade_count``, ``consecutive_wins`` and\n``consecutive_losses`` are all-time counters and read ``0``. The other\nfive have no value yet -- a typed absence (``no_position``), not a number\n-- so a condition reading one is UNKNOWN and takes no action (neither\ntrue nor false), and a ``formula`` sizing over one is refused at compile\nbecause it could never size the first entry.\n\nVIB-1367 phase 4 / VIB-1319: the sub-metric selector is named ``field``\nacross every ValueRef-style type. The legacy name ``trade_metric`` was\nremoved.",
      "properties": {
        "field": {
          "enum": [
            "trade_count",
            "consecutive_wins",
            "consecutive_losses",
            "last_pnl",
            "last_pnl_pct",
            "rolling_win_rate",
            "rolling_avg_pnl_pct",
            "bars_since_last_exit"
          ],
          "title": "Field",
          "type": "string"
        },
        "kind": {
          "const": "trade_history",
          "default": "trade_history",
          "title": "Kind",
          "type": "string"
        },
        "lookback_bars": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Lookback Bars"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "field"
      ],
      "title": "TradeHistoryValueRef",
      "type": "object"
    },
    "TrailingStopCondition": {
      "additionalProperties": false,
      "description": "Trailing stop exit that follows the extreme price since entry.\n\nFires when price retraces from the highest high (long) or lowest low (short)\nsince entry by either atr_multiplier * ATR(atr_period) or trailing_pct percent \u2014 at\nmost one. Omit both to get the default atr_multiplier=2.0 over ATR(20). Use as the\nprimary exit for trend-following strategies.",
      "properties": {
        "activation_pct": {
          "anyOf": [
            {
              "ge": 0.1,
              "le": 100.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Minimum profit % before trailing stop activates. None = activate immediately on entry.",
          "title": "Activation Pct"
        },
        "atr_multiplier": {
          "anyOf": [
            {
              "ge": 0.5,
              "le": 10.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Atr Multiplier"
        },
        "atr_period": {
          "anyOf": [
            {
              "maximum": 500,
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Atr Period"
        },
        "kind": {
          "const": "trailing_stop",
          "default": "trailing_stop",
          "title": "Kind",
          "type": "string"
        },
        "trailing_pct": {
          "anyOf": [
            {
              "ge": 0.1,
              "le": 50.0,
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Trail distance as a percent of the extreme price since entry. The stop is live from the entry fill, at the fill price less this distance, then ratchets at each bar close. Mutually exclusive with atr_multiplier.",
          "title": "Trailing Pct"
        }
      },
      "title": "TrailingStopCondition",
      "type": "object"
    },
    "TransitionCondition": {
      "additionalProperties": false,
      "description": "Fires on the bar where ``source``'s value differs from the prior bar's value.\n\nOptionally constrained to a specific (from, to) pair. Intended for\ncategorical/discrete references such as ``supertrend.direction`` that\ntake a small set of values (e.g., -1 / +1).\n\nThe first bar ``source`` has a value is a baseline, not a change: the\ncondition fires on the first CHANGE it observes, never on bar 0 or on the\nbar a warming-up source first appears. A bar where ``source`` has no value is\nunknown (no action) and does not reset the last reading.\n\nExamples:\n    SuperTrend direction flip from bearish to bullish:\n        {\"kind\": \"transition\", \"source\": {...supertrend direction ref...},\n         \"from_value\": -1, \"to_value\": 1}\n\n    Any change in value:\n        {\"kind\": \"transition\", \"source\": {...}}",
      "properties": {
        "from_value": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Prior value constraint. None matches any prior value.",
          "title": "From Value"
        },
        "kind": {
          "const": "transition",
          "default": "transition",
          "title": "Kind",
          "type": "string"
        },
        "source": {
          "$ref": "#/$defs/ValueRef",
          "description": "Value to monitor for transitions"
        },
        "to_value": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Current value constraint. None matches any current value.",
          "title": "To Value"
        }
      },
      "required": [
        "source"
      ],
      "title": "TransitionCondition",
      "type": "object"
    },
    "TriggeringZoneValueRef": {
      "additionalProperties": false,
      "description": "Reference to the zone that fired the entry's ZoneCondition (VIB-591).\n\nResolves to a level of the *triggering* zone \u2014 the zone selected by the\nentry's ZoneCondition on the signal bar. An immutable snapshot is captured\ninto ``TradeContext`` so post-entry references (e.g. a standing stop-loss\nbound to the FVG's lower wick) keep resolving to the same zone for the\ntrade's lifetime. The id is retained for persisted-state compatibility.\n\nReturns NaN when no triggering zone is bound (no entry condition with a\nrequired ZoneCondition fired) and no compatibility lookup is available.",
      "properties": {
        "kind": {
          "const": "triggering_zone",
          "default": "triggering_zone",
          "title": "Kind",
          "type": "string"
        },
        "level": {
          "$ref": "#/$defs/ZoneLevel",
          "default": "mid"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "TriggeringZoneValueRef",
      "type": "object"
    },
    "UnaryExprValueRef": {
      "additionalProperties": false,
      "description": "Unary expression over another ValueRef.\n\nWraps a single operand with an op from {abs, neg, log, log10, sqrt, pct_change}.\nCompiles to a unary IRExpression.",
      "properties": {
        "kind": {
          "const": "unary_expr",
          "default": "unary_expr",
          "title": "Kind",
          "type": "string"
        },
        "op": {
          "enum": [
            "abs",
            "neg",
            "log",
            "log10",
            "sqrt",
            "pct_change"
          ],
          "title": "Op",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ]
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "op",
        "source"
      ],
      "title": "UnaryExprValueRef",
      "type": "object"
    },
    "VWAPBandOutput": {
      "enum": [
        "upper",
        "middle",
        "lower",
        "std_dev"
      ],
      "type": "string"
    },
    "VWAPBandsValueRef": {
      "additionalProperties": false,
      "description": "VWAP Bands \u2014 stddev envelopes around anchored VWAP.\n\nOutputs:\n  - upper  = VWAP + mult * sigma\n  - middle = VWAP\n  - lower  = VWAP - mult * sigma\n  - std_dev = sigma itself, NOT multiplied by ``multiplier``\n\nwhere sigma is the volume-weighted std dev of typical price vs VWAP,\naccumulated since the last anchor reset (session/week/month).\nA distance from the VWAP in standard deviations is\n``(close - middle) / std_dev``, written with ``binary_expr``.",
      "properties": {
        "anchor": {
          "$ref": "#/$defs/AnchorPeriod",
          "default": "session"
        },
        "indicator": {
          "const": "vwap_bands",
          "default": "vwap_bands",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "multiplier": {
          "default": 2.0,
          "ge": 0.1,
          "le": 10.0,
          "title": "Multiplier",
          "type": "number"
        },
        "output": {
          "$ref": "#/$defs/VWAPBandOutput"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "output"
      ],
      "title": "VWAPBandsValueRef",
      "type": "object"
    },
    "VWAPIndicatorValueRef": {
      "additionalProperties": false,
      "description": "VWAP \u2014 anchored Volume-Weighted Average Price.\n\nResets at each anchor boundary (session/week/month). Use this ValueRef\nwhen you need the VWAP scalar as an operand in compare/cross conditions\n(e.g. ``close > vwap(session)``). For envelope/band semantics use\n``vwap_bands`` instead.\n\nMath: anchored cumulative volume-weighted typical price; zero-volume\nbars carry forward the prior VWAP value (no vol=1.0 substitution). See\n``docs/indicator-conventions.md``.",
      "properties": {
        "anchor": {
          "$ref": "#/$defs/AnchorPeriod",
          "default": "session"
        },
        "indicator": {
          "const": "vwap",
          "default": "vwap",
          "title": "Indicator",
          "type": "string"
        },
        "kind": {
          "const": "indicator",
          "default": "indicator",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "VWAPIndicatorValueRef",
      "type": "object"
    },
    "ValueRef": {
      "discriminator": {
        "mapping": {
          "anchor": "#/$defs/AnchorValueRef",
          "binary_expr": "#/$defs/BinaryExprValueRef",
          "constant": "#/$defs/ConstantValueRef",
          "entry_bar": "#/$defs/EntryBarValueRef",
          "equity": "#/$defs/EquityValueRef",
          "event_distance": "#/$defs/EventDistanceValueRef",
          "event_field": "#/$defs/EventFieldValueRef",
          "indicator": "#/$defs/IndicatorValueRef",
          "live_position": "#/$defs/LivePositionValueRef",
          "named_zone": "#/$defs/NamedZoneValueRef",
          "percentile": "#/$defs/PercentileValueRef",
          "price": "#/$defs/PriceValueRef",
          "rank": "#/$defs/RankValueRef",
          "rolling_max": "#/$defs/RollingMaxValueRef",
          "rolling_min": "#/$defs/RollingMinValueRef",
          "rolling_stats": "#/$defs/RollingStatsValueRef",
          "sequence_step": "#/$defs/SequenceStepValueRef",
          "session": "#/$defs/SessionValueRef",
          "shared_signal": "#/$defs/SharedSignalValueRef",
          "sibling": "#/$defs/SiblingValueRef",
          "swing": "#/$defs/SwingValueRef",
          "time": "#/$defs/TimeValueRef",
          "tracked": "#/$defs/TrackedValueRef",
          "trade_history": "#/$defs/TradeHistoryValueRef",
          "triggering_zone": "#/$defs/TriggeringZoneValueRef",
          "unary_expr": "#/$defs/UnaryExprValueRef",
          "volume": "#/$defs/VolumeValueRef",
          "xs_dispersion": "#/$defs/DispersionValueRef",
          "xs_zscore": "#/$defs/ZScoreValueRef"
        },
        "propertyName": "kind"
      },
      "oneOf": [
        {
          "$ref": "#/$defs/PriceValueRef"
        },
        {
          "$ref": "#/$defs/IndicatorValueRef"
        },
        {
          "$ref": "#/$defs/ConstantValueRef"
        },
        {
          "$ref": "#/$defs/AnchorValueRef"
        },
        {
          "$ref": "#/$defs/NamedZoneValueRef"
        },
        {
          "$ref": "#/$defs/TriggeringZoneValueRef"
        },
        {
          "$ref": "#/$defs/SwingValueRef"
        },
        {
          "$ref": "#/$defs/PercentileValueRef"
        },
        {
          "$ref": "#/$defs/RollingMaxValueRef"
        },
        {
          "$ref": "#/$defs/RollingMinValueRef"
        },
        {
          "$ref": "#/$defs/VolumeValueRef"
        },
        {
          "$ref": "#/$defs/EventFieldValueRef"
        },
        {
          "$ref": "#/$defs/TradeHistoryValueRef"
        },
        {
          "$ref": "#/$defs/EquityValueRef"
        },
        {
          "$ref": "#/$defs/LivePositionValueRef"
        },
        {
          "$ref": "#/$defs/SessionValueRef"
        },
        {
          "$ref": "#/$defs/TimeValueRef"
        },
        {
          "$ref": "#/$defs/EntryBarValueRef"
        },
        {
          "$ref": "#/$defs/SequenceStepValueRef"
        },
        {
          "$ref": "#/$defs/RollingStatsValueRef"
        },
        {
          "$ref": "#/$defs/UnaryExprValueRef"
        },
        {
          "$ref": "#/$defs/BinaryExprValueRef"
        },
        {
          "$ref": "#/$defs/SharedSignalValueRef"
        },
        {
          "$ref": "#/$defs/EventDistanceValueRef"
        },
        {
          "$ref": "#/$defs/RankValueRef"
        },
        {
          "$ref": "#/$defs/ZScoreValueRef"
        },
        {
          "$ref": "#/$defs/DispersionValueRef"
        },
        {
          "$ref": "#/$defs/SiblingValueRef"
        },
        {
          "$ref": "#/$defs/TrackedValueRef"
        }
      ],
      "required": [
        "kind"
      ]
    },
    "VolumeValueRef": {
      "additionalProperties": false,
      "description": "Traded volume of the current bar, or of a bar `offset` bars back.\n\nVolume is in **base-asset units** (shares, or coins for crypto) unless the\nrun is configured for quote units, in which case it is multiplied by that\nbar's close. Reading past the start of loaded history returns NaN.",
      "properties": {
        "kind": {
          "const": "volume",
          "default": "volume",
          "title": "Kind",
          "type": "string"
        },
        "offset": {
          "default": 0,
          "maximum": 500,
          "minimum": 0,
          "title": "Offset",
          "type": "integer"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "title": "VolumeValueRef",
      "type": "object"
    },
    "Weekday": {
      "enum": [
        "monday",
        "tuesday",
        "wednesday",
        "thursday",
        "friday",
        "saturday",
        "sunday"
      ],
      "type": "string"
    },
    "ZScoreValueRef": {
      "additionalProperties": false,
      "description": "Cross-sectional z-score of ``source`` among the universe's symbols on the current bar.\n\nHow many sample standard deviations this symbol's value sits from the universe\nmean. With fewer than 2 values, or when every symbol sits on the mean, there is no\nz-score: it is absent (not 0). ``source`` is evaluated once per symbol; it cannot\ncarry a ``symbol`` and cannot contain another cross-sectional operand. Requires a\nuniverse or portfolio strategy with two or more symbols.\n\nExample, how far this symbol's RSI is from the universe average:\n  {\"kind\": \"xs_zscore\", \"source\": {\"kind\": \"indicator\", \"indicator\": \"rsi\", \"period\": 14}}",
      "properties": {
        "kind": {
          "const": "xs_zscore",
          "default": "xs_zscore",
          "title": "Kind",
          "type": "string"
        },
        "resolution": {
          "anyOf": [
            {
              "$ref": "#/$defs/Timeframe"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional higher-timeframe override for this value ref. Set it inside left/right refs such as {'kind': 'indicator', ..., 'resolution': '4h'} or {'kind': 'price', 'field': 'close', 'resolution': '4h'}. Do not put timeframe/source_timeframe on the parent condition. None = strategy timeframe."
        },
        "source": {
          "anyOf": [
            {
              "$ref": "#/$defs/ValueRef"
            },
            {
              "type": "number"
            }
          ],
          "description": "Per-symbol value to standardise against the universe on the current bar. It is evaluated for each symbol on its own data."
        },
        "symbol": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "description": "Optional reference symbol this value ref reads from (e.g. 'SPY'). None = the traded/primary symbol. Mutually exclusive with resolution.",
          "title": "Symbol"
        }
      },
      "required": [
        "source"
      ],
      "title": "ZScoreValueRef",
      "type": "object"
    },
    "ZoneLevel": {
      "enum": [
        "upper",
        "lower",
        "mid"
      ],
      "type": "string"
    },
    "ZoneSelection": {
      "enum": [
        "nearest",
        "most_recent",
        "oldest",
        "any"
      ],
      "type": "string"
    }
  },
  "$ref": "#/$defs/AnyOfCondition"
}
