Files
goldenChart/전략편집기 로직 및 동작흐름.md
T

22 KiB
Raw Blame History

전략편집기 로직 및 동작흐름

goldenChart 전략편집기(Strategy Editor) 의 화면 구성, 편집·저장 방식, 그리고 저장된 전략이 백엔드에서 매매 시그널(BUY/SELL)을 감지하기까지의 전체 흐름을 정리한 문서입니다.

관련 문서: 실시간 전략체크 구현.md — 실시간 ON/OFF, CANDLE_CLOSE / REALTIME_TICK 설정 및 STOMP 프로토콜


1. 개요

구분 설명
메뉴 strategy-editor (App.tsxStrategyEditorPage)
역할 매수·매도 조건을 논리 트리(DSL) 로 구성하고 gc_strategy 테이블에 저장
평가 엔진 백엔드 Ta4jStrategyDslToTa4jAdapter가 DSL JSON → Rule 변환
실시간 시그널 LiveStrategyEvaluator + BarBuilder / LiveStrategyScheduler
레거시 화면 StrategyPage (strategy) — 동일 API·DSL, UI만 구형 트리 편집기

전략편집기에서 저장하는 것은 조건 트리 JSON(buyCondition, sellCondition)이며, 노드 좌표·팔레트 사용자 정의 항목 등은 브라우저 localStorage에만 둘 수 있습니다.


2. 화면 구성 (StrategyEditorPage)

2.1 레이아웃

┌─────────────────────────────────────────────────────────────────────────┐
│  BuilderPageShell — 제목: 전략편집기 / Strategy Builder                  │
├──────────────┬──────────────────────────────────────────┬───────────────┤
│  좌측 패널    │  중앙 편집 영역                           │  우측 패널     │
│  (리사이즈)   │                                          │  (리사이즈)    │
│              │  [매수] [매도] 탭                          │               │
│  전략 목록    │  [그래프 | 목록] 편집 모드                  │  지표 | 템플릿 │
│  새 전략      │  ┌────────────────────────────────────┐  │  팔레트       │
│  삭제        │  │ StrategyEditorCanvas (React Flow)   │  │  START/AND/  │
│  Import/     │  │  또는 StrategyListEditor            │  │  OR/NOT 칩    │
│  Export      │  └────────────────────────────────────┘  │  보조/복합    │
│              │  선택 노드 설정 바 (CondEditor)            │  지표 탭      │
├──────────────┴──────────────────────────────────────────┴───────────────┤
│  하단 터미널 — LogicExpressionPreview (논리 수식 미리보기)                  │
└─────────────────────────────────────────────────────────────────────────┘

스타일: strategyEditor.css, strategyEditorTheme.css

2.2 주요 UI 상태

State 타입 용도
signalTab 'buy' | 'sell' 매수/매도 조건 편집 대상
editorMode 'graph' | 'list' React Flow vs 목록 트리 (gc_se_editor_mode)
rightTab 'indicators' | 'templates' 지표 팔레트 vs 프리셋 템플릿
indicatorSubTab 'auxiliary' | 'composite' 단일 보조지표 vs 복합(2기간 교차)
buyCondition / sellCondition LogicNode | null 저장 대상 DSL 루트
buyLayout / sellLayout SignalFlowLayoutSnapshot 노드 좌표, 엣지, START 메타 (로컬 전용)
buyOrphans / sellOrphans LogicNode[] 그래프에서 분리된 고아 노드 (DB 미저장)
selectedId number | 'draft' 선택 전략 ID

2.3 중앙 편집 영역 동작

그래프 모드 (StrategyEditorCanvas)

  • React Flow 기반: StartNode, LogicGateNode(AND/OR/NOT), ConditionFlowNode
  • 팔레트에서 드래그 앤 드롭 → makeNode / mergeAtRoot로 트리에 삽입
  • START 노드에서 평가 분봉 선택 (1m ~ 1d)
  • START를 추가하면 다중 분기 (extraStartIds) — 저장 시 AND/OR + 여러 TIMEFRAME으로 직렬화
  • 노드 이동·연결 변경 시 saveStrategyFlowLayout → localStorage gc_se_flow_layout_v1

