diff --git a/README.md b/README.md index 6464b21..969e1ec 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ To use this crate in your project, add the following to your Cargo.toml: ```toml [dependencies] -trade_aggregation = "12" +trade_aggregation = "13" ``` Lets aggregate all trades into time based 1 minute candles, consisting of open, high, low and close information. @@ -87,7 +87,7 @@ fn main() { let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); // Notice how the aggregator is generic over the output candle type, // the aggregation rule as well as the input trade data - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = GenericAggregator::::new(time_rule, false, MyCandle::default); for t in &trades { if let Some(candle) = aggregator.update(t) { diff --git a/benches/candle_aggregation.rs b/benches/candle_aggregation.rs index f0d19d3..115e35d 100644 --- a/benches/candle_aggregation.rs +++ b/benches/candle_aggregation.rs @@ -37,19 +37,28 @@ struct CandleAll { fn time_aggregation_open(trades: &[Trade]) { let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = GenericAggregator::::new( + time_rule, + false, + CandleOpen::default, + ); let _candles = aggregate_all_trades(trades, &mut aggregator); } fn time_aggregation_ohlc(trades: &[Trade]) { let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = GenericAggregator::::new( + time_rule, + false, + CandleOHLC::default, + ); let _candles = aggregate_all_trades(trades, &mut aggregator); } fn time_aggregation_all(trades: &[Trade]) { let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = + GenericAggregator::::new(time_rule, false, CandleAll::default); let _candles = aggregate_all_trades(trades, &mut aggregator); } diff --git a/examples/aggregate_all_ohlc.rs b/examples/aggregate_all_ohlc.rs index 7e20bdd..46894b7 100644 --- a/examples/aggregate_all_ohlc.rs +++ b/examples/aggregate_all_ohlc.rs @@ -25,7 +25,8 @@ fn main() { // specify the aggregation rule to be time based let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = + GenericAggregator::::new(time_rule, false, MyCandle::default); let candles = aggregate_all_trades(&trades, &mut aggregator); println!("got {} candles", candles.len()); diff --git a/examples/streaming_aggregate_ohlc.rs b/examples/streaming_aggregate_ohlc.rs index 2382d80..25a2aea 100644 --- a/examples/streaming_aggregate_ohlc.rs +++ b/examples/streaming_aggregate_ohlc.rs @@ -20,7 +20,8 @@ fn main() { // specify the aggregation rule to be time based let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = + GenericAggregator::::new(time_rule, false, MyCandle::default); for t in &trades { if let Some(candle) = aggregator.update(t) { diff --git a/examples/user_trade_type.rs b/examples/user_trade_type.rs index 96f337a..e4eb671 100644 --- a/examples/user_trade_type.rs +++ b/examples/user_trade_type.rs @@ -88,7 +88,8 @@ fn main() { // specify the aggregation rule to be time based let time_rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut aggregator = GenericAggregator::::new(time_rule, false); + let mut aggregator = + GenericAggregator::::new(time_rule, false, MyCandle::default); let candles = aggregate_all_trades(&ticks, &mut aggregator); println!("got {} candles", candles.len()); diff --git a/src/aggregation_rules/aligned_time_rule.rs b/src/aggregation_rules/aligned_time_rule.rs index 32b96c5..11ff48e 100644 --- a/src/aggregation_rules/aligned_time_rule.rs +++ b/src/aggregation_rules/aligned_time_rule.rs @@ -105,6 +105,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( AlignedTimeRule::new(M15, TimestampResolution::Millisecond), false, + MyCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 396); @@ -126,6 +127,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( AlignedTimeRule::new(M1, TimestampResolution::Microsecond), false, + MyCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); @@ -167,6 +169,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( AlignedTimeRule::new(M1, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 2); @@ -204,6 +207,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( AlignedTimeRule::new(M1, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 2); diff --git a/src/aggregation_rules/relative_price_rule.rs b/src/aggregation_rules/relative_price_rule.rs index fcb8e76..7d4e04e 100644 --- a/src/aggregation_rules/relative_price_rule.rs +++ b/src/aggregation_rules/relative_price_rule.rs @@ -125,7 +125,8 @@ mod tests { // 0.5% candles const THRESHOLD: f64 = 0.005; let rule = RelativePriceRule::new(0.01).unwrap(); - let mut aggregator = GenericAggregator::::new(rule, false); + let mut aggregator = + GenericAggregator::::new(rule, false, OhlcCandle::default); let candles = aggregate_all_trades(&trades, &mut aggregator); assert!(!candles.is_empty()); @@ -141,7 +142,8 @@ mod tests { const THRESHOLD: f64 = 0.005; let rule = RelativePriceRule::new(THRESHOLD).unwrap(); - let mut aggregator = GenericAggregator::::new(rule, false); + let mut aggregator = + GenericAggregator::::new(rule, false, OhlcCandle::default); let candles = aggregate_all_trades(&trades, &mut aggregator); println!("got {} candles", candles.len()); diff --git a/src/aggregation_rules/tick_rule.rs b/src/aggregation_rules/tick_rule.rs index 8527e80..e7c17b0 100644 --- a/src/aggregation_rules/tick_rule.rs +++ b/src/aggregation_rules/tick_rule.rs @@ -55,8 +55,11 @@ mod tests { fn tick_rule() { let trades = load_trades_from_csv("data/Bitmex_XBTUSD_1M.csv").unwrap(); - let mut aggregator = - GenericAggregator::::new(TickRule::new(1000), false); + let mut aggregator = GenericAggregator::::new( + TickRule::new(1000), + false, + OhlcCandle::default, + ); let candles = aggregate_all_trades(&trades, &mut aggregator); // As there are 1 million trades in the test data, this will create 1000 candles assert_eq!(candles.len(), 1000); diff --git a/src/aggregation_rules/time_rule.rs b/src/aggregation_rules/time_rule.rs index 379359b..3e813c8 100644 --- a/src/aggregation_rules/time_rule.rs +++ b/src/aggregation_rules/time_rule.rs @@ -78,6 +78,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M15, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); println!("got {} candles", candles.len()); @@ -92,6 +93,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M15, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 396); @@ -99,6 +101,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M5, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 1190); @@ -106,6 +109,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(H1, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades, &mut aggregator); assert_eq!(candles.len(), 99); @@ -138,6 +142,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M15, TimestampResolution::Millisecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades_ms, &mut aggregator); assert_eq!(candles.len(), 396); @@ -145,6 +150,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M15, TimestampResolution::Microsecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades_micros, &mut aggregator); assert_eq!(candles.len(), 396); @@ -152,6 +158,7 @@ mod tests { let mut aggregator = GenericAggregator::::new( TimeRule::new(M15, TimestampResolution::Nanosecond), false, + OhlcCandle::default, ); let candles = aggregate_all_trades(&trades_ns, &mut aggregator); assert_eq!(candles.len(), 396); diff --git a/src/aggregator.rs b/src/aggregator.rs index c8d88a3..20392f0 100644 --- a/src/aggregator.rs +++ b/src/aggregator.rs @@ -53,9 +53,16 @@ where /// Examples uses include ensuring the close and open price of the current and next candle are equal. /// If that's desired, set the field to true during construction of `Self`. /// E.g on Tradingview the time aggregation would have this set to `false`, which may create gaps between close and open of subsequent candles. - pub fn new(aggregation_rule: R, include_trade_that_triggered_rule: bool) -> Self { + /// `init_candle`: this is a zero argument closure which is used to initialize the candle that is being built up by + /// the aggregator until it is triggered. This allows users to embed non-default state into the aggregation process, such + /// as tick size for binning aggregators. + pub fn new C>( + aggregation_rule: R, + include_trade_that_triggered_rule: bool, + init_candle: F, + ) -> Self { Self { - candle: Default::default(), + candle: init_candle(), aggregation_rule, include_trade_that_triggered_rule, _trade_type: PhantomData, @@ -120,7 +127,8 @@ mod tests { .expect("Could not load trades from file!"); let rule = TimeRule::new(M1, TimestampResolution::Millisecond); - let mut a = GenericAggregator::::new(rule, false); + let mut a = + GenericAggregator::::new(rule, false, MyCandle::default); let mut candle_counter: usize = 0; for t in trades.iter() { diff --git a/src/modular_candle_trait.rs b/src/modular_candle_trait.rs index 346140d..4b355ea 100644 --- a/src/modular_candle_trait.rs +++ b/src/modular_candle_trait.rs @@ -3,7 +3,7 @@ use crate::TakerTrade; /// A modular candle that can be composed of multiple components /// Is generic over the type of trade it accepts during the update step, /// as long as it implements the `TakerTrade` trait -pub trait ModularCandle: Clone + Default { +pub trait ModularCandle: Clone { /// Updates the candle information with trade information fn update(&mut self, trade: &T);