# GoldenAnalysis - MySQL DB 구조 문서

## 기본 연결 정보

| 항목 | 값 |
|------|-----|
| DB명 | `stockAnalyzer` |
| 사용자 | `stock` / 비밀번호 `analyzer` |
| Root 비밀번호 | `rootpassword` |
| 포트 | `3306` (호스트 ↔ 컨테이너) |
| 문자셋 | `utf8mb4 / utf8mb4_unicode_ci` |
| 엔진 | MySQL 8.0 |
| JDBC URL (백엔드) | `jdbc:mysql://mysql:3306/stockAnalyzer` |

---

## 전체 테이블 목록 (44개)

> **v2 신규 (GoldenChart 전용)**: `gc_chart_workspace`, `gc_chart_slot`, `gc_watchlist`, `gc_holdings`, `gc_indicator_cache` — 5개 추가

| # | 테이블명 | 분류 | 주요 용도 |
|---|----------|------|-----------|
| 1 | `users` | 인증 | 사용자 계정 |
| 2 | `upbit_candles` | 시세 | 업비트 캔들 마스터 |
| 3 | `upbit_trades` | 시세 | 업비트 체결 아카이브 |
| 4 | `upbit_ingestion_state` | 시세 | 아카이브 인제스트 상태 |
| 5 | `upbit_ws_subscription` | 시세 | WebSocket 수집 대상 |
| 6 | `analysis_candle_data` | 분석 | 분석 파이프라인용 캔들 |
| 7 | `data_update_history` | 분석 | 데이터 수집 이력 |
| 8 | `analysis_settings` | 설정 | 분석 전체 설정 |
| 9 | `indicator_metadata` | 설정 | 지표 메타 (표시명·범위 등) |
| 10 | `indicator_period_setting` | 설정 | 지표별 사용 가능 기간 |
| 11 | `indicator_calculation_setting` | 설정 | 지표 계산 시 가격 타입 |
| 12 | `user_indicator_settings` | 설정 | 사용자별 지표·색상 설정 |
| 13 | `guest_indicator_settings` | 설정 | 비회원 기기별 지표 설정 |
| 14 | `user_ui_theme_settings` | 설정 | 사용자 UI 테마 (PC/탭/모바일) |
| 15 | `system_settings` | 설정 | 시스템 전역 키-값 설정 |
| 16 | `trading_strategy` | 전략 | 매매 전략 파라미터 |
| 17 | `strategy_rule` | 전략 | DSL 기반 전략 규칙 |
| 18 | `condition_group` | 전략 | 전략 조건 그룹 (중첩 AND/OR) |
| 19 | `strategy_condition` | 전략 | 개별 지표 조건 |
| 20 | `ma_condition` | 전략 | MA 크로스 조건 |
| 21 | `strategy_performance` | 전략 | 백테스트 성과 기록 |
| 22 | `crypto_alert` | 알림 | 종목별 알림 설정 |
| 23 | `alert_condition` | 알림 | 알림 발동 조건 |
| 24 | `alert_notification` | 알림 | 알림 발송 이력 |
| 25 | `alert_strategy` | 알림 | 알림에 연결된 전략 규칙 |
| 26 | `alert_schedule_config` | 알림 | 알림 실행 스케줄 설정 |
| 27 | `fcm_tokens` | 알림 | FCM 푸시 토큰 |
| 28 | `crypto_favorites` | 종목 | 즐겨찾기 종목 |
| 29 | `crypto_holdings` | 종목 | 보유/관리 종목 목록 |
| 30 | `dashboard_snapshot` | 대시보드 | 대시보드 캐시 스냅샷 |
| 31 | `stock_price` | 주식 | 주식 OHLCV |
| 32 | `stock_indicator` | 주식 | 주식 기술 지표 |
| 33 | `stock_flow` | 주식 | 외인/기관 수급 |
| 34 | `stock_trend_score` | 주식 | 상승 추세 점수·랭킹 |
| 35 | `market_status` | 주식 | 시장 전체 상태 |
| 36 | `rising_stock_schedule_config` | 주식 | 상승종목 스캔 스케줄 |
| 37 | `chatbot_config` | 챗봇 | 챗봇 설정 |
| 38 | `chatbot_message` | 챗봇 | 챗봇 메시지 이력 |
| 39 | `chatbot_schedule_log` | 챗봇 | 챗봇 실행 로그 |
| 40 | `gc_chart_workspace` | **GoldenChart** | 멀티차트 워크스페이스 (레이아웃·동기화 옵션) |
| 41 | `gc_chart_slot` | **GoldenChart** | 슬롯별 차트 설정 (종목·지표·드로잉·스타일 등) |
| 42 | `gc_watchlist` | **GoldenChart** | 관심종목 목록 |
| 43 | `gc_holdings` | **GoldenChart** | 보유종목 목록 (매입가·수량 포함) |
| 44 | `gc_indicator_cache` | **GoldenChart** | Ta4j 지표 계산 결과 캐시 |

---

## 테이블 상세

### 1. `users` — 사용자 계정

**용도**: 로그인·인증·역할 관리

| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| `id` | BIGINT | PK, AUTO | 사용자 ID |
| `username` | VARCHAR(50) | NOT NULL, UNIQUE | 로그인 ID |
| `password` | VARCHAR(255) | NOT NULL | BCrypt 해시 |
| `email` | VARCHAR(100) | NOT NULL, UNIQUE | 이메일 |
| `name` | VARCHAR(50) | | 표시 이름 |
| `enabled` | BOOLEAN | NOT NULL | 계정 활성 여부 |
| `role` | VARCHAR(20) | NOT NULL | ROLE_USER / ROLE_ADMIN |
| `created_at` | DATETIME | | 생성일 |
| `updated_at` | DATETIME | | 수정일 |
| `last_login_at` | DATETIME | | 최근 로그인 |

**인덱스**: `idx_user_username`(unique), `idx_user_email`(unique)

---

### 2. `upbit_candles` — 업비트 캔들 데이터

**용도**: 업비트 시세 마스터. REST API / WebSocket / Python 서비스가 UPSERT. 지표 계산·차트·알림 모두 이 테이블을 기반으로 조회.

| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| `id` | BIGINT | PK, AUTO | |
| `market` | VARCHAR(50) | NOT NULL | KRW-BTC 형식 |
| `candle_type` | VARCHAR(20) | NOT NULL | minutes / days / weeks / months |
| `unit` | INT | | 분봉 단위 (1,3,5,10,15,30,60,240) |
| `candle_date_time_utc` | DATETIME | NOT NULL | UTC 기준 봉 시각 |
| `candle_date_time_kst` | DATETIME | NOT NULL | KST 기준 봉 시각 |
| `opening_price` | DECIMAL(30,8) | NOT NULL | 시가 |
| `high_price` | DECIMAL(30,8) | NOT NULL | 고가 |
| `low_price` | DECIMAL(30,8) | NOT NULL | 저가 |
| `trade_price` | DECIMAL(30,8) | NOT NULL | 종가 |
| `timestamp` | BIGINT | NOT NULL | 밀리초 타임스탬프 |
| `candle_acc_trade_price` | DECIMAL(30,8) | | 봉 누적 거래대금 |
| `candle_acc_trade_volume` | DECIMAL(30,8) | | 봉 누적 거래량 |
| `prev_closing_price` | DECIMAL(30,8) | | 전일 종가 |
| `change_price` | DECIMAL(30,8) | | 변동 금액 |
| `change_rate` | DECIMAL(20,8) | | 변동률 |
| `created_at` | DATETIME | | |
| `updated_at` | DATETIME | | |

**유니크**: `uk_market_candle` `(market, candle_type, unit, candle_date_time_utc)`  
**인덱스**: `idx_upbit_candles_lookup_kst` `(market, candle_type, unit, candle_date_time_kst)`

> **데이터 보존 정책**: 최근 3일만 유지. 매일 03:00~03:30 자동 삭제 (Java 스케줄러 + Python cleanup 서비스).

---

### 3. `upbit_trades` — 업비트 체결 아카이브

**용도**: 대용량 체결 내역 적재 (Python `upbit_data_service`)

| 컬럼 | 타입 | 제약 | 설명 |
|------|------|------|------|
| `id` | BIGINT | PK, AUTO | |
| `market` | VARCHAR(20) | NOT NULL | |
| `sequential_id` | BIGINT | NOT NULL | 업비트 순번 |
| `trade_timestamp_ms` | BIGINT | | 체결 시각(ms) |
| `trade_price` | DECIMAL(30,8) | | 체결가 |
| `trade_volume` | DECIMAL(30,8) | | 체결량 |
| `ask_bid` | VARCHAR(4) | | ASK / BID |
| `trade_date_utc` | VARCHAR(20) | | YYYY-MM-DD |
| `source` | VARCHAR(30) | 기본 'archive' | 수집 출처 |
| `created_at` | DATETIME | | |

**유니크**: `uk_trade` `(market, sequential_id)`

---

### 4. `upbit_ingestion_state` — 아카이브 인제스트 상태

**용도**: ZIP/날짜별 아카이브 적재 진행 상태 추적 (중복 스킵)

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `source_key` | VARCHAR | 파일/배치 키 |
| `market` | VARCHAR | |
| `unit_path` | VARCHAR | 시간봉 경로 |
| `target_date` | DATE | |
| `file_hash` | VARCHAR | MD5 등 |
| `status` | VARCHAR | PENDING/DONE/ERROR |
| `last_error` | LONGTEXT | 오류 메시지 |
| `created_at` / `updated_at` | DATETIME | |

**유니크**: `uk_ingest` `(source_key, market, unit_path, target_date)`

---

### 5. `upbit_ws_subscription` — WebSocket 수집 대상

**용도**: Python WebSocket 서비스가 `is_active=1` 행을 읽어 수집할 종목·시간봉을 결정. 백엔드 API로 `acquire/release` 관리.

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `market` | VARCHAR | KRW-BTC 등 |
| `interval_code` | VARCHAR | 1m / 5m / 1h 등 |
| `is_active` | BOOLEAN | 수집 활성 여부 |
| `ref_count` | INT | 참조 카운트 |
| `created_at` / `updated_at` | DATETIME | |

**유니크**: `uk_ws` `(market, interval_code)`

---

### 6. `analysis_candle_data` — 분석용 캔들