목록 모드 (StrategyListEditor)

  • 동일 DSL을 트리/섹션 UI로 편집
  • START 섹션별 분봉·조건 트리 관리

선택 노드 설정 (CondEditorstrategyEditorShared.tsx)

  • indicatorType, conditionType, leftField, rightField, period, 복합지표 기간 등
  • buildStrategyEditorDef()차트 보조지표 설정과 분리된 고정 기본 파라미터 사용

2.4 우측 팔레트

영역 내용
시작·논리 START, AND, OR, NOT (PaletteChip)
밴드·추세 MA, EMA, BOLLINGER, DONCHIAN, ICHIMOKU 등
보조지표 RSI, MACD, CCI, 신고가/신저가(NH_PRIOR/NL_PRIOR) 등 — se-palette-auxiliary-v1
복합지표 RSI+RSI, MACD+MACD 등 2기간 교차 — se-palette-composite-v1
템플릿 strategyPresets.ts — 터틀, 돈치안, 일목 등 원클릭 삽입

드래그 payload: application/json{ type, value, label, composite?, period?, ... }

2.5 하단 Logic Expression

LogicExpressionPreviewcollectEditorBranches / collectDslBranches로 분기별 수식 표시 예:

[5m] (RSI(9) CROSS_UP K_70 AND MACD CROSS_UP)
AND [1h] (ADX CROSS_UP K_25)

3. 전략 데이터 모델 (DSL)

3.1 핵심 타입 (strategyTypes.ts)

type LogicNodeType = 'AND' | 'OR' | 'NOT' | 'CONDITION' | 'TIMEFRAME';

interface ConditionDSL {
  indicatorType: string;      // RSI, MACD, DONCHIAN, NEW_HIGH, ...
  conditionType: string;      // GT, CROSS_UP, CROSS_DOWN, ...
  leftField?: string;         // RSI_VALUE, DC_UPPER_20, NH_PRIOR_9, ...
  rightField?: string;        // K_70, SIGNAL_LINE, DC_LOWER_10, ...
  period?: number;
  composite?: boolean;        // 복합지표 2기간 교차
  leftPeriod?, rightPeriod?;
  leftCandleType?, rightCandleType?;
  // ...
}

interface LogicNode {
  id: string;
  type: LogicNodeType;
  children?: LogicNode[];
  condition?: ConditionDSL;
  candleType?: string;        // TIMEFRAME 전용
}

3.2 DB/API 저장 형태 (GcStrategy)

컬럼 내용
buy_condition_json 매수(진입) 조건 루트 LogicNode
sell_condition_json 매도(청산) 조건 루트 LogicNode
name, description, enabled 메타

API: POST /api/strategiessaveStrategy() (backendApi.ts)

3.3 CONDITION 리프 예시

{
  "id": "n_abc123",
  "type": "CONDITION",
  "condition": {
    "indicatorType": "RSI",
    "conditionType": "CROSS_UP",
    "leftField": "RSI_VALUE",
    "rightField": "K_70",
    "period": 14
  }
}

3.4 논리 노드 예시

{
  "id": "n_and1",
  "type": "AND",
  "children": [
    { "type": "CONDITION", "condition": { "...": "RSI CROSS_UP" } },
    { "type": "CONDITION", "condition": { "...": "MACD CROSS_UP" } }
  ]
}

4. START · TIMEFRAME · 다중 분봉

편집기 UI의 START 노드는 DB에 그대로 저장되지 않습니다. strategyConditionSerde.ts가 저장/로드 시 변환합니다.

4.1 편집기 내부 상태 (EditorConditionState)

{
  root: LogicNode | null;              // 기본 START 분기의 조건 트리
  startMeta: { [startId]: { candleType } };
  extraStartIds: string[];             // 추가 START
  extraRoots: { [startId]: LogicNode };
  startCombineOp?: 'AND' | 'OR';       // 다중 START 결합
}
  • 기본 START ID: __strategy_start__
  • 추가 START: __strategy_start_{uuid}__

4.2 저장 시 인코딩 (encodeConditionForSave)

