CoolFace
Apppublic

manikandan18ramalingam/Agentic-AI-Options-Trading-App

sourceHugging Faceupdated 8mo agoView on Hugging Face
1likes
ml_predictor_agent_optimized.py248 linesDownload Raw Back to src
1'''2This agent does the following.3 41. Fetch the past 3 years performance of all mega cap stocks from yahoo finance. It uses Close price, RSI, MACD and Bollinger bands.52. Splits up the data set into 80-20 for training and validation. Creates LSTM (Long Short Term Memory) model that uses 3 hidden layers, 1 dense output layer with adam optimizer. The learning rate at 0.001 with 100 epochs.63. Trains and validates the model.74. Plots the loss curve for all tickers.85. Saves the model so that it does not have to train again and again for the same tickers. This improves the Agentic AI performance drastically. It just have to do forecasting in further calls.96. Uses the model to forecast the stock prices of the top option tickers we obtained from options_agent on that expiry date.107. It identifies the best strike price option that is closest to predicted stock price from the model.118. It updates this best_strike_prices in the graph state and returns it.12 13The optimizations done are,14 151. Checking for 100 epochs,162. Dropout of 0.3 was used for generalization and making model not to overfit.173. Used 3 years past performance data instead of 2 years.184. Learning rate modified to 0.001.195. Number of epochs increased to 10020 21The state output is of format,22 23'best_strike_prices': {'AAPL': {'strike': 70.0, 'openInterest': 2526, 'contractSymbol': 'AAPL261218C00070000'}, 'ABBV': {'strike': 250.0, 'openInterest': 806, 'contractSymbol': 'ABBV251121C00250000'}, 'ABT': {'strike': 180.0, 'openInterest': 118, 'contractSymbol': 'ABT251121C00180000'}, 'AVGO': {'strike': 80.0, 'openInterest': 5369, 'contractSymbol': 'AVGO251219C00080000'}}24'''25 26import pandas as pd27import yfinance as yf28import torch29import torch.nn as nn30from sklearn.preprocessing import MinMaxScaler31from langchain_core.runnables import RunnableLambda32import numpy as np33import datetime34import os35import joblib36import matplotlib.pyplot as plt37 38from sklearn.model_selection import train_test_split39 40MODEL_DIR = "models"41LOSS_PLOTS_DIR = "loss_plots"42 43os.makedirs(MODEL_DIR, exist_ok=True)44os.makedirs(LOSS_PLOTS_DIR, exist_ok=True)45 46# ----- Technical Indicators -----47def add_technical_indicators(df):48    df['RSI'] = compute_rsi(df['Close'])49    df['MACD'] = compute_macd(df['Close'])50    df['Bollinger_Upper'], df['Bollinger_Lower'] = compute_bollinger_bands(df['Close'])51    return df.dropna()52 53def compute_rsi(series, period=14):54    delta = series.diff()55    gain = delta.where(delta > 0, 0)56    loss = -delta.where(delta < 0, 0)57    avg_gain = gain.rolling(window=period).mean()58    avg_loss = loss.rolling(window=period).mean()59    rs = avg_gain / avg_loss60    return 100 - (100 / (1 + rs))61 62def compute_macd(series, fast=12, slow=26):63    ema_fast = series.ewm(span=fast, adjust=False).mean()64    ema_slow = series.ewm(span=slow, adjust=False).mean()65    return ema_fast - ema_slow66 67def compute_bollinger_bands(series, window=20, num_std=2):68    sma = series.rolling(window).mean()69    std = series.rolling(window).std()70    return sma + num_std * std, sma - num_std * std71 72# ----- LSTM Model Definition -----73class OptimizedLSTMModel(nn.Module):74    # Add hidden size, hidden layers, dropout optimizations75    def __init__(self, input_size=5, hidden_size=128, num_layers=3, output_size=1, dropout=0.3):76        super(OptimizedLSTMModel, self).__init__()77        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, dropout=dropout, batch_first=True)78        self.fc = nn.Linear(hidden_size, output_size)79 80    def forward(self, x):81        out, _ = self.lstm(x)82        return self.fc(out[:, -1, :])83 84# ----- Predict Future Price -----85def predict_price_n_days(model, scaler, seq, input_size, days):86    for _ in range(days):87        input_seq = torch.tensor(seq[-len(seq):], dtype=torch.float32).unsqueeze(0)88        with torch.no_grad():89            next_pred = model(input_seq).item()90        dummy = np.zeros((1, scaler.n_features_in_))91        dummy[0][0] = next_pred92        next_close = scaler.inverse_transform(dummy)[0][0]93        seq = np.vstack([seq, np.hstack([next_pred] * input_size)])94    return round(next_close, 2)95 96# ----- Plot Losses -----97def plot_loss_per_ticker(loss_dict):98    plt.figure(figsize=(10, 6))99    for ticker, losses in loss_dict.items():100        plt.plot(range(1, len(losses) + 1), losses, label=ticker)101    plt.xlabel("Epoch")102    plt.ylabel("Loss")103    plt.title("Training Loss Per Ticker")104    plt.legend()105    plt.grid(True)106    plt.tight_layout()107    plt.savefig(os.path.join(LOSS_PLOTS_DIR, "loss_plot.png"))108    plt.close()109 110# ----- Model Summary -----111def print_model_summary():112    print("\nModel Summary (Shared Across All Tickers)")113    print("=" * 50)114    print(f"{'Model':<20}: LSTM")115    print(f"{'Hidden Layers':<20}: 3 × LSTM(128 units) + Dropout(0.3)")116    print(f"{'Output Layer':<20}: Dense(1)")117    print(f"{'Optimizer':<20}: Adam")118    print(f"{'Loss Function':<20}: Mean Squared Error")119    print(f"{'Epochs':<20}: 100")120    print("=" * 50 + "\n")121 122# ----- Core Agent Logic -----123def _ml_predictor_agent(state):124    tickers = state["tickers"]125    expiries = state.get("expiries", {})126    top_strikes_all = state.get("top_strikes", {})127 128    print_model_summary()129    loss_history = {}130    results = {}131 132    for ticker in tickers:133        try:134            expiry_str = expiries.get(ticker)135            if not expiry_str:136                results[ticker] = {"error": "No expiry"}137                continue138 139            expiry = datetime.datetime.strptime(expiry_str, "%Y-%m-%d").date()140            today = datetime.date.today()141            days_ahead = (expiry - today).days142            if days_ahead <= 0:143                results[ticker] = {"error": "Invalid expiry"}144                continue145 146            model_path = os.path.join(MODEL_DIR, f"{ticker}_model.pt")147            scaler_path = os.path.join(MODEL_DIR, f"{ticker}_scaler.pkl")148 149            df = yf.download(ticker, period="3y", interval="1d", progress=False)150            df = add_technical_indicators(df)151            features = df[['Close', 'RSI', 'MACD', 'Bollinger_Upper', 'Bollinger_Lower']].values152 153            seq_length = 30154            input_size = features.shape[1]155 156            # Check for model presence per ticker. If present, avoid re-training for performance optimization157            if not os.path.exists(model_path) or not os.path.exists(scaler_path):158                # Scale the data using MinMaxScaler159                scaler = MinMaxScaler()160                data_scaled = scaler.fit_transform(features)161                X, y = [], []162                for i in range(len(data_scaled) - seq_length):163                    X.append(data_scaled[i:i+seq_length])164                    y.append(data_scaled[i+seq_length][0])165 166                # Train-validation split (80-20)167                X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, shuffle=False)168 169                X_train = torch.tensor(X_train, dtype=torch.float32)170                y_train = torch.tensor(y_train, dtype=torch.float32).view(-1, 1)171                X_val = torch.tensor(X_val, dtype=torch.float32)172                y_val = torch.tensor(y_val, dtype=torch.float32).view(-1, 1)173 174                model = OptimizedLSTMModel(input_size=input_size)175                optimizer = torch.optim.Adam(model.parameters(), lr=0.001)176                loss_fn = nn.MSELoss()177 178                ticker_losses = []179                180                for epoch in range(100):181                    model.train()182                    optimizer.zero_grad()183                    output = model(X_train)184                    loss = loss_fn(output, y_train)185                    loss.backward()186                    optimizer.step()187 188                    # Validation189                    model.eval()190                    with torch.no_grad():191                        val_output = model(X_val)192                        val_loss = loss_fn(val_output, y_val).item()193                    ticker_losses.append(val_loss)194 195                # Save the model for future use and avoid re-training for each call from Web app196                torch.save(model.state_dict(), model_path)197                joblib.dump(scaler, scaler_path)198                loss_history[ticker] = ticker_losses199            else:200                # Store the scaled data appropriate for each ticker201                scaler = joblib.load(scaler_path)202                data_scaled = scaler.transform(features)203                model = OptimizedLSTMModel(input_size=input_size)204                model.load_state_dict(torch.load(model_path))205                model.eval()206 207            recent_seq = data_scaled[-seq_length:]208 209            # Predict the price of expiry date in future210            predicted_price = predict_price_n_days(model, scaler, recent_seq.copy(), input_size, days_ahead)211 212            strikes = top_strikes_all.get(ticker, [])213            if strikes:214                # Get the option with closest strike price to LSTM predicted price215                best_strike = min(strikes, key=lambda s: abs(s["strike"] - predicted_price))216            else:217                best_strike = None218 219            results[ticker] = {220                "predicted_price_on_expiry": predicted_price,221                "best_matching_option": best_strike222            }223 224        except Exception as e:225            results[ticker] = {"error": str(e)}226 227    if loss_history:228        plot_loss_per_ticker(loss_history)229 230    predicted_prices = {231        ticker: data["predicted_price_on_expiry"]232        for ticker, data in results.items()233        if "predicted_price_on_expiry" in data234    }235 236    best_strike_prices = {237        ticker: data["best_matching_option"]238        for ticker, data in results.items()239        if "best_matching_option" in data240    }241 242    return {243        "predicted_prices": predicted_prices,244        "best_strike_prices": best_strike_prices245    }246 247ml_predictor_agent = RunnableLambda(_ml_predictor_agent)248