Files
MBN_STOCK_WEBVIEW/Web/candle-options-workflow.js

147 lines
5.2 KiB
JavaScript

(function (root, factory) {
"use strict";
const api = Object.freeze(factory());
if (typeof module === "object" && module.exports) module.exports = api;
if (root) root.MbnCandleOptionsWorkflow = api;
})(typeof globalThis === "object" ? globalThis : this, function () {
"use strict";
const CANDLE_BUILDER_KEY = "s8010";
const supportedSources = Object.freeze([
"legacy-stock-workflow",
"legacy-fixed-workflow",
"legacy-industry-workflow",
"legacy-overseas-workflow",
"legacy-trading-halt-workflow"
]);
function normalizeOptions(ma5, ma20) {
if (typeof ma5 !== "boolean" || typeof ma20 !== "boolean") {
throw new TypeError("Global moving-average flags must be boolean values.");
}
return Object.freeze({ ma5, ma20 });
}
function expectedGraphicType(ma5, ma20) {
const options = normalizeOptions(ma5, ma20);
const flags = [];
if (options.ma5) flags.push("MA5");
if (options.ma20) flags.push("MA20");
return flags.join(",");
}
function hasAdapters(value) {
return value && typeof value === "object" && !Array.isArray(value) &&
value.stock && value.fixed && value.industry && value.overseas && value.tradingHalt;
}
function recreateCandleEntry(value, ma5, ma20, adapters, now = new Date()) {
const movingAverages = normalizeOptions(ma5, ma20);
if (!value || typeof value !== "object" || Array.isArray(value) ||
value.builderKey !== CANDLE_BUILDER_KEY || typeof value.enabled !== "boolean" ||
!hasAdapters(adapters)) return null;
const operator = value.operator;
if (!operator || typeof operator !== "object" || !supportedSources.includes(operator.source)) return null;
const options = {
id: value.id,
fadeDuration: value.fadeDuration,
ma5: movingAverages.ma5,
ma20: movingAverages.ma20
};
let recreated = null;
try {
switch (operator.source) {
case "legacy-stock-workflow": {
const cut = adapters.stock.getStockCut(operator.cutId);
if (cut?.kind !== "candle") return null;
recreated = adapters.stock.createStockPlaylistEntry(operator.stock, cut.id, options);
break;
}
case "legacy-fixed-workflow": {
const action = adapters.fixed.actions.find(item => item.id === operator.actionId);
if (action?.builderKey !== CANDLE_BUILDER_KEY || action.available !== true) return null;
recreated = adapters.fixed.createFixedPlaylistEntry(action.id, { ...options, now });
break;
}
case "legacy-industry-workflow": {
const cut = adapters.industry.cuts.find(item => item.id === operator.cutId);
if (cut?.kind !== "candle") return null;
recreated = adapters.industry.createIndustryPlaylistEntry(operator.market, cut.id, {
...options,
selected: operator.selected,
pair: operator.pair,
now
});
break;
}
case "legacy-overseas-workflow": {
const action = adapters.overseas.stockActions.find(item => item.id === operator.actionId);
if (operator.kind !== "stock" || action?.kind !== "candle") return null;
recreated = adapters.overseas.createOverseasStockPlaylistEntry(
operator.identity,
action.id,
options);
break;
}
case "legacy-trading-halt-workflow": {
const action = adapters.tradingHalt.getAction(operator.actionId);
if (action?.kind !== "candle") return null;
recreated = adapters.tradingHalt.createTradingHaltPlaylistEntry(
operator.tradingHalt,
action.id,
options);
break;
}
default:
return null;
}
} catch {
return null;
}
return recreated ? { ...recreated, enabled: value.enabled } : null;
}
function entryAlreadyMatches(value, ma5, ma20) {
const graphicType = expectedGraphicType(ma5, ma20);
return value?.selection?.graphicType === graphicType &&
value?.graphicType === graphicType &&
value?.operator?.movingAverages?.ma5 === ma5 &&
value?.operator?.movingAverages?.ma20 === ma20;
}
function synchronizePlaylist(values, ma5, ma20, adapters, now = new Date()) {
normalizeOptions(ma5, ma20);
if (!Array.isArray(values)) throw new TypeError("A playlist array is required.");
const invalidIds = [];
let changed = false;
const playlist = values.map(value => {
if (value?.builderKey !== CANDLE_BUILDER_KEY) return value;
if (entryAlreadyMatches(value, ma5, ma20)) return value;
const recreated = recreateCandleEntry(value, ma5, ma20, adapters, now);
if (!recreated) {
invalidIds.push(typeof value?.id === "string" ? value.id : "");
return value;
}
changed = true;
return recreated;
});
return Object.freeze({
playlist: Object.freeze(playlist),
changed,
valid: invalidIds.length === 0,
invalidIds: Object.freeze(invalidIds)
});
}
return {
candleBuilderKey: CANDLE_BUILDER_KEY,
supportedSources,
normalizeOptions,
expectedGraphicType,
recreateCandleEntry,
synchronizePlaylist
};
});