**용도**: 분석 파이프라인·대시보드용 캔들 저장소 (백엔드 native upsert 지원)

`upbit_candles`와 구조 유사하나 `time_unit`(1m/5m/1d 등 문자열) 컬럼 사용.

**유니크**: `uk_market_time_unit_date` `(market, time_unit, candle_date_time_kst)`  
**인덱스**: `idx_market`, `idx_market_time_unit_date`

---

### 7. `data_update_history` — 데이터 수집 이력

**용도**: 파일별 수집 진행 상태 이력

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `file_path` | VARCHAR | UNIQUE |
| `file_name` | VARCHAR | |
| `market` | VARCHAR | |
| `time_unit` | VARCHAR | |
| `data_date` | DATE | |
| `year` | INT | |
| `record_count` | INT | |
| `file_size` | BIGINT | |
| `status` | ENUM | PENDING/PROCESSING/DONE/ERROR |
| `error_message` | TEXT | |
| `processed_at` | DATETIME | |
| `processing_duration_ms` | BIGINT | |
| `created_at` | DATETIME | |

---

### 8. `analysis_settings` — 분석 설정

**용도**: 분석 관련 전체 설정을 key-value로 저장

| 컬럼 | 설명 |
|------|------|
| `setting_key` | UNIQUE, 설정 키 |
| `setting_value` | TEXT, 설정 값 |
| `setting_type` | ENUM (STRING/NUMBER/BOOLEAN/JSON) |
| `category` | ENUM (분류) |
| `is_active` | 활성 여부 |

---

### 9. `indicator_metadata` — 지표 메타정보

**용도**: 전략 조건 UI에서 지표 표시명·허용 범위·허용 조건타입 정의

| 컬럼 | 설명 |
|------|------|
| `indicator_type` | UNIQUE, 지표 코드 (RSI/MACD/CCI…) |
| `display_name` | 화면 표시명 |
| `category` | 분류 |
| `description` | 설명 |
| `min_value` / `max_value` / `default_value` / `step` | 범위 |
| `target_value_required` | 목표값 필요 여부 |
| `helper_text` | 입력 도움말 |
| `allowed_conditions` | 사용 가능한 조건 타입 목록 |
| `display_order` | 정렬 순서 |
| `enabled` | 활성 여부 |

---

### 10. `indicator_period_setting` — 지표 기간 설정

**용도**: 각 지표별 선택 가능한 기간 목록 (기본값 포함)

**유니크**: `(indicator_type, period)`

---

### 11. `indicator_calculation_setting` — 지표 계산 가격 타입

**용도**: MACD·RSI·볼린저 등의 계산 시 종가/시가/고가/중간값 중 어느 가격을 쓸지 설정

**유니크**: `indicator_type`  
주요 컬럼: `price_type`(enum `MAPriceType`), `enabled`

---

### 12. `user_indicator_settings` — 사용자 지표 설정

**용도**: 로그인 사용자의 지표 표시 여부·기간·차트 색상 설정 저장

**유니크**: `user_id`

| 컬럼 | 설명 |
|------|------|
| `user_id` | UNIQUE |
| `settings` | JSON 문자열 (지표 on/off) |
| `indicator_periods` | JSON 문자열 (기간 설정) |
| `chart_color_settings` | JSON 문자열 (색상 설정) |
| `created_at` / `updated_at` | |

---

### 13. `guest_indicator_settings` — 비회원 지표 설정

**용도**: 비로그인 사용자를 기기ID로 식별하여 설정 저장

**유니크**: `device_id`  
`user_indicator_settings`와 동일 구조 (user_id 대신 device_id 사용)

---

### 14. `user_ui_theme_settings` — UI 테마 설정

**용도**: 사용자별 PC/태블릿/모바일 테마 ID 저장

**유니크**: `user_id`

| 컬럼 | 설명 |
|------|------|
| `user_id` | UNIQUE |
| `pc_theme` | PC 테마 ID |
| `tablet_theme` | 태블릿 테마 ID |
| `mobile_theme` | 모바일 테마 ID |

---

### 15. `system_settings` — 시스템 설정

**용도**: 시스템 전역 키-값 설정

**유니크**: `setting_key`  
주요 컬럼: `setting_key`, `setting_value`(500), `description`

---

### 16. `trading_strategy` — 매매 전략 파라미터

**용도**: 백테스트·신호 분석에 사용하는 지표 임계값·가중치 등 파라미터 집합. 전략 유형(`strategy_type`)에 따라 컬럼 활용 방식이 달라짐.

**주요 컬럼**

| 컬럼 그룹 | 설명 |
|-----------|------|
| `name`, `strategy_type`, `description`, `enabled` | 기본 정보 |
| `stochastic_*`, `cci_*`, `macd_*` | 지표별 임계값·가중치 |
| `grenvil_*`, `obv_*`, `volume_multiplier` | 신호 필터 |
| `int_buy_condition`, `int_sell_condition` | `INTEGRATED` 타입 조건 문자열 |
| `min_signal_score`, `composite_*` | `COMPOSITE` 타입 가중치 |
| `int_ma_*`, `int_macd_*`, `int_stoch_*` 등 | 통합 지표 on/off·기간 |

---

### 17. `strategy_rule` — DSL 전략 규칙

