Files
sanguo_vnpy_v2/frontend/src/mock/data.ts
T

468 lines
25 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 业务假数据 —— 覆盖各接口的核心展示数据(样板用,连真实后端时不触发) */
import { genEquity, genBenchmark, genRisk, genDailyPnl, genPaperEquity, genDates } from './series'
const N = 117
/* ========== 策略 ========== */
export const strategiesMock = {
strategies: [
{ name: '双均线', class_name: 'AShareDoubleMaStrategy' },
{ name: '布林通道', class_name: 'AShareBollStrategy' },
{ name: 'ATR 突破', class_name: 'AShareAtrStrategy' },
{ name: '全天候(组合)', class_name: 'AllWeatherStrategy' },
],
}
export const strategyParamsMock: Record<string, { parameters: string[]; defaults: Record<string, number> }> = {
AShareDoubleMaStrategy: { parameters: ['fast_window', 'slow_window', 'size'], defaults: { fast_window: 10, slow_window: 30, size: 100 } },
AShareBollStrategy: { parameters: ['boll_window', 'boll_dev', 'size'], defaults: { boll_window: 20, boll_dev: 2.0, size: 100 } },
AShareAtrStrategy: { parameters: ['atr_window', 'atr_multiplier', 'size'], defaults: { atr_window: 14, atr_multiplier: 3.0, size: 100 } },
AllWeatherStrategy: { parameters: ['rebalance_day', 'top_n'], defaults: { rebalance_day: 1, top_n: 5 } },
}
/* ========== 回测历史任务列表 ========== */
export const taskListMock = {
tasks: [
{ id: 101, task_id: 'cta_7a92d1f6', type: 'cta', status: 'done', strategy: 'AShareDoubleMaStrategy', symbol: '600519.SH', start: '2024-01-02', end: '2024-06-28', instance: '茅台双均线(10,30)·15m' },
{ id: 100, task_id: 'cta_c287f4b5', type: 'cta', status: 'done', strategy: 'AShareBollStrategy', symbol: '000858.SZ', start: '2023-06-01', end: '2024-06-01', instance: '五粮液双均线(5,20)·日线' },
{ id: 99, task_id: 'opt_3f8a2c', type: 'optimize', status: 'done', strategy: 'AShareDoubleMaStrategy', symbol: '600519.SH', start: '2023-01-03', end: '2024-06-28', instance: '茅台双均线(10,30)·15m' },
{ id: 98, task_id: 'pf_a1b2c3', type: 'portfolio', status: 'done', strategy: 'AllWeatherStrategy', symbol: '中证1000成份', start: '2022-01-04', end: '2024-06-28', instance: '全天候·中证1000·周调仓' },
{ id: 97, task_id: 'cta_e5d4c3', type: 'cta', status: 'failed', strategy: 'AShareAtrStrategy', symbol: '300750.SZ', start: '2024-03-01', end: '2024-06-28', instance: '宁德布林(20,2)·15m' },
{ id: 96, task_id: 'fc_88aabb', type: 'factor', status: 'done', strategy: '动量因子MOM20', symbol: '全市场', start: '2020-01-02', end: '2024-06-28', instance: '' },
{ id: 95, task_id: 'cta_112233', type: 'cta', status: 'running', strategy: 'AShareDoubleMaStrategy', symbol: '510300.SH', start: '2024-01-02', end: '2024-08-12', instance: '茅台双均线(10,30)·15m' },
],
}
/* ========== CTA 回测结果 ========== */
export const ctaMetricsMock = {
relative_metrics: {
total_return: 0.3842,
annual_return: 0.3125,
alpha: 0.0821,
beta: 0.7128,
sharpe_ratio: 1.4203,
sortino_ratio: 1.8841,
information_ratio: 0.9635,
annual_volatility: 0.1822,
max_drawdown: -0.1278,
benchmark_return: 0.0721,
benchmark_volatility: 0.1513,
},
statistics: {
total_return: '0.3842',
annual_return: '0.3125',
sharpe_ratio: '1.4203',
sortino_ratio: '1.8841',
max_drawdown: '-0.1278',
total_trade_count: '48',
win_rate: '0.5417',
avg_return: '0.0080',
max_dd_duration: '42',
daily_net_pnl: '0',
total_net_pnl: '384200',
total_commission: '4128',
total_slippage: '1820',
total_turnover: '0.84',
},
task_id: 'cta_7a92d1f6',
symbol: '600519.SH',
start: '2024-01-02',
end: '2024-06-28',
strategy: 'AShareDoubleMaStrategy',
params: { fast_window: 10, slow_window: 30, size: 100 },
status: 'done',
}
export const equityCurveMock = { equity_curve: genEquity(N) }
export const benchmarkCurveMock = (() => {
const e = genEquity(N)
const b = genBenchmark(N)
return { dates: e.map((p) => p.date), strategy: e.map((p) => p.balance), benchmark: b.map((p) => p.balance) }
})()
export const riskSeriesMock = genRisk(N)
export const dailyPnlMock = { daily_pnl: genDailyPnl(N) }
export const tradesMock = {
trades: [
{ datetime: '2024-01-05 09:30:00', direction: 'LONG', offset: 'OPEN', price: 1685.2, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-01-18 14:50:00', direction: 'LONG', offset: 'OPEN', price: 1702.5, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-02-08 09:35:00', direction: 'SHORT', offset: 'CLOSE', price: 1748.0, volume: 200, vt_symbol: '600519.SH' },
{ datetime: '2024-03-04 09:30:00', direction: 'LONG', offset: 'OPEN', price: 1710.6, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-04-01 10:15:00', direction: 'SHORT', offset: 'CLOSE', price: 1756.3, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-04-22 09:30:00', direction: 'LONG', offset: 'OPEN', price: 1632.8, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-05-20 13:40:00', direction: 'SHORT', offset: 'CLOSE', price: 1718.9, volume: 100, vt_symbol: '600519.SH' },
{ datetime: '2024-06-12 09:30:00', direction: 'LONG', offset: 'OPEN', price: 1508.2, volume: 100, vt_symbol: '600519.SH' },
],
}
export const klineMock = {
kline: genDates('2024-01-02', 60).map((date, i) => ({
datetime: date,
open: 1680 + Math.sin(i / 5) * 30 + i * 0.4,
high: 1690 + Math.sin(i / 5) * 30 + i * 0.4,
low: 1670 + Math.sin(i / 5) * 30 + i * 0.4,
close: 1685 + Math.sin(i / 5) * 30 + i * 0.4,
volume: 1200000 + Math.sin(i / 3) * 400000,
})),
}
export const optResultsMock = {
results: [
{ params: { fast_window: 5, slow_window: 20 }, statistics: { sharpe_ratio: 1.12, total_return: 0.28, max_drawdown: -0.15 } },
{ params: { fast_window: 5, slow_window: 30 }, statistics: { sharpe_ratio: 1.35, total_return: 0.33, max_drawdown: -0.14 } },
{ params: { fast_window: 10, slow_window: 20 }, statistics: { sharpe_ratio: 1.48, total_return: 0.37, max_drawdown: -0.12 } },
{ params: { fast_window: 10, slow_window: 30 }, statistics: { sharpe_ratio: 1.42, total_return: 0.38, max_drawdown: -0.13 } },
{ params: { fast_window: 10, slow_window: 40 }, statistics: { sharpe_ratio: 1.51, total_return: 0.41, max_drawdown: -0.11 } },
{ params: { fast_window: 15, slow_window: 30 }, statistics: { sharpe_ratio: 1.36, total_return: 0.35, max_drawdown: -0.14 } },
{ params: { fast_window: 20, slow_window: 60 }, statistics: { sharpe_ratio: 1.23, total_return: 0.30, max_drawdown: -0.16 } },
],
}
export const logMock = { log: '[2024-06-28 09:30:00] load_data bars=117\n[09:30:01] run_backtesting start\n[09:30:04] run_backtesting done\n[09:30:04] calculate_result shape=(117,25)\n[09:30:04] compute_metrics 27ms\n[09:30:04] write metrics.json done\n' }
/* ========== 模拟盘 paper ========== */
export const paperListMock = {
accounts: [
{ id: 1, name: '茅台双均线·回放', mode: 'replay', interval: 'd', status: 'running', symbols: '600519.SH', initial_capital: 1000000, start_date: '2024-01-02', end_date: '2024-06-28', last_run_date: '2024-06-28', next_run_at: null, checkpoint_date: '2024-06-27', error_msg: null, latest_equity: 1384200, latest_date: '2024-06-28', total_return: 0.3842 },
{ id: 2, name: '15分钟动量·实走', mode: 'live', interval: '15m', status: 'running', symbols: '000858.SZ,002594.SZ', initial_capital: 500000, start_date: '2024-05-01', end_date: null, last_run_date: '2024-08-12', next_run_at: '2024-08-13 15:05', checkpoint_date: '2024-08-12', error_msg: null, latest_equity: 521800, latest_date: '2024-08-12', total_return: 0.0436 },
{ id: 3, name: '全天候组合·实走', mode: 'live', interval: 'd', status: 'pending', symbols: '中证1000池', initial_capital: 1000000, start_date: '2024-03-01', end_date: null, last_run_date: '2024-08-11', next_run_at: '2024-08-13 15:05', checkpoint_date: '2024-08-11', error_msg: null, latest_equity: 1046200, latest_date: '2024-08-11', total_return: 0.0462 },
{ id: 4, name: '布林通道测试', mode: 'replay', interval: 'd', status: 'failed', symbols: '300750.SZ', initial_capital: 300000, start_date: '2024-06-01', end_date: '2024-08-01', last_run_date: '2024-08-01', next_run_at: null, checkpoint_date: null, error_msg: 'load_data bars=0:区间无日线', latest_equity: null, latest_date: null, total_return: null },
],
}
export const paperDetailMock = paperListMock.accounts[0]
export const paperEquityMock = genPaperEquity(120)
export const paperTradesMock = tradesMock.trades.map((t, i) => ({
strategy_id: 'doublema_' + (i % 2),
symbol: '600519.SH',
direction: t.direction,
price: t.price,
volume: t.volume,
commission: Math.round(t.price * t.volume * 0.0003),
rejected: 0,
reject_reason: '',
bar_date: t.datetime.slice(0, 10),
}))
export const paperStrategiesMock = [
{ strategy_id: 'doublema_0', total_orders: 32, filled: 28, rejected: 4, commission: 1280 },
{ strategy_id: 'boll_1', total_orders: 16, filled: 16, rejected: 0, commission: 720 },
]
export const paperPositionsMock = [
{ symbol: '600519.SH', volume: 200, frozen: 0, avg_price: 1689.3 },
{ symbol: '000858.SZ', volume: 500, frozen: 0, avg_price: 168.2 },
]
export const paperPendingMock = [
{ strategy_id: 'doublema_0', symbol: '002594.SZ', side: 'BUY', price: 0, volume: 300, is_market: true, match_session: 'next_open', listing_days: 1200 },
]
/* ========== 实盘 live ========== */
export const liveListMock = {
accounts: [
{ id: 11, name: '茅台 miniQMT', account: '8888001100', vt_symbol: '600519.SH', strategy_class: 'AShareDoubleMaStrategy', strategy_name: '双均线', setting: '{"fast_window":10,"slow_window":30,"size":100}', status: 'running', interval: 'd', initial_capital: 1000000, connect_wait_sec: 15, init_wait_sec: 8, mini_path: 'D:\\miniQMTuserdata', error_msg: null, created_at: '2024-07-01 10:00:00', updated_at: '2024-08-12 15:05:00', latest_equity: 1384200, latest_date: '2024-08-12', total_return: 0.3842, position_count: 2 },
{ id: 12, name: '宁德 miniQMT', account: '8888001101', vt_symbol: '300750.SZ', strategy_class: 'AShareBollStrategy', strategy_name: '布林通道', setting: '{"boll_window":20,"boll_dev":2.0,"size":100}', status: 'stopped', interval: 'd', initial_capital: 500000, connect_wait_sec: 15, init_wait_sec: 8, mini_path: 'D:\\miniQMTuserdata', error_msg: null, created_at: '2024-07-10 14:00:00', updated_at: '2024-08-10 15:05:00', latest_equity: 485200, latest_date: '2024-08-10', total_return: -0.0296, position_count: 1 },
{ id: 13, name: '五粮液 miniQMT', account: '8888001102', vt_symbol: '000858.SZ', strategy_class: 'AShareAtrStrategy', strategy_name: 'ATR突破', setting: '{"atr_window":14,"atr_multiplier":3.0,"size":100}', status: 'error', interval: 'd', initial_capital: 300000, connect_wait_sec: 15, init_wait_sec: 8, mini_path: 'D:\\miniQMTuserdata', error_msg: 'xtquant connect timeout (mini_path 未登录)', created_at: '2024-08-01 09:00:00', updated_at: '2024-08-12 09:32:00', latest_equity: null, latest_date: null, total_return: null, position_count: 0 },
],
}
export const liveDetailMock = liveListMock.accounts[0]
export const livePositionsMock = [
{ symbol: '600519.SH', volume: 200, frozen: 0, avg_price: 1689.3, updated_at: '2024-08-12 15:00:00' },
{ symbol: '000858.SZ', volume: 500, frozen: 0, avg_price: 168.2, updated_at: '2024-08-12 15:00:00' },
]
export const liveTradesMock = tradesMock.trades.slice(0, 5).map((t, i) => ({
account_id: 11, strategy_name: '双均线', symbol: '600519.SH', direction: t.direction, offset: t.offset, price: t.price, volume: t.volume, traded_at: t.datetime, vt_tradeid: 'xt_' + i,
}))
export const liveAccountMock = { account_id: 11, date: '2024-08-12', cash: 612400, market_value: 771800, total: 1384200 }
export const liveStatusMock = { account_id: 11, status: 'running', name: '茅台 miniQMT', account: '8888001100', vt_symbol: '600519.SH', strategy_name: '双均线', updated_at: '2024-08-12 15:05:00', error_msg: null }
/* ========== 组合回测 portfolio ========== */
export const portfolioResultMock = {
strategy: 'AllWeatherStrategy',
period: { start: '2022-01-04', end: '2024-06-28', trading_days: 596 },
stocks_selected: [
{ code: '002594.SZ', name: '比亚迪', amount: 200, avg_cost: 245.3, price: 268.5, value: 53700 },
{ code: '300750.SZ', name: '宁德时代', amount: 50, avg_cost: 198.4, price: 212.6, value: 10630 },
{ code: '600519.SH', name: '贵州茅台', amount: 10, avg_cost: 1689.3, price: 1718.9, value: 17189 },
{ code: '000858.SZ', name: '五粮液', amount: 300, avg_cost: 168.2, price: 158.4, value: 47520 },
{ code: '601012.SH', name: '隆基绿能', amount: 800, avg_cost: 32.1, price: 28.6, value: 22880 },
],
trades: tradesMock.trades.slice(0, 6).map((t, i) => ({ datetime: t.datetime, code: '600519.SH', side: i % 2 === 0 ? 'BUY' : 'SELL', amount: 100, filled_amount: 100, price: t.price, filled_price: t.price, commission: 18.5, status: 'FILLED' })),
equity_curve: genEquity(180, { from: '2022-01-04' }).map((p) => ({ date: p.date, equity: p.balance })),
metrics: {
total_return: 0.1523, annual_return: 0.0582, max_drawdown: -0.168, sharpe: 0.78,
win_rate_daily: 0.521, win_rate_trade: 0.483, trading_days: 596,
},
}
/* ========== 策略配置(CRUD ========== */
export const strategyConfigsMock = {
strategies: [
{ id: 1, name: '茅台双均线(10,30)', strategy_class: 'AShareDoubleMaStrategy', params: { fast_window: 10, slow_window: 30, size: 100 }, symbol_or_pool: '600519.SH', benchmark: 'hs300', interval: 'd', remark: '茅台日线双均线', created_at: '2024-07-01', updated_at: '2024-08-10' },
{ id: 2, name: '宁德布林(20,2)', strategy_class: 'AShareBollStrategy', params: { boll_window: 20, boll_dev: 2, size: 100 }, symbol_or_pool: '300750.SZ', benchmark: 'zz500', interval: 'd', remark: '宁德时代布林通道', created_at: '2024-07-15', updated_at: '2024-08-09' },
{ id: 3, name: '全天候组合', strategy_class: 'AllWeatherStrategy', params: { rebalance_day: 1, top_n: 5 }, symbol_or_pool: '中证1000池', benchmark: 'zz500', interval: 'd', remark: '组合主线·月度调仓', created_at: '2024-06-01', updated_at: '2024-08-12' },
{ id: 4, name: '五粮液ATR突破', strategy_class: 'AShareAtrStrategy', params: { atr_window: 14, atr_multiplier: 3, size: 100 }, symbol_or_pool: '000858.SZ', benchmark: 'hs300', interval: 'd', remark: '', created_at: '2024-08-01', updated_at: '2024-08-11' },
],
}
/* ========== 策略代码文件(在线编辑 mock ========== */
const DOUBLE_MA_CODE = `"""双均线策略(A股适配 · 基于 vnpy StrategyTemplate)。
fast_window 周期均线上穿 slow_window 周期均线 → 买入;
下穿 → 卖出。定寸 size 股下单(A股 100 股一手)。
"""
from typing import List
from vnpy_ctastrategy import StrategyTemplate
from vnpy.trader.object import BarData
class DoubleMaStrategy(StrategyTemplate):
"""双均线 CTA 策略。"""
author = "sanguo"
fast_window = 10
slow_window = 30
size = 100
parameters = ["fast_window", "slow_window", "size"]
variables = ["fast_ma", "slow_ma"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.fast_ma = 0.0
self.slow_ma = 0.0
def on_init(self) -> None:
self.write_log("策略初始化")
self.load_bar(self.slow_window)
def on_bar(self, bar: BarData) -> None:
am = self.am
am.update_bar(bar)
if not am.inited:
return
self.fast_ma = am.sma(self.fast_window, array=True)[-1]
self.slow_ma = am.sma(self.slow_window, array=True)[-1]
cross_over = self.fast_ma > self.slow_ma
cross_below = self.fast_ma < self.slow_ma
if cross_over and self.pos == 0:
self.buy(bar.close_price + 0.01, self.size)
elif cross_below and self.pos > 0:
self.sell(bar.close_price - 0.01, self.size)
self.put_event()
`
const ALL_WEATHER_CODE = `"""聚宽"全天候轮动"策略 · BulletTrade 框架移植。
选股 + 大小盘轮动 + 海外 ETF 兜底 + 涨停盯盘,月度调仓。
数据/下单全部走注入的 provider 和 broker_facade(聚宽风格 API)。
"""
import logging
import numpy as np
from dataclasses import dataclass, field
from typing import Any, Callable, List, Optional
logger = logging.getLogger(__name__)
@dataclass
class AllWeatherConfig:
stock_num: int = 3 # 持仓数
trend_window: int = 10 # 趋势涨幅窗口
trend_threshold: float = 10.0 # "无敌好行情"阈值
stop_loss_pct: float = 0.92 # 止损线
benchmark: str = "000300.XSHG"
class AllWeatherStrategy:
"""全天候轮动(组合主线策略)。"""
def __init__(self, provider: Any, broker: Any = None, config: Optional[AllWeatherConfig] = None):
self.provider = provider
self.broker = broker
self.config = config or AllWeatherConfig()
self.hold_list: List[str] = []
def initialize(self, context: Any) -> None:
b = self.broker
b.set_benchmark(self.config.benchmark)
b.run_daily(self.prepare_stock_list, "9:05")
b.run_monthly(self.monthly_adjustment, 1, "9:30")
b.run_daily(self.stop_loss, "14:00")
def monthly_adjustment(self, context: Any) -> None:
"""月度调仓:大小盘轮动 + 选股 + 调仓下单(详见完整源码)。"""
cfg = self.config
b_stocks = self._stock_pool("000300.XSHG", context.previous_date)
target = self._pick_big_universe(b_stocks, context)
for code in target:
self.broker.order_target_value(code, 0) # 占位:实际按 target 等权
logger.info("monthly_adjustment target=%s", target)
def stop_loss(self, context: Any) -> None:
"""持仓跌幅超 stop_loss_pct 止损。"""
pass # 详见完整实现
`
const BOLL_CODE = `"""布林带通道策略(A股)。价格突破上轨买入,跌破下轨卖出。"""
from vnpy_ctastrategy import StrategyTemplate
class BollChannelStrategy(StrategyTemplate):
author = "sanguo"
boll_window = 20
boll_dev = 2.0
size = 100
parameters = ["boll_window", "boll_dev", "size"]
variables = ["boll_up", "boll_down"]
def on_init(self):
self.load_bar(self.boll_window)
def on_bar(self, bar):
am = self.am
am.update_bar(bar)
if not am.inited:
return
self.boll_up, self.boll_down = am.boll(self.boll_window, self.boll_dev)
if am.close > self.boll_up and self.pos == 0:
self.buy(bar.close_price, self.size)
elif am.close < self.boll_down and self.pos > 0:
self.sell(bar.close_price, self.size)
`
const ATR_CODE = `"""ATR 通道突破策略(A股)。基于 ATR 的动态通道突破。"""
from vnpy_ctastrategy import StrategyTemplate
class AtrRsiStrategy(StrategyTemplate):
author = "sanguo"
atr_window = 14
atr_multiplier = 3.0
size = 100
parameters = ["atr_window", "atr_multiplier", "size"]
variables = ["atr_value", "entry_price"]
def on_init(self):
self.load_bar(self.atr_window + 5)
def on_bar(self, bar):
am = self.am
am.update_bar(bar)
if not am.inited:
return
self.atr_value = am.atr(self.atr_window)
entry = am.close[-2] + self.atr_value * self.atr_multiplier
if am.close > entry and self.pos == 0:
self.buy(entry, self.size)
self.entry_price = entry
`
export interface StrategyFile {
name: string
dir: string
class_name: string
type: 'portfolio' | 'cta'
lines: number
modified: string
code: string
}
export const strategyFilesMock: { files: StrategyFile[] } = {
files: [
{ name: 'all_weather.py', dir: 'sanguo_portfolio/strategies/', class_name: 'AllWeatherStrategy', type: 'portfolio', lines: 575, modified: '2024-08-12 14:30', code: ALL_WEATHER_CODE },
{ name: 'momentum_timing.py', dir: 'sanguo_portfolio/strategies/', class_name: 'MomentumTimingStrategy', type: 'portfolio', lines: 412, modified: '2024-08-10 10:20', code: ALL_WEATHER_CODE },
{ name: 'double_ma.py', dir: 'sanguo_live/strategies/', class_name: 'AShareDoubleMaStrategy', type: 'cta', lines: 68, modified: '2024-07-20 09:15', code: DOUBLE_MA_CODE },
{ name: 'boll_channel.py', dir: 'sanguo_live/strategies/', class_name: 'AShareBollStrategy', type: 'cta', lines: 32, modified: '2024-07-22 10:00', code: BOLL_CODE },
{ name: 'atr_breakout.py', dir: 'sanguo_live/strategies/', class_name: 'AShareAtrStrategy', type: 'cta', lines: 30, modified: '2024-08-01 11:20', code: ATR_CODE },
],
}
/* ========== 策略实例(实例层:代码下的参数变体,每实例一键 4 运行) ========== */
export interface StrategyInstance {
id: number
code_file: string
name: string
type: 'portfolio' | 'cta'
params: Record<string, unknown>
symbol_or_pool: string
interval: string
match_session: string
status: { backtest: string; replay: string; paper_live: string; live: string }
last_return: number | null
updated_at: string
}
export const strategyInstancesMock: { instances: StrategyInstance[] } = {
instances: [
{ id: 1, code_file: 'all_weather.py', name: '全天候·HS300·月调仓', type: 'portfolio', params: { stock_num: 3, trend_window: 10 }, symbol_or_pool: 'HS300', interval: 'd', match_session: 'next_open', status: { backtest: 'done', replay: 'done', paper_live: 'running', live: '-' }, last_return: 0.1523, updated_at: '2024-08-12' },
{ id: 2, code_file: 'all_weather.py', name: '全天候·中证1000·周调仓', type: 'portfolio', params: { stock_num: 5, trend_window: 5 }, symbol_or_pool: '中证1000', interval: 'd', match_session: 'next_open', status: { backtest: 'done', replay: '-', paper_live: '-', live: '-' }, last_return: 0.082, updated_at: '2024-08-10' },
{ id: 3, code_file: 'double_ma.py', name: '茅台双均线(10,30)·15m', type: 'cta', params: { fast_window: 10, slow_window: 30, size: 100 }, symbol_or_pool: '600519.SH', interval: '15m', match_session: 'next_open', status: { backtest: 'done', replay: 'done', paper_live: 'running', live: 'running' }, last_return: 0.3842, updated_at: '2024-08-12' },
{ id: 4, code_file: 'double_ma.py', name: '五粮液双均线(5,20)·日线', type: 'cta', params: { fast_window: 5, slow_window: 20, size: 100 }, symbol_or_pool: '000858.SZ', interval: 'd', match_session: 'current_close', status: { backtest: 'done', replay: '-', paper_live: '-', live: '-' }, last_return: -0.052, updated_at: '2024-08-08' },
{ id: 5, code_file: 'boll_channel.py', name: '宁德布林(20,2)·15m', type: 'cta', params: { boll_window: 20, boll_dev: 2, size: 100 }, symbol_or_pool: '300750.SZ', interval: '15m', match_session: 'next_open', status: { backtest: 'done', replay: '-', paper_live: '-', live: 'stopped' }, last_return: -0.0296, updated_at: '2024-08-10' },
],
}
/* ========== 因子 factor ========== */
export const factorListMock = {
factors: [
{ name: '动量因子 MOM20', category: '动量' },
{ name: '换手率 TURN5', category: '量价' },
{ name: '波动率 VOL20', category: '波动' },
{ name: '市值 SIZE', category: '风格' },
{ name: 'ROE_TTM', category: '质量' },
{ name: '毛利率 GROSS', category: '质量' },
],
}
export const icSummaryMock = {
ic_summary: {
MOM20: { IC_mean: 0.042, ICIR: 0.51, t_stat: 3.21, positive_ratio: 0.58 },
TURN5: { IC_mean: -0.028, ICIR: -0.33, t_stat: -2.05, positive_ratio: 0.41 },
VOL20: { IC_mean: -0.035, ICIR: -0.42, t_stat: -2.6, positive_ratio: 0.38 },
},
}
/* ========== 任务状态(progress 轮询) ========== */
export const taskStatusDoneMock = { task_id: 'cta_7a92d1f6', status: 'done', stage: 'compute_metrics' }
export const taskStatusRunningMock = { task_id: 'cta_running01', status: 'running', stage: 'run_backtesting' }
// 双轨对账(影子 vs 实盘,§8.2 四指标样例)
export const reconcileMock = {
pairs: [{
live_account_id: 5,
shadow_account_id: 39,
strategy: 'channel_test',
report: {
date: '2026-08-17',
live_account_id: 5,
shadow_account_id: 39,
trades: {
live_count: 6,
shadow_count: 6,
count_match: true,
rows: [
{ symbol: '510300', side: 'B', live_volume: 20000, shadow_volume: 20000, live_vwap: 3.986, shadow_vwap: 3.982, price_diff_bps: 1.0 },
{ symbol: '159915', side: 'B', live_volume: 15000, shadow_volume: 15000, live_vwap: 2.114, shadow_vwap: 2.108, price_diff_bps: 2.8 },
{ symbol: '600000', side: 'S', live_volume: 8000, shadow_volume: 8000, live_vwap: 8.912, shadow_vwap: 8.903, price_diff_bps: 1.0 },
],
avg_price_diff_bps: 1.6,
pass_price: true,
},
positions: {
rows: [
{ symbol: '510300', live_volume: 20000, shadow_volume: 20000, volume_diff: 0 },
{ symbol: '159915', live_volume: 15000, shadow_volume: 15000, volume_diff: 0 },
],
match: true,
},
nav: {
live_total: 1004218.5,
shadow_total: 1003886.2,
mtd_deviation_pct: 0.03,
pass_nav: true,
live_mtd_return_pct: 0.42,
shadow_mtd_return_pct: 0.39,
},
passed: true,
},
}],
}