Spaces:
Sleeping
Sleeping
| """ | |
| Pro Trading Signals - Finnhub Live Data Version | |
| """ | |
| import streamlit as st | |
| import pandas as pd | |
| import requests | |
| from datetime import datetime | |
| import time | |
| FINNHUB_API_KEY = "demo" | |
| try: | |
| import os | |
| FINNHUB_API_KEY = os.environ.get("FINNHUB_API_KEY", "demo") | |
| except: | |
| pass | |
| st.set_page_config( | |
| page_title="Pro Trading Signals", | |
| page_icon="π", | |
| layout="centered", | |
| initial_sidebar_state="collapsed" | |
| ) | |
| st.markdown(""" | |
| <style> | |
| .main-title { font-size: 2.5rem; font-weight: bold; color: #FFD700; text-align: center; } | |
| .signal-box { background: #1a1a1a; border-radius: 15px; padding: 25px; margin: 10px 0; } | |
| .prob-big { font-size: 4rem; font-weight: bold; text-align: center; } | |
| .buy-signal { color: #00E676; } | |
| .sell-signal { color: #FF5252; } | |
| .wait-signal { color: #FFD740; } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.markdown('<h1 class="main-title">π PRO TRADING SIGNALS</h1>', unsafe_allow_html=True) | |
| st.markdown('<p style="text-align:center;color:#888;">Live Finnhub Data β’ Futures & Forex</p>', unsafe_allow_html=True) | |
| SYMBOL_MAP = { | |
| "π₯ Gold (XAU/USD)": {"symbol": "XAUUSD", "type": "forex"}, | |
| "π₯ Silver (XAG/USD)": {"symbol": "XAGUSD", "type": "forex"}, | |
| "π’οΈ Crude Oil (CL)": {"symbol": "CL", "type": "commodity"}, | |
| "β½ Natural Gas (NG)": {"symbol": "NG", "type": "commodity"}, | |
| "π½ Corn (ZC)": {"symbol": "ZC", "type": "commodity"}, | |
| "πΎ Wheat (ZW)": {"symbol": "ZW", "type": "commodity"}, | |
| "EUR/USD": {"symbol": "EURUSD", "type": "forex"}, | |
| "GBP/USD": {"symbol": "GBPUSD", "type": "forex"}, | |
| "USD/JPY": {"symbol": "USDJPY", "type": "forex"}, | |
| "USD/CHF": {"symbol": "USDCHF", "type": "forex"}, | |
| "AUD/USD": {"symbol": "AUDUSD", "type": "forex"}, | |
| "USD/CAD": {"symbol": "USDCAD", "type": "forex"}, | |
| "BTC/USD": {"symbol": "BTC-USD", "type": "crypto"}, | |
| "ETH/USD": {"symbol": "ETH-USD", "type": "crypto"}, | |
| "AAPL": {"symbol": "AAPL", "type": "stock"}, | |
| "TSLA": {"symbol": "TSLA", "type": "stock"}, | |
| "NVDA": {"symbol": "NVDA", "type": "stock"}, | |
| } | |
| TIMEFRAMES = { | |
| "1 Min": {"resolution": "1", "days": 1}, | |
| "5 Min": {"resolution": "5", "days": 5}, | |
| "15 Min": {"resolution": "15", "days": 5}, | |
| "30 Min": {"resolution": "30", "days": 10}, | |
| "1 Hour": {"resolution": "60", "days": 30}, | |
| "4 Hour": {"resolution": "240", "days": 60}, | |
| "Daily": {"resolution": "D", "days": 180}, | |
| } | |
| def get_finnhub_price(symbol: str) -> float: | |
| try: | |
| url = f"https://finnhub.io/api/v1/quote?symbol={symbol}&token={FINNHUB_API_KEY}" | |
| r = requests.get(url, timeout=5) | |
| data = r.json() | |
| if data.get('c'): | |
| return data['c'] | |
| except: | |
| pass | |
| return None | |
| def get_finnhub_candle(symbol: str, resolution: str, days: int) -> pd.DataFrame: | |
| try: | |
| to_time = int(datetime.now().timestamp()) | |
| from_time = to_time - (days * 24 * 60 * 60) | |
| url = f"https://finnhub.io/api/v1/stock/candle?symbol={symbol}&resolution={resolution}&from={from_time}&to={to_time}&token={FINNHUB_API_KEY}" | |
| r = requests.get(url, timeout=10) | |
| data = r.json() | |
| if data.get('s') == 'ok': | |
| df = pd.DataFrame({ | |
| 'Open': data['o'], | |
| 'High': data['h'], | |
| 'Low': data['l'], | |
| 'Close': data['c'], | |
| 'Volume': data['v'] | |
| }, index=pd.to_datetime(data['t'], unit='s')) | |
| return df | |
| except Exception as e: | |
| pass | |
| return pd.DataFrame() | |
| col1, col2, col3 = st.columns([2, 2, 1]) | |
| with col1: | |
| selected_pair = st.selectbox("π Asset", list(SYMBOL_MAP.keys()), label_visibility="collapsed") | |
| with col2: | |
| selected_tf = st.selectbox("β± Timeframe", list(TIMEFRAMES.keys()), label_visibility="collapsed") | |
| with col3: | |
| st.write("") | |
| analyze_btn = st.button("β‘ ANALYZE", use_container_width=True) | |
| if analyze_btn: | |
| with st.spinner("π‘ Fetching Finnhub live data..."): | |
| try: | |
| start_time = time.time() | |
| asset = SYMBOL_MAP[selected_pair] | |
| symbol = asset["symbol"] | |
| tf = TIMEFRAMES[selected_tf] | |
| price = get_finnhub_price(symbol) | |
| df = get_finnhub_candle(symbol, tf["resolution"], tf["days"]) | |
| if df.empty or len(df) < 20: | |
| st.error("β No data. Try different asset or check API key.") | |
| else: | |
| close = df['Close'] | |
| df['sma9'] = close.rolling(9).mean() | |
| df['sma21'] = close.rolling(21).mean() | |
| df['sma50'] = close.rolling(50).mean() | |
| df['ema8'] = close.ewm(span=8, adjust=False).mean() | |
| df['ema21'] = close.ewm(span=21, adjust=False).mean() | |
| delta = close.diff() | |
| gain = delta.clip(lower=0) | |
| loss = (-delta).clip(lower=0) | |
| avg_gain = gain.ewm(alpha=1/14, adjust=False).mean() | |
| avg_loss = loss.ewm(alpha=1/14, adjust=False).mean() | |
| rs = avg_gain / loss.replace(0, 1e-12) | |
| df['rsi'] = 100 - (100 / (1 + rs)) | |
| ema12 = close.ewm(span=12, adjust=False).mean() | |
| ema26 = close.ewm(span=26, adjust=False).mean() | |
| df['macd'] = ema12 - ema26 | |
| df['macd_signal'] = df['macd'].ewm(span=9, adjust=False).mean() | |
| df['macd_hist'] = df['macd'] - df['macd_signal'] | |
| high = df['High'] | |
| low = df['Low'] | |
| prev_close = close.shift(1) | |
| tr = pd.concat([high - low, (high - prev_close).abs(), (low - prev_close).abs()], axis=1).max(axis=1) | |
| df['atr'] = tr.ewm(alpha=1/14, adjust=False).mean() | |
| up_move = high.diff() | |
| down_move = -low.diff() | |
| plus_dm = ((up_move > down_move) & (up_move > 0)) * up_move | |
| minus_dm = ((down_move > up_move) & (down_move > 0)) * down_move | |
| tr_s = tr.ewm(alpha=1/14, adjust=False).mean() | |
| p_dm_s = plus_dm.ewm(alpha=1/14, adjust=False).mean() | |
| m_dm_s = minus_dm.ewm(alpha=1/14, adjust=False).mean() | |
| tr_safe = tr_s.replace(0, 1e-12) | |
| df['plus_di'] = 100 * (p_dm_s / tr_safe) | |
| df['minus_di'] = 100 * (m_dm_s / tr_safe) | |
| di_sum = (df['plus_di'] + df['minus_di']).replace(0, 1e-12) | |
| dx = 100 * (df['plus_di'] - df['minus_di']).abs() / di_sum | |
| df['adx'] = dx.ewm(alpha=1/14, adjust=False).mean() | |
| last = df.iloc[-1] | |
| price = float(last['Close']) if not price else price | |
| atr = float(last['atr']) | |
| score_buy = 0 | |
| score_sell = 0 | |
| reasons = [] | |
| if price > last['sma50']: | |
| score_buy += 1 | |
| reasons.append("β Price > SMA50") | |
| else: | |
| score_sell += 1 | |
| reasons.append("β Price < SMA50") | |
| if last['sma21'] > last['sma50']: | |
| score_buy += 1 | |
| reasons.append("β SMA21 > SMA50") | |
| else: | |
| score_sell += 1 | |
| reasons.append("β SMA21 < SMA50") | |
| if last['ema8'] > last['ema21']: | |
| score_buy += 1 | |
| reasons.append("β EMA8 > EMA21") | |
| else: | |
| score_sell += 1 | |
| reasons.append("β EMA8 < EMA21") | |
| if last['macd'] > last['macd_signal']: | |
| score_buy += 1 | |
| reasons.append("β MACD bullish") | |
| else: | |
| score_sell += 1 | |
| reasons.append("β MACD bearish") | |
| if last['macd_hist'] > 0: | |
| score_buy += 1 | |
| reasons.append("β MACD histogram +") | |
| else: | |
| score_sell += 1 | |
| reasons.append("β MACD histogram -") | |
| rsi = last['rsi'] | |
| if 50 < rsi < 70: | |
| score_buy += 1 | |
| reasons.append(f"β RSI healthy ({rsi:.0f})") | |
| elif 30 < rsi < 50: | |
| score_sell += 1 | |
| reasons.append(f"β RSI weak ({rsi:.0f})") | |
| elif rsi >= 70: | |
| score_sell += 1 | |
| reasons.append(f"β RSI overbought") | |
| elif rsi <= 30: | |
| score_buy += 1 | |
| reasons.append(f"β RSI oversold") | |
| adx = last['adx'] | |
| if adx > 20: | |
| if last['plus_di'] > last['minus_di']: | |
| score_buy += 2 | |
| reasons.append(f"β Strong uptrend (ADX:{adx:.0f})") | |
| else: | |
| score_sell += 2 | |
| reasons.append(f"β Strong downtrend (ADX:{adx:.0f})") | |
| else: | |
| reasons.append(f"β Ranging market") | |
| if score_buy >= 5: | |
| prob = min(95, 55 + (score_buy * 7)) | |
| action = "π’ BUY" | |
| color_class = "buy-signal" | |
| tp = round(price + (atr * 2), 2) | |
| sl = round(price - (atr * 1), 2) | |
| elif score_sell >= 5: | |
| prob = min(95, 55 + (score_sell * 7)) | |
| action = "π΄ SELL" | |
| color_class = "sell-signal" | |
| tp = round(price - (atr * 2), 2) | |
| sl = round(price + (atr * 1), 2) | |
| else: | |
| prob = max(40, 50 + (score_buy - score_sell) * 5) | |
| action = "π‘ WAIT" | |
| color_class = "wait-signal" | |
| tp = sl = round(price, 2) | |
| fetch_time = round(time.time() - start_time, 2) | |
| st.markdown(f""" | |
| <div class="signal-box"> | |
| <h2 class="{color_class}" style="text-align:center;">{action}</h2> | |
| <h1 class="prob-big {color_class}">{prob}%</h1> | |
| <p style="text-align:center;color:#888;">WIN PROBABILITY β’ {fetch_time}s</p> | |
| <hr style="border-color:#333;"> | |
| <div style="display:flex;justify-content:space-around;text-align:center;"> | |
| <div> | |
| <p style="color:#888;">CURRENT PRICE</p> | |
| <h3>${price:.2f}</h3> | |
| </div> | |
| <div> | |
| <p style="color:#00E676;">TAKE PROFIT</p> | |
| <h3>${tp:.2f}</h3> | |
| </div> | |
| <div> | |
| <p style="color:#FF5252;">STOP LOSS</p> | |
| <h3>${sl:.2f}</h3> | |
| </div> | |
| </div> | |
| <hr style="border-color:#333;"> | |
| <h4 style="color:#FFD700;">π TECHNICAL ANALYSIS</h4> | |
| <p style="font-family:monospace;color:#ccc;">{chr(10).join(reasons)}</p> | |
| <p style="color:#888;text-align:center;">{selected_pair} β’ {selected_tf} β’ {datetime.now().strftime('%H:%M:%S')}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| except Exception as e: | |
| st.error(f"Error: {str(e)}") | |
| st.markdown("---") | |
| st.markdown('<p style="text-align:center;color:#555;font-size:12px;">Pro Trading Signals β’ Powered by Finnhub</p>', unsafe_allow_html=True) |