Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
3.4 KiB
Rust
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);
|
|
}
|
|
}
|