복합지표 전략 추가

This commit is contained in:
Macbook
2026-06-12 13:26:53 +09:00
parent 9208418c33
commit 52137cf1db
27 changed files with 2712 additions and 55 deletions
@@ -24,6 +24,34 @@ import {
syncPriceExtremeSimpleFields,
} from '../utils/priceExtremeIndicators';
import { getStrategyIndicatorDisplayName } from '../utils/strategyPaletteStorage';
import {
buildInflection33Tree,
isInflection33PairRoot,
isInflection33PaletteValue,
inflection33DisplayName,
inflection33SummaryText,
inferInflection33FilterLevel,
inflection33FilterLevelLabel,
} from '../utils/inflection33Strategy';
import {
buildStableStrategyTree,
isStableStrategyPairRoot,
isStableStrategyPaletteValue,
stableStrategyDisplayName,
stableStrategySummaryText,
inferStableStrategyFilterLevel,
stableStrategyFilterLevelLabel,
type StableStrategyId,
} from '../utils/stableStrategyPairs';
import {
buildPriceExtremeBreakoutTree,
isPriceExtremeBreakoutPairPaletteValue,
isPriceExtremeBreakoutPairRoot,
priceExtremePairDisplayName,
priceExtremePairSummaryText,
inferPriceExtremeFilterLevel,
filterLevelLabel,
} from '../utils/priceExtremeBreakoutPair';
import {
buildStochOverboughtPairTree,
isStochOverboughtPairPaletteValue,
@@ -767,10 +795,35 @@ export const nodeToText = (node: LogicNode, DEF: DefType = DEF_DEFAULTS): string
return `${indName} - ${L} ${C}`;
}
if (node.type === 'AND') {
if (isStableStrategyPairRoot(node) && node.stableStrategyPair?.mode === 'buy') {
const { strategyId, mode } = node.stableStrategyPair;
const level = inferStableStrategyFilterLevel(node);
return `${stableStrategyDisplayName(strategyId as StableStrategyId, mode)} [필터:${stableStrategyFilterLevelLabel(level)}]\n${stableStrategySummaryText(strategyId as StableStrategyId, mode, level, DEF)}`;
}
if (isInflection33PairRoot(node) && node.inflection33Pair?.mode === 'buy') {
const { mode, period } = node.inflection33Pair;
const level = inferInflection33FilterLevel(node);
return `${inflection33DisplayName(mode, period)} [필터:${inflection33FilterLevelLabel(level)}]\n${inflection33SummaryText(mode, period, level)}`;
}
if (isPriceExtremeBreakoutPairRoot(node) && node.priceExtremePair) {
const { mode, shortPeriod, longPeriod } = node.priceExtremePair;
const level = inferPriceExtremeFilterLevel(node);
return `${priceExtremePairDisplayName(mode, shortPeriod, longPeriod)} [필터:${filterLevelLabel(level)}]\n${priceExtremePairSummaryText(mode, shortPeriod, longPeriod, level)}`;
}
const parts = (node.children ?? []).map(c => nodeToText(c, DEF));
return parts.length === 0 ? '(AND 그룹)' : parts.join('\nAND ');
}
if (node.type === 'OR') {
if (isStableStrategyPairRoot(node) && node.stableStrategyPair?.mode === 'sell') {
const { strategyId, mode } = node.stableStrategyPair;
const level = inferStableStrategyFilterLevel(node);
return `${stableStrategyDisplayName(strategyId as StableStrategyId, mode)} [필터:${stableStrategyFilterLevelLabel(level)}]\n${stableStrategySummaryText(strategyId as StableStrategyId, mode, level, DEF)}`;
}
if (isInflection33PairRoot(node) && node.inflection33Pair?.mode === 'sell') {
const { mode, period } = node.inflection33Pair;
const level = inferInflection33FilterLevel(node);
return `${inflection33DisplayName(mode, period)} [필터:${inflection33FilterLevelLabel(level)}]\n${inflection33SummaryText(mode, period, level)}`;
}
if (isStochOverboughtPairRoot(node)) {
const sec = node.stochPair!.secondaryIndicatorType;
return `${stochPairDisplayName(sec)}\n${stochPairSummaryText(sec)}`;
@@ -1213,12 +1266,21 @@ export type MakeNodeOptions = {
/** Stoch 과열×보조 복합 */
stochPair?: boolean;
secondaryIndicator?: string;
/** 9·20일 신고가/신저가 복합 */
priceExtremeBreakout?: boolean;
/** 33변곡 EMA+채널 복합 */
inflection33?: boolean;
/** 추천 안정형 전략 복합 */
stableStrategy?: boolean;
};
/** 팔레트 드래그 payload → makeNode 옵션 */
export function makeNodeOptionsFromPalette(data: {
composite?: boolean;
stochPair?: boolean;
priceExtremeBreakout?: boolean;
inflection33?: boolean;
stableStrategy?: boolean;
secondaryIndicator?: string;
period?: number;
shortPeriod?: number;
@@ -1233,6 +1295,15 @@ export function makeNodeOptionsFromPalette(data: {
secondaryIndicator: data.secondaryIndicator ?? 'CCI',
};
}
if (data.priceExtremeBreakout || (data.value && isPriceExtremeBreakoutPairPaletteValue(data.value))) {
return { priceExtremeBreakout: true };
}
if (data.inflection33 || (data.value && isInflection33PaletteValue(data.value))) {
return { inflection33: true };
}
if (data.stableStrategy || (data.value && isStableStrategyPaletteValue(data.value))) {
return { stableStrategy: true };
}
if (data.composite) {
return {
composite: true,
@@ -1254,6 +1325,18 @@ export const makeNode = (
if (options?.stochPair || isStochOverboughtPairPaletteValue(value)) {
return buildStochOverboughtPairTree(options?.secondaryIndicator ?? 'CCI', DEF);
}
if (options?.priceExtremeBreakout || isPriceExtremeBreakoutPairPaletteValue(value)) {
const tree = buildPriceExtremeBreakoutTree(value, DEF);
if (tree) return tree;
}
if (options?.inflection33 || isInflection33PaletteValue(value)) {
const tree = buildInflection33Tree(value, DEF);
if (tree) return tree;
}
if (options?.stableStrategy || isStableStrategyPaletteValue(value)) {
const tree = buildStableStrategyTree(value, DEF);
if (tree) return tree;
}
if (options?.composite) {
let condition = makeCompositeCondition(value, signalType, DEF);
condition = {