Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
e941ef0d3c
commit
a5f6200636
@ -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`:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
42
web/src/tests/test_training_ui.py
Normal file
42
web/src/tests/test_training_ui.py
Normal 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
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user