enhancing backtesting functionality
This commit is contained in:
@@ -10,7 +10,7 @@ from fastapi import FastAPI, HTTPException, Query
|
|||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
from indicators.sessions import set_timezone
|
||||||
BACKEND_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
BACKEND_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
if BACKEND_DIR not in sys.path:
|
if BACKEND_DIR not in sys.path:
|
||||||
sys.path.insert(0, BACKEND_DIR)
|
sys.path.insert(0, BACKEND_DIR)
|
||||||
@@ -524,6 +524,10 @@ def get_backtest(
|
|||||||
max_consecutive_losses: int = 0,
|
max_consecutive_losses: int = 0,
|
||||||
):
|
):
|
||||||
dataset_id = _resolve_dataset(dataset)
|
dataset_id = _resolve_dataset(dataset)
|
||||||
|
if "MT5" in dataset.upper():
|
||||||
|
set_timezone("mt5")
|
||||||
|
else:
|
||||||
|
set_timezone("est")
|
||||||
candles = _get_candles_for_timeframe(dataset_id, timeframe)
|
candles = _get_candles_for_timeframe(dataset_id, timeframe)
|
||||||
|
|
||||||
strategy = _build_strategy(
|
strategy = _build_strategy(
|
||||||
|
|||||||
@@ -28,5 +28,3 @@ class Trade:
|
|||||||
exit_price: float
|
exit_price: float
|
||||||
pnl: float
|
pnl: float
|
||||||
r_multiple: float = 0.0
|
r_multiple: float = 0.0
|
||||||
partial_tp_taken: bool = False
|
|
||||||
partial_tp_realized_pnl: float = 0.0
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from datetime import time
|
from datetime import time
|
||||||
|
|
||||||
|
# EST sessions (for histdata CSVs)
|
||||||
SESSIONS_EST = {
|
SESSIONS_EST = {
|
||||||
"asian": (time(19, 0), time(3, 0)),
|
"asian": (time(19, 0), time(3, 0)),
|
||||||
"london": (time(2, 0), time(5, 0)),
|
"london": (time(2, 0), time(5, 0)),
|
||||||
@@ -8,25 +9,42 @@ SESSIONS_EST = {
|
|||||||
"london_ny_overlap": (time(8, 0), time(10, 0)),
|
"london_ny_overlap": (time(8, 0), time(10, 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# UTC+2 sessions (for MetaTrader exported CSVs)
|
||||||
|
SESSIONS_MT5 = {
|
||||||
|
"asian": (time(2, 0), time(10, 0)),
|
||||||
|
"london": (time(9, 0), time(12, 0)),
|
||||||
|
"new_york": (time(14, 0), time(17, 0)),
|
||||||
|
"london_close": (time(17, 0), time(19, 0)),
|
||||||
|
"london_ny_overlap": (time(15, 0), time(17, 0)),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Active session map (switch based on data source)
|
||||||
|
_active_sessions = SESSIONS_EST
|
||||||
|
|
||||||
|
|
||||||
|
def set_timezone(tz="est"):
|
||||||
|
global _active_sessions
|
||||||
|
if tz.lower() in ("mt5", "utc+2", "server"):
|
||||||
|
_active_sessions = SESSIONS_MT5
|
||||||
|
else:
|
||||||
|
_active_sessions = SESSIONS_EST
|
||||||
|
|
||||||
|
|
||||||
def in_session(candle_time, session_name):
|
def in_session(candle_time, session_name):
|
||||||
if session_name == "all":
|
if session_name == "all":
|
||||||
return True
|
return True
|
||||||
|
if session_name not in _active_sessions:
|
||||||
if session_name not in SESSIONS_EST:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
t = candle_time.time()
|
t = candle_time.time()
|
||||||
start, end = SESSIONS_EST[session_name]
|
start, end = _active_sessions[session_name]
|
||||||
if start > end:
|
if start > end:
|
||||||
return t >= start or t < end
|
return t >= start or t < end
|
||||||
return start <= t < end
|
return start <= t < end
|
||||||
|
|
||||||
|
|
||||||
def get_session(candle_time):
|
def get_session(candle_time):
|
||||||
for name in SESSIONS_EST:
|
for name in _active_sessions:
|
||||||
if name == "all":
|
|
||||||
continue
|
|
||||||
if in_session(candle_time, name):
|
if in_session(candle_time, name):
|
||||||
return name
|
return name
|
||||||
return "off_hours"
|
return "off_hours"
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const DAYS = [
|
|||||||
const STARTING_BALANCE = 10000;
|
const STARTING_BALANCE = 10000;
|
||||||
const PRESETS_KEY = 'nq_backtest_presets';
|
const PRESETS_KEY = 'nq_backtest_presets';
|
||||||
const RESULT_HISTORY_KEY = 'nq_backtest_recent_results';
|
const RESULT_HISTORY_KEY = 'nq_backtest_recent_results';
|
||||||
|
const RESULT_HISTORY_LIMIT = 10;
|
||||||
|
const DEFAULT_PRESET_NAME = 'Manual';
|
||||||
|
|
||||||
function formatMoney(v) {
|
function formatMoney(v) {
|
||||||
return v.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
return v.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||||
@@ -81,15 +83,14 @@ function savePresets(presets) {
|
|||||||
function loadRecentResults() {
|
function loadRecentResults() {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(localStorage.getItem(RESULT_HISTORY_KEY) || '[]');
|
const parsed = JSON.parse(localStorage.getItem(RESULT_HISTORY_KEY) || '[]');
|
||||||
return Array.isArray(parsed) ? parsed.slice(0,
|
return Array.isArray(parsed) ? parsed.slice(0, RESULT_HISTORY_LIMIT) : [];
|
||||||
5) : [];
|
|
||||||
} catch {
|
} catch {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveRecentResults(results) {
|
function saveRecentResults(results) {
|
||||||
localStorage.setItem(RESULT_HISTORY_KEY, JSON.stringify(results.slice(0, 5)));
|
localStorage.setItem(RESULT_HISTORY_KEY, JSON.stringify(results.slice(0, RESULT_HISTORY_LIMIT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function NumberInput({ label, value, onChange, min, max, step = 1 }) {
|
function NumberInput({ label, value, onChange, min, max, step = 1 }) {
|
||||||
@@ -195,6 +196,7 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
// Presets
|
// Presets
|
||||||
const [presets, setPresets] = useState(loadPresets);
|
const [presets, setPresets] = useState(loadPresets);
|
||||||
const [presetName, setPresetName] = useState('');
|
const [presetName, setPresetName] = useState('');
|
||||||
|
const [activePresetName, setActivePresetName] = useState(DEFAULT_PRESET_NAME);
|
||||||
const [showPresets, setShowPresets] = useState(false);
|
const [showPresets, setShowPresets] = useState(false);
|
||||||
const [recentResults, setRecentResults] = useState(loadRecentResults);
|
const [recentResults, setRecentResults] = useState(loadRecentResults);
|
||||||
|
|
||||||
@@ -243,12 +245,16 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
const updated = { ...presets, [name]: getSettings() };
|
const updated = { ...presets, [name]: getSettings() };
|
||||||
setPresets(updated);
|
setPresets(updated);
|
||||||
savePresets(updated);
|
savePresets(updated);
|
||||||
|
setActivePresetName(name);
|
||||||
setPresetName('');
|
setPresetName('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoadPreset = (name) => {
|
const handleLoadPreset = (name) => {
|
||||||
const preset = presets[name];
|
const preset = presets[name];
|
||||||
if (preset) applySettings(preset);
|
if (preset) {
|
||||||
|
applySettings(preset);
|
||||||
|
setActivePresetName(name);
|
||||||
|
}
|
||||||
setShowPresets(false);
|
setShowPresets(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -257,6 +263,41 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
delete updated[name];
|
delete updated[name];
|
||||||
setPresets(updated);
|
setPresets(updated);
|
||||||
savePresets(updated);
|
savePresets(updated);
|
||||||
|
if (activePresetName === name) {
|
||||||
|
setActivePresetName(DEFAULT_PRESET_NAME);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const exportRunParameters = (run) => {
|
||||||
|
const exportPayload = {
|
||||||
|
exportedAt: new Date().toISOString(),
|
||||||
|
runId: run.id,
|
||||||
|
presetName: run.presetName || DEFAULT_PRESET_NAME,
|
||||||
|
parameters: run.settings || {
|
||||||
|
timeframe: run.timeframe,
|
||||||
|
riskReward: run.riskReward,
|
||||||
|
},
|
||||||
|
queryParameters: run.queryParameters || null,
|
||||||
|
summary: {
|
||||||
|
dataset: run.dataset,
|
||||||
|
timeframe: run.timeframe,
|
||||||
|
riskReward: run.riskReward,
|
||||||
|
totalPnl: run.totalPnl,
|
||||||
|
winRate: run.winRate,
|
||||||
|
totalTrades: run.totalTrades,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const blob = new Blob([JSON.stringify(exportPayload, null, 2)], { type: 'application/json' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const anchor = document.createElement('a');
|
||||||
|
const safePreset = (run.presetName || DEFAULT_PRESET_NAME).replace(/[^a-z0-9_-]/gi, '_');
|
||||||
|
anchor.href = url;
|
||||||
|
anchor.download = `backtest-params-${run.dataset || 'dataset'}-${safePreset}-${run.id}.json`;
|
||||||
|
document.body.appendChild(anchor);
|
||||||
|
anchor.click();
|
||||||
|
document.body.removeChild(anchor);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDay = (day) => {
|
const toggleDay = (day) => {
|
||||||
@@ -426,18 +467,25 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
onBacktestComplete?.(backtestData);
|
onBacktestComplete?.(backtestData);
|
||||||
|
|
||||||
if (backtestData?.stats) {
|
if (backtestData?.stats) {
|
||||||
|
const snapshotSettings = {
|
||||||
|
...getSettings(),
|
||||||
|
dayFilter: [...dayFilter],
|
||||||
|
};
|
||||||
const snapshot = {
|
const snapshot = {
|
||||||
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||||||
runAt: new Date().toISOString(),
|
runAt: new Date().toISOString(),
|
||||||
dataset: selectedDataset,
|
dataset: selectedDataset,
|
||||||
|
presetName: activePresetName,
|
||||||
timeframe,
|
timeframe,
|
||||||
riskReward,
|
riskReward,
|
||||||
|
settings: snapshotSettings,
|
||||||
|
queryParameters: Object.fromEntries(params.entries()),
|
||||||
totalPnl: Number(backtestData.stats.total_pnl ?? 0),
|
totalPnl: Number(backtestData.stats.total_pnl ?? 0),
|
||||||
winRate: Number(backtestData.stats.win_rate ?? 0),
|
winRate: Number(backtestData.stats.win_rate ?? 0),
|
||||||
totalTrades: Number(backtestData.stats.total_trades ?? 0),
|
totalTrades: Number(backtestData.stats.total_trades ?? 0),
|
||||||
};
|
};
|
||||||
setRecentResults((prev) => {
|
setRecentResults((prev) => {
|
||||||
const next = [snapshot, ...prev].slice(0, 5);
|
const next = [snapshot, ...prev].slice(0, RESULT_HISTORY_LIMIT);
|
||||||
saveRecentResults(next);
|
saveRecentResults(next);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
@@ -463,7 +511,7 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
requireFvgObConfluence, asianSweepOnly, dayFilter,
|
requireFvgObConfluence, asianSweepOnly, dayFilter,
|
||||||
useBreakEven, beTriggerRr,
|
useBreakEven, beTriggerRr,
|
||||||
usePartialTp, partialTpRr, partialTpPercent,
|
usePartialTp, partialTpRr, partialTpPercent,
|
||||||
maxDailyLoss, maxConsecutiveLosses, onBacktestComplete,
|
maxDailyLoss, maxConsecutiveLosses, onBacktestComplete, activePresetName,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const runMonteCarlo = useCallback(async () => {
|
const runMonteCarlo = useCallback(async () => {
|
||||||
@@ -796,22 +844,30 @@ export function BacktestingTab({ datasets = [], selectedDataset, onDatasetChange
|
|||||||
<p className="text-[11px] text-[#525252] font-mono uppercase tracking-widest mb-1">Results</p>
|
<p className="text-[11px] text-[#525252] font-mono uppercase tracking-widest mb-1">Results</p>
|
||||||
<h2 className="text-[20px] font-semibold tracking-tight">Backtest Summary</h2>
|
<h2 className="text-[20px] font-semibold tracking-tight">Backtest Summary</h2>
|
||||||
<p className="text-[12px] text-[#737373] font-mono mt-2">CSV: {selectedDataset}</p>
|
<p className="text-[12px] text-[#737373] font-mono mt-2">CSV: {selectedDataset}</p>
|
||||||
|
<p className="text-[12px] text-[#737373] font-mono mt-1">Preset: {activePresetName}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-6 border border-[#1a1a1a] bg-black/30 p-4">
|
<div className="mb-6 border border-[#1a1a1a] bg-black/30 p-4">
|
||||||
<p className="text-[11px] text-[#525252] font-mono uppercase tracking-widest mb-3">Last 5 Runs</p>
|
<p className="text-[11px] text-[#525252] font-mono uppercase tracking-widest mb-3">Last {RESULT_HISTORY_LIMIT} Runs</p>
|
||||||
{recentResults.length === 0 ? (
|
{recentResults.length === 0 ? (
|
||||||
<p className="text-[12px] text-[#737373] font-mono">No previous runs saved yet.</p>
|
<p className="text-[12px] text-[#737373] font-mono">No previous runs saved yet.</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{recentResults.map((run) => (
|
{recentResults.map((run) => (
|
||||||
<div key={run.id} className="grid grid-cols-2 md:grid-cols-6 gap-2 text-[12px] font-mono border border-[#1a1a1a] bg-black/40 px-3 py-2">
|
<div key={run.id} className="grid grid-cols-2 md:grid-cols-8 gap-2 text-[12px] font-mono border border-[#1a1a1a] bg-black/40 px-3 py-2 items-center">
|
||||||
<span className="text-[#a3a3a3]">{new Date(run.runAt).toLocaleString()}</span>
|
<span className="text-[#a3a3a3]">{new Date(run.runAt).toLocaleString()}</span>
|
||||||
<span className="text-[#fafafa]">{run.dataset}</span>
|
<span className="text-[#fafafa]">{run.dataset || 'Unknown CSV'}</span>
|
||||||
<span className="text-[#a3a3a3]">{run.timeframe}m</span>
|
<span className="text-[#a3a3a3]">{run.timeframe ?? '-'}m</span>
|
||||||
<span className={run.totalPnl >= 0 ? 'text-[#10b981]' : 'text-[#ef4444]'}>${formatMoney(run.totalPnl)}</span>
|
<span className="text-[#a78bfa]">{run.presetName || DEFAULT_PRESET_NAME}</span>
|
||||||
<span className="text-[#60a5fa]">{run.winRate.toFixed(1)}%</span>
|
<span className={(run.totalPnl ?? 0) >= 0 ? 'text-[#10b981]' : 'text-[#ef4444]'}>${formatMoney(Number(run.totalPnl ?? 0))}</span>
|
||||||
<span className="text-[#fafafa]">{run.totalTrades} trades</span>
|
<span className="text-[#60a5fa]">{Number(run.winRate ?? 0).toFixed(1)}%</span>
|
||||||
|
<span className="text-[#fafafa]">{Number(run.totalTrades ?? 0)} trades</span>
|
||||||
|
<button
|
||||||
|
onClick={() => exportRunParameters(run)}
|
||||||
|
className="px-2 py-1 text-[11px] border border-[#262626] text-[#d4d4d8] hover:text-[#fafafa] hover:border-[#404040] transition-colors"
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -100,9 +100,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
const [asianSweepOnlyModes, setAsianSweepOnlyModes] = useState('true,false');
|
const [asianSweepOnlyModes, setAsianSweepOnlyModes] = useState('true,false');
|
||||||
const [useBreakEvenModes, setUseBreakEvenModes] = useState('false,true');
|
const [useBreakEvenModes, setUseBreakEvenModes] = useState('false,true');
|
||||||
const [beTriggerRrValues, setBeTriggerRrValues] = useState('1.0,1.5,2.0');
|
const [beTriggerRrValues, setBeTriggerRrValues] = useState('1.0,1.5,2.0');
|
||||||
const [usePartialTpModes, setUsePartialTpModes] = useState('false,true');
|
|
||||||
const [partialTpRrValues, setPartialTpRrValues] = useState('1.0,1.5,2.0');
|
|
||||||
const [partialTpPercentValues, setPartialTpPercentValues] = useState('50');
|
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [progressPct, setProgressPct] = useState(0);
|
const [progressPct, setProgressPct] = useState(0);
|
||||||
@@ -158,9 +155,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asianSweepOnly: params.asian_sweep_only === true || params.asian_sweep_only === 'true',
|
asianSweepOnly: params.asian_sweep_only === true || params.asian_sweep_only === 'true',
|
||||||
useBreakEven: params.use_break_even === true || params.use_break_even === 'true',
|
useBreakEven: params.use_break_even === true || params.use_break_even === 'true',
|
||||||
beTriggerRr: Number(params.be_trigger_rr ?? 1.0),
|
beTriggerRr: Number(params.be_trigger_rr ?? 1.0),
|
||||||
usePartialTp: params.use_partial_tp === true || params.use_partial_tp === 'true',
|
|
||||||
partialTpRr: Number(params.partial_tp_rr ?? 1.0),
|
|
||||||
partialTpPercent: Number(params.partial_tp_percent ?? 50),
|
|
||||||
dayFilter: [0, 1, 2, 3, 4],
|
dayFilter: [0, 1, 2, 3, 4],
|
||||||
maxDailyLoss: 0,
|
maxDailyLoss: 0,
|
||||||
maxConsecutiveLosses: 0,
|
maxConsecutiveLosses: 0,
|
||||||
@@ -250,9 +244,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
* count(asianSweepOnlyModes)
|
* count(asianSweepOnlyModes)
|
||||||
* count(useBreakEvenModes)
|
* count(useBreakEvenModes)
|
||||||
* count(beTriggerRrValues)
|
* count(beTriggerRrValues)
|
||||||
* count(usePartialTpModes)
|
|
||||||
* count(partialTpRrValues)
|
|
||||||
* count(partialTpPercentValues)
|
|
||||||
* Math.max(1, sweepFactor || boolCount);
|
* Math.max(1, sweepFactor || boolCount);
|
||||||
}, [
|
}, [
|
||||||
sessions,
|
sessions,
|
||||||
@@ -271,9 +262,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asianSweepOnlyModes,
|
asianSweepOnlyModes,
|
||||||
useBreakEvenModes,
|
useBreakEvenModes,
|
||||||
beTriggerRrValues,
|
beTriggerRrValues,
|
||||||
usePartialTpModes,
|
|
||||||
partialTpRrValues,
|
|
||||||
partialTpPercentValues,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const mcPnlHistogram = useMemo(() => {
|
const mcPnlHistogram = useMemo(() => {
|
||||||
@@ -358,9 +346,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asian_sweep_only_modes: parseBoolList(asianSweepOnlyModes, [true, false]),
|
asian_sweep_only_modes: parseBoolList(asianSweepOnlyModes, [true, false]),
|
||||||
use_break_even_modes: parseBoolList(useBreakEvenModes, [false, true]),
|
use_break_even_modes: parseBoolList(useBreakEvenModes, [false, true]),
|
||||||
be_trigger_rr_values: parseFloatList(beTriggerRrValues, [1.0, 1.5, 2.0]),
|
be_trigger_rr_values: parseFloatList(beTriggerRrValues, [1.0, 1.5, 2.0]),
|
||||||
use_partial_tp_modes: parseBoolList(usePartialTpModes, [false, true]),
|
|
||||||
partial_tp_rr_values: parseFloatList(partialTpRrValues, [1.0, 1.5, 2.0]),
|
|
||||||
partial_tp_percent_values: parseFloatList(partialTpPercentValues, [50]),
|
|
||||||
};
|
};
|
||||||
const seedValue = comboSamplingSeed.trim();
|
const seedValue = comboSamplingSeed.trim();
|
||||||
if (seedValue) {
|
if (seedValue) {
|
||||||
@@ -481,9 +466,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asianSweepOnlyModes,
|
asianSweepOnlyModes,
|
||||||
useBreakEvenModes,
|
useBreakEvenModes,
|
||||||
beTriggerRrValues,
|
beTriggerRrValues,
|
||||||
usePartialTpModes,
|
|
||||||
partialTpRrValues,
|
|
||||||
partialTpPercentValues,
|
|
||||||
activateTopResult,
|
activateTopResult,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -523,9 +505,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asian_sweep_only: String(targetParams.asian_sweep_only ?? false),
|
asian_sweep_only: String(targetParams.asian_sweep_only ?? false),
|
||||||
use_break_even: String(targetParams.use_break_even ?? false),
|
use_break_even: String(targetParams.use_break_even ?? false),
|
||||||
be_trigger_rr: String(targetParams.be_trigger_rr ?? 1.0),
|
be_trigger_rr: String(targetParams.be_trigger_rr ?? 1.0),
|
||||||
use_partial_tp: String(targetParams.use_partial_tp ?? false),
|
|
||||||
partial_tp_rr: String(targetParams.partial_tp_rr ?? 1.0),
|
|
||||||
partial_tp_percent: String(targetParams.partial_tp_percent ?? 50),
|
|
||||||
runs: String(mcRuns),
|
runs: String(mcRuns),
|
||||||
shuffle_trades: String(mcShuffleTrades),
|
shuffle_trades: String(mcShuffleTrades),
|
||||||
pnl_variation_pct: String(mcVariationPct),
|
pnl_variation_pct: String(mcVariationPct),
|
||||||
@@ -663,9 +642,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
asian_sweep_only: p.asianSweepOnly,
|
asian_sweep_only: p.asianSweepOnly,
|
||||||
use_break_even: p.useBreakEven,
|
use_break_even: p.useBreakEven,
|
||||||
be_trigger_rr: p.beTriggerRr,
|
be_trigger_rr: p.beTriggerRr,
|
||||||
use_partial_tp: p.usePartialTp,
|
|
||||||
partial_tp_rr: p.partialTpRr,
|
|
||||||
partial_tp_percent: p.partialTpPercent,
|
|
||||||
},
|
},
|
||||||
timeframe: p.timeframe,
|
timeframe: p.timeframe,
|
||||||
riskReward: p.riskReward,
|
riskReward: p.riskReward,
|
||||||
@@ -790,9 +766,6 @@ export function OptimizerTab({ datasets = [], selectedDataset, onDatasetChange,
|
|||||||
<TextListInput label="Asian Sweep Only" value={asianSweepOnlyModes} onChange={setAsianSweepOnlyModes} placeholder="true,false" />
|
<TextListInput label="Asian Sweep Only" value={asianSweepOnlyModes} onChange={setAsianSweepOnlyModes} placeholder="true,false" />
|
||||||
<TextListInput label="Use Break-Even" value={useBreakEvenModes} onChange={setUseBreakEvenModes} placeholder="false,true" />
|
<TextListInput label="Use Break-Even" value={useBreakEvenModes} onChange={setUseBreakEvenModes} placeholder="false,true" />
|
||||||
<TextListInput label="BE Trigger RR" value={beTriggerRrValues} onChange={setBeTriggerRrValues} placeholder="1.0,1.5,2.0" />
|
<TextListInput label="BE Trigger RR" value={beTriggerRrValues} onChange={setBeTriggerRrValues} placeholder="1.0,1.5,2.0" />
|
||||||
<TextListInput label="Use Partial TP" value={usePartialTpModes} onChange={setUsePartialTpModes} placeholder="false,true" />
|
|
||||||
<TextListInput label="Partial TP RR" value={partialTpRrValues} onChange={setPartialTpRrValues} placeholder="1.0,1.5,2.0" />
|
|
||||||
<TextListInput label="Partial TP %" value={partialTpPercentValues} onChange={setPartialTpPercentValues} placeholder="50" />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 border border-[#1a1a1a] bg-black/40 p-4">
|
<div className="mt-6 border border-[#1a1a1a] bg-black/40 p-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user