33변곡 템플릿 추가
This commit is contained in:
+14
-3
@@ -6310,8 +6310,13 @@ html.theme-blue {
|
|||||||
background: var(--bg3); border-radius: var(--radius);
|
background: var(--bg3); border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
.sp-pal-tmpl {
|
.sp-pal-tmpl {
|
||||||
display: flex; align-items: center; gap: 8px; padding: 10px 8px;
|
display: flex;
|
||||||
border-bottom: 1px solid var(--border); cursor: pointer;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px 8px;
|
||||||
|
padding: 10px 8px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
.sp-pal-tmpl:hover { background: var(--bg3); }
|
.sp-pal-tmpl:hover { background: var(--bg3); }
|
||||||
@@ -6320,7 +6325,13 @@ html.theme-blue {
|
|||||||
}
|
}
|
||||||
.sp-tmpl-badge--buy { background: rgba(72,199,116,.2); color: #48c774; }
|
.sp-tmpl-badge--buy { background: rgba(72,199,116,.2); color: #48c774; }
|
||||||
.sp-tmpl-badge--sell { background: rgba(255,107,107,.2); color: #ff6b6b; }
|
.sp-tmpl-badge--sell { background: rgba(255,107,107,.2); color: #ff6b6b; }
|
||||||
.sp-tmpl-name { flex: 1; color: var(--text); }
|
.sp-tmpl-name { flex: 1 1 auto; color: var(--text); }
|
||||||
|
.sp-tmpl-desc {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: var(--text2);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── 모달 ─────────────────────────────────────────────────────────────────── */
|
/* ── 모달 ─────────────────────────────────────────────────────────────────── */
|
||||||
.sp-modal-overlay {
|
.sp-modal-overlay {
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ import {
|
|||||||
saveEditorMode,
|
saveEditorMode,
|
||||||
type StrategyEditorMode,
|
type StrategyEditorMode,
|
||||||
} from '../utils/strategyEditorModeStorage';
|
} from '../utils/strategyEditorModeStorage';
|
||||||
|
import {
|
||||||
|
getStrategyTemplates,
|
||||||
|
simpleTemplateToNode,
|
||||||
|
type StrategyTemplateDef,
|
||||||
|
} from '../utils/strategyPresets';
|
||||||
import PaletteChip from './strategyEditor/PaletteChip';
|
import PaletteChip from './strategyEditor/PaletteChip';
|
||||||
import { readStoredSize, storeSize, usePanelResize } from './strategyEditor/usePanelResize';
|
import { readStoredSize, storeSize, usePanelResize } from './strategyEditor/usePanelResize';
|
||||||
import '../styles/strategyEditor.css';
|
import '../styles/strategyEditor.css';
|
||||||
@@ -396,27 +401,24 @@ export default function StrategyEditorPage({ theme, activeIndicators = [] }: Pro
|
|||||||
else setCurrentRoot(mergeAtRoot(root, newNode, false));
|
else setCurrentRoot(mergeAtRoot(root, newNode, false));
|
||||||
}, [signalTab, DEF, currentRoot, setCurrentRoot]);
|
}, [signalTab, DEF, currentRoot, setCurrentRoot]);
|
||||||
|
|
||||||
const templates = useMemo(() => [
|
const templates = useMemo(() => getStrategyTemplates(DEF), [DEF]);
|
||||||
{ 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 handleTemplate = (tmpl: typeof templates[0]) => {
|
const handleTemplate = useCallback((tmpl: StrategyTemplateDef) => {
|
||||||
const newNode: LogicNode = {
|
|
||||||
id: genId(), type: 'CONDITION',
|
|
||||||
condition: { indicatorType: tmpl.ind, conditionType: tmpl.cond, leftField: tmpl.l, rightField: tmpl.r, candleRange: 1 },
|
|
||||||
};
|
|
||||||
if (tmpl.signal !== signalTab) switchSignalTab(tmpl.signal);
|
if (tmpl.signal !== signalTab) switchSignalTab(tmpl.signal);
|
||||||
const root = tmpl.signal === 'buy' ? buyCondition : sellCondition;
|
|
||||||
const setRoot = tmpl.signal === 'buy' ? setBuyCondition : setSellCondition;
|
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);
|
if (!root) setRoot(newNode);
|
||||||
else setRoot(mergeAtRoot(root, newNode, false));
|
else setRoot(mergeAtRoot(root, newNode, false));
|
||||||
showSnack(`"${tmpl.label}" 추가됨`);
|
showSnack(`"${tmpl.label}" 추가됨`);
|
||||||
};
|
}, [signalTab, switchSignalTab, buyCondition, sellCondition, setBuyCondition, setSellCondition]);
|
||||||
|
|
||||||
const operators = [
|
const operators = [
|
||||||
{ type: 'operator' as const, value: 'AND', label: 'AND', color: 'logic-and' },
|
{ 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) => (
|
{templates.map((t, i) => (
|
||||||
<button key={i} type="button" className="se-template-item" onClick={() => handleTemplate(t)}>
|
<button key={i} type="button" className="se-template-item" onClick={() => handleTemplate(t)}>
|
||||||
<span className="se-template-label">{t.label}</span>
|
<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>
|
<span className={`se-template-signal se-template-signal--${t.signal}`}>{t.signal === 'buy' ? '매수' : '매도'}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ import {
|
|||||||
type StrategyDto as ApiStrategyDto,
|
type StrategyDto as ApiStrategyDto,
|
||||||
} from '../utils/backendApi';
|
} from '../utils/backendApi';
|
||||||
import { getIndicatorDef, getHLineLabel } from '../utils/indicatorRegistry';
|
import { getIndicatorDef, getHLineLabel } from '../utils/indicatorRegistry';
|
||||||
|
import {
|
||||||
|
getStrategyTemplates,
|
||||||
|
simpleTemplateToNode,
|
||||||
|
type StrategyTemplateDef,
|
||||||
|
} from '../utils/strategyPresets';
|
||||||
|
|
||||||
// ─── 타입 ─────────────────────────────────────────────────────────────────────
|
// ─── 타입 ─────────────────────────────────────────────────────────────────────
|
||||||
type LogicNodeType = 'AND' | 'OR' | 'NOT' | 'CONDITION';
|
type LogicNodeType = 'AND' | 'OR' | 'NOT' | 'CONDITION';
|
||||||
@@ -1184,14 +1189,19 @@ export const StrategyPage: React.FC<Props> = ({ activeIndicators = [] }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ── 템플릿 선택 ─────────────────────────────────────────────────────────────
|
// ── 템플릿 선택 ─────────────────────────────────────────────────────────────
|
||||||
const handleTemplateSelect = (tmpl: { ind: string; cond: string; l: string; r: string; signal: 'buy'|'sell'; label: string }) => {
|
const handleTemplateSelect = (tmpl: StrategyTemplateDef) => {
|
||||||
const newNode: LogicNode = {
|
|
||||||
id: genId(), type: 'CONDITION',
|
|
||||||
condition: { indicatorType: tmpl.ind, conditionType: tmpl.cond, leftField: tmpl.l, rightField: tmpl.r, candleRange: 1 },
|
|
||||||
};
|
|
||||||
if (tmpl.signal !== currentTab) setCurrentTab(tmpl.signal);
|
if (tmpl.signal !== currentTab) setCurrentTab(tmpl.signal);
|
||||||
const root = tmpl.signal === 'buy' ? buyCondition : sellCondition;
|
|
||||||
const setRoot = tmpl.signal === 'buy' ? setBuyCondition : setSellCondition;
|
const setRoot = tmpl.signal === 'buy' ? setBuyCondition : setSellCondition;
|
||||||
|
const root = tmpl.signal === 'buy' ? buyCondition : sellCondition;
|
||||||
|
|
||||||
|
if (tmpl.kind === 'composite') {
|
||||||
|
setRoot(tmpl.build());
|
||||||
|
setRightTab('preview');
|
||||||
|
showSnack(`"${tmpl.label}" 템플릿이 적용됐습니다`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newNode = simpleTemplateToNode(tmpl);
|
||||||
if (!root) { setRoot(newNode); }
|
if (!root) { setRoot(newNode); }
|
||||||
else { setRoot(mergeAtRoot(root, newNode, false)); }
|
else { setRoot(mergeAtRoot(root, newNode, false)); }
|
||||||
setRightTab('preview');
|
setRightTab('preview');
|
||||||
@@ -1217,20 +1227,7 @@ export const StrategyPage: React.FC<Props> = ({ activeIndicators = [] }) => {
|
|||||||
'TRIX:TRIX', 'VOLUME_OSC:VolumeOSC', 'VR:VR', 'DISPARITY:이격도', 'VOLUME:거래량',
|
'TRIX:TRIX', 'VOLUME_OSC:VolumeOSC', 'VR:VR', 'DISPARITY:이격도', 'VOLUME:거래량',
|
||||||
].map(s => { const [v,l] = s.split(':'); return { type:'indicator' as const, value:v, label:l, color:'ind' }; });
|
].map(s => { const [v,l] = s.split(':'); return { type:'indicator' as const, value:v, label:l, color:'ind' }; });
|
||||||
|
|
||||||
const templates = [
|
const templates = useMemo(() => getStrategyTemplates(DEF), [DEF]);
|
||||||
{ 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:'OVERSOLD_30', signal:'buy' as const, label:'RSI 과매도 매수' },
|
|
||||||
{ ind:'RSI', cond:'GTE', l:'RSI_VALUE', r:'OVERBOUGHT_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 시그널 하향 돌파' },
|
|
||||||
{ ind:'BOLLINGER', cond:'CROSS_UP', l:'CLOSE_PRICE', r:'LOWER_BAND', signal:'buy' as const, label:'볼린저 하단 돌파 매수' },
|
|
||||||
{ ind:'BOLLINGER', cond:'CROSS_UP', l:'CLOSE_PRICE', r:'UPPER_BAND', signal:'sell' as const, label:'볼린저 상단 돌파 매도' },
|
|
||||||
{ ind:'CCI', cond:'CROSS_UP', l:'CCI_VALUE', r:'OVERSOLD_NEG100', signal:'buy' as const, label:'CCI 과매도 반등' },
|
|
||||||
{ ind:'PSYCHOLOGICAL', cond:'CROSS_UP', l:'PSY_VALUE', r:'OVERBOUGHT_75', signal:'buy' as const, label:'심리도 상향 돌파' },
|
|
||||||
{ ind:'STOCHASTIC', cond:'CROSS_UP', l:'STOCH_K', r:'STOCH_D', signal:'buy' as const, label:'스토캐스틱 골든크로스' },
|
|
||||||
{ ind:'ADX', cond:'CROSS_UP', l:'ADX_VALUE', r:'ADX_25', signal:'buy' as const, label:'ADX 중앙선 상향 돌파' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const currentRoot = getCurrent();
|
const currentRoot = getCurrent();
|
||||||
|
|
||||||
@@ -1548,6 +1545,9 @@ export const StrategyPage: React.FC<Props> = ({ activeIndicators = [] }) => {
|
|||||||
{t.signal === 'buy' ? '매수' : '매도'}
|
{t.signal === 'buy' ? '매수' : '매도'}
|
||||||
</span>
|
</span>
|
||||||
<span className="sp-tmpl-name">{t.label}</span>
|
<span className="sp-tmpl-name">{t.label}</span>
|
||||||
|
{'description' in t && t.description && (
|
||||||
|
<span className="sp-tmpl-desc">{t.description}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -996,20 +996,33 @@
|
|||||||
|
|
||||||
.se-template-list { padding: 10px; overflow-y: auto; flex: 1; }
|
.se-template-list { padding: 10px; overflow-y: auto; flex: 1; }
|
||||||
.se-template-item {
|
.se-template-item {
|
||||||
display: flex; align-items: center; justify-content: space-between;
|
display: flex;
|
||||||
width: 100%; padding: 10px 12px; margin-bottom: 6px;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px 8px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
margin-bottom: 6px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--se-border);
|
border: 1px solid var(--se-border);
|
||||||
background: var(--se-palette-card-bg);
|
background: var(--se-palette-card-bg);
|
||||||
color: var(--se-text); cursor: pointer; text-align: left;
|
color: var(--se-text);
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
transition: border-color 0.12s, box-shadow 0.12s;
|
transition: border-color 0.12s, box-shadow 0.12s;
|
||||||
}
|
}
|
||||||
.se-template-item:hover {
|
.se-template-item:hover {
|
||||||
border-color: var(--se-palette-hover-border);
|
border-color: var(--se-palette-hover-border);
|
||||||
box-shadow: 0 0 12px color-mix(in srgb, var(--se-gold) 10%, transparent);
|
box-shadow: 0 0 12px color-mix(in srgb, var(--se-gold) 10%, transparent);
|
||||||
}
|
}
|
||||||
.se-template-label { font-size: 0.76rem; }
|
.se-template-label { font-size: 0.76rem; flex: 1 1 auto; }
|
||||||
.se-template-signal { font-size: 0.62rem; font-weight: 700; padding: 2px 6px; border-radius: 4px; }
|
.se-template-desc {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
font-size: 0.66rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: var(--se-text-muted);
|
||||||
|
}
|
||||||
|
.se-template-signal { font-size: 0.62rem; font-weight: 700; padding: 2px 6px; border-radius: 4px; margin-left: auto; }
|
||||||
.se-template-signal--buy { color: var(--se-buy); background: color-mix(in srgb, var(--se-buy) 12%, transparent); }
|
.se-template-signal--buy { color: var(--se-buy); background: color-mix(in srgb, var(--se-buy) 12%, transparent); }
|
||||||
.se-template-signal--sell { color: var(--se-sell); background: color-mix(in srgb, var(--se-sell) 12%, transparent); }
|
.se-template-signal--sell { color: var(--se-sell); background: color-mix(in srgb, var(--se-sell) 12%, transparent); }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import type { LogicNode } from './strategyTypes';
|
||||||
|
import { genId, type DefType } from './strategyEditorShared';
|
||||||
|
|
||||||
|
export type SimpleStrategyTemplate = {
|
||||||
|
kind: 'simple';
|
||||||
|
signal: 'buy' | 'sell';
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
ind: string;
|
||||||
|
cond: string;
|
||||||
|
l: string;
|
||||||
|
r: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CompositeStrategyTemplate = {
|
||||||
|
kind: 'composite';
|
||||||
|
signal: 'buy' | 'sell';
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
build: () => LogicNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StrategyTemplateDef = SimpleStrategyTemplate | CompositeStrategyTemplate;
|
||||||
|
|
||||||
|
function condition(
|
||||||
|
indicatorType: string,
|
||||||
|
conditionType: string,
|
||||||
|
leftField: string,
|
||||||
|
rightField: string,
|
||||||
|
candleRange = 1,
|
||||||
|
): LogicNode {
|
||||||
|
return {
|
||||||
|
id: genId(),
|
||||||
|
type: 'CONDITION',
|
||||||
|
condition: { indicatorType, conditionType, leftField, rightField, candleRange },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function andTree(...children: LogicNode[]): LogicNode {
|
||||||
|
return { id: genId(), type: 'AND', children };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 33변곡 매수 — 바닥권 시간변곡(약 33거래일 주기) 전환 구간 */
|
||||||
|
export function build33InflectionBuyTree(): LogicNode {
|
||||||
|
return andTree(
|
||||||
|
condition('RSI', 'CROSS_UP', 'RSI_VALUE', 'OVERSOLD_30'),
|
||||||
|
condition('MA', 'CROSS_UP', 'MA5', 'MA20'),
|
||||||
|
condition('MA', 'CROSS_UP', 'CLOSE_PRICE', 'MA20'),
|
||||||
|
condition('MACD', 'CROSS_UP', 'MACD_LINE', 'SIGNAL_LINE'),
|
||||||
|
condition('DISPARITY', 'CROSS_UP', 'DISPARITY20', 'OVERSOLD_95'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 33변곡 매도 — 상승 5파 끝(33·36·38일 변곡) 구간 이탈 */
|
||||||
|
export function build33InflectionSellTree(): LogicNode {
|
||||||
|
return andTree(
|
||||||
|
condition('RSI', 'GTE', 'RSI_VALUE', 'OVERBOUGHT_70'),
|
||||||
|
condition('MA', 'CROSS_DOWN', 'MA5', 'MA20'),
|
||||||
|
condition('MA', 'CROSS_DOWN', 'CLOSE_PRICE', 'MA20'),
|
||||||
|
condition('MACD', 'CROSS_DOWN', 'MACD_LINE', 'SIGNAL_LINE'),
|
||||||
|
condition('DISPARITY', 'GTE', 'DISPARITY20', 'OVERBOUGHT_105'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStrategyTemplates(def: DefType): StrategyTemplateDef[] {
|
||||||
|
const maShort = `MA${def.maLines[0]}`;
|
||||||
|
const maMid = `MA${def.maLines[1]}`;
|
||||||
|
|
||||||
|
return [
|
||||||
|
{ kind: 'simple', ind: 'MA', cond: 'CROSS_UP', l: maShort, r: maMid, signal: 'buy', label: 'MA 골든크로스 매수' },
|
||||||
|
{ kind: 'simple', ind: 'MA', cond: 'CROSS_DOWN', l: maShort, r: maMid, signal: 'sell', label: 'MA 데드크로스 매도' },
|
||||||
|
{ kind: 'simple', ind: 'RSI', cond: 'LTE', l: 'RSI_VALUE', r: 'OVERSOLD_30', signal: 'buy', label: 'RSI 과매도 매수' },
|
||||||
|
{ kind: 'simple', ind: 'RSI', cond: 'GTE', l: 'RSI_VALUE', r: 'OVERBOUGHT_70', signal: 'sell', label: 'RSI 과매수 매도' },
|
||||||
|
{ kind: 'simple', ind: 'MACD', cond: 'CROSS_UP', l: 'MACD_LINE', r: 'SIGNAL_LINE', signal: 'buy', label: 'MACD 상향 돌파' },
|
||||||
|
{ kind: 'simple', ind: 'MACD', cond: 'CROSS_DOWN', l: 'MACD_LINE', r: 'SIGNAL_LINE', signal: 'sell', label: 'MACD 하향 돌파' },
|
||||||
|
{ kind: 'simple', ind: 'BOLLINGER', cond: 'CROSS_UP', l: 'CLOSE_PRICE', r: 'LOWER_BAND', signal: 'buy', label: '볼린저 하단 돌파 매수' },
|
||||||
|
{ kind: 'simple', ind: 'BOLLINGER', cond: 'CROSS_UP', l: 'CLOSE_PRICE', r: 'UPPER_BAND', signal: 'sell', label: '볼린저 상단 돌파 매도' },
|
||||||
|
{ kind: 'simple', ind: 'CCI', cond: 'CROSS_UP', l: 'CCI_VALUE', r: 'OVERSOLD_NEG100', signal: 'buy', label: 'CCI 과매도 반등' },
|
||||||
|
{ kind: 'simple', ind: 'PSYCHOLOGICAL', cond: 'CROSS_UP', l: 'PSY_VALUE', r: 'OVERBOUGHT_75', signal: 'buy', label: '심리도 상향 돌파' },
|
||||||
|
{ kind: 'simple', ind: 'STOCHASTIC', cond: 'CROSS_UP', l: 'STOCH_K', r: 'STOCH_D', signal: 'buy', label: '스토캐스틱 골든크로스' },
|
||||||
|
{ kind: 'simple', ind: 'ADX', cond: 'CROSS_UP', l: 'ADX_VALUE', r: 'ADX_25', signal: 'buy', label: 'ADX 중앙선 상향 돌파' },
|
||||||
|
{
|
||||||
|
kind: 'composite',
|
||||||
|
signal: 'buy',
|
||||||
|
label: '33변곡 매수',
|
||||||
|
description: '바닥권 시간변곡 — RSI 과매도 탈출 + MA 골든크로스 + 20일선·이격도 돌파',
|
||||||
|
build: build33InflectionBuyTree,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: 'composite',
|
||||||
|
signal: 'sell',
|
||||||
|
label: '33변곡 매도',
|
||||||
|
description: '상승 5파 끝 변곡 — RSI 과매수 + MA 데드크로스 + 20일선·이격도 이탈',
|
||||||
|
build: build33InflectionSellTree,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function simpleTemplateToNode(tmpl: SimpleStrategyTemplate): LogicNode {
|
||||||
|
return condition(tmpl.ind, tmpl.cond, tmpl.l, tmpl.r);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user