229 lines
7.0 KiB
HTML
229 lines
7.0 KiB
HTML
<html lang='zh'>
|
||
<head>
|
||
<meta charset='utf-8'/>
|
||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||
<meta content='width=device-width, initial-scale=1.0' name='viewport'/>
|
||
<title>Strategy</title>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/amis/6.13.0/antd.min.css" rel="stylesheet"/>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/amis/6.13.0/helper.min.css" rel="stylesheet"/>
|
||
<link href="https://cdnjs.cloudflare.com/ajax/libs/amis/6.13.0/iconfont.min.css" rel="stylesheet"/>
|
||
<style>
|
||
html, body, #root {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id='root'></div>
|
||
</body>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/amis/6.13.0/sdk.min.js"></script>
|
||
<script th:inline="javascript" type='text/javascript'>
|
||
// Market data as KV: { 'YYYY-MM-DD': [open, close, low, high], ... }
|
||
const data = {
|
||
'2025-01-01': [100, 105, 98, 108],
|
||
'2025-01-02': [105, 102, 100, 107],
|
||
'2025-01-03': [102, 110, 101, 112],
|
||
'2025-01-04': [110, 108, 106, 113],
|
||
'2025-01-05': [108, 115, 107, 116],
|
||
'2025-01-06': [115, 117, 114, 120],
|
||
'2025-01-07': [117, 112, 111, 119],
|
||
'2025-01-08': [112, 118, 110, 121],
|
||
'2025-01-09': [118, 121, 117, 123],
|
||
'2025-01-10': [121, 119, 118, 122],
|
||
}
|
||
|
||
// 全局配置(颜色、尺寸、间距等),集中管理,便于统一调整
|
||
const CONFIG = {
|
||
colors: {up: '#000000FF', down: '#00000045'},
|
||
grid: {left: '2%', right: '2%', top: 40, bottom: 110},
|
||
zoom: {bottom: 16, height: 50},
|
||
linewidth: {stem: 1.5, openTick: 1.2, closeTick: 1.6, closeLine: 1.5},
|
||
tick: {min: 4, max: 10, scale: 0.4},
|
||
}
|
||
|
||
// 通用 tooltip 格式化(按索引回读原始 O/H/L/C)
|
||
function makeTooltipFormatter(dataMap, dates) {
|
||
return function (params) {
|
||
let p = Array.isArray(params) ? params[0] : params
|
||
let idx = p.dataIndex
|
||
let d = dates[idx]
|
||
let ohlc = dataMap[d] || []
|
||
let o = ohlc[0], c = ohlc[1], l = ohlc[2], h = ohlc[3]
|
||
let chg = (c - o)
|
||
let chgPct = o ? (chg / o * 100) : 0
|
||
let sign = chg >= 0 ? '+' : ''
|
||
return [
|
||
d,
|
||
'O: ' + o,
|
||
'C: ' + c,
|
||
'H: ' + h,
|
||
'L: ' + l,
|
||
'Chg: ' + sign + chg.toFixed(2) + ' (' + sign + chgPct.toFixed(2) + '%)',
|
||
].join('<br/>')
|
||
}
|
||
}
|
||
|
||
// 通用基础配置构建(legend/tooltip/grid/xAxis/yAxis/dataZoom)
|
||
function buildBaseOption(dates, series, formatter) {
|
||
return {
|
||
animation: false,
|
||
legend: {show: false},
|
||
tooltip: {trigger: 'axis', axisPointer: {type: 'cross'}, formatter},
|
||
grid: CONFIG.grid,
|
||
xAxis: {type: 'category', data: dates, boundaryGap: true, axisLine: {onZero: false}},
|
||
yAxis: {scale: true},
|
||
dataZoom: [
|
||
{type: 'inside', xAxisIndex: 0, start: 0, end: 100},
|
||
{
|
||
show: true,
|
||
type: 'slider',
|
||
xAxisIndex: 0,
|
||
bottom: CONFIG.zoom.bottom,
|
||
height: CONFIG.zoom.height,
|
||
start: 0,
|
||
end: 100,
|
||
},
|
||
],
|
||
series,
|
||
}
|
||
}
|
||
|
||
// Range Band + Close Line(高低区间带 + 收盘线):趋势与波动范围直观
|
||
function buildRangeCloseOption(dataMap) {
|
||
const dates = Object.keys(dataMap).sort()
|
||
const lowArr = dates.map(d => dataMap[d][2])
|
||
const highArr = dates.map(d => dataMap[d][3])
|
||
const closeArr = dates.map(d => dataMap[d][1])
|
||
const rangeArr = highArr.map((h, i) => h - lowArr[i])
|
||
|
||
const series = [
|
||
{
|
||
name: 'Low',
|
||
type: 'line',
|
||
data: lowArr,
|
||
stack: 'range',
|
||
symbol: 'none',
|
||
lineStyle: {width: 0},
|
||
emphasis: {disabled: true},
|
||
},
|
||
{
|
||
name: 'Range',
|
||
type: 'line',
|
||
data: rangeArr,
|
||
stack: 'range',
|
||
symbol: 'none',
|
||
lineStyle: {width: 0},
|
||
areaStyle: {color: CONFIG.colors.down, opacity: 0.6},
|
||
z: 2,
|
||
},
|
||
{
|
||
name: 'Close',
|
||
type: 'line',
|
||
data: closeArr,
|
||
symbol: 'none',
|
||
lineStyle: {color: CONFIG.colors.up, width: CONFIG.linewidth.closeLine},
|
||
z: 3,
|
||
},
|
||
]
|
||
|
||
return buildBaseOption(dates, series, makeTooltipFormatter(dataMap, dates))
|
||
}
|
||
|
||
// Minimal OHLC(竖线 + 左/右短横):信息等价于K线但形态简洁
|
||
function buildOHLCMinimalOption(dataMap) {
|
||
const dates = Object.keys(dataMap).sort()
|
||
const ohlcData = dates.map((d, i) => [i, ...dataMap[d]])
|
||
|
||
// 自定义渲染:竖线=高低区间;左短横=开盘;右短横=收盘;颜色=涨跌
|
||
function renderOHLCMinimal(params, api) {
|
||
let idx = api.value(0)
|
||
let open = api.value(1)
|
||
let close = api.value(2)
|
||
let low = api.value(3)
|
||
let high = api.value(4)
|
||
let up = close >= open
|
||
|
||
let x = api.coord([idx, 0])[0]
|
||
let highPoint = api.coord([idx, high])
|
||
let lowPoint = api.coord([idx, low])
|
||
let openPoint = api.coord([idx, open])
|
||
let closePoint = api.coord([idx, close])
|
||
|
||
let band = api.size([1, 0])[0]
|
||
let tick = Math.max(CONFIG.tick.min, Math.min(CONFIG.tick.max, band * CONFIG.tick.scale))
|
||
let color = up ? CONFIG.colors.up : CONFIG.colors.down
|
||
|
||
return {
|
||
type: 'group',
|
||
children: [
|
||
{
|
||
type: 'line',
|
||
shape: {x1: x, y1: highPoint[1], x2: x, y2: lowPoint[1]},
|
||
style: {stroke: color, lineWidth: CONFIG.linewidth.stem, opacity: 0.9},
|
||
},
|
||
{
|
||
type: 'line',
|
||
shape: {x1: x - tick, y1: openPoint[1], x2: x, y2: openPoint[1]},
|
||
style: {stroke: color, lineWidth: CONFIG.linewidth.openTick, opacity: 0.95},
|
||
},
|
||
{
|
||
type: 'line',
|
||
shape: {x1: x, y1: closePoint[1], x2: x + tick, y2: closePoint[1]},
|
||
style: {stroke: color, lineWidth: CONFIG.linewidth.closeTick, opacity: 0.95},
|
||
},
|
||
],
|
||
}
|
||
}
|
||
|
||
const series = [
|
||
{
|
||
name: 'ohlc',
|
||
type: 'custom',
|
||
renderItem: renderOHLCMinimal,
|
||
encode: {x: 0, y: [1, 2, 3, 4]},
|
||
data: ohlcData,
|
||
z: 10,
|
||
},
|
||
]
|
||
|
||
return buildBaseOption(dates, series, makeTooltipFormatter(dataMap, dates))
|
||
}
|
||
|
||
(function () {
|
||
const amis = amisRequire('amis/embed')
|
||
const amisJson = {
|
||
type: 'page',
|
||
title: 'Strategy',
|
||
body: {
|
||
type: 'tabs',
|
||
tabsMode: 'vertical',
|
||
tabs: [
|
||
{
|
||
title: 'Charts',
|
||
// 在一个 Tab 中展示两张图,便于同屏对比
|
||
body: [
|
||
{
|
||
type: 'chart',
|
||
height: 500,
|
||
config: buildRangeCloseOption(data),
|
||
},
|
||
{
|
||
type: 'chart',
|
||
height: 500,
|
||
config: buildOHLCMinimalOption(data),
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
}
|
||
// `dates` 已直接在图表配置中使用,这里无需通过 amis data 传递
|
||
amis.embed('#root', amisJson, {}, {theme: 'antd'})
|
||
})()
|
||
</script>
|
||
</html>
|