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 pair: String,
304 exchange: Exchange,
305 amount: u32,
306}
307
308impl ForexBuilder {
309 pub fn new(base: impl Into<Currency>, quote: impl Into<Currency>) -> Self {
311 let base = base.into();
312 let quote = quote.into();
313 ForexBuilder {
314 pair: format!("{}.{}", base, quote),
315 exchange: "IDEALPRO".into(),
316 amount: 20_000,
317 }
318 }
319
320 pub fn amount(mut self, amount: u32) -> Self {
322 self.amount = amount;
323 self
324 }
325
326 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
328 self.exchange = exchange.into();
329 self
330 }
331
332 pub fn build(self) -> Contract {
334 Contract {
335 symbol: Symbol::new(self.pair),
336 security_type: SecurityType::ForexPair,
337 exchange: self.exchange,
338 currency: "USD".into(), ..Default::default()
340 }
341 }
342}
343
344#[derive(Debug, Clone)]
346pub struct CryptoBuilder {
347 symbol: Symbol,
348 exchange: Exchange,
349 currency: Currency,
350}
351
352impl CryptoBuilder {
353 pub fn new(symbol: impl Into<Symbol>) -> Self {
355 CryptoBuilder {
356 symbol: symbol.into(),
357 exchange: "PAXOS".into(),
358 currency: "USD".into(),
359 }
360 }
361
362 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
364 self.exchange = exchange.into();
365 self
366 }
367
368 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
370 self.currency = currency.into();
371 self
372 }
373
374 pub fn build(self) -> Contract {
376 Contract {
377 symbol: self.symbol,
378 security_type: SecurityType::Crypto,
379 exchange: self.exchange,
380 currency: self.currency,
381 ..Default::default()
382 }
383 }
384}
385
386#[derive(Debug, Clone)]
388pub struct SpreadBuilder {
389 legs: Vec<Leg>,
390 currency: Currency,
391 exchange: Exchange,
392}
393
394#[derive(Debug, Clone)]
396pub struct Leg {
397 contract_id: i32,
398 action: LegAction,
399 ratio: i32,
400 exchange: Option<Exchange>,
401}
402
403impl SpreadBuilder {
404 pub fn new() -> Self {
406 SpreadBuilder {
407 legs: Vec::new(),
408 currency: "USD".into(),
409 exchange: "SMART".into(),
410 }
411 }
412}
413
414impl Default for SpreadBuilder {
415 fn default() -> Self {
416 Self::new()
417 }
418}
419
420impl SpreadBuilder {
421 pub fn add_leg(self, contract_id: i32, action: LegAction) -> LegBuilder {
423 LegBuilder {
424 parent: self,
425 leg: Leg {
426 contract_id,
427 action,
428 ratio: 1,
429 exchange: None,
430 },
431 }
432 }
433
434 pub fn calendar(self, near_id: i32, far_id: i32) -> Self {
436 self.add_leg(near_id, LegAction::Buy).done().add_leg(far_id, LegAction::Sell).done()
437 }
438
439 pub fn vertical(self, long_id: i32, short_id: i32) -> Self {
441 self.add_leg(long_id, LegAction::Buy).done().add_leg(short_id, LegAction::Sell).done()
442 }
443
444 pub fn iron_condor(self, long_put_id: i32, short_put_id: i32, short_call_id: i32, long_call_id: i32) -> Self {
446 self.add_leg(long_put_id, LegAction::Buy)
447 .done()
448 .add_leg(short_put_id, LegAction::Sell)
449 .done()
450 .add_leg(short_call_id, LegAction::Sell)
451 .done()
452 .add_leg(long_call_id, LegAction::Buy)
453 .done()
454 }
455
456 pub fn in_currency(mut self, currency: impl Into<Currency>) -> Self {
458 self.currency = currency.into();
459 self
460 }
461
462 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
464 self.exchange = exchange.into();
465 self
466 }
467
468 pub fn build(self) -> Result<Contract, Error> {
470 if self.legs.is_empty() {
471 return Err(Error::Simple("Spread must have at least one leg".into()));
472 }
473
474 let combo_legs: Vec<ComboLeg> = self
475 .legs
476 .into_iter()
477 .map(|leg| ComboLeg {
478 contract_id: leg.contract_id,
479 ratio: leg.ratio,
480 action: leg.action.to_string(),
481 exchange: leg.exchange.map(|e| e.to_string()).unwrap_or_default(),
482 ..Default::default()
483 })
484 .collect();
485
486 Ok(Contract {
487 security_type: SecurityType::Spread,
488 currency: self.currency,
489 exchange: self.exchange,
490 combo_legs,
491 ..Default::default()
492 })
493 }
494}
495
496pub struct LegBuilder {
498 parent: SpreadBuilder,
499 leg: Leg,
500}
501
502impl LegBuilder {
503 pub fn ratio(mut self, ratio: i32) -> Self {
505 self.leg.ratio = ratio;
506 self
507 }
508
509 pub fn on_exchange(mut self, exchange: impl Into<Exchange>) -> Self {
511 self.leg.exchange = Some(exchange.into());
512 self
513 }
514
515 pub fn done(mut self) -> SpreadBuilder {
517 self.parent.legs.push(self.leg);
518 self.parent
519 }
520}
521
522#[cfg(test)]
523mod tests;