from flask import Flask, request, render_template_string, jsonify from datasets import load_dataset from datetime import datetime, timedelta from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error import pandas as pd import numpy as np import yfinance as yf app = Flask(__name__) # ================= Load Indian Stock Symbol Data set ================= # Load a dataset from Hugging Face dataset = load_dataset("ThunderDrag/India-Stock-Symbols-and-Metadata", split="train") # Convert the dataset to a Pandas DataFrame (Table) df_stocks = pd.DataFrame(dataset) # =============== Data Fetching =============== def fetch_stock_data(symbol, period="5y"): """Fetch historical stock data from Yahoo Finance""" stock = yf.Ticker(symbol) data = stock.history(period=period) data = data.sort_index(ascending=True) return data # ================= Technical Indicators ================= def calculate_moving_averages(data): data["50MA"] = data["Close"].rolling(window=50).mean() data["200MA"] = data["Close"].rolling(window=200).mean() return data def determin_trend(data): if ( data["50MA"].iloc[-1] > data["200MA"].iloc[-1] and data["Close"].iloc[-1] > data["50MA"].iloc[-1] ): trend = "UPTREND (bullish for next 1-3 months)" elif ( data["50MA"].iloc[-1] < data["200MA"].iloc[-1] and data["Close"].iloc[-1] < data["50MA"].iloc[-1] ): trend = "DOWNTREND (bearish for next 1-3 months)" else: trend = "SIDEWAYS (uncertain)" return trend def calculate_RSI(data, window=14): delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(window=window).mean() avg_loss = loss.rolling(window=window).mean() rs = avg_gain / avg_loss data["RSI"] = 100 - (100 / (1 + rs)) return data def calculate_MACD(data, fast=12, slow=26, signal=9): data["EMA_fast"] = data["Close"].ewm(span=fast, adjust=False).mean() data["EMA_slow"] = data["Close"].ewm(span=slow, adjust=False).mean() data["MACD"] = data["EMA_fast"] - data["EMA_slow"] data["MACD_signal"] = data["MACD"].ewm(span=signal, adjust=False).mean() return data # =============== Random Forest Forecast =============== """ This function uses a Random Forest ML model to learn from historical stock indicators and predict stock prices for the next 30 days. """ def random_forest_forecast(data, days_ahead=30): """ Predict future stock prices using Random Forest Regressor """ df = data.copy() # next-day close as target df["Target"] = df["Close"].shift(-1) # Drop last row with NaN target df = df.dropna() # Features (you can add more indicators here) features = ["Close", "50MA", "200MA", "RSI", "MACD", "MACD_signal"] # drop rows with NaN from indicators df = df.dropna() # Feature matrix and target vector X = df[features] y = df["Target"] # Train/test split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, shuffle=False ) # Train model model = RandomForestRegressor(n_estimators=200, random_state=42) model.fit(X_train, y_train) # Evaluate y_pred = model.predict(X_test) mae = mean_absolute_error(y_test, y_pred) print(f"Random Forest MAE: {mae:.2f}") # Forecast future price iteratively last_known = X.iloc[-1].values.reshape(1, -1) forecast_prices = [] for _ in range(days_ahead): pred = model.predict(last_known)[0] forecast_prices.append(pred) # update only Close for simplicity last_known[0, 0] = pred return forecast_prices[-1], forecast_prices # ================= Entry / Stoploss ================= def calculate_entry_stoploss(data, trend, stoploss_percent=5): entry_price = None stop_loss = None if ( trend.startswith("UPTREND") and data["RSI"].iloc[-1] < 70 and data["MACD"].iloc[-1] > data["MACD_signal"].iloc[-1] ): entry_price = data["Close"].iloc[-1] stop_loss = entry_price * (1 - stoploss_percent / 100) return entry_price, stop_loss # ================= Autocomplete API ================= @app.route("/autocomplete") def autocomplete(): query = request.args.get("q", "").lower() if not query: return jsonify([]) # Filter dataframe where stock name contains query (case-insensitive) filtered = df_stocks[df_stocks["name"].str.lower().str.contains(query)].head(10) # Convert to list of dicts suggestions = ( filtered[["name", "ticker"]] .rename(columns={"ticker": "symbol"}) .to_dict(orient="records") ) return jsonify(suggestions) # ================= Flask Routes ================= @app.route("/", methods=["GET", "POST"]) def index(): result = None table_html = None if request.method == "POST": selected_symbol = request.form["symbol"] selected_name = request.form.get("stock_name", selected_symbol) yahoo_symbol = ( f"{selected_symbol}.NS" if not selected_symbol.endswith(".NS") else selected_symbol ) print("Selected symbol for Yahoo Finance:", yahoo_symbol) # Print selected symbol for debugging print("Selected stock symbol:", selected_symbol) data = fetch_stock_data(yahoo_symbol) # Technical Indicators data = calculate_moving_averages(data) data = calculate_RSI(data) data = calculate_MACD(data) trend = determin_trend(data) predicted_price, _ = random_forest_forecast(data) entry_price, stop_loss = calculate_entry_stoploss(data, trend) current_price = data["Close"].iloc[-1] price_difference = predicted_price - current_price profit_or_loss = ((predicted_price - current_price) / current_price) * 100 table_html = ( data.tail(30) .reset_index() .to_html(classes="table table-striped table-bordered", index=False) ) result = { "symbol": selected_symbol, "name": selected_name, "trend": trend, "current_price": f"{current_price:.2f}", "predicted_price": f"{predicted_price:.2f}", "price_difference": f"{price_difference:.2f}", "profit_or_loss": f"{profit_or_loss:.2f}%", "entry_price": f"{entry_price:.2f}" if entry_price else "No Entry Signal", "stop_loss": f"{stop_loss:.2f}" if stop_loss else "-", "date_now": data.index[-1].date(), "future_date": datetime.now().date() + timedelta(days=30), } return render_template_string( """ Stock Predictor

Predicting...

Stock Prediction Dashboard

{% if result %}
Report for {{ result.name }}

Trend: {{ result.trend }}

Current Price: ₹{{ result.current_price }} ({{ result.date_now }})

Predicted Price (Next 30 Days): ₹{{ result.predicted_price }} ({{ result.future_date }})

Price Difference: ₹{{ result.price_difference }}

Expected Return: {{ result.profit_or_loss }}

Entry Price: {{ result.entry_price }}

Stop Loss: {{ result.stop_loss }}

Last 30 Days Data
{{ table_html | safe }}
{% endif %}
""", result=result, table_html=table_html, ) # Run the app if __name__ == "__main__": # Run on local host # app.run(debug=True) # Run using public IP # app.run(host="0.0.0.0", port=5000, debug=True) # Hugging Face uses port 7860 by default app.run(host="0.0.0.0", port=7860)