경우 저장 결과
조건 없음 null
단일 분기, 분봉 1m 루트 트리만 (TIMEFRAME 생략)
단일 분기, 분봉 5m { type: "TIMEFRAME", candleType: "5m", children: [root] }
다중 START { type: "AND"|"OR", children: [ TIMEFRAME×N ] }

4.3 로드 시 디코딩 (decodeConditionForEditor)

  • 최상위가 AND/OR이고 자식이 전부 TIMEFRAME → 다중 START로 복원
  • 최상위가 TIMEFRAME 단일 → 기본 START + 해당 분봉
  • 그 외 → 기본 1m START, 루트 = 전체 DSL

4.4 지원 분봉 (strategyStartNodes.ts)

1m, 3m, 5m, 10m, 15m, 30m, 1h, 4h, 1d


5. 지표·조건 종류

5.1 단일 보조지표

  • 팔레트 또는 템플릿에서 CONDITION 노드 생성
  • leftField / rightField — 지표 플롯 vs 상수(K_70) vs 다른 플롯(SIGNAL_LINE)
  • 기간은 period 또는 필드명 접미사 (RSI_VALUE_9)

5.2 복합지표 (compositeIndicators.ts)

동일 지표 두 기간 비교 — 예: RSI(9) 상향 돌파 RSI(20)

{
  "composite": true,
  "indicatorType": "RSI",
  "conditionType": "CROSS_UP",
  "leftField": "RSI_VALUE_9",
  "rightField": "RSI_VALUE_20",
  "leftPeriod": 9,
  "rightPeriod": 20
}

요소별 서로 다른 분봉(leftCandleType, rightCandleType)은 백엔드 buildCrossTimeframeCompositeRule로 처리 (라이브 + storage 필요).

5.3 신고가·신저가 (단일 지표)

  • NEW_HIGH / NEW_LOW + NH_PRIOR_N / NL_PRIOR_N
  • 의미: 직전 N봉 최고가/최저가 대비 종가 돌파/이탈 (당일 봉 제외)
  • 백엔드: PreviousValueIndicator(HighestValueIndicator(high, N), 1)

5.4 돈치안·일목 등

  • DONCHIANDC_UPPER_N, DC_LOWER_N
  • ICHIMOKU → 구름/선행스팬 전용 conditionType (ABOVE_CLOUD, CLOUD_BREAK_UP, …)

6. 저장·로드 프로세스 (프론트엔드)

6.1 저장 (handleSave)

sequenceDiagram
  participant U as 사용자
  participant SE as StrategyEditorPage
  participant Serde as encodeConditionForSave
  participant API as POST /api/strategies
  participant DB as gc_strategy

  U->>SE: 저장 클릭
  SE->>Serde: buyEditorState / sellEditorState
  Serde-->>SE: buyCondition, sellCondition (LogicNode)
  SE->>API: saveStrategy({ name, buyCondition, sellCondition })
  API->>DB: JSON 컬럼 persist
  SE->>SE: persistFlowLayout(id) — localStorage만

검증:

  • 전략명 필수
  • 매수·매도 중 하나 이상 조건 필요
  • draft 저장 시 → DB ID 발급 후 migrateStrategyFlowLayout('draft' → id)

6.2 로드

  1. loadStrategies() — 서버 우선
  2. 실패 시 loadStratsLocal() (gc_strat_v2)
  3. 전략 선택 → decodeConditionForEditor(buy/sell) + loadStrategyFlowLayout(id)

6.3 localStorage vs DB

저장 내용 DB
gc_strategy (API) buy/sell JSON
gc_se_flow_layout_v1 노드 좌표, edge, startMeta, orphans
gc_strat_v2 전략 목록 캐시 (폴백)
se-palette-auxiliary-v1 사용자 보조지표 팔레트
se-palette-composite-v1 사용자 복합지표 팔레트
gc_se_editor_mode graph / list

Import/Export: strategyImportExport.ts — DSL + flowLayout + editorMode JSON 파일


7. 백엔드: DSL → Ta4j Rule

7.1 어댑터 (StrategyDslToTa4jAdapter)

진입: toRule(JsonNode dsl, RuleBuildContext ctx)

