diff --git a/.env.dev b/.env.dev index af1da78..96f0c0e 100644 --- a/.env.dev +++ b/.env.dev @@ -1,5 +1,5 @@ POSTGRES_USER=finwar POSTGRES_PASSWORD=password POSTGRES_DB=finwar -DATABASE_URL=postgresql://finwar:password@localhost:5432/finwar +FINWAR_MARKET_DATABASE_URL=postgresql://finwar:password@localhost:5432/finwar RUST_LOG=info diff --git a/docker-compose.yml b/docker-compose.yml index 2253c24..d6a98c8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,7 @@ services: context: ./servers/rust-server dockerfile: docker/Dockerfile environment: - DATABASE_URL: postgresql://${POSTGRES_USER:-finwar}:${POSTGRES_PASSWORD:-password}@timescaledb:5432/${POSTGRES_DB:-finwar} + FINWAR_MARKET_DATABASE_URL: postgresql://${POSTGRES_USER:-finwar}:${POSTGRES_PASSWORD:-password}@timescaledb:5432/${POSTGRES_DB:-finwar} RUST_LOG: info command: migrate depends_on: @@ -40,7 +40,8 @@ services: ports: - "4444:4444" environment: - DATABASE_URL: postgresql://${POSTGRES_USER:-finwar}:${POSTGRES_PASSWORD:-password}@timescaledb:5432/${POSTGRES_DB:-finwar} + FINWAR_MARKET_DATABASE_URL: postgresql://${POSTGRES_USER:-finwar}:${POSTGRES_PASSWORD:-password}@timescaledb:5432/${POSTGRES_DB:-finwar} + FINWAR_MARKET_TARGET_TICKER: NVDA RUST_LOG: info command: serve depends_on: diff --git a/servers/rust-server/Cargo.toml b/servers/rust-server/Cargo.toml index de07876..2a4d85c 100644 --- a/servers/rust-server/Cargo.toml +++ b/servers/rust-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "finwar-market" -version = "0.2.0" +version = "0.4.0" authors = ["Coding Kelps "] autotests = true categories = ["finance", "simulation"] diff --git a/servers/rust-server/README.md b/servers/rust-server/README.md index 6a77381..438b6c6 100644 --- a/servers/rust-server/README.md +++ b/servers/rust-server/README.md @@ -89,3 +89,13 @@ or in powershell: ```powershell Invoke-RestMethod -Uri http://localhost:4444/api/enroll -Method POST -ContentType "application/json" -Body '{"name":"bot0"}' ``` + +## Configuration + +| Name | Description | Default | +| :------------------------------------ | :-------------------------------------------------------------------- | :---------------------: | +| FINWAR_MARKET_ADDR | The HTTP server address. | `0.0.0.0` | +| FINWAR_MARKET_PORT | The HTTP server port | `4444` | +| FINWAR_MARKET_INTERVAL_SECONDS | The internal time passed (in seconds) in the Market each real second. | `60` | +| FINWAR_MARKET_TARGET_SYMBOL | The target stock symbol on which the Market simulation is based on. | `AAPL` | +| FINWAR_MARKET_DATABASE_URL | The URL with authentication information of the backend database. | | diff --git a/servers/rust-server/src/cli.rs b/servers/rust-server/src/cli.rs index bf3e319..35d7c85 100644 --- a/servers/rust-server/src/cli.rs +++ b/servers/rust-server/src/cli.rs @@ -24,7 +24,8 @@ pub async fn run() -> Result<(), crate::error::Error> { match opts.command { Command::Serve => { let database_url = - std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + std::env::var("FINWAR_MARKET_DATABASE_URL") + .expect("FINWAR_MARKET_DATABASE_URL must be set"); let db_connection = sea_orm::Database::connect(&database_url) .await @@ -34,7 +35,8 @@ pub async fn run() -> Result<(), crate::error::Error> { }, Command::Migrate => { let database_url = - std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + std::env::var("FINWAR_MARKET_DATABASE_URL") + .expect("FINWAR_MARKET_DATABASE_URL must be set"); let db_connection = sea_orm::Database::connect(&database_url) .await diff --git a/servers/rust-server/src/clock.rs b/servers/rust-server/src/clock.rs index c71bcdf..6677dbd 100644 --- a/servers/rust-server/src/clock.rs +++ b/servers/rust-server/src/clock.rs @@ -28,7 +28,7 @@ impl MarketClock { } pub fn advance(&mut self, seconds: u64) { - self.current_time = self.current_time + Duration::from_secs(seconds * 60); + self.current_time = self.current_time + Duration::from_secs(seconds); } } @@ -50,4 +50,4 @@ pub async fn time(State(state): State) -> Result]) -> Vec { pub async fn chart(state: &AppState) -> Result { let records = stocks_history::Entity::find() - .filter(stocks_history::Column::Symbol.eq("AAPL")) + .filter(stocks_history::Column::Symbol.eq(&state.target_symbol)) .order_by_asc(stocks_history::Column::Time) .limit(300) .all(&state.db) diff --git a/servers/rust-server/src/main.rs b/servers/rust-server/src/main.rs index 6794a22..d19b250 100644 --- a/servers/rust-server/src/main.rs +++ b/servers/rust-server/src/main.rs @@ -12,15 +12,11 @@ pub mod trade; use axum::{Router, response::Redirect, routing::get, routing::post}; use dotenvy::dotenv; -use sea_orm::DatabaseConnection; -use sea_orm::EntityTrait; -use sea_orm::QueryOrder; +use sea_orm::{DatabaseConnection, EntityTrait, QueryOrder, QueryFilter, ColumnTrait}; use std::sync::Arc; use tokio::sync::RwLock; -use tower_http::services::ServeDir; -use tower_http::trace::TraceLayer; -use tracing::Level; -use tracing::event; +use tower_http::{services::ServeDir, trace::TraceLayer}; +use tracing::{Level, event}; use crate::bot::bot_detail; use crate::clock::time; @@ -49,16 +45,25 @@ async fn main() -> Result<(), Error> { /// Start the HTTP server. Separated out so `main` can dispatch to either /// the server or other management subcommands (like `migrate`). pub async fn run_server(db: DatabaseConnection) -> Result<(), Error> { - let tick_interval_seconds = std::env::var("TICK_INTERVAL_SECONDS") - .unwrap_or_else(|_| "1".to_string()) + let addr = std::env::var("FINWAR_MARKET_ADDR") + .unwrap_or_else(|_| "0.0.0.0".to_string()); + + let port = std::env::var("FINWAR_MARKET_PORT") + .unwrap_or_else(|_| "4444".to_string()) + .parse::() + .expect("FINWAR_MARKET_PORT must be a valid integer"); + + let tick_interval_seconds = std::env::var("FINWAR_MARKET_INTERVAL_SECONDS") + .unwrap_or_else(|_| "60".to_string()) .parse::() - .expect("TICK_INTERVAL_SECONDS must be a valid integer"); + .expect("FINWAR_MARKET_INTERVAL_SECONDS must be a valid integer"); - let addr = "0.0.0.0"; - let port = 4444; + let target_symbol = std::env::var("FINWAR_MARKET_TARGET_SYMBOL") + .unwrap_or_else(|_| "AAPL".to_string()); let start_time = entity::stocks_history::Entity::find() .order_by_asc(entity::stocks_history::Column::Time) + .filter(entity::stocks_history::Column::Symbol.eq(&target_symbol)) .one(&db) .await .map_err(Error::InitDb)? @@ -73,7 +78,7 @@ pub async fn run_server(db: DatabaseConnection) -> Result<(), Error> { ))); start_clock(Arc::clone(&clock)); - let state = AppState::new(db, clock).await.map_err(Error::State)?; + let state = AppState::new(db, clock, target_symbol).await.map_err(Error::State)?; let app = Router::new() .route("/", get(|| async { Redirect::to("/home") })) diff --git a/servers/rust-server/src/state.rs b/servers/rust-server/src/state.rs index 5fee55d..637e7e4 100644 --- a/servers/rust-server/src/state.rs +++ b/servers/rust-server/src/state.rs @@ -30,16 +30,18 @@ pub struct AppState { pub starting_cash: f64, pub starting_assets: i32, pub clock: SharedClock, + pub target_symbol: String, } impl AppState { - pub async fn new(db: DatabaseConnection, clock: SharedClock) -> Result { + pub async fn new(db: DatabaseConnection, clock: SharedClock, target_symbol: String) -> Result { Ok(AppState { db, uuid_prefix_length: 18, starting_cash: 10000.0, starting_assets: 0, clock, + target_symbol, }) } } diff --git a/servers/rust-server/src/trade.rs b/servers/rust-server/src/trade.rs index 73b28e3..994c714 100644 --- a/servers/rust-server/src/trade.rs +++ b/servers/rust-server/src/trade.rs @@ -102,6 +102,7 @@ async fn get_current_price(state: &AppState) -> Result { let current_time = state.clock.read().await.current_time(); let stock = stocks_history::Entity::find() + .filter(stocks_history::Column::Symbol.eq(&state.target_symbol)) .filter(stocks_history::Column::Time.lte(current_time)) .order_by_desc(stocks_history::Column::Time) .one(&state.db)