**용도**: 조건 그룹 트리 + DSL JSON으로 매수/매도 규칙을 정의

**관계**: `@OneToMany` → `condition_group`, `ma_condition` (cascade ALL, orphanRemoval)

| 컬럼 그룹 | 설명 |
|-----------|------|
| `name`, `signal_type`(BUY/SELL), `trend_type`, `enabled` | 기본 정보 |
| `dsl_json` | JSON 타입 — UI에서 직접 편집된 DSL |
| `strategy_type`, `created_by_ui`, `use_fsm` | DSL 타입 메타 |
| `inter_group_operator_buy/sell` | 그룹 간 AND/OR |
| MA·일목·거래량 필터 다수 | 추가 필터 조건 |

---

### 18. `condition_group` — 전략 조건 그룹

**용도**: 중첩 AND/OR 트리 구조로 전략 조건을 그룹핑

**관계**: `@ManyToOne` → `strategy_rule`(NOT NULL), self-join `parent_group_id`(nullable)  
`@OneToMany` → `strategy_condition`

| 컬럼 | 설명 |
|------|------|
| `strategy_rule_id` | FK → strategy_rule |
| `parent_group_id` | 자기참조 (nullable) |
| `logical_operator` | AND / OR |
| `group_type` | BUY / SELL |
| `order_index` | 정렬 |

---

### 19. `strategy_condition` — 개별 지표 조건

**용도**: 각 그룹 내 단일 지표 조건 (RSI > 70, MACD 크로스 등)

**관계**: `@ManyToOne` → `condition_group`(NOT NULL)

| 컬럼 | 설명 |
|------|------|
| `condition_group_id` | FK |
| `indicator_type` | RSI / MACD / CCI … |
| `condition_type` | ABOVE / BELOW / CROSS_UP … |
| `target_value` | 기준값 |
| `secondary_value` | 보조값 |
| `period` | 기간 |
| `compare_period` | 비교 기간 |
| `candle_range` | 캔들 범위 |
| `logical_operator` | 이전 조건과의 AND/OR |

---

### 20. `ma_condition` — MA 크로스 조건

**용도**: 전략 규칙의 이동평균 크로스 매수/매도 조건

**관계**: `@ManyToOne` → `strategy_rule`(NOT NULL)

| 컬럼 | 설명 |
|------|------|
| `strategy_rule_id` | FK |
| `signal_type` | BUY / SELL |
| `ma_short_period` | 단기 MA 기간 |
| `ma_long_period` | 장기 MA 기간 |
| `ma_cross_type` | GOLDEN_CROSS / DEAD_CROSS 등 |
| `logical_operator` | AND / OR |

---

### 21. `strategy_performance` — 백테스트 성과

**용도**: 전략별 백테스트 결과 누적 기록

| 컬럼 그룹 | 설명 |
|-----------|------|
| `strategy_id`, `strategy_name`, `strategy_type` | 전략 참조 (논리적 FK) |
| `market`, `time_unit` | 테스트 대상 |
| `analysis_period_start/end` | 테스트 기간 |
| `initial_capital`, `final_capital`, `total_return`, `return_rate` | 수익 지표 |
| `total_trades`, `winning_trades`, `win_rate` | 거래 통계 |
| `max_drawdown`, `sharpe_ratio` | 위험 지표 |
| `analysis_date` | 분석 날짜 |

---

### 22. `crypto_alert` — 종목 알림 설정

**용도**: 사용자가 특정 종목에 대해 설정한 알림 (연결 조건 포함)

**관계**: `@OneToMany` → `alert_condition` (cascade ALL, orphanRemoval)

| 컬럼 | 설명 |
|------|------|
| `symbol` | KRW-BTC 등 |
| `korean_name` / `english_name` | 종목명 |
| `enabled` | 활성 여부 |
| `user_id` | 사용자 참조 (논리적 FK) |
| `strategy_rule_id` | 연결된 전략 규칙 (nullable) |

---

### 23. `alert_condition` — 알림 조건

**용도**: 알림 발동 조건 (지표값·임계값 등). 전역 조건(`is_global=true`) 지원.

**관계**: `@ManyToOne` → `crypto_alert` (nullable)

| 컬럼 | 설명 |
|------|------|
| `crypto_alert_id` | FK (nullable) |
| `is_global` | 전역 조건 여부 |
| `indicator_type` | 지표 종류 |
| `condition_type` | 조건 타입 |
| `target_value` | 기준값 |
| `period` | 기간 |

---

### 24. `alert_notification` — 알림 발송 이력

**용도**: 알림 발동 기록·읽음 상태

**관계**: `@ManyToOne` → `crypto_alert`(NOT NULL)

| 컬럼 | 설명 |
|------|------|
| `crypto_alert_id` | FK |
| `message` | TEXT 알림 메시지 |
| `condition_description` | 발동 조건 설명 |
| `is_read` | 읽음 여부 |
| `triggered_at` | 발동 시각 |
| `notified_at` | 발송 시각 |
| `candle_time` | 발동 캔들 시각 |
| `time_interval` | 시간봉 |

---

### 25. `alert_strategy` — 알림 연결 전략

**용도**: 알림 시스템과 전략 규칙 연결