LogicNode.type 빌드 메서드 Ta4j
AND buildAndRule AndRule
OR buildOrRule OrRule
NOT buildNotRule NotRule
TIMEFRAME buildTimeframeRule 분기 BarSeries + CrossSeriesRule
CONDITION buildConditionRule 비교/교차 Rule

CONDITION conditionType 매핑 (요약):

conditionType Ta4j 개념
GT, LT, GTE, LTE, EQ, NEQ Over/Under/Equals
CROSS_UP, CROSS_DOWN CrossedUpIndicatorRule, CrossedDownIndicatorRule
SLOPE_UP, SLOPE_DOWN IsRisingRule, IsFallingRule
HOLD_N_DAYS N봉 연속 충족
일목 전용 구름/선행스팬 규칙

필드 해석: resolveField — OHLCV, K_* 상수, RSI/CCI/MACD/Donchian/NH_PRIOR/NL_PRIOR 등

지표 파라미터: DB indicator_settings + DSL_TO_REGISTRY 매핑

7.2 멀티 타임프레임 (라이브)

RuleBuildContextmarket, Ta4jStorage가 있으면:

  • TIMEFRAME.candleType에 맞는 시리즈를 storage에서 로드
  • 트리거 봉(예: 1m 마감) 인덱스에서 다른 분봉 시리즈 최신 봉CrossSeriesRule로 평가

백테스트는 단일 요청 BarSeries만 사용 → 교차 TF는 primary series fallback.

7.3 Strategy 조립 (LiveStrategyEvaluator.buildStrategy)

Rule entryRule = buildRule(buyConditionJson,  series, params, market, storage);
Rule exitRule  = buildRule(sellConditionJson, series, params, market, storage);
BaseStrategy strategy = new BaseStrategy(entryRule, exitRule);

캐시 키: market:candleType:strategyId


8. 백엔드: 데이터 수집 → 평가 트리거

8.1 캔들 파이프라인

flowchart LR
  UWS[Upbit WebSocket 틱]
  MTP[MarketTickProcessor]
  BB[BarBuilder]
  TS[Ta4jStorage BarSeries]

  UWS --> MTP --> BB --> TS
  • 동일 분: updateLastBar (진행 중 봉 갱신)
  • 분 변경: commitBar → 확정봉 + 상위봉(3m~1d) 집계

8.2 어떤 분봉을 평가할지

StrategyConditionTimeframeService:

  • 전략 DSL을 walk하여 TIMEFRAME 노드의 candleType 수집
  • usesTimeframe(strategyId, candleType) — 해당 봉 마감/스케줄에서만 평가

LiveStrategyTimeframeService는 UI/설정용 gc_live_strategy_settings.candle_type resolve (가상투자 카드 시간봉 드롭다운 등).


9. 시그널 감지 프로세스

9.1 전제: 실시간 전략 설정 (GcLiveStrategySettings)

필드 의미
isLiveCheck true/false 실시간 체크 ON
executionType CANDLE_CLOSE / REALTIME_TICK 평가 타이밍
strategyId FK 사용할 GcStrategy
positionMode LONG_ONLY / SIGNAL_ONLY 포지션 추적 방식
candleType 1m1d 설정/UI 기본 분봉

가상투자·설정 화면에서 PUT /api/strategy/settings로 반영.

9.2 시그널 판정 (StrategySignalDeterminer)

positionMode BUY SELL
LONG_ONLY strategy.shouldEnter(index, record) strategy.shouldExit(index, record)
SIGNAL_ONLY entryRule.isSatisfied(index) exitRule.isSatisfied(index)

LONG_ONLY는 evaluator가 가상 TradingRecord를 캐시해 중복 진입 방지.

결과: "BUY" | "SELL" | "NONE"

9.3 방식 A — 봉 마감 직후 (CANDLE_CLOSE)

트리거: BarBuilder.commitBar()evaluateStrategyOnBarClose()

조건 (각 설정 행):

  1. isLiveCheck == true
  2. executionType == CANDLE_CLOSE
  3. usesTimeframe(strategyId, candleType)
  4. Ta4jStorage에 해당 분봉 데이터 존재

