Files
foxhunt/tests/test_common/src/builders/position_builder.rs
jgrusewski 2df1ea92e1 feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign
BREAKING CHANGES:
- Removed orphaned dqn.rs monolithic trainer (4,975 lines)
- Removed orphaned dqn_ensemble.rs module (816 lines)
- Removed orphaned tft.rs and tft_complete_int8_integration_test.rs
- TFT trainer split into modular directory structure

DQN Module Refactoring:
- Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs)
- Fixed hyperopt 39D search space (continuous params only)
- Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions
- use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues)

Clean Module Structure:
- ml/src/trainers/dqn/ directory with proper mod.rs exports
- ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs
- All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness

Documentation:
- Added comprehensive docs in docs/codebase-cleanup/
- ADR-001 for DQN refactoring decisions
- Rainbow DQN component matrix and quick reference guides

Build Status: Compiles with zero errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:46:13 +01:00

150 lines
3.4 KiB
Rust

//! Position builder for testing
use chrono::{DateTime, Utc};
/// Mock position for testing
#[derive(Clone, Debug)]
pub struct MockPosition {
pub symbol: String,
pub quantity: f64,
pub entry_price: f64,
pub current_price: f64,
pub timestamp: DateTime<Utc>,
}
impl MockPosition {
pub fn pnl(&self) -> f64 {
(self.current_price - self.entry_price) * self.quantity
}
pub fn pnl_percent(&self) -> f64 {
((self.current_price - self.entry_price) / self.entry_price) * 100.0
}
}
/// Builder for creating mock positions
pub struct PositionBuilder {
symbol: String,
quantity: f64,
entry_price: f64,
current_price: f64,
timestamp: DateTime<Utc>,
}
impl PositionBuilder {
pub fn new() -> Self {
Self {
symbol: "AAPL".to_string(),
quantity: 100.0,
entry_price: 150.0,
current_price: 150.0,
timestamp: Utc::now(),
}
}
pub fn symbol(mut self, symbol: &str) -> Self {
self.symbol = symbol.to_string();
self
}
pub fn long(mut self, quantity: f64) -> Self {
self.quantity = quantity.abs();
self
}
pub fn short(mut self, quantity: f64) -> Self {
self.quantity = -quantity.abs();
self
}
pub fn entry_price(mut self, price: f64) -> Self {
self.entry_price = price;
self
}
pub fn current_price(mut self, price: f64) -> Self {
self.current_price = price;
self
}
pub fn profitable(mut self, percent: f64) -> Self {
self.current_price = self.entry_price * (1.0 + percent / 100.0);
self
}
pub fn losing(mut self, percent: f64) -> Self {
self.current_price = self.entry_price * (1.0 - percent / 100.0);
self
}
pub fn build(self) -> MockPosition {
MockPosition {
symbol: self.symbol,
quantity: self.quantity,
entry_price: self.entry_price,
current_price: self.current_price,
timestamp: self.timestamp,
}
}
}
impl Default for PositionBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_position_builder() {
let pos = PositionBuilder::new()
.symbol("TSLA")
.long(50.0)
.entry_price(200.0)
.current_price(220.0)
.build();
assert_eq!(pos.symbol, "TSLA");
assert_eq!(pos.quantity, 50.0);
assert_eq!(pos.pnl(), 1000.0); // (220 - 200) * 50
assert_eq!(pos.pnl_percent(), 10.0);
}
#[test]
fn test_short_position() {
let pos = PositionBuilder::new()
.short(100.0)
.entry_price(150.0)
.current_price(140.0)
.build();
assert!(pos.quantity < 0.0);
assert_eq!(pos.pnl(), -1000.0); // (140 - 150) * -100
}
#[test]
fn test_profitable_position() {
let pos = PositionBuilder::new()
.entry_price(100.0)
.profitable(10.0)
.build();
assert_eq!(pos.current_price, 110.0);
assert_eq!(pos.pnl_percent(), 10.0);
}
#[test]
fn test_losing_position() {
let pos = PositionBuilder::new()
.entry_price(100.0)
.losing(5.0)
.build();
assert_eq!(pos.current_price, 95.0);
assert_eq!(pos.pnl_percent(), -5.0);
}
}