311 lines
188 KiB
HTML
311 lines
188 KiB
HTML
|
|
<!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>
|