接入 SimNow 模拟盘与期货下单、策略及品种推荐功能。

新增 vnpy CTP 桥接、以损定仓/固定张数、趋势回调与滚仓策略、按资金推荐品种及交易风控;模拟盘走 SimNow,实盘预留期货公司配置。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 10:04:37 +08:00
parent 9c0e5d9c57
commit 6e423eebfb
30 changed files with 2789 additions and 60 deletions
+101 -37
View File
@@ -242,41 +242,59 @@
function getDataZoom(c, preserve) {
var defStart = getDefaultZoomStart();
var zoom = [
{
type: 'inside',
xAxisIndex: [0, 1],
start: defStart,
end: 100,
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: false,
var xZoom = {
type: 'inside',
id: 'dzInsideX',
xAxisIndex: [0, 1],
start: defStart,
end: 100,
filterMode: 'none',
zoomOnMouseWheel: true,
moveOnMouseMove: true,
moveOnMouseWheel: false,
preventDefaultMouseMove: true,
minSpan: 2,
};
var yZoom = {
type: 'inside',
id: 'dzInsideY',
yAxisIndex: [0],
orient: 'vertical',
filterMode: 'none',
zoomOnMouseWheel: true,
moveOnMouseMove: true,
preventDefaultMouseMove: true,
};
var slider = {
type: 'slider',
id: 'dzSlider',
xAxisIndex: [0, 1],
start: defStart,
end: 100,
height: 22,
bottom: 4,
borderColor: c.grid,
backgroundColor: c.bg,
fillerColor: c.area,
handleStyle: { color: c.sliderFill },
dataBackground: {
lineStyle: { color: c.grid, opacity: 0.35 },
areaStyle: { color: c.area },
},
{
type: 'slider',
xAxisIndex: [0, 1],
start: defStart,
end: 100,
height: 22,
bottom: 4,
borderColor: c.grid,
backgroundColor: c.bg,
fillerColor: c.area,
handleStyle: { color: c.sliderFill },
dataBackground: {
lineStyle: { color: c.grid },
areaStyle: { color: c.area },
},
textStyle: { color: c.text, fontSize: 10 },
},
];
textStyle: { color: c.text, fontSize: 10 },
filterMode: 'none',
brushSelect: false,
};
var zoom = [xZoom, yZoom, slider];
if (preserve && chart) {
var opt = chart.getOption();
if (opt && opt.dataZoom) {
opt.dataZoom.forEach(function (z, i) {
if (zoom[i] && z.start != null && z.end != null) {
zoom[i].start = z.start;
zoom[i].end = z.end;
opt.dataZoom.forEach(function (z) {
if (!z.id) return;
var target = zoom.find(function (t) { return t.id === z.id; });
if (target && z.start != null && z.end != null) {
target.start = z.start;
target.end = z.end;
}
});
}
@@ -284,6 +302,11 @@
return zoom;
}
function isFollowingLatest() {
var z = getZoomRange();
return z.end >= 98;
}
function mapSeriesData(bars, values, gapDay) {
if (!gapDay) return values;
return bars.map(function (b, i) {
@@ -303,6 +326,7 @@
var times = bars.map(function (b) { return b.time; });
var isLine = data.chart_type === 'line' || data.period === 'timeshare';
var gapDay = chartOpts.gapDay;
var followLatest = preserveZoom && isFollowingLatest();
var dataZoom = getDataZoom(c, preserveZoom);
var zoom = preserveZoom ? getZoomRange() : { start: dataZoom[0].start, end: dataZoom[0].end };
var vIdx = visibleIndices(bars, zoom);
@@ -326,6 +350,7 @@
boundaryGap: gapDay ? false : true,
axisLabel: { color: c.text, fontSize: 10 },
axisLine: { lineStyle: { color: c.grid } },
splitLine: { show: false },
};
var xAxis1 = {
type: xAxisType,
@@ -333,6 +358,7 @@
boundaryGap: gapDay ? false : true,
axisLabel: { show: false },
axisLine: { lineStyle: { color: c.grid } },
splitLine: { show: false },
};
if (!gapDay) {
xAxis0.data = times;
@@ -344,14 +370,27 @@
animation: false,
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
axisPointer: { link: [{ xAxisIndex: 'all' }] },
dataZoom: dataZoom,
grid: grids,
xAxis: [xAxis0, xAxis1],
yAxis: [
{ scale: true, gridIndex: 0, splitLine: { lineStyle: { color: c.grid } }, axisLabel: { color: c.text } },
{ scale: true, gridIndex: 1, splitLine: { show: false }, axisLabel: { color: c.text, fontSize: 10 }, splitNumber: 2 },
{
scale: true,
gridIndex: 0,
splitLine: { show: false },
axisLabel: { color: c.text },
},
{
scale: true,
gridIndex: 1,
splitLine: { show: false },
axisLabel: { color: c.text, fontSize: 10 },
splitNumber: 2,
},
],
};
if (!preserveZoom) {
base.dataZoom = dataZoom;
}
var series = [];
var mainMark = {
@@ -465,7 +504,12 @@
};
}
chart.setOption(Object.assign(base, { series: series }), true);
if (preserveZoom) {
chart.setOption(Object.assign(base, { series: series }), false);
} else {
chart.setOption(Object.assign(base, { series: series }), true);
dataZoomBound = false;
}
var title = (data.chart_symbol || data.symbol || '') + ' · ' + periodLabel(data.period);
chart.setOption({
@@ -478,6 +522,22 @@
} : { show: false },
});
if (followLatest) {
var span = zoom.end - zoom.start;
chart.dispatchAction({
type: 'dataZoom',
dataZoomIndex: 0,
start: Math.max(0, 100 - span),
end: 100,
});
chart.dispatchAction({
type: 'dataZoom',
dataZoomIndex: 2,
start: Math.max(0, 100 - span),
end: 100,
});
}
bindDataZoomHL();
}
@@ -656,12 +716,16 @@
if (start === 0) end = newSpan;
else start = end - newSpan;
}
chart.dispatchAction({ type: 'dataZoom', start: start, end: end });
chart.dispatchAction({ type: 'dataZoom', dataZoomIndex: 0, start: start, end: end });
chart.dispatchAction({ type: 'dataZoom', dataZoomIndex: 2, start: start, end: end });
}
function resetDataZoom() {
if (!chart) return;
chart.dispatchAction({ type: 'dataZoom', start: getDefaultZoomStart(), end: 100 });
var start = getDefaultZoomStart();
chart.dispatchAction({ type: 'dataZoom', dataZoomIndex: 0, start: start, end: 100 });
chart.dispatchAction({ type: 'dataZoom', dataZoomIndex: 2, start: start, end: 100 });
chart.dispatchAction({ type: 'dataZoom', dataZoomIndex: 1, start: 0, end: 100 });
}
function bindPeriodTabs() {