33변곡 템플릿 추가
This commit is contained in:
@@ -37,6 +37,11 @@ import {
|
||||
saveEditorMode,
|
||||
type StrategyEditorMode,
|
||||
} from '../utils/strategyEditorModeStorage';
|
||||
import {
|
||||
getStrategyTemplates,
|
||||
simpleTemplateToNode,
|
||||
type StrategyTemplateDef,
|
||||
} from '../utils/strategyPresets';
|
||||
import PaletteChip from './strategyEditor/PaletteChip';
|
||||
import { readStoredSize, storeSize, usePanelResize } from './strategyEditor/usePanelResize';
|
||||
import '../styles/strategyEditor.css';
|
||||
@@ -396,27 +401,24 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
else setCurrentRoot(mergeAtRoot(root, newNode, false));
|
||||
}, [signalTab, DEF, currentRoot, setCurrentRoot]);
|
||||
|
||||
const templates = useMemo(() => [
|
||||
{ ind: 'MA', cond: 'CROSS_UP', l: `MA${DEF.maLines[0]}`, r: `MA${DEF.maLines[1]}`, signal: 'buy' as const, label: 'MA 골든크로스 매수' },
|
||||
{ ind: 'MA', cond: 'CROSS_DOWN', l: `MA${DEF.maLines[0]}`, r: `MA${DEF.maLines[1]}`, signal: 'sell' as const, label: 'MA 데드크로스 매도' },
|
||||
{ ind: 'RSI', cond: 'LTE', l: 'RSI_VALUE', r: 'K_30', signal: 'buy' as const, label: 'RSI 과매도 매수' },
|
||||
{ ind: 'RSI', cond: 'GTE', l: 'RSI_VALUE', r: 'K_70', signal: 'sell' as const, label: 'RSI 과매수 매도' },
|
||||
{ ind: 'MACD', cond: 'CROSS_UP', l: 'MACD_LINE', r: 'SIGNAL_LINE', signal: 'buy' as const, label: 'MACD 상향 돌파' },
|
||||
{ ind: 'MACD', cond: 'CROSS_DOWN', l: 'MACD_LINE', r: 'SIGNAL_LINE', signal: 'sell' as const, label: 'MACD 하향 돌파' },
|
||||
], [DEF.maLines]);
|
||||
const templates = useMemo(() => getStrategyTemplates(DEF), [DEF]);
|
||||
|
||||
const handleTemplate = (tmpl: typeof templates[0]) => {
|
||||
const newNode: LogicNode = {
|
||||
id: genId(), type: 'CONDITION',
|
||||
condition: { indicatorType: tmpl.ind, conditionType: tmpl.cond, leftField: tmpl.l, rightField: tmpl.r, candleRange: 1 },
|
||||
};
|
||||
const handleTemplate = useCallback((tmpl: StrategyTemplateDef) => {
|
||||
if (tmpl.signal !== signalTab) switchSignalTab(tmpl.signal);
|
||||
const root = tmpl.signal === 'buy' ? buyCondition : sellCondition;
|
||||
const setRoot = tmpl.signal === 'buy' ? setBuyCondition : setSellCondition;
|
||||
const root = tmpl.signal === 'buy' ? buyCondition : sellCondition;
|
||||
|
||||
if (tmpl.kind === 'composite') {
|
||||
setRoot(tmpl.build());
|
||||
showSnack(`"${tmpl.label}" 템플릿이 적용됐습니다`);
|
||||
return;
|
||||
}
|
||||
|
||||
const newNode = simpleTemplateToNode(tmpl);
|
||||
if (!root) setRoot(newNode);
|
||||
else setRoot(mergeAtRoot(root, newNode, false));
|
||||
showSnack(`"${tmpl.label}" 추가됨`);
|
||||
};
|
||||
}, [signalTab, switchSignalTab, buyCondition, sellCondition, setBuyCondition, setSellCondition]);
|
||||
|
||||
const operators = [
|
||||
{ type: 'operator' as const, value: 'AND', label: 'AND', color: 'logic-and' },
|
||||
@@ -705,6 +707,9 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
||||
{templates.map((t, i) => (
|
||||
<button key={i} type="button" className="se-template-item" onClick={() => handleTemplate(t)}>
|
||||
<span className="se-template-label">{t.label}</span>
|
||||
{'description' in t && t.description && (
|
||||
<span className="se-template-desc">{t.description}</span>
|
||||
)}
|
||||
<span className={`se-template-signal se-template-signal--${t.signal}`}>{t.signal === 'buy' ? '매수' : '매도'}</span>
|
||||
</button>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user