import { MT5AccountStatus, MT5ChartBarSource, MT5CommissionMode, MT5CommissionVolumeType, MT5DealEntry, MT5ExecutionMode, MT5Interval, MT5MarketStatus, MT5OrderKind, MT5OrderStateName, MT5ServerState, MT5SyncPhase, MT5TradeMode, MT5TradeSide, KalshiFutureStatus, KalshiLifecycleStatus, KalshiServerStatus, KalshiSettlementResult } from '../../common/constants/mt5.constant'; import { SerializationFormat } from '../../common/constants/http.constant'; import { WS_EVENT_NAME } from '../../core/ws/ws.events'; /** * CMS content types — mirrors `eaera_cms/core/content/routes.py`. * * The CMS exposes **two** content namespaces; the SDK uses the CRUD one: * - `/api/v1/content` (singular) — CRUD (this module). * - `/api/v1/contents` (plural) — read-only query path (list/search/count). * * All field names use camelCase to match the backend API response. * * Two response shapes recur across the API and are defined once at the top * of this module — `Paginated` for list endpoints, `ActionResponse` + * `ActionOutcome` for action endpoints. Every list / action service * aliases these rather than re-declaring the envelope. */ /** * Canonical paginated-list envelope. Every list endpoint returns * `{ items, total, page, perPage }`; services that need extra fields * (e.g. `size` instead of `perPage` for history, or `totalPages` for * pagination UI) extend this rather than redeclaring. */ export interface Paginated { items: T[]; total: number; page: number; perPage: number; } /** * Business outcome nested inside `ActionResponse.output`. * * The HTTP-200 envelope always returns 200 — guard rejections carry * the verdict inside `output.success`, not via HTTP status. `details` * is human-readable strings (e.g. "GitHub token expired (Redis TTL) — * reconnect required"); UI surfaces render them via `formatEvidence` * after masking identifiers. */ export interface ActionOutcome { success: boolean; error?: string; details?: unknown[]; } /** * Discriminated action outcome — the canonical view-model every UI * consumer should reach for. * * `ActionResponse` describes the wire envelope (HTTP 200 + nested * `output.success` business verdict) — see api_references §3.1/§9. * `ActionResult` lifts the business verdict to a single top-level * discriminator so `if (result.success)` means "the action's business * outcome succeeded" everywhere in the UI. Use `parseActionResponse` * to convert an `ActionResponse` into an `ActionResult`. */ export type ActionResult> = { success: true; output?: T; } | { success: false; error: string; details?: unknown[]; }; /** CMS content item envelope. CT-specific fields live under `data`; * `status` is the lifecycle field (draft/published/deleted). No `state`. */ export interface ContentItem> { id: number; uuid: string; /** e.g. `"Mt5Account"`, `"LlmConversation"`. */ typeName: string; data: T; /** Lifecycle field: `"draft"` | `"published"` | `"deleted"` | CT-specific. */ status: string; slug?: string | null; version?: number; parentId?: number | null; sortOrder?: number; /** ISO 8601. */ publishedAt?: string | null; createdBy?: number | null; updatedBy?: number | null; /** ISO 8601. */ createdAt?: string; updatedAt?: string; } /** * `ContentListResponse` → GET /api/v1/content. * * New wire envelope (api-inventory §5.1.1, cursor-only since the PO * ruling 2026-10-11): `{ data: ContentItem[], meta: { total_count, * next_cursor, has_more, limit } }`. `content.list()` normalizes both the * new envelope and the legacy `{ items, total, page, perPage }` shape into * this one, so consumers read `items` + `nextCursor` + `hasMore`. */ export type ContentListResponse = Paginated & { /** Opaque cursor (`meta.next_cursor`) — pass back as `cursor` for the next page. `null` when done. */ nextCursor: string | null; /** `meta.has_more` — `false` when there is no next page. */ hasMore: boolean; }; /** * `PaginatedResponse` → GET /api/v1/contents (plural, read-only query path). * Kept for reference; the CRUD service does not use it. */ export interface PaginatedResponse { items: T[]; totalCount: number; limit: number; offset: number; hasMore: boolean; pageInfo: PageInfo; } export interface PageInfo { currentPage: number; totalPages: number; perPage: number; } /** Query params for GET /api/v1/content (FastAPI Query() aliases). */ export interface ContentQuery { type?: string; status?: string; packId?: string; /** * Batch UUID lookup (`?uuids=a&uuids=b&uuids=c` on the wire — ofetch * serializes `string[]` as repeated `uuids=` query params, which the * FastAPI handler accepts at `eaera_cms/core/content/routes.py:354-389`). * Combines with `type` / `status` / `packId`. Caller should pass * `perPage >= uuids.length` so the response covers the requested set * in one page. * * @deprecated Prefer the filter DSL with the `in` operator on a natural * key (e.g. `buildFilter([{ field: 'data.symbol', op: 'in', value: [...] }])`) * — uuids are unstable references for cross-session data and the * `in` filter composes with the other query params. */ uuids?: string[]; /** * Cursor-only pagination (api-inventory §6.1, PO ruling 2026-10-11). * Page size `1..100` — the successor of `perPage`; the BE now rejects * `page` / `page_size` / `per_page` / `offset` with **400**. */ limit?: number; /** * Opaque cursor from the previous page's `meta.next_cursor` (camelized * `nextCursor`). Repeat the same query params + this cursor to walk * pages; `id` is always the final sort tiebreaker, so results stay * stable across writes. */ cursor?: string; /** @deprecated cursor-only since 2026-10-11 — the SDK drops it on the wire. */ page?: number; /** @deprecated use `limit` — the SDK maps it onto `limit` on the wire. */ perPage?: number; format?: SerializationFormat; /** * Filter expression (eaera-cms DSL string). Field names use the JSONB * key as stored in PG — for the Mt5* CTs this is snake_case * (``data.symbol``, ``data.mt5_ts``, ``data.profit_loss`` …). The * response converts to camelCase via the CMS middleware, but the * filter expression must use the storage casing. * * Operators (see {@link CONTENT_FILTER_OPERATOR} for the full set): * comparison ``eq`` ``neq`` ``gt`` ``gte`` ``lt`` ``lte``; list ``in`` * ``not_in`` ``between``; null-check ``is_null`` ``is_not_null``; * text-pattern ``contains`` ``starts_with`` ``ends_with`` ``regex`` * ``like`` ``ilike``; JSONB ``json_contains`` ``json_has_key`` * ``json_type``. Boolean composition with ``AND``, ``OR``, ``NOT`` + * parentheses. A value containing whitespace must be double-quoted * (e.g. ``data.category:eq:"CFDs on Stocks"``). * * For scalar conditions prefer the {@link buildFilter} helper, which * joins conditions with ``AND`` and quotes whitespace values * automatically. * * @example * filter: 'data.symbol:eq:EURUSD AND data.mt5_ts:gte:2026-07-01' * @example * filter: 'data.login:eq:200223 AND data.entry:eq:OUT AND data.profit_loss:gte:50' * @example * filter: 'data.symbol:contains:USD AND data.volume:gt:0' * @example * filter: 'data.category:eq:"CFDs on Stocks"' */ filter?: string; /** * Sort field. Built-in columns: ``created_at``, ``updated_at``, * ``sort_order``. JSONB leaves are addressed without the ``data.`` * prefix (the BE auto-prefixes): ``mt5_ts``, ``mt5_ts_open``, * ``mt5_ts_setup``, ``profit_loss``, ``symbol``, ``volume`` … * Falls back to ``created_at`` on unknown values. */ sortBy?: string; /** Sort direction. Case-insensitive. Default: ``'desc'``. */ sortDirection?: 'asc' | 'desc'; /** * Per-CT query params forwarded verbatim to the BE (`?serverName=...&userId=...` * via ofetch). The Mt5* pack accepts dozens of JSONB-leaf filters * (`symbol`, `isFavorites`, `tradeMode`, `marketStatus`, …) — typing each * one here would defeat the point of a generic query shape. * * The HTTP layer (`sdk.content.list`) spreads this object as the second * ofetch argument; ofetch serialises unknown keys as `?key=value` query * params, which the FastAPI handler accepts. * * @example * fetchByType('Mt5Symbol', { serverName: MT5_SERVER_NAME, tradeMode: 'FULL' }) * fetchByType('Mt5Watchlist', { userId: 42, isFavorites: true }) */ [key: string]: unknown; } /** POST /api/v1/content body (`ContentCreateRequest`). */ export interface ContentCreatePayload> { typeName: string; data: T; /** Default `"draft"`. */ status?: string; slug?: string | null; /** Parent row INT id (internal paths). Prefer `parentUuid` for API callers. */ parentId?: number | null; /** * Parent content UUID — resolved server-side to the INT `parent_id`, * linked into the closure table. Preferred addressing style for API * callers (UUIDs are what the API already deals in). */ parentUuid?: string | null; } /** PUT /api/v1/content/{uuid} body (`ContentUpdateRequest`). */ export interface ContentUpdatePayload> { data?: Partial; slug?: string | null; /** * Optimistic-concurrency stamp — the ContentItem.version the caller * last read. The backend rejects a stale version, so callers doing * read-modify-write (e.g. request-type edit) should always send it. */ version?: number; /** * Re-parent under the given UUID. An explicit `null` detaches the * content to root; omitting the key leaves the hierarchy untouched. * Applied atomically with the data update (closure subtree rewrite). */ parentUuid?: string | null; } /** A CT action declared on a content type (entry in `availableActions`). */ export interface ContentAction { /** actionName, e.g. `"change_group"`, `"change_leverage"`. */ name: string; /** Display label. */ label: string; /** Resolved codeRef, e.g. `eaera_mt5.handlers.operational:change_group`. */ handler: string; } /** GET /api/v1/content/{uuid}/actions → `AvailableActionsResponse`. */ export interface AvailableActionsResponse { contentUuid: string; typeName: string; status: string; availableActions: ContentAction[]; } /** POST /api/v1/content/{uuid}/actions/{actionName} body → `ActionInvokeRequest`. */ export interface ActionInvokePayload { /** Action-specific payload. The dashboard writes nested camelCase keys * (`{ newGroup: 'demo\\Group' }`, `{ newLeverage: 50 }`); the CMS * `CamelToSnakeMiddleware` (`eaera_cms/core/request_casing.py`) * recursively converts them to `new_group` / `new_leverage` before the * handler reads them. Both casings work end-to-end because of that * middleware — keep the dashboard source on camelCase for consistency * with the rule-execution params shape. See * `risk-rules-api-guide.md` §4.C for the full table. */ params?: Record; /** Optional UUIDv4 idempotency token. */ intentionId?: string; /** Optional optimistic-concurrency guard. */ expectedVersion?: number; } /** * ActionResponse → POST /api/v1/content/{uuid}/actions/{actionName}. * * The HTTP-200 envelope is intentionally two-layered: * * - `success` = transport — `true` whenever the action was invoked * (the backend always returns 200 on guard rejections) * - `output.success` = business — `false` for guard rejections * (e.g. `GITHUB_TOKEN_UNAVAILABLE`, `duplicate_code`, * `readiness_not_met`) * - `output.error` = business error code * - `output.details` = human-readable detail strings (rendered via the * portal's `formatEvidence`, never rendered raw) * - `error` = transport-level error (only when `success === false`) * * The `T` parameter captures any action-specific payload fields the * handler returns on top of the business outcome (e.g. `{ entityUuid, * revisionUuid, code }` for `register_legal_entity`). * * Consumers must always discriminate both layers. The portal flattens * them via `adaptActionResponse` (utils/actionResult). */ export interface ActionResponse { /** Transport outcome — true when the action was invoked. */ success: boolean; /** Business outcome + payload. `output.success === false` means the * action was invoked but rejected by a guard. */ output?: ActionOutcome & T; /** Transport-level error (only present when `success === false`). */ error?: string | null; /** Server-reported execution time in milliseconds. Optional — some * legacy / mock responses omit it. */ durationMs?: number; } /** * Params for `close_selected_positions` (WS `action` frame or REST action on * `Mt5Account`). Batch-closes a list of open positions by ticket id. * * Wire contract: `eaera-docs/cbs/eaera-webtrade/.../tradeapi/api-inventory.md` * §B.2 + §B.6. The BE rejects batch-level failures (empty list, >100 tickets). * Params are sent as `position_ids` (snake) — same as the §B.6 frame sketch. */ export interface ICloseSelectedPositionsParams { /** Open-position tickets to close (1..100 per call). */ position_ids: number[]; /** Optional comment attached to the close deals. */ comment?: string; } /** * Per-ticket result payload returned by `close_selected_positions` inside * `action_result.data` (WS) or `ActionResponse.output` (REST). Partial * success is expected — check `closedCount` / `errorCount`. * * Wire note: the WS `action_result.data` arrives **camelCase** (`closedCount`, * `errorCount` — verified against the live CMS `wss://.../api/ws`), even * though api-inventory §B.6 sketches snake_case (`closed_count`). The snake * keys are kept as optional fallbacks so the type tolerates either env. */ export interface ICloseSelectedPositionsResult { closed: { ticket: number; closed: boolean; symbol: string; }[]; errors: { ticket: number; code: string; message: string; }[]; closedCount: number; errorCount: number; /** Legacy snake_case aliases (api-inventory §B.6 sketch) — optional. */ closed_count?: number; error_count?: number; } /** `Mt5Server` (`pack.yaml`) — FEAT-006 (monitored-group list source). */ export type ServerState = MT5ServerState; export interface Mt5ServerData { /** Required, unique — used as the group source key. */ name: string; endpoint: string; login: number; /** Format: `aes-256-gcm`. */ passwordEncrypted: string; state: ServerState; syncIntervalSeconds: number; statementIntervalSeconds: number; tasksEnabled: string[]; /** FEAT-004 symbol selector source. */ fxPairs: string[]; health: Record; lastError?: string | null; deployedVersion?: string | null; /** * Broker-side UTC offset in seconds (e.g. `7200` for UTC+2, `25200` for * UTC+7, `-18000` for UTC-5). The BE always populates this on every * `Mt5Server` row — per-deployment brokers ship their own timezone without * a code change. The FE consumes this to configure chart timezone * (replaces the previously hardcoded `+7` offset). */ serverTimeOffsetSeconds: number; } export type Mt5Server = ContentItem; /** `Mt5Account` (`pack.yaml`) — FEAT-003 (group/login), FEAT-005 (name). */ export type AccountStatus = MT5AccountStatus; export interface Mt5AccountData { /** FK to `Mt5Server.name`. */ serverName: string; login: number; /** FEAT-005 detail dialog "full name". */ name: string; email?: string | null; /** FEAT-003/005 row + FEAT-006 monitored list. */ group: string; leverage: number; currency: string; balance: number; equity: number; margin: number; marginFree: number; marginLevel: number; profit: number; floating: number; status: AccountStatus; inSync: boolean; rights: number; enable: boolean; tags: string[]; lastDealId?: number | null; calcStatus: string; /** * Derived at sync from FEAT-007 `setting.product.trading` * (`prediction_account_groups` / prefixes). Pred FE filters `eq:true`. */ isPredictionAccount?: boolean; } export type Mt5Account = ContentItem; /** * `Mt5Group` — inventory §4.14 / FEAT-009. * Identity: `(data.serverName, data.group)`. State: `active ↔ archived`. * `Mt5Account.data.group` is the natural FK into this CT. */ export type Mt5GroupTradeRight = 'swaps' | 'trailing' | 'experts' | 'expiration' | 'signals_all' | 'signals_own' | 'so_compensation' | 'so_fully_hedged' | 'fifo_close' | 'hedge_prohibit' | 'deal_cost' | 'so_compensation_credit' | (string & {}); /** * Commission tier (`IMTConCommTier`). * `mode` / `type` use {@link MT5_COMMISSION_MODE} / {@link MT5_COMMISSION_VOLUME_TYPE}. */ export interface MT5ConCommTier { mode?: MT5CommissionMode; type?: MT5CommissionVolumeType; value?: number; minimal?: number; maximal?: number; rangeFrom?: number; rangeTo?: number; currency?: string; modeLabel?: string; typeLabel?: string; } /** * Group commission config (`IMTConCommission`) under `Mt5Group.data.commissions`. * Mode ints / labels are raw wire values (no enums yet). * **Pred v1:** assume one commission + one tier → `commissions[0].tiers[0]`. */ export interface MT5ConCommission { mode?: number; name?: string; path?: string; tiers?: MT5ConCommTier[]; reasons?: string[]; entryMode?: number; modeLabel?: string; rangeMode?: number; actionMode?: number; chargeMode?: number; profitMode?: number; reasonFlags?: number; entryModeLabel?: string; rangeModeLabel?: string; actionModeLabel?: string; chargeModeLabel?: string; profitModeLabel?: string; } export interface Mt5GroupData { serverName: string; /** Full backslash-delimited group path, e.g. `real\EAERA\Global\Prediction`. */ group: string; currency: string; serverId?: number; currencyDigits?: number; marginCall?: number; marginStopOut?: number; marginSoMode?: number; marginSoModeLabel?: string; marginFreeModeLabel?: string; marginFreeProfitModeLabel?: string; marginModeLabel?: string; marginFlags?: number; marginFloatingLeverage?: number | string; demoLeverage?: number; demoDeposit?: number; demoInactivityPeriod?: number; tradeFlags?: number; /** Decoded trade rights — FE gating: `hedge_prohibit`, `fifo_close`, … */ tradeRights?: Mt5GroupTradeRight[]; tradeInterestrate?: number; tradeVirtualCredit?: number; tradeTransferModeLabel?: string; limitHistoryLabel?: number | string; limitOrders?: number; limitPositions?: number; limitSymbols?: number; permissions?: string[]; authModeLabel?: string; authOtpModeLabel?: string; authPasswordMin?: number; company?: string; companyPage?: string; companyEmail?: string; companySupportPage?: string; companySupportEmail?: string; companyCatalog?: string; companyDepositPage?: string; companyWithdrawalPage?: string; reportsModeLabel?: string; reportsFlagModes?: unknown; reportsSmtp?: string; reportsEmail?: string; newsModeLabel?: string; newsCategory?: string; newsLangs?: number[]; mailModeLabel?: string; /** * Group commission configs (`MT5ConCommission`). * Pred v1: `commissions[0].tiers[0]` only (one commission + one tier). */ commissions?: MT5ConCommission[]; /** Raw `IMTConGroup` snapshot (secrets stripped). */ mt5RawConfig?: Record; lastSyncedAt?: string; } export type Mt5Group = ContentItem; /** * `Mt5SyncState` (`pack.yaml`) — per-server sync lifecycle. One row per * serverName; updated by the mt5 worker as it processes statements/deals/ticks. * * Fields reflect mt5 data-design §2.6. */ export interface Mt5SyncStateData { /** FK to `Mt5Server.data.name`. */ serverName: string; /** Current sync strategy. */ syncMode: string; /** High-level phase — UI shows this as the headline status. */ phase: MT5SyncPhase; /** Per-type status + progress counters. */ accounts?: { status: string; processed: number; total: number; }; deals?: { status: string; processed: number; total: number; }; ticks?: { status: string; processed: number; total: number; }; daily?: { status: string; processed: number; total: number; }; /** Cursor / checkpoint (id of last processed record). */ checkpoint?: string; /** ISO 8601. */ startedAt?: string | null; completedAt?: string | null; /** Last error string (if phase === 'failed'). */ lastError?: string | null; } export type Mt5SyncState = ContentItem; /** * `ReportDefinition` (`pack.yaml`) — report template schema. The full schema * is template-specific (configured per pack install); we expose the generic * envelope as `Record` until per-template shapes are modeled. */ export type ReportDefinitionData = Record; export type ReportDefinition = ContentItem; /** * `LlmConversation` — AI conversation result with provenance. Created by the * `generate_ai_summary` action on `Mt5Account`. Full schema is LLM-provider * specific; exposed as a generic envelope until per-provider shapes are modeled. */ export type LlmConversationData = Record; export type LlmConversation = ContentItem; /** * `KalshiServer` (FEAT-010) — Kalshi gateway config. Fields reflect mt5 * data-design §2.7. */ export interface KalshiServerData { /** Required, unique — gateway name. */ name: string; apiKeyId: string; /** Encrypted private key blob (Kalshi API secret). */ apiPrivateKey: string; baseUrl: string; /** FK to `Mt5Server.data.name` — the gateway proxies to this mt5 server. */ mt5Server: string; /** Lifecycle status. */ status: KalshiServerStatus; syncInterval: number; pricePollInterval: number; /** Default spread applied to mirrored Kalshi markets, in basis points. */ defaultSpreadBps: number; /** Kalshi categories to mirror (e.g. `['economics', 'sports']`). */ categories: string[]; } export type KalshiServer = ContentItem; /** * `KalshiEvent.data` — inventory §4.12. * Identity: `data.eventTicker`. State: `open → closed → finalized`. */ export interface KalshiEventData { /** Kalshi `event_ticker` (e.g. `KXBTC15M-26JUL291400`). */ eventTicker: string; title?: string; /** @deprecated Prefer `seriesTicker` → series.category (§4.12). */ category?: string; subCategory?: string; /** Parent series (`KXBTC15M`, `KXGOLDMON`, …). */ seriesTicker?: string; /** When the underlying asset settles (ISO 8601). */ strikeDate?: string; /** Cadence: `15M` / `1H` / `1D` / `W1` / `MON` / … */ strikePeriod?: string; /** When trading stops on the event (ISO 8601). */ closeTime?: string; status?: KalshiLifecycleStatus; /** True if only one market in the event can settle YES. */ mutuallyExclusive?: boolean; /** Per-market source-of-truth for resolution. */ settlementSources?: Record[]; /** Free-form Kalshi product metadata. */ productMetadata?: Record; /** Event description (pack §2.2). */ description?: string; /** * Series cadence on KalshiEvent (pack §2.2 / §2.4): * `fifteen_min` | `hourly` | `daily` | `weekly` | `monthly` | `quarterly` | * `annual` | `one_off` | `custom`. */ frequency?: string; /** Denormalized ladder size when present on Event. */ strikesCount?: number; /** Always `0` on the event row — 24h volume is a trending/future concern. */ volume24hContracts?: number; /** Cumulative traded contracts across child markets (pack §2.2). */ volumeContracts?: number; lastUpdatedTs?: number; /** Full Kalshi event JSON for audit / re-derivation. */ rawData?: Record; } export type KalshiEvent = ContentItem; /** * `KalshiFuture.data` — inventory §4.13 / pack §2.3. * Identity: `data.marketTicker`. State: `active → closed → finalized`. * Quotes are **integer cents (0–100)** — served verbatim from CMS. */ export interface KalshiFutureData { /** Kalshi `ticker` (e.g. `KXWTI-26JUL2914-T83.99`). */ marketTicker: string; /** Logical FK to `KalshiEvent.data.eventTicker`. */ eventTicker?: string; seriesTicker?: string; /** Series frequency (`daily`, `weekly`, …) — FEAT-015 cadence ladder source. */ cadence?: string; /** `Crypto` / `Commodities` / `Economics` / etc. */ kalshiCategory?: string; underlyingMt5Symbol?: string; /** Default `1`. */ underlyingLeverage?: number; title?: string; yesSubTitle?: string; noSubTitle?: string; /** MT5 custom symbol (e.g. `KS-KXWTI-26JUL2914-T83.99`). */ mt5Symbol?: string; serverName?: string; /** `binary` | `scalar` — stored market type enum. */ marketType?: string; /** Broker-spread-adjusted YES bid/ask — integer cents `0–100`. */ yesBidCents?: number; yesAskCents?: number; /** Chance % midpoint in cents; NO-side mirror per pack §1.2. */ yesPriceCents?: number; noPriceCents?: number; spreadCents?: number; volume?: number; volume24hContracts?: number; /** Cumulative traded contracts (Kalshi `volume_fp`, pack §2.3). */ volumeContracts?: number; openInterest?: number; openTime?: string; closeTime?: string; expectedExpirationTime?: string; /** Snapshot observation time (ISO-8601 UTC). */ quoteObservedAt?: string; /** `0` or `100` after settle; unset while active. */ settlementValueCents?: number | null; /** Settled outcome; `null` while active. */ result?: KalshiSettlementResult | null; status?: KalshiFutureStatus; settledAt?: string; settlementTs?: string; closedAt?: string; /** Default `true`. When false the gateway stops syncing this market. */ enabled?: boolean; /** Visibility flag for FE lists (`false` = hide). */ feIncluded?: boolean; lastSyncedAt?: string; /** Strike label for ladder rows (BE pack / `yes_sub_title` alias). */ strikeLabel?: string; /** Min contracts per order (BE pack — prefer Mt5Symbol minLot in FE). */ minContract?: number; /** Max contracts per order (BE pack — prefer Mt5Symbol maxLot in FE). */ maxContract?: number; /** ADR-005 fee coefficient (default `0.07`). */ feeCoefficient?: number; /** UI category slug when present on Future CT. */ category?: string; /** Display badge for category chips. */ categoryBadge?: string; /** `binary` | `scalar` | … when present on Event denorm. */ eventType?: string; /** Ladder size hint when denormalized on Future/Event. */ strikesCount?: number; } export type KalshiFuture = ContentItem; /** Per-order-type margin rates mirrored from MT5 ``MarginRateInitial`` / ``MarginRateMaintenance``. */ export interface Mt5MarginRateByType { buy: number; sell: number; buyStop: number; buyLimit: number; sellStop: number; sellLimit: number; buyStopLimit: number; sellStopLimit: number; } /** Weekly session slot (`tradingSession` / `quoteSessions` items — inventory §4.3). */ export interface Mt5SessionItem { /** 0 = Sunday … 6 = Saturday. */ weekday: number; weekdayLabel: string; /** Broker-local minutes from midnight. */ openMin: number; closeMin: number; /** ``"HH:MM"`` broker-local time. */ open: string; close: string; } /** `Mt5Symbol` (FEAT-001 / FEAT-016). Full catalog mirror of MT5's ``ConSymbol``. */ export interface Mt5SymbolData { serverName: string; symbol: string; fullSymbolName: string; /** Breadcrumb path the broker exposes (e.g. ``['Forex', 'Majors']``). */ path: string[]; category: string; baseCurrency: string; quoteCurrency: string; digits: number; point: number; tickSize: number; tickValue: number; contractSize: number; minLot: number; maxLot: number; lotStep: number; leverageDefault: number; marginRateDefault: number; /** Spread for anonymous / no-group viewers (int). */ spreadDefault: number; /** ``{group_name: spread_int}``. Pick the entry matching ``Mt5Account.data.group``. */ spreadByGroup: Record; marginByGroup: Record; leverageByGroup: Record; /** * ``{group_name: swap_long_float}`` — per-group long-swap rate * (USD/lot/day). The MT5 broker usually surfaces this via the * ``IMTConGroupSymbol::SwapLong`` field even when ``SpreadDiff`` * and ``MarginRate*`` are zero — so this map is the most reliable * per-group financial diff for a webtrade chart. Empty on * brokers without per-group swap overrides. */ swapLongByGroup: Record; /** * ``{group_name: swap_short_float}`` — per-group short-swap rate * (USD/lot/day). */ swapShortByGroup: Record; swapLong: number; swapShort: number; swapRollover3days: number; commissionRule: string; tradeMode: MT5TradeMode; executionMode: MT5ExecutionMode; fills: number; /** Decoded fill flags — subset of ``fok`` / ``ioc`` / ``boc``. */ fillingModes?: string[]; /** Raw trade-flags int (``tradeMode`` / order-permission bits). */ tradeFlags?: number; /** Raw tick-flags int (quote/tick rule bits). */ tickFlags?: number; tradingSession: Mt5SessionItem[]; /** Weekly QUOTE sessions — chart data flows while quoted even if trading is closed. */ quoteSessions?: Mt5SessionItem[]; /** Quote staleness threshold, seconds. */ quotesTimeout?: number; /** Trade start date (ISO-8601; empty when unset) — FEAT-016 ``not-tradable-yet`` gate. */ timeStart?: string; expiration?: string; marketStatus: MT5MarketStatus; listOrder: number; isVisible: boolean; backendNote?: string; /** ISO-8601 last sync time (worker writes). */ lastSyncedAt?: string; /** MT5 ``CalcMode`` enum (0 forex · 1 futures · 2 cfd · 3 cfdindex · 4 cfdleverage · 5 forex_no_leverage · 32 exch_stocks · 33 exch_futures · 34 exch_futures_forts · 35 exch_options · 37 exch_bonds · 64 serv_collateral). */ calcMode?: number; /** Human label for ``calcMode`` (e.g. ``"cfd"``). */ calcModeLabel?: string; /** Currency margin is charged in (``CurrencyMargin``). */ marginCurrency?: string; /** Fixed initial-margin override per lot; ``0`` = use the calc-mode formula. */ marginInitial?: number; /** Fixed maintenance margin per lot (``0`` = none). */ marginMaintenance?: number; /** Initial margin rate per order type; ``0`` entries fall back to ``marginLong`` / ``marginShort``. */ marginRateInitialByType?: Mt5MarginRateByType; /** Maintenance margin rate per order type (same fallback rule). */ marginRateMaintenanceByType?: Mt5MarginRateByType; /** Directional initial margin rates (``MarginLong`` / ``MarginShort``). */ marginLong?: number; marginShort?: number; /** Hedged (overlapping) margin rate — hedging accounts only. */ marginHedged?: number; /** MT5 margin-calculation flags (bit 0 ``CHECK_PROCESS`` · bit 1 ``CHECK_SLTP``). */ marginFlags?: number; /** Per-pending-type margin overrides (``0`` = none). */ marginLimit?: number; marginStop?: number; marginStopLimit?: number; /** Bond face value (``FaceValue``, calc mode 37). */ faceValue?: number; /** Max total volume per direction (positions + pending orders), lots; ``0`` = unlimited. */ volumeLimit?: number; /** Raw 1/10000-lot wire volumes (``VolumeLimit`` / ``VolumeMax`` / ``VolumeMin`` / ``VolumeStep``). */ volumeLimitRaw?: number; volumeMaxRaw?: number; volumeMinRaw?: number; volumeStepRaw?: number; /** Instant-execution auto-request threshold, lots (``0`` = none). */ ieVolumeMax?: number; /** Per-account-group volume grid overrides (lot space). */ volumeLimitByGroup?: Record; volumeMaxByGroup?: Record; volumeMinByGroup?: Record; volumeStepByGroup?: Record; /** ``{group_name: tradable}`` — group-scoped tradability of the symbol. */ tradableByGroup?: Record; /** Order types this symbol allows (``market``, ``limit``, ``stop``, ``stop_limit``, ``sl``, ``tp``, ``close_by``). */ allowedOrderTypes?: string[]; /** Raw order-type flags int (``allowedOrderTypes`` decoded twin). */ orderFlags?: number; /** Raw expiry flags int (``expirationModes`` decoded twin). */ expirFlags?: number; /** Decoded expiry flags — subset of ``gtc`` / ``day`` / ``specified`` / ``specified_day``. */ expirationModes?: string[]; /** MT5 groups the symbol is explicitly available to; empty = every group (fall back to ``tradeMode``). */ groups?: string[]; /** ``{group: trade_mode}``; missing groups fall back to ``tradeMode``. */ tradeModeByGroup?: Record; /** Min stop distance in points from price (SL/TP + pendings); ``0`` = unrestricted. */ stopsLevel?: number; stopsLevelByGroup?: Record; /** No-edit band in points near execution; ``0`` = unrestricted. */ freezeLevel?: number; freezeLevelByGroup?: Record; /** ``{group: triple-swap day (0-6)}``. */ swap3dayByGroup?: Record; isin?: string; cfi?: string; exchange?: string; country?: string; /** Numeric sector code. */ sector?: number; sectorLabel?: string; industry?: number; basis?: string; source?: string; /** Broker page/sheet reference. */ page?: string; /** MT5-native category label (distinct from the FE ``category``). */ mt5Category?: string; descriptionInternational?: string; /** Raw MT5 category breadcrumb (``Path``). */ color?: number; colorBackground?: number; /** Swap mode enum int. */ swapMode?: number; /** ``disabled`` / ``by_points`` / ``by_symbol_currency`` / … / ``by_profit_currency``. */ swapModeLabel?: string; /** Raw swap flags int. */ swapFlags?: number; /** Days in the swap year (usually 360 or 365). */ swapYearDays?: number; /** ``{sunday..saturday: rate}``. */ swapRateByWeekday?: Record; /** MT5 GTC mode enum int. */ gtcMode?: number; gtcModeLabel?: string; /** Request-execution flags / timeout, seconds. */ reFlags?: number; reTimeout?: number; /** Instant-execution flags / check mode / timeout, seconds. */ ieFlags?: number; ieCheckMode?: number; ieTimeout?: number; ieSlipProfit?: number; ieSlipLosing?: number; filterSoft?: number; filterSoftTicks?: number; filterHard?: number; filterHardTicks?: number; filterDiscard?: number; filterGap?: number; filterGapTicks?: number; filterSpreadMin?: number; filterSpreadMax?: number; spreadBalance?: number; /** Spread offset in points vs the broker feed. */ spreadDiff?: number; spreadDiffBalance?: number; tickBookDepth?: number; /** Quote push delay, seconds. */ subscriptionsDelay?: number; /** Raw chart-mode enum int. */ chartMode?: number; /** ``bid_price`` / ``last_price``. */ chartModeLabel?: string; /** Raw book-volume enum int. */ bookVolumeMode?: number; bookVolumeModeLabel?: string; /** Futures settlement price. */ priceSettle?: number; /** Option strike price. */ priceStrike?: number; priceLimitMax?: number; priceLimitMin?: number; /** Options style label (e.g. ``european_call``). */ optionsMode?: number; optionsModeLabel?: string; /** Bond/option accrued interest. */ accruedInterest?: number; /** Splicing behavior for continuous contracts. */ spliceType?: number; spliceTypeLabel?: string; spliceTimeType?: number; spliceTimeTypeLabel?: string; spliceTimeDays?: number; /** Contract multiplier (exchange instruments). */ multiply?: number; currencyBaseDigits?: number; currencyProfitDigits?: number; currencyMarginDigits?: number; /** Margin-rate scale factor applied before the by-type maps. */ marginRateLiquidity?: number; marginRateCurrency?: number; /** Verbatim raw MT5 ``ConSymbol`` config snapshot (opaque passthrough). */ mt5RawConfig?: Record; } export type Mt5Symbol = ContentItem; /** `Mt5Category` (FEAT-001 §US2). */ export interface Mt5CategoryData { serverName: string; name: string; label: string; path: string[]; /** Denormalised — fast list rendering on the FE. */ symbols: string[]; sortOrder: number; /** Built-in = broker-defined (read-only). False = admin-created (editable). */ isBuiltin: boolean; isVisible: boolean; /** Account groups this category is available for. Empty array = category is * not available for any account (admin-marked hidden). The FE derives the * scope from the selected account's `group` and filters the visible list * accordingly — see `symbolStore.fetchMarketCategories`. */ groups: string[]; } export type Mt5Category = ContentItem; /** `Mt5Watchlist` (FEAT-001 §US4–§US5). Per-user symbol list. */ export interface Mt5WatchlistData { userId: number; serverName: string; name: string; label: string; /** * Ordered symbol NAMES (e.g. ``['EURUSD', 'AAPL']``) — preserves * drag-drop reordering. Rows written before the name convention may still * hold symbol UUIDs; consumers detect + migrate them on read * (see webtrade `symbolStore._resolveWatchlistSymbolNames`). */ symbols: string[]; /** True only on the row with ``name === 'favorites'``. */ isFavorites: boolean; isBuiltin: boolean; sortOrder: number; detail?: boolean; } export type Mt5Watchlist = ContentItem; /** `Mt5ChartBar` (FEAT-002 / FEAT-013 §US1). OHLC bar mirrored from ClickHouse or built from live ticks. */ export interface Mt5ChartBarData { serverName: string; symbol: string; /** Canonical interval: `M1` | `M5` | … | `MN1`. */ interval: MT5Interval; /** ISO-8601 server time at open of the bar. */ mt5Ts: string; open: number; high: number; low: number; close: number; volume: number; tickVolume: number; spread: number; source: MT5ChartBarSource; isCorrection: boolean; } export type Mt5ChartBar = ContentItem; /** `Mt5PositionHistory` (FEAT-008 §history tab). */ export interface Mt5PositionHistoryData { serverName: string; login: number; positionId: number; /** ISO-8601 server time. */ mt5TsOpen: string; /** ISO-8601 server time. */ mt5TsClose?: string | null; symbol: string; type: MT5TradeSide; volume: number; openPrice: number; closePrice: number; stopLoss: number; takeProfit: number; commission: number; fee: number; swap: number; profitLoss: number; comment: string; currency: string; balanceAfter?: number | null; } export type Mt5PositionHistory = ContentItem; /** `Mt5OrderHistory` (FEAT-008 §history tab). */ export interface Mt5OrderHistoryData { serverName: string; login: number; orderId: number; /** ISO-8601 server time. */ mt5TsSetup: string; /** ISO-8601 server time, terminal state. */ mt5TsDone?: string | null; symbol: string; type: MT5OrderKind; side: MT5TradeSide; volume: number; executedPrice: number; limitPrice: number; stopPrice: number; stopLoss: number; takeProfit: number; /** * Decoded MT5 `state_str` (e.g. `FILLED`, `CANCELED`, `PARTIAL`). Use the * `MT5OrderStateNameOrUnknown` escape hatch when comparing wire values that * may include broker-introduced states not yet modelled. */ status: MT5OrderStateName | string; comment: string; } export type Mt5OrderHistory = ContentItem; /** `Mt5DealHistory` (FEAT-008 §history tab). Per executed transaction. */ export interface Mt5DealHistoryData { serverName: string; login: number; dealId: number; orderId: number; positionId: number; /** ISO-8601 server time. */ mt5Ts: string; symbol: string; entry: MT5DealEntry; action: MT5TradeSide; volume: number; price: number; commission: number; fee: number; swap: number; profitLoss: number; comment: string; } export type Mt5DealHistory = ContentItem; /** `Mt5Drawing` (FEAT-006 §drawings). User-created chart annotation, per (user, symbol). */ export interface Mt5DrawingData { userId: number; serverName: string; symbol: string; /** Client UUID for the drawing — used to disambiguate duplicates. */ drawingKey: string; drawType: string; /** Type-specific shape — points / colors / anchors. Opaque to the SDK. */ data: Record; color: string; lineWidth: number; fillEnabled: boolean; locked: boolean; } export type Mt5Drawing = ContentItem; /** Per-symbol tick envelope carried over WS topic ``symbol::``. */ export interface SymbolTickPayload { event: typeof WS_EVENT_NAME.SYMBOL_TICK | typeof WS_EVENT_NAME.LAST_TICK; symbol: string; server: string; bid: number; ask: number; last: number; volume: number; spread: number; /** ISO-8601 server time. */ timestamp: string; publisher: string; } //# sourceMappingURL=content.type.d.ts.map