This commit is contained in:
2026-03-13 04:16:16 +09:00
parent daf29a36be
commit be155467bc
2 changed files with 226 additions and 1 deletions

1
.gitignore vendored
View File

@@ -5,7 +5,6 @@ build/
dist/
wheels/
*.egg-info
upbit_db_init.py
# Virtual environments
.venv

226
upbit_db_init.py Normal file
View File

@@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""
upbit_db_init.py — upbit_quant_db MariaDB 테이블 초기화 스크립트
=================================================================
실행: python3 upbit_db_init.py
- 필요한 테이블이 없으면 생성 (IF NOT EXISTS → 재실행 안전)
- env_config에 초기 기본값 row 삽입
"""
import sys
import pymysql
import pymysql.cursors
import logging
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s", datefmt="%H:%M:%S")
logger = logging.getLogger("UpbitDBInit")
DB_CFG = dict(
host="192.168.0.141",
port=3306,
user="jae",
password="1234",
database="upbit_quant_db",
charset="utf8mb4",
autocommit=True,
cursorclass=pymysql.cursors.DictCursor,
connect_timeout=10,
)
DDL_STATEMENTS = [
# ── 1. 현재 보유 포지션 ──────────────────────────────────────────────
"""
CREATE TABLE IF NOT EXISTS active_trades (
code VARCHAR(20) NOT NULL PRIMARY KEY COMMENT '마켓코드 (KRW-BTC)',
name VARCHAR(50) COMMENT '종목명',
strategy VARCHAR(50) COMMENT '전략명',
avg_buy_price DECIMAL(20,8) COMMENT '평균 매수가',
current_price DECIMAL(20,8) COMMENT '현재가',
stop_price DECIMAL(20,8) COMMENT '손절가',
target_price DECIMAL(20,8) COMMENT '목표가',
max_price DECIMAL(20,8) COMMENT '보유 중 최고가 (트레일링스탑용)',
atr_entry DECIMAL(20,8) COMMENT '진입 시점 ATR (변동성)',
target_qty DECIMAL(30,10) COMMENT '목표 수량',
current_qty DECIMAL(30,10) COMMENT '현재 수량',
total_invested DECIMAL(20,2) COMMENT '총 투자금액 (원)',
status VARCHAR(20) DEFAULT 'HOLDING' COMMENT '상태 (HOLDING)',
buy_date DATETIME COMMENT '매수 시각',
updated_at DATETIME COMMENT '최종 업데이트',
rsi DECIMAL(8,4) COMMENT '진입 시 RSI',
volume_ratio DECIMAL(8,4) COMMENT '거래량 비율',
tail_length_pct DECIMAL(8,4) COMMENT '꼬리 길이 (%)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='현재 보유 포지션'
""",
# ── 2. 매매 기록 ────────────────────────────────────────────────────
"""
CREATE TABLE IF NOT EXISTS trade_history (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(20) COMMENT '마켓코드',
name VARCHAR(50) COMMENT '종목명',
strategy VARCHAR(50) COMMENT '전략명',
buy_price DECIMAL(20,8) COMMENT '매수가',
sell_price DECIMAL(20,8) COMMENT '매도가',
qty DECIMAL(30,10) COMMENT '거래 수량',
profit_rate DECIMAL(10,4) COMMENT '수익률 (%)',
realized_pnl DECIMAL(20,2) COMMENT '실현 손익 (원)',
hold_minutes INT COMMENT '보유 시간 (분)',
buy_date DATETIME COMMENT '매수 시각',
sell_date DATETIME COMMENT '매도 시각',
sell_reason VARCHAR(200) COMMENT '매도 사유',
rsi DECIMAL(8,4) COMMENT '진입 시 RSI',
volume_ratio DECIMAL(8,4) COMMENT '거래량 비율',
tail_length_pct DECIMAL(8,4) COMMENT '꼬리 길이 (%)',
INDEX idx_sell_date (sell_date),
INDEX idx_code (code),
INDEX idx_strategy (strategy)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='매매 기록'
""",
# ── 3. 전략 설정값 (Upbit 전용) ──────────────────────────────────────
"""
CREATE TABLE IF NOT EXISTS env_config (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
created_at DATETIME DEFAULT NOW() COMMENT '생성 시각',
-- 업비트 API 키
UPBIT_ACCESS_KEY VARCHAR(200) DEFAULT '' COMMENT '업비트 Access Key',
UPBIT_SECRET_KEY VARCHAR(200) DEFAULT '' COMMENT '업비트 Secret Key',
-- 알림 (Mattermost)
MM_SERVER_URL VARCHAR(200) DEFAULT '' COMMENT 'MM 서버 URL',
MM_BOT_TOKEN_ VARCHAR(200) DEFAULT '' COMMENT 'MM 봇 토큰',
MATTERMOST_CHANNEL VARCHAR(100) DEFAULT 'upbit' COMMENT 'MM 채널명',
-- 포지션 관리
MAX_STOCKS VARCHAR(20) DEFAULT '5' COMMENT '최대 보유 코인 수',
SLOT_MONEY_DEFAULT VARCHAR(20) DEFAULT '100000' COMMENT '코인당 투자금액 (원)',
-- 손익 기준
STOP_LOSS_PCT VARCHAR(20) DEFAULT '-0.02' COMMENT '손절 비율 (음수, 예:-0.02)',
TAKE_PROFIT_PCT VARCHAR(20) DEFAULT '0.05' COMMENT '익절 비율 (예:0.05)',
MAX_LOSS_PER_TRADE_KRW VARCHAR(20) DEFAULT '50000' COMMENT '거래당 최대 원화 손실 한도',
-- 어깨 매도 (수익 보존)
SHOULDER_CUT_PCT VARCHAR(20) DEFAULT '0.03' COMMENT '어깨매도: 고점 대비 하락률',
SHOULDER_MIN_HIGH_PCT VARCHAR(20) DEFAULT '0.01' COMMENT '어깨매도: 발동 최소 이익률',
SHOULDER_MIN_NET_PCT VARCHAR(20) DEFAULT '0.001' COMMENT '어깨매도: 수수료 반영 최소 이익',
-- ATR 스캘핑 엑시트
SCALP_ATR_UP_MULT VARCHAR(20) DEFAULT '1.0' COMMENT 'ATR 스캘핑: 상승 배수',
SCALP_ATR_DOWN_MULT VARCHAR(20) DEFAULT '0.2' COMMENT 'ATR 스캘핑: 하락 배수',
SCALP_ATR_DROP_MULT VARCHAR(20) DEFAULT '1.0' COMMENT 'ATR 스캘핑: 낙폭 배수',
-- ATR 손절/목표가 배수
STOP_ATR_MULTIPLIER_TAIL VARCHAR(20) DEFAULT '2.5' COMMENT '손절선: 진입가 - ATR * 배수',
TARGET_ATR_MULTIPLIER_TAIL VARCHAR(20) DEFAULT '7.0' COMMENT '목표가: 진입가 + ATR * 배수',
-- 스캔 조건
MIN_DROP_RATE VARCHAR(20) DEFAULT '0.03' COMMENT '매수 스캔: 최소 낙폭 (예:0.03)',
MIN_RECOVERY_RATIO VARCHAR(20) DEFAULT '0.30' COMMENT '매수 스캔: 최소 회복률',
MAX_RECOVERY_RATIO VARCHAR(20) DEFAULT '0.80' COMMENT '매수 스캔: 최대 회복률',
HIGH_PRICE_CHASE_THRESHOLD VARCHAR(20) DEFAULT '0.96' COMMENT '고점 추격 방지 임계값',
RSI_OVERHEAT_THRESHOLD VARCHAR(20) DEFAULT '78.0' COMMENT 'RSI 과열 임계값',
-- 꼬리봉 조건
TAIL_RATIO_MIN VARCHAR(20) DEFAULT '1.5' COMMENT '꼬리/몸통 최소 비율',
TAIL_PCT_MIN VARCHAR(20) DEFAULT '0.003' COMMENT '꼬리 최소 % (예:0.003)',
TAIL_SCORE_BASE VARCHAR(20) DEFAULT '5.0' COMMENT '꼬리 기본 점수',
TAIL_SCORE_RATIO_MULT VARCHAR(20) DEFAULT '2.0' COMMENT '꼬리 비율 점수 가중치',
-- 기타 매매 파라미터
REENTRY_COOLDOWN_SEC VARCHAR(20) DEFAULT '300' COMMENT '재진입 쿨다운 (초)',
ROUND_TRIP_COST_PCT VARCHAR(20) DEFAULT '0.001' COMMENT '왕복 수수료율 (업비트 0.05%×2)',
MIN_HOLD_AFTER_BUY_SEC VARCHAR(20) DEFAULT '10.0' COMMENT '매수 후 최소 보유 시간 (초)',
-- 스캔 주기
UPBIT_SCAN_INTERVAL_SEC VARCHAR(20) DEFAULT '60' COMMENT '매수 스캔 주기 (초)',
UPBIT_BUY_TOP_N VARCHAR(20) DEFAULT '2' COMMENT '스캔 후 상위 N개 매수',
UPBIT_CANDLE_UNIT VARCHAR(20) DEFAULT '3' COMMENT '스캔용 분봉 단위 (3/5/15/60)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='전략 설정값 (업비트 전용)'
""",
# ── 4. Key-Value 저장소 (API 키 등) ──────────────────────────────────
"""
CREATE TABLE IF NOT EXISTS kv_store (
k VARCHAR(100) NOT NULL PRIMARY KEY COMMENT '',
v TEXT COMMENT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Key-Value 저장소'
""",
# ── 5. 스캔 후보 목록 ───────────────────────────────────────────────
"""
CREATE TABLE IF NOT EXISTS target_candidates (
code VARCHAR(20) NOT NULL PRIMARY KEY COMMENT '마켓코드',
name VARCHAR(50) COMMENT '코인명',
score DECIMAL(10,4) COMMENT '후보 점수',
price DECIMAL(20,8) COMMENT '스캔 당시 가격',
scan_time DATETIME COMMENT '스캔 시각',
updated_at DATETIME COMMENT '최종 업데이트'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='스캔 후보 목록'
""",
# ── 6. 업비트 분봉 데이터 (백테스트용) ──────────────────────────────
"""
CREATE TABLE IF NOT EXISTS upbit_candles (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(20) NOT NULL COMMENT '마켓코드 (KRW-BTC)',
candle_time VARCHAR(12) NOT NULL COMMENT '봉 시작시각 YYYYMMDDHHMI',
timeframe SMALLINT NOT NULL DEFAULT 3 COMMENT '봉 단위 (3=3분, 60=60분봉)',
open_price DECIMAL(20,8) COMMENT '시가',
high_price DECIMAL(20,8) COMMENT '고가',
low_price DECIMAL(20,8) COMMENT '저가',
close_price DECIMAL(20,8) COMMENT '종가',
volume DECIMAL(30,8) COMMENT '체결량',
is_confirmed TINYINT DEFAULT 1 COMMENT '완성된 봉 여부',
UNIQUE KEY uk_code_time_tf (code, candle_time, timeframe),
INDEX idx_code_tf (code, timeframe),
INDEX idx_time (candle_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='업비트 분봉 OHLCV (백테스트용)'
""",
]
# env_config 초기 기본값 INSERT (이미 row가 있으면 스킵)
ENV_CONFIG_DEFAULT_INSERT = """
INSERT IGNORE INTO env_config (id, created_at) VALUES (1, NOW())
"""
def run_init():
logger.info("📦 upbit_quant_db 테이블 초기화 시작...")
try:
conn = pymysql.connect(**DB_CFG)
except Exception as e:
logger.error(f"❌ DB 연결 실패: {e}")
sys.exit(1)
try:
with conn.cursor() as cur:
for sql in DDL_STATEMENTS:
table_name = sql.strip().split("TABLE IF NOT EXISTS")[1].split("(")[0].strip()
cur.execute(sql)
logger.info(f" ✅ 테이블 생성/확인: {table_name}")
# env_config 초기 row 삽입 (최초 1회)
cur.execute("SELECT COUNT(*) as cnt FROM env_config")
if cur.fetchone()["cnt"] == 0:
cur.execute(ENV_CONFIG_DEFAULT_INSERT)
logger.info(" ✅ env_config 초기값 row 삽입 완료")
else:
logger.info(" env_config row 이미 존재 — 스킵")
# kv_store 기본 키 삽입
kv_defaults = [
("UPBIT_ACCESS_KEY", ""),
("UPBIT_SECRET_KEY", ""),
("UPBIT_SCAN_INTERVAL_SEC", "60"),
("UPBIT_BUY_TOP_N", "2"),
]
for k, v in kv_defaults:
cur.execute(
"INSERT IGNORE INTO kv_store (k, v) VALUES (%s, %s)", (k, v)
)
logger.info(" ✅ kv_store 기본 키 삽입 완료")
conn.commit()
logger.info("🎉 upbit_quant_db 초기화 완료!")
except Exception as e:
logger.error(f"❌ 초기화 중 오류: {e}")
import traceback; traceback.print_exc()
sys.exit(1)
finally:
conn.close()
if __name__ == "__main__":
run_init()