개정 사항(v2):
user_assets→user_wallets+user_holdings분리 /trading_conditions에condition_logic(AND/OR) 추가 /has_triggeredboolean →status상태값으로 변경 /news_sentiments.content→content_summary로 정정 (네이버 API는 요약만 제공)개정 사항(v3):
trading_conditions의 CHECK 제약을 조건-타입 쌍 단위로 촘촘하게 보완 / 모든 UNIQUE·CHECK 제약조건에uk_,chk_네이밍 컨벤션을 적용해 명시적으로 이름 부여 (JPA 매핑 및 예외 핸들링 대비)개정 사항(v4):
trading_conditions를 "액션(주문)" 중심으로 재편하고, 발동 조건을condition_triggers(트리거) 테이블로 1:N 분리. 이를 통해 고정가격 감시·손절(Stop-Loss)·익절(Take-Profit)·트레일링 스탑(Trailing Stop)·스탑 지정가/시장가·AI 점수 자동매매를 하나의 모델로 통합 확장. 실행 모드(execution_mode: AUTO/MANUAL) 지원. (DB는ddl-auto: none수동 관리로 전환)개정 사항(v5):
trading_conditions에is_persistent(BOOLEAN, 기본 FALSE) 추가. 조건 실행 후is_persistent=FALSE(기본, 1회성)면 기존과 동일하게is_active=FALSE로 비활성화하고,is_persistent=TRUE면 실행 후에도is_active=TRUE를 유지해 반복 감시를 지원한다.
| 테이블 | 문제점 | 변경 | 버전 |
|---|---|---|---|
user_assets |
현금/주식을 한 테이블에서 폴리모픽하게 관리 → FK 모순, 조건문 남발 | user_wallets(현금) / user_holdings(보유 종목)로 분리 |
v2 |
trading_conditions |
가격 조건 + AI 점수 조건 동시 설정 시 AND/OR 판단 불가 | condition_logic 컬럼 추가 |
v2 |
trading_conditions |
has_triggered boolean → 실패 시 영구 잠김, 재시도 불가 |
trading_histories.status로 상태 관리 이전 |
v2 |
news_sentiments |
content를 "본문"으로 정의했으나 네이버 API는 요약만 제공 |
content_summary로 컬럼명·설명 정정 |
v2 |
| 전체 | Chroma/Pinecone 등 별도 Vector DB 전제 | MySQL VECTOR(1536) 단일화 확정, 문서상 혼선 제거 |
v2 |
trading_conditions |
CHECK가 "둘 중 하나는 NOT NULL"만 강제 → target_price만 넣고 price_condition_type을 빠뜨려도 통과됨 |
조건-타입 쌍 단위로 CHECK 재정의 (chk_conditions_type_pair) |
v3 |
| 전체 테이블 | UNIQUE/CHECK 제약조건에 이름이 없거나 일관성 없음 → JPA 예외 핸들링 시 원인 식별 어려움 | uk_<table>_<column(s)>, chk_<table>_<desc> 컨벤션으로 전체 통일 |
v3 |
trading_conditions |
단일 테이블에 "발동 조건"과 "주문 액션"을 혼재 → 손절/익절(평단가 대비 %)·트레일링 스탑(고점 추적)·스탑 주문·AI 자동매매를 표현 불가 | trading_conditions(액션) + condition_triggers(트리거, 1:N)로 분리. trigger_type/base_type/compare_type/is_rate 및 execution_mode(AUTO/MANUAL) 도입 |
v4 |
JPA @Table(uniqueConstraints=...) 매핑, DataIntegrityViolationException 예외 핸들링에서 제약조건 이름으로 분기 처리할 수 있도록 아래 규칙을 전 테이블에 일괄 적용한다.
uk_<table>_<column(s)> -- UNIQUE 제약
fk_<table>_<ref_table> -- FOREIGN KEY 제약 (필요 시 명시)
chk_<table>_<desc> -- CHECK 제약
예: 종목 중복 보유 방지 시 UK_HOLDINGS_USER_STOCK 예외를 캐치해 "이미 보유 중인 종목입니다" 메시지를 매핑하는 식으로 활용한다.
users ──1:N── user_wallets
users ──1:N── user_holdings ──N:1── stocks
users ──1:N── trading_conditions ──N:1── stocks
users ──1:N── trading_histories ──N:1── stocks
trading_histories ──N:1── trading_conditions (nullable, 수동매매는 NULL)
trading_conditions ──1:N── condition_triggers
stocks ──1:N── news_sentiments
users ──1:N── ai_investment_reports ──N:1── stocks
역할: OAuth(구글/카카오) 로그인 정보를 담는 기준 테이블. JWT 발급 시 user_id가 토큰에 포함되어 모든 API에서 사용자를 식별.
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| user_id | BIGINT | PK, AUTO_INCREMENT | 내부 식별용 유저 ID |
| VARCHAR(100) | UNIQUE, NOT NULL | 유저 이메일 | |
| nickname | VARCHAR(50) | NOT NULL | 유저 닉네임 |
| provider | VARCHAR(20) | NOT NULL | OAuth 제공자 (GOOGLE, KAKAO) |
| provider_id | VARCHAR(100) | NOT NULL | OAuth사 고유 식별자 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 가입 일시 |
CREATE TABLE users (
user_id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL,
nickname VARCHAR(50) NOT NULL,
provider VARCHAR(20) NOT NULL,
provider_id VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_users_email UNIQUE (email),
CONSTRAINT uk_users_provider UNIQUE (provider, provider_id)
);역할: 실시간으로 추적할 종목 코드를 관리하는 기준 테이블. KIS Websocket 구독 목록과 네이버 뉴스 검색 키워드 모두 이 테이블 기준으로 동적 결정.
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| stock_code | VARCHAR(10) | PK | 종목 코드 (예: 005930) |
| stock_name | VARCHAR(50) | NOT NULL | 종목명 (예: 삼성전자) |
| is_monitored | BOOLEAN | DEFAULT TRUE | KIS Websocket 구독 및 뉴스 수집 대상 여부 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 등록 일시 |
CREATE TABLE stocks (
stock_code VARCHAR(10) PRIMARY KEY,
stock_name VARCHAR(50) NOT NULL,
is_monitored BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);역할: 유저별 모의투자 현금 예수금만 관리. 기존 user_assets에서 CASH 로우로 처리하던 부분을 별도 테이블로 분리해 FK 모순과 조건 분기를 제거.
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| wallet_id | BIGINT | PK, AUTO_INCREMENT | 지갑 레코드 ID |
| user_id | BIGINT | FK(users.user_id), UNIQUE, NOT NULL | 지갑 소유자 (유저당 1개) |
| balance | DECIMAL(18,4) | DEFAULT 0.0000 | 보유 현금 예수금 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 최종 갱신 일시 |
CREATE TABLE user_wallets (
wallet_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
balance DECIMAL(18,4) DEFAULT 0.0000,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_wallets_user UNIQUE (user_id),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);역할: 유저별 실제 보유 종목만 관리. stocks를 정상적으로 FK 참조할 수 있음 (v1의 CASH 값 같은 예외 케이스 없음).
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| holding_id | BIGINT | PK, AUTO_INCREMENT | 보유 종목 레코드 ID |
| user_id | BIGINT | FK(users.user_id), NOT NULL | 보유자 |
| stock_code | VARCHAR(10) | FK(stocks.stock_code), NOT NULL | 종목 코드 |
| quantity | INT | DEFAULT 0 | 보유 수량 |
| average_price | DECIMAL(18,4) | DEFAULT 0.0000 | 매입 평균 단가 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 최종 갱신 일시 |
| 복합 제약 | - | uk_holdings_user_stock UNIQUE(user_id, stock_code) |
유저당 종목별 중복 로우 방지 (제약조건 이름 명시 — v3) |
CREATE TABLE user_holdings (
holding_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
quantity INT DEFAULT 0,
average_price DECIMAL(18,4) DEFAULT 0.0000,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_holdings_user_stock UNIQUE (user_id, stock_code),
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);역할: 조건 주문의 액션(주문) 부분만 담는 부모 테이블. "언제 발동할지"는 §⑤-1 condition_triggers로 분리한다. 실행 방식(execution_mode: AUTO/MANUAL)을 지원한다.
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| condition_id | BIGINT | PK, AUTO_INCREMENT | 조건(주문 액션) 식별 ID |
| user_id | BIGINT | FK(users.user_id), NOT NULL | 조건 설정 유저 |
| stock_code | VARCHAR(10) | FK(stocks.stock_code), NOT NULL | 대상 종목 |
| order_type | VARCHAR(10) | NOT NULL | BUY / SELL |
| order_quantity | INT | NOT NULL | 조건 충족 시 주문 수량 |
| order_price_type | VARCHAR(10) | NOT NULL, DEFAULT 'MARKET' | MARKET / LIMIT |
| limit_price | DECIMAL(18,4) | NULL | LIMIT 주문 시 지정가 (MARKET이면 NULL) |
| condition_logic | VARCHAR(3) | NOT NULL, DEFAULT 'AND' | 복수 트리거 결합 기준 (AND/OR) |
| execution_mode | VARCHAR(10) | NOT NULL, DEFAULT 'AUTO' | AUTO (완전자동 체결) / MANUAL (반자동 승인 제안) |
| is_active | BOOLEAN | DEFAULT TRUE | 조건 감시 활성화 여부 |
| is_persistent | BOOLEAN | NOT NULL, DEFAULT FALSE | TRUE면 조건 충족/실행 후에도 is_active를 유지(반복 감시), FALSE(기본)면 1회 실행 후 자동 비활성화 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 갱신 일시 |
CREATE TABLE trading_conditions (
condition_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
order_type VARCHAR(10) NOT NULL COMMENT 'BUY, SELL',
order_quantity INT NOT NULL,
order_price_type VARCHAR(10) NOT NULL DEFAULT 'MARKET' COMMENT 'MARKET, LIMIT',
limit_price DECIMAL(18,4) NULL,
condition_logic VARCHAR(3) NOT NULL DEFAULT 'AND' COMMENT 'AND, OR',
execution_mode VARCHAR(10) NOT NULL DEFAULT 'AUTO' COMMENT 'AUTO, MANUAL',
is_active BOOLEAN DEFAULT TRUE,
is_persistent BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'TRUE면 실행 후에도 반복 감시 유지',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code),
CONSTRAINT chk_conditions_order_type CHECK (order_type IN ('BUY','SELL')),
CONSTRAINT chk_conditions_order_price_type CHECK (order_price_type IN ('MARKET','LIMIT')),
CONSTRAINT chk_conditions_logic CHECK (condition_logic IN ('AND','OR')),
CONSTRAINT chk_conditions_execution_mode CHECK (execution_mode IN ('AUTO','MANUAL')),
CONSTRAINT chk_conditions_limit_price CHECK (
(order_price_type = 'LIMIT' AND limit_price IS NOT NULL) OR
(order_price_type = 'MARKET' AND limit_price IS NULL)
)
);역할: 조건 주문의 트리거(발동 조건) 부분. 하나의 trading_conditions가 복수 개의 트리거를 가질 수 있으며(N:1), condition_logic에 따라 AND/OR로 결합된다.
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| trigger_id | BIGINT | PK, AUTO_INCREMENT | 트리거 식별 ID |
| condition_id | BIGINT | FK(trading_conditions.condition_id), NOT NULL, ON DELETE CASCADE | 소속 조건 |
| trigger_type | VARCHAR(20) | NOT NULL | PRICE / PROFIT_TARGET / STOP_LOSS / TRAILING_STOP / AI_SCORE |
| base_type | VARCHAR(20) | NOT NULL | 측정 기준: CURRENT_PRICE / AVG_PRICE / HIGHEST_PRICE / AI_SCORE |
| compare_type | VARCHAR(10) | NOT NULL | ABOVE / BELOW |
| target_value | DECIMAL(18,4) | NOT NULL | 목표값(절대가격 또는 %). is_rate=TRUE면 % |
| is_rate | BOOLEAN | DEFAULT FALSE | target_value가 %인지 여부 |
| trailing_highest | DECIMAL(18,4) | NULL | 트레일링 스탑의 추적 고점 (TRAILING_STOP 전용) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
CREATE TABLE condition_triggers (
trigger_id BIGINT AUTO_INCREMENT PRIMARY KEY,
condition_id BIGINT NOT NULL,
trigger_type VARCHAR(20) NOT NULL COMMENT 'PRICE, PROFIT_TARGET, STOP_LOSS, TRAILING_STOP, AI_SCORE',
base_type VARCHAR(20) NOT NULL COMMENT 'CURRENT_PRICE, AVG_PRICE, HIGHEST_PRICE, AI_SCORE',
compare_type VARCHAR(10) NOT NULL COMMENT 'ABOVE, BELOW',
target_value DECIMAL(18,4) NOT NULL,
is_rate BOOLEAN DEFAULT FALSE COMMENT 'true면 target_value는 %',
trailing_highest DECIMAL(18,4) NULL COMMENT '트레일링 스탑 추적 고점',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (condition_id) REFERENCES trading_conditions(condition_id) ON DELETE CASCADE,
CONSTRAINT chk_triggers_trigger_type CHECK (trigger_type IN ('PRICE','PROFIT_TARGET','STOP_LOSS','TRAILING_STOP','AI_SCORE')),
CONSTRAINT chk_triggers_base_type CHECK (base_type IN ('CURRENT_PRICE','AVG_PRICE','HIGHEST_PRICE','AI_SCORE')),
CONSTRAINT chk_triggers_compare_type CHECK (compare_type IN ('ABOVE','BELOW')),
CONSTRAINT chk_triggers_trailing CHECK (
(trigger_type = 'TRAILING_STOP' AND trailing_highest IS NOT NULL) OR
(trigger_type <> 'TRAILING_STOP')
)
);CREATE TABLE trading_histories (
history_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
condition_id BIGINT NULL,
stock_code VARCHAR(10) NOT NULL,
order_type VARCHAR(10) NOT NULL COMMENT 'BUY, SELL',
status VARCHAR(10) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING, FILLED, FAILED',
execution_price DECIMAL(18,4) NULL,
execution_quantity INT NULL,
total_amount DECIMAL(18,4) NULL,
failure_reason VARCHAR(255) NULL,
requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
executed_at TIMESTAMP NULL,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (condition_id) REFERENCES trading_conditions(condition_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);CREATE TABLE news_sentiments (
news_id BIGINT AUTO_INCREMENT PRIMARY KEY,
stock_code VARCHAR(10) NOT NULL,
news_url VARCHAR(500) NOT NULL,
title VARCHAR(255) NOT NULL,
content_summary TEXT NOT NULL COMMENT '네이버 뉴스 API description 필드 - 요약',
sentiment VARCHAR(10) NOT NULL COMMENT 'GOOD, BAD, NEUTRAL',
ai_score INT NOT NULL,
ai_reason TEXT NULL,
published_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
embedding VECTOR(1536) NULL,
CONSTRAINT uk_news_url UNIQUE (news_url),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);v6 정정:
GET /reports/stocks/{stockCode}(api.md §5.1)가 종목별 최신 리포트를 조회해야 하므로, 실제 구현(및AiInvestmentReport엔티티)에는stock_code컬럼과stocksFK가 처음부터 존재했다. 이 문서의 DDL에 누락되어 있던 것을 실제 스키마 기준으로 정정한다(기능 변경 없음, 문서만 갱신).
| 컬럼명 | 타입 | 제약조건 | 설명 |
|---|---|---|---|
| report_id | BIGINT | PK, AUTO_INCREMENT | 리포트 식별 ID |
| user_id | BIGINT | FK(users.user_id), NOT NULL | 리포트 대상 유저 |
| stock_code | VARCHAR(10) | FK(stocks.stock_code), NOT NULL | 리포트 대상 종목 |
| report_content | TEXT | NOT NULL | 리포트 본문(JSON 문자열) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 생성 일시 |
CREATE TABLE ai_investment_reports (
report_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
report_content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_ai_investment_reports_user_id FOREIGN KEY (user_id) REFERENCES users(user_id),
CONSTRAINT fk_ai_investment_reports_stock_code FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);v7 정정: 운영 DB의 자동생성 FK명(
ai_investment_reports_ibfk_1,FKh0vpbhfnukl9fxc1wx8pssc4w)을 위 이름으로 마이그레이션 완료(ALTER TABLE ... DROP FOREIGN KEY ... / ADD CONSTRAINT ...). 이제 문서와 실제 스키마가 일치한다.
-- 1. 기준 테이블
CREATE TABLE users (
user_id BIGINT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL,
nickname VARCHAR(50) NOT NULL,
provider VARCHAR(20) NOT NULL,
provider_id VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT uk_users_email UNIQUE (email),
CONSTRAINT uk_users_provider UNIQUE (provider, provider_id)
);
CREATE TABLE stocks (
stock_code VARCHAR(10) PRIMARY KEY,
stock_name VARCHAR(50) NOT NULL,
is_monitored BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 2. 자산 관련
CREATE TABLE user_wallets (
wallet_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
balance DECIMAL(18,4) DEFAULT 0.0000,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_wallets_user UNIQUE (user_id),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
CREATE TABLE user_holdings (
holding_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
quantity INT DEFAULT 0,
average_price DECIMAL(18,4) DEFAULT 0.0000,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT uk_holdings_user_stock UNIQUE (user_id, stock_code),
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);
-- 3. 매매 조건(액션) 및 발동 조건(트리거)
CREATE TABLE trading_conditions (
condition_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
order_type VARCHAR(10) NOT NULL COMMENT 'BUY, SELL',
order_quantity INT NOT NULL,
order_price_type VARCHAR(10) NOT NULL DEFAULT 'MARKET' COMMENT 'MARKET, LIMIT',
limit_price DECIMAL(18,4) NULL,
condition_logic VARCHAR(3) NOT NULL DEFAULT 'AND' COMMENT 'AND, OR',
execution_mode VARCHAR(10) NOT NULL DEFAULT 'AUTO' COMMENT 'AUTO, MANUAL',
is_active BOOLEAN DEFAULT TRUE,
is_persistent BOOLEAN NOT NULL DEFAULT FALSE COMMENT 'TRUE면 실행 후에도 반복 감시 유지',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code),
CONSTRAINT chk_conditions_order_type CHECK (order_type IN ('BUY','SELL')),
CONSTRAINT chk_conditions_order_price_type CHECK (order_price_type IN ('MARKET','LIMIT')),
CONSTRAINT chk_conditions_logic CHECK (condition_logic IN ('AND','OR')),
CONSTRAINT chk_conditions_execution_mode CHECK (execution_mode IN ('AUTO','MANUAL')),
CONSTRAINT chk_conditions_limit_price CHECK (
(order_price_type = 'LIMIT' AND limit_price IS NOT NULL) OR
(order_price_type = 'MARKET' AND limit_price IS NULL)
)
);
CREATE TABLE condition_triggers (
trigger_id BIGINT AUTO_INCREMENT PRIMARY KEY,
condition_id BIGINT NOT NULL,
trigger_type VARCHAR(20) NOT NULL COMMENT 'PRICE, PROFIT_TARGET, STOP_LOSS, TRAILING_STOP, AI_SCORE',
base_type VARCHAR(20) NOT NULL COMMENT 'CURRENT_PRICE, AVG_PRICE, HIGHEST_PRICE, AI_SCORE',
compare_type VARCHAR(10) NOT NULL COMMENT 'ABOVE, BELOW',
target_value DECIMAL(18,4) NOT NULL,
is_rate BOOLEAN DEFAULT FALSE COMMENT 'true면 target_value는 %',
trailing_highest DECIMAL(18,4) NULL COMMENT '트레일링 스탑 추적 고점',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (condition_id) REFERENCES trading_conditions(condition_id) ON DELETE CASCADE,
CONSTRAINT chk_triggers_trigger_type CHECK (trigger_type IN ('PRICE','PROFIT_TARGET','STOP_LOSS','TRAILING_STOP','AI_SCORE')),
CONSTRAINT chk_triggers_base_type CHECK (base_type IN ('CURRENT_PRICE','AVG_PRICE','HIGHEST_PRICE','AI_SCORE')),
CONSTRAINT chk_triggers_compare_type CHECK (compare_type IN ('ABOVE','BELOW')),
CONSTRAINT chk_triggers_trailing CHECK (
(trigger_type = 'TRAILING_STOP' AND trailing_highest IS NOT NULL) OR
(trigger_type <> 'TRAILING_STOP')
)
);
CREATE INDEX idx_conditions_user_active ON trading_conditions(user_id, is_active);
CREATE INDEX idx_conditions_stock_active ON trading_conditions(stock_code, is_active);
CREATE INDEX idx_triggers_condition ON condition_triggers(condition_id);
CREATE TABLE trading_histories (
history_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
condition_id BIGINT NULL,
stock_code VARCHAR(10) NOT NULL,
order_type VARCHAR(10) NOT NULL COMMENT 'BUY, SELL',
status VARCHAR(10) NOT NULL DEFAULT 'PENDING' COMMENT 'PENDING, FILLED, FAILED',
execution_price DECIMAL(18,4) NULL,
execution_quantity INT NULL,
total_amount DECIMAL(18,4) NULL,
failure_reason VARCHAR(255) NULL,
requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
executed_at TIMESTAMP NULL,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (condition_id) REFERENCES trading_conditions(condition_id),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);
-- 4. 뉴스/AI/RAG
CREATE TABLE news_sentiments (
news_id BIGINT AUTO_INCREMENT PRIMARY KEY,
stock_code VARCHAR(10) NOT NULL,
news_url VARCHAR(500) NOT NULL,
title VARCHAR(255) NOT NULL,
content_summary TEXT NOT NULL COMMENT '네이버 뉴스 API description 필드 - 요약',
sentiment VARCHAR(10) NOT NULL COMMENT 'GOOD, BAD, NEUTRAL',
ai_score INT NOT NULL,
ai_reason TEXT NULL,
published_at TIMESTAMP NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
embedding VECTOR(1536) NULL,
CONSTRAINT uk_news_url UNIQUE (news_url),
FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);
CREATE TABLE ai_investment_reports (
report_id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
stock_code VARCHAR(10) NOT NULL,
report_content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_ai_investment_reports_user_id FOREIGN KEY (user_id) REFERENCES users(user_id),
CONSTRAINT fk_ai_investment_reports_stock_code FOREIGN KEY (stock_code) REFERENCES stocks(stock_code)
);