Expand description
§tickbar
High-performance tick-to-bar aggregator for financial market data.
Converts raw trade/quote ticks into OHLCV bars with configurable time alignment, gap filling, VWAP, and corporate action adjustments. Processes up to 119M ticks/second from native Rust.
§Install
[dependencies]
tickbar = "0.1"Optional feature flags:
[dependencies]
tickbar = { version = "0.1", default-features = false } # no Python bindings
tickbar = { version = "0.1", features = ["arrow-export"] } # + Arrow IPC
tickbar = { version = "0.1", features = ["polars-export"] } # + Polars DataFrame§Core concepts
tickbar models tick-to-bar aggregation in three layers:
-
Tick ingestion —
Tickis a 32-byterepr(C)struct holdingtimestamp_nanos, fixed-pointprice, fixed-pointvolume, andflags. UseTickBufferfor batched ingestion with ordering and dedup policies. UseMmapTickReaderfor memory-mapped I/O from binary tick files. -
Bar aggregation —
TickAggregatoris a one-pass state machine built via itsTickAggregatorBuilder. It advances bar boundaries on each tick, optionally fills gaps, and tracks per-bar VWAP.BarAggregatoris the lower-level engine. Useaggregate_parallelfor multi-symbol parallelism. -
Output —
BarSeriesholds completedBars for one symbol. Export to CSV, Arrow IPC, or Polars DataFrame. Resample to a wider interval. Apply split/dividend adjustments.
§Quick start
use tickbar::{TickAggregator, Tick};
use std::time::Duration;
let mut agg = TickAggregator::builder()
.interval(Duration::from_secs(60))
.symbol("AAPL")
.build()?;
agg.push_tick(Tick::from_trade(0, 100.0, 1000.0))?;
agg.push_tick(Tick::from_trade(1_000_000_000, 100.5, 500.0))?;
agg.push_tick(Tick::from_trade(2_000_000_000, 101.0, 750.0))?;
let bars = agg.finalize();
assert_eq!(bars.as_slice().len(), 1);
assert_eq!(bars.as_slice()[0].tick_count, 3);§Batch processing
Use TickBuffer to collect ticks with configurable ordering
and duplicate policies, then ingest as a batch:
use tickbar::{TickAggregator, Tick, TickBuffer, DuplicatePolicy};
use std::time::Duration;
let mut buf = TickBuffer::new("MSFT")
.with_allow_unordered(false)
.with_duplicate_policy(DuplicatePolicy::Last);
buf.push(Tick::from_trade(0, 100.0, 1000.0))?;
buf.push(Tick::from_trade(1_000_000_000, 100.5, 500.0))?;
buf.push(Tick::from_trade(2_000_000_000, 101.0, 750.0))?;
let mut agg = TickAggregator::builder()
.interval(Duration::from_secs(60))
.symbol("MSFT")
.build()?;
agg.push_ticks(buf.as_slice())?;
let bars = agg.finalize();
assert_eq!(bars.as_slice().len(), 1);§Gap filling
Enable gap filling on the builder to produce empty bars for periods with no trading activity:
use tickbar::{TickAggregator, Tick};
use std::time::Duration;
let mut agg = TickAggregator::builder()
.interval(Duration::from_secs(60))
.fill_gaps(true)
.symbol("GOOG")
.build()?;
// Tick at T=0s, next tick at T=300s — four 60s gaps in between
agg.push_tick(Tick::from_trade(0, 100.0, 1000.0))?;
agg.push_tick(Tick::from_trade(300_000_000_000, 101.0, 500.0))?;
let bars = agg.finalize();
// 2 real bars + 4 gap-filled bars = 6 total
assert_eq!(bars.as_slice().len(), 6);§Parallel multi-symbol
Distribute ticks by symbol, then aggregate in parallel:
use std::collections::HashMap;
use tickbar::{Tick, AggregatorConfig, TimeAlignment, aggregate_parallel};
let mut ticks_by_symbol: HashMap<String, Vec<Tick>> = HashMap::new();
ticks_by_symbol.insert(
"AAPL".into(),
vec![Tick::from_trade(0, 100.0, 1000.0),
Tick::from_trade(1_000_000_000, 100.5, 500.0)],
);
ticks_by_symbol.insert(
"MSFT".into(),
vec![Tick::from_trade(0, 200.0, 2000.0),
Tick::from_trade(2_000_000_000, 201.0, 1000.0)],
);
let config = AggregatorConfig {
interval_nanos: 60_000_000_000,
alignment: TimeAlignment::UTC,
fill_gaps: false,
forward_fill: false,
price_decimals: 8,
volume_decimals: 6,
};
let results = aggregate_parallel(ticks_by_symbol, config);
for (symbol, result) in &results {
let series = result.as_ref().unwrap();
println!("{symbol}: {} bars", series.as_slice().len());
}§Adjustments
Apply stock splits and dividend adjustments to a completed bar series. Bars with timestamps before an event’s timestamp are adjusted backward:
use tickbar::{BarSeries, Bar, AdjustmentEvent, AdjustmentType};
let mut series = BarSeries::new("AAPL", 60_000_000_000);
series.push(Bar {
timestamp_nanos: 0,
open: 10_000_000_000,
high: 10_100_000_000,
low: 9_900_000_000,
close: 10_050_000_000,
volume: 100_000,
tick_count: 10,
vwap: 10_020_000_000,
});
// Event timestamp must be strictly after the bars it adjusts
let events = vec![AdjustmentEvent {
timestamp: 60_000_000_000,
adjustment_type: AdjustmentType::Split(4.0),
}];
series.apply_adjustments(&events);
// Prices are divided by 4, volume multiplied by 4
assert_eq!(series.as_slice()[0].open, 2_500_000_000); // 10_000_000_000 / 4
assert_eq!(series.as_slice()[0].volume, 400_000); // 100_000 * 4§Python
from tickbar import TickAggregator, Tick
agg = TickAggregator(interval_secs=60)
agg.push_tick(Tick(0, 100.0, 1000.0))
bars = agg.finalize()§Memory-mapped file reader
Read packed Tick binary files via mmap:
use tickbar::{MmapTickReader, BarAggregator, TimeAlignment};
// Assuming /tmp/ticks.bin exists with packed 32-byte Tick records
if let Ok(reader) = MmapTickReader::open("/tmp/ticks.bin") {
let ticks: Vec<_> = reader.collect();
let mut agg = BarAggregator::new(
60_000_000_000, TimeAlignment::UTC, 8, 0,
false, false,
ticks.first().map(|t| t.timestamp_nanos).unwrap_or(0),
);
agg.ingest_ticks_unchecked(&ticks);
let bars = agg.finalize();
println!("{} bars from mmap", bars.as_slice().len());
}§CSV export
Write bars to CSV format:
use tickbar::{BarSeries, Bar};
let mut series = BarSeries::new("AAPL", 60_000_000_000);
series.push(Bar {
timestamp_nanos: 0, open: 10000, high: 10100, low: 9900,
close: 10050, volume: 100000, tick_count: 10, vwap: 10020,
});
let mut buf = Vec::new();
let mut wtr = csv::Writer::from_writer(&mut buf);
series.to_csv(&mut wtr)?;
drop(wtr);
let output = String::from_utf8(buf)?;
assert!(output.contains("10000")); // open price in CSV§Trading calendar
Filter ticks outside of trading hours:
use tickbar::{TradingCalendar, Tick};
let cal = TradingCalendar::new(vec![
(9 * 3_600_000_000_000, 16 * 3_600_000_000_000), // 9:00-16:00 UTC
]);
let tick = Tick::from_trade(10 * 3_600_000_000_000, 100.0, 1000.0); // 10:00 UTC
assert!(cal.is_trading_time(tick.timestamp_nanos));
let tick = Tick::from_trade(20 * 3_600_000_000_000, 100.0, 1000.0); // 20:00 UTC
assert!(!cal.is_trading_time(tick.timestamp_nanos));§Performance
| Path | Throughput | vs pandas |
|---|---|---|
| Rust native | 119M ticks/s | 25× |
| Python buffer (PEP 3118) | 6.7M ticks/s | 1.4× |
| Python numpy | 6.5M ticks/s | 1.4× |
| pandas resample | 4.7M ticks/s | 1.0× |
Benchmarked on 70K synthetic ticks from 9 S&P tickers via yfinance.
§Feature flags
| Feature | Description | Default |
|---|---|---|
python | PyO3 bindings for maturin | yes |
arrow-export | Arrow IPC export via to_arrow() | no |
polars-export | Polars DataFrame export + arrow | no |
§Quality and CI guarantees
The repository CI enforces:
cargo test --workspace(25 unit + 9 integration + 1 doc test)cargo clippy --all-features(zero warnings)RUSTFLAGS="-D missing_docs" cargo doc --no-deps(100% docs coverage)cargo-semver-checkson release candidates
§Support
- Issues: https://github.com/gregorian-09/tickbar/issues
- Repository: https://github.com/gregorian-09/tickbar
- Crates.io: https://crates.io/crates/tickbar
- PyPI: https://pypi.org/project/tickbar/
Modules§
- ffi
- Foreign function interface bindings.
- utils
- Utility modules for fixed-point conversion and time handling.
Structs§
- Adjustment
Event - A corporate action event that affects historical bar prices.
- Aggregator
Config - Configuration for batch processing.
- Bar
- A single OHLCV bar — 48 bytes.
- BarAggregator
- Core aggregation state machine.
- BarBuilder
- Mutable builder for constructing a
Barfrom incoming ticks. - BarSeries
- A time-ordered series of bars for a single symbol.
- Mmap
Tick Reader - Iterator over
Tickvalues from a memory-mapped file. - Tick
- Packed representation of a single market data tick — 32 bytes.
- Tick
Aggregator - Public-facing aggregator with a builder pattern.
- Tick
Aggregator Builder - Builder for
TickAggregator. - Tick
Buffer - Contiguous buffer of ticks for a single symbol.
- Trading
Calendar - A simple trading calendar that defines valid trading sessions.
Enums§
- Adjustment
Type - Type of corporate action adjustment.
- Duplicate
Policy - How to handle ticks with duplicate timestamps.
- Error
- Errors that can occur during tick aggregation.
- Time
Alignment - Strategy for aligning bar boundaries to a reference time.
Functions§
- aggregate_
parallel - Aggregate ticks for multiple symbols in parallel.
Type Aliases§
- Result
- Alias for
Result<T, tickbar::Error>.