refactor(web): 移除主图威科夫选项与叠层
去掉区间/阶段/时间/VP 开关、Cycle 摘要面板及绘制逻辑;分析请求默认 include_wyckoff=0。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,9 +1,6 @@
|
|||||||
/* chart_format.js — split from chart.js */
|
/* chart_format.js — split from chart.js */
|
||||||
/* chart.js */
|
/* chart.js */
|
||||||
function updateChartDisplay() {
|
function updateChartDisplay() {
|
||||||
if (typeof renderWyckoffCycleSummary === 'function') {
|
|
||||||
renderWyckoffCycleSummary();
|
|
||||||
}
|
|
||||||
if (currentData) {
|
if (currentData) {
|
||||||
// 检测K线周期是否切换
|
// 检测K线周期是否切换
|
||||||
const curPeriod = $('#subSubPeriodKline').is(':checked') ? 'subsub' :
|
const curPeriod = $('#subSubPeriodKline').is(':checked') ? 'subsub' :
|
||||||
|
|||||||
@@ -40,12 +40,6 @@ function disposeTradingViewCharts() {
|
|||||||
|
|
||||||
var chartRoot = document.getElementById('tradingview_chart');
|
var chartRoot = document.getElementById('tradingview_chart');
|
||||||
if (chartRoot) {
|
if (chartRoot) {
|
||||||
// 重建前救出 Cycle Summary,避免 innerHTML 清空时被销毁
|
|
||||||
var summaryEl = document.getElementById('wyckoffCycleSummary');
|
|
||||||
var chartHost = chartRoot.parentElement;
|
|
||||||
if (summaryEl && chartRoot.contains(summaryEl) && chartHost) {
|
|
||||||
chartHost.appendChild(summaryEl);
|
|
||||||
}
|
|
||||||
chartRoot.innerHTML = '';
|
chartRoot.innerHTML = '';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* chart_tv_overlays.js — structure zones / wyckoff / BSP / FX / bollinger */
|
/* chart_tv_overlays.js — structure zones / BSP / FX / bollinger */
|
||||||
|
|
||||||
/** 标记 time 必须落在主 series 的 K 线 time 上,否则 LWC 会抛 Value is null */
|
/** 标记 time 必须落在主 series 的 K 线 time 上,否则 LWC 会抛 Value is null */
|
||||||
function alignMarkersToCandles(markers, candles) {
|
function alignMarkersToCandles(markers, candles) {
|
||||||
@@ -331,229 +331,6 @@ function chartTvRenderOverlays(ctx) {
|
|||||||
}
|
}
|
||||||
} catch (e) { console.error('结构区整体绘制出错:', e); }
|
} catch (e) { console.error('结构区整体绘制出错:', e); }
|
||||||
}
|
}
|
||||||
// 区间/阶段/时间/VP:框线同中枢;标记并入主 K(同 BSP),时间对齐 candles
|
|
||||||
(function drawWrLikeChan() {
|
|
||||||
const candleSeries = tvWidget.series.candleSeries
|
|
||||||
|| tvWidget.series.barSeries
|
|
||||||
|| tvWidget.series.lineSeries
|
|
||||||
|| tvWidget.series.areaSeries
|
|
||||||
|| tvWidget.series.heikinSeries
|
|
||||||
|| tvWidget.series.renkoSeries;
|
|
||||||
const candleTimes = (candles || []).map(function(c) { return c.time; })
|
|
||||||
.filter(function(t) { return t != null && !isNaN(t); });
|
|
||||||
const barStep = (candleTimes.length >= 2)
|
|
||||||
? Math.max(1, candleTimes[1] - candleTimes[0])
|
|
||||||
: 3600;
|
|
||||||
const lastKlineTime = candleTimes.length ? candleTimes[candleTimes.length - 1] : NaN;
|
|
||||||
const chartStart = candleTimes.length ? candleTimes[0] : NaN;
|
|
||||||
|
|
||||||
const toSec = function(t) {
|
|
||||||
if (t == null) return NaN;
|
|
||||||
if (typeof t === 'number') return t > 1e12 ? Math.floor(t / 1000) : Math.floor(t);
|
|
||||||
const ms = new Date(t).getTime();
|
|
||||||
return isNaN(ms) ? NaN : Math.floor(ms / 1000);
|
|
||||||
};
|
|
||||||
// 标记必须落在主 series 的 time 上(与 KLC 趋势 nearestTime 同思路)
|
|
||||||
const snapToCandle = function(t) {
|
|
||||||
if (!candleTimes.length || isNaN(t)) return t;
|
|
||||||
let best = candleTimes[0], bd = Math.abs(candleTimes[0] - t);
|
|
||||||
for (let i = 1; i < candleTimes.length; i++) {
|
|
||||||
const d = Math.abs(candleTimes[i] - t);
|
|
||||||
if (d < bd) { bd = d; best = candleTimes[i]; }
|
|
||||||
}
|
|
||||||
return best;
|
|
||||||
};
|
|
||||||
const ensureSpan = function(t0, t1) {
|
|
||||||
if (isNaN(t0) || isNaN(t1)) return [t0, t1];
|
|
||||||
if (t1 < t0) { const x = t0; t0 = t1; t1 = x; }
|
|
||||||
if (t1 <= t0) t1 = t0 + barStep;
|
|
||||||
return [t0, t1];
|
|
||||||
};
|
|
||||||
const drawBox = function(t0, t1, hi, lo, color) {
|
|
||||||
const span = ensureSpan(t0, t1);
|
|
||||||
t0 = span[0]; t1 = span[1];
|
|
||||||
if (isNaN(t0) || isNaN(t1) || isNaN(hi) || isNaN(lo)) return;
|
|
||||||
const opt = { color: color, lineWidth: 1, lastValueVisible: false, priceLineVisible: false };
|
|
||||||
mainChart.addLineSeries(opt).setData([{ time: t0, value: hi }, { time: t1, value: hi }]);
|
|
||||||
mainChart.addLineSeries(opt).setData([{ time: t0, value: lo }, { time: t1, value: lo }]);
|
|
||||||
mainChart.addLineSeries(opt).setData([{ time: t0, value: lo }, { time: t0, value: hi }]);
|
|
||||||
mainChart.addLineSeries(opt).setData([{ time: t1, value: lo }, { time: t1, value: hi }]);
|
|
||||||
};
|
|
||||||
const mkPL = function(price, color, title, style) {
|
|
||||||
if (!candleSeries) return;
|
|
||||||
const p = parseFloat(price);
|
|
||||||
if (isNaN(p)) return;
|
|
||||||
try {
|
|
||||||
candleSeries.createPriceLine({
|
|
||||||
price: p, color: color,
|
|
||||||
lineWidth: style === 2 ? 1 : 2,
|
|
||||||
lineStyle: style || 0,
|
|
||||||
axisLabelVisible: true,
|
|
||||||
title: title
|
|
||||||
});
|
|
||||||
} catch (e) { console.warn('价位线失败', title, e); }
|
|
||||||
};
|
|
||||||
const phaseColors = { A: '#f1c40f', B: '#9b59b6', C: '#e67e22', D: '#2ecc71', E: '#3498db' };
|
|
||||||
const eventColors = {
|
|
||||||
Spring: '#27ae60', SOS: '#2ecc71', LPS: '#16a085',
|
|
||||||
UTAD: '#e74c3c', SOW: '#c0392b', LPSY: '#d35400'
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrMarkers = [];
|
|
||||||
window.wrMarkers = [];
|
|
||||||
const pushMarker = function(m) {
|
|
||||||
if (!m || isNaN(m.time)) return;
|
|
||||||
m.time = snapToCandle(m.time);
|
|
||||||
if (!isNaN(chartStart) && (m.time < chartStart || m.time > lastKlineTime)) return;
|
|
||||||
wrMarkers.push(m);
|
|
||||||
};
|
|
||||||
|
|
||||||
const drawOne = function(w, cfg) {
|
|
||||||
if (!w) return;
|
|
||||||
const showR = $(cfg.rangeSel).is(':checked');
|
|
||||||
const showP = $(cfg.phasesSel).is(':checked');
|
|
||||||
const showE = $(cfg.eventsSel).is(':checked');
|
|
||||||
const showV = $(cfg.vpSel).is(':checked');
|
|
||||||
if (!showR && !showP && !showE && !showV) return;
|
|
||||||
// WYCKOFF-MULTI-CYCLE-001:遍历 cycles;无则退化为顶层单段
|
|
||||||
const cycles = (w.cycles && w.cycles.length)
|
|
||||||
? w.cycles
|
|
||||||
: (w.trading_range ? [{
|
|
||||||
id: 0, status: 'ACTIVE', role: 'latest',
|
|
||||||
trading_range: w.trading_range, phases: w.phases, events: w.events,
|
|
||||||
volume_profile: w.volume_profile, volume_confirm: w.volume_confirm,
|
|
||||||
period: { start_time: w.trading_range.start_time, end_time: w.trading_range.end_time, bars: w.trading_range.bars }
|
|
||||||
}] : []);
|
|
||||||
const tfLabel = (cfg.tfLabel || cfg.tag || 'TF').toString().toUpperCase();
|
|
||||||
|
|
||||||
cycles.forEach(function(cycle) {
|
|
||||||
const tr = cycle.trading_range;
|
|
||||||
if (!tr) return;
|
|
||||||
const cid = (cycle.id != null) ? cycle.id : 0;
|
|
||||||
const isActive = String(cycle.status || '').toUpperCase() === 'ACTIVE';
|
|
||||||
const cTag = tfLabel + ' C' + cid + ' ';
|
|
||||||
try {
|
|
||||||
let t0 = toSec(tr.start_time || (cycle.period && cycle.period.start_time));
|
|
||||||
let t1 = toSec(tr.end_time || (cycle.period && cycle.period.end_time));
|
|
||||||
// 仅 ACTIVE 可拉到最新 K;历史用 period.end
|
|
||||||
if (isActive && !isNaN(lastKlineTime)) {
|
|
||||||
t1 = lastKlineTime;
|
|
||||||
} else if (cycle.period && cycle.period.end_time) {
|
|
||||||
t1 = toSec(cycle.period.end_time);
|
|
||||||
}
|
|
||||||
const hi = parseFloat(tr.high), lo = parseFloat(tr.low);
|
|
||||||
if (showR && !isNaN(hi) && !isNaN(lo)) {
|
|
||||||
drawBox(t0, t1, hi, lo, cfg.color);
|
|
||||||
if (isActive) {
|
|
||||||
mkPL(hi, cfg.color, cfg.hiTag, 0);
|
|
||||||
mkPL(lo, cfg.color, cfg.loTag, 0);
|
|
||||||
mkPL(tr.mid, cfg.color, cfg.midTag, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (showP && cycle.phases && cycle.phases.length && !isNaN(hi)) {
|
|
||||||
cycle.phases.forEach(function(ph) {
|
|
||||||
let p0 = toSec(ph.start_time);
|
|
||||||
let p1 = ph.end_time ? toSec(ph.end_time) : t1;
|
|
||||||
const sp = ensureSpan(p0, p1);
|
|
||||||
p0 = sp[0]; p1 = sp[1];
|
|
||||||
if (isNaN(p0) || isNaN(p1)) return;
|
|
||||||
const col = phaseColors[ph.phase] || '#95a5a6';
|
|
||||||
mainChart.addLineSeries({
|
|
||||||
color: col, lineWidth: 3, lastValueVisible: false, priceLineVisible: false
|
|
||||||
}).setData([{ time: p0, value: hi }, { time: p1, value: hi }]);
|
|
||||||
pushMarker({
|
|
||||||
time: p0, position: 'aboveBar', color: col, shape: 'square',
|
|
||||||
text: cTag + 'Phase ' + String(ph.phase || ''), size: 1
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (showV && isActive && cycle.volume_profile) {
|
|
||||||
const vp = cycle.volume_profile;
|
|
||||||
mkPL(vp.poc, cfg.vpColor, cfg.pocTag, 0);
|
|
||||||
mkPL(vp.vah, cfg.vpColor, cfg.vahTag, 2);
|
|
||||||
mkPL(vp.val, cfg.vpColor, cfg.valTag, 2);
|
|
||||||
const bins = (vp.bins || []).filter(function(b) { return b && b.volume > 0; })
|
|
||||||
.slice().sort(function(a, b) { return b.volume - a.volume; }).slice(0, 8);
|
|
||||||
let maxVol = 0;
|
|
||||||
bins.forEach(function(b) { if (b.volume > maxVol) maxVol = b.volume; });
|
|
||||||
const span = (!isNaN(t0) && !isNaN(t1) && t1 > t0) ? (t1 - t0) : barStep * 12;
|
|
||||||
bins.forEach(function(b) {
|
|
||||||
if (!b.volume || maxVol <= 0 || isNaN(t1)) return;
|
|
||||||
const wSec = Math.max(barStep, Math.floor(span * 0.12 * (b.volume / maxVol)));
|
|
||||||
let leftT = Math.max(isNaN(t0) ? (t1 - wSec) : t0, t1 - wSec);
|
|
||||||
if (leftT >= t1) leftT = t1 - barStep;
|
|
||||||
if (leftT >= t1) return;
|
|
||||||
const alpha = 0.25 + 0.55 * (b.volume / maxVol);
|
|
||||||
mainChart.addLineSeries({
|
|
||||||
color: cfg.vpRgb.replace('ALPHA', alpha.toFixed(2)),
|
|
||||||
lineWidth: 2, lastValueVisible: false, priceLineVisible: false
|
|
||||||
}).setData([{ time: leftT, value: b.price }, { time: t1, value: b.price }]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (showE && cycle.events && cycle.events.length) {
|
|
||||||
const checks = (cycle.volume_confirm && cycle.volume_confirm.event_checks) || {};
|
|
||||||
cycle.events.forEach(function(ev) {
|
|
||||||
const t = toSec(ev.time);
|
|
||||||
if (isNaN(t)) return;
|
|
||||||
const typ = ev.type || '';
|
|
||||||
const chk = checks[typ] || {};
|
|
||||||
const volOk = (chk.volume_ok != null) ? chk.volume_ok : ev.volume_ok;
|
|
||||||
const ok = volOk === true ? '✓' : (volOk === false ? '✗' : '');
|
|
||||||
pushMarker({
|
|
||||||
time: t,
|
|
||||||
position: (typ === 'Spring' || typ === 'LPS' || typ === 'SOW') ? 'belowBar' : 'aboveBar',
|
|
||||||
color: eventColors[typ] || '#7f8c8d',
|
|
||||||
shape: (typ === 'Spring' || typ === 'SOW' || typ === 'LPS') ? 'arrowDown' : 'arrowUp',
|
|
||||||
text: cTag + typ + ok,
|
|
||||||
size: 1
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) { console.error('区间叠层出错', cfg.name, 'C' + cid, e); }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($('#showMainWrRange').is(':checked') || $('#showMainWrPhases').is(':checked')
|
|
||||||
|| $('#showMainWrEvents').is(':checked') || $('#showMainWrVP').is(':checked')) {
|
|
||||||
drawOne(currentData.wyckoff, {
|
|
||||||
name: '主', tag: '', tfLabel: (currentData.timeframe || '4H'), color: '#3498db', vpColor: '#8e44ad',
|
|
||||||
vpRgb: 'rgba(142, 68, 173, ALPHA)',
|
|
||||||
rangeSel: '#showMainWrRange', phasesSel: '#showMainWrPhases',
|
|
||||||
eventsSel: '#showMainWrEvents', vpSel: '#showMainWrVP',
|
|
||||||
hiTag: 'WR.H', loTag: 'WR.L', midTag: 'WR.M',
|
|
||||||
pocTag: 'POC', vahTag: 'VAH', valTag: 'VAL'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ($('#showElementWrRange').is(':checked') || $('#showElementWrPhases').is(':checked')
|
|
||||||
|| $('#showElementWrEvents').is(':checked') || $('#showElementWrVP').is(':checked')) {
|
|
||||||
drawOne(currentData.element_wyckoff, {
|
|
||||||
name: '次', tag: 'e', tfLabel: (currentData.element_timeframe || '2H'), color: '#e67e22', vpColor: '#d35400',
|
|
||||||
vpRgb: 'rgba(211, 84, 0, ALPHA)',
|
|
||||||
rangeSel: '#showElementWrRange', phasesSel: '#showElementWrPhases',
|
|
||||||
eventsSel: '#showElementWrEvents', vpSel: '#showElementWrVP',
|
|
||||||
hiTag: 'eWR.H', loTag: 'eWR.L', midTag: 'eWR.M',
|
|
||||||
pocTag: 'ePOC', vahTag: 'eVAH', valTag: 'eVAL'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ($('#showSubSubWrRange').is(':checked') || $('#showSubSubWrPhases').is(':checked')
|
|
||||||
|| $('#showSubSubWrEvents').is(':checked') || $('#showSubSubWrVP').is(':checked')) {
|
|
||||||
drawOne(currentData.sub_sub_wyckoff, {
|
|
||||||
name: '次次', tag: 's', tfLabel: (currentData.sub_sub_timeframe || '1H'), color: '#27ae60', vpColor: '#16a085',
|
|
||||||
vpRgb: 'rgba(22, 160, 133, ALPHA)',
|
|
||||||
rangeSel: '#showSubSubWrRange', phasesSel: '#showSubSubWrPhases',
|
|
||||||
eventsSel: '#showSubSubWrEvents', vpSel: '#showSubSubWrVP',
|
|
||||||
hiTag: 'sWR.H', loTag: 'sWR.L', midTag: 'sWR.M',
|
|
||||||
pocTag: 'sPOC', vahTag: 'sVAH', valTag: 'sVAL'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同 BSP:写入 window,稍后与分型/买卖点一并 setMarkers
|
|
||||||
wrMarkers.sort(function(a, b) { return a.time - b.time; });
|
|
||||||
window.wrMarkers = wrMarkers;
|
|
||||||
if (typeof renderWyckoffCycleSummary === 'function') {
|
|
||||||
renderWyckoffCycleSummary();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
// 显示未完成中枢 - 分别处理主周期、次周期和次次周期
|
// 显示未完成中枢 - 分别处理主周期、次周期和次次周期
|
||||||
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || $('#showSubSubZs').is(':checked') || $('#showSubSubBiZs').is(':checked')) {
|
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || $('#showSubSubZs').is(':checked') || $('#showSubSubBiZs').is(':checked')) {
|
||||||
console.log('绘制未完成中枢 - 已启用');
|
console.log('绘制未完成中枢 - 已启用');
|
||||||
@@ -2336,8 +2113,7 @@ function chartTvRenderOverlays(ctx) {
|
|||||||
...(window.kluDivMarkersElement || []),
|
...(window.kluDivMarkersElement || []),
|
||||||
...(window.kluDivMarkersSubSub || []),
|
...(window.kluDivMarkersSubSub || []),
|
||||||
...trendMarkersToUse,
|
...trendMarkersToUse,
|
||||||
...(window.bspMarkers || []),
|
...(window.bspMarkers || [])
|
||||||
...(window.wrMarkers || [])
|
|
||||||
];
|
];
|
||||||
if (combinedMarkers.length > 0) {
|
if (combinedMarkers.length > 0) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -2346,7 +2122,6 @@ function chartTvRenderOverlays(ctx) {
|
|||||||
'个,小周期分型:', allElementFxMarkers.length,
|
'个,小周期分型:', allElementFxMarkers.length,
|
||||||
'个,UnitTF:', (window.unittfMarkers || []).length,
|
'个,UnitTF:', (window.unittfMarkers || []).length,
|
||||||
'个,BSP标记:', (window.bspMarkers || []).length,
|
'个,BSP标记:', (window.bspMarkers || []).length,
|
||||||
'个,区间标记:', (window.wrMarkers || []).length,
|
|
||||||
'个)'
|
'个)'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2455,8 +2230,7 @@ function chartTvRenderOverlays(ctx) {
|
|||||||
...(window.kluDivMarkersElement || []),
|
...(window.kluDivMarkersElement || []),
|
||||||
...(window.kluDivMarkersSubSub || []),
|
...(window.kluDivMarkersSubSub || []),
|
||||||
...trendMarkersToUse,
|
...trendMarkersToUse,
|
||||||
...(window.bspMarkers || []),
|
...(window.bspMarkers || [])
|
||||||
...(window.wrMarkers || [])
|
|
||||||
];
|
];
|
||||||
if (onlyMainAndU.length > 0) {
|
if (onlyMainAndU.length > 0) {
|
||||||
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
||||||
|
|||||||
@@ -367,21 +367,6 @@ function chartTvBuildShell(ctx) {
|
|||||||
// 创建主图表
|
// 创建主图表
|
||||||
const mainChart = LightweightCharts.createChart(mainChartContainer, createChartOptions(true, 'main'));
|
const mainChart = LightweightCharts.createChart(mainChartContainer, createChartOptions(true, 'main'));
|
||||||
|
|
||||||
// Cycle Summary 挂到主图左下角(相对 K 线主图 pane,而非整图底边)
|
|
||||||
(function mountWyckoffCycleSummary() {
|
|
||||||
var summaryEl = document.getElementById('wyckoffCycleSummary');
|
|
||||||
if (!summaryEl) {
|
|
||||||
summaryEl = document.createElement('div');
|
|
||||||
summaryEl.id = 'wyckoffCycleSummary';
|
|
||||||
summaryEl.className = 'wyckoff-cycle-summary';
|
|
||||||
summaryEl.setAttribute('aria-live', 'polite');
|
|
||||||
}
|
|
||||||
mainChartContainer.appendChild(summaryEl);
|
|
||||||
if (typeof renderWyckoffCycleSummary === 'function') {
|
|
||||||
try { renderWyckoffCycleSummary(); } catch (e) {}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// 创建成交量图表 - 只显示底部的时间轴
|
// 创建成交量图表 - 只显示底部的时间轴
|
||||||
const volumeChart = LightweightCharts.createChart(volumeChartContainer, createChartOptions(false, 'volume'));
|
const volumeChart = LightweightCharts.createChart(volumeChartContainer, createChartOptions(false, 'volume'));
|
||||||
|
|
||||||
|
|||||||
@@ -164,9 +164,6 @@ function applyAnalyzeSuccess(data, symbol, options) {
|
|||||||
currentData = data;
|
currentData = data;
|
||||||
window._lastChartSymbol = symbol;
|
window._lastChartSymbol = symbol;
|
||||||
window._lastFullAnalyzeAt = Date.now();
|
window._lastFullAnalyzeAt = Date.now();
|
||||||
if (typeof renderWyckoffCycleSummary === 'function') {
|
|
||||||
renderWyckoffCycleSummary();
|
|
||||||
}
|
|
||||||
|
|
||||||
const ready = !!(tvWidget && tvWidget.state && tvWidget.state.isInitialized && tvWidget.mainChart);
|
const ready = !!(tvWidget && tvWidget.state && tvWidget.state.isInitialized && tvWidget.mainChart);
|
||||||
const structureZonesOn = $('#showMainStructureZone').is(':checked');
|
const structureZonesOn = $('#showMainStructureZone').is(':checked');
|
||||||
@@ -212,7 +209,8 @@ function analyzeChart(options) {
|
|||||||
end_time: ctx.endTimeMs,
|
end_time: ctx.endTimeMs,
|
||||||
elements_only: false,
|
elements_only: false,
|
||||||
zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000,
|
zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000,
|
||||||
include_structure_zones: $('#showMainStructureZone').is(':checked') ? 1 : 0
|
include_structure_zones: $('#showMainStructureZone').is(':checked') ? 1 : 0,
|
||||||
|
include_wyckoff: 0
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$('#refreshLoadingSpinner').hide();
|
$('#refreshLoadingSpinner').hide();
|
||||||
@@ -327,7 +325,8 @@ function autoRefreshChart(options) {
|
|||||||
end_time: ctx.endTimeMs,
|
end_time: ctx.endTimeMs,
|
||||||
elements_only: false,
|
elements_only: false,
|
||||||
zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000,
|
zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000,
|
||||||
include_structure_zones: $('#showMainStructureZone').is(':checked') ? 1 : 0
|
include_structure_zones: $('#showMainStructureZone').is(':checked') ? 1 : 0,
|
||||||
|
include_wyckoff: 0
|
||||||
},
|
},
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
$('#refreshLoadingSpinner').hide();
|
$('#refreshLoadingSpinner').hide();
|
||||||
|
|||||||
@@ -93,17 +93,6 @@ $(document).on('change', '#showMainStructureZone', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 区间/阶段/时间/VP:与缠论笔开关一样,本地重绘
|
|
||||||
$(document).on(
|
|
||||||
'change',
|
|
||||||
'#showMainWrRange, #showMainWrPhases, #showMainWrEvents, #showMainWrVP,' +
|
|
||||||
'#showElementWrRange, #showElementWrPhases, #showElementWrEvents, #showElementWrVP,' +
|
|
||||||
'#showSubSubWrRange, #showSubSubWrPhases, #showSubSubWrEvents, #showSubSubWrVP',
|
|
||||||
function() {
|
|
||||||
updateChartDisplay();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 添加趋势显示复选框变更事件(主/元素),变更后刷新主图
|
// 添加趋势显示复选框变更事件(主/元素),变更后刷新主图
|
||||||
$('#showMainTrend').change(function() {
|
$('#showMainTrend').change(function() {
|
||||||
updateChartDisplay();
|
updateChartDisplay();
|
||||||
|
|||||||
@@ -1,251 +1,5 @@
|
|||||||
/* ui.js */
|
/* ui.js */
|
||||||
|
|
||||||
/** Trading OS 可消费的威科夫 Cycle 摘要(Confirmed + Live 分区;cycles[0]=ACTIVE) */
|
|
||||||
function buildWyckoffCycleSummaryPayload(w, tf) {
|
|
||||||
if (!w) return null;
|
|
||||||
const cycles = (w.cycles && w.cycles.length)
|
|
||||||
? w.cycles
|
|
||||||
: (w.trading_range ? [{
|
|
||||||
id: 0, status: 'ACTIVE', role: 'latest', lifecycle: w.lifecycle || 'UNKNOWN',
|
|
||||||
trading_range: w.trading_range, bias: w.bias,
|
|
||||||
phases: w.phases || [], events: w.events || [],
|
|
||||||
confirmed: { phases: w.phases || [], events: w.events || [] },
|
|
||||||
live: w.live || null,
|
|
||||||
confidence: { overall: null },
|
|
||||||
period: {
|
|
||||||
start_time: w.trading_range.start_time,
|
|
||||||
end_time: w.trading_range.end_time,
|
|
||||||
bars: w.trading_range.bars
|
|
||||||
}
|
|
||||||
}] : []);
|
|
||||||
if (!cycles.length) return null;
|
|
||||||
const active = cycles[0]; // 禁止 cycles[-1]
|
|
||||||
const confirmed = active.confirmed || {
|
|
||||||
phases: active.phases || w.phases || [],
|
|
||||||
events: active.events || w.events || []
|
|
||||||
};
|
|
||||||
const live = active.live || w.live || null;
|
|
||||||
const cPhases = confirmed.phases || [];
|
|
||||||
const cEvents = confirmed.events || [];
|
|
||||||
const lastPhase = cPhases.length ? cPhases[cPhases.length - 1] : null;
|
|
||||||
const lastEvent = cEvents.length ? cEvents[cEvents.length - 1] : null;
|
|
||||||
const tr = active.trading_range || {};
|
|
||||||
const prev = cycles.length > 1 ? cycles[1] : null;
|
|
||||||
const biasLabel = ({
|
|
||||||
accumulation: 'Accumulation',
|
|
||||||
distribution: 'Distribution',
|
|
||||||
unknown: 'Unknown'
|
|
||||||
})[active.bias] || (active.bias || 'Unknown');
|
|
||||||
const liveCand = (live && live.event_candidates && live.event_candidates[0]) || null;
|
|
||||||
const liveConf = live && live.confidence ? live.confidence.overall : null;
|
|
||||||
return {
|
|
||||||
symbol: (typeof currentData !== 'undefined' && currentData && currentData.symbol) || $('#symbol').val() || '',
|
|
||||||
timeframe: (tf || w.timeframe || $('#timeframe').val() || '').toString().toUpperCase(),
|
|
||||||
active: {
|
|
||||||
cycle_id: active.id != null ? active.id : 0,
|
|
||||||
status: active.status || 'ACTIVE',
|
|
||||||
lifecycle: active.lifecycle || (live && live.lifecycle) || 'UNKNOWN',
|
|
||||||
structure: biasLabel,
|
|
||||||
phase_confirmed: lastPhase ? String(lastPhase.phase || '') : null,
|
|
||||||
event_confirmed: lastEvent ? String(lastEvent.type || '') : null,
|
|
||||||
phase_candidate: live ? live.phase_candidate : null,
|
|
||||||
event_candidate: liveCand ? liveCand.type : null,
|
|
||||||
event_candidate_confidence: liveCand ? liveCand.confidence : null,
|
|
||||||
next_expected: live ? live.next_expected : null,
|
|
||||||
range: {
|
|
||||||
low: tr.low,
|
|
||||||
high: tr.high,
|
|
||||||
start_time: (active.period && active.period.start_time) || tr.start_time,
|
|
||||||
end_time: (active.period && active.period.end_time) || tr.end_time,
|
|
||||||
bars: (active.period && active.period.bars) != null ? active.period.bars : tr.bars
|
|
||||||
},
|
|
||||||
confidence_confirmed: (active.confidence && active.confidence.overall != null)
|
|
||||||
? active.confidence.overall
|
|
||||||
: null,
|
|
||||||
confidence_live: liveConf
|
|
||||||
},
|
|
||||||
confirmed_history: cycles.slice(1, 4).map(function(c) {
|
|
||||||
const evs = ((c.confirmed && c.confirmed.events) || c.events || [])
|
|
||||||
.map(function(e) { return e.type; }).filter(Boolean);
|
|
||||||
return {
|
|
||||||
cycle_id: c.id,
|
|
||||||
structure: ({
|
|
||||||
accumulation: 'Accumulation',
|
|
||||||
distribution: 'Distribution',
|
|
||||||
unknown: 'Unknown'
|
|
||||||
})[c.bias] || c.bias,
|
|
||||||
events: evs,
|
|
||||||
lifecycle: c.lifecycle || 'COMPLETED'
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
live: live,
|
|
||||||
cycle_count: cycles.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function _wrLayerTogglesOn(prefix) {
|
|
||||||
// prefix: Main | Element | SubSub
|
|
||||||
return $('#show' + prefix + 'WrRange').is(':checked')
|
|
||||||
|| $('#show' + prefix + 'WrPhases').is(':checked')
|
|
||||||
|| $('#show' + prefix + 'WrEvents').is(':checked')
|
|
||||||
|| $('#show' + prefix + 'WrVP').is(':checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 面板展示用中文(机器可读 payload 仍保留英文原值) */
|
|
||||||
function _wcsLifecycleZh(v) {
|
|
||||||
return ({
|
|
||||||
UNKNOWN: '未知',
|
|
||||||
FORMING: '形成中',
|
|
||||||
CONFIRMED: '已确认',
|
|
||||||
COMPLETED: '已完成',
|
|
||||||
ACTIVE: '当前'
|
|
||||||
})[v] || v || '未知';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _wcsStructureZh(v) {
|
|
||||||
if (!v) return '—';
|
|
||||||
const key = String(v).toLowerCase();
|
|
||||||
return ({
|
|
||||||
accumulation: '吸筹',
|
|
||||||
distribution: '派发',
|
|
||||||
unknown: '未知'
|
|
||||||
})[key] || ({
|
|
||||||
Accumulation: '吸筹',
|
|
||||||
Distribution: '派发',
|
|
||||||
Unknown: '未知'
|
|
||||||
})[v] || v;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _wcsEventZh(v) {
|
|
||||||
if (v == null || v === '') return '—';
|
|
||||||
return ({
|
|
||||||
Spring: '弹簧',
|
|
||||||
UTAD: '上升后派发',
|
|
||||||
SOS: '强势信号',
|
|
||||||
SOW: '弱势信号',
|
|
||||||
LPS: '最后支撑',
|
|
||||||
LPSY: '最后供应',
|
|
||||||
Test: '回测',
|
|
||||||
PSY: '初步供应',
|
|
||||||
BC: '买气高潮',
|
|
||||||
AR: '自动回落',
|
|
||||||
ST: '二次测试',
|
|
||||||
SC: '卖气高潮'
|
|
||||||
})[v] || v;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _htmlWyckoffSummaryBlock(payload, blockClass) {
|
|
||||||
if (!payload || !payload.active) return '';
|
|
||||||
const a = payload.active;
|
|
||||||
const fmtPx = function(v) {
|
|
||||||
if (v == null || isNaN(Number(v))) return '—';
|
|
||||||
const n = Number(v);
|
|
||||||
return n >= 1000 ? n.toFixed(1) : n.toFixed(4);
|
|
||||||
};
|
|
||||||
const pct = function(v) {
|
|
||||||
if (v == null || isNaN(Number(v))) return '—';
|
|
||||||
return Math.round(Number(v) * 100) + '%';
|
|
||||||
};
|
|
||||||
let html = '<div class="wcs-block ' + (blockClass || '') + '">';
|
|
||||||
html += '<div class="wcs-title">' + (payload.symbol || '') + ' '
|
|
||||||
+ (payload.timeframe || '') + '</div>';
|
|
||||||
html += '<div><span class="wcs-badge">当前 C' + a.cycle_id + '</span> '
|
|
||||||
+ '<span class="wcs-badge" style="background:#fff8c5;color:#9a6700;">'
|
|
||||||
+ _wcsLifecycleZh(a.lifecycle) + '</span></div>';
|
|
||||||
html += '<div class="wcs-active">';
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">结构</span><span class="wcs-v">'
|
|
||||||
+ _wcsStructureZh(a.structure) + '</span></div>';
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">阶段</span><span class="wcs-v">'
|
|
||||||
+ (a.phase_candidate
|
|
||||||
? ('阶段 ' + a.phase_candidate + '(候选)')
|
|
||||||
: (a.phase_confirmed ? ('阶段 ' + a.phase_confirmed) : '—'))
|
|
||||||
+ '</span></div>';
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">事件</span><span class="wcs-v">'
|
|
||||||
+ (a.event_candidate
|
|
||||||
? (_wcsEventZh(a.event_candidate) + '(候选)')
|
|
||||||
: _wcsEventZh(a.event_confirmed))
|
|
||||||
+ '</span></div>';
|
|
||||||
if (a.event_confirmed && a.event_candidate) {
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">已确认</span><span class="wcs-v">'
|
|
||||||
+ _wcsEventZh(a.event_confirmed) + '</span></div>';
|
|
||||||
}
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">区间</span><span class="wcs-v">'
|
|
||||||
+ fmtPx(a.range && a.range.low) + ' – ' + fmtPx(a.range && a.range.high) + '</span></div>';
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">置信度</span><span class="wcs-v">'
|
|
||||||
+ pct(a.confidence_live != null ? a.confidence_live : a.confidence_confirmed) + '</span></div>';
|
|
||||||
if (a.next_expected) {
|
|
||||||
html += '<div class="wcs-row"><span class="wcs-k">下一步</span><span class="wcs-v">'
|
|
||||||
+ _wcsEventZh(a.next_expected) + '</span></div>';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
if (payload.confirmed_history && payload.confirmed_history.length) {
|
|
||||||
html += '<div class="wcs-prev"><div style="margin-bottom:2px;">已确认历史</div>';
|
|
||||||
payload.confirmed_history.forEach(function(h) {
|
|
||||||
const ev = (h.events && h.events.length)
|
|
||||||
? h.events.map(_wcsEventZh).join('、')
|
|
||||||
: '—';
|
|
||||||
html += '<div>C' + h.cycle_id + ' ' + _wcsStructureZh(h.structure) + ' · ' + ev + '</div>';
|
|
||||||
});
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderWyckoffCycleSummary() {
|
|
||||||
const $el = $('#wyckoffCycleSummary');
|
|
||||||
if (!$el.length) return;
|
|
||||||
if (!currentData) {
|
|
||||||
$el.hide().empty();
|
|
||||||
window.wyckoffCycleSummary = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const layers = [];
|
|
||||||
if (_wrLayerTogglesOn('Main') && currentData.wyckoff) {
|
|
||||||
layers.push({
|
|
||||||
key: 'main',
|
|
||||||
cls: 'wcs-main',
|
|
||||||
payload: buildWyckoffCycleSummaryPayload(
|
|
||||||
currentData.wyckoff,
|
|
||||||
currentData.timeframe || currentData.wyckoff.timeframe || $('#timeframe').val()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (_wrLayerTogglesOn('Element') && currentData.element_wyckoff) {
|
|
||||||
layers.push({
|
|
||||||
key: 'element',
|
|
||||||
cls: 'wcs-element',
|
|
||||||
payload: buildWyckoffCycleSummaryPayload(
|
|
||||||
currentData.element_wyckoff,
|
|
||||||
currentData.element_timeframe || currentData.element_wyckoff.timeframe || $('#elementTimeframe').val()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (_wrLayerTogglesOn('SubSub') && currentData.sub_sub_wyckoff) {
|
|
||||||
layers.push({
|
|
||||||
key: 'sub_sub',
|
|
||||||
cls: 'wcs-subsub',
|
|
||||||
payload: buildWyckoffCycleSummaryPayload(
|
|
||||||
currentData.sub_sub_wyckoff,
|
|
||||||
currentData.sub_sub_timeframe || currentData.sub_sub_wyckoff.timeframe || $('#subSubTimeframe').val()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const valid = layers.filter(function(L) { return L.payload && L.payload.active; });
|
|
||||||
if (!valid.length) {
|
|
||||||
$el.hide().empty();
|
|
||||||
window.wyckoffCycleSummary = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const bag = {};
|
|
||||||
let html = '';
|
|
||||||
valid.forEach(function(L) {
|
|
||||||
bag[L.key] = L.payload;
|
|
||||||
html += _htmlWyckoffSummaryBlock(L.payload, L.cls);
|
|
||||||
});
|
|
||||||
window.wyckoffCycleSummary = bag;
|
|
||||||
$el.html(html).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSymbols() {
|
function loadSymbols() {
|
||||||
$.get('/api/symbols', function(data) {
|
$.get('/api/symbols', function(data) {
|
||||||
|
|||||||
+7
-131
@@ -96,81 +96,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1; /* 确保图表在数据面板之上 */
|
z-index: 1; /* 确保图表在数据面板之上 */
|
||||||
}
|
}
|
||||||
/* 挂在主图容器内:左下角 = K线主图左下,而非整图(含副图)底边 */
|
|
||||||
.wyckoff-cycle-summary {
|
|
||||||
position: absolute;
|
|
||||||
left: 8px;
|
|
||||||
bottom: 28px; /* 略抬高,避开主图时间轴 */
|
|
||||||
top: auto;
|
|
||||||
right: auto;
|
|
||||||
z-index: 1100;
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 300px;
|
|
||||||
max-height: calc(100% - 36px);
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 8px 10px;
|
|
||||||
background: rgba(255, 255, 255, 0.94);
|
|
||||||
border: 1px solid #d0d7de;
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.08);
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 1.45;
|
|
||||||
color: #24292f;
|
|
||||||
display: none;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-block {
|
|
||||||
padding: 6px 0;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-block + .wcs-block {
|
|
||||||
border-top: 1px solid #eaeef2;
|
|
||||||
margin-top: 6px;
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-block.wcs-main { border-left: 3px solid #3498db; padding-left: 8px; }
|
|
||||||
.wyckoff-cycle-summary .wcs-block.wcs-element { border-left: 3px solid #e67e22; padding-left: 8px; }
|
|
||||||
.wyckoff-cycle-summary .wcs-block.wcs-subsub { border-left: 3px solid #27ae60; padding-left: 8px; }
|
|
||||||
.wyckoff-cycle-summary .wcs-title {
|
|
||||||
font-weight: 650;
|
|
||||||
font-size: 13px;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
letter-spacing: 0.02em;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 8px;
|
|
||||||
margin: 2px 0;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-k {
|
|
||||||
color: #656d76;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-v {
|
|
||||||
text-align: right;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-active {
|
|
||||||
margin-top: 2px;
|
|
||||||
padding: 6px 0 4px;
|
|
||||||
border-top: 1px solid #eaeef2;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-prev {
|
|
||||||
margin-top: 6px;
|
|
||||||
padding-top: 6px;
|
|
||||||
border-top: 1px dashed #eaeef2;
|
|
||||||
color: #656d76;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
.wyckoff-cycle-summary .wcs-badge {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1px 6px;
|
|
||||||
border-radius: 3px;
|
|
||||||
background: #ddf4ff;
|
|
||||||
color: #0969da;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
.chart-options {
|
.chart-options {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
@@ -1092,22 +1017,6 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showMainBsp">
|
<input class="form-check-input" type="checkbox" id="showMainBsp">
|
||||||
<label class="form-check-label" for="showMainBsp">买卖点</label>
|
<label class="form-check-label" for="showMainBsp">买卖点</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline ms-2">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainWrRange">
|
|
||||||
<label class="form-check-label" for="showMainWrRange">区间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainWrPhases">
|
|
||||||
<label class="form-check-label" for="showMainWrPhases">阶段</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainWrEvents">
|
|
||||||
<label class="form-check-label" for="showMainWrEvents">时间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainWrVP">
|
|
||||||
<label class="form-check-label" for="showMainWrVP">VP</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center mt-1">
|
<div class="d-flex align-items-center mt-1">
|
||||||
<label class="form-label me-0 mb-0">次周期:</label>
|
<label class="form-label me-0 mb-0">次周期:</label>
|
||||||
@@ -1150,22 +1059,6 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showElementBsp">
|
<input class="form-check-input" type="checkbox" id="showElementBsp">
|
||||||
<label class="form-check-label" for="showElementBsp">买卖点</label>
|
<label class="form-check-label" for="showElementBsp">买卖点</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline ms-2">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementWrRange">
|
|
||||||
<label class="form-check-label" for="showElementWrRange">区间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementWrPhases">
|
|
||||||
<label class="form-check-label" for="showElementWrPhases">阶段</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementWrEvents">
|
|
||||||
<label class="form-check-label" for="showElementWrEvents">时间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementWrVP">
|
|
||||||
<label class="form-check-label" for="showElementWrVP">VP</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center mt-1">
|
<div class="d-flex align-items-center mt-1">
|
||||||
<label class="form-label me-0 mb-0">次次周期:</label>
|
<label class="form-label me-0 mb-0">次次周期:</label>
|
||||||
@@ -1208,22 +1101,6 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showSubSubBsp">
|
<input class="form-check-input" type="checkbox" id="showSubSubBsp">
|
||||||
<label class="form-check-label" for="showSubSubBsp">买卖点</label>
|
<label class="form-check-label" for="showSubSubBsp">买卖点</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline ms-2">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showSubSubWrRange">
|
|
||||||
<label class="form-check-label" for="showSubSubWrRange">区间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showSubSubWrPhases">
|
|
||||||
<label class="form-check-label" for="showSubSubWrPhases">阶段</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showSubSubWrEvents">
|
|
||||||
<label class="form-check-label" for="showSubSubWrEvents">时间</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showSubSubWrVP">
|
|
||||||
<label class="form-check-label" for="showSubSubWrVP">VP</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1233,7 +1110,6 @@
|
|||||||
|
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div id="tradingview_chart"></div>
|
<div id="tradingview_chart"></div>
|
||||||
<div id="wyckoffCycleSummary" class="wyckoff-cycle-summary" aria-live="polite"></div>
|
|
||||||
<!-- 技术指标下拉菜单 -->
|
<!-- 技术指标下拉菜单 -->
|
||||||
<div class="indicator-dropdown dropdown">
|
<div class="indicator-dropdown dropdown">
|
||||||
<button class="add-indicator-btn dropdown-toggle" type="button" id="indicatorDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
<button class="add-indicator-btn dropdown-toggle" type="button" id="indicatorDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
@@ -1386,19 +1262,19 @@
|
|||||||
<script defer src="{{ url_for('static', filename='js/app/api_client.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/api_client.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/state.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/state.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/trend.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/trend.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/macd_ui.js') }}?v=20260809t"></script>
|
<script defer src="{{ url_for('static', filename='js/app/macd_ui.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_format.js') }}?v=20260810a"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_format.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_view.js') }}?v=20260810a"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_view.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_lifecycle.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_lifecycle.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_shell.js') }}?v=20260809j"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_shell.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_indicators.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_indicators.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_chan.js') }}?v=20260810d"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_chan.js') }}?v=20260810d"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_overlays.js') }}?v=20260810c"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_overlays.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv_finalize.js') }}?v=20260810a"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv_finalize.js') }}?v=20260810a"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tv.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tv.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_sync.js') }}?v=20260809z"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_sync.js') }}?v=20260809z"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/chart_tables.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/chart_tables.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/ui.js') }}?v=20260810a"></script>
|
<script defer src="{{ url_for('static', filename='js/app/ui.js') }}?v=20260810e"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/overlays.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/overlays.js') }}?v=20260808i"></script>
|
||||||
<script defer src="{{ url_for('static', filename='js/app/main.js') }}?v=20260808i"></script>
|
<script defer src="{{ url_for('static', filename='js/app/main.js') }}?v=20260808i"></script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user