1use super::types::*;
4use super::{ComboLeg, Contract, SecurityType};
5use crate::Error;
6
7#[derive(Debug, Clone)]
9pub struct StockBuilder<S = Missing> {
10 symbol: S,
11 exchange: Exchange,
12 currency: Currency,
13 primary_exchange: Option<Exchange>,
14 trading_class: Option<String>,
15}
16
17impl StockBuilder<Missing> {
18 pub fn new(symbol: impl Into<Symbol>) -> StockBuilder<Symbol> {
20 StockBuilder {
21 symbol: symbol.into(),
22 exchange: "SMART".into(),
23 currency: "USD".into(),
24 primary_exchange: None,
25 trading_class: None,
26 }
27 }
28}
29
30impl StockBuilder<Symbol> {
31 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
33 self.exchange = exchange.into();
34 self
35 }
36
37 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
39 self.currency = currency.into();
40 self
41 }
42
43 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
45 self.primary_exchange = Some(exchange.into());
46 self
47 }
48
49 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
51 self.trading_class = Some(class.into());
52 self
53 }
54
55 pub fn build(self) -> Contract {
57 Contract {
58 symbol: self.symbol,
59 security_type: SecurityType::Stock,
60 exchange: self.exchange,
61 currency: self.currency,
62 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
63 trading_class: self.trading_class.unwrap_or_default(),
64 ..Default::default()
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct OptionBuilder<Symbol = Missing, Strike = Missing, Expiry = Missing> {
72 symbol: Symbol,
73 right: OptionRight,
74 strike: Strike,
75 expiry: Expiry,
76 exchange: Exchange,
77 currency: Currency,
78 multiplier: u32,
79 primary_exchange: Option<Exchange>,
80 trading_class: Option<String>,
81}
82
83impl OptionBuilder<Missing, Missing, Missing> {
84 pub fn call(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
86 OptionBuilder {
87 symbol: symbol.into(),
88 right: OptionRight::Call,
89 strike: Missing,
90 expiry: Missing,
91 exchange: "SMART".into(),
92 currency: "USD".into(),
93 multiplier: 100,
94 primary_exchange: None,
95 trading_class: None,
96 }
97 }
98
99 pub fn put(symbol: impl Into<Symbol>) -> OptionBuilder<Symbol, Missing, Missing> {
101 OptionBuilder {
102 symbol: symbol.into(),
103 right: OptionRight::Put,
104 strike: Missing,
105 expiry: Missing,
106 exchange: "SMART".into(),
107 currency: "USD".into(),
108 multiplier: 100,
109 primary_exchange: None,
110 trading_class: None,
111 }
112 }
113}
114
115impl<E> OptionBuilder<Symbol, Missing, E> {
117 pub fn strike(self, price: f64) -> OptionBuilder<Symbol, Strike, E> {
119 OptionBuilder {
120 symbol: self.symbol,
121 right: self.right,
122 strike: Strike::new_unchecked(price),
123 expiry: self.expiry,
124 exchange: self.exchange,
125 currency: self.currency,
126 multiplier: self.multiplier,
127 primary_exchange: self.primary_exchange,
128 trading_class: self.trading_class,
129 }
130 }
131}
132
133impl<S> OptionBuilder<Symbol, S, Missing> {
135 pub fn expires(self, date: ExpirationDate) -> OptionBuilder<Symbol, S, ExpirationDate> {
137 OptionBuilder {
138 symbol: self.symbol,
139 right: self.right,
140 strike: self.strike,
141 expiry: date,
142 exchange: self.exchange,
143 currency: self.currency,
144 multiplier: self.multiplier,
145 primary_exchange: self.primary_exchange,
146 trading_class: self.trading_class,
147 }
148 }
149
150 pub fn expires_on(self, year: u16, month: u8, day: u8) -> OptionBuilder<Symbol, S, ExpirationDate> {
152 self.expires(ExpirationDate::new(year, month, day))
153 }
154
155 pub fn expires_weekly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
157 self.expires(ExpirationDate::next_friday())
158 }
159
160 pub fn expires_monthly(self) -> OptionBuilder<Symbol, S, ExpirationDate> {
162 self.expires(ExpirationDate::third_friday_of_month())
163 }
164}
165
166impl<S, E> OptionBuilder<Symbol, S, E> {
168 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
170 self.exchange = exchange.into();
171 self
172 }
173
174 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
176 self.currency = currency.into();
177 self
178 }
179
180 pub fn multiplier(mut self, multiplier: u32) -> Self {
182 self.multiplier = multiplier;
183 self
184 }
185
186 pub fn primary(mut self, exchange: impl Into<Exchange>) -> Self {
188 self.primary_exchange = Some(exchange.into());
189 self
190 }
191
192 pub fn trading_class(mut self, class: impl Into<String>) -> Self {
194 self.trading_class = Some(class.into());
195 self
196 }
197}
198
199impl OptionBuilder<Symbol, Strike, ExpirationDate> {
201 pub fn build(self) -> Contract {
203 Contract {
204 symbol: self.symbol,
205 security_type: SecurityType::Option,
206 strike: self.strike.value(),
207 right: self.right.to_string(),
208 last_trade_date_or_contract_month: self.expiry.to_string(),
209 exchange: self.exchange,
210 currency: self.currency,
211 multiplier: self.multiplier.to_string(),
212 primary_exchange: self.primary_exchange.unwrap_or_else(|| Exchange::from("")),
213 trading_class: self.trading_class.unwrap_or_default(),
214 ..Default::default()
215 }
216 }
217}
218
219#[derive(Debug, Clone)]
221pub struct FuturesBuilder<Symbol = Missing, Month = Missing> {
222 symbol: Symbol,
223 contract_month: Month,
224 exchange: Exchange,
225 currency: Currency,
226 multiplier: Option<u32>,
227}
228
229impl FuturesBuilder<Missing, Missing> {
230 pub fn new(symbol: impl Into<Symbol>) -> FuturesBuilder<Symbol, Missing> {
232 FuturesBuilder {
233 symbol: symbol.into(),
234 contract_month: Missing,
235 exchange: "GLOBEX".into(),
236 currency: "USD".into(),
237 multiplier: None,
238 }
239 }
240}
241
242impl FuturesBuilder<Symbol, Missing> {
243 pub fn expires_in(self, month: ContractMonth) -> FuturesBuilder<Symbol, ContractMonth> {
245 FuturesBuilder {
246 symbol: self.symbol,
247 contract_month: month,
248 exchange: self.exchange,
249 currency: self.currency,
250 multiplier: self.multiplier,
251 }
252 }
253
254 pub fn front_month(self) -> FuturesBuilder<Symbol, ContractMonth> {
256 self.expires_in(ContractMonth::front())
257 }
258
259 pub fn next_quarter(self) -> FuturesBuilder<Symbol, ContractMonth> {
261 self.expires_in(ContractMonth::next_quarter())
262 }
263}
264
265impl<M> FuturesBuilder<Symbol, M> {
266 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
268 self.exchange = exchange.into();
269 self
270 }
271
272 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
274 self.currency = currency.into();
275 self
276 }
277
278 pub fn multiplier(mut self, value: u32) -> Self {
280 self.multiplier = Some(value);
281 self
282 }
283}
284
285impl FuturesBuilder<Symbol, ContractMonth> {
286 pub fn build(self) -> Contract {
288 Contract {
289 symbol: self.symbol,
290 security_type: SecurityType::Future,
291 last_trade_date_or_contract_month: self.contract_month.to_string(),
292 exchange: self.exchange,
293 currency: self.currency,
294 multiplier: self.multiplier.map(|m| m.to_string()).unwrap_or_default(),
295 ..Default::default()
296 }
297 }
298}
299
300#[derive(Debug, Clone)]
302pub struct ForexBuilder {
303 base: Currency,
304 quote: Currency,
305 exchange: Exchange,
306}
307
308impl ForexBuilder {
309 pub fn new(base: impl Into<Currency>, quote: impl Into<Currency>) -> Self {
311 ForexBuilder {
312 base: base.into(),
313 quote: quote.into(),
314 exchange: "IDEALPRO".into(),
315 }
316 }
317
318 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
320 self.exchange = exchange.into();
321 self
322 }
323
324 pub fn build(self) -> Contract {
326 Contract {
327 symbol: Symbol::new(self.base.0),
328 security_type: SecurityType::ForexPair,
329 exchange: self.exchange,
330 currency: self.quote,
331 ..Default::default()
332 }
333 }
334}
335
336#[derive(Debug, Clone)]
338pub struct CryptoBuilder {
339 symbol: Symbol,
340 exchange: Exchange,
341 currency: Currency,
342}
343
344impl CryptoBuilder {
345 pub fn new(symbol: impl Into<Symbol>) -> Self {
347 CryptoBuilder {
348 symbol: symbol.into(),
349 exchange: "PAXOS".into(),
350 currency: "USD".into(),
351 }
352 }
353
354 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
356 self.exchange = exchange.into();
357 self
358 }
359
360 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
362 self.currency = currency.into();
363 self
364 }
365
366 pub fn build(self) -> Contract {
368 Contract {
369 symbol: self.symbol,
370 security_type: SecurityType::Crypto,
371 exchange: self.exchange,
372 currency: self.currency,
373 ..Default::default()
374 }
375 }
376}
377
378#[derive(Debug, Clone)]
380pub struct SpreadBuilder {
381 legs: Vec<Leg>,
382 currency: Currency,
383 exchange: Exchange,
384}
385
386#[derive(Debug, Clone)]
388pub struct Leg {
389 contract_id: i32,
390 action: LegAction,
391 ratio: i32,
392 exchange: Option<Exchange>,
393}
394
395impl SpreadBuilder {
396 pub fn new() -> Self {
398 SpreadBuilder {
399 legs: Vec::new(),
400 currency: "USD".into(),
401 exchange: "SMART".into(),
402 }
403 }
404}
405
406impl Default for SpreadBuilder {
407 fn default() -> Self {
408 Self::new()
409 }
410}
411
412impl SpreadBuilder {
413 pub fn add_leg(self, contract_id: i32, action: LegAction) -> LegBuilder {
415 LegBuilder {
416 parent: self,
417 leg: Leg {
418 contract_id,
419 action,
420 ratio: 1,
421 exchange: None,
422 },
423 }
424 }
425
426 pub fn calendar(self, near_id: i32, far_id: i32) -> Self {
428 self.add_leg(near_id, LegAction::Buy).done().add_leg(far_id, LegAction::Sell).done()
429 }
430
431 pub fn vertical(self, long_id: i32, short_id: i32) -> Self {
433 self.add_leg(long_id, LegAction::Buy).done().add_leg(short_id, LegAction::Sell).done()
434 }
435
436 pub fn iron_condor(self, long_put_id: i32, short_put_id: i32, short_call_id: i32, long_call_id: i32) -> Self {
438 self.add_leg(long_put_id, LegAction::Buy)
439 .done()
440 .add_leg(short_put_id, LegAction::Sell)
441 .done()
442 .add_leg(short_call_id, LegAction::Sell)
443 .done()
444 .add_leg(long_call_id, LegAction::Buy)
445 .done()
446 }
447
448 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
450 self.currency = currency.into();
451 self
452 }
453
454 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
456 self.exchange = exchange.into();
457 self
458 }
459
460 pub fn build(self) -> Result<Contract, Error> {
462 if self.legs.is_empty() {
463 return Err(Error::Simple("Spread must have at least one leg".into()));
464 }
465
466 let combo_legs: Vec<ComboLeg> = self
467 .legs
468 .into_iter()
469 .map(|leg| ComboLeg {
470 contract_id: leg.contract_id,
471 ratio: leg.ratio,
472 action: leg.action.to_string(),
473 exchange: leg.exchange.map(|e| e.to_string()).unwrap_or_default(),
474 ..Default::default()
475 })
476 .collect();
477
478 Ok(Contract {
479 security_type: SecurityType::Spread,
480 currency: self.currency,
481 exchange: self.exchange,
482 combo_legs,
483 ..Default::default()
484 })
485 }
486}
487
488pub struct LegBuilder {
490 parent: SpreadBuilder,
491 leg: Leg,
492}
493
494impl LegBuilder {
495 pub fn ratio(mut self, ratio: i32) -> Self {
497 self.leg.ratio = ratio;
498 self
499 }
500
501 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
503 self.leg.exchange = Some(exchange.into());
504 self
505 }
506
507 pub fn done(mut self) -> SpreadBuilder {
509 self.parent.legs.push(self.leg);
510 self.parent
511 }
512}
513
514#[cfg(test)]
515mod tests;