diff --git a/Trading Indicator Interpretation.csv b/Trading Indicator Interpretation.csv new file mode 100644 index 0000000..7ab4be6 --- /dev/null +++ b/Trading Indicator Interpretation.csv @@ -0,0 +1,15 @@ +Indicator,Visual Style,What it Means,Suggested Action +Z-Score (60/120),Red Heatmap,Price is +2 standard deviations above the mean (Statistically overstretched).,SELL / TRIM (Take profits) +Z-Score (60/120),Green Heatmap,Price is -2 standard deviations below the mean (Statistically depressed).,BUY / ACCUMULATE +RSI,Red Bold Text,RSI is > 70. Momentum is extremely high/overbought.,CAUTION / HOLD (Don't buy new) +RSI,Green Bold Text,RSI is < 30. Momentum is washed out/oversold.,WATCH FOR BOUNCE +BB %B,Red Bold Text,Price is above the Upper Bollinger Band (>100%).,SELL / HEDGE +BB %B,Green Bold Text,Price is below the Lower Bollinger Band (<0%).,WATCH FOR REVERSAL +52W Range,Red Bar,Price is trading near its 1-year high.,RESISTANCE AREA +52W Range,Green Bar,Price is trading near its 1-year low.,SUPPORT AREA +Z-Score,Red Heatmap,Statistically 2σ above the mean.,SELL / TRIM (Take profits) +Z-Score,Green Heatmap,Statistically 2σ below the mean.,BUY / ACCUMULATE +RSI / BB,Red Text,Overbought (>70) or outside Upper Band.,CAUTION (Stop buying) +RSI / BB,Green Text,Oversold (<30) or below Lower Band.,WATCH (Look for entry) +Dist. to EMA,Percentage %,"How far the price is ""floating"" above/below its average.",MEAN REVERSION: High % usually leads to a pull-back to the line. +KD (K/D),Value Pair,K > D: Bullish momentum. K < D: Bearish momentum.,"THE TRIGGER: Look for ""K"" crossing ""D"" to enter or exit." \ No newline at end of file diff --git a/Trading Indicator Interpretation.csv:Zone.Identifier b/Trading Indicator Interpretation.csv:Zone.Identifier new file mode 100644 index 0000000..d6c1ec6 Binary files /dev/null and b/Trading Indicator Interpretation.csv:Zone.Identifier differ diff --git a/__pycache__/engine.cpython-313.pyc b/__pycache__/engine.cpython-313.pyc index b9004c8..4bbddc4 100644 Binary files a/__pycache__/engine.cpython-313.pyc and b/__pycache__/engine.cpython-313.pyc differ diff --git a/app.py b/app.py index 4e68cb6..e52247b 100644 --- a/app.py +++ b/app.py @@ -12,6 +12,11 @@ app = Flask(__name__) cache = Cache(app, config={'CACHE_TYPE': 'SimpleCache'}) # The CSV is in the root directory (same level as app.py) CSV_PATH = os.path.join(os.path.dirname(__file__), 'instruments.csv') +# This tells Flask it is behind a proxy and to trust the HTTPS headers from Synology +@app.before_request +def before_request(): + if request.headers.get('X-Forwarded-Proto') == 'https': + request.environ['wsgi.url_scheme'] = 'https' @app.route('/settings') def settings(): diff --git a/engine.py b/engine.py index 3b06c0a..9cb179d 100644 --- a/engine.py +++ b/engine.py @@ -375,7 +375,39 @@ class DataEngine: prev_close = float(df.iloc[-2]['close']) change_pct = ((last_close - prev_close) / prev_close) * 100 count = len(df) + # --- NEW INDICATORS START --- + + # Bollinger Bands %B (20-day) + bb_pct = "N/A" + if count >= 20: + from ta.volatility import BollingerBands + indicator_bb = BollingerBands(close=df['close'], window=20, window_dev=2) + m_avg = indicator_bb.bollinger_mavg().iloc[-1] + h_band = indicator_bb.bollinger_hband().iloc[-1] + l_band = indicator_bb.bollinger_lband().iloc[-1] + # Calculate %B: where is price relative to bands? + if (h_band - l_band) != 0: + bb_pct = round((last_close - l_band) / (h_band - l_band), 2) + # RSI (14-day) + rsi_val = "N/A" + if count >= 14: + from ta.momentum import RSIIndicator + rsi_val = round(RSIIndicator(close=df['close'], window=14).rsi().iloc[-1], 1) + + # Z-Score Helper Function + def get_z_score(window): + if count >= window: + rolling_mean = df['close'].rolling(window=window).mean() + rolling_std = df['close'].rolling(window=window).std() + z = (last_close - rolling_mean.iloc[-1]) / rolling_std.iloc[-1] + return round(z, 2) + return "N/A" + + z60 = get_z_score(60) + z120 = get_z_score(120) + + # Existing EMA Logic (Distance % from EMA) def get_ema_offset(window): if count >= window: from ta.trend import EMAIndicator @@ -396,18 +428,22 @@ class DataEngine: return { "symbol": self.symbol, - "last_date": formatted_date, # <--- New field added + "last_date": formatted_date, "last_close": round(last_close, 2), "change_pct": round(change_pct, 2), "low_52": round(float(df.tail(252)['close'].min()), 2), "high_52": round(float(df.tail(252)['close'].max()), 2), + "bb_pct": bb_pct, + "rsi": rsi_val, + "z60": z60, + "z120": z120, "last_ema20": get_ema_offset(20), "last_ema50": get_ema_offset(50), "last_ema100": get_ema_offset(100), "last_ema200": get_ema_offset(200), "kd_values": f"{int(k_val)}/{int(d_val)}" if k_val != "N/A" else "N/A" } - + class StrategyEngine: """ Handles financial strategy simulations and backtesting. diff --git a/templates/index.html b/templates/index.html index a129710..7d6cc57 100644 --- a/templates/index.html +++ b/templates/index.html @@ -50,6 +50,7 @@ } .table { + /*table-layout: fixed; /* table-layout: auto; */ /* Default - let it be auto for data safety */ width: 100%; @@ -82,6 +83,21 @@ border-bottom: 1px solid #f1f1f1; padding: 10px 8px; } + /* Price Trend Colors */ + .text-up { color: #28a745 !important; } /* Success Green */ + .text-down { color: #dc3545 !important; } /* Danger Red */ + + /* Table Cell Styling */ + #tableBody td { + padding: 8px 4px; + font-size: 0.85rem; + white-space: nowrap; + transition: all 0.2s ease; + } + + /* Custom heatmaps for Z-score */ + .bg-oversold { background-color: #d1e7dd !important; color: #0f5132 !important; } + .bg-overbought { background-color: #f8d7da !important; color: #842029 !important; } /* 3. Sticky Column Logic (Instrument) */ .table td:first-child, @@ -210,7 +226,9 @@
-
Portfolio Signals
+
+ Portfolio Signals +
Updating...
+ ${rsiVal} + ${bbDisplay} + ${z60Val} + ${z120Val} ${typeof formatEma === 'function' ? formatEma(item.last_ema20) : item.last_ema20} ${typeof formatEma === 'function' ? formatEma(item.last_ema50) : item.last_ema50} ${typeof formatEma === 'function' ? formatEma(item.last_ema100) : item.last_ema100} @@ -366,18 +411,11 @@ `; }); - // 5. Final Injection tbody.innerHTML = htmlContent; } catch (error) { console.error("Fetch error:", error); - if (syncDisplay) { - syncDisplay.innerText = "OFFLINE"; - syncDisplay.classList.add('text-danger'); - } - if (tbody) { - tbody.innerHTML = 'Error loading summary. Check server logs.'; - } + if (tbody) tbody.innerHTML = 'Error loading data.'; } finally { if (loading) loading.style.display = 'none'; }