일목균형표 복합지표 추가

This commit is contained in:
Macbook
2026-06-25 01:00:23 +09:00
parent 80cf8daf55
commit 4ab8053b65
15 changed files with 3588 additions and 6 deletions
@@ -91,6 +91,10 @@ import {
type StableStrategyFilterLevel,
type StableStrategyId,
} from '../utils/stableStrategyPairs';
import {
buildIchimokuSituationTree,
isIchimokuSituationPaletteValue,
} from '../utils/ichimokuSituationStrategy';
import {
buildStochOverboughtPairTree,
findStochPairRootInForest,
@@ -1367,7 +1371,8 @@ export default function StrategyEditorPage({
const inflection33 = isInflection33PaletteValue(item.value);
const ichimokuBb = isIchimokuBbPaletteValue(item.value);
const stableStrategy = isStableStrategyPaletteValue(item.value);
const composite = item.kind === 'composite' && !stochPair && !priceExtremeBreakout && !inflection33 && !ichimokuBb && !stableStrategy;
const ichimokuSituation = isIchimokuSituationPaletteValue(item.value);
const composite = item.kind === 'composite' && !stochPair && !priceExtremeBreakout && !inflection33 && !ichimokuBb && !stableStrategy && !ichimokuSituation;
const newNode = stochPair
? buildStochOverboughtPairTree(item.secondaryIndicator ?? 'CCI', DEF)
: priceExtremeBreakout
@@ -1378,6 +1383,8 @@ export default function StrategyEditorPage({
? buildIchimokuBbTree(item.value, DEF)
: stableStrategy
? buildStableStrategyTree(item.value, DEF)
: ichimokuSituation
? buildIchimokuSituationTree(item.value, DEF)
: makeNode('indicator', item.value, signalTab, DEF, composite ? { composite: true } : undefined);
if (!newNode) return;
handleEditorStateChange(prev => {
@@ -31,6 +31,11 @@ import {
inferStableStrategyFilterLevel,
type StableStrategyId,
} from '../../utils/stableStrategyPairs';
import {
isIchimokuSituationPairRoot,
ichimokuSituationDisplayName,
type IchimokuSituationId,
} from '../../utils/ichimokuSituationStrategy';
import StableStrategyPairNodeSettings from './StableStrategyPairNodeSettings';
import {
isStochOverboughtPairRoot,
@@ -241,12 +246,13 @@ export const LogicGateNode = memo(function LogicGateNode({ id, data, selected }:
const isInflection33Pair = isInflection33PairRoot(node);
const isIchimokuBbPair = isIchimokuBbPairRoot(node);
const isStableStrategyPair = isStableStrategyPairRoot(node);
const isIchimokuSituationPair = isIchimokuSituationPairRoot(node);
const { onDragEnter, onDragOver, onDragLeave, onDrop } = usePaletteDropHandlers(id, d);
const [settingsOpen, setSettingsOpen] = useState(false);
return (
<div
className={`se-flow-node se-flow-node--logic${isStochPair ? ' se-flow-node--stoch-pair' : ''}${isPriceExtremePair ? ' se-flow-node--price-extreme-pair' : ''}${isInflection33Pair ? ' se-flow-node--inflection33-pair' : ''}${isIchimokuBbPair ? ' se-flow-node--ichimoku-bb-pair' : ''}${isStableStrategyPair ? ' se-flow-node--stable-strategy-pair' : ''}${isSell ? ' se-flow-node--sell-mode' : ''}${d.isOrphan ? ' se-flow-node--orphan' : ''} ${selected ? 'se-flow-node--selected' : ''} ${d.dragOver ? ' se-flow-node--drop' : ''}${settingsOpen ? ' se-flow-node--settings-open' : ''}`}
className={`se-flow-node se-flow-node--logic${isStochPair ? ' se-flow-node--stoch-pair' : ''}${isPriceExtremePair ? ' se-flow-node--price-extreme-pair' : ''}${isInflection33Pair ? ' se-flow-node--inflection33-pair' : ''}${isIchimokuBbPair ? ' se-flow-node--ichimoku-bb-pair' : ''}${isStableStrategyPair ? ' se-flow-node--stable-strategy-pair' : ''}${isIchimokuSituationPair ? ' se-flow-node--ichimoku-situation-pair' : ''}${isSell ? ' se-flow-node--sell-mode' : ''}${d.isOrphan ? ' se-flow-node--orphan' : ''} ${selected ? 'se-flow-node--selected' : ''} ${d.dragOver ? ' se-flow-node--drop' : ''}${settingsOpen ? ' se-flow-node--settings-open' : ''}`}
style={{ '--se-gate-color': color } as React.CSSProperties}
onDragEnter={onDragEnter}
onDragOver={onDragOver}
@@ -297,6 +303,16 @@ export const LogicGateNode = memo(function LogicGateNode({ id, data, selected }:
</button>
</div>
)}
{isIchimokuSituationPair && node.ichimokuSituationPair && (
<div className="se-flow-gate-stoch-pair">
<span className="se-flow-gate-stoch-pair-title">
{ichimokuSituationDisplayName(
node.ichimokuSituationPair.situationId as IchimokuSituationId,
node.ichimokuSituationPair.mode,
)}
</span>
</div>
)}
{isInflection33Pair && node.inflection33Pair && (
<div className="se-flow-gate-stoch-pair">
<span className="se-flow-gate-stoch-pair-title">
@@ -8,6 +8,7 @@ import { isPriceExtremeBreakoutPairPaletteValue } from '../../utils/priceExtreme
import { isInflection33PaletteValue } from '../../utils/inflection33Strategy';
import { isIchimokuBbPaletteValue } from '../../utils/ichimokuBbStrategy';
import { isStableStrategyPaletteValue, STABLE_STRATEGY_PALETTE_ITEMS } from '../../utils/stableStrategyPairs';
import { isIchimokuSituationPaletteValue } from '../../utils/ichimokuSituationStrategy';
import { getDefaultIndicatorPeriod } from '../../utils/conditionPeriods';
import { getStrategyIndicatorDisplayName } from '../../utils/strategyPaletteStorage';
import { getIndicatorPeriodLabel } from '../../utils/strategyFlowLayout';
@@ -39,6 +40,9 @@ function periodLabel(item: PaletteItem, def: DefType): string {
const meta = STABLE_STRATEGY_PALETTE_ITEMS.find(i => i.value === item.value);
return meta?.periodHint ?? '';
}
if (item.kind === 'composite' && isIchimokuSituationPaletteValue(item.value)) {
return '9 / 26 / 52';
}
if (item.kind === 'composite') {
const d = getCompositeDefaultPeriods(item.value, def);
return `${d.short} / ${d.long}`;
@@ -194,12 +198,14 @@ export default function IndicatorPaletteTab({
&& !isPriceExtremeBreakoutPairPaletteValue(item.value)
&& !isInflection33PaletteValue(item.value)
&& !isIchimokuBbPaletteValue(item.value)
&& !isStableStrategyPaletteValue(item.value)}
&& !isStableStrategyPaletteValue(item.value)
&& !isIchimokuSituationPaletteValue(item.value)}
stochPair={isStochOverboughtPairPaletteValue(item.value)}
priceExtremeBreakout={isPriceExtremeBreakoutPairPaletteValue(item.value)}
inflection33={isInflection33PaletteValue(item.value)}
ichimokuBb={isIchimokuBbPaletteValue(item.value)}
stableStrategy={isStableStrategyPaletteValue(item.value)}
ichimokuSituation={isIchimokuSituationPaletteValue(item.value)}
secondaryIndicator={item.secondaryIndicator}
period={periodLabel(item, def)}
periodValue={item.period}
@@ -29,6 +29,7 @@ interface Props {
inflection33?: boolean;
ichimokuBb?: boolean;
stableStrategy?: boolean;
ichimokuSituation?: boolean;
secondaryIndicator?: string;
selected?: boolean;
onSelect?: () => void;
@@ -37,7 +38,7 @@ interface Props {
export default function PaletteChip({
type, value, label, desc, color, period, periodValue, shortPeriod, longPeriod,
composite = false, stochPair = false, priceExtremeBreakout = false, inflection33 = false, ichimokuBb = false, stableStrategy = false, secondaryIndicator, selected = false, onSelect, onAdd,
composite = false, stochPair = false, priceExtremeBreakout = false, inflection33 = false, ichimokuBb = false, stableStrategy = false, ichimokuSituation = false, secondaryIndicator, selected = false, onSelect, onAdd,
}: Props) {
const buildPayload = (): PaletteDragPayload => ({
type, value, label,
@@ -47,6 +48,7 @@ export default function PaletteChip({
inflection33: inflection33 || undefined,
ichimokuBb: ichimokuBb || undefined,
stableStrategy: stableStrategy || undefined,
ichimokuSituation: ichimokuSituation || undefined,
secondaryIndicator,
period: periodValue,
shortPeriod,
@@ -794,6 +794,9 @@ function StrategyEditorCanvasInner({
: n.stableStrategyPair?.mode === 'sell'
? (gateType === 'OR' ? n.stableStrategyPair : undefined)
: undefined,
ichimokuSituationPair: n.ichimokuSituationPair
? n.ichimokuSituationPair
: undefined,
}
: n
);
@@ -51,6 +51,10 @@ import {
buildStableStrategyTree,
isStableStrategyPaletteValue,
} from '../utils/stableStrategyPairs';
import {
buildIchimokuSituationTree,
isIchimokuSituationPaletteValue,
} from '../utils/ichimokuSituationStrategy';
import {
buildSidewaysFilterNode,
loadSidewaysPaletteItems,
@@ -453,7 +457,8 @@ export function useStrategyEvaluationEditor({
const inflection33 = isInflection33PaletteValue(item.value);
const ichimokuBb = isIchimokuBbPaletteValue(item.value);
const stableStrategy = isStableStrategyPaletteValue(item.value);
const composite = item.kind === 'composite' && !stochPair && !priceExtremeBreakout && !inflection33 && !ichimokuBb && !stableStrategy;
const ichimokuSituation = isIchimokuSituationPaletteValue(item.value);
const composite = item.kind === 'composite' && !stochPair && !priceExtremeBreakout && !inflection33 && !ichimokuBb && !stableStrategy && !ichimokuSituation;
const newNode = stochPair
? buildStochOverboughtPairTree(item.secondaryIndicator ?? 'CCI', def)
: priceExtremeBreakout
@@ -464,6 +469,8 @@ export function useStrategyEvaluationEditor({
? buildIchimokuBbTree(item.value, def)
: stableStrategy
? buildStableStrategyTree(item.value, def)
: ichimokuSituation
? buildIchimokuSituationTree(item.value, def)
: makeNode('indicator', item.value, signalTab, def, composite ? { composite: true } : undefined);
if (!newNode) return;
handleEditorStateChange(prev => {
@@ -0,0 +1,358 @@
/**
* 일목균형표 상황별·엘리어트 파동 복합 전략
* @see 일목균형표 전략.md
*/
import type { ConditionDSL, LogicNode } from './strategyTypes';
import { initConditionPeriodsInherit, type IndicatorPeriodDef } from './conditionPeriods';
import { genId } from './strategyNodeIds';
export type IchimokuSituationCategory = 'basic' | 'alignment' | 'elliott';
export type IchimokuSituationId =
| 'GOLDEN_CROSS'
| 'BASE_BREAKUP'
| 'CLOUD_BREAKUP'
| 'LAGGING_BULL'
| 'CLOUD_SUPPORT'
| 'DEAD_CROSS'
| 'BASE_BREAKDOWN'
| 'CLOUD_BREAKDOWN'
| 'LAGGING_BEAR'
| 'CLOUD_RESISTANCE'
| 'SANYAKU_REVERSE'
| 'HOJEON'
| 'GYEOKJEON'
| 'EW1'
| 'EW2'
| 'EW3'
| 'EW4'
| 'EW5'
| 'EW_A'
| 'EW_B'
| 'EW_C';
const SITUATION_PREFIX = 'ICHIMOKU_SIT_';
export function ichimokuSituationPaletteValue(id: IchimokuSituationId, mode: 'buy' | 'sell'): string {
return `${SITUATION_PREFIX}${id}_${mode.toUpperCase()}`;
}
export interface IchimokuSituationPaletteMeta {
value: string;
label: string;
desc: string;
situationId: IchimokuSituationId;
mode: 'buy' | 'sell';
category: IchimokuSituationCategory;
}
export const ICHIMOKU_SITUATION_PALETTE_ITEMS: IchimokuSituationPaletteMeta[] = [
// §2.1 기본 매수
{ value: ichimokuSituationPaletteValue('GOLDEN_CROSS', 'buy'), label: '일목 골든크로스 매수', situationId: 'GOLDEN_CROSS', mode: 'buy', category: 'basic',
desc: 'L1 — 전환선이 기준선 상향 돌파 (호전 1차)' },
{ value: ichimokuSituationPaletteValue('BASE_BREAKUP', 'buy'), label: '일목 기준선 돌파 매수', situationId: 'BASE_BREAKUP', mode: 'buy', category: 'basic',
desc: 'L2 — 종가가 기준선 상향 돌파 (중기 추세 전환)' },
{ value: ichimokuSituationPaletteValue('CLOUD_BREAKUP', 'buy'), label: '일목 구름 돌파 매수', situationId: 'CLOUD_BREAKUP', mode: 'buy', category: 'basic',
desc: 'L3 — 구름대 상향 돌파 (매물·심리 저항 돌파)' },
{ value: ichimokuSituationPaletteValue('LAGGING_BULL', 'buy'), label: '일목 후행 호전 매수', situationId: 'LAGGING_BULL', mode: 'buy', category: 'basic',
desc: 'L4 — 후행스팬 > 26봉 전 종가 (추세 확정)' },
{ value: ichimokuSituationPaletteValue('CLOUD_SUPPORT', 'buy'), label: '일목 구름 지지 매수', situationId: 'CLOUD_SUPPORT', mode: 'buy', category: 'basic',
desc: 'L5 — 종가 > 선행1·기준선 (양운·기준선 지지 반등)' },
// §1.3 호전
{ value: ichimokuSituationPaletteValue('HOJEON', 'buy'), label: '일목 호전(정배열) 매수', situationId: 'HOJEON', mode: 'buy', category: 'alignment',
desc: '괘·구름 위·양운·후행 — 완벽한 상승 추세 4조건' },
// §2.2 기본 매도
{ value: ichimokuSituationPaletteValue('DEAD_CROSS', 'sell'), label: '일목 데드크로스 매도', situationId: 'DEAD_CROSS', mode: 'sell', category: 'basic',
desc: 'S1 — 전환선이 기준선 하향 이탈' },
{ value: ichimokuSituationPaletteValue('BASE_BREAKDOWN', 'sell'), label: '일목 기준선 이탈 매도', situationId: 'BASE_BREAKDOWN', mode: 'sell', category: 'basic',
desc: 'S2 — 종가가 기준선 하향 이탈' },
{ value: ichimokuSituationPaletteValue('CLOUD_BREAKDOWN', 'sell'), label: '일목 구름 이탈 매도', situationId: 'CLOUD_BREAKDOWN', mode: 'sell', category: 'basic',
desc: 'S3 — 구름대 하향 이탈 (지지 붕괴)' },
{ value: ichimokuSituationPaletteValue('LAGGING_BEAR', 'sell'), label: '일목 후행 역전 매도', situationId: 'LAGGING_BEAR', mode: 'sell', category: 'basic',
desc: 'S4 — 후행스팬 < 26봉 전 종가' },
{ value: ichimokuSituationPaletteValue('CLOUD_RESISTANCE', 'sell'), label: '일목 구름 저항 매도', situationId: 'CLOUD_RESISTANCE', mode: 'sell', category: 'basic',
desc: 'S5 — 종가 < 선행1·기준선 (음운·기준선 저항)' },
{ value: ichimokuSituationPaletteValue('SANYAKU_REVERSE', 'sell'), label: '일목 삼역역전 매도', situationId: 'SANYAKU_REVERSE', mode: 'sell', category: 'alignment',
desc: 'S6 — 데드 + 구름 아래 + 후행 역전 (강력 청산)' },
{ value: ichimokuSituationPaletteValue('GYEOKJEON', 'sell'), label: '일목 역전(역배열) 매도', situationId: 'GYEOKJEON', mode: 'sell', category: 'alignment',
desc: '괘·구름 아래·음운·후행 — 완벽한 하락 추세 4조건' },
// §4 엘리어트 파동
{ value: ichimokuSituationPaletteValue('EW1', 'buy'), label: '일목 1파 매수', situationId: 'EW1', mode: 'buy', category: 'elliott',
desc: '추세 전환 시동 — 기준선 돌파 + 전환>기준 (소액·분할)' },
{ value: ichimokuSituationPaletteValue('EW2', 'buy'), label: '일목 2파 매수', situationId: 'EW2', mode: 'buy', category: 'elliott',
desc: '눌림목 — 기준선·구름하단(선행2) 지지 + 전환>기준' },
{ value: ichimokuSituationPaletteValue('EW2', 'sell'), label: '일목 2파 손절', situationId: 'EW2', mode: 'sell', category: 'elliott',
desc: '2파 지지 이탈 — 기준선 이탈 OR 구름 하향 돌파' },
{ value: ichimokuSituationPaletteValue('EW3', 'buy'), label: '일목 3파 매수', situationId: 'EW3', mode: 'buy', category: 'elliott',
desc: '본격 상승 — 삼역호전(괘·구름·후행·양운) 불타기' },
{ value: ichimokuSituationPaletteValue('EW4', 'buy'), label: '일목 4파 재매수', situationId: 'EW4', mode: 'buy', category: 'elliott',
desc: '횡보 조정 — 구름 상단(선행1)·기준선 지지 반등' },
{ value: ichimokuSituationPaletteValue('EW4', 'sell'), label: '일목 4파 익절', situationId: 'EW4', mode: 'sell', category: 'elliott',
desc: '4파 조정 — 기준선 이탈 OR 구름 하향 이탈' },
{ value: ichimokuSituationPaletteValue('EW5', 'sell'), label: '일목 5파 분할매도', situationId: 'EW5', mode: 'sell', category: 'elliott',
desc: '과열 — 전환선·데드·구름 이탈 (분할 청산)' },
{ value: ichimokuSituationPaletteValue('EW_A', 'sell'), label: '일목 A파 매도', situationId: 'EW_A', mode: 'sell', category: 'elliott',
desc: '하락 개시 — 기준선·데드크로스' },
{ value: ichimokuSituationPaletteValue('EW_B', 'sell'), label: '일목 B파 매도', situationId: 'EW_B', mode: 'sell', category: 'elliott',
desc: '기술적 반등 실패 — 기준선·음운 저항 (전량 대피)' },
{ value: ichimokuSituationPaletteValue('EW_C', 'sell'), label: '일목 C파 매도', situationId: 'EW_C', mode: 'sell', category: 'elliott',
desc: '본 하락 — 삼역역전 + 구름 이탈 (물타기 금지)' },
];
const VALUE_TO_META: Record<string, { situationId: IchimokuSituationId; mode: 'buy' | 'sell' }> =
Object.fromEntries(ICHIMOKU_SITUATION_PALETTE_ITEMS.map(i => [i.value, { situationId: i.situationId, mode: i.mode }]));
export function isIchimokuSituationPaletteValue(value: string): boolean {
return value.startsWith(SITUATION_PREFIX) && value in VALUE_TO_META;
}
export function isIchimokuSituationPairRoot(node: LogicNode): boolean {
return !!node.ichimokuSituationPair?.situationId
&& (node.type === 'AND' || node.type === 'OR');
}
function makeIch(
def: IndicatorPeriodDef,
conditionType: string,
leftField: string,
rightField: string,
extra?: Partial<ConditionDSL>,
): LogicNode {
const base: ConditionDSL = {
indicatorType: 'ICHIMOKU',
conditionType,
leftField,
rightField,
candleRange: 1,
valuePeriodOverride: false,
thresholdOverride: false,
rightPeriodOverride: false,
...extra,
};
return {
id: genId(),
type: 'CONDITION',
condition: initConditionPeriodsInherit('ICHIMOKU', base, def),
};
}
function pairMeta(
situationId: IchimokuSituationId,
mode: 'buy' | 'sell',
): LogicNode['ichimokuSituationPair'] {
return { situationId, mode };
}
function wrapAnd(children: LogicNode[], meta: LogicNode['ichimokuSituationPair']): LogicNode {
return { id: genId(), type: 'AND', ichimokuSituationPair: meta, children };
}
function wrapOr(children: LogicNode[], meta: LogicNode['ichimokuSituationPair']): LogicNode {
return { id: genId(), type: 'OR', ichimokuSituationPair: meta, children };
}
type Builder = (def: IndicatorPeriodDef) => LogicNode;
const BUILDERS: Record<IchimokuSituationId, Partial<Record<'buy' | 'sell', Builder>>> = {
GOLDEN_CROSS: {
buy: def => wrapAnd([
makeIch(def, 'CROSS_UP', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('GOLDEN_CROSS', 'buy')),
},
BASE_BREAKUP: {
buy: def => wrapAnd([
makeIch(def, 'CROSS_UP', 'CLOSE_PRICE', 'BASE_LINE'),
], pairMeta('BASE_BREAKUP', 'buy')),
},
CLOUD_BREAKUP: {
buy: def => wrapAnd([
makeIch(def, 'CLOUD_BREAK_UP', 'CLOSE_PRICE', 'NONE'),
], pairMeta('CLOUD_BREAKUP', 'buy')),
},
LAGGING_BULL: {
buy: def => wrapAnd([
makeIch(def, 'LAGGING_GT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('LAGGING_BULL', 'buy')),
},
CLOUD_SUPPORT: {
buy: def => wrapAnd([
makeIch(def, 'GT', 'CLOSE_PRICE', 'LEADING_SPAN1'),
makeIch(def, 'GT', 'CLOSE_PRICE', 'BASE_LINE'),
], pairMeta('CLOUD_SUPPORT', 'buy')),
},
HOJEON: {
buy: def => wrapAnd([
makeIch(def, 'GT', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'ABOVE_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'SPAN1_GT_SPAN2', 'LEADING_SPAN1', 'LEADING_SPAN2'),
makeIch(def, 'LAGGING_GT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('HOJEON', 'buy')),
},
DEAD_CROSS: {
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('DEAD_CROSS', 'sell')),
},
BASE_BREAKDOWN: {
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CLOSE_PRICE', 'BASE_LINE'),
], pairMeta('BASE_BREAKDOWN', 'sell')),
},
CLOUD_BREAKDOWN: {
sell: def => wrapOr([
makeIch(def, 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE'),
], pairMeta('CLOUD_BREAKDOWN', 'sell')),
},
LAGGING_BEAR: {
sell: def => wrapOr([
makeIch(def, 'LAGGING_LT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('LAGGING_BEAR', 'sell')),
},
CLOUD_RESISTANCE: {
sell: def => wrapAnd([
makeIch(def, 'LT', 'CLOSE_PRICE', 'LEADING_SPAN1'),
makeIch(def, 'LT', 'CLOSE_PRICE', 'BASE_LINE'),
], pairMeta('CLOUD_RESISTANCE', 'sell')),
},
SANYAKU_REVERSE: {
sell: def => wrapAnd([
makeIch(def, 'LT', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'BELOW_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'LAGGING_LT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('SANYAKU_REVERSE', 'sell')),
},
GYEOKJEON: {
sell: def => wrapAnd([
makeIch(def, 'LT', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'BELOW_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'SPAN1_LT_SPAN2', 'LEADING_SPAN1', 'LEADING_SPAN2'),
makeIch(def, 'LAGGING_LT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('GYEOKJEON', 'sell')),
},
EW1: {
buy: def => wrapAnd([
makeIch(def, 'CROSS_UP', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'GT', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('EW1', 'buy')),
},
EW2: {
buy: def => wrapAnd([
makeIch(def, 'GT', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'GT', 'CLOSE_PRICE', 'LEADING_SPAN2'),
makeIch(def, 'GT', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('EW2', 'buy')),
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE'),
], pairMeta('EW2', 'sell')),
},
EW3: {
buy: def => wrapAnd([
makeIch(def, 'GT', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'ABOVE_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'LAGGING_GT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
makeIch(def, 'SPAN1_GT_SPAN2', 'LEADING_SPAN1', 'LEADING_SPAN2'),
], pairMeta('EW3', 'buy')),
},
EW4: {
buy: def => wrapAnd([
makeIch(def, 'GT', 'CLOSE_PRICE', 'LEADING_SPAN1'),
makeIch(def, 'GT', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('EW4', 'buy')),
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE'),
], pairMeta('EW4', 'sell')),
},
EW5: {
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CLOSE_PRICE', 'CONVERSION_LINE'),
makeIch(def, 'CROSS_DOWN', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE'),
], pairMeta('EW5', 'sell')),
},
EW_A: {
sell: def => wrapOr([
makeIch(def, 'CROSS_DOWN', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'CROSS_DOWN', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('EW_A', 'sell')),
},
EW_B: {
sell: def => wrapAnd([
makeIch(def, 'LT', 'CLOSE_PRICE', 'BASE_LINE'),
makeIch(def, 'BELOW_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'LT', 'CONVERSION_LINE', 'BASE_LINE'),
], pairMeta('EW_B', 'sell')),
},
EW_C: {
sell: def => wrapAnd([
makeIch(def, 'CLOUD_BREAK_DOWN', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'BELOW_CLOUD', 'CLOSE_PRICE', 'NONE'),
makeIch(def, 'LT', 'CONVERSION_LINE', 'BASE_LINE'),
makeIch(def, 'LAGGING_LT_PRICE', 'LAGGING_SPAN', 'CLOSE_PRICE'),
], pairMeta('EW_C', 'sell')),
},
};
export function ichimokuSituationMetaFromValue(value: string): { situationId: IchimokuSituationId; mode: 'buy' | 'sell' } | null {
return VALUE_TO_META[value] ?? null;
}
export function buildIchimokuSituationTree(value: string, def: IndicatorPeriodDef): LogicNode | null {
const meta = ichimokuSituationMetaFromValue(value);
if (!meta) return null;
const builder = BUILDERS[meta.situationId][meta.mode];
return builder ? builder(def) : null;
}
export function ichimokuSituationDisplayName(situationId: IchimokuSituationId, mode: 'buy' | 'sell'): string {
const item = ICHIMOKU_SITUATION_PALETTE_ITEMS.find(
i => i.situationId === situationId && i.mode === mode,
);
return item?.label ?? `${situationId} ${mode === 'buy' ? '매수' : '매도'}`;
}
export function ichimokuSituationPaletteDesc(value: string): string {
return ICHIMOKU_SITUATION_PALETTE_ITEMS.find(i => i.value === value)?.desc ?? '';
}
const COND_LABEL: Record<string, string> = {
CROSS_UP: '상향돌파', CROSS_DOWN: '하향돌파',
GT: '>', LT: '<',
ABOVE_CLOUD: '구름 위', BELOW_CLOUD: '구름 아래',
CLOUD_BREAK_UP: '구름 상향돌파', CLOUD_BREAK_DOWN: '구름 하향이탈',
SPAN1_GT_SPAN2: '양운', SPAN1_LT_SPAN2: '음운',
LAGGING_GT_PRICE: '후행>26봉전 종가', LAGGING_LT_PRICE: '후행<26봉전 종가',
};
function fmtNode(n: LogicNode): string {
if (n.type === 'CONDITION' && n.condition) {
const c = n.condition;
const op = COND_LABEL[c.conditionType] ?? c.conditionType;
return `${c.leftField} ${op} ${c.rightField ?? ''}`.trim();
}
if (n.type === 'AND' || n.type === 'OR') {
return (n.children ?? []).map(fmtNode).join(` ${n.type} `);
}
return '';
}
export function ichimokuSituationSummaryText(
situationId: IchimokuSituationId,
mode: 'buy' | 'sell',
def: IndicatorPeriodDef = {
rsiPeriod: 14, cciPeriod: 20, adxPeriod: 14, williamsR: 14,
trixPeriod: 12, vrPeriod: 26, psyPeriod: 12, newPsy: 12, investPsy: 12,
},
): string {
const tree = BUILDERS[situationId][mode]?.(def);
return tree ? fmtNode(tree) : '';
}
export function ichimokuSituationCategoryLabel(cat: IchimokuSituationCategory): string {
switch (cat) {
case 'basic': return '기본';
case 'alignment': return '호전·역전';
case 'elliott': return '엘리어트';
}
}
@@ -63,6 +63,14 @@ import {
stableStrategyFilterLevelLabel,
type StableStrategyId,
} from '../utils/stableStrategyPairs';
import {
buildIchimokuSituationTree,
isIchimokuSituationPairRoot,
isIchimokuSituationPaletteValue,
ichimokuSituationDisplayName,
ichimokuSituationSummaryText,
type IchimokuSituationId,
} from '../utils/ichimokuSituationStrategy';
import {
buildPriceExtremeBreakoutTree,
isPriceExtremeBreakoutPairPaletteValue,
@@ -1015,6 +1023,10 @@ export const nodeToText = (node: LogicNode, DEF: DefType = DEF_DEFAULTS): string
const cfg = inferIchimokuBbConfig(node);
return `${ichimokuBbDisplayName(cfg.mode, cfg.chikouDisplacement)}\n${ichimokuBbSummaryText(cfg)}`;
}
if (isIchimokuSituationPairRoot(node) && node.ichimokuSituationPair) {
const { situationId, mode } = node.ichimokuSituationPair;
return `${ichimokuSituationDisplayName(situationId as IchimokuSituationId, mode)}\n${ichimokuSituationSummaryText(situationId as IchimokuSituationId, mode, DEF)}`;
}
if (isPriceExtremeBreakoutPairRoot(node) && node.priceExtremePair) {
const { mode, shortPeriod, longPeriod } = node.priceExtremePair;
const level = inferPriceExtremeFilterLevel(node);
@@ -1038,6 +1050,10 @@ export const nodeToText = (node: LogicNode, DEF: DefType = DEF_DEFAULTS): string
const cfg = inferIchimokuBbConfig(node);
return `${ichimokuBbDisplayName(cfg.mode, cfg.chikouDisplacement)}\n${ichimokuBbSummaryText(cfg)}`;
}
if (isIchimokuSituationPairRoot(node) && node.ichimokuSituationPair) {
const { situationId, mode } = node.ichimokuSituationPair;
return `${ichimokuSituationDisplayName(situationId as IchimokuSituationId, mode)}\n${ichimokuSituationSummaryText(situationId as IchimokuSituationId, mode, DEF)}`;
}
if (isStochOverboughtPairRoot(node)) {
const sec = node.stochPair!.secondaryIndicatorType;
return `${stochPairDisplayName(sec)}\n${stochPairSummaryText(sec)}`;
@@ -1565,6 +1581,8 @@ export type MakeNodeOptions = {
ichimokuBb?: boolean;
/** 추천 안정형 전략 복합 */
stableStrategy?: boolean;
/** 일목균형표 상황별·엘리어트 복합 */
ichimokuSituation?: boolean;
};
/** 팔레트 드래그 payload → makeNode 옵션 */
@@ -1575,6 +1593,7 @@ export function makeNodeOptionsFromPalette(data: {
inflection33?: boolean;
ichimokuBb?: boolean;
stableStrategy?: boolean;
ichimokuSituation?: boolean;
secondaryIndicator?: string;
period?: number;
shortPeriod?: number;
@@ -1601,6 +1620,9 @@ export function makeNodeOptionsFromPalette(data: {
if (data.stableStrategy || (data.value && isStableStrategyPaletteValue(data.value))) {
return { stableStrategy: true };
}
if (data.ichimokuSituation || (data.value && isIchimokuSituationPaletteValue(data.value))) {
return { ichimokuSituation: true };
}
if (data.composite) {
return {
composite: true,
@@ -1638,6 +1660,10 @@ export const makeNode = (
const tree = buildStableStrategyTree(value, DEF);
if (tree) return tree;
}
if (options?.ichimokuSituation || isIchimokuSituationPaletteValue(value)) {
const tree = buildIchimokuSituationTree(value, DEF);
if (tree) return tree;
}
if (options?.composite) {
let condition = makeCompositeCondition(value, signalType, DEF);
condition = {
@@ -12,6 +12,7 @@ import {
isNewLow920SellPaletteValue,
} from './priceExtremeBreakoutPair';
import { STABLE_STRATEGY_PALETTE_ITEMS } from './stableStrategyPairs';
import { ichimokuSituationMetaFromValue } from './ichimokuSituationStrategy';
export type TemplatePaletteSource =
| 'user'
@@ -50,6 +51,8 @@ export function inferCompositePaletteSignal(item: PaletteItem): 'buy' | 'sell' {
}
const stable = STABLE_STRATEGY_PALETTE_ITEMS.find(s => s.value === item.value);
if (stable) return stable.mode;
const ichimokuSit = ichimokuSituationMetaFromValue(item.value);
if (ichimokuSit) return ichimokuSit.mode;
if (item.value.endsWith('_BUY')) return 'buy';
if (item.value.endsWith('_SELL')) return 'sell';
return 'buy';
@@ -26,6 +26,10 @@ import {
buildStableStrategyTree,
isStableStrategyPaletteValue,
} from './stableStrategyPairs';
import {
buildIchimokuSituationTree,
isIchimokuSituationPaletteValue,
} from './ichimokuSituationStrategy';
import {
buildSidewaysFilterNode,
loadSidewaysPaletteItems,
@@ -71,18 +75,21 @@ function buildLogicNodeFromPaletteItem(
const inflection33 = isInflection33PaletteValue(item.value);
const ichimokuBb = isIchimokuBbPaletteValue(item.value);
const stableStrategy = isStableStrategyPaletteValue(item.value);
const ichimokuSituation = isIchimokuSituationPaletteValue(item.value);
const composite = item.kind === 'composite'
&& !stochPair
&& !priceExtremeBreakout
&& !inflection33
&& !ichimokuBb
&& !stableStrategy;
&& !stableStrategy
&& !ichimokuSituation;
if (stochPair) return buildStochOverboughtPairTree(item.secondaryIndicator ?? 'CCI', def);
if (priceExtremeBreakout) return buildPriceExtremeBreakoutTree(item.value, def);
if (inflection33) return buildInflection33Tree(item.value, def);
if (ichimokuBb) return buildIchimokuBbTree(item.value, def);
if (stableStrategy) return buildStableStrategyTree(item.value, def);
if (ichimokuSituation) return buildIchimokuSituationTree(item.value, def);
return makeNode('indicator', item.value, signalTab, def, composite ? { composite: true } : undefined);
}
@@ -18,6 +18,9 @@ import {
import {
STABLE_STRATEGY_PALETTE_ITEMS,
} from './stableStrategyPairs';
import {
ICHIMOKU_SITUATION_PALETTE_ITEMS,
} from './ichimokuSituationStrategy';
import {
NEW_HIGH_9_20_BUY_VALUE,
NEW_LOW_9_20_SELL_VALUE,
@@ -125,6 +128,12 @@ export const DEFAULT_COMPOSITE_ITEMS: Omit<PaletteItem, 'id' | 'kind'>[] = [
builtIn: true,
period: 26,
},
...ICHIMOKU_SITUATION_PALETTE_ITEMS.map(i => ({
value: i.value,
label: i.label,
desc: `[${i.category === 'basic' ? '기본' : i.category === 'alignment' ? '호전·역전' : '엘리어트'}] ${i.desc}`,
builtIn: true,
})),
...STABLE_STRATEGY_PALETTE_ITEMS.map(i => ({
value: i.value,
label: i.label,
+5
View File
@@ -80,6 +80,11 @@ export interface LogicNode {
bbBand: 'UPPER_BAND' | 'MIDDLE_BAND' | 'LOWER_BAND';
conditionType: 'CROSS_UP' | 'CROSS_DOWN' | 'GT' | 'LT';
};
/** 일목균형표 상황별·엘리어트 파동 복합 — @see 일목균형표 전략.md */
ichimokuSituationPair?: {
situationId: string;
mode: 'buy' | 'sell';
};
}
export interface StrategyDSLDto {
+2680
View File
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,jsx}'],
darkMode: 'class',
theme: {
extend: {
colors: {
trust: {
DEFAULT: '#0047AB',
50: '#E8F0FA',
100: '#C5D9F2',
600: '#0047AB',
700: '#003A8C',
800: '#002D6D',
},
safety: {
DEFAULT: '#28A745',
50: '#E8F5EC',
600: '#28A745',
700: '#1E7E34',
},
emergency: {
red: '#DC3545',
orange: '#FD7E14',
},
},
fontFamily: {
sans: ['Pretendard', '-apple-system', 'BlinkMacSystemFont', 'system-ui', 'sans-serif'],
},
boxShadow: {
card: '0 2px 12px rgba(0, 71, 171, 0.08)',
'card-dark': '0 2px 16px rgba(0, 0, 0, 0.35)',
},
animation: {
'fade-in': 'fadeIn 0.35s ease-out',
'slide-up': 'slideUp 0.35s ease-out',
wave: 'wave 1.2s ease-in-out infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { opacity: '0', transform: 'translateY(12px)' },
'100%': { opacity: '1', transform: 'translateY(0)' },
},
wave: {
'0%, 100%': { transform: 'scaleY(0.4)' },
'50%': { transform: 'scaleY(1)' },
},
},
},
},
plugins: [],
};
+397
View File
@@ -0,0 +1,397 @@
# 일목균형표(Ichimoku Kinko Hyo) 전략 — 구성 요소·매매 로직·엘리어트 파동 연계
goldenChart **투자전략 DSL**의 `ICHIMOKU` 지표 조건과 연동하여, 일목균형표의 시간·가격 균형 관계와 엘리어트 파동(Elliott Wave) 단계별 전략 조건을 정리한 문서입니다.
> **관련 문서:** [매매전략조건.md](./매매전략조건.md) · [전략편집기 로직 및 동작흐름.md](./전략편집기%20로직%20및%20동작흐름.md)
> **내장 템플릿:** `ICHIMOKU_TREND` · `ICHIMOKU_SANYAKU`(삼역호전) · `ICHIMOKU_PERFECT`(5원소)
---
## 1. 일목균형표란
일목균형표는 **과거·현재·미래**의 가격 균형을 한 차트(일목)에 담은 추세·지지·저항 통합 지표입니다.
단순 이동평균 교차가 아니라, **선행(구름)·후행(26봉 지연)** 개념으로 시장의 “시간적 균형”을 읽습니다.
### 1.1 핵심 구성 요소
| 구성 요소 | DSL 필드 | 계산 (표준 9·26·52) | 시장에서의 역할 |
|-----------|----------|---------------------|-----------------|
| **전환선** (Conversion Line) | `CONVERSION_LINE` | (9봉 최고+최저) ÷ 2 | 단기 균형점. 주가의 **단기 방향·속도** |
| **기준선** (Base Line) | `BASE_LINE` | (26봉 최고+최저) ÷ 2 | 중기 균형점. **추세 그 자체**, 강한 지지·저항 |
| **선행스팬 1** (Leading Span 1) | `LEADING_SPAN1` | (전환선+기준선) ÷ 2, **26봉 선행** | 단·중기 평균을 미래로 투영 → **구름 상·하단** |
| **선행스팬 2** (Leading Span 2) | `LEADING_SPAN2` | (52봉 최고+최저) ÷ 2, **26봉 선행** | 장기 균형점 → **구름 두께**·매물대 강도 |
| **구름대** (Kumo / Cloud) | `LEADING_SPAN1``LEADING_SPAN2` | 선행1·2 사이 영역 | 지지(양운) / 저항(음운) **매물·심리 경계** |
| **후행스팬** (Lagging Span) | `LAGGING_SPAN` | 현재 종가를 **26봉 후행** 표시 | 현재 vs 26봉 전 종가 비교 → **추세 확정** |
### 1.2 구성 요소 간 관계 (개념도)
```text
[미래] ← 선행스팬1·2 (26봉 앞)
╱╲ 구름대 (Kumo)
╱ ╲
전환선 ────●────●────●──── (9봉, 가장 민감)
기준선 ────────●────●──── (26봉, 추세의 중심)
종가 ──────────●──────── (현재)
후행 ────────────────●── (종가를 26봉 뒤에 표시)
[과거]
```
| 관계 | 의미 | 해석 |
|------|------|------|
| 전환선 ↔ 기준선 | 단기 vs 중기 균형 | 교차 = **추세 전환 1차 신호** |
| 선행1 vs 선행2 | 구름 색·두께 | 선행1>선행2 → **양운**(상승 구름), 반대 → **음운** |
| 종가 vs 구름 | 현재가 위치 | 구름 위 = 강세, 아래 = 약세, 내부 = **전환·횡보** |
| 후행 vs 26봉 전 종가 | 시간 지연 확인 | 후행이 과거 종가 위 = **상승 추세 확정** |
### 1.3 호전(정배열) vs 역전(역배열)
#### 🟩 호전 — 완벽한 상승 추세
```text
종가 > 전환선 > 기준선 > 구름(양운)
후행스팬 > 26봉 전 종가
(선택) 후행스팬 > 26봉 전 구름
```
| 확인 항목 | DSL 조건 예시 |
|-----------|---------------|
| 괘(가격선) | `CONVERSION_LINE GT BASE_LINE` |
| 주가·구름 | `CLOSE_PRICE ABOVE_CLOUD` |
| 구름 성격 | `SPAN1_GT_SPAN2` (양운) |
| 후행 확정 | `LAGGING_GT_PRICE` |
#### 🟥 역전 — 완벽한 하락 추세
```text
종가 < 전환선 < 기준선 < 구름(음운)
후행스팬 < 26봉 전 종가
```
| 확인 항목 | DSL 조건 예시 |
|-----------|---------------|
| 괘 | `CONVERSION_LINE LT BASE_LINE` |
| 주가·구름 | `CLOSE_PRICE BELOW_CLOUD` |
| 구름 성격 | `SPAN1_LT_SPAN2` (음운) |
| 후행 확정 | `LAGGING_LT_PRICE` |
---
## 2. 상황별 기본 매수·매도 판단 로직
### 2.1 매수(Long) 타이밍
| # | 상황 | 판단 근거 | DSL 조건 (예) | 강도 |
|---|------|-----------|---------------|------|
| L1 | **골든크로스(호전)** | 전환선이 기준선 상향 돌파 | `CONVERSION_LINE CROSS_UP BASE_LINE` | ★★★ |
| L2 | **기준선 돌파** | 중기 추세 전환 | `CLOSE_PRICE CROSS_UP BASE_LINE` | ★★★ |
| L3 | **구름 돌파** | 매물대·심리 저항 돌파 | `CLOUD_BREAK_UP` | ★★★★ |
| L4 | **후행 호전** | 26봉 전 대비 추세 확정 | `LAGGING_GT_PRICE` (또는 `CROSS_UP`) | ★★★★ |
| L5 | **구름 지지** | 양운 상단·기준선에서 반등 | `CLOSE_PRICE GT LEADING_SPAN1` + `GT BASE_LINE` | ★★ |
| L6 | **삼역호전** | ①괘 ②구름 ③후행 동시 | 아래 §3.3 참조 | ★★★★★ |
**매수 시 주의**
- 구름 **내부**(`IN_CLOUD`)에서는 신호 빈도↑·신뢰도↓ → 필터(거래량·ADX) 권장.
- `CROSS_*` 조건은 **해당 봉 1회**만 true → 연속 보유는 `GT`/`ABOVE_CLOUD` 등 **상태 조건** 병행.
### 2.2 매도(Short / 청산) 타이밍
| # | 상황 | 판단 근거 | DSL 조건 (예) | 강도 |
|---|------|-----------|---------------|------|
| S1 | **데드크로스(역전)** | 전환선 기준선 하향 이탈 | `CONVERSION_LINE CROSS_DOWN BASE_LINE` | ★★★ |
| S2 | **기준선 이탈** | 중기 추세 붕괴 | `CLOSE_PRICE CROSS_DOWN BASE_LINE` | ★★★ |
| S3 | **구름 이탈** | 지지 붕괴 | `CLOUD_BREAK_DOWN` | ★★★★ |
| S4 | **후행 역전** | 하락 추세 확정 | `LAGGING_LT_PRICE` | ★★★★ |
| S5 | **구름 저항** | 음운 하단·기준선에서 반락 | `CLOSE_PRICE LT LEADING_SPAN1` + `LT BASE_LINE` | ★★ |
| S6 | **삼역역전** | ①데드 ②구름하 ③후행하 | §3.3 역방향 | ★★★★★ |
**매도(청산) 설계 팁**
- 추세 추종: `OR(데드크로스, 종가<전환선, 구름하향돌파)`**연속 매도 방지**.
- 익절: 5파·과열 구간에서는 전환선 이탈만으로도 1차 청산 신호로 사용.
### 2.3 구름·선행스팬 해석 요약
| 구름 상태 | 선행1 vs 선행2 | 종가 위치 | 일반적 해석 |
|-----------|----------------|-----------|-------------|
| **양운** | 선행1 > 선행2 | 종가 > 구름 | 상승 추세, 구름 = **지지** |
| **음운** | 선행1 < 선행2 | 종가 < 구름 | 하락 추세, 구름 = **저항** |
| **얇은 구름** | 두 선행 간격 좁음 | 돌파 직후 | 변곡·추세 전환 **임박** |
| **두꺼운 구름** | 두 선행 간격 넓음 | 구름 안·근처 | **강한 매물대**, 돌파 난이도↑ |
---
## 3. 삼역호전 / 삼역역전 — 핵심 복합 신호
### 3.1 삼역호전 (三役好轉) — 강력 매수
| 역(役) | 일목 요소 | 조건 |
|--------|-----------|------|
| **괘** | 전환·기준 | 전환선 > 기준선 (또는 골든크로스) |
| **주가** | 종가·구름 | 종가 > 구름 (`ABOVE_CLOUD` / `CLOUD_BREAK_UP`) |
| **후행** | 후행·과거 종가 | 후행 > 26봉 전 종가 (`LAGGING_GT_PRICE`) |
**goldenChart 템플릿:** `ICHIMOKU_SANYAKU` — 구름 돌파(현재 봉) + 단계별 필터(N봉 EXISTS_IN).
### 3.2 삼역역전 — 강력 매도·대피
| 역 | 조건 |
|----|------|
| 괘 | 전환선 < 기준선 (데드크로스) |
| 주가 | 종가 < 구름 (`BELOW_CLOUD` / `CLOUD_BREAK_DOWN`) |
| 후행 | 후행 < 26봉 전 종가 (`LAGGING_LT_PRICE`) |
### 3.3 5원소 입체 매매 (Perfect Ichimoku)
괘 · 주가 · 구름(양/음운) · 후행 **4중 AND** — ta4j `PerfectIchimokuStrategy` 동형.
**goldenChart 템플릿:** `ICHIMOKU_PERFECT` (필터 강도에 따라 2~4조건).
---
## 4. 엘리어트 파동 × 일목균형표 — 단계별 전략 조건
엘리어트 파동의 **추세 전개**와 일목의 **선행·후행 시간 구조**를 결합하면, 파동 단계별 진입·청산 근거를 명확히 할 수 있습니다.
> **전제:** 아래 “파동”은 **추정**이며, 실전에서는 상위·하위 시간대 일목을 함께 봅니다.
> 조건은 goldenChart DSL로 표현 가능한 형태로 기술합니다.
---
### 4.1 1파 — 상승 시동 (추세 전환 시작)
| 항목 | 내용 |
|------|------|
| **파동 특징** | 장기 하락 종료 후 첫 반등. 바닥 예측 난이도 **최고** |
| **일목 상태** | 하락하던 전환·기준선을 **순차 돌파** 시작. 위에는 **두터운 음운**이 여전히 존재 |
| **관계** | 종가 ≈ 전환선 근처, 기준선 **미돌파 또는 돌파 직후**, 구름 **아래·내부** |
**전략 조건**
| 구분 | 조건 | DSL 예시 |
|------|------|----------|
| 공격적 매수 | 기준선 상향 돌파 | `CLOSE_PRICE CROSS_UP BASE_LINE` |
| 보조 확인 | 전환 > 기준 | `CONVERSION_LINE GT BASE_LINE` |
| 필터(권장) | 아직 구름 아래면 **소액·분할** | `NOT ABOVE_CLOUD` → 포지션 축소 |
| 매도/관망 | 음운 하단 저항 | `CLOSE_PRICE LT LEADING_SPAN2` 유지 시 추가 매수 자제 |
**판단:** 삼역호전 **미충족**`ICHIMOKU_TREND`(골든크로스) 수준만 허용, `SANYAKU`/`PERFECT`**대기**.
---
### 4.2 2파 — 첫 번째 조정 (눌림목)
| 항목 | 내용 |
|------|------|
| **파동 특징** | 1파 되돌림(종종 50~61.8%). **마지막 저가 테스트** |
| **일목 상태** | 주가 재하락하나 **선행2(구름 하단)·과거 바닥** 미이탈. 전환·기준 **수평 방어** |
| **관계** | 종가 > 기준선 또는 구름 하단(선행2) **지지** |
**전략 조건**
| 구분 | 조건 | DSL 예시 |
|------|------|----------|
| **1차 적극 매수** | 기준선·구름 하단 지지 후 반등 | `CLOSE_PRICE GT BASE_LINE` AND `CLOSE_PRICE GT LEADING_SPAN2` |
| 확인 | 전환 > 기준 유지 | `CONVERSION_LINE GT BASE_LINE` |
| 손절 | 기준선·선행2 동시 이탈 | `CLOSE_PRICE CROSS_DOWN BASE_LINE` OR `CLOUD_BREAK_DOWN` |
| 금지 | 후행 아직 미호전 | `LAGGING_LT_PRICE` 지속 시 **풀매수 금지** |
**판단:** **2파 끝 = 기준선/양운 상단 지지** → 3파 전 **핵심 분할 매수 구간**.
---
### 4.3 3파 — 본격 상승 (핵심 수익 구간)
| 항목 | 내용 |
|------|------|
| **파동 특징** | 거래량·길이 **최대**. 추세 추종 수익 **극대화** 구간 |
| **일목 상태** | **삼역호전 완성**. 얇은 구름 돌파 → 앞선 **두터운 양운** |
| **관계** | 종가 > 전환 > 기준 > 구름, 후행 > 26봉 전 종가·구름 |
**전략 조건 (삼역호전 = 3파 확인)**
```text
AND(
CONVERSION_LINE GT BASE_LINE, /* 괘 */
CLOSE_PRICE ABOVE_CLOUD, /* 주가 — 또는 CLOUD_BREAK_UP */
LAGGING_GT_PRICE, /* 후행 */
SPAN1_GT_SPAN2 /* 양운 (선택) */
)
```
| 구분 | 전략 |
|------|------|
| **매수** | 삼역호전 충족 시 **추가 매수·불타기** (`ICHIMOKU_SANYAKU` / `PERFECT`) |
| **보유** | 종가 > **전환선** 유지 → **매도 금지** (추세 타기) |
| **청산 경고** | 전환선 이탈 **전**까지 보유 극대화 |
| **손절** | `CLOUD_BREAK_DOWN` 또는 `CONVERSION_LINE CROSS_DOWN BASE_LINE` |
**판단:** lliott **3파 ≈ 일목 삼역호전** — 시스템상 **가장 신뢰도 높은 롱 구간**.
---
### 4.4 4파 — 조정 (복잡한 횡보)
| 항목 | 내용 |
|------|------|
| **파동 특징** | 삼각형·횡보. 변동성↑, 방향성 혼란 |
| **일목 상태** | 급등 후 **기준선 지지 테스트**. 일시 **구름 내부** 진입 가능 |
| **관계** | 종가 ≈ 기준선 · 선행1(구름 상단). 전환·기준 **수렴** |
**전략 조건**
| 구분 | 조건 | DSL 예시 |
|------|------|----------|
| 일부 익절 | 기준선 이탈 | `CLOSE_PRICE CROSS_DOWN BASE_LINE` |
| 재매수 | 구름 **상단(선행1)** 지지 | `CLOSE_PRICE GT LEADING_SPAN1` AND `IN_CLOUD` 또는 `ABOVE_CLOUD` 회복 |
| 관망 | 구름 내부 장기 체류 | `IN_CLOUD` + `HOLD_N_DAYS` → 방향 재확인 전 **신규 진입 자제** |
| 손절 | 구름 하단 이탈 | `CLOUD_BREAK_DOWN` |
**판단:** **단기 매매** 구간. 추세 추종은 포지션 **축소**, 스윙만 선행1·기준선 지지에서 재진입.
---
### 4.5 5파 — 마지막 상승 (과열·분산 매도)
| 항목 | 내용 |
|------|------|
| **파동 특징** | 신고가 갱신 but **모멘텀 둔화**. 대중 **후행 참여** |
| **일목 상태** | 종가·전환·기준 **이격 과대**. 후행은 위에 있으나 **꺾임** 조짐. 구름 **얇아짐** |
| **관계** | `DIFF_GT(종가, 전환선)` 과열 · `SPAN1`/`SPAN2` 간격 축소 |
**전략 조건**
| 구분 | 조건 | 전략 |
|------|------|------|
| 분할 매도 | 신고가 + 전환선과 이격 확대 | `CLOSE_PRICE GT CONVERSION_LINE` + `DIFF_GT` (임계값) |
| 1차 청산 | **전환선 이탈** | `CLOSE_PRICE CROSS_DOWN CONVERSION_LINE` |
| 2차 청산 | 데드크로스 | `CONVERSION_LINE CROSS_DOWN BASE_LINE` |
| 전량 익절 | 구름 재진입·하향 | `CLOUD_BREAK_DOWN` 또는 `BELOW_CLOUD` |
**판단:** **매수 금지** · 보유분 **점진적 분할 매도**. 3파와 달리 **전환선 = 생명선**.
---
### 4.6 A-B-C 조정파 — 하락 전환
#### A파 (하락 개시)
| 일목 | 조건 |
|------|------|
| 기준선 **강한 이탈** | `CLOSE_PRICE CROSS_DOWN BASE_LINE` |
| 데드크로스 | `CONVERSION_LINE CROSS_DOWN BASE_LINE` |
| 전략 | **신규 매수 중단**, 5파 잔량 **청산 가속** |
#### B파 (기술적 반등)
| 일목 | 조건 |
|------|------|
| 반등하나 **기준선·음운 하단** 저항 | `CLOSE_PRICE CROSS_UP BASE_LINE``LT BASE_LINE` 재이탈 |
| 구름 | `BELOW_CLOUD` 유지, `CLOUD_BREAK_UP` **실패** |
| 전략 | 기준선 터치 후 **전량 매도·대피** (가짜 반등) |
**DSL 예시 (B파 저항 확인):**
```text
AND(
CLOSE_PRICE LT BASE_LINE,
BELOW_CLOUD,
CONVERSION_LINE LT BASE_LINE
)
```
#### C파 (본 하락)
| 일목 | 조건 |
|------|------|
| 구름 완전 이탈 | `CLOUD_BREAK_DOWN` + `BELOW_CLOUD` |
| 삼역역전 | 데드 + 구름 아래 + `LAGGING_LT_PRICE` |
| 전략 | **물타기 금지**, 현금·인버스. `ICHIMOKU_SANYAKU` 매도 OR 조건 활용 |
---
## 5. 파동 × 일목 — 한눈에 보는 매매 매트릭스
| 파동 | 일목 핵심 상태 | 매수 | 매도/청산 | goldenChart 템플릿 참고 |
|------|----------------|------|-----------|-------------------------|
| **1파** | 기준선 돌파 시도, 음운 위 | 소액·분할 | 관망 | `ICHIMOKU_TREND` (relaxed) |
| **2파** | 기준선·선행2 지지 | **적극 1차 매수** | 지지 이탈 시 손절 | `TREND` + 구름 하단 `GT` |
| **3파** | **삼역호전** | **추가·불타기** | 전환선 이탈 전 보유 | `ICHIMOKU_SANYAKU` / `PERFECT` |
| **4파** | 기준선·구름 상단 횡보 | 지지 확인 시만 | 기준선 이탈 익절 | `IN_CLOUD` 필터 |
| **5파** | 이격 과열, 얇은 구름 | **금지** | 전환선·데드 **분할 매도** | `CROSS_DOWN` 전환선 |
| **A파** | 기준선 붕괴 | 금지 | 잔량 청산 | `TREND` sell |
| **B파** | 기준선·음운 저항 | 금지 | **전량 매도** | `BELOW_CLOUD` + `LT BASE` |
| **C파** | 삼역역전 | 금지 | 대피·숏 | `ICHIMOKU_SIT_EW_C_SELL` |
> **복합지표 팔레트:** `ICHIMOKU_SIT_*` — 기본(L1~S6)·호전·역전·엘리어트(1~5·A~C) 파동별 매수/매도 조건 세트 (전략편집기 복합지표 탭)
---
## 6. goldenChart 전략편집기 적용 가이드
### 6.1 내장 템플릿 매핑
| 템플릿 | 적합 파동 | 핵심 조건 |
|--------|-----------|-----------|
| `ICHIMOKU_TREND` | 1파·2파·A파 | 전환↔기준 골든/데드 + (선택) 구름 |
| `ICHIMOKU_SANYAKU` | **3파** | 구름 돌파 + 후행·전환 필터 |
| `ICHIMOKU_PERFECT` | 3파 (strict) | 4중 AND — 신호↓ 품질↑ |
| `ICHIMOKU_BB` | 4파·5파 | 후행 vs 볼린저 — 변동성·과열 |
### 6.2 필터 강도 권장 (파동별)
| 파동 | `relaxed` | `balanced` | `strict` |
|------|-----------|------------|----------|
| 1~2파 | ○ 진입 탐색 | △ | ✕ (과도) |
| **3파** | △ | **○** | ○ (확신 시) |
| 4~5파 | — (매수 X) | 청산 보조 | 익절 강화 |
| A~C파 | — | 매도 | **삼역역전** |
### 6.3 조건 설계 시 유의사항
1. **`CROSS_*` vs `GT/LT`:** 돌파 **트리거**는 CROSS, **보유**는 GT/ABOVE_CLOUD.
2. **EXISTS_IN 윈도우:** 삼역호전 보조 필터에만 사용 — 트리거에 쓰면 시그널 과다.
3. **멀티 타임프레임:** 3파 확인 = 상위 TF `ABOVE_CLOUD` + 하위 TF `CROSS_UP` 조합 권장.
4. **엘리어트 병행:** 파동 카운트는 주관적 → 일목 **상태 조건**으로 객관화.
---
## 7. 요약 — 판단 흐름도
```text
[하락/바닥] 1파: 기준선 돌파? ──No──→ 관망
Yes (소액)
2파: 기준선·선행2 지지? ──No──→ 손절
Yes (1차 매수)
3파: 삼역호전? ──No──→ 대기/추가 관망
Yes (적극 매수·보유)
4파: 기준선·구름상단 지지? → 재매수 or 일부익절
5파: 전환선 이탈? → 분할 매도
A-B-C: 기준선 저항·삼역역전? → 전량 청산·대피
```
---
## 8. 참고 — 표준 기간 (9 · 26 · 52)
| 요소 | 기간 | 시간축 의미 |
|------|------|-------------|
| 전환선 | 9 | 단기 (약 1.5주, 일봉 기준) |
| 기준선 | 26 | 중기 (약 1달) |
| 선행스팬 | 26 선행 | **1달 앞** 매물·심리 |
| 선행2 | 52 | 장기 (약 2.5달) |
| 후행 | 26 후행 | **1달 전**과 현재 비교 |
분봉(3m·15m) 사용 시 동일 비율을 유지하되, `ICHIMOKU_SANYAKU` 템플릿의 EXISTS_IN 윈도우(N봉)를 **타임프레임에 맞게** 조절합니다.
---
*문서 버전: 2026-03 · goldenChart 전략 DSL `ICHIMOKU` 조건셋 기준*