Files
foxhunt/crates/database/src/schemas.rs
jgrusewski 9c3d741a08 refactor: restructure repo — crates/, bin/, testing/ layout
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>
2026-02-25 11:56:00 +01:00

224 lines
8.1 KiB
Rust

//! Database schema definitions for Foxhunt HFT Trading System
use serde::{Deserialize, Serialize};
use sqlx::types::Uuid;
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use common::types::Order as DomainOrder;
/// Configuration table schema
///
/// Represents a configuration key-value pair stored in the database.
///
/// Used for storing application settings that can be modified at runtime.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ConfigEntry {
/// Unique identifier for the configuration entry
pub id: Uuid,
/// Configuration key (unique identifier for the setting)
pub key: String,
/// Configuration value stored as a string
pub value: String,
/// Optional human-readable description of the configuration
pub description: Option<String>,
/// Timestamp when the configuration was created
pub created_at: DateTime<Utc>,
/// Timestamp when the configuration was last updated
pub updated_at: DateTime<Utc>,
}
/// Trading positions schema
///
/// Represents an open trading position in the database.
///
/// Tracks position size, entry price, and current profit/loss.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Position {
/// Unique identifier for the position
pub id: Uuid,
/// Trading symbol (e.g., "AAPL", "BTC/USD")
pub symbol: String,
/// Position size (positive for long, negative for short)
pub quantity: Decimal,
/// Price at which the position was entered
pub entry_price: Decimal,
/// Current market price of the instrument
pub current_price: Option<Decimal>,
/// Current profit/loss of the position
pub pnl: Option<Decimal>,
/// Timestamp when the position was opened
pub created_at: DateTime<Utc>,
/// Timestamp when the position was last updated
pub updated_at: DateTime<Utc>,
}
/// Database Order schema - simplified representation for persistence
///
/// Simplified order representation for database storage.
///
/// Use DomainOrder from common crate for business logic operations.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct DbOrder {
/// Unique identifier for the order
pub id: Uuid,
/// Trading symbol for the order
pub symbol: String,
/// Order type as string (MARKET, LIMIT, STOP, etc.)
pub order_type: String,
/// Order side as string (BUY, SELL)
pub side: String,
/// Order quantity
pub quantity: Decimal,
/// Order price (may be zero for market orders)
pub price: Decimal,
/// Order status as string (PENDING, FILLED, CANCELLED, etc.)
pub status: String,
/// Timestamp when the order was created
pub created_at: DateTime<Utc>,
/// Timestamp when the order was last updated
pub updated_at: DateTime<Utc>,
}
/// Type alias for the canonical Order from common crate
///
/// This provides a convenient alias to the domain Order type
/// for use within the database module while maintaining
/// separation between database and business logic representations.
pub type Order = DomainOrder;
/// Conversion from canonical domain Order to database Order
impl From<DomainOrder> for DbOrder {
fn from(order: DomainOrder) -> Self {
Self {
id: order.id,
symbol: order.symbol,
order_type: order.order_type.to_string(),
side: order.side.to_string(),
quantity: order.quantity,
price: order.price.unwrap_or(Decimal::ZERO),
status: order.status.to_string(),
created_at: order.created_at,
updated_at: order.updated_at,
}
}
}
/// Conversion from database Order to canonical domain Order
impl From<DbOrder> for DomainOrder {
fn from(db_order: DbOrder) -> Self {
use common::types::OrderSide;
use common::types::OrderType;
use common::types::OrderStatus;
// Parse enums with defaults for safety
let side = match db_order.side.to_uppercase().as_str() {
"BUY" => OrderSide::Buy,
"SELL" => OrderSide::Sell,
_ => {
tracing::error!("Invalid order side '{}' in database - this indicates data corruption", db_order.side);
return Err(anyhow::anyhow!("Invalid order side: {}", db_order.side));
}
};
let order_type = match db_order.order_type.to_uppercase().as_str() {
"MARKET" => OrderType::Market,
"LIMIT" => OrderType::Limit,
"STOP" => OrderType::Stop,
"STOP_LIMIT" => OrderType::StopLimit,
_ => {
tracing::error!("Invalid order type '{}' in database - this indicates data corruption", db_order.order_type);
return Err(anyhow::anyhow!("Invalid order type: {}", db_order.order_type));
}
};
let status = match db_order.status.to_uppercase().as_str() {
"PENDING" => OrderStatus::Pending,
"SUBMITTED" => OrderStatus::Submitted,
"FILLED" => OrderStatus::Filled,
"CANCELLED" => OrderStatus::Cancelled,
"REJECTED" => OrderStatus::Rejected,
_ => {
tracing::error!("Invalid order status '{}' in database - this indicates data corruption", db_order.status);
return Err(anyhow::anyhow!("Invalid order status: {}", db_order.status));
}
};
Self {
id: db_order.id,
symbol: db_order.symbol,
side,
quantity: db_order.quantity,
price: if db_order.price.is_zero() { None } else { Some(db_order.price) },
order_type,
status,
filled_quantity: Decimal::ZERO, // Not stored in simple schema
remaining_quantity: db_order.quantity, // Assume unfilled for simple schema
avg_fill_price: None, // Not stored in simple schema
created_at: db_order.created_at,
updated_at: db_order.updated_at,
expires_at: None,
client_order_id: None,
broker_order_id: None,
stop_loss: None,
take_profit: None,
}
}
}
/// Model configuration schema
///
/// Configuration for ML models including storage locations,
/// metadata, and activation status.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ModelConfig {
/// Unique identifier for the model configuration
pub id: Uuid,
/// Human-readable model name
pub name: String,
/// Model version string
pub version: String,
/// S3 path where the model is stored
pub s3_path: String,
/// Optional local cache path for the model
pub cache_path: Option<String>,
/// Additional metadata stored as JSON
pub metadata: serde_json::Value,
/// Whether this model configuration is currently active
pub is_active: bool,
/// Timestamp when the configuration was created
pub created_at: DateTime<Utc>,
/// Timestamp when the configuration was last updated
pub updated_at: DateTime<Utc>,
}
/// Model version tracking schema
///
/// Tracks different versions of ML models with performance metrics,
/// training metadata, and integrity information.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ModelVersion {
/// Unique identifier for the model version
pub id: Uuid,
/// Reference to the parent model configuration
pub model_config_id: Uuid,
/// Version string for this specific model version
pub version: String,
/// S3 path where this model version is stored
pub s3_path: String,
/// Optional local cache path for this model version
pub cache_path: Option<String>,
/// Optional checksum for integrity verification
pub checksum: Option<String>,
/// Size of the model file in bytes
pub size_bytes: Option<i64>,
/// Performance metrics stored as JSON
pub performance_metrics: serde_json::Value,
/// Training metadata and parameters stored as JSON
pub training_metadata: serde_json::Value,
/// Whether this is the current active version
pub is_current: bool,
/// Timestamp when the version was created
pub created_at: DateTime<Utc>,
/// Timestamp when the version was last updated
pub updated_at: DateTime<Utc>,
}