feat: 实盘模拟(live) + 组合回测MVP(portfolio)

[live] 实盘模拟 vnpy+miniQMT 直连(supervisor 轮询, 前后端):
- sanguo_live: LiveTradingEngine + AShareCtaTemplate(定寸/禁做空) + runner_supervisor(DB驱动) + persistence(4表WAL)
- sanguo_api/routes_live: 9路由(create/start/stop/positions/trades/account/status)
- frontend live: New/List/Monitor + api/live.ts; config/live.yaml

[portfolio] 组合回测 MVP(BulletTrade, 链路代码完成待验证):
- runner_backtest 加 JSON 入口(--json, BacktestEngine 顶层 import)
- sanguo_api/routes_portfolio: POST /portfolio/backtest SSH 触发 VPS 跑
- frontend PortfolioBacktest.vue + api/portfolio.ts: 表单+结果+净值曲线
- 路由/菜单注册(/backtest/portfolio 组合回测)
- 已知: MVP 链路未端到端验证, agent 改至中途被停; 待 Mac 起服务联调
This commit is contained in:
2026-07-18 20:04:16 +08:00
parent a68cf4905e
commit 96b1924fd5
24 changed files with 3550 additions and 9 deletions
+125
View File
@@ -0,0 +1,125 @@
import { apiClient } from './client'
/** 实盘模拟账户(API 返回行) */
export interface LiveAccount {
id: number
name: string
account: string
vt_symbol: string
strategy_class: string
strategy_name: string
/** JSON 字符串,前端 JSON.parse 得到策略参数 */
setting: string
status: string
interval: string
initial_capital: number
connect_wait_sec?: number
init_wait_sec?: number
mini_path?: string
error_msg?: string | null
created_at?: string
updated_at?: string
/** 列表端点附带(单账户 GET 不含) */
latest_equity?: number | null
latest_date?: string | null
total_return?: number | null
position_count?: number
}
export interface LiveCreateRequest {
name: string
account: string
vt_symbol: string
strategy_class: string
strategy_name: string
setting: Record<string, unknown>
interval: string
initial_capital: number
connect_wait_sec?: number
init_wait_sec?: number
mini_path?: string
}
export interface LiveStatus {
account_id: number
status: string
name: string
account: string
vt_symbol: string
strategy_name: string
updated_at?: string
error_msg?: string
}
export interface LivePosition {
symbol: string
volume: number
frozen: number
avg_price: number
updated_at?: string
}
export interface LiveTrade {
account_id: number
strategy_name: string
symbol: string
direction: string
offset: string
price: number
volume: number
traded_at: string
vt_tradeid?: string
}
export interface LiveBalance {
account_id?: number
date?: string
cash?: number
market_value?: number
total?: number
}
export async function createLive(req: LiveCreateRequest): Promise<{ accountId: number; status: string }> {
const { data } = await apiClient.post<{ account_id: number; status: string }>('/live/create', req)
return { accountId: data.account_id, status: data.status }
}
export async function listLives(): Promise<LiveAccount[]> {
const { data } = await apiClient.get<{ accounts: LiveAccount[] }>('/live')
return data.accounts
}
export async function getLive(aid: number): Promise<LiveAccount> {
const { data } = await apiClient.get<LiveAccount>(`/live/${aid}`)
return data
}
export async function startLive(aid: number): Promise<{ accountId: number; status: string }> {
const { data } = await apiClient.post<{ account_id: number; status: string }>(`/live/${aid}/start`)
return { accountId: data.account_id, status: data.status }
}
export async function stopLive(aid: number): Promise<{ accountId: number; status: string }> {
const { data } = await apiClient.post<{ account_id: number; status: string }>(`/live/${aid}/stop`)
return { accountId: data.account_id, status: data.status }
}
export async function getLivePositions(aid: number): Promise<LivePosition[]> {
const { data } = await apiClient.get<LivePosition[]>(`/live/${aid}/positions`)
return data
}
export async function getLiveTrades(aid: number): Promise<LiveTrade[]> {
const { data } = await apiClient.get<LiveTrade[]>(`/live/${aid}/trades`)
return data
}
export async function getLiveAccountBalance(aid: number): Promise<LiveBalance> {
const { data } = await apiClient.get<LiveBalance>(`/live/${aid}/account`)
return data ?? {}
}
export async function getLiveStatus(aid: number): Promise<LiveStatus> {
const { data } = await apiClient.get<LiveStatus>(`/live/${aid}/status`)
return data
}