| 컬럼 | 설명 |
|------|------|
| `strategy_rule_id` | 논리적 FK → strategy_rule |
| `enabled` | 활성 여부 |

---

### 26. `alert_schedule_config` — 알림 스케줄 설정

**용도**: 알림 체크 주기·cron 설정

| 컬럼 | 설명 |
|------|------|
| `user_id` | 사용자별 설정 (nullable = 글로벌) |
| `cron_expression` | Cron 표현식 |
| `time_interval` | 시간봉 단위 |
| `strategy_check_interval_seconds` | 전략 체크 주기(초) |
| `enabled` | 활성 여부 |
| `last_run_time` / `next_run_time` | 실행 시각 |

---

### 27. `fcm_tokens` — FCM 푸시 토큰

**용도**: Firebase Cloud Messaging 토큰 관리

**유니크**: `token`

| 컬럼 | 설명 |
|------|------|
| `token` | FCM 토큰 (UNIQUE) |
| `user_id` | 사용자 ID (논리적 FK) |
| `device_id` | 기기 식별자 |
| `active` | 활성 여부 |
| `last_used_at` | 최근 사용 시각 |

---

### 28. `crypto_favorites` — 즐겨찾기 종목

**용도**: 드롭다운 목록에서 즐겨찾기로 표시할 종목

**유니크**: `symbol`

| 컬럼 | 설명 |
|------|------|
| `symbol` | KRW-BTC 등 (UNIQUE) |
| `korean_name` / `english_name` | 종목명 |
| `display_order` | 정렬 순서 |
| `created_at` | |

---

### 29. `crypto_holdings` — 보유/관리 종목

**용도**: 모니터링 대상으로 등록한 종목

**유니크**: `symbol`  
구조는 `crypto_favorites`와 동일

---

### 30. `dashboard_snapshot` — 대시보드 스냅샷

**용도**: 대시보드 전체 데이터를 JSON으로 캐시 (단일 행, PK=1)

| 컬럼 | 설명 |
|------|------|
| `id` | 고정 1 |
| `summary_json` | MEDIUMTEXT, 요약 JSON |
| `market_data_status_json` | MEDIUMTEXT, 시장 상태 JSON |
| `computed_at` | 생성 시각 |

---

### 31~36. 주식 관련 테이블

> 업비트 암호화폐와 별도로 국내 주식 데이터 분석용

| 테이블 | 용도 | 주요 컬럼 |
|--------|------|-----------|
| `stock_price` | 주식 OHLCV | `code`, `date`, OHLCV, `high_120d` |
| `stock_indicator` | 기술지표 | `code`(UNIQUE), MA/RSI/MACD/ADX/BB |
| `stock_flow` | 수급 | `code`(UNIQUE), 외인·기관·프로그램 순매수, 체결강도 |
| `stock_trend_score` | 추세 점수 | `code`(UNIQUE), 점수·랭킹·매수신호·강도 |
| `market_status` | 시장 전체 | `market_type`(UNIQUE), 지수·VIX·매수가능 여부 |
| `rising_stock_schedule_config` | 스캔 스케줄 | `scan_interval`, `max_results`, `enabled` |

---

### 37. `chatbot_config` — 챗봇 설정

**용도**: 사용자별 챗봇 동작 설정 (대상 종목·분석 조건·알림 설정)

| 컬럼 | 설명 |
|------|------|
| `user_id` | 사용자 |
| `enabled` | 활성 여부 |
| `schedule_interval` | 실행 주기 |
| `target_symbols` | TEXT, 대상 종목 JSON |
| `candle_interval` | 분석 시간봉 |
| `strategy_conditions` | TEXT, 전략 조건 JSON |
| `notification_settings` | TEXT, 알림 설정 JSON |

---

### 38. `chatbot_message` — 챗봇 메시지 이력

**용도**: 챗봇이 생성한 분석 메시지 저장

| 컬럼 | 설명 |
|------|------|
| `config_id` | 챗봇 설정 ID (논리적 FK) |
| `symbol` / `symbol_name` | 종목 |
| `message_type` | 메시지 유형 |
| `title` / `content` | TEXT 내용 |
| `analysis_data` | 분석 원본 데이터 |
| `price` | 분석 시점 가격 |
| `indicators` | 지표값 JSON |
| `read_status` | 읽음 여부 |

---

### 39. `chatbot_schedule_log` — 챗봇 실행 로그

**용도**: 챗봇 스케줄 실행 이력·성능 추적

| 컬럼 | 설명 |
|------|------|
| `config_id` | 챗봇 설정 ID (논리적 FK) |
| `execution_time` | 실행 시각 |
| `symbols_processed` | 처리 종목 수 |
| `messages_generated` | 생성 메시지 수 |
| `errors` | TEXT, 오류 내용 |
| `duration_ms` | 소요 시간 |
| `status` | SUCCESS / ERROR |

---

## 테이블 간 관계 (ERD 요약)

