Initial commit: MT5 EA Optimizer v1.0
Full optimization system for LEGSTECH_EA_V2: - Flask + SocketIO live dashboard (dark premium UI) - MT5 process control (auto-kill, clean launch, retry) - HTML report parser (UTF-16 LE, 597 trades, metrics) - Pre-run validation and actionable error messages - Analysis engines: Reversal, TimePerfomance, EntryExit, EquityCurve - Composite scoring (Calmar-primary) - Mutation engine with knowledge_base.yaml - Validation gate: IS + Walk-Forward - Reports folder with HTML/CSV per run - Double-click launcher batch file
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
"""
|
||||
mutation/engine.py
|
||||
Translates analysis findings into concrete parameter hypotheses.
|
||||
Uses knowledge_base.yaml rules as a structured ruleset.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
import numpy as np
|
||||
import yaml
|
||||
from loguru import logger
|
||||
|
||||
from data.models import Finding, Hypothesis
|
||||
|
||||
|
||||
class MutationEngine:
|
||||
"""
|
||||
Finding → Hypothesis translator.
|
||||
|
||||
Workflow:
|
||||
1. Load knowledge_base.yaml rules
|
||||
2. For each finding, find matching rules
|
||||
3. Filter rules already tested recently (dedup)
|
||||
4. Resolve dynamic mutation values (percentile-based, derived)
|
||||
5. Build Hypothesis objects
|
||||
6. Return sorted by estimated PnL impact
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
kb_path: str | Path = "mutation/knowledge_base.yaml",
|
||||
manifest_path: str | Path = "mutation/param_manifest.yaml",
|
||||
dedup_lookback: int = 10,
|
||||
):
|
||||
with open(kb_path) as f:
|
||||
self.kb = yaml.safe_load(f)["rules"]
|
||||
with open(manifest_path) as f:
|
||||
self.manifest = yaml.safe_load(f)["parameters"]
|
||||
self.dedup_lookback = dedup_lookback
|
||||
|
||||
# ── Public ────────────────────────────────────────────────────────────────
|
||||
|
||||
def propose(
|
||||
self,
|
||||
findings: list[Finding],
|
||||
current_params: dict[str, Any],
|
||||
recent_deltas: list[dict], # from store.get_recent_param_deltas()
|
||||
max_proposals: int = 3,
|
||||
) -> list[Hypothesis]:
|
||||
"""
|
||||
Generate hypotheses from findings, de-duplicate, and return top-N.
|
||||
"""
|
||||
hypotheses: list[Hypothesis] = []
|
||||
|
||||
for finding in findings:
|
||||
for rule in self.kb:
|
||||
if not self._rule_matches(rule, finding, current_params):
|
||||
continue
|
||||
|
||||
param_delta = self._build_delta(rule, finding, current_params)
|
||||
if not param_delta:
|
||||
continue
|
||||
|
||||
# Skip if identical delta was recently tested
|
||||
if self._already_tested(param_delta, recent_deltas):
|
||||
logger.debug(f"Skipping rule {rule['id']} — already tested.")
|
||||
continue
|
||||
|
||||
h = Hypothesis(
|
||||
parent_run_id=finding.run_id,
|
||||
finding_ids=[finding.finding_id],
|
||||
description=f"[{rule['id']}] {rule['action_label']}",
|
||||
param_delta=param_delta,
|
||||
strategy=rule.get("strategy", "targeted"),
|
||||
kb_rule_id=rule["id"],
|
||||
)
|
||||
hypotheses.append((h, finding.impact_estimate_pnl))
|
||||
|
||||
# Sort by impact descending, deduplicate by KB rule
|
||||
seen_rules = set()
|
||||
ranked: list[Hypothesis] = []
|
||||
for h, impact in sorted(hypotheses, key=lambda x: x[1], reverse=True):
|
||||
if h.kb_rule_id not in seen_rules:
|
||||
seen_rules.add(h.kb_rule_id)
|
||||
ranked.append(h)
|
||||
if len(ranked) >= max_proposals:
|
||||
break
|
||||
|
||||
logger.info(f"Proposed {len(ranked)} hypotheses from {len(findings)} findings.")
|
||||
return ranked
|
||||
|
||||
# ── Rule matching ─────────────────────────────────────────────────────────
|
||||
|
||||
def _rule_matches(
|
||||
self, rule: dict, finding: Finding, current_params: dict
|
||||
) -> bool:
|
||||
"""Check if a KB rule's trigger matches this finding and current params."""
|
||||
trigger = rule.get("trigger", {})
|
||||
|
||||
# Analyzer match
|
||||
if trigger.get("analyzer") and trigger["analyzer"] != finding.analyzer:
|
||||
return False
|
||||
|
||||
# Evaluate condition expression against finding evidence + current params
|
||||
condition = trigger.get("condition", "")
|
||||
if condition:
|
||||
env = {**finding.evidence, **current_params}
|
||||
# Simple boolean parsing for conditions like "reversal_rate > 0.15"
|
||||
try:
|
||||
if not self._eval_condition(condition, env):
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.debug(f"Rule {rule['id']} condition eval error: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _eval_condition(self, condition: str, env: dict) -> bool:
|
||||
"""
|
||||
Evaluate a simple condition string.
|
||||
Supports: >, <, >=, <=, ==, AND, OR
|
||||
Variables are looked up in env dict.
|
||||
"""
|
||||
# Replace variable names with their values
|
||||
tokens = condition.split()
|
||||
resolved_tokens = []
|
||||
for token in tokens:
|
||||
if token in ("AND", "OR", "and", "or", ">", "<", ">=", "<=", "==", "!="):
|
||||
resolved_tokens.append(token.lower())
|
||||
elif token in env:
|
||||
val = env[token]
|
||||
resolved_tokens.append(str(val) if not isinstance(val, str) else f'"{val}"')
|
||||
else:
|
||||
resolved_tokens.append(token)
|
||||
|
||||
expr = " ".join(resolved_tokens)
|
||||
return bool(eval(expr, {"__builtins__": {}})) # restricted eval
|
||||
|
||||
# ── Delta building ────────────────────────────────────────────────────────
|
||||
|
||||
def _build_delta(
|
||||
self,
|
||||
rule: dict,
|
||||
finding: Finding,
|
||||
current_params: dict,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Translate a KB mutation spec into a concrete {param_name: new_value} dict.
|
||||
Handles: set, multiply, derive_from, set_to_percentile.
|
||||
"""
|
||||
delta: dict[str, Any] = {}
|
||||
mutations = rule.get("mutations", {})
|
||||
|
||||
for param_name, mutation_spec in mutations.items():
|
||||
current = current_params.get(param_name)
|
||||
spec = self.manifest.get(param_name, {})
|
||||
ptype = spec.get("type", "float")
|
||||
p_min = spec.get("min")
|
||||
p_max = spec.get("max")
|
||||
|
||||
new_val = self._resolve_mutation(
|
||||
mutation_spec, current, ptype, p_min, p_max, finding
|
||||
)
|
||||
if new_val is not None:
|
||||
delta[param_name] = new_val
|
||||
|
||||
# Auto-cascade: if enabling a bool, set defaults for depends_on params
|
||||
if ptype == "bool" and new_val is True:
|
||||
delta.update(self._cascade_dependencies(param_name, current_params))
|
||||
|
||||
return delta
|
||||
|
||||
def _resolve_mutation(
|
||||
self,
|
||||
spec: dict | Any,
|
||||
current: Any,
|
||||
ptype: str,
|
||||
p_min: Optional[float],
|
||||
p_max: Optional[float],
|
||||
finding: Finding,
|
||||
) -> Optional[Any]:
|
||||
"""Resolve a single mutation spec into a concrete value."""
|
||||
if not isinstance(spec, dict):
|
||||
return spec # bare value
|
||||
|
||||
# set: directly set to a value
|
||||
if "set" in spec:
|
||||
return spec["set"]
|
||||
|
||||
# multiply: multiply current value by factor
|
||||
if "multiply" in spec and current is not None:
|
||||
result = float(current) * spec["multiply"]
|
||||
if "clamp_min" in spec:
|
||||
result = max(spec["clamp_min"], result)
|
||||
if p_min is not None:
|
||||
result = max(p_min, result)
|
||||
if p_max is not None:
|
||||
result = min(p_max, result)
|
||||
return round(result, 2) if ptype == "float" else int(result)
|
||||
|
||||
# set_to_percentile: use Nth percentile of a finding evidence list
|
||||
if "set_to_percentile" in spec:
|
||||
pct = spec["set_to_percentile"] / 100.0
|
||||
data = finding.evidence.get("mfe_pips_distribution", [])
|
||||
if data:
|
||||
val = float(np.percentile(data, pct * 100))
|
||||
if "scale" in spec:
|
||||
val *= spec["scale"]
|
||||
if p_min is not None:
|
||||
val = max(p_min, val)
|
||||
if p_max is not None:
|
||||
val = min(p_max, val)
|
||||
return round(val, 1)
|
||||
|
||||
# derive_from: use evidence field
|
||||
if "derive_from" in spec:
|
||||
key = spec["derive_from"]
|
||||
val = finding.evidence.get(key)
|
||||
if val is not None:
|
||||
return val
|
||||
|
||||
return None
|
||||
|
||||
def _cascade_dependencies(
|
||||
self, bool_param: str, current_params: dict
|
||||
) -> dict[str, Any]:
|
||||
"""When a bool param is enabled, fill in sensible defaults for its dependents."""
|
||||
cascade = {}
|
||||
for name, spec in self.manifest.items():
|
||||
if spec.get("depends_on") != bool_param:
|
||||
continue
|
||||
# Only set if not already in current params or at a sub-optimal default
|
||||
if name not in current_params:
|
||||
cascade[name] = spec.get("default", 0)
|
||||
return cascade
|
||||
|
||||
# ── Deduplication ─────────────────────────────────────────────────────────
|
||||
|
||||
def _already_tested(self, delta: dict, recent_deltas: list[dict]) -> bool:
|
||||
"""Check if an identical param delta was tested recently."""
|
||||
delta_str = json.dumps(delta, sort_keys=True)
|
||||
for past in recent_deltas:
|
||||
if json.dumps(past, sort_keys=True) == delta_str:
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,187 @@
|
||||
rules:
|
||||
|
||||
# ── Trailing / Exit Rules ────────────────────────────────────────────────────
|
||||
|
||||
- id: KB001
|
||||
trigger:
|
||||
analyzer: reversal
|
||||
condition: "reversal_rate > 0.15"
|
||||
action_label: "Enable trailing stop to protect in-profit trades (reversal rate high)"
|
||||
mutations:
|
||||
InpUseTrailing:
|
||||
set: true
|
||||
InpTrailStartPips:
|
||||
derive_from: "mfe_p25" # 25th percentile of reversal MFE
|
||||
fallback: 20.0
|
||||
InpTrailStepPips:
|
||||
set: 10.0
|
||||
strategy: targeted
|
||||
|
||||
- id: KB002
|
||||
trigger:
|
||||
analyzer: reversal
|
||||
condition: "reversal_rate > 0.20"
|
||||
action_label: "Tighten TP ratio — too many trades reversing before target hit"
|
||||
mutations:
|
||||
InpRRRatio:
|
||||
multiply: 0.80
|
||||
clamp_min: 1.0
|
||||
strategy: targeted
|
||||
|
||||
- id: KB003
|
||||
trigger:
|
||||
analyzer: reversal
|
||||
condition: "mean_capture_ratio < 0.55"
|
||||
action_label: "Low MFE capture on winners — enable or tighten trailing"
|
||||
mutations:
|
||||
InpUseTrailing:
|
||||
set: true
|
||||
InpTrailStartPips:
|
||||
set: 15.0
|
||||
InpTrailStepPips:
|
||||
set: 8.0
|
||||
strategy: targeted
|
||||
|
||||
# ── Session / Time Filter Rules ───────────────────────────────────────────────
|
||||
|
||||
- id: KB004
|
||||
trigger:
|
||||
analyzer: time_performance
|
||||
condition: "type == 'hour_window'"
|
||||
action_label: "Exclude identified negative-edge UTC time window via session filter"
|
||||
mutations:
|
||||
InpUseSession:
|
||||
set: true
|
||||
InpSessionEnd:
|
||||
derive_from: "broker_start" # end session before bad window starts
|
||||
strategy: targeted
|
||||
|
||||
- id: KB005
|
||||
trigger:
|
||||
analyzer: time_performance
|
||||
condition: "type == 'session'"
|
||||
action_label: "Negative-edge session detected — tighten or disable session window"
|
||||
mutations:
|
||||
InpUseSession:
|
||||
set: true
|
||||
strategy: targeted
|
||||
|
||||
# ── Entry Quality Rules ────────────────────────────────────────────────────────
|
||||
|
||||
- id: KB006
|
||||
trigger:
|
||||
analyzer: entry_exit_quality
|
||||
condition: "diagnosis == 'poor_entry'"
|
||||
action_label: "Poor entry quality — tighten ATR filter and score gate"
|
||||
mutations:
|
||||
InpUseSpreadGuard:
|
||||
set: true
|
||||
InpMinScore:
|
||||
multiply: 1.125
|
||||
clamp_min: 6
|
||||
InpATRMultiplier:
|
||||
multiply: 1.20
|
||||
clamp_min: 0.3
|
||||
strategy: targeted
|
||||
|
||||
- id: KB007
|
||||
trigger:
|
||||
analyzer: entry_exit_quality
|
||||
condition: "diagnosis == 'good_entry_poor_exit'"
|
||||
action_label: "Good entries, poor exits — enable trailing with conservative start"
|
||||
mutations:
|
||||
InpUseTrailing:
|
||||
set: true
|
||||
InpTrailStartPips:
|
||||
set: 18.0
|
||||
InpTrailStepPips:
|
||||
set: 10.0
|
||||
strategy: targeted
|
||||
|
||||
- id: KB008
|
||||
trigger:
|
||||
analyzer: entry_exit_quality
|
||||
condition: "diagnosis == 'both_broken'"
|
||||
action_label: "Both entry and exit quality poor — test conservative bot mode"
|
||||
mutations:
|
||||
InpBotMode:
|
||||
set: 2
|
||||
InpMinScore:
|
||||
multiply: 1.25
|
||||
clamp_min: 6
|
||||
strategy: compound
|
||||
|
||||
# ── Risk / Drawdown Rules ─────────────────────────────────────────────────────
|
||||
|
||||
- id: KB009
|
||||
trigger:
|
||||
analyzer: equity_curve
|
||||
condition: "flatness_score > 0.50"
|
||||
action_label: "Equity spending too much time in drawdown — reduce risk per trade"
|
||||
mutations:
|
||||
InpRiskPercent:
|
||||
multiply: 0.75
|
||||
clamp_min: 0.5
|
||||
InpMaxDailyLossPct:
|
||||
multiply: 0.80
|
||||
clamp_min: 1.0
|
||||
strategy: targeted
|
||||
|
||||
- id: KB010
|
||||
trigger:
|
||||
analyzer: equity_curve
|
||||
condition: "cluster_count > 3"
|
||||
action_label: "Repeated loss clusters — limit consecutive trades and daily risk"
|
||||
mutations:
|
||||
InpMaxTradesPerDay:
|
||||
multiply: 0.75
|
||||
clamp_min: 2
|
||||
InpMaxDailyLossPct:
|
||||
set: 2.0
|
||||
strategy: targeted
|
||||
|
||||
# ── Breakeven Rules ────────────────────────────────────────────────────────────
|
||||
|
||||
- id: KB011
|
||||
trigger:
|
||||
analyzer: reversal
|
||||
condition: "reversal_rate > 0.12"
|
||||
action_label: "Enable breakeven stop to lock in partial profit before reversal"
|
||||
mutations:
|
||||
InpUseBreakeven:
|
||||
set: true
|
||||
InpBEPips:
|
||||
derive_from: "mfe_p25"
|
||||
fallback: 15.0
|
||||
InpBEBufferPips:
|
||||
set: 2.0
|
||||
strategy: targeted
|
||||
|
||||
# ── Filter Tightening Rules ────────────────────────────────────────────────────
|
||||
|
||||
- id: KB012
|
||||
trigger:
|
||||
analyzer: entry_exit_quality
|
||||
condition: "high_mae_loser_count > 10"
|
||||
action_label: "High MAE losers — require EMA slope confirmation"
|
||||
mutations:
|
||||
InpUseEMA:
|
||||
set: true
|
||||
InpRequireEMASlope:
|
||||
set: true
|
||||
InpEMASlopeBars:
|
||||
set: 2
|
||||
strategy: targeted
|
||||
|
||||
- id: KB013
|
||||
trigger:
|
||||
analyzer: entry_exit_quality
|
||||
condition: "mean_entry_quality < 0.35"
|
||||
action_label: "Very poor entry quality — tighten minimum RR gate"
|
||||
mutations:
|
||||
InpUseMinRR:
|
||||
set: true
|
||||
InpMinRRRatio:
|
||||
multiply: 1.25
|
||||
clamp_min: 1.0
|
||||
strategy: targeted
|
||||
@@ -0,0 +1,394 @@
|
||||
# Parameter Manifest — LEGSTECH_EA_V2
|
||||
# Generated from: LEGSTECH_EA_V2.set
|
||||
# Format: value = default, min/max/step = optimization bounds
|
||||
# Types: float | int | bool | enum
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
||||
parameters:
|
||||
|
||||
# ── Bot / Mode ─────────────────────────────────────────────
|
||||
InpBotMode:
|
||||
type: enum
|
||||
values: [0, 1, 2] # 0, 1, 2 per .set range 0→2 step 1
|
||||
default: 1
|
||||
category: mode
|
||||
description: "Bot operating mode"
|
||||
|
||||
# ── Timeframe Selection (PERIOD_ codes) ────────────────────
|
||||
# These are MT5 ENUM_TIMEFRAMES integer codes. Not optimized — fixed.
|
||||
InpHTF:
|
||||
type: fixed
|
||||
default: 16408 # PERIOD_H4
|
||||
category: timeframe
|
||||
InpMTF:
|
||||
type: fixed
|
||||
default: 16388 # PERIOD_H1
|
||||
category: timeframe
|
||||
InpLTF:
|
||||
type: fixed
|
||||
default: 16385 # PERIOD_M30
|
||||
category: timeframe
|
||||
|
||||
# ── Risk Management ────────────────────────────────────────
|
||||
InpRiskType:
|
||||
type: enum
|
||||
values: [0, 1] # 0=fixed lot, 1=percent risk
|
||||
default: 1
|
||||
category: risk
|
||||
|
||||
InpFixedLot:
|
||||
type: float
|
||||
min: 0.01
|
||||
max: 1.0
|
||||
step: 0.01
|
||||
default: 0.01
|
||||
category: risk
|
||||
depends_on_value: {InpRiskType: 0} # only active when using fixed lot mode
|
||||
|
||||
InpRiskPercent:
|
||||
type: float
|
||||
min: 0.5
|
||||
max: 3.0
|
||||
step: 0.5
|
||||
default: 1.0
|
||||
category: risk
|
||||
depends_on_value: {InpRiskType: 1} # only active when using percent risk mode
|
||||
|
||||
InpMaxDailyLossPct:
|
||||
type: float
|
||||
min: 1.0
|
||||
max: 5.0
|
||||
step: 0.5
|
||||
default: 3.0
|
||||
category: risk
|
||||
|
||||
InpMaxTradesPerDay:
|
||||
type: int
|
||||
min: 1
|
||||
max: 10
|
||||
step: 1
|
||||
default: 5
|
||||
category: risk
|
||||
|
||||
# ── Stop Loss ──────────────────────────────────────────────
|
||||
InpSLType:
|
||||
type: enum
|
||||
values: [0, 1] # 0=fixed pips, 1=ATR-based
|
||||
default: 0
|
||||
category: sl
|
||||
|
||||
InpSLBuffer:
|
||||
type: float
|
||||
min: 5.0
|
||||
max: 30.0
|
||||
step: 5.0
|
||||
default: 10.0
|
||||
category: sl
|
||||
description: "Buffer pips added to SL"
|
||||
|
||||
InpFixedSLPips:
|
||||
type: float
|
||||
min: 50.0
|
||||
max: 200.0
|
||||
step: 10.0
|
||||
default: 100.0
|
||||
category: sl
|
||||
depends_on_value: {InpSLType: 0}
|
||||
|
||||
InpMaxSLPips:
|
||||
type: float
|
||||
min: 100.0
|
||||
max: 400.0
|
||||
step: 50.0
|
||||
default: 200.0
|
||||
category: sl
|
||||
description: "Hard cap on calculated SL size"
|
||||
|
||||
InpUseFractalSL:
|
||||
type: bool
|
||||
default: false # 0 in .set
|
||||
category: sl
|
||||
|
||||
# ── Take Profit ────────────────────────────────────────────
|
||||
InpTPType:
|
||||
type: enum
|
||||
values: [0, 1] # 0=RR ratio, 1=fixed pips
|
||||
default: 0
|
||||
category: tp
|
||||
|
||||
InpRRRatio:
|
||||
type: float
|
||||
min: 1.0
|
||||
max: 3.0
|
||||
step: 0.5
|
||||
default: 1.5
|
||||
category: tp
|
||||
depends_on_value: {InpTPType: 0}
|
||||
|
||||
InpFixedTPPips:
|
||||
type: float
|
||||
min: 50.0
|
||||
max: 200.0
|
||||
step: 10.0
|
||||
default: 100.0
|
||||
category: tp
|
||||
depends_on_value: {InpTPType: 1}
|
||||
|
||||
InpUseFractalFilter:
|
||||
type: bool
|
||||
default: false # 0 in .set
|
||||
category: tp
|
||||
|
||||
# ── Session Filter ─────────────────────────────────────────
|
||||
InpUseSession:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_session
|
||||
|
||||
InpSessionStart:
|
||||
type: int
|
||||
min: 0
|
||||
max: 23
|
||||
step: 1
|
||||
default: 7
|
||||
category: filter_session
|
||||
depends_on: InpUseSession
|
||||
description: "Session start hour (broker local time)"
|
||||
|
||||
InpSessionEnd:
|
||||
type: int
|
||||
min: 0
|
||||
max: 23
|
||||
step: 1
|
||||
default: 20
|
||||
category: filter_session
|
||||
depends_on: InpUseSession
|
||||
description: "Session end hour (broker local time)"
|
||||
|
||||
# ── Trade Limits ───────────────────────────────────────────
|
||||
InpMaxOpenTrades:
|
||||
type: int
|
||||
min: 1
|
||||
max: 3
|
||||
step: 1
|
||||
default: 1
|
||||
category: risk
|
||||
|
||||
InpAllowMultiple:
|
||||
type: bool
|
||||
default: false # 0 in .set
|
||||
category: risk
|
||||
|
||||
# ── Execution ─────────────────────────────────────────────
|
||||
InpMagicNumber:
|
||||
type: fixed
|
||||
default: 202402
|
||||
category: execution
|
||||
description: "Fixed — do not optimize"
|
||||
|
||||
InpSlippage:
|
||||
type: int
|
||||
min: 5
|
||||
max: 30
|
||||
step: 5
|
||||
default: 10
|
||||
category: execution
|
||||
|
||||
# ── Trailing Stop ─────────────────────────────────────────
|
||||
InpUseTrailing:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: exit_trail
|
||||
|
||||
InpTrailStartPips:
|
||||
type: float
|
||||
min: 10.0
|
||||
max: 50.0
|
||||
step: 5.0
|
||||
default: 20.0
|
||||
category: exit_trail
|
||||
depends_on: InpUseTrailing
|
||||
|
||||
InpTrailStepPips:
|
||||
type: float
|
||||
min: 5.0
|
||||
max: 30.0
|
||||
step: 5.0
|
||||
default: 10.0
|
||||
category: exit_trail
|
||||
depends_on: InpUseTrailing
|
||||
|
||||
# ── Break Even ────────────────────────────────────────────
|
||||
InpUseBreakeven:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: exit_be
|
||||
|
||||
InpBEPips:
|
||||
type: float
|
||||
min: 10.0
|
||||
max: 40.0
|
||||
step: 5.0
|
||||
default: 15.0
|
||||
category: exit_be
|
||||
depends_on: InpUseBreakeven
|
||||
description: "Pips in profit to activate breakeven"
|
||||
|
||||
InpBEBufferPips:
|
||||
type: float
|
||||
min: 1.0
|
||||
max: 5.0
|
||||
step: 1.0
|
||||
default: 2.0
|
||||
category: exit_be
|
||||
depends_on: InpUseBreakeven
|
||||
description: "Buffer pips above entry for breakeven SL"
|
||||
|
||||
# ── EMA Filter ────────────────────────────────────────────
|
||||
InpUseEMA:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_ema
|
||||
|
||||
InpEMAPeriod:
|
||||
type: int
|
||||
min: 20
|
||||
max: 100
|
||||
step: 10
|
||||
default: 50
|
||||
category: filter_ema
|
||||
depends_on: InpUseEMA
|
||||
|
||||
InpRequireEMASlope:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_ema
|
||||
depends_on: InpUseEMA
|
||||
|
||||
InpEMASlopeBars:
|
||||
type: int
|
||||
min: 1
|
||||
max: 3
|
||||
step: 1
|
||||
default: 1
|
||||
category: filter_ema
|
||||
depends_on: InpRequireEMASlope
|
||||
|
||||
# ── Entry Mode ────────────────────────────────────────────
|
||||
InpEntryMode:
|
||||
type: enum
|
||||
values: [0, 1]
|
||||
default: 1
|
||||
category: entry
|
||||
|
||||
InpSLBufferMode:
|
||||
type: enum
|
||||
values: [0, 1]
|
||||
default: 1
|
||||
category: sl
|
||||
|
||||
# ── ATR ───────────────────────────────────────────────────
|
||||
InpATRPeriod:
|
||||
type: int
|
||||
min: 10
|
||||
max: 20
|
||||
step: 2
|
||||
default: 14
|
||||
category: atr
|
||||
|
||||
InpATRMultiplier:
|
||||
type: float
|
||||
min: 0.3
|
||||
max: 1.0
|
||||
step: 0.1
|
||||
default: 0.5
|
||||
category: atr
|
||||
|
||||
# ── Spread Guard ──────────────────────────────────────────
|
||||
InpUseSpreadGuard:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_spread
|
||||
|
||||
InpMaxSpreadPips:
|
||||
type: float
|
||||
min: 10.0
|
||||
max: 50.0
|
||||
step: 5.0
|
||||
default: 30.0
|
||||
category: filter_spread
|
||||
depends_on: InpUseSpreadGuard
|
||||
|
||||
# ── Minimum R:R Gate ──────────────────────────────────────
|
||||
InpUseMinRR:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_rr
|
||||
|
||||
InpMinRRRatio:
|
||||
type: float
|
||||
min: 1.0
|
||||
max: 3.0
|
||||
step: 0.5
|
||||
default: 1.5
|
||||
category: filter_rr
|
||||
depends_on: InpUseMinRR
|
||||
|
||||
# ── Score Gate ────────────────────────────────────────────
|
||||
InpUseScoreGate:
|
||||
type: bool
|
||||
default: true # 1 in .set
|
||||
category: filter_score
|
||||
|
||||
InpMinScore:
|
||||
type: int
|
||||
min: 6
|
||||
max: 11
|
||||
step: 1
|
||||
default: 8
|
||||
category: filter_score
|
||||
depends_on: InpUseScoreGate
|
||||
description: "Minimum signal quality score required to enter trade"
|
||||
|
||||
# ── Tester-Specific (fixed during automation) ─────────────
|
||||
InpTesterMode:
|
||||
type: fixed
|
||||
default: 1
|
||||
category: tester
|
||||
description: "Must be 1 during automated backtesting"
|
||||
|
||||
InpTesterInitDeposit:
|
||||
type: fixed
|
||||
default: 10000.0
|
||||
category: tester
|
||||
|
||||
InpTesterSpreadPts:
|
||||
type: int
|
||||
min: 10
|
||||
max: 50
|
||||
step: 5
|
||||
default: 20
|
||||
category: tester
|
||||
description: "Spread in points used in tester (20 pts = 2.0 pips for XAUUSD)"
|
||||
|
||||
InpShowPanel:
|
||||
type: fixed
|
||||
default: 0 # force off during automation (no GUI needed)
|
||||
category: tester
|
||||
|
||||
# ── Parameter categories (for mutation engine grouping) ──────
|
||||
categories:
|
||||
mode: [InpBotMode]
|
||||
risk: [InpRiskType, InpFixedLot, InpRiskPercent, InpMaxDailyLossPct, InpMaxTradesPerDay, InpMaxOpenTrades, InpAllowMultiple]
|
||||
sl: [InpSLType, InpSLBuffer, InpFixedSLPips, InpMaxSLPips, InpUseFractalSL, InpSLBufferMode]
|
||||
tp: [InpTPType, InpRRRatio, InpFixedTPPips, InpUseFractalFilter]
|
||||
exit_trail: [InpUseTrailing, InpTrailStartPips, InpTrailStepPips]
|
||||
exit_be: [InpUseBreakeven, InpBEPips, InpBEBufferPips]
|
||||
filter_session: [InpUseSession, InpSessionStart, InpSessionEnd]
|
||||
filter_ema: [InpUseEMA, InpEMAPeriod, InpRequireEMASlope, InpEMASlopeBars]
|
||||
filter_spread: [InpUseSpreadGuard, InpMaxSpreadPips]
|
||||
filter_rr: [InpUseMinRR, InpMinRRRatio]
|
||||
filter_score: [InpUseScoreGate, InpMinScore]
|
||||
entry: [InpEntryMode]
|
||||
atr: [InpATRPeriod, InpATRMultiplier]
|
||||
tester: [InpTesterMode, InpTesterInitDeposit, InpTesterSpreadPts, InpShowPanel]
|
||||
Reference in New Issue
Block a user