Upload folder using huggingface_hub
Browse files
frontend/app/components/dashboard/StockPredictions.tsx
CHANGED
|
@@ -89,7 +89,7 @@ const StockPredictions = () => {
|
|
| 89 |
<div className="flex items-center justify-between mb-4">
|
| 90 |
<div className="flex items-center gap-2">
|
| 91 |
<Activity className="w-5 h-5 text-success" />
|
| 92 |
-
<h2 className="text-lg font-bold">STOCK PREDICTIONS
|
| 93 |
</div>
|
| 94 |
<div className="flex items-center gap-2">
|
| 95 |
<button
|
|
@@ -165,7 +165,7 @@ const StockPredictions = () => {
|
|
| 165 |
</div>
|
| 166 |
<div className="text-right">
|
| 167 |
<div className="font-mono text-lg">
|
| 168 |
-
|
| 169 |
</div>
|
| 170 |
<div className={`text-sm font-mono ${stock.expected_change_pct >= 0 ? 'text-green-400' : 'text-red-400'}`}>
|
| 171 |
{stock.expected_change_pct >= 0 ? '+' : ''}{stock.expected_change_pct?.toFixed(2) || '0.00'}%
|
|
|
|
| 89 |
<div className="flex items-center justify-between mb-4">
|
| 90 |
<div className="flex items-center gap-2">
|
| 91 |
<Activity className="w-5 h-5 text-success" />
|
| 92 |
+
<h2 className="text-lg font-bold">CSE STOCK PREDICTIONS 🇱🇰</h2>
|
| 93 |
</div>
|
| 94 |
<div className="flex items-center gap-2">
|
| 95 |
<button
|
|
|
|
| 165 |
</div>
|
| 166 |
<div className="text-right">
|
| 167 |
<div className="font-mono text-lg">
|
| 168 |
+
LKR {stock.predicted_price?.toFixed(2) || '---'}
|
| 169 |
</div>
|
| 170 |
<div className={`text-sm font-mono ${stock.expected_change_pct >= 0 ? 'text-green-400' : 'text-red-400'}`}>
|
| 171 |
{stock.expected_change_pct >= 0 ? '+' : ''}{stock.expected_change_pct?.toFixed(2) || '0.00'}%
|
main.py
CHANGED
|
@@ -15,7 +15,7 @@ from pydantic import BaseModel
|
|
| 15 |
from typing import Dict, Any, List, Set, Optional
|
| 16 |
import asyncio
|
| 17 |
import json
|
| 18 |
-
from datetime import datetime
|
| 19 |
import sys
|
| 20 |
import os
|
| 21 |
import logging
|
|
|
|
| 15 |
from typing import Dict, Any, List, Set, Optional
|
| 16 |
import asyncio
|
| 17 |
import json
|
| 18 |
+
from datetime import datetime, timedelta
|
| 19 |
import sys
|
| 20 |
import os
|
| 21 |
import logging
|
models/stock-price-prediction/README.md
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stock Price Prediction Module 🇱🇰
|
| 2 |
+
|
| 3 |
+
BiLSTM-based stock price prediction for **10 Sri Lankan CSE stocks**.
|
| 4 |
+
|
| 5 |
+
## Stocks Covered
|
| 6 |
+
|
| 7 |
+
| Symbol | Company | Sector |
|
| 8 |
+
|--------|---------|--------|
|
| 9 |
+
| COMB | Commercial Bank of Ceylon PLC | Banking |
|
| 10 |
+
| JKH | John Keells Holdings PLC | Diversified Holdings |
|
| 11 |
+
| SAMP | Sampath Bank PLC | Banking |
|
| 12 |
+
| HNB | Hatton National Bank PLC | Banking |
|
| 13 |
+
| DIAL | Dialog Axiata PLC | Telecommunications |
|
| 14 |
+
| CTC | Ceylon Tobacco Company PLC | Consumer Goods |
|
| 15 |
+
| NEST | Nestle Lanka PLC | Consumer Goods |
|
| 16 |
+
| CARG | Cargills Ceylon PLC | Retail |
|
| 17 |
+
| HNBA | HNB Assurance PLC | Insurance |
|
| 18 |
+
| CARS | Carson Cumberbatch PLC | Diversified Holdings |
|
| 19 |
+
|
| 20 |
+
## ⚠️ Important Note
|
| 21 |
+
|
| 22 |
+
**Yahoo Finance does NOT support CSE (Colombo Stock Exchange) tickers directly.**
|
| 23 |
+
|
| 24 |
+
The module uses fallback predictions with simulated market data. For real CSE data, integrate with:
|
| 25 |
+
- CSE official API
|
| 26 |
+
- Bloomberg Terminal
|
| 27 |
+
- Reuters/Refinitiv
|
| 28 |
+
|
| 29 |
+
## Architecture
|
| 30 |
+
|
| 31 |
+
- **Model**: Bidirectional LSTM (BiLSTM)
|
| 32 |
+
- **Epochs**: 10 (configurable)
|
| 33 |
+
- **Sequence Length**: 60 days
|
| 34 |
+
- **Features**: Close price, technical indicators
|
| 35 |
+
- **Tracking**: MLflow + DagsHub
|
| 36 |
+
|
| 37 |
+
## Quick Start
|
| 38 |
+
|
| 39 |
+
```bash
|
| 40 |
+
# Train all 10 stocks
|
| 41 |
+
cd models/stock-price-prediction
|
| 42 |
+
python main.py
|
| 43 |
+
|
| 44 |
+
# Test predictor
|
| 45 |
+
python src/components/predictor.py
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
## API Endpoints
|
| 49 |
+
|
| 50 |
+
| Endpoint | Description |
|
| 51 |
+
|----------|-------------|
|
| 52 |
+
| `GET /api/stocks/predictions` | All 10 stock predictions |
|
| 53 |
+
| `GET /api/stocks/predictions/{symbol}` | Single stock (COMB, JKH, etc.) |
|
| 54 |
+
| `GET /api/stocks/model/status` | Model training status |
|
| 55 |
+
|
| 56 |
+
## Output
|
| 57 |
+
|
| 58 |
+
Predictions include:
|
| 59 |
+
- Current price (LKR)
|
| 60 |
+
- Predicted next-day price
|
| 61 |
+
- Expected change %
|
| 62 |
+
- Trend (bullish/bearish/neutral)
|
| 63 |
+
- Confidence score
|
| 64 |
+
|
| 65 |
+
## Directory Structure
|
| 66 |
+
|
| 67 |
+
```
|
| 68 |
+
stock-price-prediction/
|
| 69 |
+
├── main.py # Multi-stock training entry
|
| 70 |
+
├── src/
|
| 71 |
+
│ ├── components/
|
| 72 |
+
│ │ ├── data_ingestion.py
|
| 73 |
+
│ │ ├── data_validation.py
|
| 74 |
+
│ │ ├── data_transformation.py
|
| 75 |
+
│ │ ├── model_trainer.py
|
| 76 |
+
│ │ └── predictor.py # Inference API
|
| 77 |
+
│ └── constants/
|
| 78 |
+
│ └── training_pipeline/
|
| 79 |
+
├── Artifacts/ # Trained models
|
| 80 |
+
└── output/predictions/ # JSON predictions
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
## Airflow DAG
|
| 84 |
+
|
| 85 |
+
Schedule: **4:15 AM IST daily** (via centralized `airflow/dags/stock_prediction_dag.py`)
|
models/stock-price-prediction/src/components/predictor.py
CHANGED
|
@@ -97,17 +97,25 @@ class StockPredictor:
|
|
| 97 |
"""Generate a fallback prediction when model is not available."""
|
| 98 |
stock_info = STOCKS_TO_TRAIN.get(stock_code, {"name": stock_code, "sector": "Unknown"})
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
"
|
| 105 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
}
|
| 107 |
-
current_price =
|
| 108 |
|
| 109 |
# Generate prediction with slight randomized movement
|
| 110 |
-
change_pct = np.random.normal(0.
|
| 111 |
predicted_price = current_price * (1 + change_pct / 100)
|
| 112 |
|
| 113 |
# Determine trend
|
|
@@ -125,6 +133,8 @@ class StockPredictor:
|
|
| 125 |
"symbol": stock_code,
|
| 126 |
"name": stock_info.get("name", stock_code),
|
| 127 |
"sector": stock_info.get("sector", "Unknown"),
|
|
|
|
|
|
|
| 128 |
"current_price": round(current_price, 2),
|
| 129 |
"predicted_price": round(predicted_price, 2),
|
| 130 |
"expected_change": round(predicted_price - current_price, 2),
|
|
@@ -134,7 +144,7 @@ class StockPredictor:
|
|
| 134 |
"confidence": round(np.random.uniform(0.65, 0.85), 2),
|
| 135 |
"model_architecture": "BiLSTM",
|
| 136 |
"is_fallback": True,
|
| 137 |
-
"note": "
|
| 138 |
}
|
| 139 |
|
| 140 |
def predict_stock(self, stock_code: str) -> Dict[str, Any]:
|
|
|
|
| 97 |
"""Generate a fallback prediction when model is not available."""
|
| 98 |
stock_info = STOCKS_TO_TRAIN.get(stock_code, {"name": stock_code, "sector": "Unknown"})
|
| 99 |
|
| 100 |
+
# Realistic CSE stock prices in LKR (Sri Lankan Rupees)
|
| 101 |
+
# Based on typical market cap leaders on CSE
|
| 102 |
+
np.random.seed(hash(stock_code + datetime.now().strftime("%Y%m%d")) % 2**31)
|
| 103 |
+
base_prices_lkr = {
|
| 104 |
+
"COMB": 95.0, # Commercial Bank ~95 LKR
|
| 105 |
+
"JKH": 175.0, # John Keells Holdings ~175 LKR
|
| 106 |
+
"SAMP": 68.0, # Sampath Bank ~68 LKR
|
| 107 |
+
"HNB": 155.0, # Hatton National Bank ~155 LKR
|
| 108 |
+
"DIAL": 12.0, # Dialog Axiata ~12 LKR
|
| 109 |
+
"CTC": 1100.0, # Ceylon Tobacco ~1100 LKR
|
| 110 |
+
"NEST": 1450.0, # Nestle Lanka ~1450 LKR
|
| 111 |
+
"CARG": 215.0, # Cargills Ceylon ~215 LKR
|
| 112 |
+
"HNBA": 42.0, # HNB Assurance ~42 LKR
|
| 113 |
+
"CARS": 285.0, # Carson Cumberbatch ~285 LKR
|
| 114 |
}
|
| 115 |
+
current_price = base_prices_lkr.get(stock_code, 100.0) * (1 + np.random.uniform(-0.03, 0.03))
|
| 116 |
|
| 117 |
# Generate prediction with slight randomized movement
|
| 118 |
+
change_pct = np.random.normal(0.15, 1.5) # Mean +0.15%, std 1.5%
|
| 119 |
predicted_price = current_price * (1 + change_pct / 100)
|
| 120 |
|
| 121 |
# Determine trend
|
|
|
|
| 133 |
"symbol": stock_code,
|
| 134 |
"name": stock_info.get("name", stock_code),
|
| 135 |
"sector": stock_info.get("sector", "Unknown"),
|
| 136 |
+
"exchange": stock_info.get("exchange", "CSE"),
|
| 137 |
+
"currency": "LKR",
|
| 138 |
"current_price": round(current_price, 2),
|
| 139 |
"predicted_price": round(predicted_price, 2),
|
| 140 |
"expected_change": round(predicted_price - current_price, 2),
|
|
|
|
| 144 |
"confidence": round(np.random.uniform(0.65, 0.85), 2),
|
| 145 |
"model_architecture": "BiLSTM",
|
| 146 |
"is_fallback": True,
|
| 147 |
+
"note": "CSE data via fallback - Yahoo Finance doesn't support CSE tickers"
|
| 148 |
}
|
| 149 |
|
| 150 |
def predict_stock(self, stock_code: str) -> Dict[str, Any]:
|
models/stock-price-prediction/src/constants/training_pipeline/__init__.py
CHANGED
|
@@ -5,64 +5,75 @@ import numpy as np
|
|
| 5 |
Defining common constant variable for training pipeline
|
| 6 |
"""
|
| 7 |
|
| 8 |
-
# Stocks
|
| 9 |
-
# NOTE:
|
| 10 |
-
#
|
|
|
|
| 11 |
STOCKS_TO_TRAIN = {
|
| 12 |
-
"
|
| 13 |
-
"yahoo_symbol": "
|
| 14 |
-
"name": "
|
| 15 |
-
"sector": "
|
|
|
|
| 16 |
},
|
| 17 |
-
"
|
| 18 |
-
"yahoo_symbol": "
|
| 19 |
-
"name": "
|
| 20 |
-
"sector": "
|
|
|
|
| 21 |
},
|
| 22 |
-
"
|
| 23 |
-
"yahoo_symbol": "
|
| 24 |
-
"name": "
|
| 25 |
-
"sector": "
|
|
|
|
| 26 |
},
|
| 27 |
-
"
|
| 28 |
-
"yahoo_symbol": "
|
| 29 |
-
"name": "
|
| 30 |
-
"sector": "
|
|
|
|
| 31 |
},
|
| 32 |
-
"
|
| 33 |
-
"yahoo_symbol": "
|
| 34 |
-
"name": "
|
| 35 |
-
"sector": "
|
|
|
|
| 36 |
},
|
| 37 |
-
"
|
| 38 |
-
"yahoo_symbol": "
|
| 39 |
-
"name": "
|
| 40 |
-
"sector": "
|
|
|
|
| 41 |
},
|
| 42 |
-
"
|
| 43 |
-
"yahoo_symbol": "
|
| 44 |
-
"name": "
|
| 45 |
-
"sector": "
|
|
|
|
| 46 |
},
|
| 47 |
-
"
|
| 48 |
-
"yahoo_symbol": "
|
| 49 |
-
"name": "
|
| 50 |
-
"sector": "
|
|
|
|
| 51 |
},
|
| 52 |
-
"
|
| 53 |
-
"yahoo_symbol": "
|
| 54 |
-
"name": "
|
| 55 |
-
"sector": "
|
|
|
|
| 56 |
},
|
| 57 |
-
"
|
| 58 |
-
"yahoo_symbol": "
|
| 59 |
-
"name": "
|
| 60 |
-
"sector": "
|
|
|
|
| 61 |
}
|
| 62 |
}
|
| 63 |
|
| 64 |
# Default stock for single-stock training mode
|
| 65 |
-
DEFAULT_STOCK = "
|
| 66 |
|
| 67 |
# Legacy alias for backward compatibility
|
| 68 |
SRI_LANKA_STOCKS = STOCKS_TO_TRAIN
|
|
|
|
| 5 |
Defining common constant variable for training pipeline
|
| 6 |
"""
|
| 7 |
|
| 8 |
+
# Top 10 Sri Lankan Stocks by market cap (CSE - Colombo Stock Exchange)
|
| 9 |
+
# NOTE: Yahoo Finance does NOT support CSE tickers directly.
|
| 10 |
+
# These stocks will use fallback predictions with simulated market data.
|
| 11 |
+
# For real CSE data, integrate with CSE API or data providers like Bloomberg.
|
| 12 |
STOCKS_TO_TRAIN = {
|
| 13 |
+
"COMB": {
|
| 14 |
+
"yahoo_symbol": "COMB.N0000", # Commercial Bank of Ceylon
|
| 15 |
+
"name": "Commercial Bank of Ceylon PLC",
|
| 16 |
+
"sector": "Banking",
|
| 17 |
+
"exchange": "CSE"
|
| 18 |
},
|
| 19 |
+
"JKH": {
|
| 20 |
+
"yahoo_symbol": "JKH.N0000", # John Keells Holdings
|
| 21 |
+
"name": "John Keells Holdings PLC",
|
| 22 |
+
"sector": "Diversified Holdings",
|
| 23 |
+
"exchange": "CSE"
|
| 24 |
},
|
| 25 |
+
"SAMP": {
|
| 26 |
+
"yahoo_symbol": "SAMP.N0000", # Sampath Bank
|
| 27 |
+
"name": "Sampath Bank PLC",
|
| 28 |
+
"sector": "Banking",
|
| 29 |
+
"exchange": "CSE"
|
| 30 |
},
|
| 31 |
+
"HNB": {
|
| 32 |
+
"yahoo_symbol": "HNB.N0000", # Hatton National Bank
|
| 33 |
+
"name": "Hatton National Bank PLC",
|
| 34 |
+
"sector": "Banking",
|
| 35 |
+
"exchange": "CSE"
|
| 36 |
},
|
| 37 |
+
"DIAL": {
|
| 38 |
+
"yahoo_symbol": "DIAL.N0000", # Dialog Axiata
|
| 39 |
+
"name": "Dialog Axiata PLC",
|
| 40 |
+
"sector": "Telecommunications",
|
| 41 |
+
"exchange": "CSE"
|
| 42 |
},
|
| 43 |
+
"CTC": {
|
| 44 |
+
"yahoo_symbol": "CTC.N0000", # Ceylon Tobacco
|
| 45 |
+
"name": "Ceylon Tobacco Company PLC",
|
| 46 |
+
"sector": "Consumer Goods",
|
| 47 |
+
"exchange": "CSE"
|
| 48 |
},
|
| 49 |
+
"NEST": {
|
| 50 |
+
"yahoo_symbol": "NEST.N0000", # Nestle Lanka
|
| 51 |
+
"name": "Nestle Lanka PLC",
|
| 52 |
+
"sector": "Consumer Goods",
|
| 53 |
+
"exchange": "CSE"
|
| 54 |
},
|
| 55 |
+
"CARG": {
|
| 56 |
+
"yahoo_symbol": "CARG.N0000", # Cargills Ceylon
|
| 57 |
+
"name": "Cargills Ceylon PLC",
|
| 58 |
+
"sector": "Retail",
|
| 59 |
+
"exchange": "CSE"
|
| 60 |
},
|
| 61 |
+
"HNBA": {
|
| 62 |
+
"yahoo_symbol": "HNBA.N0000", # HNB Assurance
|
| 63 |
+
"name": "HNB Assurance PLC",
|
| 64 |
+
"sector": "Insurance",
|
| 65 |
+
"exchange": "CSE"
|
| 66 |
},
|
| 67 |
+
"CARS": {
|
| 68 |
+
"yahoo_symbol": "CARS.N0000", # Carson Cumberbatch
|
| 69 |
+
"name": "Carson Cumberbatch PLC",
|
| 70 |
+
"sector": "Diversified Holdings",
|
| 71 |
+
"exchange": "CSE"
|
| 72 |
}
|
| 73 |
}
|
| 74 |
|
| 75 |
# Default stock for single-stock training mode
|
| 76 |
+
DEFAULT_STOCK = "COMB"
|
| 77 |
|
| 78 |
# Legacy alias for backward compatibility
|
| 79 |
SRI_LANKA_STOCKS = STOCKS_TO_TRAIN
|