changedOutput schema / (root)
Previous value: -nullNew value: +{
+ "properties": {
+ "advice": {
+ "description": "The full published advice, passed through 1:1 from the upstream system. entry_zone, stop_loss, take_profit and reward_risk are null for WATCH/NEUTRAL/AVOID, because those classifications carry no concrete entry setup. Buying such a signal is still a valid $0.01 call and a complete answer: you pay for the assessment (classification, score, factors, conditions, economics, ML), not for the presence of a setup.",
+ "properties": {
+ "as_of": {
+ "description": "Unix seconds — market state this advice was computed on.",
+ "type": "integer"
+ },
+ "classification": {
+ "description": "Spot-long classification derived from opportunity_score (shorts are never advised). Bands: 80-100 STRONG_LONG, 65-79 LONG, 50-64 CONDITIONAL_LONG, 35-49 SPECULATIVE_LONG (concrete entry setup) or WATCH (no concrete setup, so entry_zone/stop_loss/take_profit are null), below 35 AVOID (actively negative signals) or NEUTRAL. EXIT is a fresh strong exit condition. Candidates with internal execution blockers are capped upstream at CONDITIONAL_LONG.",
+ "enum": [
+ "STRONG_LONG",
+ "LONG",
+ "CONDITIONAL_LONG",
+ "SPECULATIVE_LONG",
+ "WATCH",
+ "NEUTRAL",
+ "AVOID",
+ "EXIT"
+ ],
+ "type": "string"
+ },
+ "coin": {
+ "description": "Product id of the market, e.g. 'BTC-EUR'.",
+ "type": "string"
+ },
+ "conditions": {
+ "description": "Conditions under which the setup holds or strengthens, e.g. 'price holds above 58100'.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "disclaimer": {
+ "description": "Verbatim upstream disclaimer; repeated as source_disclaimer at envelope level.",
+ "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 window in which this is a current NEW-entry advice. Not a position deadline; past it the signal is honestly reported STALE.",
+ "type": "integer"
+ },
+ "entry_zone": {
+ "description": "Price range in which entering is advised; null for WATCH/NEUTRAL/AVOID (no concrete setup).",
+ "properties": {
+ "max": {
+ "description": "Upper bound; above it you would be chasing (price_vs_entry_zone ABOVE).",
+ "type": "number"
+ },
+ "min": {
+ "description": "Lower bound of the advised entry range, in the quote currency.",
+ "type": "number"
+ }
+ },
+ "type": [
+ "object",
+ "null"
+ ]
+ },
+ "estimated_win_probability": {
+ "description": "Always null until a calibrated probability model exists — we deliberately do not dress opportunity_score up as a probability. Use get_crypto_evidence for the measured win rate.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "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"
+ },
+ "expected_hold_until": {
+ "description": "Unix seconds — expected holding horizon of the position, always later than entry_valid_until. The entry window can be closed while this horizon still runs.",
+ "type": "integer"
+ },
+ "invalidation_conditions": {
+ "description": "Conditions that kill the setup; when one triggers, validate_crypto_signal reports invalidation.invalidated=true.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "ml": {
+ "description": "Machine-learning support behind the advice, per prediction horizon.",
+ "properties": {
+ "horizons": {
+ "description": "One entry per horizon; an empty array means no ML input was available.",
+ "items": {
+ "properties": {
+ "evidence_status": {
+ "description": "How far out-of-sample validation has come. UNVALIDATED = no measured outcomes yet, EARLY_EVIDENCE = too few to lean on, VALIDATED = enough measured evidence, REJECTED = measured and found not to work.",
+ "enum": [
+ "UNVALIDATED",
+ "EARLY_EVIDENCE",
+ "VALIDATED",
+ "REJECTED"
+ ],
+ "type": "string"
+ },
+ "horizon_seconds": {
+ "description": "Prediction horizon in seconds, e.g. 3600 for the 1-hour model.",
+ "type": "integer"
+ },
+ "model_version": {
+ "description": "Identifier of the model, e.g. 'gbdt-intraday-v1-1h'.",
+ "type": "string"
+ },
+ "sample_count": {
+ "description": "Measured outcomes behind this evidence_status; 0 means unproven.",
+ "type": "integer"
+ },
+ "score": {
+ "description": "Model output for this horizon (0-1); null when the model produced nothing usable.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "threshold": {
+ "description": "Value the score must exceed to count as supportive; below it is a common upstream execution blocker.",
+ "type": [
+ "number",
+ "null"
+ ]
+ }
+ },
+ "type": "object"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "negative_factors": {
+ "description": "Arguments against the setup; always read these alongside the score. May be empty.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "opportunity_score": {
+ "description": "0-100 heuristic ranking of setup quality (trend alignment, EMA separation, momentum, volume, breakout/pullback strength, efficiency ratio, regime direction, ML horizon scores, estimated net edge, data/spread quality). NOT a calibrated win probability; the measured win rate lives in get_crypto_evidence.",
+ "maximum": 100,
+ "minimum": 0,
+ "type": "integer"
+ },
+ "opportunity_type": {
+ "description": "Setup shape: PULLBACK enters on a retracement inside an existing trend, BREAKOUT enters on a move through resistance.",
+ "enum": [
+ "PULLBACK",
+ "BREAKOUT"
+ ],
+ "type": "string"
+ },
+ "positive_factors": {
+ "description": "Arguments in favour of the setup, in plain language. May be empty.",
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ },
+ "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"
+ },
+ "published_at": {
+ "description": "Unix seconds — when this version was published.",
+ "type": "integer"
+ },
+ "reward_risk": {
+ "description": "Distance to take_profit over distance to stop_loss, before costs; null without a concrete setup.",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "score_type": {
+ "description": "Which scheme produced opportunity_score. 'heuristic_v1' is a deterministic, unit-tested heuristic and explicitly signals that the score is not a calibrated probability. Scores are only comparable within one score_type: a 70 of heuristic_v1 says nothing about a 70 of a future heuristic_v2, so any ranking, threshold or history you build resets when this value changes.",
+ "enum": [
+ "heuristic_v1"
+ ],
+ "type": "string"
+ },
+ "setup_timeframe": {
+ "description": "Candle timeframe the setup was found on, e.g. '15m'; null when not applicable.",
+ "type": [
+ "string",
+ "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": {
+ "additionalProperties": true,
+ "description": "Provenance for auditing — strategy_version, analyst_model/analyst_prompt_version, critic_model/critic_prompt_version, regime_decision_id (may be null) and snapshot_id. Informational and extensible.",
+ "type": "object"
+ },
+ "stop_loss": {
+ "description": "Advised invalidation price; null for WATCH/NEUTRAL/AVOID (no concrete setup).",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "strategy": {
+ "description": "Strategy version that produced this advice, e.g. 'momentum-breakout-v1'; the scope of get_crypto_evidence.",
+ "type": "string"
+ },
+ "take_profit": {
+ "description": "Advised target price; null for WATCH/NEUTRAL/AVOID (no concrete setup).",
+ "type": [
+ "number",
+ "null"
+ ]
+ },
+ "updated_at": {
+ "description": "Unix seconds — when this advice was last updated upstream.",
+ "type": "integer"
+ },
+ "version": {
+ "description": "Version of this advice; increments on every material change (see change_types on check_crypto_signal).",
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "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"
+ },
+ "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"
+ },
+ "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"
+ ]
+ },
+ "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"
+ }
+ },
+ "required": [
+ "query",
+ "checked_at",
+ "source_as_of",
+ "advice",
+ "recommended_next_call",
+ "disclaimer"
+ ],
+ "type": "object"
+}