The real_ prefix was misleading — there is no fake data loader. Mechanical rename across 18 source files, no logic changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
155 lines
3.7 KiB
Rust
155 lines
3.7 KiB
Rust
//! OHLCV Bar builder for testing
|
|
|
|
use chrono::{DateTime, Duration, Utc};
|
|
use ml::data_loader::OHLCVBar;
|
|
|
|
/// Builder for creating OHLCV bars
|
|
pub struct BarBuilder {
|
|
timestamp: DateTime<Utc>,
|
|
open: f64,
|
|
high: f64,
|
|
low: f64,
|
|
close: f64,
|
|
volume: f64,
|
|
}
|
|
|
|
impl BarBuilder {
|
|
pub fn new() -> Self {
|
|
let price = 100.0;
|
|
Self {
|
|
timestamp: Utc::now(),
|
|
open: price,
|
|
high: price * 1.01,
|
|
low: price * 0.99,
|
|
close: price,
|
|
volume: 1000.0,
|
|
}
|
|
}
|
|
|
|
pub fn timestamp(mut self, timestamp: DateTime<Utc>) -> Self {
|
|
self.timestamp = timestamp;
|
|
self
|
|
}
|
|
|
|
pub fn price(mut self, price: f64) -> Self {
|
|
self.open = price;
|
|
self.close = price;
|
|
self.high = price * 1.01;
|
|
self.low = price * 0.99;
|
|
self
|
|
}
|
|
|
|
pub fn open(mut self, open: f64) -> Self {
|
|
self.open = open;
|
|
self
|
|
}
|
|
|
|
pub fn high(mut self, high: f64) -> Self {
|
|
self.high = high;
|
|
self
|
|
}
|
|
|
|
pub fn low(mut self, low: f64) -> Self {
|
|
self.low = low;
|
|
self
|
|
}
|
|
|
|
pub fn close(mut self, close: f64) -> Self {
|
|
self.close = close;
|
|
self
|
|
}
|
|
|
|
pub fn volume(mut self, volume: f64) -> Self {
|
|
self.volume = volume;
|
|
self
|
|
}
|
|
|
|
pub fn bullish(mut self) -> Self {
|
|
self.close = self.open * 1.05;
|
|
self.high = self.close * 1.01;
|
|
self.low = self.open * 0.99;
|
|
self
|
|
}
|
|
|
|
pub fn bearish(mut self) -> Self {
|
|
self.close = self.open * 0.95;
|
|
self.high = self.open * 1.01;
|
|
self.low = self.close * 0.99;
|
|
self
|
|
}
|
|
|
|
pub fn build(self) -> OHLCVBar {
|
|
OHLCVBar {
|
|
timestamp: self.timestamp,
|
|
open: self.open,
|
|
high: self.high,
|
|
low: self.low,
|
|
close: self.close,
|
|
volume: self.volume,
|
|
}
|
|
}
|
|
|
|
/// Build a series of bars with specified spacing
|
|
pub fn build_series(self, count: usize, interval_minutes: i64) -> Vec<OHLCVBar> {
|
|
let mut bars = Vec::with_capacity(count);
|
|
let mut current = self.build();
|
|
|
|
for _ in 0..count {
|
|
bars.push(current.clone());
|
|
current.timestamp = current.timestamp + Duration::minutes(interval_minutes);
|
|
// Add small random walk
|
|
current.open = current.close;
|
|
current.close = current.close * (1.0 + (rand::random::<f64>() - 0.5) * 0.02);
|
|
current.high = current.close.max(current.open) * 1.005;
|
|
current.low = current.close.min(current.open) * 0.995;
|
|
}
|
|
|
|
bars
|
|
}
|
|
}
|
|
|
|
impl Default for BarBuilder {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_bar_builder_defaults() {
|
|
let bar = BarBuilder::new().build();
|
|
assert_eq!(bar.open, 100.0);
|
|
assert!(bar.high >= bar.open);
|
|
assert!(bar.low <= bar.open);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bullish_bar() {
|
|
let bar = BarBuilder::new().bullish().build();
|
|
assert!(bar.close > bar.open);
|
|
assert_eq!(bar.close / bar.open, 1.05);
|
|
}
|
|
|
|
#[test]
|
|
fn test_bearish_bar() {
|
|
let bar = BarBuilder::new().bearish().build();
|
|
assert!(bar.close < bar.open);
|
|
assert_eq!(bar.close / bar.open, 0.95);
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_series() {
|
|
let bars = BarBuilder::new().build_series(10, 5);
|
|
assert_eq!(bars.len(), 10);
|
|
|
|
// Check timestamps are 5 minutes apart
|
|
for i in 0..bars.len() - 1 {
|
|
let diff = bars[i + 1].timestamp - bars[i].timestamp;
|
|
assert_eq!(diff.num_minutes(), 5);
|
|
}
|
|
}
|
|
}
|