1use super::types::*;
4use super::{ComboLeg, Contract, SecurityType};
5use crate::accounts::types::ContractId;
6use crate::Error;
7
8#[derive(Debug, Clone)]
10#[must_use = "StockBuilder does nothing until you call .build()"]
11pub struct StockBuilder<S = Missing> {
12 symbol: S,
13 exchange: Exchange,
14 currency: Currency,
15 primary_exchange: Option<Exchange>,
16 trading_class: Option<String>,
17}
18
19impl StockBuilder<Missing> {
20 pub fn new(symbol: impl Into<Symbol>) -> StockBuilder<Symbol> {
22 StockBuilder {
23 symbol: symbol.into(),
24 exchange: "SMART".into(),
25 currency: "USD".into(),
26 primary_exchange: None,
27 trading_class: None,
28 }
29 }
30}
31
32impl StockBuilder<Symbol> {
33 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
35 self.exchange = exchange.into();
36 self
37 }
38
39 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
41 self.currency = currency.into();
42 self
43 }
44
45 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
47 self.primary_exchange = Some(exchange.into());
48 self
49 }
50
51 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
53 self.trading_class = Some(class.into());
54 self
55 }
56
57 pub fn build(self) -> Contract {
59 Contract {
60 symbol: self.symbol,
61 security_type: SecurityType::Stock,
62 exchange: self.exchange,
63 currency: self.currency,
64 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
65 trading_class: self.trading_class.unwrap_or_default(),
66 ..Default::default()
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73#[must_use = "OptionBuilder does nothing until you call .build()"]
74pub struct OptionBuilder<Symbol = Missing, Strike = Missing, Expiry = Missing> {
75 symbol: Symbol,
76 right: OptionRight,
77 strike: Strike,
78 expiry: Expiry,
79 exchange: Exchange,
80 currency: Currency,
81 multiplier: u32,
82 primary_exchange: Option<Exchange>,
83 trading_class: Option<String>,
84}
85
86impl OptionBuilder<Missing, Missing, Missing> {
87 pub fn call(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
89 OptionBuilder {
90 symbol: symbol.into(),
91 right: OptionRight::Call,
92 strike: Missing,
93 expiry: Missing,
94 exchange: "SMART".into(),
95 currency: "USD".into(),
96 multiplier: 100,
97 primary_exchange: None,
98 trading_class: None,
99 }
100 }
101
102 pub fn put(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
104 OptionBuilder {
105 symbol: symbol.into(),
106 right: OptionRight::Put,
107 strike: Missing,
108 expiry: Missing,
109 exchange: "SMART".into(),
110 currency: "USD".into(),
111 multiplier: 100,
112 primary_exchange: None,
113 trading_class: None,
114 }
115 }
116}
117
118impl<E> OptionBuilder<Symbol, Missing, E> {
120 pub fn strike(self, price: f64) -> OptionBuilder<Symbol, Strike, E> {
126 OptionBuilder {
127 symbol: self.symbol,
128 right: self.right,
129 strike: Strike::new_unchecked(price),
130 expiry: self.expiry,
131 exchange: self.exchange,
132 currency: self.currency,
133 multiplier: self.multiplier,
134 primary_exchange: self.primary_exchange,
135 trading_class: self.trading_class,
136 }
137 }
138}
139
140impl<S> OptionBuilder<Symbol, S, Missing> {
142 pub fn expires(self, date: ExpirationDate) -> OptionBuilder<Symbol, S, ExpirationDate> {
144 OptionBuilder {
145 symbol: self.symbol,
146 right: self.right,
147 strike: self.strike,
148 expiry: date,
149 exchange: self.exchange,
150 currency: self.currency,
151 multiplier: self.multiplier,
152 primary_exchange: self.primary_exchange,
153 trading_class: self.trading_class,
154 }
155 }
156
157 pub fn expires_on(self, year: u16, month: u8, day: u8) -> OptionBuilder<Symbol, S, ExpirationDate> {
159 self.expires(ExpirationDate::new(year, month, day))
160 }
161
162 pub fn expires_weekly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
164 self.expires(ExpirationDate::next_friday())
165 }
166
167 pub fn expires_monthly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
169 self.expires(ExpirationDate::third_friday_of_month())
170 }
171}
172
173impl<S, E> OptionBuilder<Symbol, S, E> {
175 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
177 self.exchange = exchange.into();
178 self
179 }
180
181 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
183 self.currency = currency.into();
184 self
185 }
186
187 pub fn multiplier(mut self, multiplier: u32) -> Self {
189 self.multiplier = multiplier;
190 self
191 }
192
193 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
195 self.primary_exchange = Some(exchange.into());
196 self
197 }
198
199 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
201 self.trading_class = Some(class.into());
202 self
203 }
204}
205
206impl OptionBuilder<Symbol, Strike, ExpirationDate> {
208 pub fn build(self) -> Contract {
210 Contract {
211 symbol: self.symbol,
212 security_type: SecurityType::Option,
213 strike: self.strike.value(),
214 right: Some(self.right),
215 last_trade_date_or_contract_month: self.expiry.to_string(),
216 exchange: self.exchange,
217 currency: self.currency,
218 multiplier: self.multiplier.to_string(),
219 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
220 trading_class: self.trading_class.unwrap_or_default(),
221 ..Default::default()
222 }
223 }
224}
225
226#[derive(Debug, Clone)]
228#[must_use = "FuturesBuilder does nothing until you call .build()"]
229pub struct FuturesBuilder<Symbol = Missing, Month = Missing> {
230 symbol: Symbol,
231 contract_month: Month,
232 exchange: Exchange,
233 currency: Currency,
234 multiplier: Option<u32>,
235}
236
237impl FuturesBuilder<Missing, Missing> {
238 pub fn new(symbol: impl Into<Symbol>) -> FuturesBuilder<Symbol, Missing> {
240 FuturesBuilder {
241 symbol: symbol.into(),
242 contract_month: Missing,
243 exchange: "CME".into(),
244 currency: "USD".into(),
245 multiplier: None,
246 }
247 }
248}
249
250impl FuturesBuilder<Symbol, Missing> {
251 pub fn expires_in(self, month: ContractMonth) -> FuturesBuilder<Symbol, ContractMonth> {
253 FuturesBuilder {
254 symbol: self.symbol,
255 contract_month: month,
256 exchange: self.exchange,
257 currency: self.currency,
258 multiplier: self.multiplier,
259 }
260 }
261
262 pub fn front_month(self) -> FuturesBuilder<Symbol, ContractMonth> {
264 self.expires_in(ContractMonth::front())
265 }
266
267 pub fn next_quarter(self) -> FuturesBuilder<Symbol, ContractMonth> {
269 self.expires_in(ContractMonth::next_quarter())
270 }
271
272 pub fn any_month(self) -> FuturesBuilder<Symbol, AnyMonth> {
290 FuturesBuilder {
291 symbol: self.symbol,
292 contract_month: AnyMonth,
293 exchange: self.exchange,
294 currency: self.currency,
295 multiplier: self.multiplier,
296 }
297 }
298}
299
300impl<M> FuturesBuilder<Symbol, M> {
301 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
303 self.exchange = exchange.into();
304 self
305 }
306
307 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
309 self.currency = currency.into();
310 self
311 }
312
313 pub fn multiplier(mut self, value: u32) -> Self {
315 self.multiplier = Some(value);
316 self
317 }
318}
319
320impl FuturesBuilder<Symbol, ContractMonth> {
321 pub fn build(self) -> Contract {
323 Contract {
324 symbol: self.symbol,
325 security_type: SecurityType::Future,
326 last_trade_date_or_contract_month: self.contract_month.to_string(),
327 exchange: self.exchange,
328 currency: self.currency,
329 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
330 ..Default::default()
331 }
332 }
333}
334
335impl FuturesBuilder<Symbol, AnyMonth> {
336 pub fn build(self) -> Contract {
349 Contract {
350 symbol: self.symbol,
351 security_type: SecurityType::Future,
352 exchange: self.exchange,
354 currency: self.currency,
355 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
356 ..Default::default()
357 }
358 }
359}
360
361#[derive(Debug, Clone)]
363#[must_use = "ContinuousFuturesBuilder does nothing until you call .build()"]
364pub struct ContinuousFuturesBuilder<Symbol = Missing> {
365 symbol: Symbol,
366 exchange: Exchange,
367 currency: Currency,
368 multiplier: Option<u32>,
369}
370
371impl ContinuousFuturesBuilder<Missing> {
372 pub fn new(symbol: impl Into<Symbol>) -> ContinuousFuturesBuilder<Symbol> {
374 ContinuousFuturesBuilder {
375 symbol: symbol.into(),
376 exchange: "CME".into(),
377 currency: "USD".into(),
378 multiplier: None,
379 }
380 }
381}
382
383impl ContinuousFuturesBuilder<Symbol> {
384 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
386 self.exchange = exchange.into();
387 self
388 }
389
390 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
392 self.currency = currency.into();
393 self
394 }
395
396 pub fn multiplier(mut self, value: u32) -> Self {
398 self.multiplier = Some(value);
399 self
400 }
401
402 pub fn build(self) -> Contract {
404 Contract {
405 symbol: self.symbol,
406 security_type: SecurityType::ContinuousFuture,
407 exchange: self.exchange,
408 currency: self.currency,
409 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
410 ..Default::default()
411 }
412 }
413}
414
415#[derive(Debug, Clone)]
417#[must_use = "ForexBuilder does nothing until you call .build()"]
418pub struct ForexBuilder {
419 base: Currency,
420 quote: Currency,
421 exchange: Exchange,
422}
423
424impl ForexBuilder {
425 pub fn new(base: impl Into<Currency>, quote: impl Into<Currency>) -> Self {
427 ForexBuilder {
428 base: base.into(),
429 quote: quote.into(),
430 exchange: "IDEALPRO".into(),
431 }
432 }
433
434 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
436 self.exchange = exchange.into();
437 self
438 }
439
440 pub fn build(self) -> Contract {
442 Contract {
443 symbol: Symbol::new(self.base.0),
444 security_type: SecurityType::ForexPair,
445 exchange: self.exchange,
446 currency: self.quote,
447 ..Default::default()
448 }
449 }
450}
451
452#[derive(Debug, Clone)]
454#[must_use = "CryptoBuilder does nothing until you call .build()"]
455pub struct CryptoBuilder {
456 symbol: Symbol,
457 exchange: Exchange,
458 currency: Currency,
459}
460
461impl CryptoBuilder {
462 pub fn new(symbol: impl Into<Symbol>) -> Self {
464 CryptoBuilder {
465 symbol: symbol.into(),
466 exchange: "PAXOS".into(),
467 currency: "USD".into(),
468 }
469 }
470
471 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
473 self.exchange = exchange.into();
474 self
475 }
476
477 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
479 self.currency = currency.into();
480 self
481 }
482
483 pub fn build(self) -> Contract {
485 Contract {
486 symbol: self.symbol,
487 security_type: SecurityType::Crypto,
488 exchange: self.exchange,
489 currency: self.currency,
490 ..Default::default()
491 }
492 }
493}
494
495#[derive(Debug, Clone)]
497#[must_use = "SpreadBuilder does nothing until you call .build()"]
498pub struct SpreadBuilder {
499 legs: Vec<Leg>,
500 symbol: Symbol,
501 currency: Currency,
502 exchange: Exchange,
503}
504
505#[derive(Debug, Clone)]
507pub struct Leg {
508 contract_id: i32,
509 action: LegAction,
510 ratio: i32,
511 exchange: Option<Exchange>,
512}
513
514impl SpreadBuilder {
515 pub fn new() -> Self {
517 SpreadBuilder {
518 legs: Vec::new(),
519 symbol: Symbol::default(),
520 currency: "USD".into(),
521 exchange: "SMART".into(),
522 }
523 }
524}
525
526impl Default for SpreadBuilder {
527 fn default() -> Self {
528 Self::new()
529 }
530}
531
532impl SpreadBuilder {
533 pub fn add_leg(self, contract_id: impl Into<ContractId>, action: LegAction) -> LegBuilder {
535 LegBuilder {
536 parent: self,
537 leg: Leg {
538 contract_id: contract_id.into().value(),
539 action,
540 ratio: 1,
541 exchange: None,
542 },
543 }
544 }
545
546 pub fn long_calendar(self, near_id: impl Into<ContractId>, far_id: impl Into<ContractId>) -> Self {
550 self.add_leg(near_id, LegAction::Sell).done().add_leg(far_id, LegAction::Buy).done()
551 }
552
553 pub fn short_calendar(self, near_id: impl Into<ContractId>, far_id: impl Into<ContractId>) -> Self {
557 self.add_leg(near_id, LegAction::Buy).done().add_leg(far_id, LegAction::Sell).done()
558 }
559
560 pub fn vertical(self, long_id: impl Into<ContractId>, short_id: impl Into<ContractId>) -> Self {
572 self.add_leg(long_id, LegAction::Buy).done().add_leg(short_id, LegAction::Sell).done()
573 }
574
575 pub fn straddle(self, call_id: impl Into<ContractId>, put_id: impl Into<ContractId>) -> Self {
587 self.add_leg(call_id, LegAction::Buy).done().add_leg(put_id, LegAction::Buy).done()
588 }
589
590 pub fn strangle(self, call_id: impl Into<ContractId>, put_id: impl Into<ContractId>) -> Self {
604 self.straddle(call_id, put_id)
605 }
606
607 pub fn butterfly(self, legs: ButterflyLegs) -> Self {
626 self.add_leg(legs.lower, LegAction::Buy)
627 .done()
628 .add_leg(legs.middle, LegAction::Sell)
629 .ratio(2)
630 .done()
631 .add_leg(legs.upper, LegAction::Buy)
632 .done()
633 }
634
635 pub fn iron_condor(self, legs: IronCondorLegs) -> Self {
657 self.vertical(legs.long_put, legs.short_put).vertical(legs.long_call, legs.short_call)
658 }
659
660 pub fn collar(self, legs: CollarLegs) -> Self {
683 self.add_leg(legs.stock, LegAction::Buy)
684 .ratio(legs.shares_per_contract)
685 .done()
686 .add_leg(legs.put, LegAction::Buy)
687 .done()
688 .add_leg(legs.call, LegAction::Sell)
689 .done()
690 }
691
692 pub fn symbol(mut self, symbol: impl Into<Symbol>) -> Self {
709 self.symbol = symbol.into();
710 self
711 }
712
713 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
715 self.currency = currency.into();
716 self
717 }
718
719 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
721 self.exchange = exchange.into();
722 self
723 }
724
725 pub fn build(self) -> Result<Contract, Error> {
729 if self.legs.is_empty() {
730 return Err(Error::InvalidArgument("Spread must have at least one leg".into()));
731 }
732
733 let combo_legs: Vec<ComboLeg> = self
734 .legs
735 .into_iter()
736 .map(|leg| ComboLeg {
737 contract_id: leg.contract_id,
738 ratio: leg.ratio,
739 action: leg.action,
740 exchange: leg.exchange.map(|e| e.to_string()).unwrap_or_default(),
741 ..Default::default()
742 })
743 .collect();
744
745 Ok(Contract {
746 symbol: self.symbol,
747 security_type: SecurityType::Spread,
748 currency: self.currency,
749 exchange: self.exchange,
750 combo_legs,
751 ..Default::default()
752 })
753 }
754}
755
756#[derive(Debug, Clone, Copy, PartialEq, Eq)]
758pub struct ButterflyLegs {
759 pub lower: ContractId,
761 pub middle: ContractId,
763 pub upper: ContractId,
765}
766
767#[derive(Debug, Clone, Copy, PartialEq, Eq)]
769pub struct IronCondorLegs {
770 pub long_put: ContractId,
772 pub short_put: ContractId,
774 pub short_call: ContractId,
776 pub long_call: ContractId,
778}
779
780#[derive(Debug, Clone, Copy, PartialEq, Eq)]
782pub struct CollarLegs {
783 pub stock: ContractId,
785 pub put: ContractId,
787 pub call: ContractId,
789 pub shares_per_contract: i32,
795}
796
797#[must_use = "LegBuilder must be terminated with .done() to add the leg to the SpreadBuilder"]
799pub struct LegBuilder {
800 parent: SpreadBuilder,
801 leg: Leg,
802}
803
804impl LegBuilder {
805 pub fn ratio(mut self, ratio: i32) -> Self {
807 self.leg.ratio = ratio;
808 self
809 }
810
811 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
813 self.leg.exchange = Some(exchange.into());
814 self
815 }
816
817 pub fn done(mut self) -> SpreadBuilder {
819 self.parent.legs.push(self.leg);
820 self.parent
821 }
822}
823
824#[cfg(test)]
825#[path = "builders_tests.rs"]
826mod tests;