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