Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-02-23 19:43:30 -06:00
parent e941ef0d3c
commit a5f6200636
5 changed files with 190 additions and 6 deletions

View File

@ -17,6 +17,11 @@ In-app help popup:
- Uses a multi-screen, child-friendly guide implemented in `web/src/web_core/ui/help_content.py`
- Includes dedicated screens for core setup, filters, training mode, backtest controls, and advanced panels
Beginner interpretation panel:
- Main page always shows `What This Tool Means (Beginner Training)`
- Includes plain-English definitions of top metrics and a historical snapshot table for `1M`, `3M`, `6M`, `1Y`
- Each row includes a simple interpretation so non-traders can quickly understand whether a window was directional or noisy
## 2) Quick Start (Recommended)
From project root:
@ -93,11 +98,15 @@ Tip: sign and notarize before sharing broadly, so macOS trust prompts are reduce
- Real Bullish Bars
- Real Bearish Bars
- Fake Bars
13. Read `Trend Events` for starts and reversals.
14. Use `Live Decision Guide` to translate trend state into a practical bias/action/invalidation workflow.
15. Keep `Show past behavior examples` ON while learning to review historical entry/exit outcomes.
16. Set `Display Timezone (US)` to your preferred timezone (default is `America/Chicago`, CST/CDT).
17. Choose `Use 24-hour time` ON for `13:00` style, or OFF for `1:00 PM` style.
13. Read `What This Tool Means (Beginner Training)`:
- Open `Plain-English metric guide` for quick definitions.
- Review `1M / 3M / 6M / 1Y` snapshot rows to see how this symbol behaved recently.
- Use `What this says` to understand if conditions were trend-friendly or choppy.
14. Read `Trend Events` for starts and reversals.
15. Use `Live Decision Guide` to translate trend state into a practical bias/action/invalidation workflow.
16. Keep `Show past behavior examples` ON while learning to review historical entry/exit outcomes.
17. Set `Display Timezone (US)` to your preferred timezone (default is `America/Chicago`, CST/CDT).
18. Choose `Use 24-hour time` ON for `13:00` style, or OFF for `1:00 PM` style.
## 4.1) Advanced Features (Optional)
- `Advanced Signals`:

View File

@ -170,6 +170,11 @@ Gap handling (`hide_market_closed_gaps`):
- `Help / Quick Start`
- Help appears in a dialog with multiple navigable screens (screen picker + previous/next).
- Help copy is intentionally beginner-friendly and explains each major sidebar control group, including detailed backtest controls and why each setting matters.
- Main page includes a beginner training block:
- `What This Tool Means (Beginner Training)`
- Plain-English definitions for top metrics (`Current Trend`, real/fake bars, `Signal Quality`, `Regime`, `Recent Fake Ratio`).
- Historical learning table with trailing windows (`1M`, `3M`, `6M`, `1Y`) computed from loaded data.
- Per-window interpretation text that summarizes whether behavior was trend-dominant, bearish-dominant, or choppy/noisy.
- The onboarding markdown remains project documentation; in-app help content is rendered from `web/src/web_core/ui/help_content.py`.
## 9. Outputs
@ -178,6 +183,8 @@ Gap handling (`hide_market_closed_gaps`):
- real bullish count
- real bearish count
- fake count
- beginner training guide (plain-English metric glossary)
- historical learning snapshots (`1M`, `3M`, `6M`, `1Y`) including price change, bar-type counts, trend flips, and interpretation
- Live decision guide (optional):
- bias (long/short/neutral)
- signal confirmation status

View File

