전략편집기 복합지표 기능 반영
This commit is contained in:
@@ -4,11 +4,11 @@
|
||||
import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { ReactFlowProvider } from '@xyflow/react';
|
||||
import DraggableModalFrame from './DraggableModalFrame';
|
||||
import type { Theme, IndicatorConfig } from '../types/index';
|
||||
import type { Theme } from '../types/index';
|
||||
import { loadStrategies, saveStrategy, deleteStrategy, type StrategyDto as ApiStrategyDto } from '../utils/backendApi';
|
||||
import type { LogicNode } from '../utils/strategyTypes';
|
||||
import {
|
||||
buildDef,
|
||||
buildStrategyEditorDef,
|
||||
loadStratsLocal,
|
||||
saveStratsLocal,
|
||||
mergeAtRoot,
|
||||
@@ -19,6 +19,10 @@ import {
|
||||
type StrategyDto,
|
||||
} from '../utils/strategyEditorShared';
|
||||
import { findLogicNode, getIndicatorPeriodLabel } from '../utils/strategyFlowLayout';
|
||||
import {
|
||||
COMPOSITE_INDICATOR_ITEMS,
|
||||
compositePeriodLabel,
|
||||
} from '../utils/compositeIndicators';
|
||||
import {
|
||||
emptySignalFlowLayout,
|
||||
loadStrategyFlowLayout,
|
||||
@@ -66,7 +70,6 @@ const TERMINAL_DEFAULT = 140;
|
||||
|
||||
interface Props {
|
||||
theme: Theme;
|
||||
activeIndicators?: IndicatorConfig[];
|
||||
}
|
||||
|
||||
function readTabLayout(strategyKey: string, tab: 'buy' | 'sell'): SignalFlowLayoutSnapshot {
|
||||
@@ -80,8 +83,8 @@ function readTabLayout(strategyKey: string, tab: 'buy' | 'sell'): SignalFlowLayo
|
||||
};
|
||||
}
|
||||
|
||||
export default function StrategyEditorPage({ theme, activeIndicators = [] }: Props) {
|
||||
const DEF = useMemo(() => buildDef(activeIndicators), [activeIndicators]);
|
||||
export default function StrategyEditorPage({ theme }: Props) {
|
||||
const DEF = useMemo(() => buildStrategyEditorDef(), []);
|
||||
|
||||
const [strategies, setStrategies] = useState<StrategyDto[]>(() => loadStratsLocal());
|
||||
const [selectedId, setSelectedId] = useState<number | null>(null);
|
||||
@@ -511,8 +514,8 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
}
|
||||
}, []);
|
||||
|
||||
const applyPalette = useCallback((type: string, value: string, label: string) => {
|
||||
const newNode = makeNode(type, value, signalTab, DEF);
|
||||
const applyPalette = useCallback((type: string, value: string, _label: string, composite = false) => {
|
||||
const newNode = makeNode(type, value, signalTab, DEF, composite ? { composite: true } : undefined);
|
||||
const root = currentRoot;
|
||||
if (!root) setCurrentRoot(newNode);
|
||||
else if (type === 'operator') setCurrentRoot(mergeAtRoot(root, newNode, true));
|
||||
@@ -576,11 +579,11 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
const match = (label: string, desc?: string) =>
|
||||
!q || label.toLowerCase().includes(q) || (desc?.toLowerCase().includes(q) ?? false);
|
||||
|
||||
const paletteKey = (type: 'operator' | 'indicator', value: string) => `${type}:${value}`;
|
||||
const selectPalette = (type: 'operator' | 'indicator', value: string) => {
|
||||
const paletteKey = (type: 'operator' | 'indicator' | 'composite', value: string) => `${type}:${value}`;
|
||||
const selectPalette = (type: 'operator' | 'indicator' | 'composite', value: string) => {
|
||||
setSelectedPaletteKey(paletteKey(type, value));
|
||||
};
|
||||
const isPaletteSelected = (type: 'operator' | 'indicator', value: string) =>
|
||||
const isPaletteSelected = (type: 'operator' | 'indicator' | 'composite', value: string) =>
|
||||
selectedPaletteKey === paletteKey(type, value);
|
||||
|
||||
return (
|
||||
@@ -780,7 +783,7 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span className="se-sync-tip">차트 설정 동기화 · 기간 {getIndicatorPeriodLabel(selectedLogicNode.condition.indicatorType, DEF) || '—'}</span>
|
||||
<span className="se-sync-tip">전략 조건 전용 설정 · 차트 보조지표와 무관</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@@ -846,7 +849,7 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
</div>
|
||||
<div className="se-palette-section se-palette-section--band">
|
||||
<h3>밴드 · 추세</h3>
|
||||
<div className="se-palette-grid">
|
||||
<div className="se-palette-grid se-palette-grid--3">
|
||||
{maBandItems.filter(i => match(i.label, i.desc)).map(item => (
|
||||
<PaletteChip
|
||||
key={item.value}
|
||||
@@ -859,9 +862,9 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="se-palette-section se-palette-section--aux se-palette-section--scroll">
|
||||
<div className="se-palette-section se-palette-section--aux">
|
||||
<h3>보조지표</h3>
|
||||
<div className="se-palette-grid">
|
||||
<div className="se-palette-grid se-palette-grid--3">
|
||||
{indicatorItems.filter(i => match(i.label, i.desc)).map(item => (
|
||||
<PaletteChip
|
||||
key={item.value}
|
||||
@@ -878,6 +881,26 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="se-palette-section se-palette-section--composite se-palette-section--scroll">
|
||||
<h3>복합지표</h3>
|
||||
<div className="se-palette-grid se-palette-grid--3">
|
||||
{COMPOSITE_INDICATOR_ITEMS.filter(i => match(i.label, i.desc)).map(item => (
|
||||
<PaletteChip
|
||||
key={`composite-${item.value}`}
|
||||
type="indicator"
|
||||
value={item.value}
|
||||
label={item.label}
|
||||
desc={item.desc}
|
||||
color="composite"
|
||||
composite
|
||||
period={compositePeriodLabel(item.value, DEF)}
|
||||
selected={isPaletteSelected('composite', item.value)}
|
||||
onSelect={() => selectPalette('composite', item.value)}
|
||||
onAdd={() => applyPalette('indicator', item.value, item.label, true)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{rightTab === 'templates' && (
|
||||
|
||||
Reference in New Issue
Block a user