schwab_sdk/orders/enums.rs
1//! String-valued enums shared across the orders request/response shapes.
2//!
3//! Every variant has a `Unknown(String)` catch-all so wire values added by
4//! Schwab after this crate was published deserialize cleanly. The
5//! `string_enum!` macro lives in [`crate::macros`].
6
7use crate::macros::string_enum;
8
9string_enum! {
10 /// Which trading session the order is valid in.
11 Session {
12 /// Regular session.
13 Normal = "NORMAL",
14 /// Pre-market session.
15 Am = "AM",
16 /// Post-market session.
17 Pm = "PM",
18 /// All sessions; Schwab routes wherever the order is fillable.
19 Seamless = "SEAMLESS",
20 }
21}
22
23string_enum! {
24 /// Time-in-force for an order.
25 Duration {
26 /// Expires at the end of the regular session.
27 Day = "DAY",
28 /// Stays open until filled or explicitly cancelled.
29 GoodTillCancel = "GOOD_TILL_CANCEL",
30 /// Fill the entire order immediately or cancel it.
31 FillOrKill = "FILL_OR_KILL",
32 /// Fill whatever can fill immediately; cancel the rest.
33 ImmediateOrCancel = "IMMEDIATE_OR_CANCEL",
34 /// Expires at the end of the trading week.
35 EndOfWeek = "END_OF_WEEK",
36 /// Expires at the end of the trading month.
37 EndOfMonth = "END_OF_MONTH",
38 /// Expires at the end of the next trading month.
39 NextEndOfMonth = "NEXT_END_OF_MONTH",
40 /// Schwab sent the literal string `"UNKNOWN"`.
41 UnknownSchwab = "UNKNOWN",
42 }
43}
44
45string_enum! {
46 /// How the order's fill price is determined.
47 OrderType {
48 /// Fill at the best available market price.
49 Market = "MARKET",
50 /// Fill at the specified price or better.
51 Limit = "LIMIT",
52 /// Becomes a market order once the stop is touched.
53 Stop = "STOP",
54 /// Becomes a limit order once the stop is touched.
55 StopLimit = "STOP_LIMIT",
56 /// Stop that follows the market at a fixed offset.
57 TrailingStop = "TRAILING_STOP",
58 /// Cabinet (zero-premium) options trade.
59 Cabinet = "CABINET",
60 /// Limit order priced away from the inside market.
61 NonMarketable = "NON_MARKETABLE",
62 /// Market order executed in the closing auction.
63 MarketOnClose = "MARKET_ON_CLOSE",
64 /// Exercise of a long option.
65 Exercise = "EXERCISE",
66 /// Trailing stop that becomes a limit order once triggered.
67 TrailingStopLimit = "TRAILING_STOP_LIMIT",
68 /// Multi-leg order with a net debit price.
69 NetDebit = "NET_DEBIT",
70 /// Multi-leg order with a net credit price.
71 NetCredit = "NET_CREDIT",
72 /// Multi-leg order with a net price of zero.
73 NetZero = "NET_ZERO",
74 /// Limit order executed in the closing auction.
75 LimitOnClose = "LIMIT_ON_CLOSE",
76 /// Schwab sent the literal string `"UNKNOWN"`.
77 UnknownSchwab = "UNKNOWN",
78 }
79}
80
81string_enum! {
82 /// Top-level structure of an order envelope.
83 OrderStrategyType {
84 /// Single-leg order.
85 Single = "SINGLE",
86 /// Cancel an existing order.
87 Cancel = "CANCEL",
88 /// Recall an existing order.
89 Recall = "RECALL",
90 /// Pair-trade strategy.
91 Pair = "PAIR",
92 /// Flatten an account or position.
93 Flatten = "FLATTEN",
94 /// Two-day swap strategy.
95 TwoDaySwap = "TWO_DAY_SWAP",
96 /// Blast-all (send to multiple venues).
97 BlastAll = "BLAST_ALL",
98 /// One-cancels-other.
99 Oco = "OCO",
100 /// One-triggers-other.
101 Trigger = "TRIGGER",
102 }
103}
104
105string_enum! {
106 /// Multi-leg option strategy shape.
107 ComplexOrderStrategyType {
108 /// Not a complex strategy.
109 None = "NONE",
110 /// Covered call / covered put.
111 Covered = "COVERED",
112 /// Vertical spread.
113 Vertical = "VERTICAL",
114 /// Back-ratio spread.
115 BackRatio = "BACK_RATIO",
116 /// Calendar (horizontal) spread.
117 Calendar = "CALENDAR",
118 /// Diagonal spread.
119 Diagonal = "DIAGONAL",
120 /// Straddle.
121 Straddle = "STRADDLE",
122 /// Strangle.
123 Strangle = "STRANGLE",
124 /// Synthetic collar.
125 CollarSynthetic = "COLLAR_SYNTHETIC",
126 /// Butterfly.
127 Butterfly = "BUTTERFLY",
128 /// Condor.
129 Condor = "CONDOR",
130 /// Iron condor.
131 IronCondor = "IRON_CONDOR",
132 /// Vertical roll.
133 VerticalRoll = "VERTICAL_ROLL",
134 /// Collar paired with the underlying stock.
135 CollarWithStock = "COLLAR_WITH_STOCK",
136 /// Double diagonal.
137 DoubleDiagonal = "DOUBLE_DIAGONAL",
138 /// Unbalanced butterfly.
139 UnbalancedButterfly = "UNBALANCED_BUTTERFLY",
140 /// Unbalanced condor.
141 UnbalancedCondor = "UNBALANCED_CONDOR",
142 /// Unbalanced iron condor.
143 UnbalancedIronCondor = "UNBALANCED_IRON_CONDOR",
144 /// Unbalanced vertical roll.
145 UnbalancedVerticalRoll = "UNBALANCED_VERTICAL_ROLL",
146 /// Mutual-fund swap.
147 MutualFundSwap = "MUTUAL_FUND_SWAP",
148 /// Custom strategy that does not match a named pattern.
149 Custom = "CUSTOM",
150 }
151}
152
153string_enum! {
154 /// Trade direction / intent for an order leg.
155 Instruction {
156 /// Buy.
157 Buy = "BUY",
158 /// Sell.
159 Sell = "SELL",
160 /// Buy shares to cover an existing short position.
161 BuyToCover = "BUY_TO_COVER",
162 /// Open a new short position.
163 SellShort = "SELL_SHORT",
164 /// Open a long option position.
165 BuyToOpen = "BUY_TO_OPEN",
166 /// Close a long option position.
167 BuyToClose = "BUY_TO_CLOSE",
168 /// Open a short option position.
169 SellToOpen = "SELL_TO_OPEN",
170 /// Close a short option position.
171 SellToClose = "SELL_TO_CLOSE",
172 /// Mutual-fund exchange.
173 Exchange = "EXCHANGE",
174 /// Short sale exempt from the SEC short-sale price test.
175 SellShortExempt = "SELL_SHORT_EXEMPT",
176 }
177}
178
179string_enum! {
180 /// Lifecycle status of an order in Schwab's system.
181 ApiOrderStatus {
182 /// Waiting for a parent order in a multi-leg strategy.
183 AwaitingParentOrder = "AWAITING_PARENT_ORDER",
184 /// Waiting for a trigger condition to be met.
185 AwaitingCondition = "AWAITING_CONDITION",
186 /// Waiting for a stop condition to be met.
187 AwaitingStopCondition = "AWAITING_STOP_CONDITION",
188 /// Pending manual review by a Schwab rep.
189 AwaitingManualReview = "AWAITING_MANUAL_REVIEW",
190 /// Schwab accepted the order.
191 Accepted = "ACCEPTED",
192 /// Awaiting an "unable to route" outcome.
193 AwaitingUrOut = "AWAITING_UR_OUT",
194 /// Activation pending (e.g. for stop / trigger orders).
195 PendingActivation = "PENDING_ACTIVATION",
196 /// Queued for routing to a venue.
197 Queued = "QUEUED",
198 /// Live at the venue and working for a fill.
199 Working = "WORKING",
200 /// Schwab or the venue rejected the order.
201 Rejected = "REJECTED",
202 /// Cancel request submitted, not yet confirmed.
203 PendingCancel = "PENDING_CANCEL",
204 /// Cancelled.
205 Canceled = "CANCELED",
206 /// Replace request submitted, not yet confirmed.
207 PendingReplace = "PENDING_REPLACE",
208 /// Replaced; the original order is no longer valid.
209 Replaced = "REPLACED",
210 /// Filled in full.
211 Filled = "FILLED",
212 /// Expired (e.g. unfilled day order at session end).
213 Expired = "EXPIRED",
214 /// Newly submitted, not yet acked.
215 New = "NEW",
216 /// Waiting for a scheduled release time.
217 AwaitingReleaseTime = "AWAITING_RELEASE_TIME",
218 /// Awaiting venue acknowledgement.
219 PendingAcknowledgement = "PENDING_ACKNOWLEDGEMENT",
220 /// Recall request submitted.
221 PendingRecall = "PENDING_RECALL",
222 /// Schwab sent the literal string `"UNKNOWN"`.
223 UnknownSchwab = "UNKNOWN",
224 }
225}
226
227string_enum! {
228 /// What price feed triggers a stop order.
229 StopType {
230 /// Default stop type for the venue.
231 Standard = "STANDARD",
232 /// Trigger when the bid touches the stop.
233 Bid = "BID",
234 /// Trigger when the ask touches the stop.
235 Ask = "ASK",
236 /// Trigger when the last trade touches the stop.
237 Last = "LAST",
238 /// Trigger when the mark touches the stop.
239 Mark = "MARK",
240 }
241}
242
243string_enum! {
244 /// Reference price for a linked stop.
245 StopPriceLinkBasis {
246 /// Caller supplies the stop price directly.
247 Manual = "MANUAL",
248 /// Tied to the order's base price.
249 Base = "BASE",
250 /// Tied to the order's trigger price.
251 Trigger = "TRIGGER",
252 /// Tied to the instrument's last trade.
253 Last = "LAST",
254 /// Tied to the bid.
255 Bid = "BID",
256 /// Tied to the ask.
257 Ask = "ASK",
258 /// Tied to the ask / bid spread.
259 AskBid = "ASK_BID",
260 /// Tied to the mark.
261 Mark = "MARK",
262 /// Tied to an averaged reference price.
263 Average = "AVERAGE",
264 }
265}
266
267string_enum! {
268 /// How the stop offset is interpreted.
269 StopPriceLinkType {
270 /// Absolute dollar offset.
271 Value = "VALUE",
272 /// Percentage offset.
273 Percent = "PERCENT",
274 /// Offset measured in ticks.
275 Tick = "TICK",
276 }
277}
278
279string_enum! {
280 /// Reference price for a linked limit.
281 PriceLinkBasis {
282 /// Caller supplies the limit price directly.
283 Manual = "MANUAL",
284 /// Tied to the order's base price.
285 Base = "BASE",
286 /// Tied to the order's trigger price.
287 Trigger = "TRIGGER",
288 /// Tied to the instrument's last trade.
289 Last = "LAST",
290 /// Tied to the bid.
291 Bid = "BID",
292 /// Tied to the ask.
293 Ask = "ASK",
294 /// Tied to the ask / bid spread.
295 AskBid = "ASK_BID",
296 /// Tied to the mark.
297 Mark = "MARK",
298 /// Tied to an averaged reference price.
299 Average = "AVERAGE",
300 }
301}
302
303string_enum! {
304 /// How the limit offset is interpreted.
305 PriceLinkType {
306 /// Absolute dollar offset.
307 Value = "VALUE",
308 /// Percentage offset.
309 Percent = "PERCENT",
310 /// Offset measured in ticks.
311 Tick = "TICK",
312 }
313}
314
315string_enum! {
316 /// Tax-lot relief method to apply when closing positions.
317 TaxLotMethod {
318 /// First-in, first-out.
319 Fifo = "FIFO",
320 /// Last-in, first-out.
321 Lifo = "LIFO",
322 /// Highest cost basis first.
323 HighCost = "HIGH_COST",
324 /// Lowest cost basis first.
325 LowCost = "LOW_COST",
326 /// Average cost basis.
327 AverageCost = "AVERAGE_COST",
328 /// Caller-specified lot.
329 SpecificLot = "SPECIFIC_LOT",
330 /// Schwab's loss-harvester selection.
331 LossHarvester = "LOSS_HARVESTER",
332 }
333}
334
335string_enum! {
336 /// Special-instruction flag attached to an order.
337 SpecialInstruction {
338 /// All-or-none: do not fill partially.
339 AllOrNone = "ALL_OR_NONE",
340 /// Do-not-reduce on ex-dividend day.
341 DoNotReduce = "DO_NOT_REDUCE",
342 /// Both all-or-none and do-not-reduce.
343 AllOrNoneDoNotReduce = "ALL_OR_NONE_DO_NOT_REDUCE",
344 }
345}
346
347string_enum! {
348 /// Explicit venue the order should be routed to.
349 RequestedDestination {
350 /// INET (Nasdaq's ECN).
351 Inet = "INET",
352 /// NYSE Arca ECN.
353 EcnArca = "ECN_ARCA",
354 /// Cboe.
355 Cboe = "CBOE",
356 /// NYSE American (formerly AMEX).
357 Amex = "AMEX",
358 /// Philadelphia Stock Exchange.
359 Phlx = "PHLX",
360 /// International Securities Exchange.
361 Ise = "ISE",
362 /// Boston Options Exchange.
363 Box_ = "BOX",
364 /// New York Stock Exchange.
365 Nyse = "NYSE",
366 /// Nasdaq.
367 Nasdaq = "NASDAQ",
368 /// BATS Global Markets.
369 Bats = "BATS",
370 /// Cboe C2.
371 C2 = "C2",
372 /// Let Schwab choose the venue.
373 Auto = "AUTO",
374 }
375}
376
377string_enum! {
378 /// Asset class of an order leg.
379 OrderLegType {
380 /// Listed equity.
381 Equity = "EQUITY",
382 /// Listed option.
383 Option = "OPTION",
384 /// Index.
385 Index = "INDEX",
386 /// Mutual fund.
387 MutualFund = "MUTUAL_FUND",
388 /// Cash equivalent.
389 CashEquivalent = "CASH_EQUIVALENT",
390 /// Fixed income.
391 FixedIncome = "FIXED_INCOME",
392 /// Currency.
393 Currency = "CURRENCY",
394 /// Collective investment vehicle.
395 CollectiveInvestment = "COLLECTIVE_INVESTMENT",
396 }
397}
398
399string_enum! {
400 /// Whether a leg opens or closes a position.
401 PositionEffect {
402 /// Opening a new position.
403 Opening = "OPENING",
404 /// Closing an existing position.
405 Closing = "CLOSING",
406 /// Schwab determines the effect automatically.
407 Automatic = "AUTOMATIC",
408 }
409}
410
411string_enum! {
412 /// How a leg's quantity is denominated.
413 QuantityType {
414 /// Close out the entire existing position.
415 AllShares = "ALL_SHARES",
416 /// Dollar-denominated (fractional shares).
417 Dollars = "DOLLARS",
418 /// Whole-share count.
419 Shares = "SHARES",
420 }
421}
422
423string_enum! {
424 /// Mutual-fund dividend / capital-gains handling.
425 DivCapGains {
426 /// Reinvest distributions back into the fund.
427 Reinvest = "REINVEST",
428 /// Pay out distributions as cash.
429 Payout = "PAYOUT",
430 }
431}
432
433string_enum! {
434 /// Lifecycle event kind on an order's activity history.
435 OrderActivityType {
436 /// Execution (fill).
437 Execution = "EXECUTION",
438 /// Order lifecycle action (place / replace / cancel).
439 OrderAction = "ORDER_ACTION",
440 }
441}
442
443string_enum! {
444 /// Execution-event kind. Schwab currently only emits `FILL`.
445 ExecutionType {
446 /// Fill (partial or complete).
447 Fill = "FILL",
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn unknown_status_preserves_raw_string() {
457 let parsed: ApiOrderStatus = serde_json::from_str(r#""SOME_NEW_STATE""#).unwrap();
458 assert!(matches!(parsed, ApiOrderStatus::Unknown(ref s) if s == "SOME_NEW_STATE"));
459 assert_eq!(
460 serde_json::to_string(&parsed).unwrap(),
461 r#""SOME_NEW_STATE""#
462 );
463 }
464
465 #[test]
466 fn unknown_order_type_preserves_raw_string() {
467 let parsed: OrderType = serde_json::from_str(r#""NEW_TYPE""#).unwrap();
468 assert!(matches!(parsed, OrderType::Unknown(ref s) if s == "NEW_TYPE"));
469 }
470
471 #[test]
472 fn order_status_round_trips_each_known_variant() {
473 for raw in [
474 "AWAITING_PARENT_ORDER",
475 "ACCEPTED",
476 "WORKING",
477 "FILLED",
478 "REJECTED",
479 "CANCELED",
480 "EXPIRED",
481 "PENDING_CANCEL",
482 "PENDING_REPLACE",
483 "REPLACED",
484 ] {
485 let json = format!(r#""{raw}""#);
486 let parsed: ApiOrderStatus = serde_json::from_str(&json).unwrap();
487 assert_eq!(serde_json::to_string(&parsed).unwrap(), json);
488 }
489 }
490}