-
📈 Stock Scanner Pro
+
+ 📊
+
Stock Scanner Pro
+
{userProfile && (
@@ -74,18 +80,22 @@ function Dashboard() {
+
+
{view === "scanner" && (
<>
-
-
+
TradingView Chart
@@ -93,10 +103,12 @@ function Dashboard() {
{signal &&
}
>
)}
+ {view === "dashboard" &&
}
{view === "ribbon" &&
}
{view === "smartmoney" &&
}
{view === "marketintel" &&
}
{view === "trend" &&
}
+ {view === "watchlists" &&
}
{view === "profile" &&
}
diff --git a/src/components/AboutScanner.js b/src/components/AboutScanner.js
index f6b9c3c..e854200 100644
--- a/src/components/AboutScanner.js
+++ b/src/components/AboutScanner.js
@@ -1,71 +1,90 @@
-import React from "react";
-
-function AboutScanner() {
- return (
-
-
🧠 How The Scanner Works
-
-
- -
- 📈 Trend: Price above EMA20 indicates bullish trend.
-
-
- -
- ⚡ Momentum: RSI measures strength of buying momentum.
-
-
- -
- 🚀 Breakout: Detects stocks breaking recent resistance levels.
-
-
- -
- 🔥 Volume Ratio: Compares current volume with average volume.
-
-
- -
- 💪 Relative Strength (RS): Measures stock performance vs NIFTY.
-
-
- -
- 🏦 Smart Money Score: Estimates institutional accumulation probability.
-
-
- -
- 🤖 AI Score: Combined score using trend, RSI, breakout, volume and RS.
-
-
- -
- ✅ Confidence: Probability of a quality setup based on AI Score.
-
-
- -
- 🎯 Fund Candidate: High-volume breakout stocks showing potential accumulation.
-
-
- -
- 🏆 Trade Of The Day: Highest ranked stock across all sectors.
-
-
-
-
🏅 Score Guide
-
-
- - A+ (90-100) = Exceptional
- - A (80-89) = Strong
- - B (70-79) = Good
- - C (60-69) = Average
- - D (<60) = Avoid
-
-
- );
-}
-
+import React, { useState, useEffect } from "react";
+
+function AboutScanner({ isLoaded }) {
+ const [isCollapsed, setIsCollapsed] = useState(false);
+
+ useEffect(() => {
+ if (isLoaded) {
+ setIsCollapsed(true);
+ }
+ }, [isLoaded]);
+
+ return (
+
+
setIsCollapsed(!isCollapsed)}
+ >
+
🧠 How The Scanner Works
+ {isCollapsed ? "▼" : "▲"}
+
+
+ {!isCollapsed && (
+
+
+
+ -
+ 📈 Trend: Price above EMA20 indicates bullish trend.
+
+
+ -
+ ⚡ Momentum: RSI measures strength of buying momentum.
+
+
+ -
+ 🚀 Breakout: Detects stocks breaking recent resistance levels.
+
+
+ -
+ 🔥 Volume Ratio: Compares current volume with average volume.
+
+
+ -
+ 💪 Relative Strength (RS): Measures stock performance vs NIFTY.
+
+
+ -
+ 🏦 Smart Money Score: Estimates institutional accumulation probability.
+
+
+ -
+ 🤖 AI Score: Combined score using trend, RSI, breakout, volume and RS.
+
+
+ -
+ ✅ Confidence: Probability of a quality setup based on AI Score.
+
+
+ -
+ 🎯 Fund Candidate: High-volume breakout stocks showing potential accumulation.
+
+
+ -
+ 🏆 Trade Of The Day: Highest ranked stock across all sectors.
+
+
+
+
🏅 Score Guide
+
+
+ - A+ (90-100) = Exceptional
+ - A (80-89) = Strong
+ - B (70-79) = Good
+ - C (60-69) = Average
+ - D (<60) = Avoid
+
+
+ )}
+
+ );
+}
+
export default AboutScanner;
\ No newline at end of file
diff --git a/src/components/Backtest.js b/src/components/Backtest.js
index 1dbc9ef..a6dadd9 100644
--- a/src/components/Backtest.js
+++ b/src/components/Backtest.js
@@ -1,163 +1,163 @@
-import React, { useEffect, useState } from "react";
-import axios from "axios";
-
-function Backtest() {
- const [data, setData] = useState({});
-
- // ✅ Fetch backtest data
- const loadBacktest = async () => {
- try {
- const res = await axios.get("http://127.0.0.1:8000/backtest");
- setData(res.data);
- } catch (err) {
- console.error("Backtest Error:", err);
- }
- };
-
- useEffect(() => {
- loadBacktest();
- }, []);
-
- return (
-
-
- 📊 Backtest Dashboard (Strategy Performance)
-
-
- {/* ✅ EXPLANATION PANEL */}
-
-
📘 How to Read Backtest
-
-
- ✅ Trades = Total opportunities generated
-
-
- ✅ Wins = Profitable trades
-
-
- ✅ Loss = Losing trades
-
-
- ✅ Win Rate = Success % of strategy
-
-
- ✅ Profit = Net gain (simulated)
-
-
-
-
-
- 💡 Best Strategy Rules:
-
-
- - Win Rate ≥ 60% ✅
- - Trades ≥ 10 ✅
- - Profit positive ✅
-
-
-
- {/* ✅ SECTORS */}
- {Object.entries(data).map(([sector, stocks]) => (
-
-
- {sector}
-
-
-
-
-
- | Stock |
- Trades |
- Wins |
- Loss |
- Win % |
- Profit |
-
-
-
-
- {stocks.map((s, i) => (
-
- | {s.symbol} |
-
- {s.trades} |
-
-
- {s.wins}
- |
-
-
- {s.loss}
- |
-
- {/* ✅ WIN RATE COLOR */}
- = 70
- ? "#3fb950"
- : s.win_rate >= 60
- ? "#d29922"
- : "#f85149",
- fontWeight: "bold",
- }}
- >
- {s.win_rate}%
- |
-
- {/* ✅ PROFIT COLOR */}
- 0 ? "#3fb950" : "#f85149",
- fontWeight: "bold",
- }}
- >
- {s.profit}
- |
-
- ))}
-
-
-
- ))}
-
- );
-}
-
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+
+function Backtest() {
+ const [data, setData] = useState({});
+
+ // ✅ Fetch backtest data
+ const loadBacktest = async () => {
+ try {
+ const res = await axios.get("/api/backtest");
+ setData(res.data);
+ } catch (err) {
+ console.error("Backtest Error:", err);
+ }
+ };
+
+ useEffect(() => {
+ loadBacktest();
+ }, []);
+
+ return (
+
+
+ 📊 Backtest Dashboard (Strategy Performance)
+
+
+ {/* ✅ EXPLANATION PANEL */}
+
+
📘 How to Read Backtest
+
+
+ ✅ Trades = Total opportunities generated
+
+
+ ✅ Wins = Profitable trades
+
+
+ ✅ Loss = Losing trades
+
+
+ ✅ Win Rate = Success % of strategy
+
+
+ ✅ Profit = Net gain (simulated)
+
+
+
+
+
+ 💡 Best Strategy Rules:
+
+
+ - Win Rate ≥ 60% ✅
+ - Trades ≥ 10 ✅
+ - Profit positive ✅
+
+
+
+ {/* ✅ SECTORS */}
+ {Object.entries(data).map(([sector, stocks]) => (
+
+
+ {sector}
+
+
+
+
+
+ | Stock |
+ Trades |
+ Wins |
+ Loss |
+ Win % |
+ Profit |
+
+
+
+
+ {stocks.map((s, i) => (
+
+ | {s.symbol} |
+
+ {s.trades} |
+
+
+ {s.wins}
+ |
+
+
+ {s.loss}
+ |
+
+ {/* ✅ WIN RATE COLOR */}
+ = 70
+ ? "#3fb950"
+ : s.win_rate >= 60
+ ? "#d29922"
+ : "#f85149",
+ fontWeight: "bold",
+ }}
+ >
+ {s.win_rate}%
+ |
+
+ {/* ✅ PROFIT COLOR */}
+ 0 ? "#3fb950" : "#f85149",
+ fontWeight: "bold",
+ }}
+ >
+ {s.profit}
+ |
+
+ ))}
+
+
+
+ ))}
+
+ );
+}
+
export default Backtest;
\ No newline at end of file
diff --git a/src/components/Chart.js b/src/components/Chart.js
index c1ded22..cb18a9f 100644
--- a/src/components/Chart.js
+++ b/src/components/Chart.js
@@ -1,149 +1,149 @@
-import React, { useEffect, useRef } from "react";
-import { createChart, CandlestickSeries, LineSeries } from "lightweight-charts";
-import axios from "axios";
-
-function Chart({ symbol }) {
- const ref = useRef(null);
-
- useEffect(() => {
- if (!ref.current || !symbol) return;
-
- ref.current.innerHTML = "";
-
- try {
- const chart = createChart(ref.current, {
- width: 800,
- height: 500,
- layout: {
- background: { color: "#111" },
- textColor: "#DDD",
- },
- grid: {
- vertLines: { color: "#333" },
- horzLines: { color: "#333" },
- },
- });
-
- // ✅ MAIN CANDLE SERIES
- const candleSeries = chart.addSeries(CandlestickSeries);
-
- // ✅ EMA LINE
- const emaSeries = chart.addSeries(LineSeries, {
- color: "yellow",
- lineWidth: 2,
- });
-
- // ✅ RSI LINE (drawn on same chart for simplicity)
- const rsiSeries = chart.addSeries(LineSeries, {
- color: "cyan",
- lineWidth: 1,
- });
-
- // ✅ EMA CALCULATION
- const calculateEMA = (data, period = 20) => {
- const k = 2 / (period + 1);
- let ema = data[0].close;
- return data.map((d) => {
- ema = d.close * k + ema * (1 - k);
- return { time: d.time, value: ema };
- });
- };
-
- // ✅ RSI CALCULATION
- const calculateRSI = (data, period = 14) => {
- let gains = 0;
- let losses = 0;
-
- const result = [];
-
- for (let i = 1; i < data.length; i++) {
- const diff = data[i].close - data[i - 1].close;
-
- if (diff > 0) gains += diff;
- else losses -= diff;
-
- if (i >= period) {
- const rs = gains / (losses || 1);
- const rsi = 100 - 100 / (1 + rs);
-
- result.push({ time: data[i].time, value: rsi });
- }
- }
-
- return result;
- };
-
- // ✅ LOAD DATA FUNCTION
- const loadData = () => {
- axios
- .get(`http://localhost:8000/history?symbol=${symbol}`)
- .then((res) => {
- if (!res.data || res.data.length === 0) return;
-
- const formatted = res.data.map((d) => ({
- time: Math.floor(d.time),
- open: Number(d.open),
- high: Number(d.high),
- low: Number(d.low),
- close: Number(d.close),
- }));
-
- // ✅ SET CANDLES
- candleSeries.setData(formatted);
-
- // ✅ EMA
- const emaData = calculateEMA(formatted);
- emaSeries.setData(emaData);
-
- // ✅ RSI
- const rsiData = calculateRSI(formatted);
- rsiSeries.setData(rsiData);
-
- // ✅ ENTRY / SL / TARGET
- const lastPrice = formatted[formatted.length - 1].close;
-
- candleSeries.createPriceLine({
- price: lastPrice,
- color: "blue",
- lineWidth: 2,
- title: "Entry",
- });
-
- candleSeries.createPriceLine({
- price: lastPrice * 0.97,
- color: "red",
- title: "SL",
- });
-
- candleSeries.createPriceLine({
- price: lastPrice * 1.05,
- color: "green",
- title: "Target",
- });
-
- chart.timeScale().fitContent();
- })
- .catch((err) => console.error(err));
- };
-
- // ✅ INITIAL LOAD
- loadData();
-
- // ✅ LIVE UPDATE (every 1 min)
- const interval = setInterval(loadData, 60000);
-
- return () => {
- clearInterval(interval);
- chart.remove();
- };
-
- } catch (err) {
- console.error("Chart crash:", err);
- }
-
- }, [symbol]);
-
- return
;
-}
-
+import React, { useEffect, useRef } from "react";
+import { createChart, CandlestickSeries, LineSeries } from "lightweight-charts";
+import axios from "axios";
+
+function Chart({ symbol }) {
+ const ref = useRef(null);
+
+ useEffect(() => {
+ if (!ref.current || !symbol) return;
+
+ ref.current.innerHTML = "";
+
+ try {
+ const chart = createChart(ref.current, {
+ width: 800,
+ height: 500,
+ layout: {
+ background: { color: "#111" },
+ textColor: "#DDD",
+ },
+ grid: {
+ vertLines: { color: "#333" },
+ horzLines: { color: "#333" },
+ },
+ });
+
+ // ✅ MAIN CANDLE SERIES
+ const candleSeries = chart.addSeries(CandlestickSeries);
+
+ // ✅ EMA LINE
+ const emaSeries = chart.addSeries(LineSeries, {
+ color: "yellow",
+ lineWidth: 2,
+ });
+
+ // ✅ RSI LINE (drawn on same chart for simplicity)
+ const rsiSeries = chart.addSeries(LineSeries, {
+ color: "cyan",
+ lineWidth: 1,
+ });
+
+ // ✅ EMA CALCULATION
+ const calculateEMA = (data, period = 20) => {
+ const k = 2 / (period + 1);
+ let ema = data[0].close;
+ return data.map((d) => {
+ ema = d.close * k + ema * (1 - k);
+ return { time: d.time, value: ema };
+ });
+ };
+
+ // ✅ RSI CALCULATION
+ const calculateRSI = (data, period = 14) => {
+ let gains = 0;
+ let losses = 0;
+
+ const result = [];
+
+ for (let i = 1; i < data.length; i++) {
+ const diff = data[i].close - data[i - 1].close;
+
+ if (diff > 0) gains += diff;
+ else losses -= diff;
+
+ if (i >= period) {
+ const rs = gains / (losses || 1);
+ const rsi = 100 - 100 / (1 + rs);
+
+ result.push({ time: data[i].time, value: rsi });
+ }
+ }
+
+ return result;
+ };
+
+ // ✅ LOAD DATA FUNCTION
+ const loadData = () => {
+ axios
+ .get(`http://localhost:8000/history?symbol=${symbol}`)
+ .then((res) => {
+ if (!res.data || res.data.length === 0) return;
+
+ const formatted = res.data.map((d) => ({
+ time: Math.floor(d.time),
+ open: Number(d.open),
+ high: Number(d.high),
+ low: Number(d.low),
+ close: Number(d.close),
+ }));
+
+ // ✅ SET CANDLES
+ candleSeries.setData(formatted);
+
+ // ✅ EMA
+ const emaData = calculateEMA(formatted);
+ emaSeries.setData(emaData);
+
+ // ✅ RSI
+ const rsiData = calculateRSI(formatted);
+ rsiSeries.setData(rsiData);
+
+ // ✅ ENTRY / SL / TARGET
+ const lastPrice = formatted[formatted.length - 1].close;
+
+ candleSeries.createPriceLine({
+ price: lastPrice,
+ color: "blue",
+ lineWidth: 2,
+ title: "Entry",
+ });
+
+ candleSeries.createPriceLine({
+ price: lastPrice * 0.97,
+ color: "red",
+ title: "SL",
+ });
+
+ candleSeries.createPriceLine({
+ price: lastPrice * 1.05,
+ color: "green",
+ title: "Target",
+ });
+
+ chart.timeScale().fitContent();
+ })
+ .catch((err) => console.error(err));
+ };
+
+ // ✅ INITIAL LOAD
+ loadData();
+
+ // ✅ LIVE UPDATE (every 1 min)
+ const interval = setInterval(loadData, 60000);
+
+ return () => {
+ clearInterval(interval);
+ chart.remove();
+ };
+
+ } catch (err) {
+ console.error("Chart crash:", err);
+ }
+
+ }, [symbol]);
+
+ return
;
+}
+
export default Chart;
\ No newline at end of file
diff --git a/src/components/ChartPage.js b/src/components/ChartPage.js
index 393f90c..255f9e6 100644
--- a/src/components/ChartPage.js
+++ b/src/components/ChartPage.js
@@ -1,21 +1,21 @@
-import React from "react";
-
-function ChartPage({ symbol }) {
- const tvSymbol = symbol || "NSE:RELIANCE";
-
- const url = `https://www.tradingview.com/chart/?symbol=${tvSymbol}`;
-
- return (
-
-
-
- );
-}
-
+import React from "react";
+
+function ChartPage({ symbol }) {
+ const tvSymbol = symbol || "NSE:RELIANCE";
+
+ const url = `https://www.tradingview.com/chart/?symbol=${tvSymbol}`;
+
+ return (
+
+
+
+ );
+}
+
export default ChartPage;
\ No newline at end of file
diff --git a/src/components/Login.js b/src/components/Login.js
index 80a7c87..fa14713 100644
--- a/src/components/Login.js
+++ b/src/components/Login.js
@@ -15,13 +15,13 @@ function Login() {
setLoading(true);
setError("");
try {
- const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
+ const resKey = await axios.get("/api/auth/public-key");
const publicKeyPem = resKey.data.public_key;
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const encrypted = publicKey.encrypt(password, 'RSA-OAEP');
const encryptedBase64 = forge.util.encode64(encrypted);
- const res = await axios.post("http://127.0.0.1:8000/api/auth/login", {
+ const res = await axios.post("/api/auth/login", {
username,
password: encryptedBase64
});
diff --git a/src/components/LongTerm.js b/src/components/LongTerm.js
index 97129dc..5272c53 100644
--- a/src/components/LongTerm.js
+++ b/src/components/LongTerm.js
@@ -1,114 +1,114 @@
-import React, { useEffect, useState } from "react";
-import axios from "axios";
-
-function LongTerm() {
- const [data, setData] = useState({});
-
- const loadData = async () => {
- try {
- const res = await axios.get("http://127.0.0.1:8000/longterm");
- setData(res.data);
- } catch (err) {
- console.error(err);
- }
- };
-
- useEffect(() => {
- loadData();
- }, []);
-
- return (
-
-
-
- 📈 Long-Term Investment Dashboard
-
-
- {/* ✅ EXPLANATION */}
-
-
📘 Strategy
-
-
- - ✅ Golden Cross = 50 EMA > 200 EMA
- - ✅ Support = Price above 200 EMA
- - ✅ Momentum = Breakout + Volume
-
-
-
💡 Only strong trend reversal stocks are shown
-
-
- {Object.entries(data).map(([sector, info]) => {
-
-
-const filtered = info.stocks.filter(
- s => s.golden_cross || s.support_strength
-);
-
-
- if (filtered.length === 0) return null;
-
- return (
-
-
-
- {sector} ✅ Long-Term Strong
-
-
-
-
-
- | Stock |
- Score |
- RSI |
- Golden Cross |
- Support |
- Signal |
-
-
-
-
- {filtered.map((s, i) => (
-
- | {s.symbol} |
- {s.score} |
- {s.rsi} |
-
-
- ✅
- |
-
-
- ✅
- |
-
-
- {s.signal}
- |
-
- ))}
-
-
-
-
- );
- })}
-
- );
-}
-
-export default LongTerm;
+import React, { useEffect, useState } from "react";
+import axios from "axios";
+
+function LongTerm() {
+ const [data, setData] = useState({});
+
+ const loadData = async () => {
+ try {
+ const res = await axios.get("/api/longterm");
+ setData(res.data);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ useEffect(() => {
+ loadData();
+ }, []);
+
+ return (
+
+
+
+ 📈 Long-Term Investment Dashboard
+
+
+ {/* ✅ EXPLANATION */}
+
+
📘 Strategy
+
+
+ - ✅ Golden Cross = 50 EMA > 200 EMA
+ - ✅ Support = Price above 200 EMA
+ - ✅ Momentum = Breakout + Volume
+
+
+
💡 Only strong trend reversal stocks are shown
+
+
+ {Object.entries(data).map(([sector, info]) => {
+
+
+const filtered = info.stocks.filter(
+ s => s.golden_cross || s.support_strength
+);
+
+
+ if (filtered.length === 0) return null;
+
+ return (
+
+
+
+ {sector} ✅ Long-Term Strong
+
+
+
+
+
+ | Stock |
+ Score |
+ RSI |
+ Golden Cross |
+ Support |
+ Signal |
+
+
+
+
+ {filtered.map((s, i) => (
+
+ | {s.symbol} |
+ {s.score} |
+ {s.rsi} |
+
+
+ ✅
+ |
+
+
+ ✅
+ |
+
+
+ {s.signal}
+ |
+
+ ))}
+
+
+
+
+ );
+ })}
+
+ );
+}
+
+export default LongTerm;
diff --git a/src/components/MainDashboard.js b/src/components/MainDashboard.js
new file mode 100644
index 0000000..7300584
--- /dev/null
+++ b/src/components/MainDashboard.js
@@ -0,0 +1,155 @@
+import React, { useState, useRef, useEffect } from "react";
+import axios from "axios";
+
+function MainDashboard({ userProfile }) {
+ const [searchQuery, setSearchQuery] = useState("");
+ const [searchResults, setSearchResults] = useState([]);
+ const [searching, setSearching] = useState(false);
+ const searchTimeout = useRef(null);
+
+ const formatMarketCap = (cap) => {
+ if (!cap) return "N/A";
+ if (cap >= 1e12) return "₹" + (cap / 1e12).toFixed(2) + "T";
+ if (cap >= 1e9) return "₹" + (cap / 1e9).toFixed(2) + "B";
+ if (cap >= 1e7) return "₹" + (cap / 1e7).toFixed(2) + "Cr";
+ return "₹" + cap.toLocaleString();
+ };
+
+ const getMarketCapCategory = (cap) => {
+ if (!cap) return null;
+ const cr = cap / 1e7;
+ if (cr >= 20000) return "Large Cap";
+ if (cr >= 5000) return "Mid Cap";
+ return "Small Cap";
+ };
+
+ const formatPrice = (price) => {
+ if (!price) return "N/A";
+ return "₹" + price.toFixed(2);
+ };
+
+ const handleSearch = (e) => {
+ const query = e.target.value;
+ setSearchQuery(query);
+
+ if (searchTimeout.current) clearTimeout(searchTimeout.current);
+
+ if (query.trim().length < 2) {
+ setSearchResults([]);
+ return;
+ }
+
+ searchTimeout.current = setTimeout(async () => {
+ setSearching(true);
+ try {
+ const res = await axios.get(`/api/scanner/search?q=${query}`);
+ setSearchResults(res.data);
+ } catch (err) {
+ console.error(err);
+ }
+ setSearching(false);
+ }, 500);
+ };
+
+ const openChart = (symbol) => {
+ window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
+ };
+
+ return (
+
+
+
+
+ 🔍
+
+ {searching && Searching...}
+
+
+ {/* Search Results Dropdown */}
+ {searchResults.length > 0 && searchQuery.length >= 2 && (
+
+ {searchResults.map((result, i) => (
+
openChart(result.symbol)}
+ style={{
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ padding: '15px',
+ borderBottom: '1px solid var(--border-color)',
+ background: 'var(--bg-tertiary)',
+ marginBottom: '8px',
+ borderRadius: '12px',
+ cursor: 'pointer',
+ transition: 'background 0.2s'
+ }}
+ onMouseOver={(e) => e.currentTarget.style.background = 'rgba(59, 130, 246, 0.1)'}
+ onMouseOut={(e) => e.currentTarget.style.background = 'var(--bg-tertiary)'}
+ >
+
+
+ {result.symbol}
+ {result.sector && (
+
+ {result.sector}
+
+ )}
+ {result.marketCap && (
+
+ {getMarketCapCategory(result.marketCap)}
+
+ )}
+
+
+ {result.longname || result.shortname}
+ {result.currentPrice && (
+ <>
+ •
+ Price: {formatPrice(result.currentPrice)}
+ >
+ )}
+ {result.marketCap && (
+ <>
+ •
+ {formatMarketCap(result.marketCap)}
+ >
+ )}
+
+
+
+
+ ))}
+
+ )}
+
+
+
+ );
+}
+
+export default MainDashboard;
diff --git a/src/components/MarketIntel.js b/src/components/MarketIntel.js
index 2a0e3f3..52eb37f 100644
--- a/src/components/MarketIntel.js
+++ b/src/components/MarketIntel.js
@@ -10,7 +10,7 @@ function MarketIntel() {
const loadData = async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/scanner/marketintel");
+ const res = await axios.get("/api/scanner/marketintel");
setData(res.data);
} catch (err) {
console.error(err);
diff --git a/src/components/Popup.js b/src/components/Popup.js
index 0005458..7ede032 100644
--- a/src/components/Popup.js
+++ b/src/components/Popup.js
@@ -1,22 +1,22 @@
-import React, { useEffect } from "react";
-
-function Popup({ signal }) {
- useEffect(() => {
- const timer = setTimeout(() => {
- const popup = document.getElementById("popup");
- if (popup) popup.style.display = "none";
- }, 4000);
-
- return () => clearTimeout(timer);
- }, []);
-
- return (
-
- );
-}
-
+import React, { useEffect } from "react";
+
+function Popup({ signal }) {
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ const popup = document.getElementById("popup");
+ if (popup) popup.style.display = "none";
+ }, 4000);
+
+ return () => clearTimeout(timer);
+ }, []);
+
+ return (
+
+ );
+}
+
export default Popup;
\ No newline at end of file
diff --git a/src/components/Profile.js b/src/components/Profile.js
index 238a860..f73a454 100644
--- a/src/components/Profile.js
+++ b/src/components/Profile.js
@@ -43,7 +43,7 @@ function Profile({ userProfile, onProfileUpdated }) {
if (payload.password) {
// Fetch Public Key to encrypt new password
- const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
+ const resKey = await axios.get("/api/auth/public-key");
const publicKey = forge.pki.publicKeyFromPem(resKey.data.public_key);
const encrypted = publicKey.encrypt(payload.password, 'RSA-OAEP');
payload.password = forge.util.encode64(encrypted);
@@ -51,7 +51,7 @@ function Profile({ userProfile, onProfileUpdated }) {
delete payload.password;
}
- await axios.put("http://127.0.0.1:8000/api/auth/update-profile", payload);
+ await axios.put("/api/auth/update-profile", payload);
setMessage("Profile updated successfully! ✅");
if (onProfileUpdated) onProfileUpdated();
setFormData(prev => ({ ...prev, password: "" })); // Clear password field
diff --git a/src/components/Ribbon.js b/src/components/Ribbon.js
index 266e1ce..a0d44eb 100644
--- a/src/components/Ribbon.js
+++ b/src/components/Ribbon.js
@@ -7,7 +7,7 @@ function Ribbon() {
const loadData = async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/scanner/ribbon");
+ const res = await axios.get("/api/scanner/ribbon");
setData(res.data || []);
setLoading(false);
} catch (err) {
@@ -26,7 +26,18 @@ function Ribbon() {
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
};
- if (loading) return
Loading Ribbon...
;
+ const removeStockFromSector = async (symbol, sector) => {
+ try {
+ await axios.delete("/api/scanner/sectors", {
+ data: { sector, symbol }
+ });
+ setData(prev => prev.filter(s => s.symbol !== symbol));
+ } catch (err) {
+ console.error("Error removing stock", err);
+ }
+ };
+
+ if (loading) return
Loading Ribbon (this may take up to 60 seconds for large watchlists)...
;
return (
@@ -65,9 +76,23 @@ function Ribbon() {
-
+
+
+ {s.sector && (
+
+ )}
+
|
))}
diff --git a/src/components/Scanner.js b/src/components/Scanner.js
index 9a988fd..dba4037 100644
--- a/src/components/Scanner.js
+++ b/src/components/Scanner.js
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
+import AboutScanner from "./AboutScanner";
function Scanner({ onSelectStock, onSignal }) {
const [data, setData] = useState(null);
@@ -7,7 +8,7 @@ function Scanner({ onSelectStock, onSignal }) {
const loadData = async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/scanner/scan");
+ const res = await axios.get("/api/scanner/scan");
const newData = res.data;
const best = newData.best_5 && newData.best_5.length > 0 ? newData.best_5[0] : null;
@@ -33,10 +34,37 @@ function Scanner({ onSelectStock, onSignal }) {
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
};
- if (!data) return
Loading Scanner Data...
;
+ const removeStockFromSector = async (symbol, sector) => {
+ try {
+ await axios.delete("/api/scanner/sectors", {
+ data: { sector, symbol }
+ });
+ setData(prev => {
+ const newData = JSON.parse(JSON.stringify(prev));
+ if (newData.sectors && newData.sectors[sector]) {
+ newData.sectors[sector].stocks = newData.sectors[sector].stocks.filter(s => s.symbol !== symbol);
+ }
+ if (newData.best_5) {
+ newData.best_5 = newData.best_5.filter(s => s.symbol !== symbol);
+ }
+ return newData;
+ });
+ } catch (err) {
+ console.error("Error removing stock", err);
+ }
+ };
+
+ if (!data) return (
+ <>
+
+
Loading Scanner Data (this may take up to 60 seconds for large watchlists)...
+ >
+ );
return (
+
+
{/* Top 5 Trades */}
{(data.best_5 || []).length > 0 && (
@@ -72,9 +100,23 @@ function Scanner({ onSelectStock, onSignal }) {
{s.grade} |
{s.institutional ? 🔥 Yes : No} |
-
+
+
+ {s?.sector && (
+
+ )}
+
|
))}
@@ -125,9 +167,21 @@ function Scanner({ onSelectStock, onSignal }) {
₹{s?.target} |
{s?.ai_score} |
-
+
+
+
+
|
))}
diff --git a/src/components/Signup.js b/src/components/Signup.js
index dd4f7aa..2bbaab8 100644
--- a/src/components/Signup.js
+++ b/src/components/Signup.js
@@ -15,13 +15,13 @@ function Signup() {
setLoading(true);
setError("");
try {
- const resKey = await axios.get("http://127.0.0.1:8000/api/auth/public-key");
+ const resKey = await axios.get("/api/auth/public-key");
const publicKeyPem = resKey.data.public_key;
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const encrypted = publicKey.encrypt(password, 'RSA-OAEP');
const encryptedBase64 = forge.util.encode64(encrypted);
- await axios.post("http://127.0.0.1:8000/api/auth/signup", {
+ await axios.post("/api/auth/signup", {
username,
password: encryptedBase64
});
diff --git a/src/components/SmartMoney.js b/src/components/SmartMoney.js
index 19c9e2f..c5911e1 100644
--- a/src/components/SmartMoney.js
+++ b/src/components/SmartMoney.js
@@ -12,7 +12,7 @@ function SmartMoney() {
const loadData = async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/scanner/smartmoney");
+ const res = await axios.get("/api/scanner/smartmoney");
setData(res.data);
} catch (err) {
console.error(err);
@@ -23,7 +23,30 @@ function SmartMoney() {
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
};
- if (!data) return
Loading Smart Money Data...
;
+ const removeStockFromSector = async (symbol, sector) => {
+ try {
+ await axios.delete("/api/scanner/sectors", {
+ data: { sector, symbol }
+ });
+ setData(prev => {
+ const newData = JSON.parse(JSON.stringify(prev));
+ if (newData.top_10) {
+ newData.top_10 = newData.top_10.filter(s => s.symbol !== symbol);
+ }
+ if (newData.all) {
+ newData.all = newData.all.filter(s => s.symbol !== symbol);
+ }
+ if (newData.trade_of_day && newData.trade_of_day.symbol === symbol) {
+ newData.trade_of_day = newData.top_10.length > 0 ? newData.top_10[0] : null;
+ }
+ return newData;
+ });
+ } catch (err) {
+ console.error("Error removing stock", err);
+ }
+ };
+
+ if (!data) return
Loading Smart Money Data (this may take up to 60 seconds for large watchlists)...
;
return (
@@ -80,7 +103,21 @@ function SmartMoney() {
{s.breakout ? "🔥 YES" : "NO"} |
{s.reason} |
-
+
+
+ {s.sector && (
+
+ )}
+
|
))}
diff --git a/src/components/TrendScanner.js b/src/components/TrendScanner.js
index d876751..3e7a1d1 100644
--- a/src/components/TrendScanner.js
+++ b/src/components/TrendScanner.js
@@ -12,7 +12,7 @@ function TrendScanner() {
const loadData = async () => {
try {
- const res = await axios.get("http://127.0.0.1:8000/api/scanner/trend");
+ const res = await axios.get("/api/scanner/trend");
setData(res.data);
} catch (err) {
console.error(err);
@@ -23,7 +23,24 @@ function TrendScanner() {
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
};
- if (!data) return
Loading Trend Data...
;
+ const removeStockFromSector = async (symbol, sector) => {
+ try {
+ await axios.delete("/api/scanner/sectors", {
+ data: { sector, symbol }
+ });
+ setData(prev => {
+ const newData = JSON.parse(JSON.stringify(prev));
+ if (newData.top_20) {
+ newData.top_20 = newData.top_20.filter(s => s.symbol !== symbol);
+ }
+ return newData;
+ });
+ } catch (err) {
+ console.error("Error removing stock", err);
+ }
+ };
+
+ if (!data) return
Loading Trend Data (this may take up to 60 seconds for large watchlists)...
;
return (
@@ -63,7 +80,21 @@ function TrendScanner() {
{s.trend_reason} |
-
+
+
+ {s.sector && (
+
+ )}
+
|
))}
diff --git a/src/components/WatchlistManager.js b/src/components/WatchlistManager.js
new file mode 100644
index 0000000..a229162
--- /dev/null
+++ b/src/components/WatchlistManager.js
@@ -0,0 +1,279 @@
+import React, { useState, useEffect, useRef } from "react";
+import axios from "axios";
+
+function WatchlistManager({ userProfile, onProfileUpdated }) {
+ const [sectorMap, setSectorMap] = useState({});
+ const [newSectorName, setNewSectorName] = useState("");
+ const [searchQuery, setSearchQuery] = useState("");
+ const [searchResults, setSearchResults] = useState([]);
+ const [searching, setSearching] = useState(false);
+ const [selectedSector, setSelectedSector] = useState("Personal");
+ const [saving, setSaving] = useState(false);
+ const [message, setMessage] = useState("");
+
+ const searchTimeout = useRef(null);
+
+ const formatMarketCap = (cap) => {
+ if (!cap) return "N/A";
+ if (cap >= 1e12) return "₹" + (cap / 1e12).toFixed(2) + "T";
+ if (cap >= 1e9) return "₹" + (cap / 1e9).toFixed(2) + "B";
+ if (cap >= 1e7) return "₹" + (cap / 1e7).toFixed(2) + "Cr";
+ return "₹" + cap.toLocaleString();
+ };
+
+ const getMarketCapCategory = (cap) => {
+ if (!cap) return null;
+ const cr = cap / 1e7; // Convert to Crores
+ if (cr >= 20000) return "Large Cap";
+ if (cr >= 5000) return "Mid Cap";
+ return "Small Cap";
+ };
+
+ const formatPrice = (price) => {
+ if (!price) return "N/A";
+ return "₹" + price.toFixed(2);
+ };
+
+ useEffect(() => {
+ if (userProfile && userProfile.sector_map) {
+ setSectorMap(userProfile.sector_map);
+ }
+ }, [userProfile]);
+
+ const saveSectors = async (updatedMap) => {
+ setSaving(true);
+ setMessage("");
+ try {
+ await axios.put("/api/auth/update-sectors", { sector_map: updatedMap });
+ setSectorMap(updatedMap);
+ if (onProfileUpdated) onProfileUpdated();
+ setMessage("Sectors saved! ✅");
+ setTimeout(() => setMessage(""), 3000);
+ } catch (err) {
+ console.error(err);
+ setMessage("Failed to save sectors.");
+ }
+ setSaving(false);
+ };
+
+ const handleSearch = (e) => {
+ const query = e.target.value;
+ setSearchQuery(query);
+
+ if (searchTimeout.current) clearTimeout(searchTimeout.current);
+
+ if (query.trim().length < 2) {
+ setSearchResults([]);
+ return;
+ }
+
+ searchTimeout.current = setTimeout(async () => {
+ setSearching(true);
+ try {
+ const res = await axios.get(`/api/scanner/search?q=${query}`);
+ setSearchResults(res.data);
+ } catch (err) {
+ console.error(err);
+ }
+ setSearching(false);
+ }, 500);
+ };
+
+ const addStockToSector = (symbol, targetSector) => {
+ const updatedMap = { ...sectorMap };
+ if (!updatedMap[targetSector]) updatedMap[targetSector] = [];
+ if (!updatedMap[targetSector].includes(symbol)) {
+ updatedMap[targetSector].push(symbol);
+ saveSectors(updatedMap);
+ }
+ };
+
+ const removeStockFromSector = (symbol, targetSector) => {
+ const updatedMap = { ...sectorMap };
+ if (updatedMap[targetSector]) {
+ updatedMap[targetSector] = updatedMap[targetSector].filter(s => s !== symbol);
+ saveSectors(updatedMap);
+ }
+ };
+
+ const createSector = () => {
+ const name = newSectorName.trim();
+ if (name && !sectorMap[name]) {
+ const updatedMap = { ...sectorMap, [name]: [] };
+ saveSectors(updatedMap);
+ setNewSectorName("");
+ }
+ };
+
+ const deleteSector = (sector) => {
+ if (sector === "Personal") return; // Protect Personal
+ if (window.confirm(`Are you sure you want to delete the ${sector} watchlist?`)) {
+ const updatedMap = { ...sectorMap };
+ delete updatedMap[sector];
+ saveSectors(updatedMap);
+ if (selectedSector === sector) setSelectedSector("Personal");
+ }
+ };
+
+ return (
+
+
+ {/* LEFT PANEL: Sector List & Creator */}
+
+
📋 Watchlists
+
+ {Object.keys(sectorMap).map(sector => (
+
setSelectedSector(sector)}
+ style={{
+ padding: '10px 15px',
+ background: selectedSector === sector ? 'rgba(59, 130, 246, 0.2)' : 'var(--bg-tertiary)',
+ border: selectedSector === sector ? '1px solid var(--accent-blue)' : '1px solid var(--border-color)',
+ borderRadius: '8px', cursor: 'pointer',
+ display: 'flex', justifyContent: 'space-between', alignItems: 'center',
+ transition: 'all 0.2s'
+ }}
+ >
+
+ {sector} ({sectorMap[sector].length})
+
+ {sector !== "Personal" && (
+
+ )}
+
+ ))}
+
+
+
+
Create new watchlist
+
+ setNewSectorName(e.target.value)}
+ placeholder="E.g., EV Stocks"
+ style={{ flex: 1, padding: '10px', borderRadius: '5px', border: '1px solid var(--border-color)', background: 'var(--bg-tertiary)', color: 'white' }}
+ />
+
+
+
+
+
+ {/* RIGHT PANEL: Search & Stock List */}
+
+
+
+ {selectedSector} Watchlist
+
+ {saving && Saving...}
+ {message && {message}}
+
+
+ {/* Search Bar */}
+
+
+ {searching &&
Searching...}
+
+ {/* Search Results Dropdown */}
+ {searchResults.length > 0 && searchQuery.length >= 2 && (
+
+ {searchResults.map((result, i) => (
+
+
+
+ {result.symbol}
+ {result.sector && (
+
+ {result.sector}
+
+ )}
+ {result.marketCap && (
+
+ {getMarketCapCategory(result.marketCap)}
+
+ )}
+
+
+ {result.longname || result.shortname}
+ {result.currentPrice && (
+ <>
+ •
+ Price: {formatPrice(result.currentPrice)}
+ >
+ )}
+ {result.marketCap && (
+ <>
+ •
+ {formatMarketCap(result.marketCap)}
+ >
+ )}
+
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Stock List Grid */}
+ {sectorMap[selectedSector] && sectorMap[selectedSector].length === 0 ? (
+
No stocks in this watchlist. Search above to add some!
+ ) : (
+
+
+ {(sectorMap[selectedSector] || []).map(symbol => (
+
+
+ {/* Top Row: Symbol and Trash */}
+
+
{symbol}
+
+
+
+ {/* Bottom Row: + Personal */}
+ {selectedSector !== "Personal" && (
+
+ )}
+
+ ))}
+
+
+ )}
+
+
+ );
+}
+
+export default WatchlistManager;
diff --git a/src/index.css b/src/index.css
index e0205d5..012f9cb 100644
--- a/src/index.css
+++ b/src/index.css
@@ -83,9 +83,9 @@ h2 {
background: var(--bg-tertiary);
color: var(--text-primary);
border: 1px solid var(--border-color);
- padding: 10px 20px;
+ padding: 8px 14px;
border-radius: 8px;
- font-size: 0.95rem;
+ font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;