@ -36,7 +36,7 @@ from web_core.auth.profile_store import (
from web_core.ui.sidebar_ui import render_sidebar
from web_core.strategy import classify_bars, detect_trends
from web_core.market.symbols import resolve_symbol_identity
from web_core.ui.training_ui import render_training_panel
from web_core.ui.training_ui import render_beginner_training_panel, render_training_panel
from web_core.time_display import format_timestamp
@ -180,6 +180,12 @@ def main() -> None:
q1.metric("Signal Quality", f"{quality['score']} ({quality['label']})")
q2.metric("Regime", regime_label)
q3.metric("Recent Fake Ratio", f"{quality['fake_ratio']}%")
render_beginner_training_panel(
analyzed=analyzed_view,
trend_now=trend_now,
signal_quality=quality,
regime_label=regime_label,
)
run_advanced_panels = bool(sidebar_settings.get("advanced_auto_run", False) or sidebar_settings.get("run_advanced_now", False))
if (bool(sidebar_settings["enable_multi_tf_confirmation"]) or bool(sidebar_settings["enable_compare_symbols"])) and not run_advanced_panels:

View File

@ -0,0 +1,42 @@
from __future__ import annotations
import pandas as pd
from web_core.constants import TREND_BEAR, TREND_BULL, TREND_NEUTRAL
from web_core.ui.training_ui import build_learning_window_rows
def _make_analyzed(start: str = "2025-01-01", periods: int = 420) -> pd.DataFrame:
idx = pd.date_range(start, periods=periods, freq="D", tz="UTC")
closes = [100.0 + (i * 0.2) for i in range(periods)]
classifications_cycle = ["real_bull", "fake", "real_bear", "fake", "real_bull"]
trend_cycle = [TREND_BULL, TREND_BULL, TREND_BEAR, TREND_BEAR, TREND_NEUTRAL]
classifications = [classifications_cycle[i % len(classifications_cycle)] for i in range(periods)]
trends = [trend_cycle[i % len(trend_cycle)] for i in range(periods)]
return pd.DataFrame({"Close": closes, "classification": classifications, "trend_state": trends}, index=idx)
def test_build_learning_window_rows_includes_standard_windows() -> None:
analyzed = _make_analyzed()
rows = build_learning_window_rows(analyzed)
assert list(rows["Window"]) == ["1M", "3M", "6M", "1Y"]
assert set(rows.columns) == {
"Window",
"Bars",
"Price Change %",
"Real Bull Bars",
"Real Bear Bars",
"Fake Bars",
"Trend Flips",
"What this says",
}
def test_build_learning_window_rows_fallbacks_with_short_history() -> None:
analyzed = _make_analyzed(periods=10)
rows = build_learning_window_rows(analyzed)
assert len(rows) == 1
assert rows.iloc[0]["Window"] == "All data"
assert int(rows.iloc[0]["Bars"]) == 10

View File

@ -6,6 +6,126 @@ import streamlit as st
from web_core.time_display import format_timestamp
def build_learning_window_rows(analyzed: pd.DataFrame) -> pd.DataFrame:
if analyzed.empty:
return pd.DataFrame(
columns=[
"Window",
"Bars",
"Price Change %",
"Real Bull Bars",
"Real Bear Bars",
"Fake Bars",
"Trend Flips",
"What this says",
]
)
indexed = analyzed.sort_index()
def _all_data_row(message: str) -> pd.DataFrame:
first_close = float(indexed.iloc[0]["Close"]) if len(indexed) else 0.0
last_close = float(indexed.iloc[-1]["Close"]) if len(indexed) else 0.0
change_pct = round(((last_close - first_close) / first_close) * 100.0, 2) if first_close else 0.0
return pd.DataFrame(
[
{
"Window": "All data",
"Bars": int(len(indexed)),
"Price Change %": change_pct,
"Real Bull Bars": int((indexed["classification"] == "real_bull").sum()),
"Real Bear Bars": int((indexed["classification"] == "real_bear").sum()),
"Fake Bars": int((indexed["classification"] == "fake").sum()),
"Trend Flips": int(max(0, (indexed["trend_state"] != indexed["trend_state"].shift(1)).sum() - 1)),
"What this says": message,
}
]
)
if len(indexed) < 2:
return _all_data_row("Not enough history yet. Load a longer period.")
latest_ts = indexed.index[-1]
earliest_ts = indexed.index[0]
if latest_ts - earliest_ts < pd.Timedelta(days=30):
return _all_data_row("Short history loaded. Increase Period for 1M/3M/6M/1Y views.")
windows = [("1M", 30), ("3M", 90), ("6M", 180), ("1Y", 365)]
rows: list[dict[str, object]] = []
for label, days in windows:
start_ts = latest_ts - pd.Timedelta(days=days)
window = indexed[indexed.index >= start_ts].copy()
if len(window) < 2:
continue
first_close = float(window.iloc[0]["Close"])
last_close = float(window.iloc[-1]["Close"])
change_pct = round(((last_close - first_close) / first_close) * 100.0, 2) if first_close else 0.0
bull_count = int((window["classification"] == "real_bull").sum())
bear_count = int((window["classification"] == "real_bear").sum())
fake_count = int((window["classification"] == "fake").sum())
trend_flips = int((window["trend_state"] != window["trend_state"].shift(1)).sum() - 1)
trend_flips = max(0, trend_flips)
if fake_count > max(bull_count, bear_count):
interpretation = "Mostly noisy/choppy. Keep risk small or stand aside."
elif bull_count > bear_count and change_pct > 0:
interpretation = "Buy-side pressure dominated this window."
elif bear_count > bull_count and change_pct < 0:
interpretation = "Sell-side pressure dominated this window."
else:
interpretation = "Mixed behavior. Wait for cleaner confirmation."
rows.append(
{
"Window": label,
"Bars": int(len(window)),
"Price Change %": change_pct,
"Real Bull Bars": bull_count,
"Real Bear Bars": bear_count,
"Fake Bars": fake_count,
"Trend Flips": trend_flips,
"What this says": interpretation,
}
)
if not rows:
return _all_data_row("Short history loaded. Increase Period for 1M/3M/6M/1Y views.")
return pd.DataFrame(rows)
def render_beginner_training_panel(
analyzed: pd.DataFrame,
trend_now: str,
signal_quality: dict[str, float | str],
regime_label: str,
) -> None:
st.subheader("What This Tool Means (Beginner Training)")
st.caption("This app is a chart interpreter. It labels candles, tracks trend state, and shows how similar signals behaved in the past.")
with st.expander("Plain-English metric guide", expanded=False):
st.markdown(
f"""
- `Current Trend`: **{trend_now}**. This is the app's directional state right now.
- `Real Bullish Bars`: candles that closed above the prior range (upside pressure).
- `Real Bearish Bars`: candles that closed below the prior range (downside pressure).
- `Fake Bars`: candles that stayed inside the prior range (noise, indecision).
- `Signal Quality`: **{signal_quality['score']} ({signal_quality['label']})**. Higher means cleaner recent structure.
- `Regime`: **{regime_label}**. `Trending` means directional movement; `Choppy` means frequent whipsaws.
- `Recent Fake Ratio`: **{signal_quality['fake_ratio']}%**. High values usually mean harder trading conditions.
"""
)
window_rows = build_learning_window_rows(analyzed)
st.caption("Historical training snapshots from the loaded data window (1M/3M/6M/1Y).")
st.dataframe(window_rows, use_container_width=True)
st.info(
"How to use this: if most windows show high fake bars and many trend flips, treat signals as low-confidence. "
"If windows show consistent real-bar dominance with fewer flips, conditions are usually cleaner."
)
def render_training_panel(
show_past_behavior: bool,
example_trades: pd.DataFrame,