{"id":257260,"date":"2026-09-14T11:36:08","date_gmt":"2026-09-14T09:36:08","guid":{"rendered":"https:\/\/greaterwaves.com\/?page_id=257260"},"modified":"2026-09-15T16:47:05","modified_gmt":"2026-09-15T14:47:05","slug":"prop-firm-backtest-simulator","status":"publish","type":"page","link":"https:\/\/greaterwaves.com\/es\/prop-firm-backtest-simulator\/","title":{"rendered":"Prop Firm Challenge Calculator"},"content":{"rendered":"<div class=\"et_pb_section_0 et_pb_section et_section_regular et_flex_section\">\n<div class=\"et_pb_row_0 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_0 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_heading_0 et_pb_heading et_pb_module et_flex_module\"><div class=\"et_pb_heading_container\"><h1 class=\"et_pb_module_header\">Prop Firm Backtest Simulator<\/h1><\/div><\/div>\n\n<div class=\"et_pb_text_0 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><p>Buying a prop firm challenge is a bet with a capped downside and an uncapped-looking upside \u2014 but most tools that estimate your odds do it with a formula, assuming your strategy behaves like a textbook random walk. Real markets don't. A real losing streak clusters in ways a closed-form model can't see, and a mandatory hold period before your first payout can quietly give back profit a formula never accounted for.<\/p>\n<p>This prop firm backtest simulator skips the formula. Upload your own MT5 Strategy Tester report, and it replays every real historical starting point in your trade history against the challenge's actual rules \u2014 phase targets, drawdown, daily loss limits, payout timing \u2014 to show you what would genuinely have happened, not what a model predicts should happen.<\/p>\n<p>It takes two inputs \u2014 your strategy's trading statistics and a prop firm's rules \u2014 and returns what actually matters before you pay for an evaluation: your probability of passing, your expected value per attempt, your ROI, and roughly how long it will take.<\/p>\n<\/div><\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_row_1 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_1 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_heading_1 et_pb_heading et_pb_module et_flex_module\"><div class=\"et_pb_heading_container\"><h2 class=\"et_pb_module_header\">How It Works<\/h2><\/div><\/div>\n\n<div class=\"et_pb_text_1 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><p>Upload your MT5 Strategy Tester .html export \u2014 it's read entirely in your browser and never leaves your device. The tool normalizes every trade to a risk-adjusted unit (a multiple of what you actually risked per trade), then confirms the risk % it detected from your backtest's inputs.<\/p>\n<p>From there, it runs a walk-forward simulation: for every single real day your history offers as a starting point, it replays your actual subsequent trades forward through the challenge's rules \u2014 evaluation targets, drawdown, daily loss limit, minimum trading days \u2014 and into the funded stage, including the mandatory hold period before a first payout. Starting points without enough real trading history ahead of them to reach a resolution are discarded, never filled in with invented data.<\/p>\n<p>The result is a real distribution built entirely from your own trading, not an assumption about how markets behave.<\/p>\n<\/div><\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_row_2 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_2 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_heading_2 et_pb_heading et_pb_module et_flex_module\"><div class=\"et_pb_heading_container\"><h2 class=\"et_pb_module_header\">How to Read Your Results<\/h2><\/div><\/div>\n\n<div class=\"et_pb_text_2 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><p><strong>P(pass evaluation)<\/strong> \u2014 the share of your own real historical starting points that would have reached the funded stage.<\/p>\n<p><strong>Median outcome<\/strong> \u2014 the typical real net result (payouts + refund \u2212 price) across all usable starting points. More reliable than the average, which a handful of unusually strong stretches can pull upward.<\/p>\n<p><strong>Effective sample size<\/strong> \u2014 how many genuinely independent tests your history actually provides, correcting for the fact that nearby starting points overlap and aren't separate evidence. A low number is a real caution flag, not a technicality.<\/p>\n<p><strong>How your attempts typically played out<\/strong> \u2014 the split between never funded, funded but never paid out, and funded with at least one payout.<\/p>\n<p><strong>Economics per attempt<\/strong> \u2014 expected value, average and largest payout, and a couple of real-data honesty checks: the worst actual run of consecutive failed attempts in your history (no independence assumption), and what share of funded accounts were still active \u2014 not failed \u2014 when your data or horizon simply ran out.<\/p>\n<p><strong>Real distribution table<\/strong> \u2014 worst case through best case, each figure a real outcome from an actual day in your history, not a projection.<\/p>\n<\/div><\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_row_3 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_3 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_code_0 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\"><!--\r\n  Prop Firm Backtest Simulator \u2014 \u00a9 GreaterWaves (greaterwaves.com)\r\n  Licensed under the MIT License.\r\n\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy\r\n  of this software and associated documentation files, to deal in the Software\r\n  without restriction, including the rights to use, copy, modify, merge,\r\n  publish, distribute, sublicense, and\/or sell copies of the Software, subject\r\n  to the following condition: the above copyright notice and this permission\r\n  notice shall be included in all copies or substantial portions of the Software.\r\n\r\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r\n-->\r\n<style>\r\n\/* GreaterWaves brand palette \u2014 same tokens as the Risk Desk calculator *\/\r\n:root{\r\n  --bg:#0B1220; --bg-alt:#0F172A; --surface:#020617; --surface-2:#0B1220; --surface-3:#0F172A;\r\n  --text:#CBD5E1; --text-secondary:#94A3B8; --text-muted:#64748B; --text-strong:#F8FAFC;\r\n  --border: rgba(100,116,139,.28);\r\n  --accent:#14B8A6; --accent-hover:#2DD4BF; --accent-soft: rgba(20,184,166,.14); --accent-contrast:#0B1220;\r\n  --good:#22C55E; --good-soft: rgba(34,197,94,.14);\r\n  --warn:#F59E0B; --warn-soft: rgba(245,158,11,.14);\r\n  --bad:#EF4444; --bad-soft: rgba(239,68,68,.14);\r\n  --shadow: 0 12px 32px -14px rgba(0,0,0,.6);\r\n}\r\n*{box-sizing:border-box;}\r\nbody{background:var(--bg); color:var(--text); font-family:'Inter',system-ui,-apple-system,sans-serif; font-size:14.5px; line-height:1.5;}\r\nh1,h2,h3{font-weight:600; letter-spacing:-.01em; text-wrap:balance; margin:0; color:var(--text-strong);}\r\n.mono, input, select, .num{font-variant-numeric:tabular-nums;}\r\n.wrap{max-width:920px; margin:0 auto; padding:28px 20px 64px;}\r\n\r\nheader.top{margin-bottom:22px;}\r\nheader.top h2.widget-title{font-size:24px;}\r\nheader.top .tag{color:var(--text-secondary); font-size:13px; max-width:60ch; margin-top:4px;}\r\n\r\n.stack{display:flex; flex-direction:column; gap:16px;}\r\n.step-num{display:inline-flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:50%;\r\n  background:var(--accent-soft); color:var(--accent); font-size:11px; font-weight:700; margin-right:8px; flex:none;}\r\n\r\n.panel{background:var(--surface); border:1px solid var(--border); border-radius:10px; box-shadow:var(--shadow); padding:16px 18px;}\r\n.panel > h2{font-size:15px; display:flex; align-items:center; margin-bottom:14px;}\r\n.panel > .subtext{font-size:12.5px; color:var(--text-muted); margin:-8px 0 14px; padding-left:28px;}\r\n\r\n.dropzone{border:1.5px dashed var(--border); border-radius:10px; padding:32px 20px; text-align:center; cursor:pointer; background:var(--surface-2); transition:.15s;}\r\n.dropzone:hover{border-color:var(--accent);}\r\n.dropzone.drag{border-color:var(--accent); background:var(--accent-soft);}\r\n.dropzone .big{font-size:14px; color:var(--text); margin-bottom:4px;}\r\n.dropzone .small{font-size:12px; color:var(--text-muted);}\r\ninput[type=file]{display:none;}\r\n\r\n.field{display:grid; grid-template-columns:1fr 110px; gap:10px; align-items:center; padding:6px 0;}\r\n.field label{font-size:12.5px; color:var(--text);}\r\n.field input, .field select{\r\n  width:100%; background:var(--surface-2); border:1px solid var(--border); border-radius:6px;\r\n  padding:6px 8px; color:var(--text); font-size:13px; text-align:right;\r\n}\r\n.field select{text-align:left;}\r\n.radiofield{display:flex; align-items:center; gap:8px; padding:4px 0; font-size:13px;}\r\n\r\n.btnrow{display:flex; gap:8px; margin-top:14px; flex-wrap:wrap;}\r\nbutton{background:var(--surface-2); border:1px solid var(--border); color:var(--text); border-radius:7px; padding:9px 15px; font-size:13px; cursor:pointer; font-family:inherit;}\r\nbutton:hover{border-color:var(--accent); color:var(--text-strong);}\r\nbutton:focus-visible{outline:2px solid var(--accent); outline-offset:1px;}\r\nbutton.primary{background:var(--accent); border-color:var(--accent); color:var(--accent-contrast); font-weight:600;}\r\nbutton.primary:hover{background:var(--accent-hover); border-color:var(--accent-hover);}\r\n\r\n.summary{display:grid; grid-template-columns:repeat(4,1fr); gap:12px;}\r\n@media (max-width:640px){ .summary{grid-template-columns:repeat(2,1fr);} }\r\n.tile{background:var(--surface); border:1px solid var(--border); border-radius:10px; box-shadow:var(--shadow); padding:14px 14px 12px;}\r\n.tile .lbl{font-size:10.5px; text-transform:uppercase; letter-spacing:.08em; color:var(--text-muted); margin-bottom:6px;}\r\n.tile .val{font-size:23px; font-weight:600; line-height:1; color:var(--text-strong);}\r\n.tile .sub{font-size:11.5px; color:var(--text-secondary); margin-top:5px;}\r\n.tile.good .val{color:var(--good);} .tile.bad .val{color:var(--bad);} .tile.warn .val{color:var(--warn);}\r\n\r\ntable{width:100%; border-collapse:collapse; font-size:12.5px;}\r\nthead tr{background:var(--surface-2);}\r\nth{text-align:left; font-size:10.5px; text-transform:uppercase; letter-spacing:.06em; color:var(--text-secondary); font-weight:700; padding:8px 10px; border-bottom:1px solid var(--border);}\r\ntd{padding:7px 10px; border-bottom:1px solid var(--border); color:var(--text);}\r\ntd.num, th.num{text-align:right;}\r\ntr:last-child td{border-bottom:none;}\r\n.tablewrap{overflow-x:auto;}\r\n\r\n.banner{padding:12px 14px; border-radius:8px; font-size:13px; margin-top:10px; border:1px solid var(--border);}\r\n.banner.good{background:var(--good-soft); color:var(--good);}\r\n.banner.bad{background:var(--bad-soft); color:var(--bad);}\r\n.banner.warn{background:var(--warn-soft); color:var(--warn);}\r\n.banner a{color:inherit; text-decoration:underline;}\r\n\r\n.kv{display:grid; grid-template-columns:1fr 1fr; gap:6px 18px; font-size:13px;}\r\n.kv div span{color:var(--text-muted);}\r\n.kv b{color:var(--text-strong);}\r\n\r\n.note{font-size:11.5px; color:var(--text-muted); margin-top:10px; line-height:1.5;}\r\n.cta-panel{text-align:center;}\r\n.cta-panel p{font-size:13.5px; color:var(--text); margin:0 0 12px; line-height:1.5;}\r\n.cta-link{color:var(--accent); font-weight:700; text-decoration:underline; font-size:14px;}\r\n.cta-link:hover{color:var(--accent-hover);}\r\n.foot{margin-top:26px; font-size:11.5px; color:var(--text-muted); text-align:center;}\r\n\r\n.verdict{display:flex; align-items:flex-start; gap:10px; padding:12px 14px; border-radius:9px; margin-bottom:14px; border:1px solid var(--border); font-size:13.5px; line-height:1.5;}\r\n.verdict.good{background:var(--good-soft); color:var(--text-strong);}\r\n.verdict.warn{background:var(--warn-soft); color:var(--text-strong);}\r\n.verdict.bad{background:var(--bad-soft); color:var(--text-strong);}\r\n.verdict .dot{width:9px; height:9px; border-radius:50%; margin-top:5px; flex:none;}\r\n.verdict.good .dot{background:var(--good);} .verdict.warn .dot{background:var(--warn);} .verdict.bad .dot{background:var(--bad);}\r\n\r\n.outcomebar{display:flex; height:26px; border-radius:6px; overflow:hidden; margin:10px 0 8px; border:1px solid var(--border);}\r\n.outcomebar .seg{display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:600; color:var(--accent-contrast); white-space:nowrap; overflow:hidden;}\r\n.outcomebar .seg.fail{background:var(--bad);} .outcomebar .seg.zero{background:var(--warn);} .outcomebar .seg.paid{background:var(--good);}\r\n.barlegend{display:flex; gap:16px; flex-wrap:wrap; font-size:12px; color:var(--text-secondary); margin-bottom:6px;}\r\n.barlegend span{display:inline-flex; align-items:center; gap:6px;}\r\n.barlegend i{width:9px; height:9px; border-radius:2px; display:inline-block;}\r\n.barlegend .fail i{background:var(--bad);} .barlegend .zero i{background:var(--warn);} .barlegend .paid i{background:var(--good);}\r\n\r\ndetails.explain{margin-top:10px; font-size:12.5px;}\r\ndetails.explain summary{cursor:pointer; color:var(--accent); list-style:none; display:inline-flex; align-items:center; gap:5px;}\r\ndetails.explain summary::-webkit-details-marker{display:none;}\r\ndetails.explain summary::before{content:'+'; font-weight:700; width:14px; text-align:center;}\r\ndetails.explain[open] summary::before{content:'\\2212';}\r\ndetails.explain .body{color:var(--text-secondary); line-height:1.55; margin-top:6px; padding-left:19px;}\r\n\r\n.histrow{display:grid; grid-template-columns:150px 1fr 46px; gap:10px; align-items:center; margin:6px 0; font-size:12px;}\r\n.histrow .hlabel{color:var(--text-secondary); text-align:right;}\r\n.histrow .htrack{background:var(--surface-2); border:1px solid var(--border); border-radius:4px; height:18px; overflow:hidden;}\r\n.histrow .hbar{height:100%; background:var(--accent); border-radius:3px 0 0 3px;}\r\n.histrow.loss .hbar{background:var(--bad);}\r\n.histrow .hpct{color:var(--text-strong); font-weight:600;}\r\n@media (max-width:560px){ .histrow{grid-template-columns:1fr; gap:2px;} .histrow .hlabel{text-align:left;} }\r\n\r\n@media print{\r\n  \/* Deliberately as simple as possible: one flat plain-paper look, no dark frame, no\r\n     per-card theming. Fewer moving pieces = fewer ways for this to go wrong again. *\/\r\n  @page{ margin:0; }\r\n  *{ background:#FFFFFF !important; color:#111111 !important; border-color:#CCCCCC !important;\r\n     box-shadow:none !important; text-shadow:none !important;\r\n     -webkit-print-color-adjust:exact !important; print-color-adjust:exact !important; }\r\n\r\n  .no-print{display:none !important;}\r\n\r\n  \/* Isolate the widget from the rest of the WordPress page it's embedded in\r\n     (theme header\/logo, nav, sidebar, footer) \u2014 we don't control that markup. *\/\r\n  *:not(:has(#gw-sim-widget)):not(#gw-sim-widget):not(#gw-sim-widget *){display:none !important;}\r\n  #gw-sim-widget{margin:0; padding:24px;}\r\n\r\n  .panel{border:1px solid #CCCCCC; break-inside:avoid;}\r\n  .panel + .panel{margin-top:14px;}\r\n\r\n  details.explain:not([open]) > *:not(summary){display:block !important;}\r\n  details.explain summary::before{content:none;}\r\n\r\n  \/* table-layout:fixed forces the table to respect 100% of its container no matter what\r\n     the content wants, instead of bleeding past the card edge. *\/\r\n  .tablewrap{overflow-x:visible !important;}\r\n  table{font-size:9.5px; table-layout:fixed; width:100%;}\r\n  th, td{padding:4px 5px; overflow-wrap:break-word;}\r\n\r\n  \/* Bars: no color, just a plain grey fill so the proportions are still visible at a\r\n     glance, not just as numbers next to an empty outline. *\/\r\n  .seg, .hbar{background:#AAAAAA !important;}\r\n  .seg.fail, .histrow.loss .hbar{background:#666666 !important;}\r\n  .dot{background:#111111 !important;}\r\n}\r\n<\/style>\r\n\r\n<div class=\"wrap\" id=\"gw-sim-widget\">\r\n  <header class=\"top\">\r\n    <h2 class=\"widget-title\" id=\"widgetTitle\">Prop Firm Backtest Simulator<\/h2>\r\n    <div class=\"tag\" id=\"widgetTag\">Upload your MT5 Strategy Tester report and see exactly what would have happened, replaying every real starting point in your own trade history.<\/div>\r\n  <\/header>\r\n\r\n  <div class=\"stack\">\r\n\r\n    <div class=\"panel no-print\">\r\n      <h2 id=\"step1Title\"><span class=\"step-num\">1<\/span> Upload your report<\/h2>\r\n      <div class=\"subtext\" id=\"step1Subtext\">MT5 Strategy Tester export (.html). Read entirely in your browser \u2014 the file is never sent anywhere.<\/div>\r\n      <div class=\"dropzone\" id=\"dropzone\">\r\n        <div class=\"big\" id=\"dzText\">Drag your report here, or click to choose a file<\/div>\r\n        <div class=\"small\" id=\"dzSmall\">.html only, any language<\/div>\r\n      <\/div>\r\n      <input type=\"file\" id=\"fileInput\" accept=\".html,.htm\">\r\n    <\/div>\r\n\r\n    <div id=\"output\"><\/div>\r\n  <\/div>\r\n\r\n  <div class=\"foot\" id=\"footNote\">Walk-forward simulation over your own historical trades. An estimate, not a guarantee \u2014 always check the firm's current rulebook.<\/div>\r\n<\/div>\r\n\r\n<script>\r\n\/* ============================================================\r\n   i18n \u2014 only strings the JS itself generates. Static HTML text\r\n   above is left in English for TranslatePress to translate.\r\n   ============================================================ *\/\r\nconst DICT = {\r\n  en: {\r\n    contact: `Get in touch at <a href=\"https:\/\/greaterwaves.com\/contact\" target=\"_blank\" rel=\"noopener\">greaterwaves.com\/contact<\/a> with this exact error message, so we can look into it.`,\r\n    errNoDeals: 'Could not read this file.',\r\n    errNoDealsDetail: 'The closed-trades table (with a Profit\/Beneficio and Balance column) could not be located.',\r\n    errCountTitle: 'Validation failed: trade count.',\r\n    errCountDetail: (declared, parsed, balanceRows) => `The report declares ${declared} deals, but ${parsed} were read (excluding ${balanceRows} opening-balance row(s)).`,\r\n    errCountNote: \"We don't proceed with partially-read data.\",\r\n    step1Title: 'Confirm the risk per trade',\r\n    detectedAs: (name, raw, pct) => `Detected in the Inputs: <b>${name} = ${raw}<\/b> \u2192 <b>${pct}%<\/b> of the account per trade.`,\r\n    implausible: pct => `This value (${pct}%) is unusual for a fixed risk-per-trade setting \u2014 double-check it before continuing.`,\r\n    yesCorrect: 'Yes, that\u2019s correct \u2014 continue',\r\n    noCorrect: 'Not correct \/ use another value',\r\n    multiplePrompt: 'Several inputs could be the risk-per-trade setting \u2014 pick the right one:',\r\n    noneOfThese: 'None of these \u2014 enter it manually',\r\n    manualValue: 'Manual value (%):',\r\n    confirmContinue: 'Confirm and continue',\r\n    noneDetected: 'No percent-of-equity risk input was detected in this report.',\r\n    manualLabel: 'If your EA uses a different name for it, enter it here (%):',\r\n    notApplicable: \"My backtest doesn\u2019t use fixed % risk sizing\",\r\n    unsuitableTitle: 'This backtest isn\u2019t suitable for the simulator.',\r\n    unsuitableBody: 'The simulator requires trades to be sized by a fixed % risk of equity \u2014 it\u2019s what prop firms require, and the only thing we can normalize reliably. Re-run the backtest with that sizing method and upload it again.',\r\n    step1ConfirmedTitle: 'Risk per trade',\r\n    riskConfirmedAs: pct => `Confirmed: <b>${pct}%<\/b> per trade.`,\r\n    changeRisk: 'Change confirmed risk',\r\n    summaryTitle: 'Detected summary',\r\n    lSymbol: 'Symbol', lPeriod: 'Period', lTotalTrades: 'Total Trades', lTotalDeals: 'Total Deals',\r\n    lDecimal: 'Decimal separator detected', lComma: 'comma', lDot: 'dot',\r\n    previewTitle: 'Preview \u2014 first 10 closed trades',\r\n    hDate:'Date\/Time', hSymbol:'Symbol', hType:'Type', hProfit:'Profit', hBalance:'Balance before', hUnits:'Result (risk units)',\r\n    parseOk: deals => `\u2713 Parsed and validated \u2014 ${deals} closed trades match the report\u2019s own \"Total Deals\" figure exactly.`,\r\n    strategyTitle: 'Your strategy, as we\u2019ll use it',\r\n    sWinRate: 'Win rate', sAvgWin: 'Average win', sAvgLoss: 'Average loss', sPF: 'Profit factor', sUnits: 'risk units',\r\n    sNote: 'Calculated from every trade weighted equally in risk-adjusted terms \u2014 not the raw \u20ac figures in your report, which can be skewed by how much your equity grew during the backtest.',\r\n    step2Title: 'Challenge rules', step3Title: 'Price & payout economics',\r\n    lSimRisk: 'Risk per trade during evaluation (%)',\r\n    lSimRiskFunded: 'Risk per trade once funded (%)',\r\n    explainSimRisk: 'Simulating at a different risk than the backtest?',\r\n    explainSimRiskBody: 'We rescale every trade\\'s \u20ac result in proportion to this value versus your confirmed backtest risk \u2014 this is mathematically equivalent to how MT5 position sizing itself scales profit and loss, so it\\'s a solid estimate. You can set a different risk for the funded stage than for evaluation \u2014 many traders size down once funded, to protect the account and the payouts already at stake. Two things this rescaling can\\'t capture: (1) broker lot-size rounding (e.g. 0.01 lots), which matters more at very low risk % or small accounts; (2) margin\/leverage limits \u2014 at very high risk % the position size implied may exceed what your account could actually open. Treat results far from your confirmed risk with extra caution.',\r\n    lTargets: 'Phase targets (%, comma-separated \u2014 e.g. \"10,5\" for 2 phases)',\r\n    lMaxDD: 'Max drawdown (%)', lDDType: 'Drawdown type', ddStatic: 'Static', ddDynamic: 'Dynamic (closed trades)',\r\n    lDailyLimit: 'Daily loss limit (%, 0 = not applied)', lEodHour: 'Daily reset offset (hours, in the report\u2019s own timezone)',\r\n    lMinDays: 'Minimum trading days (0 = not applied)', lMinDaysPayout: 'Min. days to 1st payout',\r\n    lPayoutCycle: 'Payout cycle (days)', lHorizon: 'Simulation horizon (months)',\r\n    lBalance: 'Account balance (\u20ac)', lBasePrice: 'Base challenge price (\u20ac)', lDiscount: 'Discount (%)',\r\n    lSplit: 'Profit split (%)', lRefund: '1st-payout refund (%, 0 = none)',\r\n    runBtn: 'Run simulation (walk-forward)',\r\n    resTitle: 'Result \u2014 walk-forward',\r\n    exportBtn: '\ud83d\udcc4 Export as PDF',\r\n    tUsablePoints: 'Usable starting points', tEffN: 'Effective sample size (approx.)',\r\n    tPassRate: 'P(pass evaluation)', tPmedian: 'Median outcome',\r\n    lowSample: 'Very small effective sample \u2014 interpret these results with a lot of caution. You need a longer backtest or a shorter horizon.',\r\n    econTitle: 'Economics per attempt (\u20ac)',\r\n    tFundedZero: 'Funded, never paid out', tFundedSome: 'Funded, \u22651 payout', tAvgPayouts: 'Avg. payouts per funded account',\r\n    tAvgPayoutSize: 'Avg. payout size', tMaxPayoutSize: 'Largest single payout observed',\r\n    tAvgDaysToPass: 'Avg. time to pass evaluation', tAvgDaysFunded: 'Avg. time spent funded',\r\n    fmtDays: days => days < 60 ? `${Math.round(days)} days` : `${(days\/30).toFixed(1)} months`,\r\n    tWorstStreak: 'Worst real run of failed attempts in your history',\r\n    worstStreakVal: (count, cap, costE) => count===0 ? 'None \u2014 evaluation always passed' : `${count}${count>=cap?'+':''} in a row (${costE})`,\r\n    worstStreakNote: 'Found by chaining real failures together: whenever an evaluation attempt failed on a specific real day in your history, we immediately started the next attempt on the next real trading day after that \u2014 exactly what re-buying and trying again right away would look like \u2014 and tracked how many times in a row that actually happened anywhere in your data. Unlike the average above, this uses no independence assumption \u2014 it\\'s what really happened in your worst historical stretch.',\r\n    tStillActive: 'Funded accounts still active when the segment ended',\r\n    stillActiveNote: 'These funded accounts hadn\u2019t failed \u2014 they were still trading when either your historical data or the simulation horizon ran out. Payouts they\u2019d already collected by then are included in the result, but any profit sitting in an in-progress payout cycle at that cutoff point isn\u2019t \u2014 it\\'s treated as zero rather than guessed at.',\r\n    tPrice: 'Final attempt price', tEVmean: 'EV per attempt (mean)', tPosRate: '% of attempts with a positive outcome',\r\n    distTitle: n => `Real distribution (over the ${n} usable starting points)`,\r\n    hWorst:'Worst case', hP10:'P10', hP25:'P25', hMedian:'Median', hP75:'P75', hP90:'P90', hBest:'Best case',\r\n    distNote: 'Each figure is the real net outcome (payouts + refund \u2212 price) of having actually started the challenge on that specific day in your history \u2014 not a projection, it\u2019s what really would have happened at each usable starting point.',\r\n    verdict: (passPct, medianE, priceE) => `Based on your own trading history: repeating this challenge many times, you\u2019d have passed about <b>${passPct}%<\/b> of attempts, and the typical (median) result was <b>${medianE}<\/b> \u2014 though a meaningful share of attempts would have simply lost the <b>${priceE}<\/b> entry cost.`,\r\n    tExpectedSpend: 'Est. total spent before passing (on average)', tExpectedAttempts: 'Est. attempts needed (on average)',\r\n    expectedSpendNote: 'Estimated from your pass rate above, assuming you buy a new attempt each time one fails: on average it took about this many attempts, and this much total entry cost, before one finally passed. This assumes each attempt is an independent trial, which is a simplification \u2014 real consecutive attempts happen back-to-back in the same market conditions, so treat this as a rough guide, not an exact figure.',\r\n    outcomeTitle: 'How your attempts typically played out',\r\n    legFail: 'Never funded', legZero: 'Funded, never paid out', legPaid: 'Funded, paid out',\r\n    explainSample: 'What does \"effective sample size\" mean?',\r\n    explainSampleBody: 'This counts how many truly independent examples your own trading history actually provides. Starting dates that are close together overlap almost completely \u2014 they trade through mostly the same days \u2014 so they don\u2019t count as separate evidence, only genuinely distinct stretches of history do. A low number here means: be cautious, your own track record hasn\u2019t yet given this enough truly different real-world tests.',\r\n    explainPct: 'What do P10, P25, Median mean?',\r\n    explainPctBody: 'These are your own real results, sorted from worst to best, then read off at different points. \"P10\" means: in the worst 10% of cases across your history, you ended up with this result or worse. \"Median\" is the middle result \u2014 half your history\u2019s attempts did better, half did worse. A handful of especially strong stretches can pull an average far above what\u2019s typical, so the median is usually the better guide to \"what would probably happen.\"',\r\n    evNote: 'This average is pulled up by a handful of especially strong stretches in your own history \u2014 the median result above is a more typical single outcome.',\r\n    explainHorizon: 'What is the \"simulation horizon\"?',\r\n    explainHorizonBody: hint => `This is how far into the future we test from each starting day in your history \u2014 long enough to pass the challenge and go through a few payout cycles. Longer horizons need more real trading days ahead of each starting point to test against, so a very long horizon can leave you with fewer usable starting points (and a lower \"effective sample size\" above). ${hint} If unsure, start around 6 months, run the simulation, and shorten it if too many starting points get discarded.`,\r\n    horizonHint: (years, suggestedLow, suggestedHigh) => `Your uploaded backtest covers about <b>${years} years<\/b> of trading \u2014 a horizon of roughly <b>${suggestedLow}\u2013${suggestedHigh} months<\/b> is a reasonable starting point.`,\r\n    histTitle: 'How outcomes were spread out',\r\n    bucketLost: 'Lost the entry cost',\r\n    ofAttempts: '% of attempts',\r\n    ctaBody: 'If this made it clear you don\u2019t yet have a solid validation process before paying for a challenge, our free course covers exactly that.',\r\n    ctaBtn: 'Start the free course \u2192',\r\n    widgetTitle: 'Prop Firm Backtest Simulator',\r\n    widgetTag: 'Upload your MT5 Strategy Tester report and see exactly what would have happened, replaying every real starting point in your own trade history.',\r\n    step1UploadTitle: 'Upload your report',\r\n    step1UploadSubtext: 'MT5 Strategy Tester export (.html). Read entirely in your browser \u2014 the file is never sent anywhere.',\r\n    dzBig: 'Drag your report here, or click to choose a file',\r\n    dzSmall: '.html only \u2014 English or Spanish reports',\r\n    footNoteText: \"Walk-forward simulation over your own historical trades. An estimate, not a guarantee \u2014 always check the firm's current rulebook.\",\r\n  },\r\n  es: {\r\n    contact: `Escr\u00edbenos en <a href=\"https:\/\/greaterwaves.com\/contact\" target=\"_blank\" rel=\"noopener\">greaterwaves.com\/contact<\/a> indicando este mensaje de error exacto, para que podamos revisarlo.`,\r\n    errNoDeals: 'No se pudo leer este archivo.',\r\n    errNoDealsDetail: 'No se encontr\u00f3 la tabla de operaciones cerradas (con columna Profit\/Beneficio y Balance).',\r\n    errCountTitle: 'Validaci\u00f3n fallida: recuento de operaciones.',\r\n    errCountDetail: (declared, parsed, balanceRows) => `El informe declara ${declared} transacciones, pero se han le\u00eddo ${parsed} (excluyendo ${balanceRows} fila(s) de apertura de balance).`,\r\n    errCountNote: 'No continuamos con datos parcialmente le\u00eddos.',\r\n    step1Title: 'Confirma el riesgo por operaci\u00f3n',\r\n    detectedAs: (name, raw, pct) => `Detectado en los Inputs: <b>${name} = ${raw}<\/b> \u2192 <b>${pct}%<\/b> de la cuenta por operaci\u00f3n.`,\r\n    implausible: pct => `Este valor (${pct}%) es inusual para un riesgo fijo por operaci\u00f3n \u2014 vuelve a comprobarlo antes de continuar.`,\r\n    yesCorrect: 'S\u00ed, es correcto \u2014 continuar',\r\n    noCorrect: 'No es correcto \/ usar otro valor',\r\n    multiplePrompt: 'Se han detectado varios inputs que podr\u00edan ser el riesgo por operaci\u00f3n \u2014 elige el correcto:',\r\n    noneOfThese: 'Ninguna de estas \u2014 indicar manualmente',\r\n    manualValue: 'Valor manual (%):',\r\n    confirmContinue: 'Confirmar y continuar',\r\n    noneDetected: 'No se detect\u00f3 ning\u00fan input de riesgo por % de equity en este informe.',\r\n    manualLabel: 'Si tu EA lo usa bajo otro nombre, ind\u00edcalo aqu\u00ed (%):',\r\n    notApplicable: 'Mi backtest no usa % de riesgo fijo',\r\n    unsuitableTitle: 'Este backtest no es apto para el simulador.',\r\n    unsuitableBody: 'El simulador requiere que las operaciones est\u00e9n dimensionadas por % fijo de riesgo sobre la equity \u2014 es lo que exigen las prop firms y lo \u00fanico que podemos normalizar con fiabilidad. Vuelve a correr el backtest con ese m\u00e9todo de sizing y s\u00fabelo de nuevo.',\r\n    step1ConfirmedTitle: 'Riesgo por operaci\u00f3n',\r\n    riskConfirmedAs: pct => `Confirmado: <b>${pct}%<\/b> por operaci\u00f3n.`,\r\n    changeRisk: 'Cambiar el riesgo confirmado',\r\n    summaryTitle: 'Resumen detectado',\r\n    lSymbol: 'S\u00edmbolo', lPeriod: 'Periodo', lTotalTrades: 'Total de operaciones', lTotalDeals: 'Total de transacciones',\r\n    lDecimal: 'Separador decimal detectado', lComma: 'coma', lDot: 'punto',\r\n    previewTitle: 'Vista previa \u2014 primeras 10 operaciones cerradas',\r\n    hDate:'Fecha\/Hora', hSymbol:'S\u00edmbolo', hType:'Tipo', hProfit:'Beneficio', hBalance:'Balance antes', hUnits:'Resultado (unidades de riesgo)',\r\n    parseOk: deals => `\u2713 Parseo correcto y validado \u2014 ${deals} operaciones cerradas coinciden exactamente con el \"Total Deals\" del informe.`,\r\n    strategyTitle: 'Tu estrategia, tal como la vamos a usar',\r\n    sWinRate: 'Win rate', sAvgWin: 'Ganancia media', sAvgLoss: 'P\u00e9rdida media', sPF: 'Profit factor', sUnits: 'unidades de riesgo',\r\n    sNote: 'Calculado dando el mismo peso a cada operaci\u00f3n en t\u00e9rminos ajustados por riesgo \u2014 no los \u20ac crudos de tu informe, que pueden estar sesgados por cu\u00e1nto creci\u00f3 tu equity durante el backtest.',\r\n    step2Title: 'Reglas del reto', step3Title: 'Precio y econom\u00eda',\r\n    lSimRisk: 'Riesgo por operaci\u00f3n en evaluaci\u00f3n (%)',\r\n    lSimRiskFunded: 'Riesgo por operaci\u00f3n una vez fondeado (%)',\r\n    explainSimRisk: '\u00bfSimular con un riesgo distinto al del backtest?',\r\n    explainSimRiskBody: 'Reescalamos el resultado en \u20ac de cada operaci\u00f3n en proporci\u00f3n a este valor frente al riesgo confirmado de tu backtest \u2014 esto es matem\u00e1ticamente equivalente a c\u00f3mo el propio dimensionamiento de posici\u00f3n de MT5 escala la ganancia y la p\u00e9rdida, as\u00ed que es una buena estimaci\u00f3n. Puedes fijar un riesgo distinto para la fase fondeada que para la evaluaci\u00f3n \u2014 muchos traders reducen el riesgo al fondear, para proteger la cuenta y los retiros ya en juego. Hay dos cosas que este reescalado no puede capturar: (1) el redondeo del lote m\u00ednimo del br\u00f3ker (p. ej. 0.01 lotes), que pesa m\u00e1s con riesgos % muy bajos o cuentas peque\u00f1as; (2) los l\u00edmites de margen\/apalancamiento \u2014 a riesgos % muy altos, el tama\u00f1o de posici\u00f3n impl\u00edcito podr\u00eda superar lo que tu cuenta realmente podr\u00eda abrir. Trata con m\u00e1s cautela los resultados alejados de tu riesgo confirmado.',\r\n    lTargets: 'Objetivos de fase (%, separados por coma \u2014 ej. \"10,5\" para 2 fases)',\r\n    lMaxDD: 'Drawdown m\u00e1ximo (%)', lDDType: 'Tipo de drawdown', ddStatic: 'Est\u00e1tico', ddDynamic: 'Din\u00e1mico (closed trades)',\r\n    lDailyLimit: 'L\u00edmite de p\u00e9rdida diaria (%, 0 = no aplicar)', lEodHour: 'Hora de reset diario (offset en horas, zona del informe)',\r\n    lMinDays: 'D\u00edas m\u00ednimos de trading (0 = no aplicar)', lMinDaysPayout: 'D\u00edas m\u00ednimos al 1er retiro',\r\n    lPayoutCycle: 'Ciclo de retiros (d\u00edas)', lHorizon: 'Horizonte de la simulaci\u00f3n (meses)',\r\n    lBalance: 'Balance de la cuenta (\u20ac)', lBasePrice: 'Precio base del reto (\u20ac)', lDiscount: 'Descuento (%)',\r\n    lSplit: 'Reparto de beneficios (%)', lRefund: 'Refund al 1er retiro (%, 0 = ninguno)',\r\n    runBtn: 'Ejecutar simulaci\u00f3n (walk-forward)',\r\n    resTitle: 'Resultado \u2014 walk-forward',\r\n    exportBtn: '\ud83d\udcc4 Exportar a PDF',\r\n    tUsablePoints: 'Puntos de partida usables', tEffN: 'Tama\u00f1o de muestra efectivo (aprox.)',\r\n    tPassRate: 'P(pasar evaluaci\u00f3n)', tPmedian: 'Resultado mediano',\r\n    lowSample: 'Muestra efectiva muy peque\u00f1a \u2014 interpreta estos resultados con mucha cautela. Necesitas un backtest m\u00e1s largo o un horizonte m\u00e1s corto.',\r\n    econTitle: 'Econom\u00eda por intento (\u20ac)',\r\n    tFundedZero: 'Fondeadas sin cobrar nunca', tFundedSome: 'Fondeadas con \u22651 retiro', tAvgPayouts: 'Retiros medios por cuenta fondeada',\r\n    tAvgPayoutSize: 'Tama\u00f1o medio del retiro', tMaxPayoutSize: 'Mayor retiro observado',\r\n    tAvgDaysToPass: 'Tiempo medio en pasar la evaluaci\u00f3n', tAvgDaysFunded: 'Tiempo medio fondeada',\r\n    fmtDays: days => days < 60 ? `${Math.round(days)} d\u00edas` : `${(days\/30).toFixed(1)} meses`,\r\n    tWorstStreak: 'Peor racha real de intentos fallidos en tu historial',\r\n    worstStreakVal: (count, cap, costE) => count===0 ? 'Ninguna \u2014 la evaluaci\u00f3n siempre se pas\u00f3' : `${count}${count>=cap?'+':''} seguidos (${costE})`,\r\n    worstStreakNote: 'Se calcula encadenando fallos reales: cada vez que un intento de evaluaci\u00f3n fall\u00f3 en un d\u00eda concreto de tu historial, arrancamos el siguiente intento en el siguiente d\u00eda real de trading tras ese fallo \u2014 justo lo que pasar\u00eda si recompraras e intentaras de nuevo enseguida \u2014 y contamos cu\u00e1ntas veces seguidas ocurri\u00f3 esto de verdad en alg\u00fan punto de tus datos. A diferencia de la media de arriba, esto no asume independencia \u2014 es lo que realmente pas\u00f3 en tu peor tramo hist\u00f3rico.',\r\n    tStillActive: 'Cuentas fondeadas a\u00fan activas al terminar el segmento',\r\n    stillActiveNote: 'Estas cuentas fondeadas no hab\u00edan fallado \u2014 segu\u00edan operando cuando se acab\u00f3 tu hist\u00f3rico o el horizonte de simulaci\u00f3n. Los retiros que ya hab\u00edan cobrado hasta ese punto s\u00ed se incluyen en el resultado, pero el beneficio acumulado en un ciclo de retiro todav\u00eda en curso en ese corte no se incluye \u2014 se trata como cero en vez de estimarlo.',\r\n    tPrice: 'Precio final del intento', tEVmean: 'EV por intento (media)', tPosRate: '% de intentos con resultado positivo',\r\n    distTitle: n => `Distribuci\u00f3n real (sobre los ${n} puntos de partida usables)`,\r\n    hWorst:'Peor caso', hP10:'P10', hP25:'P25', hMedian:'Mediana', hP75:'P75', hP90:'P90', hBest:'Mejor caso',\r\n    distNote: 'Cada valor es el resultado neto real (retiros + refund \u2212 precio) de haber empezado el reto justo ese d\u00eda concreto del hist\u00f3rico \u2014 no es una proyecci\u00f3n, es lo que habr\u00eda pasado de verdad en cada punto de partida usable.',\r\n    verdict: (passPct, medianE, priceE) => `Seg\u00fan tu propio historial de operaciones: repitiendo este reto muchas veces, habr\u00edas pasado alrededor del <b>${passPct}%<\/b> de los intentos, y el resultado t\u00edpico (mediana) fue <b>${medianE}<\/b> \u2014 aunque una parte significativa de los intentos habr\u00eda perdido simplemente el coste de entrada de <b>${priceE}<\/b>.`,\r\n    tExpectedSpend: 'Gasto total estimado hasta pasar (de media)', tExpectedAttempts: 'Intentos estimados necesarios (de media)',\r\n    expectedSpendNote: 'Estimado a partir de tu tasa de aciertos de arriba, asumiendo que compras un nuevo intento cada vez que falla: de media hicieron falta unos esos intentos, y ese coste de entrada total, hasta que uno por fin pas\u00f3. Esto asume que cada intento es una prueba independiente, lo cual es una simplificaci\u00f3n \u2014 tus reintentos reales ocurren uno tras otro en las mismas condiciones de mercado, as\u00ed que tr\u00e1talo como una gu\u00eda aproximada, no una cifra exacta.',\r\n    outcomeTitle: 'C\u00f3mo suelen ir tus intentos',\r\n    legFail: 'Nunca fondea', legZero: 'Fondea, nunca cobra', legPaid: 'Fondea y cobra',\r\n    explainSample: '\u00bfQu\u00e9 significa \"tama\u00f1o de muestra efectivo\"?',\r\n    explainSampleBody: 'Cuenta cu\u00e1ntos ejemplos verdaderamente independientes te da tu propio historial de operaciones. Los puntos de partida cercanos entre s\u00ed se solapan casi por completo \u2014 atraviesan casi los mismos d\u00edas \u2014 as\u00ed que no cuentan como evidencia separada, solo los tramos de historial genuinamente distintos cuentan. Un n\u00famero bajo aqu\u00ed significa: ten cautela, tu propio hist\u00f3rico todav\u00eda no ha pasado por suficientes pruebas realmente distintas.',\r\n    explainPct: '\u00bfQu\u00e9 significan P10, P25, Mediana?',\r\n    explainPctBody: 'Son tus propios resultados reales, ordenados de peor a mejor, y le\u00eddos en distintos puntos. \"P10\" significa: en el 10% de casos peores de tu historial, acabaste con este resultado o peor. La \"Mediana\" es el resultado del medio \u2014 la mitad de los intentos de tu historial salieron mejor, la mitad peor. Un pu\u00f1ado de tramos especialmente buenos pueden inflar una media muy por encima de lo t\u00edpico, as\u00ed que la mediana suele ser mejor gu\u00eda de \"qu\u00e9 pasar\u00eda probablemente.\"',\r\n    evNote: 'Esta media est\u00e1 inflada por un pu\u00f1ado de tramos especialmente buenos de tu propio historial \u2014 el resultado mediano de arriba es un caso individual m\u00e1s t\u00edpico.',\r\n    explainHorizon: '\u00bfQu\u00e9 es el \"horizonte de simulaci\u00f3n\"?',\r\n    explainHorizonBody: hint => `Es hasta cu\u00e1ndo simulamos hacia adelante desde cada d\u00eda de partida de tu historial \u2014 lo suficiente para pasar el reto y completar varios ciclos de retiro. Horizontes m\u00e1s largos necesitan m\u00e1s d\u00edas reales de trading por delante de cada punto de partida, as\u00ed que un horizonte muy largo puede dejarte con menos puntos de partida usables (y un \"tama\u00f1o de muestra efectivo\" m\u00e1s bajo, arriba). ${hint} Si no est\u00e1s seguro, empieza con unos 6 meses, ejecuta la simulaci\u00f3n, y ac\u00f3rtalo si se descartan demasiados puntos de partida.`,\r\n    horizonHint: (years, suggestedLow, suggestedHigh) => `Tu informe subido cubre unos <b>${years} a\u00f1os<\/b> de trading \u2014 un horizonte de aproximadamente <b>${suggestedLow}\u2013${suggestedHigh} meses<\/b> es un buen punto de partida.`,\r\n    histTitle: 'C\u00f3mo se repartieron los resultados',\r\n    bucketLost: 'Pierde el precio de entrada',\r\n    ofAttempts: '% de intentos',\r\n    ctaBody: 'Si esto te ha hecho ver que a\u00fan no tienes un proceso de validaci\u00f3n s\u00f3lido antes de pagar por un challenge, el curso gratuito cubre exactamente ese proceso.',\r\n    ctaBtn: 'Empieza el curso gratuito \u2192',\r\n    widgetTitle: 'Simulador de Backtest para Prop Firms',\r\n    widgetTag: 'Sube tu informe de MT5 Strategy Tester y descubre exactamente qu\u00e9 habr\u00eda pasado, repitiendo cada punto de partida real de tu propio historial de operaciones.',\r\n    step1UploadTitle: 'Sube tu informe',\r\n    step1UploadSubtext: 'Exportaci\u00f3n de MT5 Strategy Tester (.html). Se lee enteramente en tu navegador \u2014 el archivo nunca se env\u00eda a ning\u00fan sitio.',\r\n    dzBig: 'Arrastra tu informe aqu\u00ed, o haz clic para elegir un archivo',\r\n    dzSmall: '.html \u00fanicamente \u2014 informes en ingl\u00e9s o espa\u00f1ol',\r\n    footNoteText: 'Simulaci\u00f3n walk-forward sobre tu propio historial de operaciones. Una estimaci\u00f3n, no una garant\u00eda \u2014 comprueba siempre el reglamento vigente de la prop firm.',\r\n  }\r\n};\r\nconst COURSE_URL = {\r\n  en: 'https:\/\/greaterwaves.com\/free-algorithmic-trading-course-registration\/',\r\n  es: 'https:\/\/greaterwaves.com\/es\/free-algorithmic-trading-course-registration\/',\r\n};\r\nfunction currentLang(){\r\n  const segments = location.pathname.toLowerCase().split('\/').filter(Boolean);\r\n  if (segments.includes('es')) return 'es';\r\n  const attr = (document.documentElement.lang || '').toLowerCase();\r\n  if (attr.startsWith('es')) return 'es';\r\n  return 'en';\r\n}\r\nfunction t(){ return DICT[currentLang()] || DICT.en; }\r\n\r\n\/* ============================================================\r\n   1) ENCODING DETECTION + DECODE\r\n   ============================================================ *\/\r\nfunction decodeSmart(arrayBuffer){\r\n  const bytes = new Uint8Array(arrayBuffer);\r\n  let encoding = 'utf-8', offset = 0;\r\n  if (bytes[0]===0xFF && bytes[1]===0xFE){ encoding='utf-16le'; offset=2; }\r\n  else if (bytes[0]===0xFE && bytes[1]===0xFF){ encoding='utf-16be'; offset=2; }\r\n  else if (bytes[0]===0xEF && bytes[1]===0xBB && bytes[2]===0xBF){ encoding='utf-8'; offset=3; }\r\n  return new TextDecoder(encoding).decode(bytes.slice(offset));\r\n}\r\n\r\n\/* ============================================================\r\n   2) LABEL DICTIONARY for the summary section only \u2014 data rows\r\n      are located structurally, not by translated label text.\r\n   ============================================================ *\/\r\nconst LABELS = {\r\n  symbol:      ['Symbol:', 'S\u00edmbolo:'],\r\n  period:      ['Period:', 'Per\u00edodo:'],\r\n  totalTrades: ['Total Trades:', 'Total de operaciones ejecutadas:'],\r\n  totalDeals:  ['Total Deals:', 'Total de transacciones:'],\r\n};\r\n\r\n\/* ============================================================\r\n   3) NUMBER PARSING\r\n   ============================================================ *\/\r\nfunction detectDecimalSeparator(sampleStrings){\r\n  let commaVotes = 0, dotVotes = 0;\r\n  for (const s of sampleStrings){\r\n    const hasComma = s.includes(','), hasDot = s.includes('.');\r\n    if (hasComma && !hasDot) commaVotes++;\r\n    else if (hasDot && !hasComma) dotVotes++;\r\n    else if (hasComma && hasDot){ if (s.lastIndexOf(',') > s.lastIndexOf('.')) commaVotes++; else dotVotes++; }\r\n  }\r\n  return commaVotes > dotVotes ? ',' : '.';\r\n}\r\nfunction parseNum(str, decimalSep){\r\n  if (str == null) return NaN;\r\n  let s = String(str).trim();\r\n  if (s === '') return NaN;\r\n  const other = decimalSep === ',' ? '.' : ',';\r\n  s = s.split(other).join('');\r\n  s = s.replace(\/\\s\/g, '');\r\n  s = s.split(decimalSep).join('.');\r\n  return parseFloat(s);\r\n}\r\n\r\n\/* ============================================================\r\n   4) DATE PARSING \u2014 MT5 always uses YYYY.MM.DD HH:MM:SS\r\n   ============================================================ *\/\r\nconst MT5_DATE_RE = \/^(\\d{4})\\.(\\d{2})\\.(\\d{2})\\s(\\d{2}):(\\d{2}):(\\d{2})$\/;\r\nfunction parseMt5Date(str){\r\n  const m = MT5_DATE_RE.exec(String(str).trim());\r\n  if (!m) return null;\r\n  return new Date(Date.UTC(+m[1], +m[2]-1, +m[3], +m[4], +m[5], +m[6]));\r\n}\r\n\r\n\/* ============================================================\r\n   5) MAIN PARSER \u2014 returns structured data + error codes only;\r\n      all display text is localized at the render layer.\r\n   ============================================================ *\/\r\nfunction findLabelValue(allRows, labelVariants){\r\n  for (const row of allRows){\r\n    for (let i=0;i<row.length;i++){\r\n      if (labelVariants.includes(row[i].trim())){\r\n        for (let j=i+1;j<row.length;j++){ if (row[j].trim() !== '') return row[j].trim(); }\r\n      }\r\n    }\r\n  }\r\n  return null;\r\n}\r\n\r\nfunction parseMt5Report(html){\r\n  const doc = new DOMParser().parseFromString(html, 'text\/html');\r\n  const trs = Array.from(doc.querySelectorAll('tr'));\r\n  const allRows = trs.map(tr => Array.from(tr.querySelectorAll('td')).map(td => td.textContent));\r\n\r\n  const symbol = findLabelValue(allRows, LABELS.symbol);\r\n  const periodRaw = findLabelValue(allRows, LABELS.period);\r\n  const totalTrades = findLabelValue(allRows, LABELS.totalTrades);\r\n  const totalDeals = findLabelValue(allRows, LABELS.totalDeals);\r\n\r\n  const fullText = doc.body.textContent;\r\n  const riskCandidates = [];\r\n  const riskRe = \/\\b([A-Za-z_][A-Za-z0-9_]*)\\s*=\\s*(-?[\\d.,]+)\/g;\r\n  let rm;\r\n  while ((rm = riskRe.exec(fullText))){\r\n    const name = rm[1];\r\n    if (\/risk\/i.test(name) && \/(perc|pct|percent|trade)\/i.test(name)) riskCandidates.push({ name, raw: rm[2] });\r\n  }\r\n\r\n  const PROFIT_HDR = ['Profit', 'Beneficio']; const BALANCE_HDR = ['Balance'];\r\n  let headerRowIdx = -1, profitCol = -1, balanceCol = -1, timeCol = 0;\r\n  for (let i=0;i<allRows.length;i++){\r\n    const row = allRows[i].map(c=>c.trim());\r\n    const pIdx = row.findIndex(c => PROFIT_HDR.includes(c));\r\n    const bIdx = row.findIndex(c => BALANCE_HDR.includes(c));\r\n    if (pIdx !== -1 && bIdx !== -1){ headerRowIdx = i; profitCol = pIdx; balanceCol = bIdx; break; }\r\n  }\r\n  if (headerRowIdx === -1) return { error: 'NO_DEALS_TABLE' };\r\n\r\n  const probeRows = allRows.slice(headerRowIdx+1, headerRowIdx+30);\r\n  const numericProbe = [];\r\n  for (const row of probeRows) for (const cell of row){\r\n    const c = cell.trim();\r\n    if (\/^-?\\d+([.,]\\d+)?$\/.test(c)) numericProbe.push(c);\r\n  }\r\n  const decimalSep = detectDecimalSeparator(numericProbe);\r\n\r\n  const dealRows = [];\r\n  let i = headerRowIdx + 1;\r\n  for (; i < allRows.length; i++){\r\n    const row = allRows[i];\r\n    if (!row.length) break;\r\n    const d = parseMt5Date(row[timeCol]);\r\n    if (!d) break;\r\n    dealRows.push({ row, date: d });\r\n  }\r\n\r\n  const balanceRowCount = dealRows.filter(({row}) => (row[3]||'').trim().toLowerCase() === 'balance').length;\r\n  const comparableCount = dealRows.length - balanceRowCount;\r\n  const declaredTotalDeals = totalDeals != null ? parseInt(totalDeals.replace(\/[^\\d]\/g,''),10) : null;\r\n  if (declaredTotalDeals != null && declaredTotalDeals !== comparableCount){\r\n    return { error: 'COUNT_MISMATCH', declaredTotalDeals, parsedCount: comparableCount, balanceRowCount };\r\n  }\r\n\r\n  const trades = [];\r\n  let prevBalance = null;\r\n  for (const { row, date } of dealRows){\r\n    const profit = parseNum(row[profitCol], decimalSep);\r\n    const balance = parseNum(row[balanceCol], decimalSep);\r\n    const symbolCell = row[2] !== undefined ? row[2].trim() : '';\r\n    const typeCell = row[3] !== undefined ? row[3].trim() : '';\r\n    if (prevBalance !== null && profit !== 0 && !isNaN(profit)){\r\n      trades.push({ date, symbol: symbolCell, type: typeCell, profit, balanceBefore: prevBalance, balanceAfter: balance });\r\n    }\r\n    prevBalance = balance;\r\n  }\r\n\r\n  return { ok: true, symbol, periodRaw, totalTrades, totalDeals: declaredTotalDeals, riskCandidates, decimalSep, dealRowCount: dealRows.length, balanceRowCount, trades };\r\n}\r\n\r\n\/* ============================================================\r\n   6) SIMULATION ENGINE \u2014 walk-forward over every real starting point\r\n   ============================================================ *\/\r\nfunction dateKeyE(d){ return d.toISOString().slice(0,10); }\r\nfunction tradingDayBucketE(date, eodOffsetHours){ return dateKeyE(new Date(date.getTime() - eodOffsetHours*3600000)); }\r\n\r\nfunction simulateJourney(trades, startIdx, horizonDays, rules){\r\n  const startDate = trades[startIdx].date;\r\n  const endDate = new Date(startDate.getTime() + horizonDays*86400000);\r\n  let i = startIdx, phase='eval', equityUnits=0, hwmUnits=0;\r\n  const tradingDaysSet = new Set();\r\n  let dailyBucketKey=null, dailyBucketPnL=0, cycleStartDate=null, firstPayoutDone=false;\r\n  const payouts=[]; let evalPassedDate=null, evalFailedDate=null, fundedFailedDate=null, resolved=false;\r\n\r\n  let lastTradeDate = startDate;\r\n  while (i < trades.length && trades[i].date <= endDate){\r\n    const tr = trades[i];\r\n    lastTradeDate = tr.date;\r\n    \/\/ DD\/daily-limit thresholds are expressed in units, and a unit's real-money meaning depends on\r\n    \/\/ which risk% is being simulated \u2014 so they must be re-derived per phase, not fixed for the whole run.\r\n    const ddUnits = phase==='eval' ? rules.evalDdUnits : rules.fundedDdUnits;\r\n    const dailyLimitUnits = phase==='eval' ? rules.evalDailyLimitUnits : rules.fundedDailyLimitUnits;\r\n    if (dailyLimitUnits){\r\n      const bk = tradingDayBucketE(tr.date, rules.eodOffsetHours);\r\n      if (dailyBucketKey !== bk){ dailyBucketKey=bk; dailyBucketPnL=0; }\r\n      dailyBucketPnL += tr.units;\r\n      if (dailyBucketPnL <= -dailyLimitUnits){ if(phase==='eval') evalFailedDate=tr.date; else fundedFailedDate=tr.date; resolved=true; break; }\r\n    }\r\n    equityUnits += tr.units; hwmUnits = Math.max(hwmUnits, equityUnits); tradingDaysSet.add(dateKeyE(tr.date));\r\n    const floor = rules.ddType==='dynamic' ? hwmUnits-ddUnits : -ddUnits;\r\n    if (equityUnits <= floor){ if(phase==='eval') evalFailedDate=tr.date; else fundedFailedDate=tr.date; resolved=true; break; }\r\n    if (phase==='eval'){\r\n      if (equityUnits >= rules.targetUnits && (!rules.minTradingDays || tradingDaysSet.size>=rules.minTradingDays)){\r\n        evalPassedDate=tr.date; phase='funded'; equityUnits=0; hwmUnits=0; cycleStartDate=tr.date; firstPayoutDone=false;\r\n      }\r\n    } else {\r\n      const daysSince = (tr.date-cycleStartDate)\/86400000;\r\n      const req = firstPayoutDone ? rules.payoutCycleDays : rules.minDaysFirstPayout;\r\n      if (daysSince >= req && equityUnits > 0){\r\n        payouts.push({date:tr.date, units:equityUnits});\r\n        firstPayoutDone=true; equityUnits=0; hwmUnits=0; cycleStartDate=tr.date;\r\n      }\r\n    }\r\n    i++;\r\n  }\r\n  const ranOutOfData = (i >= trades.length) && !resolved && (trades[trades.length-1].date < endDate);\r\n  \/\/ Unresolvable only while still in eval: we can't know whether it would have passed or failed.\r\n  \/\/ A funded run that runs out of real data isn't unresolvable \u2014 it's a known-good outcome (still\r\n  \/\/ funded, not blown) with whatever real payouts it already collected, just cut short by the data.\r\n  if (ranOutOfData && phase==='eval') return { discarded:true };\r\n  return { discarded:false, passedEval: !!evalPassedDate, startDate, evalPassedDate, evalFailedDate, fundedFailedDate, payouts,\r\n    finalPhase: phase, fundedEndedBySegmentLimit: phase==='funded' && !fundedFailedDate, lastTradeDate, tradesConsumed: i-startIdx };\r\n}\r\n\r\nfunction walkForward(unitTrades, horizonDays, rules){\r\n  const outcomes = [];\r\n  for (let s=0; s<unitTrades.length; s++){\r\n    const r = simulateJourney(unitTrades, s, horizonDays, rules);\r\n    if (!r.discarded) outcomes.push(r);\r\n  }\r\n  return outcomes;\r\n}\r\n\r\n\/\/ Real-data worst case: from each real starting day, keep re-attempting immediately after each\r\n\/\/ failed evaluation (next real trade after the failure) and find the longest such chain of real\r\n\/\/ failures anywhere in the history \u2014 an honest alternative to assuming independent attempts.\r\nfunction worstFailStreak(unitTrades, horizonDays, rules, maxChain){\r\n  let worstCount = 0;\r\n  for (let s=0; s<unitTrades.length; s++){\r\n    let idx = s, fails = 0;\r\n    while (fails < maxChain && idx < unitTrades.length){\r\n      const r = simulateJourney(unitTrades, idx, horizonDays, rules);\r\n      if (r.discarded || r.passedEval) break;\r\n      fails++;\r\n      idx = idx + r.tradesConsumed + 1;\r\n    }\r\n    if (fails > worstCount) worstCount = fails;\r\n  }\r\n  return worstCount;\r\n}\r\n\r\nfunction bucketizeOutcomes(netOutcomes, price){\r\n  const bounds = [0, 2*price, 5*price, 15*price, 40*price, Infinity];\r\n  const buckets = [];\r\n  for (let i=0;i<bounds.length;i++){\r\n    const lo = i===0 ? -Infinity : bounds[i-1];\r\n    const hi = bounds[i];\r\n    const count = netOutcomes.filter(x => x>lo && x<=hi).length;\r\n    buckets.push({ lo, hi, count, isLoss: i===0 });\r\n  }\r\n  return buckets;\r\n}\r\n\r\nfunction percentile(arr, p){\r\n  if (!arr.length) return NaN;\r\n  const idx = (arr.length-1)*p, lo = Math.floor(idx), hi = Math.ceil(idx);\r\n  return lo===hi ? arr[lo] : arr[lo] + (arr[hi]-arr[lo])*(idx-lo);\r\n}\r\n\r\n\/* ============================================================\r\n   7) UI\r\n   ============================================================ *\/\r\nconst $ = id => document.getElementById(id);\r\nconst RISK_MIN_PLAUSIBLE = 0.05, RISK_MAX_PLAUSIBLE = 15;\r\n\r\nfunction dedupeCandidates(list){\r\n  const seen = new Set(), out = [];\r\n  for (const c of list){ const key = c.name+'='+c.raw; if (!seen.has(key)){ seen.add(key); out.push(c); } }\r\n  return out;\r\n}\r\n\r\nfunction renderResult(res){\r\n  const d = t(); const out = $('output');\r\n  if (res.error === 'NO_DEALS_TABLE'){\r\n    out.innerHTML = `<div class=\"panel\"><div class=\"banner bad\"><b>${d.errNoDeals}<\/b><br>${d.errNoDealsDetail}<br>${d.contact}<\/div><\/div>`;\r\n    return;\r\n  }\r\n  if (res.error === 'COUNT_MISMATCH'){\r\n    out.innerHTML = `<div class=\"panel\"><div class=\"banner bad\"><b>${d.errCountTitle}<\/b><br>${d.errCountDetail(res.declaredTotalDeals, res.parsedCount, res.balanceRowCount)}<br>${d.errCountNote} ${d.contact}<\/div><\/div>`;\r\n    return;\r\n  }\r\n  renderRiskGate(res);\r\n}\r\n\r\nfunction riskPlausibilityNote(pct){\r\n  const d = t();\r\n  if (isNaN(pct)) return '';\r\n  if (pct < RISK_MIN_PLAUSIBLE || pct > RISK_MAX_PLAUSIBLE) return `<div class=\"banner warn\">${d.implausible(pct)}<\/div>`;\r\n  return '';\r\n}\r\n\r\nfunction renderRiskGate(res){\r\n  const d = t(); const out = $('output');\r\n  const candidates = dedupeCandidates(res.riskCandidates);\r\n  let html = `<div class=\"panel\"><h2><span class=\"step-num\">2<\/span> ${d.step1Title}<\/h2>`;\r\n\r\n  if (candidates.length === 1){\r\n    const c = candidates[0]; const pct = parseFloat(c.raw.replace(',','.'));\r\n    html += `<div>${d.detectedAs(c.name, c.raw, pct)}<\/div>\r\n      ${riskPlausibilityNote(pct)}\r\n      <div class=\"btnrow\">\r\n        <button class=\"primary\" id=\"btnRiskYes\" data-pct=\"${pct}\">${d.yesCorrect}<\/button>\r\n        <button id=\"btnRiskNo\">${d.noCorrect}<\/button>\r\n      <\/div>`;\r\n  } else if (candidates.length > 1){\r\n    html += `<div>${d.multiplePrompt}<\/div><div style=\"margin-top:8px;\">`;\r\n    candidates.forEach((c,i) => {\r\n      const pct = parseFloat(c.raw.replace(',','.'));\r\n      html += `<div class=\"radiofield\"><label><input type=\"radio\" name=\"riskPick\" value=\"${pct}\" ${i===0?'checked':''}> ${c.name} = ${c.raw} (${pct}%)<\/label><\/div>`;\r\n    });\r\n    html += `<div class=\"radiofield\"><label><input type=\"radio\" name=\"riskPick\" value=\"manual\"> ${d.noneOfThese}<\/label><\/div><\/div>\r\n      <div class=\"field\"><label>${d.manualValue}<\/label><input type=\"number\" step=\"0.01\" id=\"riskManual\"><\/div>\r\n      <div class=\"btnrow\"><button class=\"primary\" id=\"btnRiskConfirmMulti\">${d.confirmContinue}<\/button><\/div>`;\r\n  } else {\r\n    html += `<div class=\"banner warn\">${d.noneDetected}<\/div>\r\n      <div class=\"field\"><label>${d.manualLabel}<\/label><input type=\"number\" step=\"0.01\" id=\"riskManual\"><\/div>\r\n      <div class=\"btnrow\">\r\n        <button class=\"primary\" id=\"btnRiskConfirmManual\">${d.confirmContinue}<\/button>\r\n        <button id=\"btnRiskNotApplicable\">${d.notApplicable}<\/button>\r\n      <\/div>`;\r\n  }\r\n  html += `<\/div>`;\r\n  out.innerHTML = html;\r\n\r\n  $('btnRiskYes')?.addEventListener('click', e => renderFinal(res, parseFloat(e.target.dataset.pct)));\r\n  $('btnRiskNo')?.addEventListener('click', () => renderRiskManualOnly(res));\r\n  $('btnRiskConfirmMulti')?.addEventListener('click', () => {\r\n    const picked = document.querySelector('input[name=\"riskPick\"]:checked').value;\r\n    const pct = picked === 'manual' ? parseFloat($('riskManual').value) : parseFloat(picked);\r\n    if (!isNaN(pct)) renderFinal(res, pct);\r\n  });\r\n  $('btnRiskConfirmManual')?.addEventListener('click', () => {\r\n    const pct = parseFloat($('riskManual').value);\r\n    if (!isNaN(pct)) renderFinal(res, pct);\r\n  });\r\n  $('btnRiskNotApplicable')?.addEventListener('click', () => renderRiskUnsuitable());\r\n}\r\n\r\nfunction renderRiskManualOnly(res){\r\n  const d = t();\r\n  $('output').innerHTML = `<div class=\"panel\"><h2><span class=\"step-num\">2<\/span> ${d.step1Title}<\/h2>\r\n    <div class=\"field\"><label>${d.manualValue}<\/label><input type=\"number\" step=\"0.01\" id=\"riskManual\"><\/div>\r\n    <div class=\"btnrow\">\r\n      <button class=\"primary\" id=\"btnRiskConfirmManual2\">${d.confirmContinue}<\/button>\r\n      <button id=\"btnRiskNotApplicable2\">${d.notApplicable}<\/button>\r\n    <\/div><\/div>`;\r\n  $('btnRiskConfirmManual2').addEventListener('click', () => {\r\n    const pct = parseFloat($('riskManual').value);\r\n    if (!isNaN(pct)) renderFinal(res, pct);\r\n  });\r\n  $('btnRiskNotApplicable2').addEventListener('click', () => renderRiskUnsuitable());\r\n}\r\n\r\nfunction renderRiskUnsuitable(){\r\n  const d = t();\r\n  $('output').innerHTML = `<div class=\"panel\"><div class=\"banner warn\"><b>${d.unsuitableTitle}<\/b><br>${d.unsuitableBody}<\/div><\/div>`;\r\n}\r\n\r\nfunction renderFinal(res, riskPct){\r\n  const d = t(); const out = $('output');\r\n  let html = '';\r\n\r\n  const spanMs = res.trades[res.trades.length-1].date - res.trades[0].date;\r\n  const spanYears = (spanMs\/(365.25*86400000)).toFixed(1);\r\n  const spanMonths = spanMs\/(30*86400000);\r\n  const suggestedHigh = Math.max(3, Math.min(12, Math.floor(spanMonths\/10)));\r\n  const suggestedLow = Math.max(1, Math.floor(suggestedHigh\/2));\r\n  const horizonHintHtml = d.horizonHint(spanYears, suggestedLow, suggestedHigh);\r\n\r\n  html += `<div class=\"panel no-print\"><h2><span class=\"step-num\">2<\/span> ${d.step1ConfirmedTitle}<\/h2>\r\n    <div>${d.riskConfirmedAs(riskPct)}<\/div>\r\n    <button id=\"btnChangeRisk\" style=\"margin-top:10px;\">${d.changeRisk}<\/button><\/div>`;\r\n\r\n  html += `<div class=\"panel\"><h2>${d.summaryTitle}<\/h2><div class=\"kv\">\r\n    <div><span>${d.lSymbol}:<\/span> <b>${res.symbol ?? '\u2014'}<\/b><\/div>\r\n    <div><span>${d.lPeriod}:<\/span> <b>${res.periodRaw ?? '\u2014'}<\/b><\/div>\r\n    <div><span>${d.lTotalTrades}:<\/span> <b>${res.totalTrades ?? '\u2014'}<\/b><\/div>\r\n    <div><span>${d.lTotalDeals}:<\/span> <b>${res.totalDeals ?? '\u2014'} \u2713<\/b><\/div>\r\n    <div><span>${d.lDecimal}:<\/span> <b>${res.decimalSep === ',' ? d.lComma : d.lDot}<\/b><\/div>\r\n  <\/div><\/div>`;\r\n\r\n  \/\/ strategy stats \u2014 every trade weighted equally, in risk-adjusted units (not raw \u20ac)\r\n  const allUnits = res.trades.map(tr => tr.profit\/((riskPct\/100)*tr.balanceBefore));\r\n  const wins = allUnits.filter(u=>u>0), losses = allUnits.filter(u=>u<0);\r\n  const sWinRate = allUnits.length ? wins.length\/allUnits.length*100 : NaN;\r\n  const sAvgWin = wins.length ? wins.reduce((a,b)=>a+b,0)\/wins.length : NaN;\r\n  const sAvgLoss = losses.length ? Math.abs(losses.reduce((a,b)=>a+b,0)\/losses.length) : NaN;\r\n  const sPF = losses.length ? Math.abs(wins.reduce((a,b)=>a+b,0)\/losses.reduce((a,b)=>a+b,0)) : NaN;\r\n\r\n  html += `<div class=\"panel\"><h2>${d.strategyTitle}<\/h2><div class=\"kv\">\r\n    <div><span>${d.sWinRate}:<\/span> <b>${sWinRate.toFixed(1)}%<\/b><\/div>\r\n    <div><span>${d.sPF}:<\/span> <b>${sPF.toFixed(2)}<\/b><\/div>\r\n    <div><span>${d.sAvgWin}:<\/span> <b>${sAvgWin.toFixed(3)} ${d.sUnits}<\/b><\/div>\r\n    <div><span>${d.sAvgLoss}:<\/span> <b>${sAvgLoss.toFixed(3)} ${d.sUnits}<\/b><\/div>\r\n  <\/div>\r\n  <div class=\"note\">${d.sNote}<\/div><\/div>`;\r\n\r\n  html += `<div class=\"panel\"><h2>${d.previewTitle}<\/h2>\r\n    <div class=\"tablewrap\"><table><thead><tr>\r\n      <th>${d.hDate}<\/th><th>${d.hSymbol}<\/th><th>${d.hType}<\/th><th class=\"num\">${d.hProfit}<\/th><th class=\"num\">${d.hBalance}<\/th><th class=\"num\">${d.hUnits}<\/th>\r\n    <\/tr><\/thead><tbody>`;\r\n  const fmt2 = n => n.toLocaleString(currentLang()==='es'?'es-ES':'en-US',{maximumFractionDigits:2});\r\n  for (const tr of res.trades.slice(0,10)){\r\n    const units = tr.profit\/((riskPct\/100)*tr.balanceBefore);\r\n    html += `<tr><td>${tr.date.toISOString().replace('T',' ').slice(0,19)}<\/td><td>${tr.symbol}<\/td><td>${tr.type}<\/td>\r\n      <td class=\"num\">${fmt2(tr.profit)}<\/td><td class=\"num\">${fmt2(tr.balanceBefore)}<\/td><td class=\"num\">${units.toFixed(3)}<\/td><\/tr>`;\r\n  }\r\n  html += `<\/tbody><\/table><\/div>\r\n    <div class=\"banner good\">${d.parseOk(res.totalDeals)}<\/div><\/div>`;\r\n\r\n  html += `<div class=\"panel no-print\"><h2><span class=\"step-num\">3<\/span> ${d.step2Title}<\/h2>\r\n    <div class=\"field\"><label>${d.lSimRisk}<\/label><input type=\"number\" step=\"0.01\" id=\"rSimRisk\" value=\"${riskPct}\"><\/div>\r\n    <div class=\"field\"><label>${d.lSimRiskFunded}<\/label><input type=\"number\" step=\"0.01\" id=\"rSimRiskFunded\" value=\"${riskPct}\"><\/div>\r\n    <details class=\"explain\"><summary>${d.explainSimRisk}<\/summary><div class=\"body\">${d.explainSimRiskBody}<\/div><\/details>\r\n    <div class=\"field\" style=\"grid-template-columns:1fr 130px\"><label>${d.lTargets}<\/label><input type=\"text\" id=\"rTargets\" value=\"10,5\"><\/div>\r\n    <div class=\"field\"><label>${d.lMaxDD}<\/label><input type=\"number\" id=\"rMaxDD\" value=\"10\"><\/div>\r\n    <div class=\"field\"><label>${d.lDDType}<\/label>\r\n      <select id=\"rDDType\"><option value=\"static\">${d.ddStatic}<\/option><option value=\"dynamic\">${d.ddDynamic}<\/option><\/select><\/div>\r\n    <div class=\"field\"><label>${d.lDailyLimit}<\/label><input type=\"number\" id=\"rDailyLimit\" value=\"5\"><\/div>\r\n    <div class=\"field\"><label>${d.lEodHour}<\/label><input type=\"number\" id=\"rEodHour\" value=\"23\"><\/div>\r\n    <div class=\"field\"><label>${d.lMinDays}<\/label><input type=\"number\" id=\"rMinDays\" value=\"0\"><\/div>\r\n    <div class=\"field\"><label>${d.lMinDaysPayout}<\/label><input type=\"number\" id=\"rMinDaysPayout\" value=\"15\"><\/div>\r\n    <div class=\"field\"><label>${d.lPayoutCycle}<\/label><input type=\"number\" id=\"rPayoutCycle\" value=\"10\"><\/div>\r\n    <div class=\"field\"><label>${d.lHorizon}<\/label><input type=\"number\" id=\"rHorizonMonths\" value=\"${suggestedHigh}\"><\/div>\r\n    <div class=\"note\">${horizonHintHtml}<\/div>\r\n    <details class=\"explain\"><summary>${d.explainHorizon}<\/summary><div class=\"body\">${d.explainHorizonBody('')}<\/div><\/details>\r\n  <\/div>`;\r\n\r\n  html += `<div class=\"panel no-print\"><h2><span class=\"step-num\">4<\/span> ${d.step3Title}<\/h2>\r\n    <div class=\"field\"><label>${d.lBalance}<\/label><input type=\"number\" id=\"eBalance\" value=\"10000\"><\/div>\r\n    <div class=\"field\"><label>${d.lBasePrice}<\/label><input type=\"number\" id=\"eBasePrice\" value=\"79\"><\/div>\r\n    <div class=\"field\"><label>${d.lDiscount}<\/label><input type=\"number\" id=\"eDiscount\" value=\"30\"><\/div>\r\n    <div class=\"field\"><label>${d.lSplit}<\/label><input type=\"number\" id=\"eSplit\" value=\"80\"><\/div>\r\n    <div class=\"field\"><label>${d.lRefund}<\/label><input type=\"number\" id=\"eRefund\" value=\"0\"><\/div>\r\n    <div class=\"btnrow\"><button class=\"primary\" id=\"btnRunSim\">${d.runBtn}<\/button><\/div>\r\n  <\/div>`;\r\n\r\n  html += `<div id=\"simOutput\"><\/div>`;\r\n\r\n  out.innerHTML = html;\r\n  $('btnChangeRisk').addEventListener('click', () => renderRiskGate(res));\r\n  $('btnRunSim').addEventListener('click', () => runSimulation(res, riskPct));\r\n}\r\n\r\nfunction tileClass(v, goodMin, warnMin){\r\n  return v>=goodMin ? 'good' : v>=warnMin ? 'warn' : 'bad';\r\n}\r\n\r\nfunction runSimulation(res, riskPct){\r\n  const d = t();\r\n  const simRiskPct = parseFloat($('rSimRisk').value) || riskPct;\r\n  const fundedSimRiskPct = parseFloat($('rSimRiskFunded').value) || simRiskPct;\r\n  const targets = $('rTargets').value.split(',').map(s=>parseFloat(s.trim())).filter(n=>!isNaN(n));\r\n  const targetTotalPct = targets.reduce((a,b)=>a+b,0);\r\n  const maxDDPct = parseFloat($('rMaxDD').value);\r\n  const ddType = $('rDDType').value;\r\n  const dailyLimitPct = parseFloat($('rDailyLimit').value);\r\n  const eodOffsetHours = parseFloat($('rEodHour').value);\r\n  const minTradingDays = parseInt($('rMinDays').value,10);\r\n  const minDaysFirstPayout = parseFloat($('rMinDaysPayout').value);\r\n  const payoutCycleDays = parseFloat($('rPayoutCycle').value);\r\n  const horizonDays = parseFloat($('rHorizonMonths').value) * 30;\r\n\r\n  const rules = {\r\n    targetUnits: targetTotalPct \/ simRiskPct,\r\n    evalDdUnits: maxDDPct \/ simRiskPct, fundedDdUnits: maxDDPct \/ fundedSimRiskPct, ddType,\r\n    evalDailyLimitUnits: dailyLimitPct > 0 ? dailyLimitPct \/ simRiskPct : 0,\r\n    fundedDailyLimitUnits: dailyLimitPct > 0 ? dailyLimitPct \/ fundedSimRiskPct : 0,\r\n    eodOffsetHours, minTradingDays: minTradingDays || 0, minDaysFirstPayout, payoutCycleDays\r\n  };\r\n  const unitTrades = res.trades.map(tr => ({ date: tr.date, units: tr.profit\/((riskPct\/100)*tr.balanceBefore) }));\r\n  const outcomes = walkForward(unitTrades, horizonDays, rules);\r\n  const n = outcomes.length;\r\n  const avgTradesConsumed = n ? outcomes.reduce((a,o)=>a+o.tradesConsumed,0)\/n : 0;\r\n  const effectiveN = avgTradesConsumed>0 ? Math.round(n\/avgTradesConsumed) : 0;\r\n\r\n  const passed = outcomes.filter(o=>o.passedEval);\r\n  const fundedZero = passed.filter(o=>o.payouts.length===0);\r\n  const fundedSome = passed.filter(o=>o.payouts.length>0);\r\n  const totalPayouts = passed.reduce((a,o)=>a+o.payouts.length,0);\r\n  const avgPayoutsPerFunded = passed.length ? (totalPayouts\/passed.length) : 0;\r\n  const fundedStillActive = passed.filter(o=>o.fundedEndedBySegmentLimit);\r\n  const fundedStillActivePct = passed.length ? fundedStillActive.length\/passed.length*100 : 0;\r\n\r\n  const daysToPassArr = passed.map(o => (o.evalPassedDate - o.startDate)\/86400000);\r\n  const avgDaysToPass = daysToPassArr.length ? daysToPassArr.reduce((a,b)=>a+b,0)\/daysToPassArr.length : NaN;\r\n  const daysFundedArr = passed.map(o => ((o.fundedFailedDate || o.lastTradeDate) - o.evalPassedDate)\/86400000);\r\n  const avgDaysFunded = daysFundedArr.length ? daysFundedArr.reduce((a,b)=>a+b,0)\/daysFundedArr.length : NaN;\r\n\r\n  const balance = parseFloat($('eBalance').value);\r\n  const basePrice = parseFloat($('eBasePrice').value);\r\n  const discountPct = parseFloat($('eDiscount').value);\r\n  const splitPct = parseFloat($('eSplit').value);\r\n  const refundPct = parseFloat($('eRefund').value);\r\n  const price = basePrice * (1 - discountPct\/100);\r\n\r\n  const payoutAmounts = [];\r\n  for (const o of passed) for (const p of o.payouts) payoutAmounts.push(p.units * (fundedSimRiskPct\/100) * balance * (splitPct\/100));\r\n  const avgPayoutSize = payoutAmounts.length ? payoutAmounts.reduce((a,b)=>a+b,0)\/payoutAmounts.length : NaN;\r\n  const maxPayoutSize = payoutAmounts.length ? Math.max(...payoutAmounts) : NaN;\r\n\r\n  const MAX_FAIL_CHAIN = 6;\r\n  const worstStreak = worstFailStreak(unitTrades, horizonDays, rules, MAX_FAIL_CHAIN);\r\n\r\n  const netOutcomes = outcomes.map(o => {\r\n    if (!o.passedEval) return -price;\r\n    let total = 0;\r\n    for (const p of o.payouts) total += p.units * (fundedSimRiskPct\/100) * balance * (splitPct\/100);\r\n    if (o.payouts.length >= 1) total += (refundPct\/100) * price;\r\n    return total - price;\r\n  }).sort((a,b)=>a-b);\r\n\r\n  const mean = netOutcomes.length ? netOutcomes.reduce((a,b)=>a+b,0)\/netOutcomes.length : NaN;\r\n  const median = percentile(netOutcomes, 0.5);\r\n  const profitableRate = netOutcomes.length ? netOutcomes.filter(x=>x>0).length\/netOutcomes.length : NaN;\r\n  const loc = currentLang()==='es' ? 'es-ES' : 'en-US';\r\n  const fmtE = x => (x<0?'-':'') + '\u20ac' + Math.abs(Math.round(x)).toLocaleString(loc);\r\n  const passRate = n ? passed.length\/n : NaN;\r\n\r\n  \/\/ plain-language breakdown, relative to ALL usable attempts (not just the passed ones)\r\n  const failEvalPct = n ? (n-passed.length)\/n*100 : 0;\r\n  const fundedZeroPct = n ? fundedZero.length\/n*100 : 0;\r\n  const fundedSomePct = n ? fundedSome.length\/n*100 : 0;\r\n  const verdictTone = median>0 && passRate>=0.5 ? 'good' : median>0 ? 'warn' : 'bad';\r\n  const expectedAttempts = passRate>0 ? 1\/passRate : NaN;\r\n  const expectedTotalSpend = passRate>0 ? price\/passRate : NaN;\r\n\r\n  const el = $('simOutput');\r\n  el.innerHTML = `<div class=\"panel\">\r\n    <div style=\"display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:14px;\">\r\n      <h2 style=\"margin:0; font-size:15px;\">${d.resTitle}<\/h2>\r\n      <button class=\"no-print\" id=\"btnExportPdf\" style=\"font-size:12.5px;\">${d.exportBtn}<\/button>\r\n    <\/div>\r\n\r\n    <div class=\"verdict ${verdictTone}\"><span class=\"dot\"><\/span><span>${d.verdict((passRate*100).toFixed(0), fmtE(median), fmtE(price))}<\/span><\/div>\r\n\r\n    <div class=\"summary\">\r\n      <div class=\"tile\"><div class=\"lbl\">${d.tUsablePoints}<\/div><div class=\"val\">${n}<\/div><div class=\"sub\">\/ ${unitTrades.length}<\/div><\/div>\r\n      <div class=\"tile ${effectiveN<30?'warn':''}\"><div class=\"lbl\">${d.tEffN}<\/div><div class=\"val\">${effectiveN}<\/div><\/div>\r\n      <div class=\"tile ${isNaN(passRate)?'':tileClass(passRate,0.6,0.35)}\"><div class=\"lbl\">${d.tPassRate}<\/div><div class=\"val\">${n?(passRate*100).toFixed(1):'\u2014'}%<\/div><\/div>\r\n      <div class=\"tile ${median>0?'good':'bad'}\"><div class=\"lbl\">${d.tPmedian}<\/div><div class=\"val\">${fmtE(median)}<\/div><\/div>\r\n    <\/div>\r\n    ${effectiveN < 30 ? `<div class=\"banner warn\">${d.lowSample}<\/div>` : ''}\r\n    <details class=\"explain\"><summary>${d.explainSample}<\/summary><div class=\"body\">${d.explainSampleBody}<\/div><\/details>\r\n\r\n    <h2 style=\"margin-top:18px;\">${d.outcomeTitle}<\/h2>\r\n    <div class=\"barlegend\">\r\n      <span class=\"fail\"><i><\/i>${d.legFail} (${failEvalPct.toFixed(0)}%)<\/span>\r\n      <span class=\"zero\"><i><\/i>${d.legZero} (${fundedZeroPct.toFixed(0)}%)<\/span>\r\n      <span class=\"paid\"><i><\/i>${d.legPaid} (${fundedSomePct.toFixed(0)}%)<\/span>\r\n    <\/div>\r\n    <div class=\"outcomebar\">\r\n      <div class=\"seg fail\" style=\"width:${failEvalPct}%\">${failEvalPct>=8?failEvalPct.toFixed(0)+'%':''}<\/div>\r\n      <div class=\"seg zero\" style=\"width:${fundedZeroPct}%\">${fundedZeroPct>=8?fundedZeroPct.toFixed(0)+'%':''}<\/div>\r\n      <div class=\"seg paid\" style=\"width:${fundedSomePct}%\">${fundedSomePct>=8?fundedSomePct.toFixed(0)+'%':''}<\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <div class=\"panel\"><h2>${d.econTitle}<\/h2>\r\n    <div class=\"kv\">\r\n      <div><span>${d.tPrice}:<\/span> <b>${fmtE(price)}<\/b><\/div>\r\n      <div><span>${d.tEVmean}:<\/span> <b>${fmtE(mean)}<\/b><\/div>\r\n      <div><span>${d.tPosRate}:<\/span> <b>${(profitableRate*100).toFixed(1)}%<\/b><\/div>\r\n      <div><span>${d.tFundedZero}:<\/span> <b>${passed.length?(fundedZero.length\/passed.length*100).toFixed(1):'\u2014'}%<\/b><\/div>\r\n      <div><span>${d.tFundedSome}:<\/span> <b>${passed.length?(fundedSome.length\/passed.length*100).toFixed(1):'\u2014'}%<\/b><\/div>\r\n      <div><span>${d.tAvgPayouts}:<\/span> <b>${avgPayoutsPerFunded.toFixed(2)}<\/b><\/div>\r\n      <div><span>${d.tAvgPayoutSize}:<\/span> <b>${payoutAmounts.length?fmtE(avgPayoutSize):'\u2014'}<\/b><\/div>\r\n      <div><span>${d.tMaxPayoutSize}:<\/span> <b>${payoutAmounts.length?fmtE(maxPayoutSize):'\u2014'}<\/b><\/div>\r\n      <div><span>${d.tAvgDaysToPass}:<\/span> <b>${passed.length?d.fmtDays(avgDaysToPass):'\u2014'}<\/b><\/div>\r\n      <div><span>${d.tAvgDaysFunded}:<\/span> <b>${passed.length?d.fmtDays(avgDaysFunded):'\u2014'}<\/b><\/div>\r\n    <\/div>\r\n    <div class=\"note\">${d.evNote}<\/div>\r\n\r\n    <div style=\"margin-top:14px;padding-top:12px;border-top:1px solid var(--border);\">\r\n      <div class=\"kv\">\r\n        <div><span>${d.tExpectedSpend}:<\/span> <b>${passRate>0?fmtE(expectedTotalSpend):'\u2014'}<\/b><\/div>\r\n        <div><span>${d.tExpectedAttempts}:<\/span> <b>${passRate>0?expectedAttempts.toFixed(1):'\u2014'}<\/b><\/div>\r\n      <\/div>\r\n      <div class=\"note\">${d.expectedSpendNote}<\/div>\r\n    <\/div>\r\n\r\n    <div style=\"margin-top:14px;padding-top:12px;border-top:1px solid var(--border);\">\r\n      <div class=\"kv\"><div><span>${d.tWorstStreak}:<\/span> <b>${d.worstStreakVal(worstStreak, MAX_FAIL_CHAIN, fmtE(worstStreak*price))}<\/b><\/div><\/div>\r\n      <div class=\"note\">${d.worstStreakNote}<\/div>\r\n    <\/div>\r\n\r\n    <div style=\"margin-top:14px;padding-top:12px;border-top:1px solid var(--border);\">\r\n      <div class=\"kv\"><div><span>${d.tStillActive}:<\/span> <b>${passed.length?fundedStillActivePct.toFixed(1):'\u2014'}%<\/b><\/div><\/div>\r\n      <div class=\"note\">${d.stillActiveNote}<\/div>\r\n    <\/div>\r\n\r\n    <h2 style=\"margin-top:16px;\">${d.histTitle}<\/h2>\r\n    ${(() => {\r\n      const buckets = bucketizeOutcomes(netOutcomes, price);\r\n      const maxCount = Math.max(...buckets.map(b=>b.count), 1);\r\n      return buckets.map(b => {\r\n        const pct = n ? (b.count\/n*100) : 0;\r\n        const barPct = (b.count\/maxCount*100);\r\n        const label = b.isLoss ? d.bucketLost : b.hi===Infinity ? `${fmtE(b.lo)}+` : `${fmtE(b.lo)} \u2013 ${fmtE(b.hi)}`;\r\n        return `<div class=\"histrow ${b.isLoss?'loss':''}\">\r\n          <div class=\"hlabel\">${label}<\/div>\r\n          <div class=\"htrack\"><div class=\"hbar\" style=\"width:${barPct}%\"><\/div><\/div>\r\n          <div class=\"hpct\">${pct.toFixed(0)}%<\/div>\r\n        <\/div>`;\r\n      }).join('');\r\n    })()}\r\n\r\n    <h2 style=\"margin-top:16px;\">${d.distTitle(n)}<\/h2>\r\n    <div class=\"tablewrap\"><table><thead><tr>\r\n      <th class=\"num\">${d.hWorst}<\/th><th class=\"num\">${d.hP10}<\/th><th class=\"num\">${d.hP25}<\/th><th class=\"num\">${d.hMedian}<\/th><th class=\"num\">${d.hP75}<\/th><th class=\"num\">${d.hP90}<\/th><th class=\"num\">${d.hBest}<\/th>\r\n    <\/tr><\/thead><tbody><tr>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0.10))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0.25))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0.50))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0.75))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,0.90))}<\/td>\r\n      <td class=\"num\">${fmtE(percentile(netOutcomes,1))}<\/td>\r\n    <\/tr><\/tbody><\/table><\/div>\r\n    <div class=\"note\">${d.distNote}<\/div>\r\n    <details class=\"explain\"><summary>${d.explainPct}<\/summary><div class=\"body\">${d.explainPctBody}<\/div><\/details>\r\n  <\/div>\r\n\r\n  <div class=\"panel no-print cta-panel\">\r\n    <p>${d.ctaBody}<\/p>\r\n    <a class=\"cta-link\" href=\"${COURSE_URL[currentLang()] || COURSE_URL.en}\" target=\"_blank\" rel=\"noopener\">${d.ctaBtn}<\/a>\r\n  <\/div>`;\r\n  $('btnExportPdf').addEventListener('click', () => window.print());\r\n}\r\n\r\n\/* ---- file input wiring ---- *\/\r\nfunction handleFile(file){\r\n  file.arrayBuffer().then(buf => {\r\n    const html = decodeSmart(buf);\r\n    renderResult(parseMt5Report(html));\r\n  });\r\n}\r\n$('fileInput').addEventListener('change', e => { if (e.target.files[0]) handleFile(e.target.files[0]); });\r\n$('dropzone').addEventListener('click', () => $('fileInput').click());\r\n$('dropzone').addEventListener('dragover', e => { e.preventDefault(); $('dropzone').classList.add('drag'); });\r\n$('dropzone').addEventListener('dragleave', () => $('dropzone').classList.remove('drag'));\r\n$('dropzone').addEventListener('drop', e => {\r\n  e.preventDefault(); $('dropzone').classList.remove('drag');\r\n  if (e.dataTransfer.files[0]) handleFile(e.dataTransfer.files[0]);\r\n});\r\n\r\n\/* ---- localize the static shell (left in English for TranslatePress originally,\r\n   but it wasn't reliably picking it up in production, so drive it from DICT instead) ---- *\/\r\n(function localizeStaticShell(){\r\n  const d = t();\r\n  $('widgetTitle').textContent = d.widgetTitle;\r\n  $('widgetTag').textContent = d.widgetTag;\r\n  $('step1Title').innerHTML = `<span class=\"step-num\">1<\/span> ${d.step1UploadTitle}`;\r\n  $('step1Subtext').textContent = d.step1UploadSubtext;\r\n  $('dzText').textContent = d.dzBig;\r\n  $('dzSmall').textContent = d.dzSmall;\r\n  $('footNote').textContent = d.footNoteText;\r\n})();\r\n<\/script>\r\n<\/div><\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_row_4 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_4 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_heading_3 et_pb_heading et_pb_module et_flex_module\"><div class=\"et_pb_heading_container\"><h2 class=\"et_pb_module_header\">FAQ<\/h2><\/div><\/div>\n\n<div class=\"et_pb_text_3 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h3>Why walk-forward instead of a Monte Carlo simulation or a formula?<\/h3>\n<p>A formula assumes your strategy behaves like a statistical model \u2014 Monte Carlo resampling invents synthetic trade sequences that never happened. Walk-forward does neither: it replays only real, actual sequences of your own trades, so it naturally captures real market clustering \u2014 the way a bad stretch tends to bring several losses close together \u2014 that a formula or synthetic resample can miss.<\/p>\n<\/div><\/div>\n\n<div class=\"et_pb_text_4 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h3>Why is \"effective sample size\" so much smaller than the number of usable starting points?<\/h3>\n<p>Two starting points a day apart share almost the exact same subsequent trades \u2014 they're not independent evidence, just the same evidence counted twice. Effective sample size estimates how many truly distinct tests your history provides once that overlap is accounted for. A low number means: get a longer backtest or use a shorter horizon before trusting the result too far.<\/p>\n<\/div><\/div>\n\n<div class=\"et_pb_text_5 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h3>Can I see what happens at a different risk % without re-running my backtest?<\/h3>\n<p>Yes \u2014 the simulator lets you set a risk % to simulate separately from the risk % actually used in your uploaded backtest, for both the evaluation and funded stages. It rescales your real trade outcomes proportionally, which mirrors how position sizing itself works \u2014 though it can't account for lot-size rounding or margin limits at extreme risk levels.<\/p>\n<\/div><\/div>\n\n<div class=\"et_pb_text_6 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h3>Is a high pass rate a guarantee?<\/h3>\n<p>No. It's what actually happened across every real starting point your own history provides \u2014 not a prediction for any single future attempt. Markets change, and a strategy that performed well in the past isn't guaranteed to keep doing so.<\/p>\n<\/div><\/div>\n\n<div class=\"et_pb_text_7 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h3>Where do the challenge rules and prices come from?<\/h3>\n<p>You enter them yourself, exactly as the firm lists them at checkout. The simulator makes no assumption about any specific firm's current terms \u2014 always confirm against the firm's live rulebook before paying.<\/p>\n<\/div><\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_row_5 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_5 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_heading_4 et_pb_heading et_pb_module et_flex_module\"><div class=\"et_pb_heading_container\"><h2 class=\"et_pb_module_header\">Disclaimer<\/h2><\/div><\/div>\n\n<div class=\"et_pb_text_8 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><p>This tool is for educational purposes only. It replays your own uploaded backtest against rules you provide \u2014 it does not predict, guarantee, or recommend any specific trading strategy, prop firm, or purchase. A backtest is not a guarantee of future performance, and market conditions change. Verify every rule, price, and add-on against the prop firm's current terms before making a purchase decision. Nothing on this page is financial advice.<\/p>\n<\/div><\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>A free tool that replays your own MT5 backtest against real prop firm challenge rules \u2014 walk-forward, using only your actual historical trades, never synthetic data.<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-257260","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.8 (Yoast SEO v28.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Prop Firm Backtest Simulator (Walk-Forward, MT5) | GreaterWaves<\/title>\n<meta name=\"description\" content=\"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/greaterwaves.com\/es\/prop-firm-backtest-simulator\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Prop Firm Challenge Calculator\" \/>\n<meta property=\"og:description\" content=\"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/greaterwaves.com\/es\/prop-firm-backtest-simulator\/\" \/>\n<meta property=\"og:site_name\" content=\"GreaterWaves Academy\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/greaterwavessystems\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-15T14:47:05+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/greaterwaves.com\\\/prop-firm-backtest-simulator\\\/\",\"url\":\"https:\\\/\\\/greaterwaves.com\\\/prop-firm-backtest-simulator\\\/\",\"name\":\"Prop Firm Backtest Simulator (Walk-Forward, MT5) | GreaterWaves\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/greaterwaves.com\\\/#website\"},\"datePublished\":\"2026-09-14T09:36:08+00:00\",\"dateModified\":\"2026-09-15T14:47:05+00:00\",\"description\":\"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/greaterwaves.com\\\/prop-firm-backtest-simulator\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/greaterwaves.com\\\/prop-firm-backtest-simulator\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/greaterwaves.com\\\/prop-firm-backtest-simulator\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/greaterwaves.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Prop Firm Challenge Calculator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/greaterwaves.com\\\/#website\",\"url\":\"https:\\\/\\\/greaterwaves.com\\\/\",\"name\":\"GreaterWaves Academy\",\"description\":\"Algorithmic Trading Academy focused on real, validated trading systems\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/greaterwaves.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Prop Firm Backtest Simulator (Walk-Forward, MT5) | GreaterWaves","description":"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/greaterwaves.com\/es\/prop-firm-backtest-simulator\/","og_locale":"es_ES","og_type":"article","og_title":"Prop Firm Challenge Calculator","og_description":"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.","og_url":"https:\/\/greaterwaves.com\/es\/prop-firm-backtest-simulator\/","og_site_name":"GreaterWaves Academy","article_publisher":"https:\/\/www.facebook.com\/greaterwavessystems","article_modified_time":"2026-09-15T14:47:05+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/greaterwaves.com\/prop-firm-backtest-simulator\/","url":"https:\/\/greaterwaves.com\/prop-firm-backtest-simulator\/","name":"Prop Firm Backtest Simulator (Walk-Forward, MT5) | GreaterWaves","isPartOf":{"@id":"https:\/\/greaterwaves.com\/#website"},"datePublished":"2026-09-14T09:36:08+00:00","dateModified":"2026-09-15T14:47:05+00:00","description":"Use this prop firm challenge calculator to see your pass probability, expected ROI, and payout timeline \u2014 before you pay for an evaluation.","breadcrumb":{"@id":"https:\/\/greaterwaves.com\/prop-firm-backtest-simulator\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/greaterwaves.com\/prop-firm-backtest-simulator\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/greaterwaves.com\/prop-firm-backtest-simulator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/greaterwaves.com\/"},{"@type":"ListItem","position":2,"name":"Prop Firm Challenge Calculator"}]},{"@type":"WebSite","@id":"https:\/\/greaterwaves.com\/#website","url":"https:\/\/greaterwaves.com\/","name":"GreaterWaves Academy","description":"Algorithmic Trading Academy focused on real, validated trading systems","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/greaterwaves.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es"}]}},"_links":{"self":[{"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/pages\/257260","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/comments?post=257260"}],"version-history":[{"count":6,"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/pages\/257260\/revisions"}],"predecessor-version":[{"id":257267,"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/pages\/257260\/revisions\/257267"}],"wp:attachment":[{"href":"https:\/\/greaterwaves.com\/es\/wp-json\/wp\/v2\/media?parent=257260"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}