{"components":{"schemas":{"AccountResponse":{"additionalProperties":true,"description":"MT5 account_info() fields.","properties":{"balance":{"description":"Raw balance in account currency units.","example":500000.0,"type":"number"},"currency":{"description":"Account currency. `USC` indicates a cent account.","example":"USC","type":"string"},"equity":{"example":499850.0,"type":"number"},"leverage":{"example":500,"type":"integer"},"login":{"example":123456789,"type":"integer"},"margin":{"example":150.0,"type":"number"},"margin_free":{"example":499700.0,"type":"number"},"margin_level":{"description":"Margin level as a percentage.","example":333200.0,"type":"number"},"name":{"example":"NOVOSKY Bot","type":"string"},"profit":{"example":-150.0,"type":"number"},"server":{"example":"RoboForex-Pro","type":"string"},"trade_allowed":{"example":true,"type":"boolean"},"trade_expert":{"example":true,"type":"boolean"}},"type":"object"},"DealResponse":{"additionalProperties":true,"description":"A closed deal from trade history.","properties":{"comment":{"example":"novosky","type":"string"},"commission":{"example":-0.05,"type":"number"},"entry":{"description":"0=entry (open), 1=exit (close), 2=reversal.","example":1,"type":"integer"},"magic":{"example":20250101,"type":"integer"},"order":{"description":"Linked order ticket.","example":123456789,"type":"integer"},"position_id":{"example":123456789,"type":"integer"},"price":{"example":95200.0,"type":"number"},"profit":{"description":"Profit in raw account currency (USC for cent accounts \u2014 NOT converted to USD).","example":200.0,"type":"number"},"swap":{"example":-0.12,"type":"number"},"symbol":{"example":"BTCUSD","type":"string"},"ticket":{"example":111111111,"type":"integer"},"time":{"description":"Deal execution time as Unix epoch.","example":1777879100,"type":"integer"},"type":{"description":"0=BUY, 1=SELL, 2=BALANCE, 3=CREDIT.","example":1,"type":"integer"},"volume":{"example":0.01,"type":"number"}},"type":"object"},"ErrorResponse":{"additionalProperties":true,"description":"MT5 error payload returned on non-2xx responses.","properties":{"message":{"example":"Invalid stops","type":"string"},"retcode":{"description":"MT5 error code (e.g. 10014 = invalid stops, 10018 = market closed).","example":10014,"type":"integer"}},"type":"object"},"OpenRequest":{"additionalProperties":true,"description":"Request body for placing a new order.","properties":{"comment":{"example":"novosky","type":"string"},"deviation":{"description":"Max allowed price deviation in points.","example":20,"type":"integer"},"expiration":{"description":"Expiry time as Unix epoch (for SPECIFIED type_time).","type":"integer"},"magic":{"description":"EA magic number for trade identification.","example":20250101,"type":"integer"},"price":{"description":"Order price (required for limit/stop orders).","example":95000.0,"type":"number"},"sl":{"description":"Stop loss price.","example":94800.0,"type":"number"},"stoplimit":{"description":"Limit price for BUY_STOP_LIMIT / SELL_STOP_LIMIT orders.","type":"number"},"symbol":{"example":"BTCUSD","type":"string"},"tp":{"description":"Take profit price.","example":95300.0,"type":"number"},"type":{"description":"Order type. The bot only uses BUY and SELL; the rest are available for manual use.","enum":["BUY","SELL","BUY_LIMIT","SELL_LIMIT","BUY_STOP","SELL_STOP","BUY_STOP_LIMIT","SELL_STOP_LIMIT"],"example":"BUY","type":"string"},"type_time":{"description":"Order expiry type: GTC, TODAY, SPECIFIED, SPECIFIED_DAY.","example":"GTC","type":"string"},"volume":{"description":"Lot size.","example":0.01,"type":"number"}},"required":["symbol","type","volume"],"type":"object"},"OrderResponse":{"additionalProperties":true,"description":"A pending order.","properties":{"comment":{"example":"novosky","type":"string"},"magic":{"example":20250101,"type":"integer"},"price_open":{"example":94500.0,"type":"number"},"sl":{"example":94300.0,"type":"number"},"symbol":{"example":"BTCUSD","type":"string"},"ticket":{"example":987654321,"type":"integer"},"time_setup":{"description":"Order creation time as Unix epoch.","example":1777879028,"type":"integer"},"tp":{"example":94800.0,"type":"number"},"type":{"description":"2=BUY_LIMIT, 3=SELL_LIMIT, 4=BUY_STOP, 5=SELL_STOP, 6=BUY_STOP_LIMIT, 7=SELL_STOP_LIMIT.","example":2,"type":"integer"},"volume_current":{"example":0.01,"type":"number"}},"type":"object"},"OrderUpdateRequest":{"additionalProperties":true,"description":"Fields to modify on a pending order.","properties":{"price":{"description":"New order price.","example":94600.0,"type":"number"},"sl":{"description":"New stop loss price.","example":94400.0,"type":"number"},"tp":{"description":"New take profit price.","example":94900.0,"type":"number"},"type_time":{"description":"New expiry type.","example":"GTC","type":"string"}},"type":"object"},"PingBrokerResponse":{"additionalProperties":true,"description":"Live broker connection quality metrics.","properties":{"connected":{"example":true,"type":"boolean"},"ping_ms":{"description":"Round-trip latency in milliseconds.","example":12.4,"type":"number"},"ping_us":{"description":"Round-trip latency in microseconds.","example":12400,"type":"integer"},"quality":{"description":"Human-readable quality label: excellent (<80 ms) / good (<200 ms) / fair (<500 ms) / poor (>=500 ms).","enum":["excellent","good","fair","poor"],"example":"excellent","type":"string"},"retransmission":{"description":"TCP packet retransmission rate (0.0 = perfect). Source: mt5.terminal_info().retransmission.","example":0.0,"type":"number"}},"type":"object"},"PositionCloseRequest":{"additionalProperties":false,"description":"Optional body for partial close. Omit entirely to close the full position.","properties":{"deviation":{"description":"Max allowed price deviation in points.","example":20,"type":"integer"},"volume":{"description":"Lot size to close. Omit to close the full position.","example":0.005,"type":"number"}},"type":"object"},"PositionResponse":{"additionalProperties":true,"description":"An open position.","properties":{"comment":{"example":"novosky","type":"string"},"magic":{"description":"EA magic number.","example":20250101,"type":"integer"},"price_current":{"example":95050.0,"type":"number"},"price_open":{"example":95000.0,"type":"number"},"profit":{"description":"Floating profit in account currency.","example":5.0,"type":"number"},"sl":{"example":94800.0,"type":"number"},"swap":{"example":-0.12,"type":"number"},"symbol":{"example":"BTCUSD","type":"string"},"ticket":{"example":123456789,"type":"integer"},"time":{"description":"Position open time as Unix epoch.","example":1777879028,"type":"integer"},"tp":{"example":95300.0,"type":"number"},"type":{"description":"0 = BUY, 1 = SELL.","example":0,"type":"integer"},"volume":{"example":0.01,"type":"number"}},"type":"object"},"PositionUpdateRequest":{"additionalProperties":false,"description":"Partial update for stop loss and/or take profit.","properties":{"sl":{"description":"New stop loss price. Omit to leave unchanged.","example":94700.0,"type":"number"},"tp":{"description":"New take profit price. Omit to leave unchanged.","example":95400.0,"type":"number"}},"type":"object"},"RateResponse":{"additionalProperties":true,"description":"A single OHLCV candle.","properties":{"close":{"example":95050.0,"type":"number"},"high":{"example":95200.0,"type":"number"},"low":{"example":94900.0,"type":"number"},"open":{"example":95000.0,"type":"number"},"real_volume":{"example":0,"type":"integer"},"spread":{"description":"Spread in raw points. For BTCUSD on RoboForex: 1459 pts \u00d7 $0.01 = $14.59 round-turn.","example":1459,"type":"integer"},"tick_volume":{"example":1234,"type":"integer"},"time":{"description":"Candle open time as Unix epoch (broker server time).","example":1777879200,"type":"integer"}},"type":"object"},"StatusResponse":{"properties":{"status":{"example":"ok","type":"string"}},"required":["status"],"type":"object"},"SuccessResponse":{"properties":{"success":{"example":true,"type":"boolean"}},"required":["success"],"type":"object"},"SymbolInfoResponse":{"additionalProperties":true,"description":"MT5 symbol_info() fields for a single symbol.","properties":{"description":{"example":"Bitcoin vs US Dollar","type":"string"},"digits":{"description":"Price decimal places.","example":2,"type":"integer"},"name":{"example":"BTCUSD","type":"string"},"point":{"description":"Minimum price change (1 / 10^digits).","example":0.01,"type":"number"},"spread":{"description":"Current spread in raw points.","example":1400,"type":"integer"},"trade_allowed":{"example":true,"type":"boolean"},"trade_contract_size":{"description":"Contract size in base currency units (0.01 for cent accounts, 1.0 for BTCUSD/BTCUSDm standard).","example":1.0,"type":"number"},"trade_tick_size":{"example":0.01,"type":"number"},"trade_tick_value":{"description":"Profit per tick per lot in account currency. Used for pip_value calculation.","example":0.01,"type":"number"},"volume_max":{"example":100.0,"type":"number"},"volume_min":{"description":"Minimum lot size \u2014 used by bot as default lot.","example":0.01,"type":"number"},"volume_step":{"example":0.01,"type":"number"}},"type":"object"},"TerminalInfoResponse":{"additionalProperties":true,"description":"MT5 terminal_info() fields.","properties":{"build":{"type":"integer"},"community_account":{"type":"boolean"},"community_connection":{"type":"boolean"},"connected":{"type":"boolean"},"dlls_allowed":{"type":"boolean"},"name":{"example":"MetaTrader 5","type":"string"},"path":{"type":"string"},"ping_last":{"type":"integer"},"retransmission":{"type":"number"},"trade_allowed":{"type":"boolean"}},"type":"object"},"TickResponse":{"additionalProperties":true,"description":"Latest market tick for a symbol.","properties":{"ask":{"example":95014.0,"type":"number"},"bid":{"example":95000.0,"type":"number"},"flags":{"example":6,"type":"integer"},"last":{"example":0.0,"type":"number"},"symbol":{"example":"BTCUSD","type":"string"},"time":{"description":"Tick time as Unix epoch (broker server time).","example":1777879028,"type":"integer"},"volume":{"example":0,"type":"integer"}},"type":"object"},"TimeResponse":{"description":"Broker server time vs machine UTC, used to auto-detect the broker timezone.","properties":{"server_epoch":{"description":"Broker server time as Unix epoch. Null when market is closed and no tick is available.","example":1777879029,"nullable":true,"type":"integer"},"server_iso":{"example":"2026-05-04T07:17:09+00:00","format":"date-time","nullable":true,"type":"string"},"used_symbol":{"description":"Symbol whose tick was used to read server time.","example":"BTCUSD","nullable":true,"type":"string"},"utc_epoch":{"description":"Current UTC time as Unix epoch.","example":1777879028,"type":"integer"},"utc_iso":{"example":"2026-05-04T07:17:08+00:00","format":"date-time","type":"string"},"utc_offset_hours":{"description":"Detected broker UTC offset rounded to nearest hour. Null when server_epoch is null.","example":0,"nullable":true,"type":"integer"},"utc_offset_seconds":{"example":0,"nullable":true,"type":"integer"},"warning":{"description":"Present when no tick was available and server time could not be read.","nullable":true,"type":"string"}},"type":"object"}},"securitySchemes":{"bearerAuth":{"description":"Set `API_TOKEN` in `.env` to match the token configured on the server.","scheme":"bearer","type":"http"}}},"info":{"description":"Self-hosted HTTP REST API bridging the NOVOSKY trading bot to a MetaTrader 5 terminal. All bot-to-broker communication goes through this service. Authentication is required on all endpoints except `GET /ping`.","title":"MT5 HEADLESS","version":"1.0.0"},"openapi":"3.0.3","paths":{"/account":{"get":{"description":"Returns `mt5.account_info()` fields including live balance, equity, margin, leverage, and currency. The bot uses `currency == 'USC'` to auto-detect cent accounts and adjust profit calculations.","operationId":"get_account","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountResponse"}}},"description":"Account info"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Account info unavailable"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get account information","tags":["Account"]}},"/error":{"get":{"description":"Returns the most recent MT5 error code and message from `mt5.last_error()`. Useful for diagnosing failed operations.","operationId":"get_last_error","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Last error details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"}},"summary":"Last MT5 error","tags":["System"]}},"/history/deals":{"get":{"description":"Returns executed deals (fills) in a Unix epoch time range via `mt5.history_deals_get()`. Each position generates two deals: one on open (entry=0) and one on close (entry=1). The closing deal's `profit` field is stored raw in Supabase (USC for cent accounts \u2014 not converted to USD).","operationId":"get_history_deals","parameters":[{"description":"Start of range as Unix epoch","in":"query","name":"from","required":true,"schema":{"example":1777800000,"type":"integer"}},{"description":"End of range as Unix epoch","in":"query","name":"to","required":true,"schema":{"example":1777900000,"type":"integer"}},{"description":"Filter by symbol (optional)","in":"query","name":"symbol","required":false,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DealResponse"},"type":"array"}}},"description":"Historical deals"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid from/to parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get historical deals","tags":["History"]}},"/history/orders":{"get":{"description":"Returns closed/cancelled orders in a Unix epoch time range via `mt5.history_orders_get()`.","operationId":"get_history_orders","parameters":[{"description":"Start of range as Unix epoch","in":"query","name":"from","required":true,"schema":{"example":1777800000,"type":"integer"}},{"description":"End of range as Unix epoch","in":"query","name":"to","required":true,"schema":{"example":1777900000,"type":"integer"}},{"description":"Filter by symbol (optional)","in":"query","name":"symbol","required":false,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OrderResponse"},"type":"array"}}},"description":"Historical orders"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid from/to parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get historical orders","tags":["History"]}},"/orders":{"get":{"description":"Returns all pending (non-filled) orders, optionally filtered by symbol.","operationId":"list_orders","parameters":[{"description":"Filter by symbol (optional)","in":"query","name":"symbol","required":false,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OrderResponse"},"type":"array"}}},"description":"Active pending orders"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"List active pending orders","tags":["Orders"]},"post":{"description":"Places a market or pending order via `mt5.order_send()`. The NOVOSKY bot only places `BUY` and `SELL` market orders. Limit, stop, and stop-limit order types are available for manual use.","operationId":"place_order","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionResponse"}}},"description":"Order filled \u2014 open position returned"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}},"description":"Pending order placed"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid order parameters (check retcode \u2014 10014 = bad SL/TP, 10019 = insufficient funds)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Order send failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized or market closed (retcode 10018)"}},"summary":"Place a new order","tags":["Orders"]}},"/orders/{ticket}":{"delete":{"operationId":"cancel_order","parameters":[{"description":"Order ticket number","in":"path","name":"ticket","required":true,"schema":{"example":987654321,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}},"description":"Order cancelled"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Order not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Cancellation failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Cancel a pending order","tags":["Orders"]},"get":{"operationId":"get_order","parameters":[{"description":"Order ticket number","in":"path","name":"ticket","required":true,"schema":{"example":987654321,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}},"description":"Order details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Order not found (may have been filled or cancelled)"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get a pending order by ticket","tags":["Orders"]},"put":{"description":"Updates price, SL, TP, and/or expiry on a pending order.","operationId":"modify_order","parameters":[{"description":"Order ticket number","in":"path","name":"ticket","required":true,"schema":{"example":987654321,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}},"description":"Modified order"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid modification parameters"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Order not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Modification failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Modify a pending order","tags":["Orders"]}},"/ping":{"get":{"description":"Returns `{\"status\": \"ok\"}` when the API is running. This is the only endpoint that skips authentication.","operationId":"ping","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusResponse"}}},"description":"API is running"}},"security":[],"summary":"Health check","tags":["System"]}},"/ping/broker":{"get":{"description":"Measures round-trip latency to the MT5 broker and returns connection quality. `retransmission` is the TCP packet retransmission rate \u2014 0.0 means a perfect connection. Returns 503 if the MT5 terminal is not connected.","operationId":"ping_broker","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PingBrokerResponse"}}},"description":"Connection quality metrics"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 terminal not connected or not initialized"}},"summary":"Broker latency and connectivity","tags":["System"]}},"/positions":{"get":{"description":"Returns all open positions, optionally filtered by symbol.","operationId":"list_positions","parameters":[{"description":"Filter by symbol (optional)","in":"query","name":"symbol","required":false,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PositionResponse"},"type":"array"}}},"description":"Open positions"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"List open positions","tags":["Positions"]}},"/positions/{ticket}":{"delete":{"description":"Closes all or part of an open position at market price. Omit the request body to close the full position. Pass `volume` to do a partial close.","operationId":"close_position","parameters":[{"description":"Position ticket number","in":"path","name":"ticket","required":true,"schema":{"example":123456789,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionCloseRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealResponse"}}},"description":"Closing deal result"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Position not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Close order failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized or market closed (retcode 10018)"}},"summary":"Close a position","tags":["Positions"]},"get":{"operationId":"get_position","parameters":[{"description":"Position ticket number","in":"path","name":"ticket","required":true,"schema":{"example":123456789,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionResponse"}}},"description":"Position details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Position not found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get a position by ticket","tags":["Positions"]},"put":{"description":"Updates the SL and/or TP on an existing open position via `mt5.order_send()` with `ORDER_TYPE_SLTP`.","operationId":"update_position","parameters":[{"description":"Position ticket number","in":"path","name":"ticket","required":true,"schema":{"example":123456789,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionUpdateRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionResponse"}}},"description":"Updated position"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid SL/TP values (retcode 10014 = stops too close to price)"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Position not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Order send failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Modify stop loss or take profit","tags":["Positions"]}},"/symbols":{"get":{"description":"Returns symbol names available on the broker. Optionally filter by group (e.g. `?group=*BTC*` for all Bitcoin pairs). Used by the bot's symbol auto-discovery to find the correct BTC/USD pair name.","operationId":"list_symbols","parameters":[{"description":"Symbol group filter pattern (MT5 wildcard syntax).","in":"query","name":"group","required":false,"schema":{"example":"*BTC*","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"example":["BTCUSD","BTCUSDm"],"items":{"type":"string"},"type":"array"}}},"description":"Symbol name list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"List available symbols","tags":["Symbols"]}},"/symbols/{symbol}":{"get":{"description":"Returns `mt5.symbol_info()` for a single symbol. Key fields used by the bot: `volume_min` (default lot size), `trade_tick_value`, `trade_tick_size` (pip_value = tick_value / tick_size), `trade_contract_size`, `digits`.","operationId":"get_symbol_info","parameters":[{"description":"Symbol name","in":"path","name":"symbol","required":true,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SymbolInfoResponse"}}},"description":"Symbol details"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Symbol not found on this broker"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get symbol details","tags":["Symbols"]}},"/symbols/{symbol}/rates":{"get":{"description":"Returns historical OHLCV candles via `mt5.copy_rates_from_pos()`. Candle `time` is broker server time \u2014 the bot normalises to UTC using the detected offset from `/time`. `spread` is in raw points (e.g. 1400 pts \u00d7 $0.01 = $14.00 for BTCUSD on RoboForex).","operationId":"get_rates","parameters":[{"description":"Symbol name","in":"path","name":"symbol","required":true,"schema":{"example":"BTCUSD","type":"string"}},{"description":"MT5 timeframe: M1 M5 M15 M30 H1 H4 D1 W1 MN1","in":"query","name":"timeframe","required":false,"schema":{"default":"M1","example":"M15","type":"string"}},{"description":"Number of candles to return (most recent first)","in":"query","name":"count","required":false,"schema":{"default":100,"example":500,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RateResponse"},"type":"array"}}},"description":"OHLCV candles"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Invalid timeframe or count"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get OHLCV candles","tags":["Symbols"]}},"/symbols/{symbol}/rates/ta":{"post":{"description":"Fetches candles like `GET /symbols/{symbol}/rates` (same query params), then forwards them to the candlewick TA sidecar for indicator calculation. Returns both bars and TA results.","operationId":"get_rates_ta","parameters":[{"description":"Symbol name","in":"path","name":"symbol","required":true,"schema":{"example":"BTCUSD","type":"string"}},{"description":"MT5 timeframe: M1 M5 M15 M30 H1 H4 D1 W1 MN1","in":"query","name":"timeframe","required":false,"schema":{"default":"M1","example":"M15","type":"string"}},{"description":"Number of candles","in":"query","name":"count","required":false,"schema":{"default":100,"example":500,"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"indicators":{"additionalProperties":true,"description":"Candlewick indicator spec (e.g. {\"rsi\": {\"period\": 14}, \"ema\": {\"period\": 21}}).","type":"object"},"recentBars":{"description":"Only return TA for the N most recent bars.","example":50,"type":"integer"}},"required":["indicators"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"bars":{"items":{"$ref":"#/components/schemas/RateResponse"},"type":"array"},"symbol":{"type":"string"},"ta":{"additionalProperties":true,"nullable":true,"type":"object"},"timeframe":{"type":"string"}},"type":"object"}}},"description":"Bars with TA analysis"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid indicators body"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Candlewick sidecar unavailable or rejected request"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get OHLCV candles with technical analysis","tags":["Symbols"]}},"/symbols/{symbol}/tick":{"get":{"description":"Returns the most recent bid/ask tick for a symbol via `mt5.symbol_info_tick()`. Tick `time` is broker server time (Unix epoch).","operationId":"get_tick","parameters":[{"description":"Symbol name","in":"path","name":"symbol","required":true,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TickResponse"}}},"description":"Latest tick"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Symbol not found or no tick available"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get latest tick","tags":["Symbols"]}},"/symbols/{symbol}/ticks":{"get":{"description":"Returns the most recent tick-level data via `mt5.copy_ticks_from()`. Useful for spread analysis.","operationId":"get_ticks","parameters":[{"description":"Symbol name","in":"path","name":"symbol","required":true,"schema":{"example":"BTCUSD","type":"string"}},{"description":"Number of ticks to return","in":"query","name":"count","required":false,"schema":{"default":100,"example":1000,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TickResponse"},"type":"array"}}},"description":"Historical ticks"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get historical ticks","tags":["Symbols"]}},"/terminal":{"get":{"description":"Returns `mt5.terminal_info()` fields including connection state, build number, trade permissions, and latency.","operationId":"get_terminal_info","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TerminalInfoResponse"}}},"description":"Terminal info"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Get terminal info","tags":["Terminal"]}},"/terminal/init":{"post":{"description":"Calls `mt5.initialize()`. Required before any trading operations if the terminal has been shut down.","operationId":"init_terminal","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}},"description":"Terminal initialized"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Initialization failed \u2014 check MT5 installation path"}},"summary":"Initialize MT5 terminal connection","tags":["Terminal"]}},"/terminal/restart":{"post":{"description":"Equivalent to calling `/terminal/shutdown` then `/terminal/init`. Useful for recovering from a hung terminal.","operationId":"restart_terminal","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}},"description":"Terminal restarted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Restart failed"}},"summary":"Restart the MT5 terminal","tags":["Terminal"]}},"/terminal/shutdown":{"post":{"description":"Calls `mt5.shutdown()`. After this call, all trading endpoints return errors until `/terminal/init` is called again.","operationId":"shutdown_terminal","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}},"description":"Terminal shut down"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"}},"summary":"Shutdown the MT5 terminal","tags":["Terminal"]}},"/time":{"get":{"description":"Returns the broker server timestamp alongside machine UTC time and the detected UTC offset (broker_time \u2212 utc_epoch, rounded to the nearest hour). The bot calls this at startup to auto-detect the broker timezone including DST changes (e.g. RoboForex shows +3 in summer, +2 in winter). Pass `?symbol=BTCUSD` to pin which symbol's tick is used for server time. When the market is closed and no tick is available, `server_epoch` will be `null` and a `warning` field is included.","operationId":"get_broker_time","parameters":[{"description":"Symbol to read tick time from. Uses first available tick if omitted.","in":"query","name":"symbol","required":false,"schema":{"example":"BTCUSD","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimeResponse"}}},"description":"Broker time and UTC offset"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"Missing or invalid Bearer token"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}},"description":"MT5 not initialized"}},"summary":"Broker server time vs UTC","tags":["Time"]}}},"security":[{"bearerAuth":[]}],"servers":[{"url":"/"}],"tags":[{"description":"Health, latency, and error diagnostics","name":"System"},{"description":"MT5 terminal lifecycle and server time","name":"Terminal"},{"description":"Broker server time and UTC offset detection","name":"Time"},{"description":"Live balance, equity, and margin","name":"Account"},{"description":"Symbol specs, ticks, and OHLCV rates","name":"Symbols"},{"description":"Open position management","name":"Positions"},{"description":"Pending order placement and management","name":"Orders"},{"description":"Closed deals and historical orders","name":"History"}]}
