feat(shadow-desk): P1-b/c 影子柜台常驻进程+本地撮合broker+前端引擎选择: ShadowBroker(实时价±滑点即时成交/佣金印花最低佣金/A股整手/T+1日锁/资金不足拒单/均价加权/duck-typed BrokerBase协议); runner挂bullet_trade LiveEngine同实盘唯一差=broker_factory换影子(双轨对账基础),成交落paper_trades+30s快照落持仓净值; CLI单实例文件锁(python -m sanguo_trader.shadow); paper_accounts加engine列(eod_replay/shadow迁移); 前端:模拟盘新建组合卡撮合引擎单选+列表影子/日终徽标; 10 broker单测 [vps]
This commit is contained in:
@@ -21,6 +21,8 @@ export interface PaperCreate {
|
||||
pool?: string
|
||||
max_pool?: number
|
||||
benchmark?: string
|
||||
// 撮合引擎(影子柜台 P1):eod_replay=日终回放 / shadow=影子柜台
|
||||
engine?: string
|
||||
}
|
||||
|
||||
export interface PaperAccount {
|
||||
@@ -30,6 +32,7 @@ export interface PaperAccount {
|
||||
interval: string
|
||||
status: string
|
||||
strategy_type?: string
|
||||
engine?: string
|
||||
symbols?: string
|
||||
initial_capital?: number
|
||||
start_date?: string
|
||||
|
||||
@@ -13,6 +13,8 @@ export interface PortfolioBacktestReq {
|
||||
stamp_duty_rate?: number
|
||||
min_commission?: number
|
||||
slippage?: number
|
||||
// K线周期(组合回放暂仅日线 d)
|
||||
interval?: string
|
||||
}
|
||||
|
||||
export interface EquityPoint {
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
background: rgba(255, 176, 0, 0.1);
|
||||
border-color: rgba(255, 176, 0, 0.3);
|
||||
}
|
||||
.chip-shadow {
|
||||
color: var(--brand);
|
||||
background: rgba(0, 229, 255, 0.1);
|
||||
border-color: rgba(0, 229, 255, 0.3);
|
||||
margin-left: 4px;
|
||||
}
|
||||
.chip-portfolio {
|
||||
color: var(--amber);
|
||||
background: rgba(255, 176, 0, 0.08);
|
||||
|
||||
@@ -188,7 +188,12 @@ async function saveEdit(): Promise<void> {
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模式" width="80">
|
||||
<template #default="{ row }"><span class="chip chip-paper">{{ modeLabel[row.mode] || row.mode }}</span></template>
|
||||
<template #default="{ row }">
|
||||
<span class="chip chip-paper">{{ modeLabel[row.mode] || row.mode }}</span>
|
||||
<span v-if="row.strategy_type === 'portfolio'" class="chip chip-shadow" title="影子柜台=盘中实时本地撮合 / 日终回放=每晚全量重放">
|
||||
{{ row.engine === 'shadow' ? '影子' : '日终' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="频率" width="70">
|
||||
<template #default="{ row }"><span class="mono">{{ row.interval }}</span></template>
|
||||
|
||||
@@ -115,6 +115,8 @@ onMounted(async () => {
|
||||
|
||||
const isPortfolio = computed(() => strategyType.value === 'portfolio')
|
||||
const portfolioStrategy = ref('all_weather')
|
||||
// 撮合引擎:日终回放(NAS 每晚 20:30) / 影子柜台(VPS 盘中实时本地撮合)
|
||||
const engine = ref<'eod_replay' | 'shadow'>('eod_replay')
|
||||
|
||||
function setMode(m: string): void {
|
||||
// 组合类型锁定实走:回放卡片不可选(历史回放走「组合回测」页)
|
||||
@@ -149,9 +151,12 @@ async function onSubmit(): Promise<void> {
|
||||
payload.pool = poolForm.pool
|
||||
payload.max_pool = Number(poolForm.max_pool)
|
||||
payload.benchmark = poolForm.benchmark
|
||||
payload.engine = engine.value
|
||||
}
|
||||
const aid = await createPaper(payload)
|
||||
ElMessage.success(`已创建模拟盘 #${aid}(今晚 20:30 起每日结算)`)
|
||||
ElMessage.success(strategyType.value === 'portfolio' && engine.value === 'shadow'
|
||||
? `已创建影子柜台模拟盘 #${aid}(VPS 柜台运行期间盘中实时结算)`
|
||||
: `已创建模拟盘 #${aid}(今晚 20:30 起每日结算)`)
|
||||
router.push(payload.mode === 'live' ? `/paper/live/${aid}` : `/paper/result/${aid}`)
|
||||
} catch (e: unknown) {
|
||||
ElMessage.error(e instanceof Error ? e.message : '创建失败')
|
||||
@@ -245,6 +250,17 @@ function onSymbols(v: string): void {
|
||||
<el-option v-for="b in BENCH_OPTIONS" :key="b.value" :label="b.label" :value="b.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="撮合引擎">
|
||||
<el-radio-group v-model="engine">
|
||||
<el-radio-button value="eod_replay">日终回放</el-radio-button>
|
||||
<el-radio-button value="shadow">影子柜台</el-radio-button>
|
||||
</el-radio-group>
|
||||
<span class="muted form-hint">
|
||||
{{ engine === 'shadow'
|
||||
? 'VPS 盘中实时行情本地撮合(需 VPS 影子柜台进程运行中)'
|
||||
: '每晚 20:30 全量重放结算(NAS)' }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form v-else :model="form" label-width="120px">
|
||||
<el-form-item label="标的(逗号分隔)">
|
||||
|
||||
Reference in New Issue
Block a user