diff --git a/mac/src/App/ContentView.swift b/mac/src/App/ContentView.swift
index 281b5c0..3b282d6 100644
--- a/mac/src/App/ContentView.swift
+++ b/mac/src/App/ContentView.swift
@@ -21,6 +21,7 @@ struct ContentView: View {
@AppStorage("mt_enable_auto_refresh") private var storedEnableAutoRefresh = false
@AppStorage("mt_refresh_sec") private var storedRefreshSeconds = 60
@State private var showSetupSheet = false
+ @State private var showHelpSheet = false
@State private var setupDraft = UserSetupPreferences.default
#if DEBUG
@State private var showDebugPanel = false
@@ -54,6 +55,9 @@ struct ContentView: View {
.onDisappear { host.stop() }
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
+ Button("Help") {
+ showHelpSheet = true
+ }
Button("Setup") {
syncStateFromSharedSettingsFileIfAvailable()
setupDraft = storedWebPreferences.normalized().setupDefaults
@@ -74,6 +78,9 @@ struct ContentView: View {
.sheet(isPresented: $showSetupSheet) {
setupSheet
}
+ .sheet(isPresented: $showHelpSheet) {
+ HelpSheetView()
+ }
}
private var sharedSettingsURL: URL {
@@ -330,6 +337,64 @@ struct ContentView: View {
#endif
}
+private struct HelpSheetView: View {
+ @Environment(\.dismiss) private var dismiss
+
+ private var helpFileURL: URL? {
+ let fm = FileManager.default
+ let candidates = [
+ Bundle.main.url(forResource: "help", withExtension: "html", subdirectory: "Help"),
+ Bundle.main.url(forResource: "help", withExtension: "html"),
+ Bundle.main.resourceURL?.appendingPathComponent("Help/help.html"),
+ Bundle.main.resourceURL?.appendingPathComponent("help.html"),
+ ].compactMap { $0 }
+ return candidates.first(where: { fm.fileExists(atPath: $0.path) })
+ }
+
+ var body: some View {
+ NavigationStack {
+ Group {
+ if let helpFileURL {
+ HelpDocumentWebView(fileURL: helpFileURL)
+ } else {
+ VStack(alignment: .leading, spacing: 8) {
+ Text("Help file not found.")
+ .font(.title3.weight(.semibold))
+ Text("Expected bundled file: help.html")
+ .foregroundStyle(.secondary)
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
+ .padding(24)
+ }
+ }
+ .navigationTitle("Help & Quick Start")
+ .toolbar {
+ ToolbarItem(placement: .confirmationAction) {
+ Button("Done") { dismiss() }
+ }
+ }
+ }
+ .frame(minWidth: 860, minHeight: 640)
+ }
+}
+
+private struct HelpDocumentWebView: NSViewRepresentable {
+ let fileURL: URL
+
+ func makeNSView(context: Context) -> WKWebView {
+ let webView = WKWebView(frame: .zero)
+ webView.allowsBackForwardNavigationGestures = true
+ webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL.deletingLastPathComponent())
+ return webView
+ }
+
+ func updateNSView(_ webView: WKWebView, context: Context) {
+ if webView.url != fileURL {
+ webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL.deletingLastPathComponent())
+ }
+ }
+}
+
#Preview {
ContentView()
}
diff --git a/mac/src/App/EmbeddedBackend/WebBackend b/mac/src/App/EmbeddedBackend/WebBackend
index 4819df2..c31469b 100755
Binary files a/mac/src/App/EmbeddedBackend/WebBackend and b/mac/src/App/EmbeddedBackend/WebBackend differ
diff --git a/mac/src/App/Help/help.html b/mac/src/App/Help/help.html
new file mode 100644
index 0000000..027daa5
--- /dev/null
+++ b/mac/src/App/Help/help.html
@@ -0,0 +1,196 @@
+
+
+
+
+
+ Help & Quick Start
+
+
+
+
+ Help & Quick Start
+ A quick guide to reading signals, choosing settings, and troubleshooting.
+
+
+ Start in 60 Seconds
+
+ - Set a symbol like
AAPL or BTC-USD.
+ - Choose
Timeframe (1d is a good default) and Period (6mo).
+ - Keep
Ignore potentially live last bar enabled.
+ - Review trend status and chart markers.
+ - Use Export to download CSV/PDF outputs.
+
+
+
+
+ Signal Rules
+
+ real_bull close above previous high
+ real_bear close below previous low
+ fake close inside previous range
+
+
+ - Trend starts after 2 consecutive real bars in the same direction.
+ - Trend reverses only after 2 consecutive opposite real bars.
+ - Fake bars are noise and do not reverse trend.
+
+
+
+
+ Data Settings
+ Core fields
+
+ Symbol: ticker or pair, e.g. AAPL, MSFT, BTC-USD.
+ Timeframe: candle size. Start with 1d for cleaner swings.
+ Period: amount of history to load. Start with 6mo.
+ Max bars: limits loaded candles for speed and chart readability.
+
+ Optional filters
+
+ Use previous body range: ignores wick-only breakouts.
+ Enable volume filter: treats low-volume bars as fake.
+ Hide market-closed gaps: recommended ON for stocks.
+ Enable auto-refresh: useful for live monitoring only.
+
+
+
+
+ Chart Reading
+
+ - Green triangle-up markers show
real_bull bars.
+ - Red triangle-down markers show
real_bear bars.
+ - Gray candles (if enabled) de-emphasize fake/noise bars.
+ - Volume bars are color-coded by trend state.
+
+
+
+
+ Troubleshooting
+
+ - If no data appears, verify ticker format (for example
BTC-USD, not BTCUSD).
+ - If results look noisy, switch to
1d and reduce optional filters.
+ - If trend seems delayed, remember trend transitions require two real bars.
+ - This tool is analysis-only and does not place trades.
+
+
+
+
+
diff --git a/mac/src/MacShell.xcodeproj/project.pbxproj b/mac/src/MacShell.xcodeproj/project.pbxproj
index 1804bec..045f0a6 100644
--- a/mac/src/MacShell.xcodeproj/project.pbxproj
+++ b/mac/src/MacShell.xcodeproj/project.pbxproj
@@ -30,17 +30,17 @@
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
- EAB6606E2F3FD5C000ED41BA /* ManeshTraderMac */ = {
+ EAB6606E2F3FD5C000ED41BA /* App */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = App;
sourceTree = "";
};
- EAB6607C2F3FD5C100ED41BA /* ManeshTraderMacTests */ = {
+ EAB6607C2F3FD5C100ED41BA /* AppTests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = AppTests;
sourceTree = "";
};
- EAB660862F3FD5C100ED41BA /* ManeshTraderMacUITests */ = {
+ EAB660862F3FD5C100ED41BA /* AppUITests */ = {
isa = PBXFileSystemSynchronizedRootGroup;
path = AppUITests;
sourceTree = "";
@@ -75,9 +75,9 @@
EAB660632F3FD5C000ED41BA = {
isa = PBXGroup;
children = (
- EAB6606E2F3FD5C000ED41BA /* ManeshTraderMac */,
- EAB6607C2F3FD5C100ED41BA /* ManeshTraderMacTests */,
- EAB660862F3FD5C100ED41BA /* ManeshTraderMacUITests */,
+ EAB6606E2F3FD5C000ED41BA /* App */,
+ EAB6607C2F3FD5C100ED41BA /* AppTests */,
+ EAB660862F3FD5C100ED41BA /* AppUITests */,
EAB6606D2F3FD5C000ED41BA /* Products */,
);
sourceTree = "";
@@ -108,7 +108,7 @@
dependencies = (
);
fileSystemSynchronizedGroups = (
- EAB6606E2F3FD5C000ED41BA /* ManeshTraderMac */,
+ EAB6606E2F3FD5C000ED41BA /* App */,
);
name = ManeshTraderMac;
packageProductDependencies = (
@@ -131,7 +131,7 @@
EAB6607B2F3FD5C100ED41BA /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
- EAB6607C2F3FD5C100ED41BA /* ManeshTraderMacTests */,
+ EAB6607C2F3FD5C100ED41BA /* AppTests */,
);
name = ManeshTraderMacTests;
packageProductDependencies = (
@@ -154,7 +154,7 @@
EAB660852F3FD5C100ED41BA /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
- EAB660862F3FD5C100ED41BA /* ManeshTraderMacUITests */,
+ EAB660862F3FD5C100ED41BA /* AppUITests */,
);
name = ManeshTraderMacUITests;
packageProductDependencies = (
@@ -186,7 +186,7 @@
};
};
};
- buildConfigurationList = EAB660672F3FD5C000ED41BA /* Build configuration list for PBXProject "ManeshTraderMac" */;
+ buildConfigurationList = EAB660672F3FD5C000ED41BA /* Build configuration list for PBXProject "MacShell" */;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -540,7 +540,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- EAB660672F3FD5C000ED41BA /* Build configuration list for PBXProject "ManeshTraderMac" */ = {
+ EAB660672F3FD5C000ED41BA /* Build configuration list for PBXProject "MacShell" */ = {
isa = XCConfigurationList;
buildConfigurations = (
EAB6608B2F3FD5C100ED41BA /* Debug */,
diff --git a/mac/src/README.md b/mac/src/README.md
index 6ffd69a..2fec4d0 100644
--- a/mac/src/README.md
+++ b/mac/src/README.md
@@ -6,6 +6,7 @@ Native macOS shell around the web app in `web/src/`.
- Starts/stops bundled backend executable from app resources
- Hosts UI in `WKWebView` at local `127.0.0.1` URL
- Keeps user inside app window (no external browser)
+- Provides a native toolbar `Help` button that opens bundled quick-start docs in-app
## Build Self-Contained App
From repo root:
diff --git a/web/src/app.py b/web/src/app.py
index 9a62571..af43560 100644
--- a/web/src/app.py
+++ b/web/src/app.py
@@ -155,6 +155,19 @@ def save_web_settings(settings: dict[str, Any]) -> None:
SETTINGS_PATH.write_text(json.dumps(normalize_web_settings(settings), indent=2), encoding="utf-8")
+@st.cache_data(show_spinner=False)
+def load_help_markdown() -> str:
+ onboarding_path = Path(__file__).with_name("ONBOARDING.md")
+ if onboarding_path.exists():
+ return onboarding_path.read_text(encoding="utf-8")
+ return "Help content not found."
+
+
+@st.dialog("Help & Quick Start", width="large")
+def help_dialog() -> None:
+ st.markdown(load_help_markdown())
+
+
@st.cache_data(show_spinner=False, ttl=3600)
def lookup_symbol_candidates(query: str, max_results: int = 10) -> list[dict[str, str]]:
cleaned = query.strip()
@@ -226,51 +239,19 @@ def resolve_symbol_identity(symbol: str) -> dict[str, str]:
return {"symbol": normalized_symbol, "name": "", "exchange": ""}
-@st.cache_data(show_spinner=False)
-def load_onboarding_markdown() -> str:
- onboarding_path = Path(__file__).with_name("ONBOARDING.md")
- if onboarding_path.exists():
- return onboarding_path.read_text(encoding="utf-8")
- return "ONBOARDING.md not found in project root."
-
-
-@st.dialog("Onboarding Guide", width="large")
-def onboarding_dialog() -> None:
- st.markdown(load_onboarding_markdown())
-
-
def main() -> None:
st.set_page_config(page_title="Real Bars vs Fake Bars Analyzer", layout="wide")
st.title("Real Bars vs Fake Bars Trend Analyzer")
st.caption(
"Price-action tool that classifies closed bars, filters fake bars, and tracks trend persistence using only real bars."
)
- if st.button("Open ONBOARDING.md", type="tertiary"):
- onboarding_dialog()
-
- with st.expander("Help / Quick Start", expanded=False):
- st.markdown(
- """
- **Start in 60 seconds**
- 1. Set a symbol like `AAPL` or `BTC-USD`.
- 2. Choose `Timeframe` (`1d` is a good default) and `Period` (`6mo`).
- 3. Keep **Ignore potentially live last bar** enabled.
- 4. Review trend status and markers:
- - Green triangle: `real_bull`
- - Red triangle: `real_bear`
- - `fake` bars are noise and ignored by trend logic
- 5. Use **Export** to download CSV/PDF outputs.
-
- **Rule summary**
- - `real_bull`: close > previous high
- - `real_bear`: close < previous low
- - `fake`: close inside previous range
- - Trend starts/reverses only after 2 consecutive real bars in that direction.
- """
- )
with st.sidebar:
st.header("Data Settings")
+ if st.button("Help / Quick Start", use_container_width=True):
+ help_dialog()
+ st.divider()
+
query_params = st.query_params
persisted_settings = load_web_settings()