```
users (1) ─────────────────────────────────────────────────────────────
  │  ├── (논리) user_indicator_settings.user_id (1:1)
  │  ├── (논리) guest_indicator_settings.device_id (기기 구분)
  │  ├── (논리) user_ui_theme_settings.user_id (1:1)
  │  ├── (논리) crypto_alert.user_id
  │  ├── (논리) alert_schedule_config.user_id
  │  ├── (논리) fcm_tokens.user_id
  │  └── (논리) chatbot_config.user_id

strategy_rule (1) ─┬── (JPA) condition_group (*) ─── strategy_condition (*)
                   ├── (JPA) ma_condition (*)
                   └── (논리) alert_strategy.strategy_rule_id

condition_group ── 자기참조 (parent_group_id, 중첩 AND/OR 트리)

crypto_alert (1) ─┬── (JPA) alert_condition (*)
                  └── (JPA) alert_notification (*)

trading_strategy ─── (논리) strategy_performance.strategy_id

upbit_candles ─── 지표 계산의 입력 원본
                  ├── IndicatorCalculationService
                  ├── BacktestService
                  ├── AlertSchedulerService
                  └── Python upbit_data_service / upbit_websock_data_service (WRITE)
```

> **(논리)**: JPA `@ManyToOne` 없이 ID 값으로만 연결 (FK 제약 없음)  
> **(JPA)**: `@ManyToOne` / `@OneToMany` 실제 ORM 연관 존재

---

## Frontend ↔ Backend ↔ DB 연결 매핑

| 프론트 서비스 파일 | API 경로 | 관련 테이블 |
|------------------|----------|------------|
| `authApi.ts` | `/api/auth/*` | `users` |
| `cryptoFavoriteApi.ts` | `/api/favorites/*` | `crypto_favorites` |
| `cryptoHoldingApi.ts` | `/api/holdings/*` | `crypto_holdings` |
| `alertApi.ts` | `/api/alerts/*` | `crypto_alert`, `alert_condition`, `alert_notification` |
| `alertStrategyApi.ts` | `/api/alert-strategies` | `alert_strategy` |
| `strategyRuleApi.ts` | `/api/strategy-rules/*` | `strategy_rule`, `condition_group`, `strategy_condition`, `ma_condition` |
| `tradingStrategyApi.ts` | `/api/strategies/*` | `trading_strategy` |
| `backtestApi.ts` | `/api/backtest/*` | `trading_strategy`, `upbit_candles` |
| `dashboardApi.ts` | `/api/dashboard/*` | `dashboard_snapshot`, `strategy_performance` |
| `settingsApi.ts` | `/api/settings/*` | `analysis_settings` |
| `indicatorSettingsApi.ts` | `/api/user/settings/*` | `user_indicator_settings`, `guest_indicator_settings` |
| `upbitApi.ts` `getCurrentPrice` | `/api/upbit/ticker` | (업비트 외부 API) |
| `upbitApi.ts` `analyzeMacd` | `/api/upbit/analyze` | `upbit_candles` → 지표 계산 |
| `upbitWsSubscriptionApi.ts` | `/api/upbit/ws-subscriptions/*` | `upbit_ws_subscription` |
| `upbitDbStatusApi.ts` | `/api/upbit/db-status` | `upbit_candles` 통계 |
| `dataCollectionApi.ts` | `/api/data-collection/*` | `data_update_history`, `upbit_ingestion_state`, `upbit_trades` |
| `chatbotApi.ts` | `/api/chatbot/*` | `chatbot_config`, `chatbot_message`, `chatbot_schedule_log` |
| `risingStockApi.ts` | `/api/rising-stocks/*` | `stock_trend_score`, `market_status`, `rising_stock_schedule_config` |
| `CryptoNameComboBox` | `/api/upbit/ticker`, `/api/crypto/list` | `crypto_holdings` (목록), 외부 API (시세) |

---

## Python 서비스 → DB 쓰기 매핑

| 서비스 | 파일 | 테이블 | 작업 |
|--------|------|--------|------|
| `upbit_data_service` | `db_upsert.py` | `upbit_candles` | INSERT ON DUPLICATE KEY UPDATE |
| `upbit_data_service` | `db_upsert.py` | `upbit_trades` | INSERT ON DUPLICATE KEY UPDATE |
| `upbit_data_service` | `db_upsert.py` | `upbit_ingestion_state` | SELECT + INSERT UPSERT |
| `upbit_data_service` | `data_cleanup.py` | `upbit_candles` | DELETE (3일 이전), OPTIMIZE TABLE |
| `upbit_websock_data_service` | `db_upsert.py` | `upbit_candles` | WebSocket 수신 시 upsert (고저가 GREATEST/LEAST) |
| `upbit_websock_data_service` | `db_upsert.py` | `upbit_ws_subscription` | SELECT (is_active=1 구독 목록 읽기) |

---

## 데이터 보존 정책

| 테이블 | 보존 기간 | 삭제 방식 |
|--------|-----------|-----------|
| `upbit_candles` | **3일** | Java `CandleDataSyncScheduler` 매일 03:30 배치 삭제<br>Python `upbit-data-cleanup` 서비스 매일 03:00 배치 삭제 |
| `alert_notification` | 무제한 | 수동 관리 |
| `chatbot_message` | 설정가능 | `ChatbotMessageRepository.deleteOldMessages()` |
| `chatbot_schedule_log` | 설정가능 | `ChatbotScheduleLogRepository.deleteOldLogs()` |
| `stock_trend_score` | 최신 유지 | `StockTrendScoreRepository` bulk UPDATE |
| binlog | **1일** | `binlog_expire_logs_seconds=86400` (MySQL 설정) |

