first comit

This commit is contained in:
zastian-dev
2026-02-09 19:20:47 +00:00
commit 79625743bd
24 changed files with 8726 additions and 0 deletions

207
src/main.rs Normal file
View File

@@ -0,0 +1,207 @@
//! Tech Giants Trading Bot - Algorithmic trading system using Alpaca API.
//!
//! A momentum + mean-reversion hybrid strategy for 50 stocks across multiple sectors.
//! Uses RSI, MACD, EMA, ADX, Bollinger Bands, and momentum indicators for trade signals.
//!
//! # Usage
//!
//! ```bash
//! # Live paper trading
//! invest-bot
//!
//! # Backtest with historical data
//! invest-bot --backtest --years 3
//! invest-bot --backtest --years 5 --capital 50000
//! ```
mod alpaca;
mod backtester;
mod bot;
mod config;
mod dashboard;
mod indicators;
mod paths;
mod types;
use anyhow::{Context, Result};
use clap::Parser;
use std::env;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
use crate::alpaca::AlpacaClient;
use crate::backtester::{save_backtest_results, Backtester};
use crate::bot::TradingBot;
use crate::config::{Timeframe, DEFAULT_INITIAL_CAPITAL};
/// Vibe Invest - Trade with Alpaca
#[derive(Parser, Debug)]
#[command(name = "vibe-invest")]
#[command(author = "Vibe Invest Team")]
#[command(version = "0.1.0")]
#[command(about = "Tech Giants Trading Bot - Algorithmic trading system using Alpaca API")]
#[command(
long_about = "A momentum + mean-reversion hybrid strategy for 50 stocks across multiple sectors.\n\
Uses RSI, MACD, EMA, ADX, Bollinger Bands, and momentum indicators for trade signals.\n\n\
Examples:\n \
Live trading: invest-bot\n \
Live daily bars: invest-bot --timeframe daily\n \
Backtest 3 years: invest-bot --backtest --years 3\n \
Backtest 6 months: invest-bot --backtest --months 6\n \
Backtest 1y 6m: invest-bot --backtest --years 1 --months 6\n \
Custom capital: invest-bot --backtest --years 5 --capital 50000\n \
Hourly backtest: invest-bot --backtest --years 1 --timeframe hourly"
)]
struct Args {
/// Run in backtest mode instead of live trading
#[arg(short, long)]
backtest: bool,
/// Number of years to backtest (default: 1 if --months not specified)
#[arg(short, long, default_value_t = 0.0)]
years: f64,
/// Number of months to backtest (can combine with --years)
#[arg(short, long, default_value_t = 0.0)]
months: f64,
/// Initial capital for backtesting
#[arg(short, long, default_value_t = DEFAULT_INITIAL_CAPITAL)]
capital: f64,
/// Timeframe for bars
#[arg(short, long, value_enum, default_value_t = Timeframe::Daily)]
timeframe: Timeframe,
}
fn setup_logging(backtest_mode: bool) {
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info"));
if backtest_mode {
// Console only for backtest
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer())
.init();
} else {
// Console + file for live trading
let log_path = &paths::LOG_FILE;
let log_dir = log_path.parent().unwrap();
let log_file_name = log_path.file_name().unwrap();
let file_appender = tracing_appender::rolling::never(log_dir, log_file_name);
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer())
.with(fmt::layer().with_writer(non_blocking).with_ansi(false))
.init();
}
}
fn print_banner(backtest: bool) {
if backtest {
println!(
r#"
+---------------------------------------------------------+
| TECH GIANTS TRADING BOT - BACKTEST MODE |
+---------------------------------------------------------+
| Symbols: AAPL, MSFT, GOOGL, AMZN, META, NVDA, TSLA |
| Strategy: Momentum + RSI Mean Reversion + MACD |
+---------------------------------------------------------+
"#
);
} else {
let dashboard_port = env::var("DASHBOARD_PORT").unwrap_or_else(|_| "5000".to_string());
println!(
r#"
+---------------------------------------------------------+
| TECH GIANTS TRADING BOT - PAPER TRADING |
+---------------------------------------------------------+
| Symbols: AAPL, MSFT, GOOGL, AMZN, META, NVDA, TSLA |
| Strategy: Momentum + RSI Mean Reversion + MACD |
| Mode: Alpaca Paper Trading |
| Dashboard: http://localhost:{:<29}|
+---------------------------------------------------------+
"#,
dashboard_port
);
}
}
fn get_credentials() -> Result<(String, String)> {
let api_key = env::var("ALPACA_API_KEY").context(
"Missing ALPACA_API_KEY. Please set it in your .env file or environment.",
)?;
let api_secret = env::var("ALPACA_SECRET_KEY").context(
"Missing ALPACA_SECRET_KEY. Please set it in your .env file or environment.",
)?;
Ok((api_key, api_secret))
}
#[tokio::main]
async fn main() -> Result<()> {
// Load .env file if present
dotenvy::dotenv().ok();
let args = Args::parse();
setup_logging(args.backtest);
print_banner(args.backtest);
let (api_key, api_secret) = match get_credentials() {
Ok(creds) => creds,
Err(e) => {
eprintln!("\nConfiguration Error: {}", e);
eprintln!("\nPlease create a .env file with your Alpaca credentials:");
eprintln!("ALPACA_API_KEY=your_api_key");
eprintln!("ALPACA_SECRET_KEY=your_secret_key");
eprintln!("\nGet your free paper trading API keys at: https://alpaca.markets/");
std::process::exit(1);
}
};
if args.backtest {
run_backtest(api_key, api_secret, args).await
} else {
run_live_trading(api_key, api_secret, args).await
}
}
async fn run_backtest(api_key: String, api_secret: String, args: Args) -> Result<()> {
// Combine years and months (default to 1 year if neither specified)
let total_years = args.years + (args.months / 12.0);
let total_years = if total_years <= 0.0 { 1.0 } else { total_years };
let client = AlpacaClient::new(api_key, api_secret)?;
let mut backtester = Backtester::new(args.capital, args.timeframe);
let result = backtester.run(&client, total_years).await?;
// Save results to CSV
save_backtest_results(&result)?;
Ok(())
}
async fn run_live_trading(api_key: String, api_secret: String, args: Args) -> Result<()> {
let dashboard_port: u16 = env::var("DASHBOARD_PORT")
.unwrap_or_else(|_| "5000".to_string())
.parse()
.unwrap_or(5000);
// Create a separate client for the dashboard
let dashboard_client = AlpacaClient::new(api_key.clone(), api_secret.clone())?;
// Spawn dashboard in background
tokio::spawn(async move {
if let Err(e) = dashboard::start_dashboard(dashboard_client, dashboard_port).await {
tracing::error!("Dashboard error: {}", e);
}
});
// Run the trading bot
let mut bot = TradingBot::new(api_key, api_secret, args.timeframe).await?;
bot.run().await
}