repos
/ finance-rust master

finance-rust

mirror archived upstream

Single-binary self-hosted market watcher for stocks, ETFs, indexes, and futures: live charts, key stats, fundamentals, SEC filings, and SSE streaming.

axumdockerfinancerustself-hostedsqlitestocksvite

2.4 KB · 90 lines · Rust Raw History
 1mod app;
 2mod compute;
 3mod db;
 4mod guard;
 5mod market;
 6mod middleware;
 7mod models;
 8mod providers;
 9mod render;
10mod routes;
11mod scheduler;
12mod seed;
13mod sp500;
14mod stream;
15mod templates;
16mod watchlist;
17
18pub use app::{AppState, Config};
19
20use std::net::SocketAddr;
21
22#[tokio::main]
23async fn main() -> anyhow::Result<()> {
24    dotenvy::dotenv().ok();
25    tracing_subscriber::fmt()
26        .with_env_filter(
27            tracing_subscriber::EnvFilter::try_from_default_env()
28                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info,sqlx=warn")),
29        )
30        .init();
31
32    // Subcommand dispatch; anything else falls through to the HTTP server.
33    let mut args = std::env::args().skip(1);
34    if let Some(cmd) = args.next() {
35        match cmd.as_str() {
36            "seed" => return run_seed().await,
37            "--help" | "-h" => {
38                print_usage();
39                return Ok(());
40            }
41            other => {
42                eprintln!("unknown subcommand: {other}");
43                print_usage();
44                std::process::exit(2);
45            }
46        }
47    }
48
49    serve().await
50}
51
52fn print_usage() {
53    eprintln!(
54        "finance: single-binary axum market-watching app\n\
55         \n\
56         Usage:\n  \
57           finance         run the HTTP server\n  \
58           finance seed    (re-)import the curated universe and its history\n"
59    );
60}
61
62async fn run_seed() -> anyhow::Result<()> {
63    let state = AppState::from_env().await?;
64    let client = providers::http::build_client(&state.config);
65    // Yahoo serves deep daily history (one `interval=1d&range=max` call per
66    // symbol) as well as live quotes; it is the app's only price source.
67    let history = providers::yahoo::YahooProvider::new(client);
68    seed::run(&state.pool, &state.config, &history).await
69}
70
71async fn serve() -> anyhow::Result<()> {
72    let port: u16 = std::env::var("PORT")
73        .ok()
74        .and_then(|v| v.parse().ok())
75        .unwrap_or(8000);
76
77    let state = AppState::from_env().await?;
78    scheduler::spawn(state.pool.clone(), state.config.clone(), state.hub.clone());
79    let router = app::router(state);
80
81    let addr = SocketAddr::from(([0, 0, 0, 0], port));
82    let listener = tokio::net::TcpListener::bind(addr).await?;
83    tracing::info!(
84        "finance listening on http://{addr} ({} S&P 500 movers names loaded)",
85        sp500::count()
86    );
87    axum::serve(listener, router).await?;
88    Ok(())
89}