Skip to main content

Crate tickbar

Crate tickbar 

Source
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 ingestionTick is a 32-byte repr(C) struct holding timestamp_nanos, fixed-point price, fixed-point volume, and flags. Use TickBuffer for batched ingestion with ordering and dedup policies. Use MmapTickReader for memory-mapped I/O from binary tick files.

  • Bar aggregationTickAggregator is a one-pass state machine built via its TickAggregatorBuilder. It advances bar boundaries on each tick, optionally fills gaps, and tracks per-bar VWAP. BarAggregator is the lower-level engine. Use aggregate_parallel for multi-symbol parallelism.

  • OutputBarSeries holds completed Bars 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

PathThroughputvs pandas
Rust native119M ticks/s25×
Python buffer (PEP 3118)6.7M ticks/s1.4×
Python numpy6.5M ticks/s1.4×
pandas resample4.7M ticks/s1.0×

Benchmarked on 70K synthetic ticks from 9 S&P tickers via yfinance.

§Feature flags

FeatureDescriptionDefault
pythonPyO3 bindings for maturinyes
arrow-exportArrow IPC export via to_arrow()no
polars-exportPolars DataFrame export + arrowno

§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-checks on release candidates

§Support

Modules§

ffi
Foreign function interface bindings.
utils
Utility modules for fixed-point conversion and time handling.

Structs§

AdjustmentEvent
A corporate action event that affects historical bar prices.
AggregatorConfig
Configuration for batch processing.
Bar
A single OHLCV bar — 48 bytes.
BarAggregator
Core aggregation state machine.
BarBuilder
Mutable builder for constructing a Bar from incoming ticks.
BarSeries
A time-ordered series of bars for a single symbol.
MmapTickReader
Iterator over Tick values from a memory-mapped file.
Tick
Packed representation of a single market data tick — 32 bytes.
TickAggregator
Public-facing aggregator with a builder pattern.
TickAggregatorBuilder
Builder for TickAggregator.
TickBuffer
Contiguous buffer of ticks for a single symbol.
TradingCalendar
A simple trading calendar that defines valid trading sessions.

Enums§

AdjustmentType
Type of corporate action adjustment.
DuplicatePolicy
How to handle ticks with duplicate timestamps.
Error
Errors that can occur during tick aggregation.
TimeAlignment
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>.