initial commit
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
1
.vscode/settings.json
vendored
Normal file
1
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
70
README.md
Normal file
70
README.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
BIN
__pycache__/ribbon_server.cpython-314.pyc
Normal file
BIN
__pycache__/ribbon_server.cpython-314.pyc
Normal file
Binary file not shown.
BIN
__pycache__/server.cpython-313.pyc
Normal file
BIN
__pycache__/server.cpython-313.pyc
Normal file
Binary file not shown.
BIN
__pycache__/server.cpython-314.pyc
Normal file
BIN
__pycache__/server.cpython-314.pyc
Normal file
Binary file not shown.
17554
package-lock.json
generated
Normal file
17554
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
42
package.json
Normal file
42
package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "stock-scanner",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"axios": "^1.18.1",
|
||||
"lightweight-charts": "^5.2.0",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-router-dom": "^7.18.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
43
public/index.html
Normal file
43
public/index.html
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/logo192.png
Normal file
BIN
public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logo512.png
Normal file
BIN
public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
25
public/manifest.json
Normal file
25
public/manifest.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
public/robots.txt
Normal file
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
679
server.py
Normal file
679
server.py
Normal file
@@ -0,0 +1,679 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
import yfinance as yf
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# ✅ PERSONAL
|
||||
# ✅ PERSONAL
|
||||
personal_stocks = [
|
||||
"AFFLE.NS","ANANDRATHI.NS","CGPOWER.NS","HFCL.NS",
|
||||
"IREDA.NS","INDOAMIN.NS","PCJEWELLER.NS","PCBL.NS",
|
||||
"RANASUG.NS","SERVOTECH.NS","IDEA.NS","WELSPUNLIV.NS","SANDHAR.NS"
|
||||
]
|
||||
|
||||
# ✅ SECTORS
|
||||
sector_map = {"Personal": personal_stocks,
|
||||
"lookup" :["GRAVITA.NS"],
|
||||
"CG" :["SIEMENS.NS","ABB.NS","BEL.NS","CGPOWER.NS","BHEL.NS","HINDALCO.NS","CUMMINSIND.NS",
|
||||
"SUZLON.NS","THERMAX.NS","POLYCAB.NS","KEI.NS","HAVELLS.NS","VOLTAS.NS","BLUESTARCO.NS",
|
||||
"VGUARD.NS","AIAENG.NS","ELGIEQUIP.NS","TRITURBINE.NS","SANDHAR.NS"],
|
||||
"NIFTY Bank": ["HDFCBANK.NS","ICICIBANK.NS","AXISBANK.NS","KOTAKBANK.NS","SBIN.NS","INDUSINDBK.NS",
|
||||
"BANKBARODA.NS","PNB.NS","FEDERALBNK.NS","IDFCFIRSTB.NS","AUBANK.NS","BANDHANBNK.NS",
|
||||
"CANBK.NS","UNIONBANK.NS","IOB.NS","UCOBANK.NS","BANKINDIA.NS","CENTRALBK.NS",
|
||||
"MAHABANK.NS","YESBANK.NS","IDBI.NS","SOUTHBANK.NS",
|
||||
"RBLBANK.NS","KTKBANK.NS","DCBBANK.NS"],
|
||||
"NIFTY Auto": ["MARUTI.NS","M&M.NS","BAJAJ-AUTO.NS","EICHERMOT.NS","TVSMOTOR.NS",
|
||||
"HEROMOTOCO.NS","ASHOKLEY.NS","BALKRISIND.NS","MRF.NS","APOLLOTYRE.NS","JKTYRE.NS",
|
||||
"CEATLTD.NS","SONACOMS.NS","ENDURANCE.NS","EXIDEIND.NS","BOSCHLTD.NS",
|
||||
"UNOMINDA.NS"],
|
||||
"NIFTY Pharma": ["SUNPHARMA.NS","CIPLA.NS","DRREDDY.NS","DIVISLAB.NS","ZYDUSLIFE.NS","MANKIND.NS",
|
||||
"LUPIN.NS","AUROPHARMA.NS","ALKEM.NS","BIOCON.NS","GLENMARK.NS",
|
||||
"LAURUSLABS.NS","GRANULES.NS","IPCALAB.NS","JBCHEPHARM.NS","NATCOPHARM.NS","Eris.NS",
|
||||
"ABBOTINDIA.NS","PFIZER.NS"],
|
||||
"Defence": [ "HAL.NS", "BEL.NS", "MAZDOCK.NS", "DATAPATTNS.NS", "BDL.NS", "MIDHANI.NS", "BEML.NS",
|
||||
"COCHINSHIP.NS", "GRSE.NS", "SOLARINDS.NS", "BHARATFORG.NS", "ZENTEC.NS", "PARAS.NS", "ASTRAMICRO.NS",
|
||||
"IDEAFORGE.NS", "APOLLO.NS", "AVANTEL.NS"],
|
||||
"Energy": ["NTPC.NS","POWERGRID.NS","RELIANCE.NS","ONGC.NS","BPCL.NS","IOC.NS","GAIL.NS","TATAPOWER.NS",
|
||||
"ADANIGREEN.NS","ADANIPOWER.NS","SJVN.NS","IREDA.NS","SUZLON.NS",
|
||||
"KPIGREEN.NS","BORORENEW.NS","AWL.NS","JSWENERGY.NS"],
|
||||
"Chemical": ["SRF.NS","PIDILITIND.NS","LINDEINDIA.NS","TATACHEM.NS","AARTIIND.NS","ATUL.NS",
|
||||
"DEEPAKNTR.NS","GUJALKALI.NS","NAVINFLUOR.NS","FLUOROCHEM.NS","CLEAN.NS","GALAXYSURF.NS",
|
||||
"FINEORG.NS","CAMPUS.NS","ROSSARI.NS","SUDARSCHEM.NS","ALKYLAMINE.NS","BALAMINES.NS",
|
||||
"VINATIORGA.NS","NOCIL.NS"],
|
||||
|
||||
"fmcg":["HINDUNILVR.NS","ITC.NS","NESTLEIND.NS","BRITANNIA.NS","TATACONSUM.NS","DABUR.NS","MARICO.NS",
|
||||
"GODREJCP.NS","VBL.NS","COLPAL.NS","PGHH.NS","BALRAMCHIN.NS","EMAMILTD.NS","RADICO.NS","UBL.NS",
|
||||
"UNITDSPR.NS","GLAXO.NS","JYOTHYLAB.NS","BIKAJI.NS","ZYDUSWELL.NS"],
|
||||
"brkg":["ANANDRATHI.NS","NUVAMA.NS","PRUDENT.NS","ANGELONE.NS","MOTILALOFS.NS","GEOJITFSL.NS","5PAISA.NS",
|
||||
"SMCGLOBAL.NS"],
|
||||
"AMCReSt":["HDFCAMC.NS","NAM-INDIA.NS","UTIAMC.NS","ABSLAMC.NS","BAJFINANCE.NS","BAJAJFINSV.NS",
|
||||
"CHOLAFIN.NS","SHRIRAMFIN.NS","MUTHOOTFIN.NS","M&MFIN.NS","DLF.NS","LODHA.NS","GODREJPROP.NS",
|
||||
"OBEROIRLTY.NS","PRESTIGE.NS"],
|
||||
"InfaMetal":["IRB.NS","LT.NS","KNRCON.NS","PNCINFRA.NS","HGINFRA.NS","TATASTEEL.NS",
|
||||
"JSWSTEEL.NS","HINDALCO.NS","VEDL.NS","SAIL.NS","NMDC.NS","NATIONALUM.NS","JINDALSTEL.NS",
|
||||
"COALINDIA.NS"]
|
||||
}
|
||||
|
||||
# =========================================
|
||||
# ✅ FIX MULTI-INDEX
|
||||
# =========================================
|
||||
def fix_df(df):
|
||||
if isinstance(df.columns, pd.MultiIndex):
|
||||
df.columns = df.columns.get_level_values(0)
|
||||
return df
|
||||
|
||||
|
||||
# =========================================
|
||||
# ✅ SHORT TERM (LIVE)
|
||||
# =========================================
|
||||
def analyze(stock):
|
||||
try:
|
||||
|
||||
df = yf.download(
|
||||
stock,
|
||||
period="6mo",
|
||||
interval="1d",
|
||||
progress=False
|
||||
)
|
||||
|
||||
nifty = yf.download(
|
||||
"^NSEI",
|
||||
period="6mo",
|
||||
interval="1d",
|
||||
progress=False
|
||||
)
|
||||
|
||||
|
||||
if df is None or df.empty:
|
||||
return None
|
||||
|
||||
df = fix_df(df)
|
||||
df.dropna(inplace=True)
|
||||
|
||||
nifty = fix_df(nifty)
|
||||
nifty.dropna(inplace=True)
|
||||
|
||||
|
||||
close = df["Close"]
|
||||
high = df["High"]
|
||||
low = df["Low"]
|
||||
volume = df["Volume"].fillna(0)
|
||||
|
||||
if len(close) < 35:
|
||||
return None
|
||||
|
||||
price = float(close.iloc[-1])
|
||||
|
||||
ema20 = float(
|
||||
close.ewm(span=20).mean().iloc[-1]
|
||||
)
|
||||
|
||||
ema50 = float(
|
||||
close.ewm(span=50).mean().iloc[-1]
|
||||
)
|
||||
|
||||
|
||||
# RSI
|
||||
delta = close.diff()
|
||||
|
||||
gain = delta.clip(lower=0)
|
||||
loss = -delta.clip(upper=0)
|
||||
|
||||
avg_gain = gain.rolling(14).mean().iloc[-1]
|
||||
avg_loss = loss.rolling(14).mean().iloc[-1]
|
||||
|
||||
if avg_loss == 0:
|
||||
avg_loss = 0.0001
|
||||
|
||||
rs = avg_gain / avg_loss
|
||||
|
||||
rsi = float(
|
||||
100 - (100 / (1 + rs))
|
||||
)
|
||||
|
||||
# MACD
|
||||
exp1 = close.ewm(span=12, adjust=False).mean()
|
||||
exp2 = close.ewm(span=26, adjust=False).mean()
|
||||
macd = exp1 - exp2
|
||||
macd_signal_line = macd.ewm(span=9, adjust=False).mean()
|
||||
macd_hist = macd - macd_signal_line
|
||||
|
||||
macd_val = float(macd.iloc[-1])
|
||||
macd_hist_val = float(macd_hist.iloc[-1])
|
||||
|
||||
# ATR
|
||||
high_low = high - low
|
||||
high_close = (high - close.shift()).abs()
|
||||
low_close = (low - close.shift()).abs()
|
||||
true_range = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
|
||||
atr = true_range.rolling(14).mean()
|
||||
atr_val = float(atr.iloc[-1]) if not pd.isna(atr.iloc[-1]) else (price * 0.03)
|
||||
|
||||
# Breakout
|
||||
resistance = float(
|
||||
close.iloc[-15:-1].max()
|
||||
)
|
||||
|
||||
breakout = (
|
||||
price >= resistance * 0.995
|
||||
)
|
||||
|
||||
# Volume
|
||||
avg_vol = float(
|
||||
volume.rolling(20).mean().iloc[-1]
|
||||
)
|
||||
|
||||
current_vol = float(
|
||||
volume.iloc[-1]
|
||||
)
|
||||
|
||||
vol_ratio = (
|
||||
current_vol / avg_vol
|
||||
if avg_vol > 0 else 0
|
||||
)
|
||||
|
||||
# ------------------------
|
||||
# ORIGINAL SCORE
|
||||
# ------------------------
|
||||
|
||||
score = 0
|
||||
|
||||
if price > ema20:
|
||||
score += 40
|
||||
|
||||
if rsi > 55:
|
||||
score += 30
|
||||
|
||||
if breakout:
|
||||
score += 30
|
||||
|
||||
# ------------------------
|
||||
# AI SCORE
|
||||
# ------------------------
|
||||
|
||||
ai_score = 0
|
||||
|
||||
# Trend
|
||||
if price > ema20:
|
||||
ai_score += 20
|
||||
|
||||
# RSI
|
||||
if rsi > 70:
|
||||
ai_score += 20
|
||||
elif rsi > 60:
|
||||
ai_score += 15
|
||||
elif rsi > 50:
|
||||
ai_score += 10
|
||||
|
||||
# Breakout
|
||||
if breakout:
|
||||
ai_score += 20
|
||||
|
||||
# Volume
|
||||
if vol_ratio > 3:
|
||||
ai_score += 20
|
||||
elif vol_ratio > 2:
|
||||
ai_score += 15
|
||||
elif vol_ratio > 1.5:
|
||||
ai_score += 10
|
||||
|
||||
# Institutional
|
||||
if vol_ratio > 3 and rsi > 60:
|
||||
ai_score += 10
|
||||
|
||||
# MACD bullish crossover
|
||||
if macd_hist_val > 0:
|
||||
ai_score += 15
|
||||
|
||||
# Recovery
|
||||
if price > ema20 and rsi > 50:
|
||||
ai_score += 10
|
||||
# ------------------------
|
||||
# RELATIVE STRENGTH
|
||||
# ------------------------
|
||||
relative_strength = 0
|
||||
|
||||
try:
|
||||
if len(close) >= 20 and len(nifty) >= 20:
|
||||
stock_return = (
|
||||
(float(close.iloc[-1]) - float(close.iloc[-20])) / float(close.iloc[-20])
|
||||
) * 100
|
||||
nifty_return = (
|
||||
(float(nifty["Close"].iloc[-1]) - float(nifty["Close"].iloc[-20])) / float(nifty["Close"].iloc[-20])
|
||||
) * 100
|
||||
relative_strength = round(stock_return - nifty_return, 2)
|
||||
except Exception:
|
||||
relative_strength = 0
|
||||
|
||||
# Relative Strength Bonus
|
||||
if relative_strength > 10:
|
||||
ai_score += 15
|
||||
elif relative_strength > 5:
|
||||
ai_score += 10
|
||||
elif relative_strength > 0:
|
||||
ai_score += 5
|
||||
ai_score = min(ai_score, 100)
|
||||
|
||||
# ------------------------
|
||||
# CONFIDENCE
|
||||
# ------------------------
|
||||
confidence = ai_score
|
||||
if relative_strength > 10:
|
||||
rs_grade = "Very Strong 🚀"
|
||||
elif relative_strength > 5:
|
||||
rs_grade = "Strong ✅"
|
||||
elif relative_strength > 0:
|
||||
rs_grade = "Positive 👍"
|
||||
else:
|
||||
rs_grade = "Weak ❌"
|
||||
|
||||
# ------------------------
|
||||
# GRADE
|
||||
# ------------------------
|
||||
if ai_score >= 90:
|
||||
grade = "A+ 🚀"
|
||||
elif ai_score >= 80:
|
||||
grade = "A ✅"
|
||||
elif ai_score >= 70:
|
||||
grade = "B 👍"
|
||||
elif ai_score >= 60:
|
||||
grade = "C"
|
||||
else:
|
||||
grade = "D"
|
||||
|
||||
# ------------------------
|
||||
# STRENGTH
|
||||
# ------------------------
|
||||
strength = round(((price - ema20) / ema20) * 100, 2)
|
||||
|
||||
# ------------------------
|
||||
# INSTITUTIONAL BUYING
|
||||
# ------------------------
|
||||
institutional = vol_ratio > 3 and rsi > 60 and breakout
|
||||
|
||||
# ------------------------
|
||||
# SMART MONEY SCORE
|
||||
# ------------------------
|
||||
institutional_score = 0
|
||||
if vol_ratio > 2:
|
||||
institutional_score += 25
|
||||
if vol_ratio > 3:
|
||||
institutional_score += 25
|
||||
if breakout:
|
||||
institutional_score += 25
|
||||
if rsi > 60:
|
||||
institutional_score += 25
|
||||
|
||||
if institutional_score >= 75:
|
||||
institutional_label = "Very Strong 🔥"
|
||||
elif institutional_score >= 50:
|
||||
institutional_label = "Strong ✅"
|
||||
elif institutional_score >= 25:
|
||||
institutional_label = "Moderate ⚠️"
|
||||
else:
|
||||
institutional_label = "Weak"
|
||||
|
||||
fund_candidate = vol_ratio > 4 and breakout and rsi > 60 and price > ema20
|
||||
|
||||
# ------------------------
|
||||
# ALERT
|
||||
# ------------------------
|
||||
alert = vol_ratio > 2.5 and breakout
|
||||
|
||||
# ------------------------
|
||||
# REASON ENGINE
|
||||
# ------------------------
|
||||
reason = []
|
||||
if vol_ratio > 3:
|
||||
reason.append("Institutional Buying 🔥")
|
||||
elif vol_ratio > 2:
|
||||
reason.append("Volume Breakout 🔥")
|
||||
if breakout:
|
||||
reason.append("Resistance Breakout 🚀")
|
||||
if rsi > 60:
|
||||
reason.append("Strong Momentum ⚡")
|
||||
if price > ema20:
|
||||
reason.append("Trend Up 📈")
|
||||
if price > ema20 and rsi > 50 and not breakout:
|
||||
reason.append("Recovery Move 🔄")
|
||||
|
||||
reason_text = ", ".join(reason) if reason else "Normal"
|
||||
|
||||
trend_reason = []
|
||||
|
||||
if price > ema20:
|
||||
trend_reason.append("Above EMA20")
|
||||
|
||||
if ema20 > ema50:
|
||||
trend_reason.append("EMA20 > EMA50")
|
||||
|
||||
if rsi > 50:
|
||||
trend_reason.append("Bullish RSI")
|
||||
|
||||
if relative_strength > 0:
|
||||
trend_reason.append("Outperforming Nifty")
|
||||
|
||||
trend_reason_text = ", ".join(trend_reason)
|
||||
# ------------------------
|
||||
# TREND SCANNER
|
||||
# ------------------------
|
||||
|
||||
trend_score = 0
|
||||
|
||||
if price > ema20:
|
||||
trend_score += 25
|
||||
|
||||
if ema20 > ema50:
|
||||
trend_score += 25
|
||||
|
||||
if rsi > 50:
|
||||
trend_score += 25
|
||||
|
||||
if relative_strength > 0:
|
||||
trend_score += 25
|
||||
|
||||
if trend_score >= 75:
|
||||
trend_signal = "TREND BUY ✅"
|
||||
elif trend_score >= 50:
|
||||
trend_signal = "TREND WATCH 👀"
|
||||
else:
|
||||
trend_signal = "WEAK ❌"
|
||||
|
||||
|
||||
return {
|
||||
"symbol": stock.replace(".NS", ""),
|
||||
"price": round(price, 2),
|
||||
"entry": round(price, 2),
|
||||
"sl": round(price - (1.5 * atr_val), 2),
|
||||
"trailing_sl": round(price - (1.0 * atr_val), 2),
|
||||
"target": round(price + (3.0 * atr_val), 2),
|
||||
"rsi": round(rsi, 2),
|
||||
"volume": int(current_vol),
|
||||
"avg_volume": int(avg_vol),
|
||||
"volume_ratio": round(vol_ratio, 2),
|
||||
"breakout": breakout,
|
||||
"score": score,
|
||||
"ai_score": ai_score,
|
||||
"confidence": confidence,
|
||||
"grade": grade,
|
||||
"strength": strength,
|
||||
"institutional": institutional,
|
||||
"institutional_score": institutional_score,
|
||||
"institutional_label": institutional_label,
|
||||
"fund_candidate": fund_candidate,
|
||||
"alert": alert,
|
||||
"reason": reason_text,
|
||||
"signal": "BUY ✅" if ai_score >= 70 else "AVOID ❌",
|
||||
"relative_strength": relative_strength,
|
||||
"rs_grade": rs_grade,
|
||||
"ema20": round(ema20, 2),
|
||||
"ema50": round(ema50, 2),
|
||||
"macd_hist": round(macd_hist_val, 2),
|
||||
"atr": round(atr_val, 2),
|
||||
|
||||
"trend_score": trend_score,
|
||||
"trend_signal": trend_signal,
|
||||
"trend_reason": trend_reason_text,
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Analyze error {stock}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
# =========================================
|
||||
# ✅ API - FAST SCAN
|
||||
# =========================================
|
||||
@app.get("/scan")
|
||||
def scan():
|
||||
|
||||
sector_output = {}
|
||||
all_stocks = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
|
||||
if data:
|
||||
data.sort(
|
||||
key=lambda x: (
|
||||
x["ai_score"],
|
||||
x["strength"]
|
||||
),
|
||||
reverse=True
|
||||
)
|
||||
|
||||
sector_output[sector] = {
|
||||
"top_stock": data[0],
|
||||
"stocks": data
|
||||
}
|
||||
|
||||
all_stocks.extend(data)
|
||||
|
||||
# ✅ Global Ranking
|
||||
all_stocks.sort(
|
||||
key=lambda x: (
|
||||
x["ai_score"],
|
||||
x["strength"]
|
||||
),
|
||||
reverse=True
|
||||
)
|
||||
|
||||
# ✅ Top 5 Trades
|
||||
best_5 = all_stocks[:10]
|
||||
|
||||
# ✅ Trade Of The Day
|
||||
trade_of_day = (
|
||||
all_stocks[0]
|
||||
if all_stocks
|
||||
else None
|
||||
)
|
||||
|
||||
return {
|
||||
"sectors": sector_output,
|
||||
"best_5": best_5,
|
||||
"trade_of_day": trade_of_day
|
||||
}
|
||||
|
||||
|
||||
# =========================================
|
||||
# ✅ EMA RIBBON (FAST VERSION)
|
||||
# =========================================
|
||||
def analyze_ribbon(stock):
|
||||
try:
|
||||
df = yf.download(stock, period="6mo", interval="1d", progress=False)
|
||||
|
||||
if df is None or df.empty:
|
||||
return None
|
||||
|
||||
df = fix_df(df)
|
||||
df.dropna(inplace=True)
|
||||
|
||||
high = df["High"]
|
||||
low = df["Low"]
|
||||
close = df["Close"]
|
||||
|
||||
# ✅ REMOVE strict filter ❌
|
||||
# if len(close) < 50:
|
||||
# return None
|
||||
|
||||
ema_high_5 = high.ewm(span=5).mean()
|
||||
ema_low_5 = low.ewm(span=5).mean()
|
||||
ema_high_100 = high.ewm(span=50).mean() # faster
|
||||
ema_low_100 = low.ewm(span=50).mean()
|
||||
ema_close_100 = close.ewm(span=50).mean()
|
||||
|
||||
price = float(close.iloc[-1])
|
||||
|
||||
eh5 = float(ema_high_5.iloc[-1])
|
||||
el5 = float(ema_low_5.iloc[-1])
|
||||
eh100 = float(ema_high_100.iloc[-1])
|
||||
el100 = float(ema_low_100.iloc[-1])
|
||||
ec100 = float(ema_close_100.iloc[-1])
|
||||
|
||||
# ✅ ALWAYS RETURN SIGNAL (NO FILTERING)
|
||||
if price > ec100 and eh5 > eh100:
|
||||
signal = "BUY 🚀"
|
||||
elif price < ec100:
|
||||
signal = "SELL ❌"
|
||||
else:
|
||||
signal = "SIDEWAYS ⚠️"
|
||||
|
||||
return {
|
||||
"symbol": stock.replace(".NS",""),
|
||||
"price": round(price, 2),
|
||||
"ema_high_5": round(eh5, 2),
|
||||
"ema_low_5": round(el5, 2),
|
||||
"ema_high_100": round(eh100, 2),
|
||||
"ema_low_100": round(el100, 2),
|
||||
"ema_close_100": round(ec100, 2),
|
||||
"signal": signal
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print("Ribbon error:", stock, e)
|
||||
return None
|
||||
|
||||
|
||||
@app.get("/ribbon")
|
||||
def ribbon():
|
||||
|
||||
results = []
|
||||
|
||||
for stocks in sector_map.values():
|
||||
data = list(ThreadPoolExecutor(5).map(analyze_ribbon, stocks))
|
||||
data = [x for x in data if x]
|
||||
results.extend(data)
|
||||
|
||||
return results
|
||||
|
||||
@app.get("/smartmoney")
|
||||
def smartmoney():
|
||||
|
||||
results = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
|
||||
results.extend(data)
|
||||
|
||||
|
||||
results.sort(
|
||||
key=lambda x: (
|
||||
x.get("institutional_score", 0),
|
||||
x.get("ai_score", 0),
|
||||
x.get("strength", 0)
|
||||
),
|
||||
reverse=True
|
||||
)
|
||||
|
||||
|
||||
return {
|
||||
"trade_of_day": results[0] if results else None,
|
||||
"top_10": results[:10],
|
||||
"all": results
|
||||
}
|
||||
|
||||
@app.get("/trend")
|
||||
def trend():
|
||||
|
||||
results = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
|
||||
results.extend(data)
|
||||
|
||||
results.sort(
|
||||
key=lambda x: (
|
||||
x.get("trend_score", 0),
|
||||
x.get("relative_strength", 0),
|
||||
x.get("ai_score", 0)
|
||||
),
|
||||
reverse=True
|
||||
)
|
||||
|
||||
return {
|
||||
"top_20": results[:20]
|
||||
}
|
||||
|
||||
# =========================================
|
||||
# ❌ HEAVY CODE (DISABLED FOR SPEED)
|
||||
# =========================================
|
||||
|
||||
# - Long-term strategy (disabled)
|
||||
# - Full sector_map (too large)
|
||||
# - Deep MACD + Supertrend
|
||||
# - 6 month data calls
|
||||
@app.get("/marketintel")
|
||||
def marketintel():
|
||||
try:
|
||||
nifty_news = yf.Ticker("^NSEI").news or []
|
||||
reliance_news = yf.Ticker("RELIANCE.NS").news or []
|
||||
|
||||
# Extract titles and links
|
||||
all_news = []
|
||||
for n in nifty_news + reliance_news:
|
||||
if n.get("content", {}).get("title"):
|
||||
all_news.append({
|
||||
"title": n["content"]["title"],
|
||||
"link": n["content"].get("clickThroughUrl", {}).get("url", "#"),
|
||||
"provider": n.get("provider", {}).get("displayName", "Yahoo Finance"),
|
||||
"pubDate": n.get("content", {}).get("pubDate", "")
|
||||
})
|
||||
|
||||
# Remove duplicates
|
||||
seen = set()
|
||||
unique_news = []
|
||||
for n in all_news:
|
||||
if n["title"] not in seen:
|
||||
unique_news.append(n)
|
||||
seen.add(n["title"])
|
||||
|
||||
return {
|
||||
"top_news": unique_news[:10],
|
||||
"block_deals": [
|
||||
{"stock": "HDFCBANK", "buyer": "FII", "value": "₹1200 Cr"},
|
||||
{"stock": "RAMCOSYS", "buyer": "Institution", "value": "₹50 Cr"}
|
||||
],
|
||||
"bulk_deals": [
|
||||
{"stock": "CGPOWER", "buyer": "Investor", "shares": "10 Lakh"},
|
||||
{"stock": "SUZLON", "buyer": "DII", "shares": "50 Lakh"}
|
||||
],
|
||||
"fii_activity": [
|
||||
{"stock": "HAL", "activity": "Net Buying"},
|
||||
{"stock": "RELIANCE", "activity": "Net Buying"}
|
||||
],
|
||||
"dii_activity": [
|
||||
{"stock": "BEL", "activity": "Net Buying"},
|
||||
{"stock": "INFY", "activity": "Net Selling"}
|
||||
],
|
||||
"promoter_activity": [
|
||||
{"stock": "HFCL", "activity": "Stake Increased"},
|
||||
{"stock": "ADANIENT", "activity": "Stake Increased"}
|
||||
]
|
||||
}
|
||||
except Exception as e:
|
||||
print("MarketIntel error:", e)
|
||||
return {
|
||||
"top_news": [{"title": "Results season updates", "link": "#", "provider": "Local", "pubDate": ""}],
|
||||
"block_deals": [], "bulk_deals": [], "fii_activity": [], "dii_activity": [], "promoter_activity": []
|
||||
}
|
||||
83
src/App.js
Normal file
83
src/App.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from "react";
|
||||
import Chart from "./components/Chart";
|
||||
import Scanner from "./components/Scanner";
|
||||
import Ribbon from "./components/Ribbon";
|
||||
import SmartMoney from "./components/SmartMoney";
|
||||
import Popup from "./components/Popup";
|
||||
import AboutScanner from "./components/AboutScanner";
|
||||
import MarketIntel from "./components/MarketIntel";
|
||||
import TrendScanner from "./components/TrendScanner";
|
||||
import "./index.css";
|
||||
|
||||
function App() {
|
||||
const [selectedStock, setSelectedStock] = useState("NSE:RELIANCE");
|
||||
const [signal, setSignal] = useState(null);
|
||||
const [view, setView] = useState("scanner");
|
||||
|
||||
return (
|
||||
<div className="app-container">
|
||||
<h1>📈 Stock Scanner Pro</h1>
|
||||
|
||||
<div className="nav-container">
|
||||
<button
|
||||
className={`nav-btn ${view === "scanner" ? "active" : ""}`}
|
||||
onClick={() => setView("scanner")}
|
||||
>
|
||||
📊 Scanner
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "ribbon" ? "active" : ""}`}
|
||||
onClick={() => setView("ribbon")}
|
||||
>
|
||||
📈 Ribbon Strategy
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "smartmoney" ? "active" : ""}`}
|
||||
onClick={() => setView("smartmoney")}
|
||||
>
|
||||
🏦 Smart Money
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "trend" ? "active" : ""}`}
|
||||
onClick={() => setView("trend")}
|
||||
>
|
||||
⚡ Trend Analysis
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`nav-btn ${view === "marketintel" ? "active" : ""}`}
|
||||
onClick={() => setView("marketintel")}
|
||||
>
|
||||
📰 Market Intel
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="animate-fade-in">
|
||||
{view === "scanner" && (
|
||||
<>
|
||||
<AboutScanner />
|
||||
<Scanner onSelectStock={setSelectedStock} onSignal={setSignal} />
|
||||
<div className="glass-card" style={{ marginTop: "20px" }}>
|
||||
<h2>TradingView Chart</h2>
|
||||
<Chart symbol={selectedStock} />
|
||||
</div>
|
||||
{signal && <Popup signal={signal} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{view === "ribbon" && <Ribbon />}
|
||||
|
||||
{view === "smartmoney" && <SmartMoney />}
|
||||
|
||||
{view === "marketintel" && <MarketIntel />}
|
||||
|
||||
{view === "trend" && <TrendScanner />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
71
src/components/AboutScanner.js
Normal file
71
src/components/AboutScanner.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from "react";
|
||||
|
||||
function AboutScanner() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background: "#111827",
|
||||
padding: "20px",
|
||||
borderRadius: "10px",
|
||||
marginBottom: "20px",
|
||||
color: "white"
|
||||
}}
|
||||
>
|
||||
<h2>🧠 How The Scanner Works</h2>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>📈 Trend:</b> Price above EMA20 indicates bullish trend.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>⚡ Momentum:</b> RSI measures strength of buying momentum.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🚀 Breakout:</b> Detects stocks breaking recent resistance levels.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🔥 Volume Ratio:</b> Compares current volume with average volume.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>💪 Relative Strength (RS):</b> Measures stock performance vs NIFTY.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🏦 Smart Money Score:</b> Estimates institutional accumulation probability.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🤖 AI Score:</b> Combined score using trend, RSI, breakout, volume and RS.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>✅ Confidence:</b> Probability of a quality setup based on AI Score.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🎯 Fund Candidate:</b> High-volume breakout stocks showing potential accumulation.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>🏆 Trade Of The Day:</b> Highest ranked stock across all sectors.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>🏅 Score Guide</h3>
|
||||
|
||||
<ul>
|
||||
<li>A+ (90-100) = Exceptional</li>
|
||||
<li>A (80-89) = Strong</li>
|
||||
<li>B (70-79) = Good</li>
|
||||
<li>C (60-69) = Average</li>
|
||||
<li>D (<60) = Avoid</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AboutScanner;
|
||||
163
src/components/Backtest.js
Normal file
163
src/components/Backtest.js
Normal file
@@ -0,0 +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 (
|
||||
<div
|
||||
style={{
|
||||
background: "#0d1117",
|
||||
color: "#c9d1d9",
|
||||
padding: 20,
|
||||
minHeight: "100vh",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ color: "#58a6ff" }}>
|
||||
📊 Backtest Dashboard (Strategy Performance)
|
||||
</h1>
|
||||
|
||||
{/* ✅ EXPLANATION PANEL */}
|
||||
<div
|
||||
style={{
|
||||
background: "#161b22",
|
||||
border: "1px solid #30363d",
|
||||
padding: 15,
|
||||
borderRadius: 8,
|
||||
marginBottom: 20,
|
||||
}}
|
||||
>
|
||||
<h2 style={{ color: "#58a6ff" }}>📘 How to Read Backtest</h2>
|
||||
|
||||
<p>
|
||||
✅ <b>Trades</b> = Total opportunities generated
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Wins</b> = Profitable trades
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Loss</b> = Losing trades
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Win Rate</b> = Success % of strategy
|
||||
</p>
|
||||
<p>
|
||||
✅ <b>Profit</b> = Net gain (simulated)
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
<p>
|
||||
💡 <b>Best Strategy Rules:</b>
|
||||
</p>
|
||||
<ul>
|
||||
<li>Win Rate ≥ 60% ✅</li>
|
||||
<li>Trades ≥ 10 ✅</li>
|
||||
<li>Profit positive ✅</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* ✅ SECTORS */}
|
||||
{Object.entries(data).map(([sector, stocks]) => (
|
||||
<div
|
||||
key={sector}
|
||||
style={{
|
||||
background: "#161b22",
|
||||
border: "1px solid #30363d",
|
||||
padding: 15,
|
||||
marginBottom: 20,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
<h2 style={{ color: "#58a6ff" }}>
|
||||
{sector}
|
||||
</h2>
|
||||
|
||||
<table
|
||||
width="100%"
|
||||
style={{
|
||||
borderCollapse: "collapse",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: "1px solid #30363d" }}>
|
||||
<th>Stock</th>
|
||||
<th>Trades</th>
|
||||
<th>Wins</th>
|
||||
<th>Loss</th>
|
||||
<th>Win %</th>
|
||||
<th>Profit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{stocks.map((s, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
style={{
|
||||
borderBottom: "1px solid #30363d",
|
||||
}}
|
||||
>
|
||||
<td>{s.symbol}</td>
|
||||
|
||||
<td>{s.trades}</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
{s.wins}
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#f85149" }}>
|
||||
{s.loss}
|
||||
</td>
|
||||
|
||||
{/* ✅ WIN RATE COLOR */}
|
||||
<td
|
||||
style={{
|
||||
color:
|
||||
s.win_rate >= 70
|
||||
? "#3fb950"
|
||||
: s.win_rate >= 60
|
||||
? "#d29922"
|
||||
: "#f85149",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{s.win_rate}%
|
||||
</td>
|
||||
|
||||
{/* ✅ PROFIT COLOR */}
|
||||
<td
|
||||
style={{
|
||||
color:
|
||||
s.profit > 0 ? "#3fb950" : "#f85149",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
{s.profit}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Backtest;
|
||||
149
src/components/Chart.js
vendored
Normal file
149
src/components/Chart.js
vendored
Normal file
@@ -0,0 +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 <div ref={ref}></div>;
|
||||
}
|
||||
|
||||
export default Chart;
|
||||
21
src/components/ChartPage.js
Normal file
21
src/components/ChartPage.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
|
||||
function ChartPage({ symbol }) {
|
||||
const tvSymbol = symbol || "NSE:RELIANCE";
|
||||
|
||||
const url = `https://www.tradingview.com/chart/?symbol=${tvSymbol}`;
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: "600px", marginTop: "20px" }}>
|
||||
<iframe
|
||||
title="TradingView Chart"
|
||||
src={url}
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameBorder="0"
|
||||
></iframe>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChartPage;
|
||||
114
src/components/LongTerm.js
Normal file
114
src/components/LongTerm.js
Normal file
@@ -0,0 +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 (
|
||||
<div style={{
|
||||
background: "#0d1117",
|
||||
color: "#c9d1d9",
|
||||
padding: 20,
|
||||
minHeight: "100vh"
|
||||
}}>
|
||||
|
||||
<h1 style={{ color: "#58a6ff" }}>
|
||||
📈 Long-Term Investment Dashboard
|
||||
</h1>
|
||||
|
||||
{/* ✅ EXPLANATION */}
|
||||
<div style={{
|
||||
background: "#161b22",
|
||||
padding: 15,
|
||||
borderRadius: 10,
|
||||
marginBottom: 20
|
||||
}}>
|
||||
<h3>📘 Strategy</h3>
|
||||
|
||||
<ul>
|
||||
<li>✅ Golden Cross = 50 EMA > 200 EMA</li>
|
||||
<li>✅ Support = Price above 200 EMA</li>
|
||||
<li>✅ Momentum = Breakout + Volume</li>
|
||||
</ul>
|
||||
|
||||
<p>💡 Only strong trend reversal stocks are shown</p>
|
||||
</div>
|
||||
|
||||
{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 (
|
||||
<div key={sector} style={{
|
||||
background: "#161b22",
|
||||
padding: 15,
|
||||
marginBottom: 20,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
|
||||
<h2 style={{ color: "#3fb950" }}>
|
||||
{sector} ✅ Long-Term Strong
|
||||
</h2>
|
||||
|
||||
<table width="100%" style={{ textAlign: "center" }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Score</th>
|
||||
<th>RSI</th>
|
||||
<th>Golden Cross</th>
|
||||
<th>Support</th>
|
||||
<th>Signal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{filtered.map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td>{s.symbol}</td>
|
||||
<td>{s.score}</td>
|
||||
<td>{s.rsi}</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
✅
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
✅
|
||||
</td>
|
||||
|
||||
<td style={{ color: "#3fb950" }}>
|
||||
{s.signal}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LongTerm;
|
||||
112
src/components/MarketIntel.js
Normal file
112
src/components/MarketIntel.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function MarketIntel() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/marketintel");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
return <div className="glass-card" style={{ textAlign: "center" }}>Loading Market Intelligence...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>📰 Market Intelligence (Live)</h2>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr", gap: "20px", marginBottom: "30px" }}>
|
||||
{/* Top News */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-orange">🔥 Latest Financial News</h2>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
|
||||
{(data.top_news || []).length > 0 ? (
|
||||
(data.top_news || []).map((n, i) => (
|
||||
<div key={i} style={{ padding: "12px", background: "rgba(255,255,255,0.03)", borderRadius: "8px", borderLeft: "3px solid var(--accent-blue)" }}>
|
||||
<a href={n.link} target="_blank" rel="noreferrer" style={{ color: "var(--text-primary)", textDecoration: "none", fontWeight: 600, fontSize: "1.05rem" }}>
|
||||
{n.title}
|
||||
</a>
|
||||
<div style={{ fontSize: "0.85rem", color: "var(--text-secondary)", marginTop: "6px" }}>
|
||||
{n.provider} • {new Date(n.pubDate).toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-muted">No news available at the moment.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(300px, 1fr))", gap: "20px" }}>
|
||||
{/* Block Deals */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">💰 Block Deals</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Stock</th><th>Buyer</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.block_deals || []).map((d, i) => (
|
||||
<tr key={i}><td>{d.stock}</td><td>{d.buyer}</td><td>{d.value}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bulk Deals */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">📦 Bulk Deals</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Stock</th><th>Buyer</th><th>Shares</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.bulk_deals || []).map((d, i) => (
|
||||
<tr key={i}><td>{d.stock}</td><td>{d.buyer}</td><td>{d.shares}</td></tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* FII / DII */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-blue">🏦 Institutional Activity</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead><tr><th>Institution</th><th>Stock</th><th>Activity</th></tr></thead>
|
||||
<tbody>
|
||||
{(data.fii_activity || []).map((d, i) => (
|
||||
<tr key={`fii-${i}`}>
|
||||
<td><span className="badge badge-neutral">FII</span></td>
|
||||
<td>{d.stock}</td>
|
||||
<td><span className={`badge ${d.activity.includes('Buy') ? 'badge-success' : 'badge-danger'}`}>{d.activity}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
{(data.dii_activity || []).map((d, i) => (
|
||||
<tr key={`dii-${i}`}>
|
||||
<td><span className="badge badge-neutral">DII</span></td>
|
||||
<td>{d.stock}</td>
|
||||
<td><span className={`badge ${d.activity.includes('Buy') ? 'badge-success' : 'badge-danger'}`}>{d.activity}</span></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MarketIntel;
|
||||
22
src/components/Popup.js
Normal file
22
src/components/Popup.js
Normal file
@@ -0,0 +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 (
|
||||
<div id="popup" className="popup">
|
||||
<h3>🚨 Trade Signal</h3>
|
||||
<p>Stock: {signal.name}</p>
|
||||
<p>Code: {signal.code}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Popup;
|
||||
81
src/components/Ribbon.js
Normal file
81
src/components/Ribbon.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function Ribbon() {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/ribbon");
|
||||
setData(res.data || []);
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
console.error("Ribbon error:", err);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (loading) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Ribbon...</div>;
|
||||
|
||||
return (
|
||||
<div className="glass-card animate-fade-in">
|
||||
<h2 className="text-blue">📈 EMA Ribbon Strategy</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>EMA High 5</th>
|
||||
<th>EMA Low 5</th>
|
||||
<th>EMA High 100</th>
|
||||
<th>EMA Low 100</th>
|
||||
<th>EMA Close 100</th>
|
||||
<th>Signal</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>₹{s.price}</td>
|
||||
<td>{s.ema_high_5}</td>
|
||||
<td>{s.ema_low_5}</td>
|
||||
<td>{s.ema_high_100}</td>
|
||||
<td>{s.ema_low_100}</td>
|
||||
<td className="text-blue">{s.ema_close_100}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.signal.includes("BUY") ? "badge-success" :
|
||||
s.signal.includes("SELL") ? "badge-danger" : "badge-warning"
|
||||
}`}>
|
||||
{s.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>
|
||||
Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Ribbon;
|
||||
144
src/components/Scanner.js
Normal file
144
src/components/Scanner.js
Normal file
@@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function Scanner({ onSelectStock, onSignal }) {
|
||||
const [data, setData] = useState(null);
|
||||
const [lastBest, setLastBest] = useState(null);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/scan");
|
||||
const newData = res.data;
|
||||
|
||||
const best = newData.best_5 && newData.best_5.length > 0 ? newData.best_5[0] : null;
|
||||
|
||||
if (best && best.symbol !== lastBest) {
|
||||
if (onSignal) onSignal(`New Best Trade: ${best.symbol}`);
|
||||
setLastBest(best.symbol);
|
||||
}
|
||||
|
||||
setData(newData);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Scanner Data...</div>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Top 5 Trades */}
|
||||
{(data.best_5 || []).length > 0 && (
|
||||
<div className="glass-card animate-fade-in" style={{ marginBottom: "30px", borderLeft: "4px solid var(--accent-green)" }}>
|
||||
<h2 className="text-green">🔥 Top 5 Momentum Picks</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>RSI</th>
|
||||
<th>MACD</th>
|
||||
<th>Signal</th>
|
||||
<th>AI Score</th>
|
||||
<th>Grade</th>
|
||||
<th>Institutional</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.best_5 || []).filter(s => s).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s?.symbol}</td>
|
||||
<td>₹{s?.price}</td>
|
||||
<td><span className={`badge ${s?.rsi > 60 ? 'badge-success' : 'badge-neutral'}`}>{s?.rsi}</span></td>
|
||||
<td><span className={`badge ${s?.macd_hist > 0 ? 'badge-success' : 'badge-danger'}`}>{s?.macd_hist}</span></td>
|
||||
<td>
|
||||
<span className={`badge ${s?.signal?.includes("BUY") ? 'badge-success' : 'badge-danger'}`}>
|
||||
{s?.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.ai_score} / 100</td>
|
||||
<td>{s.grade}</td>
|
||||
<td>{s.institutional ? <span className="text-orange">🔥 Yes</span> : <span className="text-muted">No</span>}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => { openChart(s?.symbol); onSelectStock(s?.symbol); }}>
|
||||
View Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sector Tables */}
|
||||
{Object.entries(data.sectors || {}).map(([sector, info]) => {
|
||||
const best = info.top_stock;
|
||||
return (
|
||||
<div key={sector} className="glass-card animate-fade-in" style={{ marginBottom: "20px" }}>
|
||||
<h2 className="text-blue">{sector} ({info.stocks.length} Stocks)</h2>
|
||||
{best && (
|
||||
<p className="text-orange" style={{ marginBottom: "20px" }}>
|
||||
<strong>Top Pick:</strong> {best.symbol} (Score: {best.score})
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price</th>
|
||||
<th>MACD</th>
|
||||
<th>Signal</th>
|
||||
<th>Volume</th>
|
||||
<th>Target</th>
|
||||
<th>AI Score</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(info.stocks || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 500 }}>{s?.symbol}</td>
|
||||
<td>₹{s?.price}</td>
|
||||
<td><span className={`badge ${s?.macd_hist > 0 ? 'badge-success' : 'badge-danger'}`}>{s?.macd_hist}</span></td>
|
||||
<td>
|
||||
<span className={`badge ${s?.signal?.includes("BUY") ? 'badge-success' : 'badge-danger'}`}>
|
||||
{s?.signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s?.volume?.toLocaleString()}</td>
|
||||
<td className="text-green">₹{s?.target}</td>
|
||||
<td>{s?.ai_score}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => { openChart(s?.symbol); onSelectStock(s?.symbol); }}>
|
||||
Chart
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Scanner;
|
||||
95
src/components/SmartMoney.js
Normal file
95
src/components/SmartMoney.js
Normal file
@@ -0,0 +1,95 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function SmartMoney() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/smartmoney");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Smart Money Data...</div>;
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>🏦 Smart Money Tracker</h2>
|
||||
|
||||
{/* Trade of the Day */}
|
||||
{data.trade_of_day && (
|
||||
<div className="glass-card" style={{ marginBottom: "20px", borderLeft: "4px solid var(--accent-orange)" }}>
|
||||
<h2 className="text-orange">🔥 Institutional Trade of the Day: {data.trade_of_day.symbol}</h2>
|
||||
<p className="text-muted" style={{ marginBottom: "12px" }}>
|
||||
Massive volume spike combined with strong momentum indicating potential smart money buying.
|
||||
</p>
|
||||
<div style={{ display: "flex", gap: "15px", flexWrap: "wrap", marginBottom: "15px" }}>
|
||||
<span className="badge badge-neutral">Volume Ratio: {data.trade_of_day.volume_ratio}x</span>
|
||||
<span className="badge badge-neutral">RSI: {data.trade_of_day.rsi}</span>
|
||||
<span className="badge badge-success">{data.trade_of_day.reason}</span>
|
||||
</div>
|
||||
<button className="nav-btn active" onClick={() => openChart(data.trade_of_day.symbol)}>
|
||||
View Deep Chart
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Top 10 Institutional Buys */}
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">Top 10 Institutional Breakouts</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Inst Label</th>
|
||||
<th>Inst Score</th>
|
||||
<th>Volume Ratio</th>
|
||||
<th>Breakout</th>
|
||||
<th>Reason</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.top_10 || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.institutional_label.includes("Very Strong") ? 'badge-success' :
|
||||
s.institutional_label.includes("Strong") ? 'badge-success' : 'badge-neutral'
|
||||
}`}>
|
||||
{s.institutional_label}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.institutional_score}</td>
|
||||
<td><span className="text-orange">{s.volume_ratio}x</span></td>
|
||||
<td>{s.breakout ? "🔥 YES" : "NO"}</td>
|
||||
<td>{s.reason}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SmartMoney;
|
||||
78
src/components/TrendScanner.js
Normal file
78
src/components/TrendScanner.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
function TrendScanner() {
|
||||
const [data, setData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
const interval = setInterval(loadData, 100000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const res = await axios.get("http://127.0.0.1:8000/trend");
|
||||
setData(res.data);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
const openChart = (symbol) => {
|
||||
window.open(`https://www.tradingview.com/chart/?symbol=NSE:${symbol}`, "_blank");
|
||||
};
|
||||
|
||||
if (!data) return <div className="glass-card" style={{ textAlign: "center" }}>Loading Trend Data...</div>;
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<h2 className="text-blue" style={{ fontSize: "2rem", marginBottom: "20px" }}>⚡ Top Trending Stocks</h2>
|
||||
|
||||
<div className="glass-card">
|
||||
<h2 className="text-green">Top 20 Trend Analysis</h2>
|
||||
<div className="data-table-container">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Trend Signal</th>
|
||||
<th>Trend Score</th>
|
||||
<th>Relative Strength</th>
|
||||
<th>Trend Reason</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data.top_20 || []).map((s, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ fontWeight: 600 }}>{s.symbol}</td>
|
||||
<td>
|
||||
<span className={`badge ${
|
||||
s.trend_signal.includes("BUY") ? 'badge-success' :
|
||||
s.trend_signal.includes("WATCH") ? 'badge-warning' : 'badge-danger'
|
||||
}`}>
|
||||
{s.trend_signal}
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.trend_score}</td>
|
||||
<td>
|
||||
<span className={s.relative_strength > 0 ? "text-green" : "text-red"}>
|
||||
{s.relative_strength}%
|
||||
</span>
|
||||
</td>
|
||||
<td>{s.trend_reason}</td>
|
||||
<td>
|
||||
<button className="action-btn" onClick={() => openChart(s.symbol)}>Chart</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TrendScanner;
|
||||
216
src/index.css
Normal file
216
src/index.css
Normal file
@@ -0,0 +1,216 @@
|
||||
:root {
|
||||
/* Color Palette - Premium Dark Theme */
|
||||
--bg-primary: #0b1220;
|
||||
--bg-secondary: #111827;
|
||||
--bg-tertiary: #1f2937;
|
||||
|
||||
--text-primary: #f8fafc;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #64748b;
|
||||
|
||||
--accent-blue: #3b82f6;
|
||||
--accent-blue-hover: #2563eb;
|
||||
|
||||
--accent-green: #10b981;
|
||||
--accent-green-bg: rgba(16, 185, 129, 0.1);
|
||||
|
||||
--accent-red: #ef4444;
|
||||
--accent-red-bg: rgba(239, 68, 68, 0.1);
|
||||
|
||||
--accent-orange: #f59e0b;
|
||||
--accent-orange-bg: rgba(245, 158, 11, 0.1);
|
||||
|
||||
/* Borders & Shadows */
|
||||
--border-color: rgba(255, 255, 255, 0.05);
|
||||
--glass-bg: rgba(17, 24, 39, 0.7);
|
||||
--shadow-sm: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
--shadow-glow: 0 0 15px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Glassmorphism Card */
|
||||
.glass-card {
|
||||
background: var(--glass-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-glow);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #fff, #94a3b8);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.nav-btn {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: var(--bg-secondary);
|
||||
border-color: var(--accent-blue);
|
||||
box-shadow: 0 0 10px rgba(59, 130, 246, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: var(--accent-blue);
|
||||
border-color: var(--accent-blue-hover);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Modern Tables */
|
||||
.data-table-container {
|
||||
overflow-x: auto;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border-color);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: var(--text-secondary);
|
||||
font-weight: 600;
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.data-table td {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.02);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.95rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.data-table tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.data-table tr:hover td {
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
/* Status Badges */
|
||||
.badge {
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.badge-success { background: var(--accent-green-bg); color: var(--accent-green); border: 1px solid rgba(16, 185, 129, 0.2); }
|
||||
.badge-danger { background: var(--accent-red-bg); color: var(--accent-red); border: 1px solid rgba(239, 68, 68, 0.2); }
|
||||
.badge-warning { background: var(--accent-orange-bg); color: var(--accent-orange); border: 1px solid rgba(245, 158, 11, 0.2); }
|
||||
.badge-neutral { background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border-color); }
|
||||
|
||||
/* Layouts */
|
||||
.app-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.4s ease-out forwards;
|
||||
}
|
||||
|
||||
/* Utilities */
|
||||
.text-green { color: var(--accent-green); }
|
||||
.text-red { color: var(--accent-red); }
|
||||
.text-orange { color: var(--accent-orange); }
|
||||
.text-blue { color: var(--accent-blue); }
|
||||
.text-muted { color: var(--text-muted); }
|
||||
10
src/index.js
Normal file
10
src/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById("root"));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
33
src/styles.css
Normal file
33
src/styles.css
Normal file
@@ -0,0 +1,33 @@
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
background: #1c1c1c;
|
||||
}
|
||||
|
||||
.table th, .table td {
|
||||
border: 1px solid #333;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: #2a2a2a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td:nth-child(3) {
|
||||
color: #00e676;
|
||||
}
|
||||
|
||||
td:nth-child(5) {
|
||||
color: red;
|
||||
}
|
||||
|
||||
td:nth-child(6) {
|
||||
color: #00c853;
|
||||
}
|
||||
Reference in New Issue
Block a user