diff --git a/.gitignore b/.gitignore
index 95855bb..9d5ddb4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,8 @@ __pycache__/
*.pyc
.venv/
venv/
+
+# signal CSVs — example files in examples/ are tracked; any CSV dropped in
+# the root or scripts/ is personal data and should not be committed
+/*.csv
+/scripts/*.csv
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4fd98b0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 davidcyberdash
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 5fe5e50..1f66373 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
**Backtest any MetaTrader 5 indicator by talking to Claude.**
+**Requirements:** Python 3.9+ · MetaTrader 5 (Windows) · Claude Code
+
Drop one include into your indicator, and MBT lets you fetch live data, read
your indicator's signals, and run a full backtest with an HTML report — all
through Claude, no scripting.
@@ -129,8 +131,31 @@ a timestamp.
| Tool | Purpose |
|------|---------|
-| `get_ohlcv` | live OHLCV bars for any symbol/timeframe |
+| `ping` | check MT5 is running and reachable |
+| `get_ohlcv` | live OHLCV bars for any symbol/timeframe (max 2000) |
| `get_signals` | read your indicator's logged signals |
-| `backtest` | replay + full metrics + HTML report |
+| `backtest` | replay + full metrics + HTML report (requires internet for chart) |
| `validate_signals` | check SL/TP geometry of every signal |
| `get_config` | show the active terminal + signal file |
+
+> **Note:** the HTML report is written to `reports/` on your machine and opened in a
+> browser. Claude cannot open it directly — it will give you the file path.
+
+---
+
+## Examples
+
+`examples/` contains sample signal CSVs you can use to try MBT without attaching
+an indicator. Point `signal_file` in `config.yaml` to any of them:
+
+```yaml
+signal_file: "C:/abs/path/to/MBT/examples/test_signals.csv"
+```
+
+---
+
+## Research scripts
+
+`scripts/` contains one-off analysis scripts used during strategy development.
+They are not part of the core toolkit and have no documentation — treat them as
+reference material, not user tools.
diff --git a/config.example.yaml b/config.example.yaml
index f560445..9c438e9 100644
--- a/config.example.yaml
+++ b/config.example.yaml
@@ -3,7 +3,7 @@
# Full path to the terminal64.exe of the MT5 instance your indicator runs on.
# Right-click your MT5 shortcut -> Properties -> Target to find it.
-mt5_path: "C:/Users/Administrator/Desktop/Ava Trade MT5 Terminal/terminal64.exe"
+mt5_path: "C:/Path/To/Your/terminal64.exe"
# The signal file your indicator writes (via SignalLogger.mqh).
# Either a bare filename (looked up inside the MT5 .../MQL5/Files folder)
@@ -22,3 +22,11 @@ ambiguous_bar: "loss"
# Where generated HTML reports are written (relative to the MBT folder).
reports_dir: "reports"
+
+# Spread + slippage allowance in points, used to estimate transaction cost per trade.
+# 5 is a reasonable default for most forex pairs; increase for gold/indices.
+slippage_points: 5.0
+
+# Risk per trade as a decimal (0.01 = 1%) — used only to translate R units into
+# account-percentage returns in the backtest report. Does not affect WIN/LOSS outcomes.
+risk_per_trade: 0.01
diff --git a/core/report_html.py b/core/report_html.py
index eddc433..8f50827 100644
--- a/core/report_html.py
+++ b/core/report_html.py
@@ -5,6 +5,7 @@ Uses Chart.js from a CDN so the output is a single self-contained .html the
user can open in any browser or screenshot for content.
"""
+import html as _html
import os
from datetime import datetime
@@ -33,6 +34,8 @@ def render(rep: BacktestReport, filename: str = None) -> str:
path = os.path.join(reports_dir(), filename)
+ sym = _html.escape(rep.symbol)
+ tf = _html.escape(rep.timeframe)
pf_str = "∞" if rep.profit_factor == float("inf") else f"{rep.profit_factor:.2f}"
cards = "".join([
@@ -79,7 +82,7 @@ def render(rep: BacktestReport, filename: str = None) -> str:
-MBT Backtest — {rep.symbol} {rep.timeframe}
+MBT Backtest — {sym} {tf}
- Backtest Report — {rep.symbol} {rep.timeframe}
+ Backtest Report — {sym} {tf}
{rep.total} trades taken · {rep.signals_skipped} signals skipped (trade already open)
· generated {datetime.now().strftime('%Y-%m-%d %H:%M')}
· ambiguous bars counted as losses (conservative)
diff --git a/core/signals.py b/core/signals.py
index ea7609e..608d85e 100644
--- a/core/signals.py
+++ b/core/signals.py
@@ -62,7 +62,7 @@ def load_signals(path: Optional[str] = None) -> List[Signal]:
cfg = load_config()
path = path or signal_file_path()
- with open(path, encoding="ansi", newline="") as f:
+ with open(path, encoding="cp1252", errors="replace", newline="") as f:
rows = list(csv.reader(f))
if not rows:
diff --git a/arm_signals_EURUSD_H1.csv b/examples/arm_signals_EURUSD_H1.csv
similarity index 100%
rename from arm_signals_EURUSD_H1.csv
rename to examples/arm_signals_EURUSD_H1.csv
diff --git a/gold_signals.csv b/examples/gold_signals.csv
similarity index 100%
rename from gold_signals.csv
rename to examples/gold_signals.csv
diff --git a/test_signals.csv b/examples/test_signals.csv
similarity index 100%
rename from test_signals.csv
rename to examples/test_signals.csv
diff --git a/install.py b/install.py
index 70d17ff..e35d62b 100644
--- a/install.py
+++ b/install.py
@@ -25,8 +25,10 @@ def step(msg):
def install_deps():
step("Installing Python dependencies")
- subprocess.run([sys.executable, "-m", "pip", "install", "-r",
- os.path.join(ROOT, "requirements.txt")], check=False)
+ result = subprocess.run([sys.executable, "-m", "pip", "install", "-r",
+ os.path.join(ROOT, "requirements.txt")])
+ if result.returncode != 0:
+ print("WARNING: pip install failed. Check the error above before continuing.")
def make_config():
diff --git a/mcp_server.py b/mcp_server.py
index 3ce0847..df42c23 100644
--- a/mcp_server.py
+++ b/mcp_server.py
@@ -15,7 +15,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from mcp.server.fastmcp import FastMCP
-from core.connection import load_config, signal_file_path
+from core.connection import load_config, connect, signal_file_path
from core.ohlcv import fetch_recent
from core.signals import load_signals
from core.backtest import run_backtest, report_to_dict
@@ -29,7 +29,9 @@ def get_ohlcv(symbol: str, timeframe: str, count: int = 100) -> dict:
"""
Fetch live OHLCV bars from the configured MT5 terminal (newest first).
timeframe: 1m 5m 15m 30m 1h 4h 1d 1w. Generic — works for any symbol.
+ count: max 2000 bars.
"""
+ count = min(count, 2000)
try:
bars = fetch_recent(symbol, timeframe, count)
return {"symbol": symbol, "timeframe": timeframe, "count": len(bars), "bars": bars}
@@ -75,7 +77,9 @@ def backtest(since_date: str = None, html_report: bool = True) -> dict:
profit factor, expectancy, drawdown, per-regime breakdown) in R units.
since_date: optional 'YYYY-MM-DD' filter on signals.
- html_report: also write a standalone HTML report with an equity curve.
+ html_report: write a standalone HTML report to reports/ and return its path.
+ The report requires an internet connection (loads Chart.js from CDN).
+ Claude cannot open the file — share the path with the user to open in a browser.
"""
try:
sigs = load_signals()
@@ -124,7 +128,8 @@ def validate_signals() -> dict:
@mcp.tool()
def get_config() -> dict:
- """Show the active MBT configuration (which terminal and signal file are in use)."""
+ """Show the active MBT configuration (which terminal and signal file are in use).
+ Note: the output includes local file paths — do not share it publicly."""
try:
cfg = load_config()
return {"config": cfg, "resolved_signal_file": signal_file_path()}
@@ -132,5 +137,25 @@ def get_config() -> dict:
return {"error": str(e)}
+@mcp.tool()
+def ping() -> dict:
+ """Check whether the MT5 terminal is running and reachable. Use this first
+ if any other tool returns an error, to confirm the connection is alive."""
+ try:
+ connect()
+ import MetaTrader5 as mt5
+ info = mt5.terminal_info()
+ if info is None:
+ return {"connected": False, "error": "terminal_info() returned None"}
+ return {
+ "connected": True,
+ "terminal": info.name,
+ "build": info.build,
+ "trade_allowed": info.trade_allowed,
+ }
+ except Exception as e:
+ return {"connected": False, "error": str(e)}
+
+
if __name__ == "__main__":
mcp.run(transport="stdio")
diff --git a/run_2021.py b/scripts/run_2021.py
similarity index 100%
rename from run_2021.py
rename to scripts/run_2021.py
diff --git a/run_adx_filter.py b/scripts/run_adx_filter.py
similarity index 100%
rename from run_adx_filter.py
rename to scripts/run_adx_filter.py
diff --git a/run_byyear.py b/scripts/run_byyear.py
similarity index 100%
rename from run_byyear.py
rename to scripts/run_byyear.py
diff --git a/run_efficiency.py b/scripts/run_efficiency.py
similarity index 100%
rename from run_efficiency.py
rename to scripts/run_efficiency.py
diff --git a/run_exit_opt.py b/scripts/run_exit_opt.py
similarity index 100%
rename from run_exit_opt.py
rename to scripts/run_exit_opt.py
diff --git a/run_fade.py b/scripts/run_fade.py
similarity index 100%
rename from run_fade.py
rename to scripts/run_fade.py
diff --git a/run_feature_analysis.py b/scripts/run_feature_analysis.py
similarity index 100%
rename from run_feature_analysis.py
rename to scripts/run_feature_analysis.py
diff --git a/run_feature_split.py b/scripts/run_feature_split.py
similarity index 100%
rename from run_feature_split.py
rename to scripts/run_feature_split.py
diff --git a/run_htf.py b/scripts/run_htf.py
similarity index 100%
rename from run_htf.py
rename to scripts/run_htf.py
diff --git a/run_paths.py b/scripts/run_paths.py
similarity index 100%
rename from run_paths.py
rename to scripts/run_paths.py