평가: LiveStrategyEvaluator.evaluateCandleClose(market, candleType, maturedIndex)

  • 확정된 직전 봉 인덱스에서 1회 판정

9.4 방식 B — 실시간 틱 (REALTIME_TICK)

트리거: LiveStrategyScheduler3초 주기 (@Scheduled)

조건: executionType == REALTIME_TICK 등 동일

평가: evaluateRealtimeTick(market, candleType)

  • currentIndex = series.getEndIndex()진행 중(provisional) 봉
  • 중복 방지: 동일 (market, candleType)에서 같은 index에 이미 시그널 냈으면 skip
  • 평가 전 strategy 캐시 제거updateLastBar 후 지표 캐시 stale 방지

9.5 보완 — 마감 직후 REALTIME 재평가

BarBuilder: CANDLE_CLOSENONE이면 → evaluateRealtimeAtClose(maturedIndex)

  • 봉 마감 시점의 교차(CROSS) 를 3초 스케줄러가 놓치는 경우 대비

9.6 평가 흐름 (단일 종목·단일 시점)

flowchart TD
  A[트리거: 봉 마감 또는 3초 틱]
  B{isLiveCheck?}
  C{usesTimeframe?}
  D[LiveStrategyEvaluator.evaluate]
  E[buildStrategy - DSL to Ta4j]
  F[StrategySignalDeterminer.determineSignal]
  G{결과}
  H[publishStrategySignal]
  I[NONE - 종료]

  A --> B
  B -->|false| I
  B -->|true| C
  C -->|false| I
  C -->|true| D
  D --> E --> F --> G
  G -->|BUY/SELL| H
  G -->|NONE| I

주의: 동일 마켓에 활성 설정이 여러 개면 evaluator는 첫 non-NONE 시그널만 반환할 수 있음.


10. 시그널 발행·후처리

10.1 STOMP WebSocket

토픽: /sub/charts/{market}/{candleType}
엔드포인트: ws://host/ws/trading (SockJS)

페이로드 (CandleBarDto): OHLCV + 지표값 + signal: "BUY" | "SELL" | null

{
  "t": 1779229260,
  "o": 92080000.0,
  "c": 92050000.0,
  "signal": "BUY"
}

별도 “시그널 전용” 토픽은 없음 — 차트 캔들 스트림에 시그널 필드로 포함.

10.2 DB·알림·주문

단계 클래스
DB 저장 TradeSignalService.saveGcTradeSignal
FCM FcmPushService
주문 큐 OrderExecutionQueue.submitSignal (deviceId 있을 때)

10.3 프론트 차트 반영

  • STOMP 구독 → 캔들 수신 시 signal 필드로 마커(setMarkers) 표시
  • LiveSignalNotifier, 차트 슬롯 등

11. 조건 현황 API (시그널과 별개)

용도: 가상투자 카드, 모니터링 UI — “지금 각 조건이 얼마나 충족됐는지”

항목 내용
API GET /api/strategy/live-conditions?market=&strategyId=
구현 LiveConditionStatusService
방식 buy/sell JSON의 각 CONDITION 리프를 개별 Rule로 평가
matchRate 충족 리프 수 / 평가 가능 리프 수 (전체 AND/OR 트리 충족과 다름)

실제 BUY/SELL 발화는 §9 경로만 사용.


12. 백테스트 vs 실시간

항목 백테스트 (BacktestingService) 실시간 (LiveStrategyEvaluator)
BarSeries 요청 bars로 일회성 생성 Ta4jStorage 실시간 누적
adapter toRule(dsl, series, params) toRule(..., market, storage)
스캔 전 구간 0..barCount-1 단일 index (마감 or provisional)
청산 DSL sell + 손절/익절/트레일링 OR 합성 DSL sell (+ 리스크 모니터 별도)
출력 BacktestResponse.signals, gc_backtest_result STOMP + gc_trade_signal
멀티 TF storage 없으면 단일 시리즈 fallback TIMEFRAME / 교차 TF 지원

