Files

207 lines
8.1 KiB
Python

"""
XAU/USD Gold Signals — Clean Monitor
Shows: current signal, entry, SL, TP. Nothing else.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
import warnings
warnings.filterwarnings("ignore")
import streamlit as st
import pandas as pd
import numpy as np
import yfinance as yf
from datetime import datetime, timezone
st.set_page_config(page_title="XAU Signals", page_icon="🥇", layout="centered")
# Hide streamlit branding
st.markdown("""
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
header {visibility: hidden;}
.stApp { background: #0D1117; }
.block-container { padding: 1rem 1rem !important; max-width: 500px; }
div[data-testid="stSidebar"] {display: none;}
</style>
""", unsafe_allow_html=True)
# ─── Import strategy ───
from strategies.xau_scalp import add_indicators_xau, generate_signals_xau, calculate_performance_xau
# ─── Fetch XAU/USD data directly ───
def fetch_gold(tf="5m", days=3):
raw = yf.download("GC=F", period=f"{max(1,days)}d", interval=tf, progress=False)
if raw is None or raw.empty:
return None
if isinstance(raw.columns, pd.MultiIndex):
raw.columns = raw.columns.get_level_values(0)
df = raw.reset_index()
df.columns = [c.lower().strip() for c in df.columns]
m = {"datetime":"time","date":"time","open":"open","high":"high","low":"low","close":"close","volume":"volume"}
df = df.rename(columns={k:v for k,v in m.items() if k in df.columns})
df["time"] = pd.to_datetime(df["time"])
return df.sort_values("time").reset_index(drop=True)
def format_price(v):
return f"${v:,.2f}" if v == v else "—"
# ─── Load & compute (cached to avoid rate limits) ───
@st.cache_data(ttl=300)
def load_gold():
return fetch_gold("5m", 3)
df = load_gold()
if df is None or len(df) < 60:
st.error("Failed to load XAU/USD data. Try again in a minute.")
st.stop()
df = add_indicators_xau(df)
df = generate_signals_xau(df, mom_threshold=0.55, atr_sl_mult=1.2, atr_tp_mult=2.0, max_hold_bars=4)
latest = df.iloc[-1]
pos = latest.get("position", 0)
price = latest["close"]
rsi_val = latest.get("rsi", 50)
atr_val = latest.get("atr_pct", 0)
# ─── Find latest signal ───
signals = df[df["signal"] != 0]
last_signal = signals.iloc[-1] if not signals.empty else None
# Also look at last 5 for history
recent_signals = signals.tail(10) if not signals.empty else pd.DataFrame()
# ─── UI ───
cols = st.columns([1, 2, 1])
with cols[1]:
st.markdown(f"<h2 style='text-align:center;color:#E0E0E0;margin:0;'>🥇 XAU/USD</h2>", unsafe_allow_html=True)
st.markdown(f"<p style='text-align:center;color:#9E9E9E;font-size:0.8rem;margin-top:-5px;'>{datetime.now(timezone.utc).strftime('%H:%M UTC')}</p>", unsafe_allow_html=True)
# ─── Current Price ───
st.markdown(f"<h1 style='text-align:center;font-size:3.5rem;font-weight:800;color:#E0E0E0;margin:0;'>{format_price(price)}</h1>", unsafe_allow_html=True)
# ─── Signal Badge ───
if pos == 1:
signal_color = "#00C853"
signal_text = "🟢 BUY"
signal_bg = "#003D1A"
elif pos == -1:
signal_color = "#FF1744"
signal_text = "🔴 SHORT"
signal_bg = "#3D0010"
else:
signal_color = "#757575"
signal_text = "⚪ WAIT"
signal_bg = "#1A1A1A"
st.markdown(f"""
<div style="text-align:center;padding:0.3rem;border-radius:8px;background:{signal_bg};margin:0.5rem 0;">
<span style="font-size:2rem;font-weight:700;color:{signal_color};">{signal_text}</span>
</div>
""", unsafe_allow_html=True)
# ─── SL / TP ───
if last_signal is not None:
entry_px = last_signal["close"]
sl_px = last_signal.get("sl_price", np.nan)
tp_px = last_signal.get("tp_price", np.nan)
pos2 = last_signal.get("position", last_signal.get("signal", 0))
if pos2 == 0:
pos2 = last_signal["signal"]
if pos2 == 1:
sl_label = "🛑 Stop Loss"
tp_label = "🎯 Take Profit"
sl_color = "#FF5252"
tp_color = "#69F0AE"
elif pos2 == -1:
sl_label = "🛑 Stop Loss"
tp_label = "🎯 Take Profit"
sl_color = "#FF5252"
tp_color = "#69F0AE"
else:
sl_label = "SL"
tp_label = "TP"
sl_color = "#757575"
tp_color = "#757575"
col_sl, col_tp = st.columns(2)
with col_sl:
sl_val = format_price(sl_px) if sl_px == sl_px else "—"
st.markdown(f"""
<div style="text-align:center;padding:0.8rem;border-radius:8px;background:#1A1D23;border:1px solid #2D3039;">
<div style="color:#9E9E9E;font-size:0.8rem;">{sl_label}</div>
<div style="font-size:1.5rem;font-weight:700;color:{sl_color};">{sl_val}</div>
</div>
""", unsafe_allow_html=True)
with col_tp:
tp_val = format_price(tp_px) if tp_px == tp_px else "—"
if pos2 == 1:
diff_px = tp_px - entry_px
diff_pct = diff_px / entry_px * 100
tp_detail = f"+${diff_px:.2f} (+{diff_pct:.2f}%)" if diff_px == diff_px else ""
elif pos2 == -1:
diff_px = entry_px - tp_px
diff_pct = diff_px / entry_px * 100
tp_detail = f"+${diff_px:.2f} (+{diff_pct:.2f}%)" if diff_px == diff_px else ""
else:
tp_detail = ""
st.markdown(f"""
<div style="text-align:center;padding:0.8rem;border-radius:8px;background:#1A1D23;border:1px solid #2D3039;">
<div style="color:#9E9E9E;font-size:0.8rem;">{tp_label}</div>
<div style="font-size:1.5rem;font-weight:700;color:{tp_color};">{tp_val}</div>
<div style="font-size:0.7rem;color:#69F0AE;">{tp_detail}</div>
</div>
""", unsafe_allow_html=True)
else:
st.markdown(f"""
<div style="text-align:center;padding:1rem;color:#9E9E9E;">
No active signal. Waiting for setup conditions...
</div>
""", unsafe_allow_html=True)
# ─── Context row ───
st.markdown("---")
col_rsi, col_atr, col_vol = st.columns(3)
with col_rsi:
rsi_c = "#00C853" if 40 <= rsi_val <= 60 else "#FF5252"
st.markdown(f"<div style='text-align:center;'><span style='color:#9E9E9E;font-size:0.7rem;'>RSI</span><br><span style='color:{rsi_c};font-size:1.2rem;font-weight:600;'>{rsi_val:.1f}</span></div>", unsafe_allow_html=True)
with col_atr:
st.markdown(f"<div style='text-align:center;'><span style='color:#9E9E9E;font-size:0.7rem;'>ATR%</span><br><span style='color:#E0E0E0;font-size:1.2rem;font-weight:600;'>{atr_val:.3f}%</span></div>", unsafe_allow_html=True)
with col_vol:
vol = latest.get("volume", 0)
st.markdown(f"<div style='text-align:center;'><span style='color:#9E9E9E;font-size:0.7rem;'>Volume</span><br><span style='color:#E0E0E0;font-size:1.2rem;font-weight:600;'>{int(vol):,}</span></div>", unsafe_allow_html=True)
# ─── Mini Price Line ───
st.markdown("---")
st.markdown("<p style='color:#9E9E9E;font-size:0.7rem;margin:0;'>Price (last 50 candles)</p>", unsafe_allow_html=True)
chart_data = df[["time", "close"]].tail(50).copy()
chart_data.columns = ["t", "price"]
st.line_chart(chart_data.set_index("t"), height=150, color="#FFD600")
# ─── Recent Signal History ───
if not recent_signals.empty:
st.markdown("---")
st.markdown("<p style='color:#9E9E9E;font-size:0.7rem;margin:0;'>Recent Signals</p>", unsafe_allow_html=True)
hist = recent_signals[["time", "close", "signal", "exit_reason"]].copy()
hist["time"] = hist["time"].dt.strftime("%H:%M")
hist["signal"] = hist["signal"].map({1: "🟢 BUY", -1: "🔴 SELL"})
hist = hist.rename(columns={"time": "T", "close": "Price", "signal": "Sig", "exit_reason": "Exit"})
hist["Exit"] = hist["Exit"].replace("", "—")
st.dataframe(hist, width="stretch", hide_index=True, height=200)
else:
st.markdown("---")
st.markdown("<p style='color:#9E9E9E;font-size:0.7rem;text-align:center;'>No signals generated in recent data.</p>", unsafe_allow_html=True)
# ─── Footer ───
st.markdown("---")
st.markdown("<p style='text-align:center;color:#3D4048;font-size:0.6rem;'>Data: Yahoo Finance GC=F · Refreshes every 5 min · Pull down to reload</p>", unsafe_allow_html=True)