A modern Zig library for fetching and parsing Open-High-Low-Close-Volume (OHLCV) financial data from remote CSV files—no API keys or registration required.
Multiple Data Sources: HTTP, local files, in-memory data
Preset Datasets: BTC, S&P 500, ETH, Gold (from GitHub or local)
High-Performance Parsing:
- Standard CSV parser with robust error handling
- NEW: Streaming CSV parser for processing large datasets without full memory load
- NEW: Optimized fast parser with SIMD-aware line counting
- Handles headers, skips invalid/zero rows automatically
Memory Management:
- NEW: Memory pooling system for efficient allocation reuse
- NEW: IndicatorArena for batch indicator calculations
- All allocations are explicit and easy to free
Time Series Management: Efficient slicing, filtering, and operations
33 Technical Indicators: Complete suite including trend (SMA, EMA, ADX), momentum (RSI, MACD, Stochastic), volatility (Bollinger Bands, ATR, Keltner Channels), volume (OBV, MFI, CMF), and advanced systems (Ichimoku Cloud, Heikin Ashi)
Performance Testing:
- NEW: Comprehensive performance benchmarks
- NEW: Streaming vs non-streaming comparison tools
- Memory profiling capabilities
Extensible: add new data sources, indicators, or parsers easily
Build the library and demo:
zig build
Run the demo application:
zig build run
The demo fetches S&P 500 data and prints a sample of parsed rows.
Run tests:
zig build testRun benchmarks:
zig build benchmark # Basic benchmark zig build benchmark-performance # Comprehensive performance tests zig build benchmark-streaming # Compare streaming vs non-streaming zig build profile-memory # Memory usage profiler
# Fetch from GitHub
zig fetch --save https://github.com/Mario-SO/ohlcv/archive/refs/heads/main.tar.gzconstohlcv_dep=b.dependency("ohlcv", .{
.target=target,
.optimize=optimize,
});
exe.root_module.addImport("ohlcv", ohlcv_dep.module("ohlcv"));constohlcv=@import("ohlcv");
// Your code herevarseries=tryohlcv.fetchPreset(.btc_usd, allocator);
deferseries.deinit();See USAGE.md for detailed integration guide.
conststd=@import("std");
constohlcv=@import("ohlcv");
pubfnmain() !void {
vargpa=std.heap.GeneralPurposeAllocator(.{}){};
defer_=gpa.deinit();
constallocator=gpa.allocator();
// Fetch preset datavarseries=tryohlcv.fetchPreset(.sp500, allocator);
deferseries.deinit();
std.debug.print("Fetched {d} rows of data.\n", .{series.len()});
// Slice by time rangeconstfrom_ts=1672531200; // 2023-01-01constto_ts=1704067199; // 2023-12-31varfiltered=tryseries.sliceByTime(from_ts, to_ts);
deferfiltered.deinit();
// Calculate SMAconstsma=ohlcv.SmaIndicator{ .u32_period=20 };
varresult=trysma.calculate(filtered, allocator);
deferresult.deinit();
// Print sampleconstcount=@min(5, result.len());
for (0..count) |i| {
std.debug.print("TS: {d}, SMA: {d:.2}\n", .{result.arr_timestamps[i], result.arr_values[i]});
}
}conststd=@import("std");
constohlcv=@import("lib/ohlcv.zig");
pubfnprocessLargeDataset(allocator: std.mem.Allocator) !void {
// Use streaming parser for large filesvarparser=ohlcv.StreamingCsvParser.init(allocator);
deferparser.deinit();
// Process data in chunks without loading entire fileconstfile=trystd.fs.cwd().openFile("huge_dataset.csv", .{});
deferfile.close();
while (tryparser.parseChunk(file.reader())) |chunk| {
deferchunk.deinit();
// Process each chunk independentlyfor (chunk.rows) |row| {
// Your processing logic here
}
}
}// Use memory pool for efficient indicator calculationsvarpool=tryohlcv.MemoryPool.init(allocator, 1024*1024); // 1MB pooldeferpool.deinit();
vararena=ohlcv.IndicatorArena.init(&pool);
// All allocations within arena are automatically managedconstresult=trysma.calculateWithArena(series, &arena);
// No need to manually free result - arena handles itOhlcvRow— Full OHLCV record:pubconstOhlcvRow=struct { u64_timestamp: u64, f64_open: f64, f64_high: f64, f64_low: f64, f64_close: f64, u64_volume: u64, };
OhlcBar— OHLC without volume:pubconstOhlcBar=struct { u64_timestamp: u64, f64_open: f64, f64_high: f64, f64_low: f64, f64_close: f64, };
PresetSource— Available presets:pubconstPresetSource=enum { btc_usd, sp500, eth_usd, gold_usd };
TimeSeries— Data container with operationsIndicatorResult— Results from indicators
Data Sources:
DataSource,HttpDataSource,FileDataSource,MemoryDataSourceParsers:
CsvParser- Standard CSV parser with robust error handlingStreamingCsvParser- Process large files in chunks- Fast parser primitives for optimized parsing
Memory Management:
MemoryPool- Reusable memory allocation poolIndicatorArena- Arena allocator for batch calculations
33 Indicators: Including trend analysis (SMA, EMA, WMA, ADX, DMI, Parabolic SAR), momentum oscillators (RSI, MACD, Stochastic, Stochastic RSI, Ultimate Oscillator, TRIX), volatility bands (Bollinger Bands, Keltner Channels, Donchian Channels, Price Channels), volume analysis (OBV, MFI, CMF, Force Index, A/D Line), and advanced systems (Ichimoku Cloud, Heikin Ashi, Pivot Points, Elder Ray, Aroon, Zig Zag)
Convenience:
fetchPreset(source: PresetSource, allocator) !TimeSeries
For detailed usage, see USAGE.md
ParseError— Possible parsing errors:InvalidFormat,InvalidTimestamp,InvalidOpen,InvalidHigh,InvalidLow,InvalidClose,InvalidVolume,InvalidDateFormat,DateBeforeEpoch,OutOfMemory,EndOfStream
FetchError—HttpErroror anyParseError
ohlcv/
- CLAUDE.md # Claude Code guidance
- docs/ # Extended documentation
- CHANGELOG.md # Project changelog
- PROFILING.md # Performance profiling guide
- USAGE.md # Detailed usage guide
- README.md # Documentation index
- build.zig
- build.zig.zon
- benchmark/
- performance_benchmark.zig
- simple_benchmark.zig
- simple_memory_profiler.zig
- streaming_benchmark.zig
- data/
- btc.csv
- eth.csv
- gold.csv
- sp500.csv
- demo.zig
- lib/
- data_source/
- data_source.zig
- file_data_source.zig
- http_data_source.zig
- memory_data_source.zig
- indicators/ # 33 technical indicators
- indicator_result.zig
- [Single-line indicators: SMA, EMA, WMA, RSI, ATR, ROC, Momentum, etc.]
- [Multi-line indicators: MACD, Bollinger Bands, Ichimoku Cloud, etc.]
- README.md # Complete indicator documentation
- ohlcv.zig
- parser/
- csv_parser.zig
- fast_parser.zig # Optimized parsing primitives
- streaming_csv_parser.zig # Chunked processing
- utils/
- date.zig
- memory_pool.zig # Memory pooling system
- time_series.zig
- types/
- ohlc_bar.zig
- ohlcv_row.zig
- README.md
- scripts/
- boxify.ts
- update_assets.py
- test/
- fixtures/
- sample_data.csv
- integration/
- test_full_workflow.zig
- README.md
- test_all.zig
- test_helpers.zig
- unit/
- test_csv_parser.zig
- test_data_sources.zig
- test_indicators.zig
- test_time_series.zig
- zig-out/
- The parser skips:
- The header row
- Rows with invalid format or parser errors
- Rows with pre-1970 dates
- Rows where any of the OHLCV values are zero
- This means the number of parsed rows may be less than the number of lines in the CSV file.
- Add new formats: add new parser functions in
parser.zig - PRs and issues welcome!
- demo.zig — Full example usage
- USAGE.md — Detailed usage guide and integration examples
- lib/ohlcv.zig — Public API
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ using Zig