백테스트도 동일 DSL·동일 어댑터를 사용하므로, 편집기에서 저장한 전략이 백테스트와 라이브에서 같은 의미로 해석됩니다 (데이터·타이밍 차이는 있음).


13. 전략 사용 경로 (편집기 이후)

flowchart TB
  SE[전략편집기 저장]
  DB[(gc_strategy)]
  SE --> DB

  DB --> BT[백테스팅 화면]
  DB --> LIVE[실시간 전략 설정]
  DB --> VT[가상투자 카드]

  LIVE --> LSS[gc_live_strategy_settings]
  LSS --> EVAL[LiveStrategyEvaluator]
  VT --> LC[live-conditions API]

  EVAL --> STOMP[STOMP signal]
  EVAL --> TSIG[gc_trade_signal]
  1. 전략편집기 — DSL 작성·저장
  2. 설정 / 가상투자 — 종목별 strategyId, executionType, positionMode, 평가 분봉
  3. 백테스트 — 과거 봉으로 전 구간 시뮬레이션
  4. 라이브 — Upbit 틱 → Ta4j → 시그널 → STOMP·DB·(자동매매)

14. 주요 소스 파일

프론트엔드

경로 역할
frontend/src/components/StrategyEditorPage.tsx 전략편집기 메인
frontend/src/components/strategyEditor/StrategyEditorCanvas.tsx React Flow 캔버스
frontend/src/components/strategyEditor/StrategyListEditor.tsx 목록 편집기
frontend/src/components/strategyEditor/LogicExpressionPreview.tsx 논리 수식 미리보기
frontend/src/utils/strategyTypes.ts DSL 타입·조건 상수
frontend/src/utils/strategyConditionSerde.ts START↔TIMEFRAME 직렬화
frontend/src/utils/strategyEditorShared.tsx CondEditor, 트리 조작, DEF
frontend/src/utils/strategyEditorLayoutStorage.ts Flow 레이아웃 localStorage
frontend/src/utils/compositeIndicators.ts 복합지표 정의
frontend/src/utils/strategyStartNodes.ts 분봉·START 상수
frontend/src/utils/strategyPresets.ts 템플릿
frontend/src/utils/backendApi.ts saveStrategy, loadStrategies

백엔드

경로 역할
backend/.../entity/GcStrategy.java 전략 엔티티
backend/.../entity/GcLiveStrategySettings.java 실시간 설정
backend/.../service/StrategyDslToTa4jAdapter.java DSL → Rule
backend/.../service/LiveStrategyEvaluator.java 실시간 평가
backend/.../service/StrategySignalDeterminer.java BUY/SELL 판정
backend/.../service/BacktestingService.java 백테스트
backend/.../service/LiveConditionStatusService.java 조건 현황 API
backend/.../service/StrategyConditionTimeframeService.java DSL 분봉 수집
backend/.../websocket/BarBuilder.java 봉 조립·마감 평가
backend/.../websocket/LiveStrategyScheduler.java 3초 REALTIME_TICK
backend/.../websocket/TradingWebSocketBroker.java STOMP 발행
backend/.../storage/Ta4jStorage.java BarSeries 저장소

15. 핵심 체크리스트 (디버깅용)

  1. 저장 DSLencodeConditionForSave 후 JSON에 TIMEFRAME / AND+TIMEFRAME 구조가 의도대로인가?
  2. 분봉 — 조건 리프의 timeframe vs TIMEFRAME 래핑 candleType 일치 여부
  3. 실시간 ONisLiveCheck, strategyId 연결
  4. 실행 방식CANDLE_CLOSE vs REALTIME_TICK
  5. 데이터Ta4jStorage.exists(market, candleType), warm-up(pinCandleWatch)
  6. 포지션 모드LONG_ONLY에서 이미 포지션일 때 BUY 무시
  7. STOMP — 구독 토픽 /sub/charts/{market}/{candleType} 와 평가 분봉 일치
  8. UI 현황 vs 시그널live-conditions matchRate ≠ 실제 BUY/SELL 발화 조건

문서 작성 기준: goldenChart 저장소 프론트/백엔드 소스 구조. API·클래스명은 변경될 수 있으므로 구현 확인 시 §14 파일 목록을 참고하세요.