---

---

## GoldenChart 전용 테이블 상세 (v2 신규)

### 40. `gc_chart_workspace` — 멀티차트 워크스페이스

**용도**: 사용자(또는 기기)별 차트 워크스페이스 상태 저장. 레이아웃 ID 및 동기화 옵션을 관리.  
**관계**: `@OneToMany` → `gc_chart_slot` (cascade ALL, orphanRemoval)

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `user_id` | BIGINT | `users.id` 논리 참조 (로그인 사용자) |
| `device_id` | VARCHAR(100) | 비회원 기기 식별자 |
| `layout_id` | VARCHAR(20) NOT NULL | 레이아웃 코드 (1x1 / 2x1 / 1x2 / 2x2 / 3x1 등) |
| `sync_options_json` | JSON | 차트 간 동기화 옵션 `{symbol, timeframe, crosshair}` |
| `created_at` / `updated_at` | DATETIME | |

**인덱스**: `idx_gc_ws_user(user_id)`, `idx_gc_ws_device(device_id)`

---

### 41. `gc_chart_slot` — 슬롯별 차트 설정

**용도**: 멀티차트 레이아웃의 각 슬롯에 대한 모든 설정값 저장.  
**관계**: `@ManyToOne` → `gc_chart_workspace` (FK, CASCADE DELETE)

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `workspace_id` | BIGINT NOT NULL | FK → `gc_chart_workspace` |
| `slot_index` | INT NOT NULL | 슬롯 인덱스 (0-based) |
| `symbol` | VARCHAR(50) | 종목 코드 (`KRW-BTC`, `AAPL` 등) |
| `timeframe` | VARCHAR(10) | 타임프레임 (1m/5m/15m/30m/1h/4h/1D/1W/1M) |
| `chart_type` | VARCHAR(20) | candlestick / bar / line / area / baseline |
| `theme` | VARCHAR(20) | dark / light / blue |
| `mode` | VARCHAR(20) | chart / trading |
| `log_scale` | TINYINT(1) | 로그 스케일 여부 |
| `indicators_json` | JSON | `IndicatorConfig[]` — 보조지표 설정 배열 |
| `drawings_json` | JSON | `Drawing[]` — 드로잉 객체 배열 (fibtzSettings 포함) |
| `drawings_locked` | TINYINT(1) | 드로잉 잠금 여부 |
| `drawings_visible` | TINYINT(1) | 드로잉 표시 여부 |
| `pane_layout_json` | JSON | 보조지표 창 높이 배열 `[{paneIndex, height}]` |
| `main_chart_style_json` | JSON | 캔들 색상 등 메인 차트 스타일 |
| `created_at` / `updated_at` | DATETIME | |

**유니크**: `uk_slot_workspace_index(workspace_id, slot_index)`

> **indicators_json 구조** (frontend `IndicatorConfig[]` 동일):
> ```json
> [
>   { "id": "EMA_1", "type": "EMA", "params": {"length":21,"src":"close"},
>     "hidden": false, "plotVisibility": {} }
> ]
> ```

> **drawings_json 구조** (frontend `Drawing[]` 동일):
> ```json
> [
>   { "id": "uuid", "type": "fibtz", "points": [{"time":1700000000,"price":50000},...],
>     "color": "#2196F3", "lineWidth": 1, "style": "dashed",
>     "fibtzSettings": {"baseline":{...}, "levels":[...]} }
> ]
> ```

---

### 42. `gc_watchlist` — 관심종목

**용도**: GoldenChart 관심종목 목록 (기존 `crypto_favorites` 와 별도 관리).

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `user_id` | BIGINT | 로그인 사용자 (nullable) |
| `device_id` | VARCHAR(100) | 비회원 기기 ID (nullable) |
| `symbol` | VARCHAR(50) NOT NULL | 종목 코드 |
| `korean_name` / `english_name` | VARCHAR(100) | 종목명 |
| `display_order` | INT | 표시 순서 |
| `created_at` | DATETIME | |

**유니크**: `uk_watchlist_user_symbol(user_id, symbol)`, `uk_watchlist_device_symbol(device_id, symbol)`

---

### 43. `gc_holdings` — 보유종목

**용도**: GoldenChart 보유종목 목록. 평균 매입가·수량 포함.

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `user_id` | BIGINT | 로그인 사용자 (nullable) |
| `device_id` | VARCHAR(100) | 비회원 기기 ID (nullable) |
| `symbol` | VARCHAR(50) NOT NULL | 종목 코드 |
| `korean_name` / `english_name` | VARCHAR(100) | 종목명 |
| `avg_price` | DECIMAL(30,8) | 평균 매입가 |
| `quantity` | DECIMAL(30,8) | 보유 수량 |
| `display_order` | INT | 표시 순서 |
| `created_at` / `updated_at` | DATETIME | |

**유니크**: `uk_holdings_user_symbol(user_id, symbol)`, `uk_holdings_device_symbol(device_id, symbol)`

---

### 44. `gc_indicator_cache` — 지표 계산 결과 캐시

**용도**: Ta4j 지표 계산 결과를 캐시하여 동일 요청 반복 계산 방지.

