Two-task plan for #3: (1) the meta module — InstrumentGeometry + from_sidecar_bytes parser + serde_json dep + README update + unit tests; (2) DataServer::symbol_meta + deterministic tempdir integration tests. refs #3
data-server
High-performance, thread-safe loader and cache for binary market data files
(Pepperstone M1 / tick format). Each file is a ZIP archive containing
one .bin entry of tightly packed records; it is read and parsed exactly once,
then shared lock-free across many readers via Arc<[T]> chunks.
Standalone leaf crate — depends only on chrono, regex, zip.
Add as dependency
data-server = { git = "http://192.168.178.103:3000/Brummel/data-server.git", branch = "main" }
Interface
Entry points
| Item | Description |
|---|---|
DataServer::new(base_path) -> DataServer |
Scan a directory and build the symbol index. Wrap in Arc to stream (see below). |
init_data_server(base_path) -> Arc<DataServer> |
Initialize the global singleton. Panics if called twice. |
init_default_data_server() |
Initialize the global from DEFAULT_DATA_PATH if that dir exists. Idempotent / safe to call repeatedly. |
get_data_server() -> Option<Arc<DataServer>> |
The global instance, or None if not initialized. |
DEFAULT_DATA_PATH: &str |
"/mnt/tickdata/Pepperstone". |
Querying & streaming (DataServer)
| Method | Returns |
|---|---|
has_symbol(symbol) -> bool |
Whether the symbol has any files. |
symbols() -> Vec<Arc<str>> |
All known symbols, sorted. |
file_count(symbol, DataFormat) -> Option<usize> |
Number of files for that symbol/format. |
stream_m1(symbol) / stream_tick(symbol) |
Option<SymbolChunkIter<_>> over all data. |
stream_m1_windowed(symbol, from_ms, to_ms) / stream_tick_windowed(...) |
Same, restricted to an inclusive [from_ms, to_ms] Unix-millisecond window (Option<i64> bounds; files outside are skipped without I/O). |
The stream_* methods take self: &Arc<Self> (background prefetch needs a
shared owner), so the server must be an Arc<DataServer>. They return
None if the symbol is unknown (windowed: also if no file falls in the window).
Consuming chunks (SymbolChunkIter<T>)
next_chunk(&mut self) -> Option<Arc<[T]>> yields successive chunks
(≤ CHUNK_SIZE = 1024 records) in chronological order, prefetching the next
file in the background. This is an inherent method, not the Iterator
trait — drive it with a while let loop. While the iterator is alive the
symbol's data is pinned in the cache; on Drop the symbol is released and, if
no other consumer holds it, evicted.
Record types (data_server::records)
M1Parsed { time_ms: i64, open, high, low, close, spread: f64, volume: i64 }TickParsed { time_ms: i64, ask: f64, bid: f64 }enum DataFormat { M1, Tick }trait HasTimestamp { fn time_ms(&self) -> i64; }(implemented by both parsed types)delphi_to_unix_ms(f64) -> i64,unix_ms_to_year_month(i64) -> (u16, u8)RawM1Record/RawTickRecord: the on-disk#[repr(C, packed)]layouts, plusM1Parsed::from_raw/TickParsed::from_raw.
All timestamps are Unix milliseconds (converted from the Delphi
TDateTime epoch on load).
Lower-level modules
data_server::loader—load_m1_file/load_tick_file(ZIP → parsed chunks),CHUNK_SIZE.data_server::cache—FileCache,FileKey,ChunkVec<T>. The many-readers / rare-writer cache used internally; rarely needed directly.
Example
use std::sync::Arc;
use data_server::DataServer;
let server = Arc::new(DataServer::new("/mnt/tickdata/Pepperstone"));
if server.has_symbol("EURUSD") {
let mut it = server
.stream_m1_windowed("EURUSD", Some(from_ms), Some(to_ms))
.expect("symbol has data in window");
while let Some(chunk) = it.next_chunk() {
for r in chunk.iter() {
// r: M1Parsed — r.time_ms, r.open, r.high, r.low, r.close, ...
}
}
}
Concurrency
Designed to back hundreds of single-threaded VMs in parallel: DataServer is
Send + Sync, shared as Arc<DataServer>. Each file loads once; concurrent
requests for the same file block on a Condvar rather than loading twice.
Data files
Files must be named SYMBOL_YYYY_MM.m1 or SYMBOL_YYYY_MM.tick (the symbol
may contain dots, e.g. AAPL.US_2006_08.m1) and be ZIP archives containing a
single *.bin entry of packed records.