/** * Composable for managing backtest state across components. */ import { ref, computed } from 'vue' import type { BacktestResult, BacktestSummary, StrategyInfo, SymbolInfo } from '@/api/types' import { getStrategies, getSymbols, getBacktests, getBacktest, runBacktest, deleteBacktest } from '@/api/client' import type { BacktestRequest } from '@/api/types' // Shared state const strategies = ref([]) const symbols = ref([]) const runs = ref([]) const currentResult = ref(null) const selectedRuns = ref([]) const loading = ref(false) const error = ref(null) // Computed const symbolsByMarket = computed(() => { const grouped: Record = {} for (const s of symbols.value) { const key = `${s.market_type}` if (!grouped[key]) grouped[key] = [] grouped[key].push(s) } return grouped }) export function useBacktest() { /** * Load strategies and symbols on app init. */ async function init() { try { const [stratRes, symRes] = await Promise.all([ getStrategies(), getSymbols(), ]) strategies.value = stratRes.strategies symbols.value = symRes.symbols } catch (e) { error.value = `Failed to load initial data: ${e}` } } /** * Refresh the run history list. */ async function refreshRuns() { try { const res = await getBacktests({ limit: 100 }) runs.value = res.runs } catch (e) { error.value = `Failed to load runs: ${e}` } } /** * Execute a new backtest. */ async function executeBacktest(request: BacktestRequest) { loading.value = true error.value = null try { const result = await runBacktest(request) currentResult.value = result await refreshRuns() return result } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e) error.value = `Backtest failed: ${msg}` throw e } finally { loading.value = false } } /** * Load a specific run by ID. */ async function loadRun(runId: string) { loading.value = true error.value = null try { const result = await getBacktest(runId) currentResult.value = result return result } catch (e) { error.value = `Failed to load run: ${e}` throw e } finally { loading.value = false } } /** * Delete a run. */ async function removeRun(runId: string) { try { await deleteBacktest(runId) await refreshRuns() if (currentResult.value?.run_id === runId) { currentResult.value = null } selectedRuns.value = selectedRuns.value.filter(id => id !== runId) } catch (e) { error.value = `Failed to delete run: ${e}` } } /** * Toggle run selection for comparison. */ function toggleRunSelection(runId: string) { const idx = selectedRuns.value.indexOf(runId) if (idx >= 0) { selectedRuns.value.splice(idx, 1) } else if (selectedRuns.value.length < 5) { selectedRuns.value.push(runId) } } /** * Clear all selections. */ function clearSelections() { selectedRuns.value = [] } return { // State strategies, symbols, symbolsByMarket, runs, currentResult, selectedRuns, loading, error, // Actions init, refreshRuns, executeBacktest, loadRun, removeRun, toggleRunSelection, clearSelections, } }