| 컬럼 | 타입 | 설명 |
|------|------|------|
| `id` | BIGINT PK | |
| `cache_key` | VARCHAR(200) UNIQUE | `symbol:timeframe:type:params_hash` |
| `symbol` | VARCHAR(50) | 종목 코드 |
| `timeframe` | VARCHAR(10) | 타임프레임 |
| `indicator_type` | VARCHAR(50) | 지표 타입 (RSI, MACD 등) |
| `params_json` | JSON | 계산 파라미터 |
| `result_json` | MEDIUMTEXT | `IndicatorResponse` JSON |
| `bar_count` | INT | 계산에 사용된 캔들 수 |
| `last_bar_time` | BIGINT | 마지막 캔들 Unix 타임스탬프(초) |
| `computed_at` | DATETIME | 계산 시각 |
| `expired_at` | DATETIME | 만료 시각 (NULL = 영구) |

---

## GoldenChart Frontend ↔ Backend ↔ DB 연결 매핑

| 프론트 모듈 | API 경로 | 관련 테이블 |
|-------------|----------|------------|
| `backendApi.loadWorkspace()` | `GET /api/chart/workspace` | `gc_chart_workspace`, `gc_chart_slot` |
| `backendApi.saveWorkspace()` | `POST /api/chart/workspace` | `gc_chart_workspace`, `gc_chart_slot` |
| `backendApi.saveSlot()` | `PUT /api/chart/slot/{idx}` | `gc_chart_slot` |
| `backendApi.loadWatchlist()` | `GET /api/watchlist` | `gc_watchlist` |
| `backendApi.addWatchlistItem()` | `POST /api/watchlist` | `gc_watchlist` |
| `backendApi.loadHoldings()` | `GET /api/holdings` | `gc_holdings` |
| `backendApi.upsertHoldingsItem()` | `POST /api/holdings` | `gc_holdings` |
| `backendApi.calculateIndicator()` | `POST /api/indicators/calculate` | `gc_indicator_cache` (캐시) |

---

## GoldenChart 프로젝트 구조

```
goldenChart/
  frontend/                   ← React + Vite 프론트엔드
    src/
      utils/backendApi.ts     ← 백엔드 API 클라이언트
      hooks/useWorkspacePersist.ts ← DB 자동 저장·복원 훅
    vite.config.ts            ← /api → localhost:8080 프록시 추가
    .env.example
  backend/                    ← Spring Boot 백엔드
    pom.xml                   ← Ta4j 0.16, Spring Boot 3.3
    src/main/java/com/goldenchart/
      GoldenChartApplication.java
      config/                 ← CorsConfig, SecurityConfig
      entity/                 ← GcChartWorkspace, GcChartSlot, GcWatchlist, GcHoldings
      repository/             ← JPA Repository 인터페이스
      service/                ← ChartSettingsService, IndicatorService
      controller/             ← ChartSettingsController, IndicatorController, WatchlistController
      dto/                    ← OhlcvBar, IndicatorRequest/Response
    src/main/resources/
      application.yml
      db/migration/V2__create_gc_tables.sql
  ta4j-master/                ← Ta4j 소스 참조용
  db_struct.md                ← 이 파일
```

---

## 주의사항

1. **JPA FK vs 논리 참조**: `users`, `strategy_rule` 등을 참조하는 컬럼 다수가 JPA `@ManyToOne` 없이 Long ID만 보관하므로 DB 레벨 FK 제약이 없습니다. 참조 무결성은 애플리케이션 로직에 의존합니다.
2. **`trading_strategy` vs `strategy_rule`**: 두 테이블은 서로 다른 전략 관리 체계입니다. `trading_strategy`는 파라미터 기반, `strategy_rule`은 DSL/조건 트리 기반입니다.
3. **`upbit_candles` 인덱스**: `(market, candle_type, unit, candle_date_time_kst)` 복합 인덱스. `candle_date_time_kst` 단독 범위 쿼리는 인덱스 효율이 낮습니다.
4. **`autoTradingApi.ts`**: 프론트에서 `/api/auto-trading/*` 호출하나 현재 백엔드에 해당 컨트롤러 없음 (미구현).
5. **GoldenChart 인증**: `gc_*` 테이블은 `user_id` (로그인) 또는 `device_id` (비회원) 중 하나를 식별자로 사용. 헤더 `x-user-id` / `x-device-id` 로 전달.
6. **GoldenChart 지표 계산**: 프론트엔드는 `lightweight-charts-indicators` 로 차트 렌더링, 백엔드는 `Ta4j 0.16` 으로 동일 지표를 계산하여 전략 판단에 사용. EMA 초기화 방식 차이로 인해 초기 수십 봉은 값이 다를 수 있으므로 `warmup-bars: 200` 설정으로 충분한 워밍업 데이터를 확보해야 한다.
7. **`gc_backtest_result`** (V8 마이그레이션): 백테스팅 실행 이력. `analysis_json` 컬럼에 Ta4j `AnalysisCriterion` 전체 계산 결과(총수익률, 승률, MDD, 샤프비율 등)를 JSON으로 저장. `signals_json` 에는 BUY/SELL 시그널 배열, `settings_json` 에는 실행 당시 설정 스냅샷 보관.
