feat: wavelets simulation
This commit is contained in:
@@ -4,7 +4,7 @@ import { Waves, Play, Save, Trash2, X } from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
import { api } from '../hooks/useApi'
|
||||
import {
|
||||
WaveletTriggerKind, WaveletPositionMode, WaveletExitVariant, WaveletOptimizationResult,
|
||||
WaveletTriggerKind, WaveletPositionMode, WaveletExitVariant, WaveletOptimizationResult, WaveletDateRange,
|
||||
WAVELET_TRIGGER_KINDS, runFullOptimization, formatBandIndex,
|
||||
} from '../lib/waveletTrade'
|
||||
|
||||
@@ -57,6 +57,9 @@ export default function WaveletsSimulation() {
|
||||
// ── Form state ──────────────────────────────────────────────────────────────
|
||||
const [selectedInstruments, setSelectedInstruments] = useState<Set<string>>(new Set())
|
||||
const [period, setPeriod] = useState('2y')
|
||||
const [useCustomRange, setUseCustomRange] = useState(false)
|
||||
const [customStart, setCustomStart] = useState('2013-01-01')
|
||||
const [customEnd, setCustomEnd] = useState('2019-12-31')
|
||||
const [levels, setLevels] = useState(4)
|
||||
const [lookbacks, setLookbacks] = useState<number[]>([90, 130])
|
||||
const [wavelets, setWavelets] = useState<Set<string>>(new Set(['gmw']))
|
||||
@@ -94,17 +97,21 @@ export default function WaveletsSimulation() {
|
||||
|
||||
const runOptimization = async () => {
|
||||
if (selectedInstruments.size === 0 || lookbacks.length === 0 || wavelets.size === 0 || kinds.size === 0) return
|
||||
if (useCustomRange && !customStart) return
|
||||
setRunning(true)
|
||||
setFailures([])
|
||||
setResults([])
|
||||
setExpandedRow(null)
|
||||
|
||||
const dateRange: WaveletDateRange = useCustomRange ? { startDate: customStart, endDate: customEnd || undefined } : null
|
||||
|
||||
// Create the persisted run up-front so results can be appended incrementally —
|
||||
// a run spanning many combos shouldn't lose progress to a refresh/interruption.
|
||||
const form = {
|
||||
instruments: [...selectedInstruments], period, levels, wavelets: [...wavelets],
|
||||
lookbacks, bands: [...bands], kinds: [...kinds], modes: [...modes],
|
||||
takeProfits, stopLosses, method, filterBand,
|
||||
customRange: dateRange,
|
||||
}
|
||||
const created = await api.post('/wavelet/simulations', { name: simName, form, results: [] }).then(r => r.data)
|
||||
setActiveSimId(created.id)
|
||||
@@ -126,6 +133,7 @@ export default function WaveletsSimulation() {
|
||||
},
|
||||
method,
|
||||
filterBand,
|
||||
dateRange,
|
||||
)
|
||||
setRunning(false)
|
||||
qc.invalidateQueries({ queryKey: ['wavelet-simulations'] })
|
||||
@@ -212,11 +220,26 @@ export default function WaveletsSimulation() {
|
||||
|
||||
<div className="grid grid-cols-4 gap-4 text-xs">
|
||||
<div>
|
||||
<div className="text-slate-500 mb-1">Période</div>
|
||||
<select value={period} onChange={e => setPeriod(e.target.value)}
|
||||
className="bg-dark-900 border border-slate-700/40 rounded px-2 py-1 text-white w-full">
|
||||
{['6mo', '1y', '2y', '5y'].map(p => <option key={p} value={p}>{p}</option>)}
|
||||
</select>
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-slate-500">Période</span>
|
||||
<label className="flex items-center gap-1 cursor-pointer text-slate-500">
|
||||
<input type="checkbox" checked={useCustomRange} onChange={e => setUseCustomRange(e.target.checked)} /> Personnalisée
|
||||
</label>
|
||||
</div>
|
||||
{useCustomRange ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<input type="date" value={customStart} onChange={e => setCustomStart(e.target.value)}
|
||||
className="bg-dark-900 border border-slate-700/40 rounded px-1.5 py-1 text-white w-full" />
|
||||
<span className="text-slate-600">→</span>
|
||||
<input type="date" value={customEnd} onChange={e => setCustomEnd(e.target.value)}
|
||||
className="bg-dark-900 border border-slate-700/40 rounded px-1.5 py-1 text-white w-full" />
|
||||
</div>
|
||||
) : (
|
||||
<select value={period} onChange={e => setPeriod(e.target.value)}
|
||||
className="bg-dark-900 border border-slate-700/40 rounded px-2 py-1 text-white w-full">
|
||||
{['6mo', '1y', '2y', '5y'].map(p => <option key={p} value={p}>{p}</option>)}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-slate-500 mb-1">Niveaux</div>
|
||||
@@ -316,7 +339,7 @@ export default function WaveletsSimulation() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button onClick={runOptimization} disabled={running || selectedInstruments.size === 0}
|
||||
<button onClick={runOptimization} disabled={running || selectedInstruments.size === 0 || (useCustomRange && !customStart)}
|
||||
className="flex items-center gap-1.5 px-4 py-2 rounded bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium disabled:opacity-40 transition-colors">
|
||||
<Play className="w-4 h-4" /> {running ? 'Optimisation en cours…' : "Lancer l'optimisation"}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user