Skip to main content

limitless/models/
mod.rs

1//! Request and response model types for the Limitless Exchange API.
2//!
3//! All REST API responses are fully typed with concrete structs matching
4//! the API's JSON shape. WebSocket event types and order types are also
5//! fully typed for compile-time safety.
6//!
7//! # Convention
8//!
9//! - Field names use `snake_case` in Rust and `camelCase` on the wire via
10//!   `#[serde(rename = "...")]` where needed.
11//! - Numeric fields that arrive as JSON strings use `serde_helpers` to
12//!   deserialize transparently.
13
14pub mod order;
15
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18
19// ── Generic wrapper ──
20
21/// Wraps data with a server-side timestamp for freshness tracking.
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Timed<T> {
24    pub time: u64,
25    pub data: T,
26}
27
28// ═══════════════════════════════════════════════════════════════════════════
29//  Markets
30// ═══════════════════════════════════════════════════════════════════════════
31
32/// Response from `GET /markets/active`.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ActiveMarketsResponse {
35    pub data: Vec<MarketSummary>,
36    #[serde(rename = "totalMarketsCount")]
37    pub total_markets_count: i32,
38}
39
40/// Summary view of a market in the active-markets list.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct MarketSummary {
43    pub id: i32,
44    pub slug: String,
45    pub title: String,
46    #[serde(rename = "proxyTitle", default)]
47    pub proxy_title: Option<String>,
48    #[serde(default)]
49    pub description: String,
50    #[serde(rename = "collateralToken")]
51    pub collateral_token: CollateralTokenInfo,
52    #[serde(rename = "expirationDate")]
53    pub expiration_date: String,
54    #[serde(rename = "expirationTimestamp")]
55    pub expiration_timestamp: i64,
56    #[serde(default)]
57    pub expired: Option<bool>,
58    #[serde(rename = "createdAt")]
59    pub created_at: String,
60    #[serde(rename = "updatedAt")]
61    pub updated_at: String,
62    pub categories: Vec<String>,
63    pub status: String,
64    pub creator: MarketCreatorInfo,
65    pub tags: Vec<String>,
66    #[serde(rename = "tradeType")]
67    pub trade_type: String,
68    #[serde(rename = "marketType")]
69    pub market_type: String,
70    #[serde(rename = "priorityIndex")]
71    pub priority_index: i32,
72    pub metadata: MarketMetadataInfo,
73    #[serde(default)]
74    pub volume: Option<String>,
75    #[serde(rename = "volumeFormatted", default)]
76    pub volume_formatted: Option<String>,
77    #[serde(rename = "automationType", default)]
78    pub automation_type: Option<String>,
79    #[serde(rename = "imageUrl", default)]
80    pub image_url: Option<String>,
81    #[serde(default)]
82    pub trends: Option<Value>,
83    #[serde(rename = "openInterest", default)]
84    pub open_interest: Option<String>,
85    #[serde(rename = "openInterestFormatted", default)]
86    pub open_interest_formatted: Option<String>,
87    #[serde(default)]
88    pub liquidity: Option<String>,
89    #[serde(rename = "liquidityFormatted", default)]
90    pub liquidity_formatted: Option<String>,
91    #[serde(rename = "positionIds", default)]
92    pub position_ids: Vec<String>,
93    #[serde(rename = "conditionId", default)]
94    pub condition_id: Option<String>,
95    #[serde(rename = "negRiskRequestId", default)]
96    pub neg_risk_request_id: Option<String>,
97    #[serde(default)]
98    pub tokens: Option<MarketTokensInfo>,
99    #[serde(default)]
100    pub prices: Vec<f64>,
101    #[serde(rename = "tradePrices", default)]
102    pub trade_prices: Option<TradePricesInfo>,
103    #[serde(rename = "isRewardable", default)]
104    pub is_rewardable: Option<bool>,
105    #[serde(default)]
106    pub settings: Option<Value>,
107    #[serde(default)]
108    pub venue: Option<VenueInfo>,
109    #[serde(default)]
110    pub logo: Option<String>,
111    #[serde(rename = "priceOracleMetadata", default)]
112    pub price_oracle_data: Option<Value>,
113    #[serde(rename = "orderInGroup", default)]
114    pub order_in_group: Option<i32>,
115    #[serde(rename = "winningOutcomeIndex", default)]
116    pub winning_outcome_idx: Option<i32>,
117    #[serde(rename = "outcomeTokens", default)]
118    pub outcome_tokens: Vec<String>,
119    #[serde(rename = "ogImageURI", default)]
120    pub og_image_uri: Option<String>,
121    #[serde(rename = "negRiskMarketId", default)]
122    pub neg_risk_market_id: Option<String>,
123    #[serde(default)]
124    pub markets: Vec<MarketSummary>,
125    #[serde(rename = "dailyReward", default)]
126    pub daily_reward: Option<String>,
127    #[serde(default)]
128    pub address: Option<String>,
129    #[serde(rename = "type", default)]
130    pub market_type_legacy: Option<String>,
131    #[serde(default)]
132    pub outcomes: Vec<OutcomeInfo>,
133    #[serde(rename = "resolutionDate", default)]
134    pub resolution_date: Option<String>,
135}
136
137// NOTE: MarketSummary and MarketDetail share the same shape from the API.
138// We alias them for semantic clarity.
139pub type MarketDetail = MarketSummary;
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct CollateralTokenInfo {
143    pub address: String,
144    pub decimals: i32,
145    pub symbol: String,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct MarketCreatorInfo {
150    pub name: String,
151    #[serde(rename = "imageURI", default)]
152    pub image_uri: Option<String>,
153    #[serde(default)]
154    pub link: Option<String>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct MarketMetadataInfo {
159    pub fee: bool,
160    #[serde(rename = "isBannered", default)]
161    pub is_bannered: Option<bool>,
162    #[serde(rename = "isPolyArbitrage", default)]
163    pub is_poly_arbitrage: Option<bool>,
164    #[serde(rename = "shouldMarketMake", default)]
165    pub should_market_make: Option<bool>,
166    #[serde(rename = "openPrice", default)]
167    pub open_price: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct MarketTokensInfo {
172    pub yes: String,
173    pub no: String,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct TradePricesInfo {
178    pub buy: PriceSideInfo,
179    pub sell: PriceSideInfo,
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct PriceSideInfo {
184    pub market: [f64; 2],
185    pub limit: [f64; 2],
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct VenueInfo {
190    pub exchange: String,
191    #[serde(default)]
192    pub adapter: Option<String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct OutcomeInfo {
197    pub id: i32,
198    pub title: String,
199    #[serde(rename = "tokenId")]
200    pub token_id: String,
201    #[serde(default)]
202    pub price: Option<f64>,
203}
204
205/// Response from `GET /markets/categories/count`.
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct CategoryCountResponse {
208    pub data: Value,
209}
210
211/// Active market slug entry from `GET /markets/active/slugs`.
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ActiveSlug {
214    pub slug: String,
215    #[serde(default)]
216    pub ticker: Option<String>,
217    #[serde(default)]
218    pub strike_price: Option<String>,
219    #[serde(default)]
220    pub deadline: Option<String>,
221}
222
223/// Response from `GET /markets/{addr}/oracle-candles`.
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct OracleCandlesResponse {
226    pub data: Vec<OracleCandle>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct OracleCandle {
231    pub timestamp: i64,
232    pub open: f64,
233    pub high: f64,
234    pub low: f64,
235    pub close: f64,
236}
237
238/// Response from `GET /markets/{slug}/get-feed-events`.
239#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct FeedEventsResponse {
241    pub events: Vec<FeedEvent>,
242    #[serde(default)]
243    pub total: Option<i64>,
244    #[serde(default)]
245    pub page: Option<i64>,
246    #[serde(default)]
247    pub limit: Option<i64>,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct FeedEvent {
252    #[serde(rename = "type")]
253    pub event_type: String,
254    #[serde(default)]
255    pub data: Option<Value>,
256    #[serde(default)]
257    pub timestamp: Option<String>,
258}
259
260/// Response from `GET /markets/search`.
261#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct SearchResponse {
263    pub data: Vec<MarketSummary>,
264    #[serde(default)]
265    pub total: Option<i64>,
266}
267
268// ═══════════════════════════════════════════════════════════════════════════
269//  Trading
270// ═══════════════════════════════════════════════════════════════════════════
271
272/// Response from `POST /orders`.
273#[derive(Debug, Clone, Serialize, Deserialize)]
274pub struct CreateOrderResponse {
275    pub order: CreatedOrderInfo,
276    #[serde(rename = "makerMatches", default)]
277    pub maker_matches: Vec<MakerMatchInfo>,
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct CreatedOrderInfo {
282    pub id: String,
283    #[serde(rename = "createdAt", default)]
284    pub created_at: Option<String>,
285    #[serde(rename = "makerAmount")]
286    pub maker_amount: Value,
287    #[serde(rename = "takerAmount")]
288    pub taker_amount: Value,
289    #[serde(default)]
290    pub expiration: Option<String>,
291    #[serde(rename = "signatureType")]
292    pub signature_type: i32,
293    pub salt: Value,
294    pub maker: String,
295    pub signer: String,
296    pub taker: String,
297    #[serde(rename = "tokenId")]
298    pub token_id: String,
299    pub side: Value,
300    #[serde(rename = "feeRateBps")]
301    pub fee_rate_bps: i32,
302    pub nonce: i32,
303    pub signature: String,
304    #[serde(rename = "orderType")]
305    pub order_type: String,
306    #[serde(default)]
307    pub price: Option<f64>,
308    #[serde(rename = "marketId")]
309    pub market_id: i32,
310    #[serde(default)]
311    pub status: Option<String>,
312    #[serde(rename = "filledSize", default)]
313    pub filled_size: Option<Value>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct MakerMatchInfo {
318    pub id: String,
319    #[serde(rename = "createdAt", default)]
320    pub created_at: Option<String>,
321    #[serde(rename = "matchedSize")]
322    pub matched_size: Value,
323    #[serde(rename = "orderId")]
324    pub order_id: String,
325}
326
327/// Response from `POST /orders/status/batch`.
328pub type OrderStatusBatchResponse = Value;
329
330/// Response from `POST /orders/cancel` and `DELETE /orders/:id`.
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct CancelOrderResponse {
333    #[serde(default)]
334    pub message: Option<String>,
335    #[serde(default)]
336    pub id: Option<String>,
337}
338
339/// Response from `POST /orders/cancel-batch`.
340pub type CancelBatchResponse = Value;
341
342/// Response from `DELETE /orders/all/:slug`.
343#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct CancelAllResponse {
345    #[serde(default)]
346    pub message: Option<String>,
347}
348
349/// Response from `GET /markets/:slug/orderbook`.
350#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct OrderbookResponse {
352    pub bids: Vec<OrderbookEntry>,
353    pub asks: Vec<OrderbookEntry>,
354    #[serde(rename = "tokenId")]
355    pub token_id: String,
356    #[serde(rename = "adjustedMidpoint")]
357    pub adjusted_midpoint: f64,
358    #[serde(rename = "maxSpread")]
359    pub max_spread: String,
360    #[serde(rename = "minSize")]
361    pub min_size: String,
362    #[serde(rename = "lastTradePrice")]
363    pub last_trade_price: f64,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct OrderbookEntry {
368    pub price: f64,
369    pub size: f64,
370    pub side: String,
371}
372
373/// Historical price data point from `GET /markets/:slug/historical-price`.
374#[derive(Debug, Clone, Serialize, Deserialize)]
375pub struct HistoricalPriceData {
376    pub timestamp: i64,
377    pub price: f64,
378}
379
380/// Response from `GET /markets/:slug/locked-balance`.
381#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct LockedBalanceResponse {
383    #[serde(rename = "lockedBalance")]
384    pub locked_balance: String,
385    #[serde(rename = "lockedBalanceFormatted", default)]
386    pub locked_balance_formatted: Option<String>,
387}
388
389/// Response from `GET /markets/:slug/user-orders`.
390#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct UserOrdersResponse {
392    pub data: Vec<UserOrderInfo>,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct UserOrderInfo {
397    pub id: String,
398    #[serde(rename = "createdAt")]
399    pub created_at: String,
400    #[serde(rename = "makerAmount")]
401    pub maker_amount: Value,
402    #[serde(rename = "takerAmount")]
403    pub taker_amount: Value,
404    #[serde(default)]
405    pub expiration: Option<String>,
406    #[serde(rename = "signatureType")]
407    pub signature_type: i32,
408    pub salt: Value,
409    pub maker: String,
410    pub signer: String,
411    pub taker: String,
412    #[serde(rename = "tokenId")]
413    pub token_id: String,
414    pub side: Value,
415    #[serde(rename = "feeRateBps")]
416    pub fee_rate_bps: i32,
417    pub nonce: i32,
418    pub signature: String,
419    #[serde(rename = "orderType")]
420    pub order_type: String,
421    #[serde(default)]
422    pub price: Option<f64>,
423    #[serde(rename = "marketId")]
424    pub market_id: i32,
425    #[serde(default)]
426    pub status: Option<String>,
427    #[serde(rename = "filledSize", default)]
428    pub filled_size: Option<Value>,
429}
430
431/// Response from `GET /markets/:slug/events`.
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct MarketEventsResponse {
434    pub events: Vec<Value>,
435    #[serde(default)]
436    pub total: Option<i64>,
437    #[serde(default)]
438    pub page: Option<i64>,
439    #[serde(default)]
440    pub limit: Option<i64>,
441}
442
443// ═══════════════════════════════════════════════════════════════════════════
444//  Portfolio
445// ═══════════════════════════════════════════════════════════════════════════
446
447/// Response from `GET /profiles/:account`.
448#[derive(Debug, Clone, Serialize, Deserialize)]
449pub struct ProfileResponse {
450    pub id: i32,
451    pub account: String,
452    #[serde(default)]
453    pub rank: Option<RankInfo>,
454    #[serde(rename = "createdAt", default)]
455    pub created_at: Option<String>,
456    #[serde(default)]
457    pub username: Option<String>,
458    #[serde(rename = "displayName", default)]
459    pub display_name: Option<String>,
460    #[serde(rename = "pfpUrl", default)]
461    pub pfp_url: Option<String>,
462    #[serde(default)]
463    pub bio: Option<String>,
464    #[serde(rename = "socialUrl", default)]
465    pub social_url: Option<String>,
466    #[serde(rename = "tradeWalletOption", default)]
467    pub trade_wallet_option: Option<String>,
468    #[serde(rename = "embeddedAccount", default)]
469    pub embedded_account: Option<String>,
470    #[serde(default)]
471    pub points: Option<f64>,
472    #[serde(rename = "accumulativePoints", default)]
473    pub accumulative_points: Option<f64>,
474    #[serde(rename = "enrolledInPointsProgram", default)]
475    pub enrolled_in_points_program: Option<bool>,
476    #[serde(rename = "leaderboardPosition", default)]
477    pub leaderboard_position: Option<i32>,
478    #[serde(rename = "referralData", default)]
479    pub referral_data: Vec<ReferralDataInfo>,
480    #[serde(rename = "referredUsersCount", default)]
481    pub referred_users_count: Option<i32>,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct RankInfo {
486    pub id: i32,
487    pub name: String,
488    #[serde(rename = "feeRateBps")]
489    pub fee_rate_bps: i32,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct ReferralDataInfo {
494    #[serde(rename = "createdAt")]
495    pub created_at: String,
496    pub id: i32,
497    #[serde(rename = "referredProfileId")]
498    pub referred_profile_id: i32,
499    #[serde(rename = "pfpUrl", default)]
500    pub pfp_url: Option<String>,
501    #[serde(rename = "displayName")]
502    pub display_name: String,
503}
504
505/// AMM trade entry from `GET /portfolio/trades`.
506#[derive(Debug, Clone, Serialize, Deserialize)]
507pub struct TradeEntry {
508    #[serde(rename = "transactionHash", default)]
509    pub transaction_hash: Option<String>,
510    #[serde(rename = "blockTimestamp")]
511    pub block_timestamp: i64,
512    #[serde(rename = "collateralAmount", default)]
513    pub collateral_amount: Option<String>,
514    #[serde(default)]
515    pub market: Option<TradeMarketInfo>,
516    #[serde(rename = "outcomeIndex", default)]
517    pub outcome_index: Option<i32>,
518    #[serde(rename = "outcomeTokenAmount", default)]
519    pub outcome_token_amount: Option<String>,
520    #[serde(default)]
521    pub strategy: Option<String>,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct TradeMarketInfo {
526    pub id: String,
527    pub slug: String,
528    pub title: String,
529    pub closed: bool,
530}
531
532/// Response from `GET /portfolio/positions`.
533#[derive(Debug, Clone, Serialize, Deserialize)]
534pub struct PositionsResponse {
535    #[serde(default)]
536    pub amm: Vec<AmmPositionEntry>,
537    #[serde(default)]
538    pub clob: Vec<ClobPositionEntry>,
539    #[serde(default)]
540    pub group: Vec<Value>,
541    #[serde(default)]
542    pub points: Option<String>,
543    #[serde(rename = "accumulativePoints", default)]
544    pub accumulative_points: Option<String>,
545    #[serde(default)]
546    pub rewards: Option<Value>,
547}
548
549#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct AmmPositionEntry {
551    pub market: PositionMarketInfo,
552    pub account: String,
553    #[serde(rename = "outcomeIndex")]
554    pub outcome_index: i32,
555    #[serde(rename = "collateralAmount")]
556    pub collateral_amount: String,
557    #[serde(rename = "outcomeTokenAmount")]
558    pub outcome_token_amount: String,
559    #[serde(rename = "averageFillPrice")]
560    pub average_fill_price: String,
561    #[serde(rename = "totalBuysCost")]
562    pub total_buys_cost: String,
563    #[serde(rename = "totalSellsCost")]
564    pub total_sells_cost: String,
565    #[serde(rename = "realizedPnl")]
566    pub realized_pnl: String,
567    #[serde(rename = "unrealizedPnl")]
568    pub unrealized_pnl: String,
569    #[serde(rename = "latestTrade", default)]
570    pub latest_trade: Option<Value>,
571}
572
573#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct ClobPositionEntry {
575    pub market: PositionMarketInfo,
576    #[serde(rename = "makerAddress")]
577    pub maker_address: String,
578    pub positions: ClobPositionSides,
579    #[serde(rename = "tokensBalance")]
580    pub tokens_balance: PositionTokenBalance,
581    #[serde(rename = "latestTrade")]
582    pub latest_trade: PositionLatestTrade,
583    #[serde(default)]
584    pub orders: Option<Value>,
585    #[serde(default)]
586    pub rewards: Option<Value>,
587}
588
589#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct PositionMarketInfo {
591    pub id: Value,
592    pub slug: String,
593    pub title: String,
594    #[serde(default)]
595    pub status: Option<String>,
596    pub closed: bool,
597    pub deadline: String,
598    #[serde(rename = "conditionId", default)]
599    pub condition_id: Option<String>,
600    #[serde(rename = "winningOutcomeIndex", default)]
601    pub winning_outcome_index: Option<i32>,
602    #[serde(default)]
603    pub group: Option<Value>,
604}
605
606#[derive(Debug, Clone, Serialize, Deserialize)]
607pub struct ClobPositionSides {
608    pub yes: PositionSideInfo,
609    pub no: PositionSideInfo,
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize)]
613pub struct PositionSideInfo {
614    pub cost: String,
615    #[serde(rename = "fillPrice")]
616    pub fill_price: String,
617    #[serde(rename = "marketValue")]
618    pub market_value: String,
619    #[serde(rename = "realisedPnl")]
620    pub realised_pnl: String,
621    #[serde(rename = "unrealizedPnl")]
622    pub unrealized_pnl: String,
623}
624
625#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct PositionTokenBalance {
627    pub yes: String,
628    pub no: String,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize)]
632pub struct PositionLatestTrade {
633    #[serde(rename = "latestYesPrice", default)]
634    pub latest_yes_price: Option<f64>,
635    #[serde(rename = "latestNoPrice", default)]
636    pub latest_no_price: Option<f64>,
637    #[serde(rename = "outcomeTokenPrice", default)]
638    pub outcome_token_price: Option<f64>,
639}
640
641/// Response from `GET /portfolio/pnl-chart`.
642#[derive(Debug, Clone, Serialize, Deserialize)]
643pub struct PnlChartResponse {
644    #[serde(default)]
645    pub data: Vec<PnlChartPoint>,
646    #[serde(rename = "totalValue", default)]
647    pub total_value: Option<f64>,
648    #[serde(rename = "totalUnrealizedPnl", default)]
649    pub total_unrealized_pnl: Option<f64>,
650    #[serde(rename = "totalRealizedPnl", default)]
651    pub total_realized_pnl: Option<f64>,
652}
653
654#[derive(Debug, Clone, Serialize, Deserialize)]
655pub struct PnlChartPoint {
656    pub timestamp: i64,
657    pub value: f64,
658}
659
660/// Response from `GET /portfolio/points`.
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct PointsResponse {
663    #[serde(default)]
664    pub points: Option<f64>,
665    #[serde(rename = "accumulativePoints", default)]
666    pub accumulative_points: Option<f64>,
667    #[serde(default)]
668    pub breakdown: Vec<Value>,
669}
670
671/// Response from `GET /portfolio/history`.
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct HistoryResponse {
674    pub data: Vec<HistoryEntry>,
675    #[serde(rename = "nextCursor")]
676    pub next_cursor: Option<String>,
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize)]
680pub struct HistoryEntry {
681    #[serde(rename = "blockTimestamp")]
682    pub block_timestamp: i64,
683    #[serde(rename = "collateralAmount", default)]
684    pub collateral_amount: Option<String>,
685    #[serde(default)]
686    pub market: Option<HistoryMarketInfo>,
687    #[serde(rename = "outcomeIndex", default)]
688    pub outcome_index: Option<i32>,
689    #[serde(rename = "outcomeTokenAmount", default)]
690    pub outcome_token_amount: Option<String>,
691    #[serde(rename = "outcomeTokenAmounts", default)]
692    pub outcome_token_amounts: Option<Vec<String>>,
693    #[serde(rename = "outcomeTokenPrice", default)]
694    pub outcome_token_price: Option<Value>,
695    #[serde(default)]
696    pub strategy: Option<String>,
697    #[serde(rename = "transactionHash", default)]
698    pub transaction_hash: Option<String>,
699}
700
701#[derive(Debug, Clone, Serialize, Deserialize)]
702pub struct HistoryMarketInfo {
703    pub closed: bool,
704    #[serde(default)]
705    pub collateral: Option<Value>,
706    #[serde(default)]
707    pub group: Option<Value>,
708    #[serde(rename = "conditionId", default)]
709    pub condition_id: Option<String>,
710    #[serde(default)]
711    pub funding: Option<String>,
712    pub id: String,
713    pub slug: String,
714    pub title: String,
715    #[serde(rename = "expirationDate", default)]
716    pub expiration_date: Option<String>,
717}
718
719/// Response from `GET /portfolio/trading/allowance`.
720#[derive(Debug, Clone, Serialize, Deserialize)]
721pub struct AllowanceResponse {
722    #[serde(default)]
723    pub allowance: Option<String>,
724    #[serde(rename = "allowanceFormatted", default)]
725    pub allowance_formatted: Option<String>,
726    #[serde(rename = "approvedSpender", default)]
727    pub approved_spender: Option<String>,
728    #[serde(rename = "approvedAmount", default)]
729    pub approved_amount: Option<String>,
730}
731
732// ═══════════════════════════════════════════════════════════════════════════
733//  Navigation
734// ═══════════════════════════════════════════════════════════════════════════
735
736/// Navigation tree node from `GET /navigation`.
737#[derive(Debug, Clone, Serialize, Deserialize)]
738pub struct NavigationNode {
739    pub id: String,
740    pub name: String,
741    pub slug: String,
742    pub path: String,
743    #[serde(default)]
744    pub icon: Option<String>,
745    #[serde(default)]
746    pub children: Vec<NavigationNode>,
747}
748
749/// Market page from `GET /market-pages/by-path`.
750#[derive(Debug, Clone, Serialize, Deserialize)]
751pub struct MarketPage {
752    pub id: String,
753    pub name: String,
754    pub slug: String,
755    #[serde(rename = "fullPath")]
756    pub full_path: String,
757    #[serde(default)]
758    pub description: Option<String>,
759    #[serde(rename = "baseFilter")]
760    pub base_filter: Value,
761    #[serde(rename = "filterGroups")]
762    pub filter_groups: Vec<FilterGroupInfo>,
763    pub metadata: Value,
764    pub breadcrumb: Vec<BreadcrumbItem>,
765}
766
767#[derive(Debug, Clone, Serialize, Deserialize)]
768pub struct FilterGroupInfo {
769    #[serde(default)]
770    pub name: Option<String>,
771    #[serde(default)]
772    pub slug: Option<String>,
773    #[serde(rename = "allowMultiple", default)]
774    pub allow_multiple: Option<bool>,
775    #[serde(default)]
776    pub presentation: Option<String>,
777    #[serde(default)]
778    pub options: Vec<FilterGroupOption>,
779    #[serde(default)]
780    pub source: Option<Value>,
781}
782
783#[derive(Debug, Clone, Serialize, Deserialize)]
784pub struct FilterGroupOption {
785    pub label: String,
786    pub value: String,
787    #[serde(default)]
788    pub metadata: Option<Value>,
789}
790
791#[derive(Debug, Clone, Serialize, Deserialize)]
792pub struct BreadcrumbItem {
793    pub name: String,
794    pub slug: String,
795    pub path: String,
796}
797
798/// Response from `GET /market-pages/:id/markets`.
799#[derive(Debug, Clone, Serialize, Deserialize)]
800pub struct PageMarketsResponse {
801    pub data: Vec<MarketSummary>,
802    #[serde(default)]
803    pub pagination: Option<OffsetPagination>,
804    #[serde(default)]
805    pub cursor: Option<CursorPagination>,
806}
807
808#[derive(Debug, Clone, Serialize, Deserialize)]
809pub struct OffsetPagination {
810    pub page: i32,
811    pub limit: i32,
812    pub total: i32,
813    #[serde(rename = "totalPages")]
814    pub total_pages: i32,
815}
816
817#[derive(Debug, Clone, Serialize, Deserialize)]
818pub struct CursorPagination {
819    #[serde(rename = "nextCursor", default)]
820    pub next_cursor: Option<String>,
821}
822
823/// Property key from `GET /property-keys` and `GET /property-keys/:id`.
824#[derive(Debug, Clone, Serialize, Deserialize)]
825pub struct PropertyKey {
826    pub id: String,
827    pub name: String,
828    pub slug: String,
829    #[serde(rename = "type")]
830    pub property_type: String,
831    pub metadata: Value,
832    #[serde(rename = "isSystem")]
833    pub is_system: bool,
834    #[serde(default)]
835    pub options: Vec<PropertyOption>,
836    #[serde(rename = "createdAt")]
837    pub created_at: String,
838    #[serde(rename = "updatedAt")]
839    pub updated_at: String,
840}
841
842/// Property option from `GET /property-keys/:id/options`.
843#[derive(Debug, Clone, Serialize, Deserialize)]
844pub struct PropertyOption {
845    pub id: String,
846    #[serde(rename = "propertyKeyId")]
847    pub property_key_id: String,
848    pub value: String,
849    pub label: String,
850    #[serde(rename = "sortOrder")]
851    pub sort_order: i32,
852    #[serde(rename = "parentOptionId", default)]
853    pub parent_option_id: Option<String>,
854    pub metadata: Value,
855    #[serde(rename = "createdAt")]
856    pub created_at: String,
857    #[serde(rename = "updatedAt")]
858    pub updated_at: String,
859}
860
861// ═══════════════════════════════════════════════════════════════════════════
862//  WebSocket events — OME, settlement, and position types
863// ═══════════════════════════════════════════════════════════════════════════
864//
865// NOTE: General-purpose WS event types (OrderbookUpdate, TradeEvent,
866// MarketCreatedEvent, NewPriceData, etc.) now live in `ws::channel`.
867// The types below are the detailed OME/settlement/position events that
868// are unique to this implementation.
869
870#[derive(Debug, Clone, Serialize, Deserialize)]
871pub struct OmeEvent {
872    pub source: String,
873    #[serde(rename = "type")]
874    pub event_type: String,
875    #[serde(rename = "eventId")]
876    pub event_id: u64,
877    #[serde(rename = "orderId")]
878    pub order_id: String,
879    #[serde(rename = "clientOrderId")]
880    pub client_order_id: Option<String>,
881    #[serde(rename = "userId")]
882    pub user_id: u64,
883    #[serde(rename = "marketId")]
884    pub market_id: String,
885    pub token: String,
886    pub side: String,
887    pub price: String,
888    #[serde(rename = "remainingSize")]
889    pub remaining_size: String,
890    pub timestamp: String,
891}
892
893#[derive(Debug, Clone, Serialize, Deserialize)]
894pub struct SettlementEvent {
895    pub source: String,
896    #[serde(rename = "type")]
897    pub event_type: String,
898    #[serde(rename = "eventId")]
899    pub event_id: String,
900    #[serde(rename = "orderId")]
901    pub order_id: Option<String>,
902    #[serde(rename = "clientOrderId")]
903    pub client_order_id: Option<String>,
904    #[serde(rename = "userId")]
905    pub user_id: u64,
906    #[serde(rename = "takerOrderId")]
907    pub taker_order_id: Option<String>,
908    #[serde(rename = "takerAccount")]
909    pub taker_account: Option<String>,
910    #[serde(rename = "makerMatches")]
911    pub maker_matches: Option<Vec<MakerMatch>>,
912    #[serde(rename = "marketSlug")]
913    pub market_slug: Option<String>,
914    #[serde(rename = "txHash")]
915    pub tx_hash: Option<String>,
916    pub timestamp: String,
917}
918
919#[derive(Debug, Clone, Serialize, Deserialize)]
920pub struct MakerMatch {
921    pub account: String,
922    #[serde(rename = "orderId")]
923    pub order_id: String,
924    #[serde(rename = "matchedSize")]
925    pub matched_size: String,
926    pub price: String,
927}
928
929#[derive(Debug, Clone, Serialize, Deserialize)]
930#[serde(tag = "type")]
931pub enum PositionUpdate {
932    #[serde(rename = "AMM")]
933    Amm(AmmPositionData),
934    #[serde(rename = "CLOB")]
935    Clob(ClobPositionData),
936}
937
938#[derive(Debug, Clone, Serialize, Deserialize)]
939pub struct AmmPositionData {
940    pub account: String,
941    #[serde(rename = "marketAddress")]
942    pub market_address: String,
943    pub positions: Vec<AmmPosition>,
944}
945
946#[derive(Debug, Clone, Serialize, Deserialize)]
947pub struct AmmPosition {
948    #[serde(rename = "tokenId")]
949    pub token_id: String,
950    pub balance: String,
951    #[serde(rename = "outcomeIndex")]
952    pub outcome_index: u8,
953    #[serde(rename = "collateralOutOnSell")]
954    pub collateral_out_on_sell: String,
955}
956
957#[derive(Debug, Clone, Serialize, Deserialize)]
958pub struct ClobPositionData {
959    pub account: String,
960    #[serde(rename = "marketSlug")]
961    pub market_slug: String,
962    pub positions: Vec<ClobPosition>,
963    #[serde(rename = "tokenIds")]
964    pub token_ids: Vec<String>,
965}
966
967#[derive(Debug, Clone, Serialize, Deserialize)]
968pub struct ClobPosition {
969    #[serde(rename = "tokenId")]
970    pub token_id: String,
971    #[serde(rename = "ctfBalance")]
972    pub ctf_balance: String,
973    #[serde(rename = "averageFillPrice")]
974    pub average_fill_price: String,
975    #[serde(rename = "costBasis")]
976    pub cost_basis: String,
977    #[serde(rename = "marketValue")]
978    pub market_value: String,
979    #[serde(rename = "marketId")]
980    pub market_id: u64,
981}