Dev backtest (#1134)

This commit is contained in:
Rick
2025-11-28 21:34:27 +08:00
committed by GitHub
parent 04f006abad
commit 11b6c6ba3e
39 changed files with 9293 additions and 125 deletions

View File

@@ -12,12 +12,53 @@ import type {
UpdateModelConfigRequest,
UpdateExchangeConfigRequest,
CompetitionData,
BacktestRunsResponse,
BacktestStartConfig,
BacktestStatusPayload,
BacktestEquityPoint,
BacktestTradeEvent,
BacktestMetrics,
BacktestRunMetadata,
} from '../types'
import { CryptoService } from './crypto'
import { httpClient } from './httpClient'
const API_BASE = '/api'
// Helper function to get auth headers
function getAuthHeaders(): Record<string, string> {
const token = localStorage.getItem('auth_token')
const headers: Record<string, string> = {
'Content-Type': 'application/json',
}
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
return headers
}
async function handleJSONResponse<T>(res: Response): Promise<T> {
const text = await res.text()
if (!res.ok) {
let message = text || res.statusText
try {
const data = text ? JSON.parse(text) : null
if (data && typeof data === 'object') {
message = data.error || data.message || message
}
} catch {
/* ignore JSON parse errors */
}
throw new Error(message || '请求失败')
}
if (!text) {
return {} as T
}
return JSON.parse(text) as T
}
export const api = {
// AI交易员管理接口
async getTraders(): Promise<TraderInfo[]> {
@@ -106,6 +147,16 @@ export const api = {
return result.data!
},
async getPromptTemplates(): Promise<string[]> {
const res = await fetch(`${API_BASE}/prompt-templates`)
if (!res.ok) throw new Error('获取提示词模板失败')
const data = await res.json()
if (Array.isArray(data.templates)) {
return data.templates.map((item: { name: string }) => item.name)
}
return []
},
async updateModelConfigs(request: UpdateModelConfigRequest): Promise<void> {
// 获取RSA公钥
const publicKey = await CryptoService.fetchPublicKey()
@@ -341,4 +392,175 @@ export const api = {
if (!result.success) throw new Error('获取服务器IP失败')
return result.data!
},
// Backtest APIs
async getBacktestRuns(params?: {
state?: string
search?: string
limit?: number
offset?: number
}): Promise<BacktestRunsResponse> {
const query = new URLSearchParams()
if (params?.state) query.set('state', params.state)
if (params?.search) query.set('search', params.search)
if (params?.limit) query.set('limit', String(params.limit))
if (params?.offset) query.set('offset', String(params.offset))
const res = await fetch(
`${API_BASE}/backtest/runs${query.toString() ? `?${query}` : ''}`,
{
headers: getAuthHeaders(),
}
)
return handleJSONResponse<BacktestRunsResponse>(res)
},
async startBacktest(config: BacktestStartConfig): Promise<BacktestRunMetadata> {
const res = await fetch(`${API_BASE}/backtest/start`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ config }),
})
return handleJSONResponse<BacktestRunMetadata>(res)
},
async pauseBacktest(runId: string): Promise<BacktestRunMetadata> {
const res = await fetch(`${API_BASE}/backtest/pause`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ run_id: runId }),
})
return handleJSONResponse<BacktestRunMetadata>(res)
},
async resumeBacktest(runId: string): Promise<BacktestRunMetadata> {
const res = await fetch(`${API_BASE}/backtest/resume`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ run_id: runId }),
})
return handleJSONResponse<BacktestRunMetadata>(res)
},
async stopBacktest(runId: string): Promise<BacktestRunMetadata> {
const res = await fetch(`${API_BASE}/backtest/stop`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ run_id: runId }),
})
return handleJSONResponse<BacktestRunMetadata>(res)
},
async updateBacktestLabel(
runId: string,
label: string
): Promise<BacktestRunMetadata> {
const res = await fetch(`${API_BASE}/backtest/label`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ run_id: runId, label }),
})
return handleJSONResponse<BacktestRunMetadata>(res)
},
async deleteBacktestRun(runId: string): Promise<void> {
const res = await fetch(`${API_BASE}/backtest/delete`, {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ run_id: runId }),
})
if (!res.ok) {
throw new Error(await res.text())
}
},
async getBacktestStatus(runId: string): Promise<BacktestStatusPayload> {
const res = await fetch(`${API_BASE}/backtest/status?run_id=${runId}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<BacktestStatusPayload>(res)
},
async getBacktestEquity(
runId: string,
timeframe?: string,
limit?: number
): Promise<BacktestEquityPoint[]> {
const query = new URLSearchParams({ run_id: runId })
if (timeframe) query.set('tf', timeframe)
if (limit) query.set('limit', String(limit))
const res = await fetch(`${API_BASE}/backtest/equity?${query}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<BacktestEquityPoint[]>(res)
},
async getBacktestTrades(
runId: string,
limit = 200
): Promise<BacktestTradeEvent[]> {
const query = new URLSearchParams({
run_id: runId,
limit: String(limit),
})
const res = await fetch(`${API_BASE}/backtest/trades?${query}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<BacktestTradeEvent[]>(res)
},
async getBacktestMetrics(runId: string): Promise<BacktestMetrics> {
const res = await fetch(`${API_BASE}/backtest/metrics?run_id=${runId}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<BacktestMetrics>(res)
},
async getBacktestTrace(
runId: string,
cycle?: number
): Promise<DecisionRecord> {
const query = new URLSearchParams({ run_id: runId })
if (cycle) query.set('cycle', String(cycle))
const res = await fetch(`${API_BASE}/backtest/trace?${query}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<DecisionRecord>(res)
},
async getBacktestDecisions(
runId: string,
limit = 20,
offset = 0
): Promise<DecisionRecord[]> {
const query = new URLSearchParams({
run_id: runId,
limit: String(limit),
offset: String(offset),
})
const res = await fetch(`${API_BASE}/backtest/decisions?${query}`, {
headers: getAuthHeaders(),
})
return handleJSONResponse<DecisionRecord[]>(res)
},
async exportBacktest(runId: string): Promise<Blob> {
const res = await fetch(`${API_BASE}/backtest/export?run_id=${runId}`, {
headers: getAuthHeaders(),
})
if (!res.ok) {
const text = await res.text()
try {
const data = text ? JSON.parse(text) : null
throw new Error(
data?.error || data?.message || text || '导出失败,请稍后再试'
)
} catch (err) {
if (err instanceof Error && err.message) {
throw err
}
throw new Error(text || '导出失败,请稍后再试')
}
}
return res.blob()
},
}