Files
codex_jxs_code/backtest_outputs/charts/bb_backtest_visualization.html

311 lines
188 KiB
HTML
Raw Normal View History

2026-03-04 16:48:24 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>布林带策略回测可视化 - 2026年3月</title>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #0a0e27;
color: #e0e6ed;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
#chart {
width: 100%;
height: 100%;
}
.legend {
position: fixed;
top: 20px;
right: 20px;
background: rgba(15, 23, 42, 0.95);
padding: 20px;
border-radius: 12px;
border: 1px solid #334155;
font-size: 13px;
line-height: 1.8;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
z-index: 1000;
}
.legend-title {
font-weight: 600;
margin-bottom: 12px;
color: #f1f5f9;
font-size: 14px;
letter-spacing: 0.5px;
}
.legend-item {
display: flex;
align-items: center;
margin: 8px 0;
}
.legend-marker {
width: 14px;
height: 14px;
margin-right: 10px;
border-radius: 2px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script>
</head>
<body>
<div id="chart"></div>
<div class="legend">
<div class="legend-title">📊 交易标记说明</div>
<div class="legend-item">
<div class="legend-marker" style="background: #00ff00;"></div>
<span>开多/加多</span>
</div>
<div class="legend-item">
<div class="legend-marker" style="background: #ff0000;"></div>
<span>开空/加空</span>
</div>
<div class="legend-item">
<div class="legend-marker" style="background: #ffff00;"></div>
<span>平仓50%</span>
</div>
<div class="legend-item">
<div class="legend-marker" style="background: #ff00ff;"></div>
<span>平仓100%</span>
</div>
</div>
<script>
const chartData = [{"timestamp": 1772323200000, "datetime": "2026-03-01 00:00", "open": 1963.49, "high": 1968.5, "low": 1963.15, "close": 1966.41, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772323500000, "datetime": "2026-03-01 00:05", "open": 1966.39, "high": 1966.4, "low": 1958.45, "close": 1959.51, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772323800000, "datetime": "2026-03-01 00:10", "open": 1959.52, "high": 1961.37, "low": 1959.16, "close": 1961.31, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772324100000, "datetime": "2026-03-01 00:15", "open": 1961.33, "high": 1962.68, "low": 1959.09, "close": 1959.81, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772324400000, "datetime": "2026-03-01 00:20", "open": 1959.8, "high": 1963.85, "low": 1959.8, "close": 1961.07, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772324700000, "datetime": "2026-03-01 00:25", "open": 1961.31, "high": 1961.56, "low": 1956.69, "close": 1957.58, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772325000000, "datetime": "2026-03-01 00:30", "open": 1957.57, "high": 1960.66, "low": 1956.19, "close": 1957.19, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772325300000, "datetime": "2026-03-01 00:35", "open": 1957.19, "high": 1960.0, "low": 1957.19, "close": 1957.69, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772325600000, "datetime": "2026-03-01 00:40", "open": 1957.68, "high": 1959.0, "low": 1953.7, "close": 1955.62, "bb_upper": null, "bb_mid": null, "bb_lower": null}, {"timestamp": 1772325900000, "datetime": "2026-03-01 00:45", "open": 1955.33, "high": 1957.8, "low": 1954.71, "close": 1955.97, "bb_upper": 1967.2347557915455, "bb_mid": 1959.216, "bb_lower": 1951.1972442084543}, {"timestamp": 1772326200000, "datetime": "2026-03-01 00:50", "open": 1955.99, "high": 1963.18, "low": 1955.9, "close": 1962.81, "bb_upper": 1964.8917637830225, "bb_mid": 1958.8560000000002, "bb_lower": 1952.820236216978}, {"timestamp": 1772326500000, "datetime": "2026-03-01 00:55", "open": 1962.8, "high": 1964.12, "low": 1961.35, "close": 1961.67, "bb_upper": 1965.4991645726905, "bb_mid": 1959.0720000000001, "bb_lower": 1952.6448354273098}, {"timestamp": 1772326800000, "datetime": "2026-03-01 01:00", "open": 1961.66, "high": 1961.93, "low": 1954.63, "close": 1954.63, "bb_upper": 1965.363438036061, "bb_mid": 1958.404, "bb_lower": 1951.444561963939}, {"timestamp": 1772327100000, "datetime": "2026-03-01 01:05", "open": 1954.64, "high": 1959.25, "low": 1952.8, "close": 1953.16, "bb_upper": 1965.6817242212953, "bb_mid": 1957.739, "bb_lower": 1949.7962757787047}, {"timestamp": 1772327400000, "datetime": "2026-03-01 01:10", "open": 1953.41, "high": 1955.89, "low": 1946.16, "close": 1948.03, "bb_upper": 1966.8769709319859, "bb_mid": 1956.435, "bb_lower": 1945.993029068014}, {"timestamp": 1772327700000, "datetime": "2026-03-01 01:15", "open": 1948.08, "high": 1948.46, "low": 1943.1, "close": 1947.34, "bb_upper": 1967.9921888349231, "bb_mid": 1955.411, "bb_lower": 1942.829811165077}, {"timestamp": 1772328000000, "datetime": "2026-03-01 01:20", "open": 1947.34, "high": 1957.09, "low": 1947.33, "close": 1956.0, "bb_upper": 1967.79124331043, "bb_mid": 1955.292, "bb_lower": 1942.7927566895698}, {"timestamp": 1772328300000, "datetime": "2026-03-01 01:25", "open": 1955.91, "high": 1956.51, "low": 1952.01, "close": 1952.89, "bb_upper": 1967.2476108280478, "bb_mid": 1954.812, "bb_lower": 1942.376389171952}, {"timestamp": 1772328600000, "datetime": "2026-03-01 01:30", "open": 1952.89, "high": 1961.15, "low": 1951.06, "close": 1959.78, "bb_upper": 1968.2713454723514, "bb_mid": 1955.2280000000003, "bb_lower": 1942.1846545276492}, {"timestamp": 1772328900000, "datetime": "2026-03-01 01:35", "open": 1959.78, "high": 1973.56, "low": 1959.78, "close": 1968.92, "bb_upper": 1973.5020641101194, "bb_mid": 1956.523, "bb_lower": 1939.5439358898805}, {"timestamp": 1772329200000, "datetime": "2026-03-01 01:40", "open":
const tradesMarkers = [{"timestamp": "2026-03-01 01:35", "price": 1970.895742, "action": "开short", "reason": "触上轨开空", "index": 19, "show_reason_label": true, "short_reason": "上轨开空", "type": "open_short", "color": "#ff0000", "symbol": "triangle"}, {"timestamp": "2026-03-01 01:50", "price": 2022.33, "action": "平仓100%", "reason": "止损", "index": 22, "show_reason_label": true, "short_reason": "止损", "type": "close_all", "color": "#ff00ff", "symbol": "circle"}, {"timestamp": "2026-03-01 03:50", "price": 2019.4038, "action": "开long", "reason": "触下轨开多", "index": 46, "show_reason_label": true, "short_reason": "下轨开多", "type": "open_long", "color": "#00ff00", "symbol": "triangle"}, {"timestamp": "2026-03-01 03:55", "price": 2018.383596, "action": "加long", "reason": "触下轨加多", "index": 47, "show_reason_label": true, "short_reason": "下轨加多", "type": "open_long", "color": "#00ff00", "symbol": "triangle"}, {"timestamp": "2026-03-01 04:00", "price": 2028.348, "action": "平仓50%", "reason": "触中轨平50%-1m(04:02)回踩中轨", "index": 48, "show_reason_label": false, "short_reason": "中轨平半", "type": "close_half", "color": "#ffff00", "symbol": "diamond"}, {"timestamp": "2026-03-01 04:05", "price": 2018.7258371425564, "action": "平仓100%", "reason": "回开仓价全平", "index": 49, "show_reason_label": false, "short_reason": "回本全平", "type": "close_all", "color": "#ff00ff", "symbol": "circle"}, {"timestamp": "2026-03-01 04:15", "price": 1999.6626196004336, "action": "开long", "reason": "触下轨开多", "index": 51, "show_reason_label": true, "short_reason": "下轨开多", "type": "open_long", "color": "#00ff00", "symbol": "triangle"}, {"timestamp": "2026-03-01 04:35", "price": 2014.63, "action": "平仓50%", "reason": "触中轨平50%-1m(04:39)回踩中轨", "index": 55, "show_reason_label": false, "short_reason": "中轨平半", "type": "close_half", "color": "#ffff00", "symbol": "diamond"}, {"timestamp": "2026-03-01 05:10", "price": 2017.8250206638015, "action": "平仓100%", "reason": "延迟反转-同K回调确认-平多", "index": 62, "show_reason_label": true, "short_reason": "延迟反转", "type": "close_all", "color": "#ff00ff", "symbol": "circle"}, {"timestamp": "2026-03-01 05:10", "price": 2017.4214556596687, "action": "开short", "reason": "延迟反转-同K回调确认-开空", "index": 62, "show_reason_label": true, "short_reason": "延迟反转", "type": "open_short", "color": "#ff0000", "symbol": "triangle"}, {"timestamp": "2026-03-01 05:15", "price": 2014.157, "action": "平仓50%", "reason": "触中轨平50%-1m(05:16)反抽中轨", "index": 63, "show_reason_label": false, "short_reason": "中轨平半", "type": "close_half", "color": "#ffff00", "symbol": "diamond"}, {"timestamp": "2026-03-01 05:20", "price": 2017.4214556596687, "action": "平仓100%", "reason": "回开仓价全平", "index": 64, "show_reason_label": false, "short_reason": "回本全平", "type": "close_all", "color": "#ff00ff", "symbol": "circle"}, {"timestamp": "2026-03-01 05:20", "price": 2020.9165042134855, "action": "开short", "reason": "触上轨开空", "index": 64, "show_reason_label": true, "short_reason": "上轨开空", "type": "open_short", "color": "#ff0000", "symbol": "triangle"}, {"timestamp": "2026-03-01 05:35", "price": 2016.474, "action": "平仓50%", "reason": "触中轨平50%-1m(05:38)反抽中轨", "index": 67, "show_reason_label": false, "short_reason": "中轨平半", "type": "close_half", "color": "#ffff00", "symbol": "diamond"}, {"timestamp": "2026-03-01 06:10", "price": 1993.0582785593572, "action": "平仓100%", "reason": "延迟反转-同K反弹确认-平空", "index": 74, "show_reason_label": true, "short_reason": "延迟反转", "type": "close_all", "color": "#ff00ff", "symbol": "circle"}, {"timestamp": "2026-03-01 06:10", "price": 1993.456890215069, "action": "开long", "reason": "延迟反转-同K反弹确认-开多", "index": 74, "show_reason_label": true, "short_reason": "延迟反转", "type": "open_long", "color": "#00
function main() {
const categoryData = [];
const klineData = [];
const upper = [];
const mid = [];
const lower = [];
for (const k of chartData) {
const d = new Date(k.timestamp);
const label = `${d.getMonth()+1}/${d.getDate()} ${d.getHours().toString().padStart(2, "0")}:${d
.getMinutes()
.toString()
.padStart(2, "0")}`;
categoryData.push(label);
klineData.push([k.open, k.close, k.low, k.high]);
upper.push(k.bb_upper);
mid.push(k.bb_mid);
lower.push(k.bb_lower);
}
// 准备交易标记数据
const openLongData = [];
const openShortData = [];
const closeHalfData = [];
const closeAllData = [];
for (const marker of tradesMarkers) {
const point = {
value: [marker.index, marker.price],
reason: marker.reason,
action: marker.action,
shortReason: marker.short_reason,
label: marker.show_reason_label
? {
show: true,
formatter: marker.short_reason,
color: "#f8fafc",
backgroundColor: "rgba(15,23,42,0.85)",
borderColor: "#475569",
borderWidth: 1,
borderRadius: 4,
padding: [2, 4],
fontSize: 10,
}
: { show: false },
itemStyle: { color: marker.color },
};
if (marker.type === 'open_long') {
openLongData.push(point);
} else if (marker.type === 'open_short') {
openShortData.push(point);
} else if (marker.type === 'close_half') {
closeHalfData.push(point);
} else if (marker.type === 'close_all') {
closeAllData.push(point);
}
}
const chartDom = document.getElementById("chart");
const chart = echarts.init(chartDom, null, { renderer: "canvas" });
const option = {
backgroundColor: "#0a0e27",
tooltip: {
trigger: "axis",
axisPointer: { type: "cross" },
backgroundColor: "rgba(15, 23, 42, 0.95)",
borderColor: "#334155",
textStyle: { color: "#e0e6ed" },
},
axisPointer: {
link: [{ xAxisIndex: "all" }],
},
grid: {
left: "3%",
right: "200px",
top: "6%",
bottom: "8%",
containLabel: true,
},
xAxis: {
type: "category",
data: categoryData,
scale: true,
boundaryGap: true,
axisLine: { lineStyle: { color: "#475569" } },
axisLabel: {
color: "#94a3b8",
rotate: 45,
fontSize: 11
},
},
yAxis: {
scale: true,
axisLine: { lineStyle: { color: "#475569" } },
splitLine: { lineStyle: { color: "#1e293b" } },
axisLabel: { color: "#94a3b8" },
},
dataZoom: [
{
type: "inside",
start: 0,
end: 100,
},
{
type: "slider",
start: 0,
end: 100,
height: 30,
backgroundColor: "#1e293b",
fillerColor: "rgba(100, 116, 139, 0.3)",
borderColor: "#334155",
handleStyle: {
color: "#64748b",
borderColor: "#94a3b8"
},
textStyle: { color: "#94a3b8" },
},
],
series: [
{
name: "K线",
type: "candlestick",
data: klineData,
itemStyle: {
color: "#10b981",
color0: "#ef4444",
borderColor: "#10b981",
borderColor0: "#ef4444",
},
},
{
name: "BB上轨",
type: "line",
data: upper,
symbol: "none",
lineStyle: { color: "#f59e0b", width: 2 },
z: 1,
},
{
name: "BB中轨",
type: "line",
data: mid,
symbol: "none",
lineStyle: { color: "#8b5cf6", width: 2, type: "dashed" },
z: 1,
},
{
name: "BB下轨",
type: "line",
data: lower,
symbol: "none",
lineStyle: { color: "#f59e0b", width: 2 },
z: 1,
},
{
name: "开多/加多",
type: "scatter",
symbol: "triangle",
symbolSize: 12,
symbolOffset: [0, 8], // 向下偏移,使三角形底部对齐价格
data: openLongData,
itemStyle: { color: "#00ff00" },
z: 10,
tooltip: {
formatter: (params) => {
const marker = params.data;
return `开多/加多<br/>价格: ${marker.value[1].toFixed(2)}<br/>原因: ${marker.reason}`;
}
}
},
{
name: "开空/加空",
type: "scatter",
symbol: "triangle",
symbolSize: 12,
symbolRotate: 180,
symbolOffset: [0, -8], // 向上偏移,使三角形底部对齐价格
data: openShortData,
itemStyle: { color: "#ff0000" },
z: 10,
tooltip: {
formatter: (params) => {
const marker = params.data;
return `开空/加空<br/>价格: ${marker.value[1].toFixed(2)}<br/>原因: ${marker.reason}`;
}
}
},
{
name: "平仓50%",
type: "scatter",
symbol: "diamond",
symbolSize: 10,
data: closeHalfData,
itemStyle: { color: "#ffff00" },
z: 10,
tooltip: {
formatter: (params) => {
const marker = params.data;
return `平仓50%<br/>价格: ${marker.value[1].toFixed(2)}<br/>原因: ${marker.reason}`;
}
}
},
{
name: "平仓100%",
type: "scatter",
symbol: "circle",
symbolSize: 10,
data: closeAllData,
itemStyle: { color: "#ff00ff" },
z: 10,
tooltip: {
formatter: (params) => {
const marker = params.data;
return `平仓100%<br/>价格: ${marker.value[1].toFixed(2)}<br/>原因: ${marker.reason}`;
}
}
},
],
};
chart.setOption(option);
window.addEventListener("resize", () => chart.resize());
}
main();
</script>
</body>
</html>