Skip to main content

ib_flex/types/
common.rs

1//! Common enums used across FLEX statements
2
3use chrono::NaiveDate;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6
7/// Asset category (security type)
8///
9/// Maps to IB's AssetCategory field. Represents the type of financial instrument.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
11#[serde(rename_all = "UPPERCASE")]
12pub enum AssetCategory {
13    /// Stock
14    #[serde(rename = "STK")]
15    Stock,
16
17    /// Option
18    #[serde(rename = "OPT")]
19    Option,
20
21    /// Future
22    #[serde(rename = "FUT")]
23    Future,
24
25    /// Future Option
26    #[serde(rename = "FOP")]
27    FutureOption,
28
29    /// Cash/Forex
30    #[serde(rename = "CASH")]
31    Cash,
32
33    /// Bond
34    #[serde(rename = "BOND")]
35    Bond,
36
37    /// Treasury Bill (maturity < 1 year)
38    #[serde(rename = "BILL")]
39    Bill,
40
41    /// Commodity
42    #[serde(rename = "CMDTY")]
43    Commodity,
44
45    /// Contract for Difference
46    #[serde(rename = "CFD")]
47    Cfd,
48
49    /// Forex CFD
50    #[serde(rename = "FXCFD")]
51    ForexCfd,
52
53    /// Warrant
54    #[serde(rename = "WAR")]
55    Warrant,
56
57    /// Mutual Fund
58    #[serde(rename = "FUND")]
59    Fund,
60
61    /// Structured Product / Dutch Warrant / Indexed Option
62    #[serde(rename = "IOPT")]
63    StructuredProduct,
64
65    /// Combination / Basket order (spread, combo legs)
66    #[serde(rename = "BAG")]
67    Bag,
68
69    /// Cryptocurrency
70    #[serde(rename = "CRYPTO")]
71    Cryptocurrency,
72
73    /// Physical metals (gold, silver, etc.)
74    #[serde(rename = "METAL")]
75    Metal,
76
77    /// Exchange for Physical
78    #[serde(rename = "EFP")]
79    ExchangeForPhysical,
80
81    /// Event Contract
82    #[serde(rename = "EC")]
83    EventContract,
84
85    /// Index
86    #[serde(rename = "IND")]
87    Index,
88
89    /// Options on Futures (futures-style settlement)
90    #[serde(rename = "FSFOP")]
91    OptionsOnFutures,
92
93    /// Futures-style option
94    #[serde(rename = "FSOPT")]
95    OptionsFuturesStyle,
96
97    /// Unknown or unrecognized asset category
98    #[serde(other)]
99    Unknown,
100}
101
102/// Buy or Sell side
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
104#[serde(rename_all = "UPPERCASE")]
105pub enum BuySell {
106    /// Buy
107    #[serde(rename = "BUY")]
108    Buy,
109
110    /// Sell
111    #[serde(rename = "SELL")]
112    Sell,
113
114    /// Cancelled buy
115    #[serde(rename = "BUY (Ca.)")]
116    CancelBuy,
117
118    /// Cancelled sell
119    #[serde(rename = "SELL (Ca.)")]
120    CancelSell,
121
122    /// Unknown
123    #[serde(other)]
124    Unknown,
125}
126
127/// Open or Close indicator (for options/futures)
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
129pub enum OpenClose {
130    /// Opening trade
131    #[serde(rename = "O")]
132    Open,
133
134    /// Closing trade
135    #[serde(rename = "C")]
136    Close,
137
138    /// Close and open (same-day round trip)
139    #[serde(rename = "C;O")]
140    CloseOpen,
141
142    /// Unknown
143    #[serde(other)]
144    Unknown,
145}
146
147/// Order type
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
149#[serde(rename_all = "UPPERCASE")]
150pub enum OrderType {
151    /// Market order
152    #[serde(rename = "MKT")]
153    Market,
154
155    /// Limit order
156    #[serde(rename = "LMT")]
157    Limit,
158
159    /// Stop order
160    #[serde(rename = "STP")]
161    Stop,
162
163    /// Stop limit order. Flex XML emits the no-space form `STPLMT`; the
164    /// spaced TWS/API spelling is accepted as an alias.
165    #[serde(rename = "STPLMT", alias = "STP LMT")]
166    StopLimit,
167
168    /// Market on close
169    #[serde(rename = "MOC")]
170    MarketOnClose,
171
172    /// Limit on close
173    #[serde(rename = "LOC")]
174    LimitOnClose,
175
176    /// Market if touched
177    #[serde(rename = "MIT")]
178    MarketIfTouched,
179
180    /// Limit if touched
181    #[serde(rename = "LIT")]
182    LimitIfTouched,
183
184    /// Trailing stop
185    #[serde(rename = "TRAIL")]
186    TrailingStop,
187
188    /// Trailing limit. Flex XML emits the no-space form `TRAILLMT`; the
189    /// spaced TWS/API spelling is accepted as an alias.
190    #[serde(rename = "TRAILLMT", alias = "TRAIL LMT")]
191    TrailingLimit,
192
193    /// Mid-price order
194    #[serde(rename = "MIDPX")]
195    MidPrice,
196
197    /// Relative order
198    #[serde(rename = "REL")]
199    Relative,
200
201    /// Multiple order types (complex orders)
202    #[serde(rename = "MULTIPLE")]
203    Multiple,
204
205    /// Unknown or unrecognized order type
206    #[serde(other)]
207    Unknown,
208}
209
210/// Put or Call (for options)
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
212pub enum PutCall {
213    /// Put option
214    #[serde(rename = "P")]
215    Put,
216
217    /// Call option
218    #[serde(rename = "C")]
219    Call,
220
221    /// Unknown
222    #[serde(other)]
223    Unknown,
224}
225
226/// Long or Short position side
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
228pub enum LongShort {
229    /// Long position
230    Long,
231
232    /// Short position
233    Short,
234
235    /// Unknown
236    #[serde(other)]
237    Unknown,
238}
239
240/// Transaction type for trades
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
242pub enum TradeType {
243    /// Exchange trade
244    ExchTrade,
245
246    /// Book trade
247    BookTrade,
248
249    /// Delivery vs Payment trade
250    DvpTrade,
251
252    /// Fractional share trade
253    FracShare,
254
255    /// Fractional share cancellation
256    FracShareCancel,
257
258    /// Manual adjustment
259    Adjustment,
260
261    /// Trade correction
262    TradeCorrect,
263
264    /// Trade cancellation
265    TradeCancel,
266
267    /// IBKR trade
268    IBKRTrade,
269
270    /// Unknown
271    #[serde(other)]
272    Unknown,
273}
274
275/// Cash transaction action type
276///
277/// Represents the type of cash transaction (dividend, interest, fee, etc.).
278/// This enum is used by the `CashTransaction` struct to classify cash activity
279/// in the account statement.
280///
281/// **XML Mapping**: Maps to the `type` attribute in `<CashTransaction>` elements.
282#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
283pub enum CashTransactionType {
284    /// Deposits and withdrawals
285    #[serde(rename = "Deposits & Withdrawals")]
286    DepositsWithdrawals,
287
288    /// Dividend payments
289    Dividends,
290
291    /// Withholding tax
292    WithholdingTax,
293
294    /// Broker interest paid
295    #[serde(rename = "Broker Interest Paid")]
296    BrokerInterestPaid,
297
298    /// Broker interest received
299    #[serde(rename = "Broker Interest Received")]
300    BrokerInterestReceived,
301
302    /// Bond interest received
303    #[serde(rename = "Bond Interest Received")]
304    BondInterestReceived,
305
306    /// Bond interest paid
307    #[serde(rename = "Bond Interest Paid")]
308    BondInterestPaid,
309
310    /// Bond interest (generic)
311    #[serde(rename = "Bond Interest")]
312    BondInterest,
313
314    /// Payment in lieu of dividends
315    #[serde(rename = "Payment In Lieu Of Dividends")]
316    PaymentInLieuOfDividends,
317
318    /// Other fees
319    #[serde(rename = "Other Fees")]
320    OtherFees,
321
322    /// Commission adjustments
323    #[serde(rename = "Commission Adjustments")]
324    CommissionAdjustments,
325
326    /// Advisor fees
327    #[serde(rename = "Advisor Fees")]
328    AdvisorFees,
329
330    /// Cash receipts
331    #[serde(rename = "Cash Receipts")]
332    CashReceipts,
333
334    /// Fees
335    Fees,
336
337    /// Unknown type
338    #[serde(other)]
339    Unknown,
340}
341
342/// Corporate action reorganization type
343///
344/// Represents the type of corporate action (split, merger, spinoff, etc.).
345/// This enum is used by the `CorporateAction` struct to classify corporate events
346/// that affect security positions and holdings.
347///
348/// **XML Mapping**: Maps to the `type` attribute in `<CorporateAction>` elements.
349#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
350pub enum CorporateActionType {
351    /// Stock split (forward split)
352    #[serde(rename = "Stock Split")]
353    StockSplit,
354
355    /// Forward split (issue)
356    #[serde(rename = "Forward Split (Issue)")]
357    ForwardSplitIssue,
358
359    /// Forward split
360    #[serde(rename = "Forward Split")]
361    ForwardSplit,
362
363    /// Reverse split
364    #[serde(rename = "Reverse Split")]
365    ReverseSplit,
366
367    /// Merger
368    Merger,
369
370    /// Spinoff
371    Spinoff,
372
373    /// Contract spinoff
374    #[serde(rename = "Contract Spinoff")]
375    ContractSpinoff,
376
377    /// Stock dividend
378    #[serde(rename = "Stock Dividend")]
379    StockDividend,
380
381    /// Cash dividend
382    #[serde(rename = "Cash Dividend")]
383    CashDividend,
384
385    /// Choice dividend
386    #[serde(rename = "Choice Dividend")]
387    ChoiceDividend,
388
389    /// Choice dividend (delivery)
390    #[serde(rename = "Choice Dividend (Delivery)")]
391    ChoiceDivDelivery,
392
393    /// Choice dividend (issue)
394    #[serde(rename = "Choice Dividend (Issue)")]
395    ChoiceDivIssue,
396
397    /// Dividend rights issue
398    #[serde(rename = "Dividend Rights Issue")]
399    DivRightsIssue,
400
401    /// Expired dividend right
402    #[serde(rename = "Expired Dividend Right")]
403    ExpiredDivRight,
404
405    /// Delisted
406    Delisted,
407
408    /// Delist (worthless)
409    #[serde(rename = "Delist (Worthless)")]
410    DelistWorthless,
411
412    /// Name change
413    #[serde(rename = "Name Change")]
414    NameChange,
415
416    /// Symbol change
417    #[serde(rename = "Symbol Change")]
418    SymbolChange,
419
420    /// Issue change
421    #[serde(rename = "Issue Change")]
422    IssueChange,
423
424    /// Bond conversion
425    #[serde(rename = "Bond Conversion")]
426    BondConversion,
427
428    /// Bond maturity
429    #[serde(rename = "Bond Maturity")]
430    BondMaturity,
431
432    /// T-Bill maturity
433    #[serde(rename = "T-Bill Maturity")]
434    TBillMaturity,
435
436    /// Convertible issue
437    #[serde(rename = "Convertible Issue")]
438    ConvertibleIssue,
439
440    /// Coupon payment
441    #[serde(rename = "Coupon Payment")]
442    CouponPayment,
443
444    /// Contract consolidation
445    #[serde(rename = "Contract Consolidation")]
446    ContractConsolidation,
447
448    /// Contract split
449    #[serde(rename = "Contract Split")]
450    ContractSplit,
451
452    /// Contract termination
453    #[serde(rename = "CFD Termination")]
454    CfdTermination,
455
456    /// Fee allocation
457    #[serde(rename = "Fee Allocation")]
458    FeeAllocation,
459
460    /// Rights issue
461    #[serde(rename = "Rights Issue")]
462    RightsIssue,
463
464    /// Subscribe rights
465    #[serde(rename = "Subscribe Rights")]
466    SubscribeRights,
467
468    /// Tender
469    Tender,
470
471    /// Tender (issue)
472    #[serde(rename = "Tender (Issue)")]
473    TenderIssue,
474
475    /// Proxy vote
476    #[serde(rename = "Proxy Vote")]
477    ProxyVote,
478
479    /// Generic voluntary
480    #[serde(rename = "Generic Voluntary")]
481    GenericVoluntary,
482
483    /// Asset purchase
484    #[serde(rename = "Asset Purchase")]
485    AssetPurchase,
486
487    /// Purchase (issue)
488    #[serde(rename = "Purchase (Issue)")]
489    PurchaseIssue,
490
491    /// Unknown
492    #[serde(other)]
493    Unknown,
494}
495
496/// Option action type
497#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
498pub enum OptionAction {
499    /// Assignment
500    Assignment,
501
502    /// Exercise
503    Exercise,
504
505    /// Expiration
506    Expiration,
507
508    /// Expire (alternate form)
509    Expire,
510
511    /// Cash settlement
512    #[serde(rename = "Cash Settlement")]
513    CashSettlement,
514
515    /// Buy to open/close
516    Buy,
517
518    /// Sell to open/close
519    Sell,
520
521    /// Unknown
522    #[serde(other)]
523    Unknown,
524}
525
526/// Transfer type
527#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
528pub enum TransferType {
529    /// ACATS transfer (older statements abbreviate this as `ACAT`)
530    #[serde(rename = "ACATS", alias = "ACAT")]
531    ACATS,
532
533    /// ATON transfer
534    ATON,
535
536    /// Free of payment
537    FOP,
538
539    /// Internal transfer
540    INTERNAL,
541
542    /// Intercompany transfer
543    INTERCOMPANY,
544
545    /// Over-the-counter transfer
546    OTC,
547
548    /// Delivery vs payment
549    DVP,
550
551    /// Unknown
552    #[serde(other)]
553    Unknown,
554}
555
556/// Transaction code
557///
558/// Comprehensive list of IB transaction classification codes.
559/// These codes appear in `notes` fields and can be combined (e.g., "C;W" for closing + wash sale).
560/// They provide critical context for tax reporting and trade classification.
561#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
562pub enum TransactionCode {
563    /// Assignment - Option assignment triggering stock delivery
564    #[serde(rename = "A")]
565    Assignment,
566
567    /// Adjustment - Manual adjustment affecting cost basis
568    #[serde(rename = "Adj")]
569    Adjustment,
570
571    /// Allocation - Trade allocation to sub-account (master/sub allocation)
572    #[serde(rename = "Al")]
573    Allocation,
574
575    /// Auto Exercise - Automatic exercise (dividend-related, exercise before ex-div)
576    #[serde(rename = "Ae")]
577    AutoExercise,
578
579    /// Auto FX - AutoFX currency conversion for settlement
580    #[serde(rename = "Af")]
581    AutoFx,
582
583    /// Away Trade - Trade executed away from IB (third-party execution)
584    #[serde(rename = "Aw")]
585    AwayTrade,
586
587    /// Buy-In - Forced purchase to cover failed delivery (forced short cover)
588    #[serde(rename = "B")]
589    BuyIn,
590
591    /// Borrow - Securities borrowing fee (lending charge)
592    #[serde(rename = "Bo")]
593    BorrowFee,
594
595    /// Cancellation - Trade cancelled/busted (trade reversed)
596    #[serde(rename = "Ca")]
597    Cancelled,
598
599    /// Closing - Closing trade (reduces position)
600    #[serde(rename = "C")]
601    Closing,
602
603    /// Cash Delivery - Cash delivery for exercise (cash vs physical)
604    #[serde(rename = "Cd")]
605    CashDelivery,
606
607    /// Complex Position - Complex/combo position (multi-leg strategy)
608    #[serde(rename = "Cp")]
609    ComplexPosition,
610
611    /// Correction - Trade correction (amended execution)
612    #[serde(rename = "Cr")]
613    Correction,
614
615    /// Crossing - Internal IB cross (matched internally)
616    #[serde(rename = "Cs")]
617    Crossing,
618
619    /// Dual Agent - IB dual agent capacity (disclosed dual role)
620    #[serde(rename = "D")]
621    DualAgent,
622
623    /// ETF - ETF creation/redemption (in-kind basket)
624    #[serde(rename = "Et")]
625    Etf,
626
627    /// Expired - From expired position (option/warrant expiry)
628    #[serde(rename = "Ex")]
629    Expired,
630
631    /// Exercise - Option exercise (long option exercised)
632    #[serde(rename = "O")]
633    Exercise,
634
635    /// Guaranteed - Guaranteed account segment (special margin)
636    #[serde(rename = "G")]
637    Guaranteed,
638
639    /// Highest Cost - Highest cost tax lot (tax lot selection)
640    #[serde(rename = "Hc")]
641    HighestCost,
642
643    /// HF Investment - Hedge fund investment (fund subscription)
644    #[serde(rename = "Hi")]
645    HfInvestment,
646
647    /// HF Redemption - Hedge fund redemption (fund redemption)
648    #[serde(rename = "Hr")]
649    HfRedemption,
650
651    /// Internal - Internal transfer (between IB accounts)
652    #[serde(rename = "I")]
653    InternalTransfer,
654
655    /// Affiliate - Affiliate execution (related party trade)
656    #[serde(rename = "Ia")]
657    Affiliate,
658
659    /// Investor - Investment from investor (capital contribution)
660    #[serde(rename = "Iv")]
661    Investor,
662
663    /// Margin Violation - Liquidation due to margin (forced liquidation)
664    #[serde(rename = "L")]
665    MarginLiquidation,
666
667    /// LIFO - LIFO tax lot (tax lot selection)
668    #[serde(rename = "Li")]
669    Lifo,
670
671    /// Loan - Securities lending income (lending income)
672    #[serde(rename = "Ln")]
673    Loan,
674
675    /// Long-Term - Long-term gain/loss (holding > 1 year)
676    #[serde(rename = "Lt")]
677    LongTermGain,
678
679    /// Manual - Manual IB entry (manual adjustment)
680    #[serde(rename = "M")]
681    ManualEntry,
682
683    /// Max Loss - Maximize losses (tax optimization)
684    #[serde(rename = "Ml")]
685    MaxLoss,
686
687    /// Min LT Gain - Minimize long-term gain (tax optimization)
688    #[serde(rename = "Mn")]
689    MinLongTermGain,
690
691    /// Max ST Gain - Maximize short-term gain (tax optimization)
692    #[serde(rename = "Ms")]
693    MaxShortTermGain,
694
695    /// Min ST Gain - Minimize short-term gain (tax optimization)
696    #[serde(rename = "Mi")]
697    MinShortTermGain,
698
699    /// Manual Exercise - Manual exercise (discretionary exercise)
700    #[serde(rename = "Mx")]
701    ManualExercise,
702
703    /// Opening - Opening trade (new position)
704    #[serde(rename = "P")]
705    Opening,
706
707    /// Partial - Partial execution (partial fill)
708    #[serde(rename = "Pt")]
709    Partial,
710
711    /// Frac Riskless - Fractional riskless principal (fractional share method)
712    #[serde(rename = "Fr")]
713    FracRiskless,
714
715    /// Frac Principal - Fractional principal (fractional share method)
716    #[serde(rename = "Fp")]
717    FracPrincipal,
718
719    /// Price Improvement - Better than quoted (price improvement)
720    #[serde(rename = "Pi")]
721    PriceImprovement,
722
723    /// Post Accrual - Accrual posting (accrual entry)
724    #[serde(rename = "Pa")]
725    PostAccrual,
726
727    /// Principal - IB principal execution (principal trade)
728    #[serde(rename = "Pr")]
729    Principal,
730
731    /// Reinvestment - Dividend reinvestment (DRIP)
732    #[serde(rename = "Re")]
733    Reinvestment,
734
735    /// Redemption - Capital distribution (fund redemption)
736    #[serde(rename = "Rd")]
737    Redemption,
738
739    /// Reopen - Position reopened (wash sale reopen)
740    #[serde(rename = "R")]
741    Reopen,
742
743    /// Reverse - Accrual reversal (accounting reversal)
744    #[serde(rename = "Rv")]
745    Reverse,
746
747    /// Reimbursement - Fee refund (expense refund)
748    #[serde(rename = "Ri")]
749    Reimbursement,
750
751    /// Solicited IB - IB solicited order (IB-initiated)
752    #[serde(rename = "Si")]
753    SolicitedIb,
754
755    /// Specific Lot - Specific tax lot (tax lot selection)
756    #[serde(rename = "Sp")]
757    SpecificLot,
758
759    /// Solicited Other - Third-party solicited (broker-solicited)
760    #[serde(rename = "So")]
761    SolicitedOther,
762
763    /// Short Settlement - T+0 or T+1 settlement (accelerated settle)
764    #[serde(rename = "Ss")]
765    ShortSettlement,
766
767    /// Short-Term - Short-term gain/loss (holding <= 1 year)
768    #[serde(rename = "St")]
769    ShortTermGain,
770
771    /// Stock Yield - Stock yield eligible (lending eligible)
772    #[serde(rename = "Sy")]
773    StockYield,
774
775    /// Transfer - Position transfer
776    #[serde(rename = "T")]
777    Transfer,
778
779    /// Wash Sale - Wash sale (loss disallowed)
780    #[serde(rename = "W")]
781    WashSale,
782
783    /// Unknown code
784    #[serde(other)]
785    Unknown,
786}
787
788/// Direction (To/From)
789#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
790pub enum ToFrom {
791    /// To
792    To,
793
794    /// From
795    From,
796
797    /// Unknown
798    #[serde(other)]
799    Unknown,
800}
801
802/// Direction (In/Out)
803#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
804pub enum InOut {
805    /// Incoming
806    IN,
807
808    /// Outgoing
809    OUT,
810
811    /// Unknown
812    #[serde(other)]
813    Unknown,
814}
815
816/// Delivered or Received
817#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
818pub enum DeliveredReceived {
819    /// Delivered
820    Delivered,
821
822    /// Received
823    Received,
824
825    /// Unknown
826    #[serde(other)]
827    Unknown,
828}
829
830/// Level of detail for reporting
831///
832/// Specifies the granularity of data in FLEX reports.
833/// Used by `Trade`, `Position`, and `CashTransaction` structs to indicate
834/// the level of detail requested in the FLEX query.
835///
836/// **XML Mapping**: Maps to the `levelOfDetail` attribute in various elements.
837#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
838pub enum LevelOfDetail {
839    /// Summary level - aggregated data with minimal details
840    Summary,
841
842    /// Detail level - standard reporting with all key fields
843    Detail,
844
845    /// Execution level - detailed execution information including time and venue
846    Execution,
847
848    /// Lot level - tax lot level details for cost basis tracking
849    Lot,
850
851    /// Unknown or unrecognized level of detail
852    #[serde(other)]
853    Unknown,
854}
855
856/// Security identifier type
857///
858/// Specifies the type of security identifier used in the `securityID` field.
859/// Different identifiers are used in different markets and contexts.
860///
861/// **XML Mapping**: Maps to the `securityIDType` attribute in various elements.
862///
863/// **Used by**: `Trade`, `SecurityInfo`
864#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
865pub enum SecurityIdType {
866    /// CUSIP - Committee on Uniform Securities Identification Procedures
867    /// 9-character alphanumeric identifier for North American securities
868    #[serde(rename = "CUSIP")]
869    Cusip,
870
871    /// ISIN - International Securities Identification Number
872    /// 12-character alphanumeric code (ISO 6166 standard)
873    #[serde(rename = "ISIN")]
874    Isin,
875
876    /// FIGI - Financial Instrument Global Identifier
877    /// 12-character alphanumeric identifier (Bloomberg Open Symbology)
878    #[serde(rename = "FIGI")]
879    Figi,
880
881    /// SEDOL - Stock Exchange Daily Official List
882    /// 7-character alphanumeric identifier for UK and Irish securities
883    #[serde(rename = "SEDOL")]
884    Sedol,
885
886    /// Unknown or unrecognized security ID type
887    #[serde(other)]
888    Unknown,
889}
890
891/// Security sub-category
892///
893/// Provides additional classification for securities beyond the basic asset category.
894/// Most commonly used for stocks to distinguish between common shares, ETFs, ADRs, REITs, etc.
895///
896/// **XML Mapping**: Maps to the `subCategory` attribute in various elements.
897///
898/// **Used by**: `Trade`, `Position`, `SecurityInfo`
899#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
900pub enum SubCategory {
901    /// Exchange-traded fund
902    #[serde(rename = "ETF")]
903    Etf,
904
905    /// American Depositary Receipt - represents foreign company shares traded in US
906    #[serde(rename = "ADR")]
907    Adr,
908
909    /// Real Estate Investment Trust
910    #[serde(rename = "REIT")]
911    Reit,
912
913    /// Preferred stock
914    #[serde(rename = "Preferred", alias = "PREFERRED")]
915    Preferred,
916
917    /// Common stock
918    #[serde(rename = "Common", alias = "COMMON")]
919    Common,
920
921    /// Depositary Receipt (general)
922    #[serde(rename = "DR")]
923    DepositaryReceipt,
924
925    /// Global Depositary Receipt
926    #[serde(rename = "GDR")]
927    Gdr,
928
929    /// Limited Partnership
930    #[serde(rename = "LP")]
931    LimitedPartnership,
932
933    /// Master Limited Partnership
934    #[serde(rename = "MLP")]
935    MasterLimitedPartnership,
936
937    /// Right (subscription right)
938    #[serde(rename = "Right")]
939    Right,
940
941    /// Unit (combination of securities)
942    #[serde(rename = "Unit")]
943    Unit,
944
945    /// When-Issued security
946    #[serde(rename = "WI")]
947    WhenIssued,
948
949    /// Tracking stock
950    #[serde(rename = "Tracking")]
951    Tracking,
952
953    /// Closed-end fund
954    #[serde(rename = "CEF")]
955    ClosedEndFund,
956
957    /// Unknown or unrecognized sub-category
958    #[serde(other)]
959    Unknown,
960}
961
962/// Derivative instrument information
963///
964/// Contains structured information about derivative contracts (options, futures, warrants).
965/// This enum consolidates derivative-specific fields based on the instrument type.
966///
967/// **Used by**: `Trade`, `Position`
968#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
969#[serde(tag = "type")]
970pub enum DerivativeInfo {
971    /// Option contract (equity or index option)
972    ///
973    /// Standard option giving the right (but not obligation) to buy or sell
974    /// an underlying asset at a specified strike price by the expiration date.
975    Option {
976        /// Strike price - price at which the option can be exercised
977        strike: Decimal,
978
979        /// Expiration date - last date the option can be exercised
980        expiry: NaiveDate,
981
982        /// Put or Call - right to sell (Put) or buy (Call)
983        #[serde(rename = "putCall")]
984        put_call: PutCall,
985
986        /// Symbol of the underlying security (e.g., "AAPL" for Apple stock)
987        #[serde(rename = "underlyingSymbol")]
988        underlying_symbol: String,
989
990        /// IB contract ID of the underlying security
991        #[serde(rename = "underlyingConid")]
992        underlying_conid: Option<String>,
993    },
994
995    /// Future contract
996    ///
997    /// Agreement to buy or sell an asset at a predetermined price
998    /// on a specified future date.
999    Future {
1000        /// Expiration date - settlement date for the futures contract
1001        expiry: NaiveDate,
1002
1003        /// Symbol of the underlying asset
1004        #[serde(rename = "underlyingSymbol")]
1005        underlying_symbol: String,
1006
1007        /// IB contract ID of the underlying asset
1008        #[serde(rename = "underlyingConid")]
1009        underlying_conid: Option<String>,
1010    },
1011
1012    /// Future option (option on a futures contract)
1013    ///
1014    /// Option where the underlying asset is a futures contract rather than a stock.
1015    FutureOption {
1016        /// Strike price for the option
1017        strike: Decimal,
1018
1019        /// Expiration date of the option
1020        expiry: NaiveDate,
1021
1022        /// Put or Call
1023        #[serde(rename = "putCall")]
1024        put_call: PutCall,
1025
1026        /// Symbol of the underlying futures contract
1027        #[serde(rename = "underlyingSymbol")]
1028        underlying_symbol: String,
1029
1030        /// IB contract ID of the underlying futures
1031        #[serde(rename = "underlyingConid")]
1032        underlying_conid: Option<String>,
1033    },
1034
1035    /// Warrant
1036    ///
1037    /// Long-term option-like security issued by a company, typically
1038    /// with longer expiration periods than standard options.
1039    Warrant {
1040        /// Strike price (if applicable)
1041        strike: Option<Decimal>,
1042
1043        /// Expiration date (if applicable)
1044        expiry: Option<NaiveDate>,
1045
1046        /// Symbol of the underlying security (if applicable)
1047        #[serde(rename = "underlyingSymbol")]
1048        underlying_symbol: Option<String>,
1049    },
1050}
1051
1052#[cfg(test)]
1053mod tests {
1054    use super::*;
1055
1056    #[test]
1057    fn test_asset_category_basic() {
1058        // Test enum construction and comparison
1059        let stock = AssetCategory::Stock;
1060        assert_eq!(stock, AssetCategory::Stock);
1061        assert_ne!(stock, AssetCategory::Option);
1062    }
1063
1064    #[test]
1065    fn test_buy_sell_basic() {
1066        // Test enum construction and comparison
1067        let buy = BuySell::Buy;
1068        assert_eq!(buy, BuySell::Buy);
1069        assert_ne!(buy, BuySell::Sell);
1070    }
1071
1072    #[test]
1073    fn test_open_close_basic() {
1074        let open = OpenClose::Open;
1075        assert_eq!(open, OpenClose::Open);
1076        assert_ne!(open, OpenClose::Close);
1077    }
1078
1079    #[test]
1080    fn test_put_call_basic() {
1081        let call = PutCall::Call;
1082        assert_eq!(call, PutCall::Call);
1083        assert_ne!(call, PutCall::Put);
1084    }
1085}