feat: 迭代03-05 交易记录接入、行情实时、QMT连接配置、data store 标准化

- R-004 QMT连接配置:多配置管理 + 会话头部快捷切换(03-QMT连接配置)
- R-005 WS盘中价格实时更新:服务端中转+缓存+前端轮询(04-WS盘中价格实时更新)
- R-006 策略数据 JSON data store 标准化(store.json/market.json/schema.json)
- R-007 交易记录接入:当日订单+成交单表合并(委托主行+展开成交明细)、时间段查询(今日/本周/本月+手动起止)、费用计算(佣金费率万m_dCommission+最低5元、印花税卖出万5、过户费双向万0.1)
- 架构治理:api 按领域拆分、component 目录、QmtHealthMonitor
This commit is contained in:
2026-09-01 13:21:49 +08:00
parent 5a723d514e
commit bb3f8bd28d
57 changed files with 5244 additions and 397 deletions
+45 -16
View File
@@ -13,11 +13,14 @@
* 设计约束:技术约束-001/003、产品约束-001/002/003
*/
import { QmtBridgeRestDataSource } from './data-source/qmt-bridge-rest.js';
import { registerSettings } from './settings.js';
import { AllocationStorage } from './storage.js';
import { PositionManager } from './position/manager.js';
import { registerApi } from './api.js';
import { QmtBridgeRestDataSource } from './component/QmtBridgeRestDataSource.js';
import { registerSettings, resolveStartupConnection } from './settings.js';
import { DataStore } from './component/DataStore.js';
import { PositionManager } from './component/PositionManager.js';
import { registerApi } from './api/index.js';
import { MarketDataHub } from './component/MarketDataHub.js';
import { MarketFeed } from './component/MarketFeed.js';
import { QmtHealthMonitor } from './component/QmtHealthMonitor.js';
const name = 'one-divine-lot';
@@ -27,25 +30,49 @@ const inject = ['settings', 'webServer'];
async function apply(ctx, config) {
const logger = ctx.logger;
// S2: 数据源(REST 直连 QMT Bridge
const dataSource = new QmtBridgeRestDataSource({
baseUrl: config?.qmtBaseUrl,
timeoutMs: config?.qmtTimeoutMs,
});
// S3: 设置管理(策略=标签 + 显示开关)
// S3: 设置管理(策略=标签 + 显示开关 + QMT 连接配置
const settings = registerSettings(ctx, config);
// S4: 分仓数据本地存储
const storage = new AllocationStorage({
// S2: 数据源(REST 直连 QMT Bridge
// R-004(技术约束-008):启动激活地址 = 默认配置 → 上次激活 → 第一条;列表为空回退 cordis qmtBaseUrlQ4 不迁移)
const settingsScope = settings;
let startup;
try {
startup = await resolveStartupConnection(settingsScope, config);
} catch (e) {
logger.warn('[one-divine-lot] 启动连接解析失败,回退 cordis qmtBaseUrl: ' + e.message);
startup = { connection: null, baseUrl: config?.qmtBaseUrl ?? null, source: 'cordis-fallback' };
}
const dataSource = new QmtBridgeRestDataSource({
baseUrl: startup.baseUrl ?? config?.qmtBaseUrl,
timeoutMs: config?.qmtTimeoutMs,
});
logger.info(
'[one-divine-lot] QMT 连接激活: ' + (startup.connection ? startup.connection.name + ' (' + startup.connection.baseUrl + ')' : String(startup.baseUrl ?? '(未配置)')) +
' [来源: ' + startup.source + ']'
);
// S4: 分仓数据本地存储(R-006JSON data store,策略为中心)
const storage = new DataStore({
dataDir: config?.dataDir,
});
// R-005:行情数据(MarketDataHub 缓存 + MarketFeed 获取,页面轮询读取)
const marketHub = new MarketDataHub({ logger, dataStore: storage });
// 启动时从磁盘加载行情缓存(首屏快速展示,不依赖实盘订阅)
marketHub.warmup().catch((e) => logger.warn('[one-divine-lot] 行情预热失败: ' + e.message));
const marketFeed = new MarketFeed({ hub: marketHub, runtime: { settings, dataSource }, logger });
marketFeed.start(startup.baseUrl ?? config?.qmtBaseUrl);
// QMT 连接健康检查(服务端定时探测 + 缓存,2026-09-01
const qmtHealthMonitor = new QmtHealthMonitor({ runtime: { settings, dataSource }, logger });
qmtHealthMonitor.start();
// S5: 分仓逻辑(份额分配/查询)
const manager = new PositionManager({ dataSource, storage });
// S6: 服务端 HTTP API
registerApi(ctx, { manager, settings });
// S6: 服务端 HTTP APIR-004:注入 dataSource 以编排激活热切换)
registerApi(ctx, { manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor });
// 启动时可用性检查(日志,不阻塞)
dataSource.isAvailable().then((ok) => {
@@ -57,6 +84,8 @@ async function apply(ctx, config) {
ctx.effect(() => {
logger.info('[one-divine-lot] 插件已加载(S1-S6 就绪)');
return () => {
marketFeed.stop();
qmtHealthMonitor.stop();
logger.info('[one-divine-lot] 插件已释放');
};
}, 'one-divine-lot.dispose');