147 lines
5.2 KiB
JavaScript
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
|
|
};
|
|
});
|