{"asyncapi":"3.0.0","info":{"title":"Whales Bet Signal Stream (WebSocket)","version":"1.1.0","description":"Real-time signal events. Connect, optionally send a subscribe message, then receive a stream of typed events. Wraps the same events.jsonl log exposed at GET /api/history."},"servers":{"production":{"host":"signals.whales.bet","pathname":"/ws","protocol":"wss","description":"TLS-encrypted public WebSocket"}},"channels":{"signals":{"address":"/ws","messages":{"hello":{"$ref":"#/components/messages/Hello"},"subscribed":{"$ref":"#/components/messages/Subscribed"},"pong":{"$ref":"#/components/messages/Pong"},"signal_published":{"$ref":"#/components/messages/SignalPublished"},"trade_executed":{"$ref":"#/components/messages/TradeExecuted"},"trade_failed":{"$ref":"#/components/messages/TradeFailed"},"stop_loss_alert":{"$ref":"#/components/messages/StopLossAlert"},"position_resolved":{"$ref":"#/components/messages/PositionResolved"},"forecast_update":{"$ref":"#/components/messages/ForecastUpdate"},"cycle_complete":{"$ref":"#/components/messages/CycleComplete"},"subscribe_request":{"$ref":"#/components/messages/SubscribeRequest"},"ping_request":{"$ref":"#/components/messages/PingRequest"}}}},"operations":{"receiveEvents":{"action":"receive","channel":{"$ref":"#/channels/signals"},"summary":"Stream of bot events","messages":[{"$ref":"#/channels/signals/messages/hello"},{"$ref":"#/channels/signals/messages/subscribed"},{"$ref":"#/channels/signals/messages/pong"},{"$ref":"#/channels/signals/messages/signal_published"},{"$ref":"#/channels/signals/messages/trade_executed"},{"$ref":"#/channels/signals/messages/trade_failed"},{"$ref":"#/channels/signals/messages/stop_loss_alert"},{"$ref":"#/channels/signals/messages/position_resolved"},{"$ref":"#/channels/signals/messages/forecast_update"},{"$ref":"#/channels/signals/messages/cycle_complete"}]},"sendClientMessages":{"action":"send","channel":{"$ref":"#/channels/signals"},"summary":"Client → server messages","messages":[{"$ref":"#/channels/signals/messages/subscribe_request"},{"$ref":"#/channels/signals/messages/ping_request"}]}},"components":{"messages":{"Hello":{"name":"hello","summary":"Greeting sent immediately after connect, includes current stats","payload":{"$ref":"#/components/schemas/HelloPayload"}},"Subscribed":{"name":"subscribed","summary":"Confirmation of a subscribe request","payload":{"$ref":"#/components/schemas/SubscribedPayload"}},"Pong":{"name":"pong","summary":"Response to client ping","payload":{"$ref":"#/components/schemas/PongPayload"}},"SignalPublished":{"name":"signal_published","summary":"Signal approved by quality + AI gates. Fires BEFORE the bot's local execution attempt — clients receive every signal the strategy endorses, regardless of whether the bot itself has balance/exposure room to take it. A later trade_executed or trade_failed event with the same token_id tells you whether the bot followed through.","payload":{"$ref":"#/components/schemas/SignalPublishedEvent"}},"TradeExecuted":{"name":"trade_executed","summary":"CLOB accepted the order","payload":{"$ref":"#/components/schemas/TradeExecutedEvent"}},"TradeFailed":{"name":"trade_failed","summary":"Order rejected (slippage, no liquidity, etc.)","payload":{"$ref":"#/components/schemas/TradeFailedEvent"}},"StopLossAlert":{"name":"stop_loss_alert","summary":"An open position has dropped past the stop-loss threshold with less than min_hours_to_resolution left. The bot also attempts an auto-sell against the best bid; the result is embedded in payload.bot_sell_attempt. Integrators should use this signal to exit their own mirrored positions — the alert fires before the bot's sell goes out, so you can race ahead of our own exit order if you choose.","payload":{"$ref":"#/components/schemas/StopLossAlertEvent"}},"PositionResolved":{"name":"position_resolved","summary":"Market closed — P&L realized","payload":{"$ref":"#/components/schemas/PositionResolvedEvent"}},"ForecastUpdate":{"name":"forecast_update","summary":"New multi-day ensemble forecast snapshot","payload":{"$ref":"#/components/schemas/ForecastUpdateEvent"}},"CycleComplete":{"name":"cycle_complete","summary":"End-of-cycle heartbeat with counters","payload":{"$ref":"#/components/schemas/CycleCompleteEvent"}},"SubscribeRequest":{"name":"subscribe","summary":"Client requests to filter event types","payload":{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"subscribe"},"topics":{"type":"array","items":{"type":"string"},"description":"Event types to receive, or ['*'] for all","examples":[["signal_published","position_resolved"],["*"]]}}}},"PingRequest":{"name":"ping","summary":"Client heartbeat","payload":{"type":"object","required":["type"],"properties":{"type":{"type":"string","const":"ping"}}}}},"schemas":{"EnvelopeBase":{"type":"object","required":["ts","seq","type","payload"],"properties":{"ts":{"type":"string","format":"date-time"},"seq":{"type":"integer","description":"Monotonic event sequence"},"type":{"type":"string"},"payload":{"type":"object"}}},"HelloPayload":{"type":"object","properties":{"type":{"type":"string","const":"hello"},"ts":{"type":"string","format":"date-time"},"stats":{"type":"object","description":"Snapshot from GET /api/stats"}}},"SubscribedPayload":{"type":"object","properties":{"type":{"type":"string","const":"subscribed"},"topics":{"type":"array","items":{"type":"string"}}}},"PongPayload":{"type":"object","properties":{"type":{"type":"string","const":"pong"},"ts":{"type":"number"}}},"SignalPublishedEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"signal_published"},"payload":{"type":"object","properties":{"side":{"type":"string","enum":["BUY","SELL"]},"target_token":{"type":"string","enum":["YES","NO"]},"price":{"type":"number","minimum":0,"maximum":1},"edge_pct":{"type":"number"},"size_usd":{"type":"number"},"city":{"type":"string"},"market_type":{"type":"string"},"market_question":{"type":"string"},"bucket":{"type":"string"},"token_id":{"type":"string"},"condition_id":{"type":"string"},"neg_risk":{"type":"boolean"},"target_date":{"type":"string","format":"date"},"horizon_days":{"type":"integer"}}}}}]},"TradeExecutedEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"trade_executed"},"payload":{"type":"object","properties":{"order_id":{"type":"string"},"status":{"type":"string"},"side":{"type":"string"},"target_token":{"type":"string"},"price":{"type":"number"},"size_usd":{"type":"number"},"shares":{"type":"number"},"city":{"type":"string"},"market_type":{"type":"string"},"token_id":{"type":"string"},"condition_id":{"type":"string"}}}}}]},"TradeFailedEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"trade_failed"},"payload":{"type":"object","properties":{"reason":{"type":"string"},"status":{"type":"string"},"side":{"type":"string"},"target_token":{"type":"string"},"price":{"type":"number"},"bucket":{"type":"string"},"token_id":{"type":"string"}}}}}]},"StopLossAlertEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"stop_loss_alert"},"payload":{"type":"object","required":["urgency","market_id","token_id","entry_price","current_price","price_drop_pct","shares"],"properties":{"urgency":{"type":"string","enum":["STOP_LOSS"]},"position_key":{"type":"string"},"market_id":{"type":"string"},"token_id":{"type":"string","description":"ERC1155 token id of the position to sell"},"condition_id":{"type":"string"},"city":{"type":"string"},"market_type":{"type":"string","enum":["temperature","precipitation","binary_weather"]},"bucket":{"type":"string"},"side":{"type":"string","enum":["BUY","SELL"]},"target_token":{"type":"string","enum":["YES","NO"]},"neg_risk":{"type":"boolean","description":"If true, exit order must be signed against NegRiskCTFExchange"},"entry_price":{"type":"number","minimum":0,"maximum":1},"current_price":{"type":"number","minimum":0,"maximum":1},"price_drop_pct":{"type":"number","description":"Percent drop from entry to current"},"shares":{"type":"number","description":"Number of held shares — also the exit-order size"},"hours_to_resolution":{"type":["number","null"]},"cost_usd":{"type":"number"},"current_value_usd":{"type":"number"},"unrealized_pnl_usd":{"type":"number"},"bot_sell_attempt":{"type":["object","null"],"description":"Result of the bot's own auto-sell attempt. Null if absent.","properties":{"status":{"type":"string","description":"SUBMITTED, FAILED_NO_LIQUIDITY, TEST_MODE, FAILED, ..."},"order_id":{"type":["string","null"]},"limit_price":{"type":"number"},"shares":{"type":"number"},"reason":{"type":"string"}}}}}}}]},"PositionResolvedEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"position_resolved"},"payload":{"type":"object","properties":{"order_id":{"type":"string"},"condition_id":{"type":"string"},"won":{"type":"boolean"},"pnl_usd":{"type":"number"},"cost_usd":{"type":"number"},"payout_usd":{"type":"number"},"side":{"type":"string"},"target_token":{"type":"string"},"winning_outcome":{"type":["string","null"],"description":"Null when closed_early=true (the market hadn't resolved yet)"},"city":{"type":"string"},"market_type":{"type":"string"},"bucket":{"type":"string"},"shares":{"type":"number"},"closed_early":{"type":"boolean","description":"True when the position was exited before resolution via stop-loss"},"sell_price":{"type":["number","null"],"description":"Limit price of the SELL order. Populated only when closed_early=true"},"sold_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the early sale. Populated only when closed_early=true"},"sell_urgency":{"type":["string","null"],"description":"Reason for the early sale (e.g. STOP_LOSS). Populated only when closed_early=true"},"sell_order_id":{"type":["string","null"],"description":"CLOB order id of the sell. Populated only when closed_early=true"}}}}}]},"ForecastUpdateEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"forecast_update"},"payload":{"type":"object","properties":{"by_city":{"type":"object","additionalProperties":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"temp_max_mean_c":{"type":"number"},"temp_max_std_c":{"type":"number"},"temp_min_mean_c":{"type":"number"},"temp_min_std_c":{"type":"number"},"precip_prob_mean":{"type":"number"},"precip_prob_std":{"type":"number"},"sources_used":{"type":"array","items":{"type":"string"}},"n_sources":{"type":"integer"}}}}}}}}}]},"CycleCompleteEvent":{"allOf":[{"$ref":"#/components/schemas/EnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"cycle_complete"},"payload":{"type":"object","properties":{"results":{"type":"integer"},"markets":{"type":"integer"},"raw_signals":{"type":"integer"},"new_trades":{"type":"integer"},"skipped_dup":{"type":"integer"},"skipped_exposure":{"type":"integer"},"skipped_quality":{"type":"integer"},"failed_orders":{"type":"integer"},"failed_breakdown":{"type":"object"}}}}}]}}}}