Hub market: add option expiry Friday yellow dashed splits.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -197,9 +197,11 @@
|
|||||||
const elPrevCloseLine = document.getElementById("market-prev-close-line");
|
const elPrevCloseLine = document.getElementById("market-prev-close-line");
|
||||||
const elPrevHlLines = document.getElementById("market-prev-hl-lines");
|
const elPrevHlLines = document.getElementById("market-prev-hl-lines");
|
||||||
const elDaySplit = document.getElementById("market-day-split");
|
const elDaySplit = document.getElementById("market-day-split");
|
||||||
|
const elOptionExpirySplit = document.getElementById("market-option-expiry-split");
|
||||||
const PREV_CLOSE_LINE_STORAGE_KEY = "hub-market-prev-close-line";
|
const PREV_CLOSE_LINE_STORAGE_KEY = "hub-market-prev-close-line";
|
||||||
const PREV_HL_LINES_STORAGE_KEY = "hub-market-prev-hl-lines";
|
const PREV_HL_LINES_STORAGE_KEY = "hub-market-prev-hl-lines";
|
||||||
const DAY_SPLIT_STORAGE_KEY = "hub-market-day-split";
|
const DAY_SPLIT_STORAGE_KEY = "hub-market-day-split";
|
||||||
|
const OPTION_EXPIRY_SPLIT_STORAGE_KEY = "hub-market-option-expiry-split";
|
||||||
const BJ_OFFSET_SEC = 8 * 60 * 60;
|
const BJ_OFFSET_SEC = 8 * 60 * 60;
|
||||||
const elFsToolbar = document.getElementById("market-fs-toolbar");
|
const elFsToolbar = document.getElementById("market-fs-toolbar");
|
||||||
const elFsExchange = document.getElementById("market-fs-exchange");
|
const elFsExchange = document.getElementById("market-fs-exchange");
|
||||||
@@ -342,6 +344,14 @@
|
|||||||
saveBoolPref(DAY_SPLIT_STORAGE_KEY, on);
|
saveBoolPref(DAY_SPLIT_STORAGE_KEY, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadOptionExpirySplitPref() {
|
||||||
|
return loadBoolPref(OPTION_EXPIRY_SPLIT_STORAGE_KEY, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveOptionExpirySplitPref(on) {
|
||||||
|
saveBoolPref(OPTION_EXPIRY_SPLIT_STORAGE_KEY, on);
|
||||||
|
}
|
||||||
|
|
||||||
function loadPrevCloseLinePref() {
|
function loadPrevCloseLinePref() {
|
||||||
return loadBoolPref(PREV_CLOSE_LINE_STORAGE_KEY, false);
|
return loadBoolPref(PREV_CLOSE_LINE_STORAGE_KEY, false);
|
||||||
}
|
}
|
||||||
@@ -456,6 +466,18 @@
|
|||||||
applyTradingDaySplit(on);
|
applyTradingDaySplit(on);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyOptionExpirySplit(enabled) {
|
||||||
|
if (window.HubChartDraw && typeof window.HubChartDraw.setOptionExpirySplit === "function") {
|
||||||
|
window.HubChartDraw.setOptionExpirySplit(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncOptionExpirySplitUi() {
|
||||||
|
const on = !!(elOptionExpirySplit && elOptionExpirySplit.checked);
|
||||||
|
saveOptionExpirySplitPref(on);
|
||||||
|
applyOptionExpirySplit(on);
|
||||||
|
}
|
||||||
|
|
||||||
function ensureDrawLayer() {
|
function ensureDrawLayer() {
|
||||||
if (drawAttached || !window.HubChartDraw || !chart || !candleSeries) return;
|
if (drawAttached || !window.HubChartDraw || !chart || !candleSeries) return;
|
||||||
window.HubChartDraw.attach({
|
window.HubChartDraw.attach({
|
||||||
@@ -471,6 +493,9 @@
|
|||||||
});
|
});
|
||||||
window.HubChartDraw.setViewKey(currentChartViewKey());
|
window.HubChartDraw.setViewKey(currentChartViewKey());
|
||||||
applyTradingDaySplit(elDaySplit ? elDaySplit.checked : loadDaySplitPref());
|
applyTradingDaySplit(elDaySplit ? elDaySplit.checked : loadDaySplitPref());
|
||||||
|
applyOptionExpirySplit(
|
||||||
|
elOptionExpirySplit ? elOptionExpirySplit.checked : loadOptionExpirySplitPref()
|
||||||
|
);
|
||||||
drawAttached = true;
|
drawAttached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3481,6 +3506,11 @@
|
|||||||
elDaySplit.addEventListener("change", syncTradingDaySplitUi);
|
elDaySplit.addEventListener("change", syncTradingDaySplitUi);
|
||||||
applyTradingDaySplit(elDaySplit.checked);
|
applyTradingDaySplit(elDaySplit.checked);
|
||||||
}
|
}
|
||||||
|
if (elOptionExpirySplit) {
|
||||||
|
elOptionExpirySplit.checked = loadOptionExpirySplitPref();
|
||||||
|
elOptionExpirySplit.addEventListener("change", syncOptionExpirySplitUi);
|
||||||
|
applyOptionExpirySplit(elOptionExpirySplit.checked);
|
||||||
|
}
|
||||||
const pageMarket = document.getElementById("page-market");
|
const pageMarket = document.getElementById("page-market");
|
||||||
const fsKeyTargets = [window, pageMarket, elChartWrap, chartHost].filter(Boolean);
|
const fsKeyTargets = [window, pageMarket, elChartWrap, chartHost].filter(Boolean);
|
||||||
fsKeyTargets.forEach(function (el) {
|
fsKeyTargets.forEach(function (el) {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
let unsubClick = null;
|
let unsubClick = null;
|
||||||
let mainBound = false;
|
let mainBound = false;
|
||||||
let tradingDaySplitEnabled = false;
|
let tradingDaySplitEnabled = false;
|
||||||
|
let optionExpirySplitEnabled = false;
|
||||||
const BJ_OFFSET_SEC = 8 * 60 * 60;
|
const BJ_OFFSET_SEC = 8 * 60 * 60;
|
||||||
|
|
||||||
function uid() {
|
function uid() {
|
||||||
@@ -409,6 +410,53 @@
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** OKX 期权到期:每周五 08:00 UTC(=北京 16:00) */
|
||||||
|
function collectOptionExpiryBoundaries(candles) {
|
||||||
|
if (!candles.length) return [];
|
||||||
|
const minT = Number(candles[0].time);
|
||||||
|
const maxT = Number(candles[candles.length - 1].time);
|
||||||
|
if (!Number.isFinite(minT) || !Number.isFinite(maxT)) return [];
|
||||||
|
const out = [];
|
||||||
|
const cur = new Date(minT * 1000);
|
||||||
|
cur.setUTCHours(0, 0, 0, 0);
|
||||||
|
cur.setUTCDate(cur.getUTCDate() - 1);
|
||||||
|
const endSec = maxT + 2 * 86400;
|
||||||
|
while (cur.getTime() / 1000 <= endSec) {
|
||||||
|
if (cur.getUTCDay() === 5) {
|
||||||
|
const boundary = Math.floor(
|
||||||
|
Date.UTC(cur.getUTCFullYear(), cur.getUTCMonth(), cur.getUTCDate(), 8, 0, 0) / 1000
|
||||||
|
);
|
||||||
|
if (boundary >= minT - 3600 && boundary <= maxT + 3600) {
|
||||||
|
if (!out.length || out[out.length - 1] !== boundary) out.push(boundary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur.setUTCDate(cur.getUTCDate() + 1);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawOptionExpirySplits(ctx, w, h) {
|
||||||
|
if (!optionExpirySplitEnabled || !chart) return;
|
||||||
|
const candles = getCandles();
|
||||||
|
if (!candles.length) return;
|
||||||
|
const boundaries = collectOptionExpiryBoundaries(candles);
|
||||||
|
if (!boundaries.length) return;
|
||||||
|
ctx.save();
|
||||||
|
ctx.strokeStyle = "#eab308";
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.setLineDash([5, 4]);
|
||||||
|
boundaries.forEach(function (t) {
|
||||||
|
const x = timeToX(t);
|
||||||
|
if (x == null || !Number.isFinite(x) || x < -2 || x > w + 2) return;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, h);
|
||||||
|
ctx.stroke();
|
||||||
|
});
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
function drawRect(ctx, x1, y1, x2, y2, selected) {
|
function drawRect(ctx, x1, y1, x2, y2, selected) {
|
||||||
if (x1 == null || y1 == null || x2 == null || y2 == null) return;
|
if (x1 == null || y1 == null || x2 == null || y2 == null) return;
|
||||||
const l = Math.min(x1, x2);
|
const l = Math.min(x1, x2);
|
||||||
@@ -743,6 +791,7 @@
|
|||||||
const h = hostEl.clientHeight;
|
const h = hostEl.clientHeight;
|
||||||
ctx.clearRect(0, 0, w, h);
|
ctx.clearRect(0, 0, w, h);
|
||||||
drawTradingDaySplits(ctx, w, h);
|
drawTradingDaySplits(ctx, w, h);
|
||||||
|
drawOptionExpirySplits(ctx, w, h);
|
||||||
drawings.forEach(function (d) {
|
drawings.forEach(function (d) {
|
||||||
if (d.hidden) ctx.globalAlpha = 0.14;
|
if (d.hidden) ctx.globalAlpha = 0.14;
|
||||||
renderDrawing(ctx, d, w, h, d.id === selectedId);
|
renderDrawing(ctx, d, w, h, d.id === selectedId);
|
||||||
@@ -1451,10 +1500,16 @@
|
|||||||
scheduleRedraw();
|
scheduleRedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOptionExpirySplit(enabled) {
|
||||||
|
optionExpirySplitEnabled = !!enabled;
|
||||||
|
scheduleRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
window.HubChartDraw = {
|
window.HubChartDraw = {
|
||||||
attach: attach,
|
attach: attach,
|
||||||
setViewKey: setViewKey,
|
setViewKey: setViewKey,
|
||||||
setTradingDaySplit: setTradingDaySplit,
|
setTradingDaySplit: setTradingDaySplit,
|
||||||
|
setOptionExpirySplit: setOptionExpirySplit,
|
||||||
resize: scheduleRedraw,
|
resize: scheduleRedraw,
|
||||||
redraw: scheduleRedraw,
|
redraw: scheduleRedraw,
|
||||||
destroy: destroy,
|
destroy: destroy,
|
||||||
|
|||||||
@@ -324,6 +324,9 @@
|
|||||||
<label class="market-day-split-opt" title="北京时间 8:00 交易切日竖线">
|
<label class="market-day-split-opt" title="北京时间 8:00 交易切日竖线">
|
||||||
<input type="checkbox" id="market-day-split" /> 交易间隔日
|
<input type="checkbox" id="market-day-split" /> 交易间隔日
|
||||||
</label>
|
</label>
|
||||||
|
<label class="market-day-split-opt" title="OKX 期权到期:每周五 08:00 UTC(北京 16:00) 黄虚线">
|
||||||
|
<input type="checkbox" id="market-option-expiry-split" /> 期权间隔日
|
||||||
|
</label>
|
||||||
<details class="market-ind-menu">
|
<details class="market-ind-menu">
|
||||||
<summary>技术指标</summary>
|
<summary>技术指标</summary>
|
||||||
<div class="market-ind-options">
|
<div class="market-ind-options">
|
||||||
@@ -1393,8 +1396,8 @@
|
|||||||
|
|
||||||
<div id="toast"></div>
|
<div id="toast"></div>
|
||||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||||
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
<script src="/assets/chart_draw.js?v=20260720-option-expiry-split"></script>
|
||||||
<script src="/assets/chart.js?v=20260715-fs-landscape"></script>
|
<script src="/assets/chart.js?v=20260720-option-expiry-split"></script>
|
||||||
<script src="/assets/plan.js?v=20260720-autofill"></script>
|
<script src="/assets/plan.js?v=20260720-autofill"></script>
|
||||||
<script src="/assets/calculator.js?v=20260715-calc-tabs"></script>
|
<script src="/assets/calculator.js?v=20260715-calc-tabs"></script>
|
||||||
<script src="/assets/trade_stats_calendar.js?v=3"></script>
|
<script src="/assets/trade_stats_calendar.js?v=3"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user