From 990b3a071099990431506cfd2cb69e487f6dbd9c Mon Sep 17 00:00:00 2001 From: Macbook Date: Fri, 29 May 2026 02:07:53 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A0=84=EB=9E=B5=ED=8E=B8=EC=A7=91=EA=B8=B0?= =?UTF-8?q?=20=EC=A7=80=ED=91=9C=EC=9A=94=EC=86=8C=EA=B0=92=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/conditionPeriods.ts | 34 +++--- frontend/src/utils/strategyEditorShared.tsx | 36 ++++-- frontend/src/utils/strategyFlowLayout.ts | 13 +-- .../src/utils/strategyIndicatorPeriods.ts | 108 ++++++++++++++++++ 4 files changed, 154 insertions(+), 37 deletions(-) create mode 100644 frontend/src/utils/strategyIndicatorPeriods.ts diff --git a/frontend/src/utils/conditionPeriods.ts b/frontend/src/utils/conditionPeriods.ts index 24c6067..73a7b8a 100644 --- a/frontend/src/utils/conditionPeriods.ts +++ b/frontend/src/utils/conditionPeriods.ts @@ -28,6 +28,11 @@ import { THRESHOLD_HL_UNDER, } from './thresholdSymbols'; import { DEFAULT_START_CANDLE, normalizeStartCandleType } from './strategyStartNodes'; +import { + getIndicatorPrimaryPeriod, + formatIndicatorPeriodLabel, +} from './strategyIndicatorPeriods'; +import type { DefType } from './strategyEditorShared'; export interface IndicatorPeriodDef { rsiPeriod: number; @@ -52,22 +57,19 @@ export const VALUE_FIELD_PREFIX: Record = { INVEST_PSYCHOLOGICAL: 'INVEST_PSY_VALUE', }; -export function getDefaultIndicatorPeriod(indicatorType: string, def: IndicatorPeriodDef): number { - switch (indicatorType) { - case 'NEW_HIGH': - case 'NEW_LOW': - return 9; - case 'RSI': return def.rsiPeriod; - case 'CCI': return def.cciPeriod; - case 'ADX': return def.adxPeriod; - case 'WILLIAMS_R': return def.williamsR; - case 'TRIX': return def.trixPeriod; - case 'VR': return def.vrPeriod; - case 'PSYCHOLOGICAL': - case 'NEW_PSYCHOLOGICAL': return def.newPsy; - case 'INVEST_PSYCHOLOGICAL': return def.investPsy; - default: return 14; - } +export function getDefaultIndicatorPeriod( + indicatorType: string, + def: IndicatorPeriodDef | DefType, +): number { + return getIndicatorPrimaryPeriod(indicatorType, def as DefType); +} + +/** @internal DefType 전체 필드 — 팔레트 라벨 등 */ +export function getDefaultIndicatorPeriodLabel( + indicatorType: string, + def: IndicatorPeriodDef | DefType, +): string { + return formatIndicatorPeriodLabel(indicatorType, def as DefType); } function parseFieldPeriod(field?: string): number | null { diff --git a/frontend/src/utils/strategyEditorShared.tsx b/frontend/src/utils/strategyEditorShared.tsx index 24c6290..8c8eba9 100644 --- a/frontend/src/utils/strategyEditorShared.tsx +++ b/frontend/src/utils/strategyEditorShared.tsx @@ -393,8 +393,9 @@ export const getFieldOpts = ( } case 'ADX': { const { over, mid, under } = th({ over: 40, mid: 25, under: 20 }); + const adxP = cond ? getConditionValuePeriod({ ...cond, indicatorType: 'ADX' }, DEF) : DEF.adxPeriod; return condOpts([ - { value:'ADX_VALUE', label:`ADX 라인(${DEF.adxPeriod}일)` }, + { value:'ADX_VALUE', label:`ADX 라인(${adxP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -402,8 +403,9 @@ export const getFieldOpts = ( } case 'TRIX': { const { mid } = th({ mid:0 }); + const trixP = cond ? getConditionValuePeriod({ ...cond, indicatorType: 'TRIX' }, DEF) : DEF.trixPeriod; return condOpts([ - { value:'TRIX_VALUE', label:`TRIX 라인(${DEF.trixPeriod}일)` }, + { value:'TRIX_VALUE', label:`TRIX 라인(${trixP}일)` }, { value:'TRIX_SIGNAL', label:`TRIX 시그널(${DEF.trixSignal}일)` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, ]); @@ -449,8 +451,11 @@ export const getFieldOpts = ( case 'PSYCHOLOGICAL': case 'NEW_PSYCHOLOGICAL': { const { over, mid, under } = th({ over:75, mid:50, under:25 }); + const psyP = cond + ? getConditionValuePeriod({ ...cond, indicatorType: ind }, DEF) + : DEF.newPsy; return condOpts([ - { value:'PSY_VALUE', label:`심리도 라인(${DEF.newPsy}일)` }, + { value:'PSY_VALUE', label:`심리도 라인(${psyP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -458,8 +463,11 @@ export const getFieldOpts = ( } case 'INVEST_PSYCHOLOGICAL': { const { over, mid, under } = th({ over:75, mid:50, under:25 }); + const ipsyP = cond + ? getConditionValuePeriod({ ...cond, indicatorType: 'INVEST_PSYCHOLOGICAL' }, DEF) + : DEF.investPsy; return condOpts([ - { value:'INVEST_PSY_VALUE', label:`투자심리도 라인(${DEF.investPsy}일)` }, + { value:'INVEST_PSY_VALUE', label:`투자심리도 라인(${ipsyP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -467,8 +475,9 @@ export const getFieldOpts = ( } case 'WILLIAMS_R': { const { over, mid, under } = th({ over:-20, mid:-50, under:-80 }); + const wrP = cond ? getConditionValuePeriod({ ...cond, indicatorType: 'WILLIAMS_R' }, DEF) : DEF.williamsR; return condOpts([ - { value:'WILLIAMS_R_VALUE', label:`Williams %R 라인(${DEF.williamsR}일)` }, + { value:'WILLIAMS_R_VALUE', label:`Williams %R 라인(${wrP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -476,8 +485,9 @@ export const getFieldOpts = ( } case 'BWI': { const { over, mid, under } = th({ over:80, mid:50, under:20 }); + const bwiP = DEF.bwiPeriod; return condOpts([ - { value:'BWI_VALUE', label:`BWI 라인(${DEF.bwiPeriod}일)` }, + { value:'BWI_VALUE', label:`BWI 라인(${bwiP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -485,8 +495,9 @@ export const getFieldOpts = ( } case 'VR': { const { over, mid, under } = th({ over:200, mid:100, under:50 }); + const vrP = cond ? getConditionValuePeriod({ ...cond, indicatorType: 'VR' }, DEF) : DEF.vrPeriod; return condOpts([ - { value:'VR_VALUE', label:`VR 라인(${DEF.vrPeriod}일)` }, + { value:'VR_VALUE', label:`VR 라인(${vrP}일)` }, { value:THRESHOLD_HL_OVER, label:`${overTerm}(${over})` }, { value:THRESHOLD_HL_MID, label:`중앙선(${mid})` }, { value:THRESHOLD_HL_UNDER, label:`${underTerm}(${under})` }, @@ -1002,9 +1013,14 @@ export const CondEditor: React.FC = ({ cond, signalType, onChan disabled={isHoldType} onFieldChange={v => { const bases: Record = { - RSI: 'RSI_VALUE', CCI: 'CCI_VALUE', ADX: 'ADX_VALUE', - WILLIAMS_R: 'WILLIAMS_R_VALUE', TRIX: 'TRIX_VALUE', VR: 'VR_VALUE', - PSYCHOLOGICAL: 'PSY_VALUE', NEW_PSYCHOLOGICAL: 'PSY_VALUE', + RSI: 'RSI_VALUE', + CCI: 'CCI_VALUE', + ADX: 'ADX_VALUE', + WILLIAMS_R: 'WILLIAMS_R_VALUE', + TRIX: 'TRIX_VALUE', + VR: 'VR_VALUE', + PSYCHOLOGICAL: 'PSY_VALUE', + NEW_PSYCHOLOGICAL: 'PSY_VALUE', INVEST_PSYCHOLOGICAL: 'INVEST_PSY_VALUE', }; const base = bases[normalized.indicatorType]; diff --git a/frontend/src/utils/strategyFlowLayout.ts b/frontend/src/utils/strategyFlowLayout.ts index a8dcb16..12c47dd 100644 --- a/frontend/src/utils/strategyFlowLayout.ts +++ b/frontend/src/utils/strategyFlowLayout.ts @@ -1,6 +1,7 @@ import type { Connection, Edge, Node } from '@xyflow/react'; import type { LogicNode, ConditionDSL } from './strategyTypes'; import { nodeToText, type DefType } from './strategyEditorShared'; +import { formatIndicatorPeriodLabel } from './strategyIndicatorPeriods'; import { START_NODE_ID, defaultStartMeta, @@ -735,17 +736,7 @@ export function logicNodeToFlow( } export function getIndicatorPeriodLabel(indicator: string, def: DefType): string { - switch (indicator) { - case 'RSI': return String(def.rsiPeriod); - case 'MACD': return `${def.macdFast}/${def.macdSlow}`; - case 'CCI': return String(def.cciPeriod); - case 'STOCHASTIC': return `${def.stochK}/${def.stochD}`; - case 'ADX': return String(def.adxPeriod); - case 'MA': return String(def.maLines[0] ?? 5); - case 'EMA': return String(def.maLines[0] ?? 5); - case 'BOLLINGER': return String(def.bbPeriod); - default: return ''; - } + return formatIndicatorPeriodLabel(indicator, def); } /** 트리에서 노드 추출 (부모에서 제거 후 반환) */ diff --git a/frontend/src/utils/strategyIndicatorPeriods.ts b/frontend/src/utils/strategyIndicatorPeriods.ts new file mode 100644 index 0000000..d2f3d7d --- /dev/null +++ b/frontend/src/utils/strategyIndicatorPeriods.ts @@ -0,0 +1,108 @@ +/** + * 전략편집기 — 보조지표 설정(DEF)에서 기간 표시·기본값 조회. + * 팔레트 칩, PaletteItemModal, getDefaultIndicatorPeriod, getFieldOpts 라벨에 공통 사용. + */ +import type { DefType } from './strategyEditorShared'; + +/** 팔레트·요약용 기간 문자열 (복수 파라미터는 슬래시 구분) */ +export function formatIndicatorPeriodLabel(indicator: string, def: DefType): string { + switch (indicator) { + case 'RSI': + return String(def.rsiPeriod); + case 'CCI': + return String(def.cciPeriod); + case 'ADX': + return String(def.adxPeriod); + case 'WILLIAMS_R': + return String(def.williamsR); + case 'TRIX': + return `${def.trixPeriod}/${def.trixSignal}`; + case 'VR': + return String(def.vrPeriod); + case 'BWI': + return String(def.bwiPeriod); + case 'OBV': + return def.obvPeriod === def.obvSignal + ? String(def.obvPeriod) + : `${def.obvPeriod}/${def.obvSignal}`; + case 'MACD': + return `${def.macdFast}/${def.macdSlow}/${def.macdSignal}`; + case 'STOCHASTIC': + return `${def.stochK}/${def.stochD}`; + case 'DMI': + return String(def.dmiPeriod); + case 'VOLUME_OSC': + return `${def.volOscShort}/${def.volOscLong}`; + case 'DISPARITY': + return `${def.dispUltra}/${def.dispShort}/${def.dispMid}/${def.dispLong}`; + case 'PSYCHOLOGICAL': + case 'NEW_PSYCHOLOGICAL': + return String(def.newPsy); + case 'INVEST_PSYCHOLOGICAL': + return String(def.investPsy); + case 'MA': + return def.maLines.length ? def.maLines.join('/') : '5'; + case 'EMA': + return def.maLines.length ? def.maLines.slice(0, 4).join('/') : '5'; + case 'BOLLINGER': + return `${def.bbPeriod}(σ${def.bbStdDev})`; + case 'ICHIMOKU': + return `${def.ichTenkan}/${def.ichKijun}/${def.ichSenkouB}`; + case 'NEW_HIGH': + case 'NEW_LOW': + return '9'; + case 'VOLUME': + return ''; + default: + return ''; + } +} + +/** 팔레트·프리셋용 대표 기간(일) — 단일 숫자 입력 지표 */ +export function getIndicatorPrimaryPeriod(indicator: string, def: DefType): number { + switch (indicator) { + case 'RSI': + return def.rsiPeriod; + case 'CCI': + return def.cciPeriod; + case 'ADX': + return def.adxPeriod; + case 'WILLIAMS_R': + return def.williamsR; + case 'TRIX': + return def.trixPeriod; + case 'VR': + return def.vrPeriod; + case 'BWI': + return def.bwiPeriod; + case 'OBV': + return def.obvPeriod; + case 'MACD': + return def.macdFast; + case 'STOCHASTIC': + return def.stochK; + case 'DMI': + return def.dmiPeriod; + case 'VOLUME_OSC': + return def.volOscShort; + case 'DISPARITY': + return def.dispShort; + case 'PSYCHOLOGICAL': + case 'NEW_PSYCHOLOGICAL': + return def.newPsy; + case 'INVEST_PSYCHOLOGICAL': + return def.investPsy; + case 'MA': + case 'EMA': + return def.maLines[0] ?? 5; + case 'BOLLINGER': + return def.bbPeriod; + case 'ICHIMOKU': + return def.ichTenkan; + case 'NEW_HIGH': + case 'NEW_LOW': + return 9; + default: + return 14; + } +}