1use super::types::*;
4use super::{ComboLeg, Contract, SecurityType};
5use crate::Error;
6
7#[derive(Debug, Clone)]
9#[must_use = "StockBuilder does nothing until you call .build()"]
10pub struct StockBuilder<S = Missing> {
11 symbol: S,
12 exchange: Exchange,
13 currency: Currency,
14 primary_exchange: Option<Exchange>,
15 trading_class: Option<String>,
16}
17
18impl StockBuilder<Missing> {
19 pub fn new(symbol: impl Into<Symbol>) -> StockBuilder<Symbol> {
21 StockBuilder {
22 symbol: symbol.into(),
23 exchange: "SMART".into(),
24 currency: "USD".into(),
25 primary_exchange: None,
26 trading_class: None,
27 }
28 }
29}
30
31impl StockBuilder<Symbol> {
32 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
34 self.exchange = exchange.into();
35 self
36 }
37
38 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
40 self.currency = currency.into();
41 self
42 }
43
44 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
46 self.primary_exchange = Some(exchange.into());
47 self
48 }
49
50 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
52 self.trading_class = Some(class.into());
53 self
54 }
55
56 pub fn build(self) -> Contract {
58 Contract {
59 symbol: self.symbol,
60 security_type: SecurityType::Stock,
61 exchange: self.exchange,
62 currency: self.currency,
63 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
64 trading_class: self.trading_class.unwrap_or_default(),
65 ..Default::default()
66 }
67 }
68}
69
70#[derive(Debug, Clone)]
72#[must_use = "OptionBuilder does nothing until you call .build()"]
73pub struct OptionBuilder<Symbol = Missing, Strike = Missing, Expiry = Missing> {
74 symbol: Symbol,
75 right: OptionRight,
76 strike: Strike,
77 expiry: Expiry,
78 exchange: Exchange,
79 currency: Currency,
80 multiplier: u32,
81 primary_exchange: Option<Exchange>,
82 trading_class: Option<String>,
83}
84
85impl OptionBuilder<Missing, Missing, Missing> {
86 pub fn call(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
88 OptionBuilder {
89 symbol: symbol.into(),
90 right: OptionRight::Call,
91 strike: Missing,
92 expiry: Missing,
93 exchange: "SMART".into(),
94 currency: "USD".into(),
95 multiplier: 100,
96 primary_exchange: None,
97 trading_class: None,
98 }
99 }
100
101 pub fn put(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
103 OptionBuilder {
104 symbol: symbol.into(),
105 right: OptionRight::Put,
106 strike: Missing,
107 expiry: Missing,
108 exchange: "SMART".into(),
109 currency: "USD".into(),
110 multiplier: 100,
111 primary_exchange: None,
112 trading_class: None,
113 }
114 }
115}
116
117impl<E> OptionBuilder<Symbol, Missing, E> {
119 pub fn strike(self, price: f64) -> OptionBuilder<Symbol, Strike, E> {
121 OptionBuilder {
122 symbol: self.symbol,
123 right: self.right,
124 strike: Strike::new_unchecked(price),
125 expiry: self.expiry,
126 exchange: self.exchange,
127 currency: self.currency,
128 multiplier: self.multiplier,
129 primary_exchange: self.primary_exchange,
130 trading_class: self.trading_class,
131 }
132 }
133}
134
135impl<S> OptionBuilder<Symbol, S, Missing> {
137 pub fn expires(self, date: ExpirationDate) -> OptionBuilder<Symbol, S, ExpirationDate> {
139 OptionBuilder {
140 symbol: self.symbol,
141 right: self.right,
142 strike: self.strike,
143 expiry: date,
144 exchange: self.exchange,
145 currency: self.currency,
146 multiplier: self.multiplier,
147 primary_exchange: self.primary_exchange,
148 trading_class: self.trading_class,
149 }
150 }
151
152 pub fn expires_on(self, year: u16, month: u8, day: u8) -> OptionBuilder<Symbol, S, ExpirationDate> {
154 self.expires(ExpirationDate::new(year, month, day))
155 }
156
157 pub fn expires_weekly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
159 self.expires(ExpirationDate::next_friday())
160 }
161
162 pub fn expires_monthly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
164 self.expires(ExpirationDate::third_friday_of_month())
165 }
166}
167
168impl<S, E> OptionBuilder<Symbol, S, E> {
170 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
172 self.exchange = exchange.into();
173 self
174 }
175
176 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
178 self.currency = currency.into();
179 self
180 }
181
182 pub fn multiplier(mut self, multiplier: u32) -> Self {
184 self.multiplier = multiplier;
185 self
186 }
187
188 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
190 self.primary_exchange = Some(exchange.into());
191 self
192 }
193
194 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
196 self.trading_class = Some(class.into());
197 self
198 }
199}
200
201impl OptionBuilder<Symbol, Strike, ExpirationDate> {
203 pub fn build(self) -> Contract {
205 Contract {
206 symbol: self.symbol,
207 security_type: SecurityType::Option,
208 strike: self.strike.value(),
209 right: Some(self.right),
210 last_trade_date_or_contract_month: self.expiry.to_string(),
211 exchange: self.exchange,
212 currency: self.currency,
213 multiplier: self.multiplier.to_string(),
214 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
215 trading_class: self.trading_class.unwrap_or_default(),
216 ..Default::default()
217 }
218 }
219}
220
221#[derive(Debug, Clone)]
223#[must_use = "FuturesBuilder does nothing until you call .build()"]
224pub struct FuturesBuilder<Symbol = Missing, Month = Missing> {
225 symbol: Symbol,
226 contract_month: Month,
227 exchange: Exchange,
228 currency: Currency,
229 multiplier: Option<u32>,
230}
231
232impl FuturesBuilder<Missing, Missing> {
233 pub fn new(symbol: impl Into<Symbol>) -> FuturesBuilder<Symbol, Missing> {
235 FuturesBuilder {
236 symbol: symbol.into(),
237 contract_month: Missing,
238 exchange: "CME".into(),
239 currency: "USD".into(),
240 multiplier: None,
241 }
242 }
243}
244
245impl FuturesBuilder<Symbol, Missing> {
246 pub fn expires_in(self, month: ContractMonth) -> FuturesBuilder<Symbol, ContractMonth> {
248 FuturesBuilder {
249 symbol: self.symbol,
250 contract_month: month,
251 exchange: self.exchange,
252 currency: self.currency,
253 multiplier: self.multiplier,
254 }
255 }
256
257 pub fn front_month(self) -> FuturesBuilder<Symbol, ContractMonth> {
259 self.expires_in(ContractMonth::front())
260 }
261
262 pub fn next_quarter(self) -> FuturesBuilder<Symbol, ContractMonth> {
264 self.expires_in(ContractMonth::next_quarter())
265 }
266
267 pub fn any_month(self) -> FuturesBuilder<Symbol, AnyMonth> {
285 FuturesBuilder {
286 symbol: self.symbol,
287 contract_month: AnyMonth,
288 exchange: self.exchange,
289 currency: self.currency,
290 multiplier: self.multiplier,
291 }
292 }
293}
294
295impl<M> FuturesBuilder<Symbol, M> {
296 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
298 self.exchange = exchange.into();
299 self
300 }
301
302 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
304 self.currency = currency.into();
305 self
306 }
307
308 pub fn multiplier(mut self, value: u32) -> Self {
310 self.multiplier = Some(value);
311 self
312 }
313}
314
315impl FuturesBuilder<Symbol, ContractMonth> {
316 pub fn build(self) -> Contract {
318 Contract {
319 symbol: self.symbol,
320 security_type: SecurityType::Future,
321 last_trade_date_or_contract_month: self.contract_month.to_string(),
322 exchange: self.exchange,
323 currency: self.currency,
324 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
325 ..Default::default()
326 }
327 }
328}
329
330impl FuturesBuilder<Symbol, AnyMonth> {
331 pub fn build(self) -> Contract {
344 Contract {
345 symbol: self.symbol,
346 security_type: SecurityType::Future,
347 exchange: self.exchange,
349 currency: self.currency,
350 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
351 ..Default::default()
352 }
353 }
354}
355
356#[derive(Debug, Clone)]
358#[must_use = "ContinuousFuturesBuilder does nothing until you call .build()"]
359pub struct ContinuousFuturesBuilder<Symbol = Missing> {
360 symbol: Symbol,
361 exchange: Exchange,
362 currency: Currency,
363 multiplier: Option<u32>,
364}
365
366impl ContinuousFuturesBuilder<Missing> {
367 pub fn new(symbol: impl Into<Symbol>) -> ContinuousFuturesBuilder<Symbol> {
369 ContinuousFuturesBuilder {
370 symbol: symbol.into(),
371 exchange: "CME".into(),
372 currency: "USD".into(),
373 multiplier: None,
374 }
375 }
376}
377
378impl ContinuousFuturesBuilder<Symbol> {
379 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
381 self.exchange = exchange.into();
382 self
383 }
384
385 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
387 self.currency = currency.into();
388 self
389 }
390
391 pub fn multiplier(mut self, value: u32) -> Self {
393 self.multiplier = Some(value);
394 self
395 }
396
397 pub fn build(self) -> Contract {
399 Contract {
400 symbol: self.symbol,
401 security_type: SecurityType::ContinuousFuture,
402 exchange: self.exchange,
403 currency: self.currency,
404 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
405 ..Default::default()
406 }
407 }
408}
409
410#[derive(Debug, Clone)]
412#[must_use = "ForexBuilder does nothing until you call .build()"]
413pub struct ForexBuilder {
414 base: Currency,
415 quote: Currency,
416 exchange: Exchange,
417}
418
419impl ForexBuilder {
420 pub fn new(base: impl Into<Currency>, quote: impl Into<Currency>) -> Self {
422 ForexBuilder {
423 base: base.into(),
424 quote: quote.into(),
425 exchange: "IDEALPRO".into(),
426 }
427 }
428
429 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
431 self.exchange = exchange.into();
432 self
433 }
434
435 pub fn build(self) -> Contract {
437 Contract {
438 symbol: Symbol::new(self.base.0),
439 security_type: SecurityType::ForexPair,
440 exchange: self.exchange,
441 currency: self.quote,
442 ..Default::default()
443 }
444 }
445}
446
447#[derive(Debug, Clone)]
449#[must_use = "CryptoBuilder does nothing until you call .build()"]
450pub struct CryptoBuilder {
451 symbol: Symbol,
452 exchange: Exchange,
453 currency: Currency,
454}
455
456impl CryptoBuilder {
457 pub fn new(symbol: impl Into<Symbol>) -> Self {
459 CryptoBuilder {
460 symbol: symbol.into(),
461 exchange: "PAXOS".into(),
462 currency: "USD".into(),
463 }
464 }
465
466 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
468 self.exchange = exchange.into();
469 self
470 }
471
472 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
474 self.currency = currency.into();
475 self
476 }
477
478 pub fn build(self) -> Contract {
480 Contract {
481 symbol: self.symbol,
482 security_type: SecurityType::Crypto,
483 exchange: self.exchange,
484 currency: self.currency,
485 ..Default::default()
486 }
487 }
488}
489
490#[derive(Debug, Clone)]
492#[must_use = "SpreadBuilder does nothing until you call .build()"]
493pub struct SpreadBuilder {
494 legs: Vec<Leg>,
495 currency: Currency,
496 exchange: Exchange,
497}
498
499#[derive(Debug, Clone)]
501pub struct Leg {
502 contract_id: i32,
503 action: LegAction,
504 ratio: i32,
505 exchange: Option<Exchange>,
506}
507
508impl SpreadBuilder {
509 pub fn new() -> Self {
511 SpreadBuilder {
512 legs: Vec::new(),
513 currency: "USD".into(),
514 exchange: "SMART".into(),
515 }
516 }
517}
518
519impl Default for SpreadBuilder {
520 fn default() -> Self {
521 Self::new()
522 }
523}
524
525impl SpreadBuilder {
526 pub fn add_leg(self, contract_id: i32, action: LegAction) -> LegBuilder {
528 LegBuilder {
529 parent: self,
530 leg: Leg {
531 contract_id,
532 action,
533 ratio: 1,
534 exchange: None,
535 },
536 }
537 }
538
539 pub fn calendar(self, near_id: i32, far_id: i32) -> Self {
541 self.add_leg(near_id, LegAction::Buy).done().add_leg(far_id, LegAction::Sell).done()
542 }
543
544 pub fn vertical(self, long_id: i32, short_id: i32) -> Self {
546 self.add_leg(long_id, LegAction::Buy).done().add_leg(short_id, LegAction::Sell).done()
547 }
548
549 pub fn iron_condor(self, long_put_id: i32, short_put_id: i32, short_call_id: i32, long_call_id: i32) -> Self {
551 self.add_leg(long_put_id, LegAction::Buy)
552 .done()
553 .add_leg(short_put_id, LegAction::Sell)
554 .done()
555 .add_leg(short_call_id, LegAction::Sell)
556 .done()
557 .add_leg(long_call_id, LegAction::Buy)
558 .done()
559 }
560
561 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
563 self.currency = currency.into();
564 self
565 }
566
567 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
569 self.exchange = exchange.into();
570 self
571 }
572
573 pub fn build(self) -> Result<Contract, Error> {
575 if self.legs.is_empty() {
576 return Err(Error::InvalidArgument("Spread must have at least one leg".into()));
577 }
578
579 let combo_legs: Vec<ComboLeg> = self
580 .legs
581 .into_iter()
582 .map(|leg| ComboLeg {
583 contract_id: leg.contract_id,
584 ratio: leg.ratio,
585 action: leg.action,
586 exchange: leg.exchange.map(|e| e.to_string()).unwrap_or_default(),
587 ..Default::default()
588 })
589 .collect();
590
591 Ok(Contract {
592 security_type: SecurityType::Spread,
593 currency: self.currency,
594 exchange: self.exchange,
595 combo_legs,
596 ..Default::default()
597 })
598 }
599}
600
601#[must_use = "LegBuilder must be terminated with .done() to add the leg to the SpreadBuilder"]
603pub struct LegBuilder {
604 parent: SpreadBuilder,
605 leg: Leg,
606}
607
608impl LegBuilder {
609 pub fn ratio(mut self, ratio: i32) -> Self {
611 self.leg.ratio = ratio;
612 self
613 }
614
615 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
617 self.leg.exchange = Some(exchange.into());
618 self
619 }
620
621 pub fn done(mut self) -> SpreadBuilder {
623 self.parent.legs.push(self.leg);
624 self.parent
625 }
626}
627
628#[cfg(test)]
629mod tests;