changedOutput schema / (root)
Previous value: -nullNew value: +{
+ "properties": {
+ "checked_at": {
+ "description": "When we produced this answer (ISO 8601, UTC). The only ISO timestamp in the payload; every other timestamp is unix seconds.",
+ "format": "date-time",
+ "type": "string"
+ },
+ "current_price": {
+ "description": "Last observed price of the coin upstream; null when no usable price was available (then price_vs_entry_zone is UNKNOWN).",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "data_quality": {
+ "description": "Quality of the market data behind this validation: GOOD = fresh and complete, DEGRADED = usable but partial or wide/uncertain, STALE = too old to rely on (treat the price-derived fields with caution).",
+ "enum": [
+ "GOOD",
+ "DEGRADED",
+ "STALE"
+ ],
+ "type": "string"
+ },
+ "disclaimer": {
+ "description": "Our product boundary, verbatim in every paid advice response: market intelligence only, no execution, no custody, no position sizing, and opportunity_score is a heuristic rather than a calibrated win probability.",
+ "type": "string"
+ },
+ "economics": {
+ "description": "Estimated round-trip economics in percent of position value (1.8 = 1.8%). Taker = crossing the spread for an immediate fill; maker = a cheaper passive limit order that may never fill. Values the source cannot compute are null (e.g. for WATCH/NEUTRAL/AVOID, which have no concrete target).",
+ "properties": {
+ "estimated_maker_cost_pct": {
+ "description": "Estimated round-trip cost in percent assuming passive maker fills on both sides.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "estimated_net_maker_pct": {
+ "description": "gross_target_pct minus estimated_maker_cost_pct — only realistic if your passive order actually fills.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "estimated_net_taker_pct": {
+ "description": "gross_target_pct minus estimated_taker_cost_pct — the edge that survives immediate execution. Can be negative.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "estimated_taker_cost_pct": {
+ "description": "Estimated round-trip cost in percent when entering and exiting as taker (fees plus expected spread/slippage).",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "gross_target_pct": {
+ "description": "Gross move from entry to take_profit in percent, before costs; null without a concrete setup.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "maker_fill_not_guaranteed": {
+ "description": "Always true: the maker figures assume an unguaranteed passive fill, so never plan on them as if the fill were certain.",
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "entry_valid_until": {
+ "description": "Unix seconds — end of the 30-minute new-entry window (not a position deadline; the hold horizon lives on the advice as expected_hold_until).",
+ "type": "integer"
+ },
+ "entry_window_remaining_seconds": {
+ "description": "Seconds left in that entry window; 0 or below means it closed and status is no longer ACTIVE.",
+ "type": "integer"
+ },
+ "execution": {
+ "description": "Internal eligibility metadata of the UPSTREAM system — informational only, never a permission or prohibition for you. Upstream may block a candidate for its own reasons while the opportunity is perfectly publishable.",
+ "properties": {
+ "allowed": {
+ "description": "Whether the upstream system would trade this itself right now. Says nothing about whether you should.",
+ "type": "boolean"
+ },
+ "blocked_by": {
+ "description": "snake_case blocker tokens behind allowed=false, e.g. ml_1h_below_threshold, net_edge_below_cost_floor, intraday_trading_disabled, max_positions_reached, not_promoted_for_live, cooldown_active. Empty when allowed is true.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "execution_blockers_changed": {
+ "description": "True when the upstream system's internal execution blockers differ from those published with the advice; informational only, never a permission.",
+ "type": "boolean"
+ },
+ "invalidation": {
+ "description": "Whether the advice's own invalidation conditions have triggered.",
+ "properties": {
+ "invalidated": {
+ "description": "True when at least one published invalidation condition has been met; the setup is off.",
+ "type": "boolean"
+ },
+ "reasons": {
+ "description": "Reasons behind invalidated=true; empty array when not invalidated.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "next_check_after": {
+ "description": "Only present when status is not ACTIVE — unix seconds before which polling again is pointless (now + 1800).",
+ "type": "integer"
+ },
+ "price_as_of": {
+ "description": "Unix seconds — when current_price was observed.",
+ "type": "integer"
+ },
+ "price_vs_entry_zone": {
+ "description": "Where current_price sits relative to the advised entry zone: BELOW entry_zone.min, INSIDE the zone, ABOVE entry_zone.max (chasing), or UNKNOWN when there is no price or no concrete entry zone.",
+ "enum": [
+ "BELOW",
+ "INSIDE",
+ "ABOVE",
+ "UNKNOWN"
+ ],
+ "type": "string"
+ },
+ "publication_status": {
+ "description": "Publication state of the advice. PUBLISHED = live and current, STALE = retrievable history whose entry window closed, INVALIDATED = an invalidation condition triggered. Expired ids stay retrievable with their real status.",
+ "enum": [
+ "PUBLISHED",
+ "STALE",
+ "INVALIDATED"
+ ],
+ "type": "string"
+ },
+ "query": {
+ "description": "The validated input echoed back, i.e. the signal_id you asked for.",
+ "properties": {
+ "signal_id": {
+ "description": "Stable id of the opportunity across versions; the input for every other call in the chain.",
+ "pattern": "^sig_[0-9a-f]{12}$",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "recommended_next_call": {
+ "description": "Machine-readable next step in the chain (scan → signal → validate → evidence → check), so you can follow the workflow without documentation. null means there is explicitly nothing useful to call now; next_check_after then says when to come back.",
+ "properties": {
+ "arguments": {
+ "description": "Arguments to pass verbatim, e.g. {\"signal_id\":\"sig_a1b2c3d4e5f6\"}, plus known_version for check_crypto_signal.",
+ "type": "object"
+ },
+ "reason": {
+ "description": "Why this is the useful next step, in plain language.",
+ "type": "string"
+ },
+ "tool": {
+ "description": "Name of the MCP tool to call next.",
+ "enum": [
+ "get_crypto_signal",
+ "validate_crypto_signal",
+ "get_crypto_evidence",
+ "check_crypto_signal"
+ ],
+ "type": "string"
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "signal_id": {
+ "description": "Stable id of the opportunity across versions; the input for every other call in the chain.",
+ "pattern": "^sig_[0-9a-f]{12}$",
+ "type": "string"
+ },
+ "source_as_of": {
+ "description": "Unix seconds — freshness of the upstream advice system's answer.",
+ "type": "integer"
+ },
+ "source_disclaimer": {
+ "description": "Verbatim disclaimer of the upstream advice system, passed through unedited.",
+ "type": "string"
+ },
+ "spread_bps": {
+ "description": "Observed bid/ask spread in basis points; null when not measurable. A wide spread eats the net edge in economics.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "status": {
+ "description": "Live status at call time. ACTIVE = published AND entry window still open, STALE = entry window closed (the hold horizon may still run), INVALIDATED = an invalidation condition triggered. Only ACTIVE is a current new-entry advice.",
+ "enum": [
+ "ACTIVE",
+ "STALE",
+ "INVALIDATED"
+ ],
+ "type": "string"
+ },
+ "version": {
+ "description": "Version of the advice this validation describes.",
+ "type": "integer"
+ }
+ },
+ "required": [
+ "query",
+ "checked_at",
+ "source_as_of",
+ "signal_id",
+ "status",
+ "current_price",
+ "spread_bps",
+ "recommended_next_call",
+ "disclaimer"
+ ],
+ "type": "object"
+}