일목균형표 전략 수정
This commit is contained in:
@@ -143,12 +143,12 @@ export const STABLE_STRATEGY_PALETTE_ITEMS: StableStrategyPaletteMeta[] = [
|
||||
},
|
||||
{
|
||||
value: ICHIMOKU_SANYAKU_BUY, label: '일목 삼역호전 매수', strategyId: 'ICHIMOKU_SANYAKU', mode: 'buy',
|
||||
desc: '4단계 Full-AND — 후행 구름 돌파 1회 트리거 + 삼역호전 필터',
|
||||
desc: '구름 상향 돌파 트리거 + 삼역호전 필터 (보통: 후행·양운, 강함: MA·거래량)',
|
||||
periodHint: '9 / 26 / 52',
|
||||
},
|
||||
{
|
||||
value: ICHIMOKU_SANYAKU_SELL, label: '일목 삼역호전 매도', strategyId: 'ICHIMOKU_SANYAKU', mode: 'sell',
|
||||
desc: '전환·기준·구름 하향 돌파 이벤트 OR (유지조건 제외)',
|
||||
desc: '전환·기준·구름 이탈 OR — 돌파 우선, 이탈 유지 fallback',
|
||||
periodHint: '9 / 26 / 52',
|
||||
},
|
||||
{
|
||||
@@ -371,44 +371,69 @@ function buildIchimokuTrendSell(def: IndicatorPeriodDef, level: StableStrategyFi
|
||||
return wrapOrRoot(branches, pairMeta('ICHIMOKU_TREND', 'sell', level));
|
||||
}
|
||||
|
||||
/** 일목 삼역호전(三役好轉) — 4단계 Full-AND 매수 (1회성 트리거) */
|
||||
function buildIchimokuSanyakuBuy(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode {
|
||||
const nodes: LogicNode[] = [
|
||||
// [트리거] 후행스팬 26봉전 구름 상향 돌파 — 삼역호전 최종 잠금 순간 1회
|
||||
/** 일목 삼역호전 — 매수 트리거 (1회성 돌파) */
|
||||
function ichimokuSanyakuBuyTrigger(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode {
|
||||
if (level === 'strict') {
|
||||
return wrapOr([
|
||||
makeCondition('ICHIMOKU', 'CLOUD_BREAK_UP', 'NONE', 'NONE', def),
|
||||
makeCondition('ICHIMOKU', 'CHIKOU_CROSS_ABOVE_PRIOR_CLOUD', 'NONE', 'NONE', def),
|
||||
// [1단계] 주가·단기 추세 (진입 봉 동시 충족)
|
||||
]);
|
||||
}
|
||||
return makeCondition('ICHIMOKU', 'CLOUD_BREAK_UP', 'NONE', 'NONE', def);
|
||||
}
|
||||
|
||||
/** 일목 삼역호전 — 공통 추세·구름 필터 */
|
||||
function ichimokuSanyakuCoreFilters(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode[] {
|
||||
const nodes: LogicNode[] = [
|
||||
makeCondition('ICHIMOKU', 'GT', 'CONVERSION_LINE', 'BASE_LINE', def),
|
||||
makeCondition('ICHIMOKU', 'ABOVE_CLOUD', 'CLOSE_PRICE', 'NONE', def),
|
||||
];
|
||||
if (level === 'relaxed') {
|
||||
nodes.unshift(makeCondition('ICHIMOKU', 'GT', 'CLOSE_PRICE', 'CONVERSION_LINE', def));
|
||||
return nodes;
|
||||
}
|
||||
nodes.unshift(
|
||||
makeCondition('ICHIMOKU', 'GT', 'CLOSE_PRICE', 'CONVERSION_LINE', def),
|
||||
makeCondition('ICHIMOKU', 'GT', 'CLOSE_PRICE', 'BASE_LINE', def),
|
||||
makeCondition('ICHIMOKU', 'GT', 'CONVERSION_LINE', 'BASE_LINE', def),
|
||||
// [2단계] 구름대·양운
|
||||
makeCondition('ICHIMOKU', 'ABOVE_CLOUD', 'CLOSE_PRICE', 'NONE', def),
|
||||
);
|
||||
nodes.push(
|
||||
makeCondition('ICHIMOKU', 'SPAN1_GT_SPAN2', 'LEADING_SPAN1', 'LEADING_SPAN2', def),
|
||||
// [3단계] 후행스팬 호전 유지
|
||||
makeCondition('ICHIMOKU', 'LAGGING_GT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE', def),
|
||||
makeCondition('ICHIMOKU', 'LAGGING_ABOVE_PRIOR_CLOUD', 'NONE', 'NONE', def),
|
||||
];
|
||||
if (level !== 'relaxed') {
|
||||
nodes.push(makeCondition('MA', 'GT', 'CLOSE_PRICE', maField(60), def));
|
||||
);
|
||||
if (level === 'strict') {
|
||||
nodes.push(makeVolumeMaRatioFilter(def, 1.5, 5));
|
||||
} else {
|
||||
nodes.push(makeCondition('VOLUME', 'GT', 'VOLUME_VALUE', 'VOLUME_MA', def, { params: { length: 5 } }));
|
||||
nodes.push(makeCondition('ICHIMOKU', 'LAGGING_ABOVE_PRIOR_CLOUD', 'NONE', 'NONE', def));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/** 일목 삼역호전(三役好轉) — 구름 돌파 트리거 + 단계별 필터 */
|
||||
function buildIchimokuSanyakuBuy(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode {
|
||||
const nodes: LogicNode[] = [
|
||||
ichimokuSanyakuBuyTrigger(def, level),
|
||||
...ichimokuSanyakuCoreFilters(def, level),
|
||||
];
|
||||
if (level === 'strict') {
|
||||
nodes.push(makeCondition('MA', 'GT', 'CLOSE_PRICE', maField(60), def));
|
||||
nodes.push(makeVolumeMaRatioFilter(def, 1.5, 5));
|
||||
}
|
||||
return wrapAnd(nodes, pairMeta('ICHIMOKU_SANYAKU', 'buy', level));
|
||||
}
|
||||
|
||||
/** 일목 삼역호전 — 분할 청산 OR (돌파 이벤트만, 유지조건 제외) */
|
||||
/** 일목 삼역호전 — 청산 (돌파 이벤트 + 이탈 fallback, 포지션 교대 시 1회) */
|
||||
function buildIchimokuSanyakuSell(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode {
|
||||
void level;
|
||||
return wrapOrRoot([
|
||||
const branches: LogicNode[] = [
|
||||
makeCondition('ICHIMOKU', 'CROSS_DOWN', 'CLOSE_PRICE', 'CONVERSION_LINE', def),
|
||||
wrapOr([
|
||||
makeCondition('ICHIMOKU', 'CROSS_DOWN', 'CLOSE_PRICE', 'BASE_LINE', def),
|
||||
makeCondition('ICHIMOKU', 'CROSS_DOWN', 'CONVERSION_LINE', 'BASE_LINE', def),
|
||||
]),
|
||||
makeCondition('ICHIMOKU', 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE', def),
|
||||
], pairMeta('ICHIMOKU_SANYAKU', 'sell', level));
|
||||
];
|
||||
if (level !== 'strict') {
|
||||
branches.push(makeCondition('ICHIMOKU', 'LT', 'CLOSE_PRICE', 'CONVERSION_LINE', def));
|
||||
if (level === 'relaxed') {
|
||||
branches.push(makeCondition('ICHIMOKU', 'BELOW_CLOUD', 'CLOSE_PRICE', 'NONE', def));
|
||||
}
|
||||
}
|
||||
return wrapOrRoot(branches, pairMeta('ICHIMOKU_SANYAKU', 'sell', level));
|
||||
}
|
||||
|
||||
function buildMacdMomentumBuy(def: IndicatorPeriodDef, level: StableStrategyFilterLevel): LogicNode {
|
||||
@@ -529,9 +554,9 @@ export function stableStrategyFilterSummary(
|
||||
}
|
||||
if (strategyId === 'ICHIMOKU_TREND' && level === 'relaxed') parts.push('구름필터 없음');
|
||||
if (strategyId === 'ICHIMOKU_SANYAKU') {
|
||||
if (level === 'strict') parts.push('MA60·거래량150%');
|
||||
else if (level === 'balanced') parts.push('MA60·거래량>MA5');
|
||||
else parts.push('MA60·거래량 없음');
|
||||
if (level === 'strict') parts.push('후행구름·MA60·거래량150%');
|
||||
else if (level === 'balanced') parts.push('구름돌파·양운·후행');
|
||||
else parts.push('구름돌파·핵심만');
|
||||
}
|
||||
if (strategyId === 'MACD_MOMENTUM' && level === 'relaxed') parts.push('EMA60·ADX 없음');
|
||||
if (strategyId === 'MA_TREND' && level === 'relaxed') parts.push('ADX 없음');
|
||||
@@ -564,14 +589,16 @@ export function inferStableStrategyFilterLevel(node: LogicNode): StableStrategyF
|
||||
return 'relaxed';
|
||||
}
|
||||
if (id === 'ICHIMOKU_SANYAKU') {
|
||||
const hasTrigger = conds.some(c => c.conditionType === 'CHIKOU_CROSS_ABOVE_PRIOR_CLOUD');
|
||||
const hasMa = conds.some(c => c.indicatorType === 'MA' && c.rightField === maField(60));
|
||||
const hasCloudBreak = conds.some(c => c.conditionType === 'CLOUD_BREAK_UP');
|
||||
const hasChikouCross = conds.some(c => c.conditionType === 'CHIKOU_CROSS_ABOVE_PRIOR_CLOUD');
|
||||
const hasLaggingCloud = conds.some(c => c.conditionType === 'LAGGING_ABOVE_PRIOR_CLOUD');
|
||||
const hasVolRatio = conds.some(c => c.conditionType === 'VOLUME_GT_MA_RATIO');
|
||||
const hasVol = conds.some(c => c.indicatorType === 'VOLUME');
|
||||
if (!hasTrigger) return 'balanced';
|
||||
if (!hasMa && !hasVol) return 'relaxed';
|
||||
if (hasVolRatio) return 'strict';
|
||||
if (hasMa && hasVol) return 'balanced';
|
||||
const hasMa = conds.some(c => c.indicatorType === 'MA' && c.rightField === maField(60));
|
||||
const hasLagging = conds.some(c => c.conditionType === 'LAGGING_GT_PRICE');
|
||||
if (hasVolRatio || hasLaggingCloud) return 'strict';
|
||||
if (hasMa) return 'strict';
|
||||
if (hasLagging && (hasCloudBreak || hasChikouCross)) return 'balanced';
|
||||
if (hasCloudBreak || hasChikouCross) return 'relaxed';
|
||||
}
|
||||
if (id === 'MACD_MOMENTUM' && !conds.some(c => c.rightField === emaField(60))) return 'relaxed';
|
||||
if (id === 'MA_TREND' && !conds.some(c => c.indicatorType === 'ADX')) return 'relaxed';
|
||||
|
||||
Reference in New Issue
Block a user