feat: add --symbol/--all CLI to all training scripts for per-symbol pipeline
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +1,57 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 맥미니에서 학습한 모델을 LXC 컨테이너 볼륨 경로로 전송한다.
|
# 맥미니에서 학습한 모델을 LXC 컨테이너 볼륨 경로로 전송한다.
|
||||||
# 사용법: bash scripts/deploy_model.sh [lgbm|mlx]
|
# 사용법: bash scripts/deploy_model.sh [lgbm|mlx] [--symbol TRXUSDT]
|
||||||
#
|
#
|
||||||
# 예시:
|
# 예시:
|
||||||
# bash scripts/deploy_model.sh # LightGBM (기본값)
|
# bash scripts/deploy_model.sh # LightGBM (기본값), models/ 루트
|
||||||
# bash scripts/deploy_model.sh mlx # MLX 신경망
|
# bash scripts/deploy_model.sh mlx # MLX 신경망, models/ 루트
|
||||||
|
# bash scripts/deploy_model.sh --symbol TRXUSDT # LightGBM, models/trxusdt/
|
||||||
|
# bash scripts/deploy_model.sh mlx --symbol XRPUSDT # MLX, models/xrpusdt/
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BACKEND="${1:-lgbm}"
|
# ── 인자 파싱 ────────────────────────────────────────────────────────────────
|
||||||
|
BACKEND="lgbm"
|
||||||
|
SYMBOL_ARG=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--symbol)
|
||||||
|
SYMBOL_ARG="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
mlx|lgbm)
|
||||||
|
BACKEND="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
LXC_HOST="root@10.1.10.24"
|
LXC_HOST="root@10.1.10.24"
|
||||||
LXC_MODELS_PATH="/root/cointrader/models"
|
LXC_MODELS_PATH="/root/cointrader/models"
|
||||||
LOCAL_LOG="models/training_log.json"
|
|
||||||
|
# ── 심볼별 경로 결정 ─────────────────────────────────────────────────────────
|
||||||
|
if [ -n "$SYMBOL_ARG" ]; then
|
||||||
|
SYM_LOWER=$(echo "$SYMBOL_ARG" | tr '[:upper:]' '[:lower:]')
|
||||||
|
LOCAL_MODEL_DIR="models/$SYM_LOWER"
|
||||||
|
REMOTE_MODEL_DIR="$LXC_MODELS_PATH/$SYM_LOWER"
|
||||||
|
LOCAL_LOG="models/$SYM_LOWER/training_log.json"
|
||||||
|
else
|
||||||
|
LOCAL_MODEL_DIR="models"
|
||||||
|
REMOTE_MODEL_DIR="$LXC_MODELS_PATH"
|
||||||
|
LOCAL_LOG="models/training_log.json"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── 백엔드별 파일 목록 설정 ──────────────────────────────────────────────────
|
# ── 백엔드별 파일 목록 설정 ──────────────────────────────────────────────────
|
||||||
# mlx: ONNX 파일만 전송 (Linux 서버는 onnxruntime으로 추론)
|
# mlx: ONNX 파일만 전송 (Linux 서버는 onnxruntime으로 추론)
|
||||||
# lgbm: pkl 파일 전송
|
# lgbm: pkl 파일 전송
|
||||||
if [ "$BACKEND" = "mlx" ]; then
|
if [ "$BACKEND" = "mlx" ]; then
|
||||||
LOCAL_FILES=("models/mlx_filter.weights.onnx")
|
LOCAL_FILES=("$LOCAL_MODEL_DIR/mlx_filter.weights.onnx")
|
||||||
else
|
else
|
||||||
LOCAL_FILES=("models/lgbm_filter.pkl")
|
LOCAL_FILES=("$LOCAL_MODEL_DIR/lgbm_filter.pkl")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 파일 존재 확인 ────────────────────────────────────────────────────────────
|
# ── 파일 존재 확인 ────────────────────────────────────────────────────────────
|
||||||
@@ -30,26 +62,26 @@ for f in "${LOCAL_FILES[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "=== 모델 전송 시작 (백엔드: ${BACKEND}) ==="
|
echo "=== 모델 전송 시작 (백엔드: ${BACKEND}${SYMBOL_ARG:+, 심볼: $SYMBOL_ARG}) ==="
|
||||||
echo " 대상: ${LXC_HOST}:${LXC_MODELS_PATH}"
|
echo " 대상: ${LXC_HOST}:${REMOTE_MODEL_DIR}"
|
||||||
|
|
||||||
# ── 원격 디렉터리 생성 + 백업 + 상대 백엔드 파일 제거 ───────────────────────
|
# ── 원격 디렉터리 생성 + 백업 + 상대 백엔드 파일 제거 ───────────────────────
|
||||||
# lgbm 배포 시: 기존 lgbm 백업 후 ONNX 파일 삭제 (ONNX 우선순위 때문에 lgbm이 무시되는 것 방지)
|
# lgbm 배포 시: 기존 lgbm 백업 후 ONNX 파일 삭제 (ONNX 우선순위 때문에 lgbm이 무시되는 것 방지)
|
||||||
# mlx 배포 시: lgbm 파일 삭제 (명시적으로 mlx만 사용)
|
# mlx 배포 시: lgbm 파일 삭제 (명시적으로 mlx만 사용)
|
||||||
ssh "${LXC_HOST}" "
|
ssh "${LXC_HOST}" "
|
||||||
mkdir -p '${LXC_MODELS_PATH}'
|
mkdir -p '${REMOTE_MODEL_DIR}'
|
||||||
if [ '$BACKEND' = 'lgbm' ]; then
|
if [ '$BACKEND' = 'lgbm' ]; then
|
||||||
if [ -f '${LXC_MODELS_PATH}/lgbm_filter.pkl' ]; then
|
if [ -f '${REMOTE_MODEL_DIR}/lgbm_filter.pkl' ]; then
|
||||||
cp '${LXC_MODELS_PATH}/lgbm_filter.pkl' '${LXC_MODELS_PATH}/lgbm_filter_prev.pkl'
|
cp '${REMOTE_MODEL_DIR}/lgbm_filter.pkl' '${REMOTE_MODEL_DIR}/lgbm_filter_prev.pkl'
|
||||||
echo ' 기존 lgbm 모델 백업 완료'
|
echo ' 기존 lgbm 모델 백업 완료'
|
||||||
fi
|
fi
|
||||||
if [ -f '${LXC_MODELS_PATH}/mlx_filter.weights.onnx' ]; then
|
if [ -f '${REMOTE_MODEL_DIR}/mlx_filter.weights.onnx' ]; then
|
||||||
rm '${LXC_MODELS_PATH}/mlx_filter.weights.onnx'
|
rm '${REMOTE_MODEL_DIR}/mlx_filter.weights.onnx'
|
||||||
echo ' ONNX 파일 제거 완료 (lgbm 우선 적용)'
|
echo ' ONNX 파일 제거 완료 (lgbm 우선 적용)'
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ -f '${LXC_MODELS_PATH}/lgbm_filter.pkl' ]; then
|
if [ -f '${REMOTE_MODEL_DIR}/lgbm_filter.pkl' ]; then
|
||||||
rm '${LXC_MODELS_PATH}/lgbm_filter.pkl'
|
rm '${REMOTE_MODEL_DIR}/lgbm_filter.pkl'
|
||||||
echo ' lgbm 파일 제거 완료 (mlx 우선 적용)'
|
echo ' lgbm 파일 제거 완료 (mlx 우선 적용)'
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -68,12 +100,12 @@ _send() {
|
|||||||
|
|
||||||
# ── 모델 파일 전송 ────────────────────────────────────────────────────────────
|
# ── 모델 파일 전송 ────────────────────────────────────────────────────────────
|
||||||
for f in "${LOCAL_FILES[@]}"; do
|
for f in "${LOCAL_FILES[@]}"; do
|
||||||
_send "$f" "${LXC_MODELS_PATH}/$(basename "$f")"
|
_send "$f" "${REMOTE_MODEL_DIR}/$(basename "$f")"
|
||||||
done
|
done
|
||||||
|
|
||||||
# ── 학습 로그 전송 ────────────────────────────────────────────────────────────
|
# ── 학습 로그 전송 ────────────────────────────────────────────────────────────
|
||||||
if [[ -f "$LOCAL_LOG" ]]; then
|
if [[ -f "$LOCAL_LOG" ]]; then
|
||||||
_send "$LOCAL_LOG" "${LXC_MODELS_PATH}/training_log.json"
|
_send "$LOCAL_LOG" "${REMOTE_MODEL_DIR}/training_log.json"
|
||||||
echo " 학습 로그 전송 완료"
|
echo " 학습 로그 전송 완료"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -333,9 +333,22 @@ def main():
|
|||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# 하위 호환: --symbol 단독 사용 시 symbols로 통합
|
# --symbol 모드: 단일 거래 심볼 + 상관관계 심볼 자동 추가, 출력 경로 자동 결정
|
||||||
if args.symbol and args.symbols == ["XRPUSDT"]:
|
if args.symbol:
|
||||||
args.symbols = [args.symbol]
|
from src.config import Config
|
||||||
|
try:
|
||||||
|
cfg = Config()
|
||||||
|
corr_symbols = cfg.correlation_symbols
|
||||||
|
except Exception:
|
||||||
|
corr_symbols = ["BTCUSDT", "ETHUSDT"]
|
||||||
|
args.symbols = [args.symbol] + corr_symbols
|
||||||
|
if args.output == "data/combined_15m.parquet":
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
os.makedirs(f"data/{sym_lower}", exist_ok=True)
|
||||||
|
args.output = f"data/{sym_lower}/combined_15m.parquet"
|
||||||
|
# 하위 호환: 단일 심볼만 지정된 경우
|
||||||
|
elif args.symbols == ["XRPUSDT"] and not args.symbol:
|
||||||
|
pass # 기본값 유지
|
||||||
|
|
||||||
if len(args.symbols) == 1:
|
if len(args.symbols) == 1:
|
||||||
df = asyncio.run(fetch_klines(args.symbols[0], args.interval, args.days))
|
df = asyncio.run(fetch_klines(args.symbols[0], args.interval, args.days))
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 맥미니에서 전체 학습 파이프라인을 실행하고 LXC로 배포한다.
|
# 맥미니에서 전체 학습 파이프라인을 실행하고 LXC로 배포한다.
|
||||||
# 사용법: bash scripts/train_and_deploy.sh [mlx|lgbm] [wf-splits]
|
# 사용법: bash scripts/train_and_deploy.sh [mlx|lgbm] [--symbol TRXUSDT] [--all] [wf-splits]
|
||||||
#
|
#
|
||||||
# 예시:
|
# 예시:
|
||||||
# bash scripts/train_and_deploy.sh # LightGBM + Walk-Forward 5폴드 (기본값)
|
# bash scripts/train_and_deploy.sh # 전체 심볼 (SYMBOLS 환경변수) + LightGBM
|
||||||
# bash scripts/train_and_deploy.sh mlx # MLX GPU 학습 + Walk-Forward 5폴드
|
# bash scripts/train_and_deploy.sh --symbol TRXUSDT # TRXUSDT만 학습+배포
|
||||||
# bash scripts/train_and_deploy.sh lgbm 3 # LightGBM + Walk-Forward 3폴드
|
# bash scripts/train_and_deploy.sh mlx --symbol TRXUSDT # MLX + TRXUSDT만
|
||||||
# bash scripts/train_and_deploy.sh mlx 0 # MLX 학습만 (Walk-Forward 건너뜀)
|
# bash scripts/train_and_deploy.sh --all # 전체 심볼 순차 처리
|
||||||
# bash scripts/train_and_deploy.sh lgbm 0 # LightGBM 학습만 (Walk-Forward 건너뜀)
|
# bash scripts/train_and_deploy.sh lgbm 3 # 전체 심볼 + Walk-Forward 3폴드
|
||||||
|
# bash scripts/train_and_deploy.sh mlx 0 # 전체 심볼 + MLX 학습만 (WF 건너뜀)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -26,77 +27,132 @@ else
|
|||||||
echo "경고: 가상환경을 찾을 수 없습니다 ($VENV_PATH). 시스템 Python을 사용합니다." >&2
|
echo "경고: 가상환경을 찾을 수 없습니다 ($VENV_PATH). 시스템 Python을 사용합니다." >&2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BACKEND="${1:-lgbm}"
|
|
||||||
WF_SPLITS="${2:-5}" # 두 번째 인자: Walk-Forward 폴드 수 (0이면 건너뜀)
|
|
||||||
|
|
||||||
cd "$PROJECT_ROOT"
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
mkdir -p data
|
# ── 인자 파싱 ───────────────────────────────────────────────────────────────
|
||||||
|
BACKEND="lgbm"
|
||||||
|
WF_SPLITS="5"
|
||||||
|
SYMBOL_ARG=""
|
||||||
|
ALL_FLAG=false
|
||||||
|
|
||||||
PARQUET_FILE="data/combined_15m.parquet"
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--symbol)
|
||||||
|
SYMBOL_ARG="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--all)
|
||||||
|
ALL_FLAG=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
mlx|lgbm)
|
||||||
|
BACKEND="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# 숫자면 WF_SPLITS로 처리
|
||||||
|
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||||
|
WF_SPLITS="$1"
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
echo ""
|
# ── 대상 심볼 결정 ──────────────────────────────────────────────────────────
|
||||||
echo "========================================"
|
if [ -n "$SYMBOL_ARG" ]; then
|
||||||
echo " 학습 파이프라인 시작: $(date '+%Y-%m-%d %H:%M:%S %Z')"
|
TARGETS=("$SYMBOL_ARG")
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
echo "=== [1/3] 데이터 수집 (XRP + BTC + ETH 3심볼 + OI/펀딩비) ==="
|
|
||||||
if [ ! -f "$PARQUET_FILE" ]; then
|
|
||||||
echo " [최초 실행] 기존 데이터 없음 → 1년치(365일) 전체 수집 (--no-upsert)"
|
|
||||||
FETCH_DAYS=365
|
|
||||||
UPSERT_FLAG="--no-upsert"
|
|
||||||
else
|
else
|
||||||
echo " [일반 실행] 기존 데이터 존재 → 35일치 Upsert (OI/펀딩비 0.0 구간 보충)"
|
# .env에서 SYMBOLS 로드 (없으면 XRPUSDT 기본값)
|
||||||
FETCH_DAYS=35
|
TARGETS=($(python -c "from dotenv import load_dotenv; load_dotenv(); from src.config import Config; c=Config(); print(' '.join(c.symbols))"))
|
||||||
UPSERT_FLAG=""
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
python scripts/fetch_history.py \
|
|
||||||
--symbols XRPUSDT BTCUSDT ETHUSDT \
|
|
||||||
--interval 15m \
|
|
||||||
--days "$FETCH_DAYS" \
|
|
||||||
$UPSERT_FLAG \
|
|
||||||
--output "$PARQUET_FILE"
|
|
||||||
|
|
||||||
DECAY="${TIME_WEIGHT_DECAY:-2.0}"
|
DECAY="${TIME_WEIGHT_DECAY:-2.0}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== [1.5/3] OI 파생 피처 A/B 비교 ==="
|
echo "========================================"
|
||||||
python scripts/train_model.py --compare --data "$PARQUET_FILE" --decay "$DECAY" || true
|
echo " 학습 파이프라인 시작: $(date '+%Y-%m-%d %H:%M:%S %Z')"
|
||||||
|
echo " 대상 심볼: ${TARGETS[*]}"
|
||||||
|
echo " 백엔드: ${BACKEND}, WF 폴드: ${WF_SPLITS}"
|
||||||
|
echo "========================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== [2/3] 모델 학습 (26개 피처: XRP 13 + BTC/ETH 8 + OI/펀딩비 2 + OI파생 2 + ADX) ==="
|
|
||||||
if [ "$BACKEND" = "mlx" ]; then
|
|
||||||
echo " 백엔드: MLX (Apple Silicon GPU), decay=${DECAY}"
|
|
||||||
python scripts/train_mlx_model.py --data data/combined_15m.parquet --decay "$DECAY"
|
|
||||||
else
|
|
||||||
echo " 백엔드: LightGBM (CPU), decay=${DECAY}"
|
|
||||||
python scripts/train_model.py --data data/combined_15m.parquet --decay "$DECAY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Walk-Forward 검증 (WF_SPLITS > 0 인 경우)
|
# ── 심볼별 파이프라인 ───────────────────────────────────────────────────────
|
||||||
if [ "$WF_SPLITS" -gt 0 ] 2>/dev/null; then
|
for SYM in "${TARGETS[@]}"; do
|
||||||
|
SYM_LOWER=$(echo "$SYM" | tr '[:upper:]' '[:lower:]')
|
||||||
|
mkdir -p "data/$SYM_LOWER" "models/$SYM_LOWER"
|
||||||
|
|
||||||
|
PARQUET_FILE="data/$SYM_LOWER/combined_15m.parquet"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== [2.5/3] Walk-Forward 검증 (${WF_SPLITS}폴드) ==="
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " [$SYM] 파이프라인 시작"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
|
||||||
|
# === [1/3] 데이터 수집 ===
|
||||||
|
echo ""
|
||||||
|
echo "=== [$SYM] [1/3] 데이터 수집 (+ BTC/ETH 상관관계 + OI/펀딩비) ==="
|
||||||
|
if [ ! -f "$PARQUET_FILE" ]; then
|
||||||
|
echo " [최초 실행] 기존 데이터 없음 → 1년치(365일) 전체 수집 (--no-upsert)"
|
||||||
|
FETCH_DAYS=365
|
||||||
|
UPSERT_FLAG="--no-upsert"
|
||||||
|
else
|
||||||
|
echo " [일반 실행] 기존 데이터 존재 → 35일치 Upsert (OI/펀딩비 0.0 구간 보충)"
|
||||||
|
FETCH_DAYS=35
|
||||||
|
UPSERT_FLAG=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
python scripts/fetch_history.py \
|
||||||
|
--symbol "$SYM" \
|
||||||
|
--interval 15m \
|
||||||
|
--days "$FETCH_DAYS" \
|
||||||
|
$UPSERT_FLAG
|
||||||
|
|
||||||
|
# === [1.5/3] OI 파생 피처 A/B 비교 ===
|
||||||
|
echo ""
|
||||||
|
echo "=== [$SYM] [1.5/3] OI 파생 피처 A/B 비교 ==="
|
||||||
|
python scripts/train_model.py --compare --symbol "$SYM" --decay "$DECAY" || true
|
||||||
|
|
||||||
|
# === [2/3] 모델 학습 ===
|
||||||
|
echo ""
|
||||||
|
echo "=== [$SYM] [2/3] 모델 학습 ==="
|
||||||
|
if [ "$BACKEND" = "mlx" ]; then
|
||||||
|
echo " 백엔드: MLX (Apple Silicon GPU), decay=${DECAY}"
|
||||||
|
python scripts/train_mlx_model.py --data "$PARQUET_FILE" --decay "$DECAY"
|
||||||
|
else
|
||||||
|
echo " 백엔드: LightGBM (CPU), decay=${DECAY}"
|
||||||
|
python scripts/train_model.py --symbol "$SYM" --decay "$DECAY"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Walk-Forward 검증 (WF_SPLITS > 0 인 경우)
|
||||||
|
if [ "$WF_SPLITS" -gt 0 ] 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
echo "=== [$SYM] [2.5/3] Walk-Forward 검증 (${WF_SPLITS}폴드) ==="
|
||||||
if [ "$BACKEND" = "mlx" ]; then
|
if [ "$BACKEND" = "mlx" ]; then
|
||||||
python scripts/train_mlx_model.py \
|
python scripts/train_mlx_model.py \
|
||||||
--data data/combined_15m.parquet \
|
--data "$PARQUET_FILE" \
|
||||||
--decay "$DECAY" \
|
--decay "$DECAY" \
|
||||||
--wf \
|
--wf \
|
||||||
--wf-splits "$WF_SPLITS"
|
--wf-splits "$WF_SPLITS"
|
||||||
else
|
else
|
||||||
python scripts/train_model.py \
|
python scripts/train_model.py \
|
||||||
--data data/combined_15m.parquet \
|
--symbol "$SYM" \
|
||||||
--decay "$DECAY" \
|
--decay "$DECAY" \
|
||||||
--wf \
|
--wf \
|
||||||
--wf-splits "$WF_SPLITS"
|
--wf-splits "$WF_SPLITS"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
# === [3/3] 배포 ===
|
||||||
echo "=== [3/3] LXC 배포 ==="
|
echo ""
|
||||||
bash scripts/deploy_model.sh "$BACKEND"
|
echo "=== [$SYM] [3/3] LXC 배포 ==="
|
||||||
|
bash scripts/deploy_model.sh "$BACKEND" --symbol "$SYM"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo " [$SYM] 파이프라인 완료"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
done
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== 전체 파이프라인 완료: $(date '+%Y-%m-%d %H:%M:%S %Z') ==="
|
echo "=== 전체 파이프라인 완료: $(date '+%Y-%m-%d %H:%M:%S %Z') ==="
|
||||||
|
|||||||
@@ -531,7 +531,9 @@ def compare(data_path: str, time_weight_decay: float = 2.0, tuned_params_path: s
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--data", default="data/combined_15m.parquet")
|
parser.add_argument("--data", default=None)
|
||||||
|
parser.add_argument("--symbol", type=str, default=None,
|
||||||
|
help="학습 대상 심볼 (예: TRXUSDT). data/{symbol}/ 에서 데이터 로드, models/{symbol}/ 에 저장")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--decay", type=float, default=2.0,
|
"--decay", type=float, default=2.0,
|
||||||
help="시간 가중치 감쇠 강도 (0=균등, 2.0=최신이 ~7.4배 높음)",
|
help="시간 가중치 감쇠 강도 (0=균등, 2.0=최신이 ~7.4배 높음)",
|
||||||
@@ -546,6 +548,20 @@ def main():
|
|||||||
help="OI 파생 피처 추가 전후 A/B 성능 비교")
|
help="OI 파생 피처 추가 전후 A/B 성능 비교")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# --symbol 모드: 심볼별 디렉토리 경로 자동 결정
|
||||||
|
if args.symbol:
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
if args.data is None:
|
||||||
|
args.data = f"data/{sym_lower}/combined_15m.parquet"
|
||||||
|
global MODEL_PATH, PREV_MODEL_PATH, LOG_PATH, ACTIVE_PARAMS_PATH
|
||||||
|
MODEL_PATH = Path(f"models/{sym_lower}/lgbm_filter.pkl")
|
||||||
|
PREV_MODEL_PATH = Path(f"models/{sym_lower}/lgbm_filter_prev.pkl")
|
||||||
|
LOG_PATH = Path(f"models/{sym_lower}/training_log.json")
|
||||||
|
ACTIVE_PARAMS_PATH = Path(f"models/{sym_lower}/active_lgbm_params.json")
|
||||||
|
MODEL_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
elif args.data is None:
|
||||||
|
args.data = "data/combined_15m.parquet"
|
||||||
|
|
||||||
if args.compare:
|
if args.compare:
|
||||||
compare(args.data, time_weight_decay=args.decay, tuned_params_path=args.tuned_params)
|
compare(args.data, time_weight_decay=args.decay, tuned_params_path=args.tuned_params)
|
||||||
elif args.wf:
|
elif args.wf:
|
||||||
|
|||||||
@@ -308,9 +308,10 @@ def measure_baseline(
|
|||||||
n_splits: int,
|
n_splits: int,
|
||||||
train_ratio: float,
|
train_ratio: float,
|
||||||
min_recall: float = 0.35,
|
min_recall: float = 0.35,
|
||||||
|
active_params_path: "Path | None" = None,
|
||||||
) -> tuple[float, dict]:
|
) -> tuple[float, dict]:
|
||||||
"""현재 실전 파라미터(active 파일 또는 하드코딩 기본값)로 베이스라인을 측정한다."""
|
"""현재 실전 파라미터(active 파일 또는 하드코딩 기본값)로 베이스라인을 측정한다."""
|
||||||
active_path = Path("models/active_lgbm_params.json")
|
active_path = active_params_path or Path("models/active_lgbm_params.json")
|
||||||
|
|
||||||
if active_path.exists():
|
if active_path.exists():
|
||||||
with open(active_path, "r", encoding="utf-8") as f:
|
with open(active_path, "r", encoding="utf-8") as f:
|
||||||
@@ -518,7 +519,9 @@ def save_results(
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Optuna LightGBM 하이퍼파라미터 튜닝")
|
parser = argparse.ArgumentParser(description="Optuna LightGBM 하이퍼파라미터 튜닝")
|
||||||
parser.add_argument("--data", default="data/combined_15m.parquet", help="학습 데이터 경로")
|
parser.add_argument("--data", default=None, help="학습 데이터 경로")
|
||||||
|
parser.add_argument("--symbol", type=str, default=None,
|
||||||
|
help="튜닝 대상 심볼 (예: TRXUSDT). data/{symbol}/ 에서 데이터 로드, models/{symbol}/ 에 저장")
|
||||||
parser.add_argument("--trials", type=int, default=50, help="Optuna trial 수 (기본: 50)")
|
parser.add_argument("--trials", type=int, default=50, help="Optuna trial 수 (기본: 50)")
|
||||||
parser.add_argument("--folds", type=int, default=5, help="Walk-Forward 폴드 수 (기본: 5)")
|
parser.add_argument("--folds", type=int, default=5, help="Walk-Forward 폴드 수 (기본: 5)")
|
||||||
parser.add_argument("--train-ratio", type=float, default=0.6, help="학습 구간 비율 (기본: 0.6)")
|
parser.add_argument("--train-ratio", type=float, default=0.6, help="학습 구간 비율 (기본: 0.6)")
|
||||||
@@ -526,16 +529,31 @@ def main():
|
|||||||
parser.add_argument("--no-baseline", action="store_true", help="베이스라인 측정 건너뜀")
|
parser.add_argument("--no-baseline", action="store_true", help="베이스라인 측정 건너뜀")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# --symbol 모드: 심볼별 디렉토리 경로 자동 결정
|
||||||
|
if args.symbol:
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
if args.data is None:
|
||||||
|
args.data = f"data/{sym_lower}/combined_15m.parquet"
|
||||||
|
elif args.data is None:
|
||||||
|
args.data = "data/combined_15m.parquet"
|
||||||
|
|
||||||
# 1. 데이터셋 로드 (1회)
|
# 1. 데이터셋 로드 (1회)
|
||||||
X, y, w, source = load_dataset(args.data)
|
X, y, w, source = load_dataset(args.data)
|
||||||
|
|
||||||
# 2. 베이스라인 측정
|
# 2. 베이스라인 측정
|
||||||
|
if args.symbol:
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
_active_params_path = Path(f"models/{sym_lower}/active_lgbm_params.json")
|
||||||
|
else:
|
||||||
|
_active_params_path = None
|
||||||
|
|
||||||
if args.no_baseline:
|
if args.no_baseline:
|
||||||
baseline_score, baseline_details = 0.0, {}
|
baseline_score, baseline_details = 0.0, {}
|
||||||
print("베이스라인 측정 건너뜀 (--no-baseline)\n")
|
print("베이스라인 측정 건너뜀 (--no-baseline)\n")
|
||||||
else:
|
else:
|
||||||
baseline_score, baseline_details = measure_baseline(
|
baseline_score, baseline_details = measure_baseline(
|
||||||
X, y, w, source, args.folds, args.train_ratio, args.min_recall,
|
X, y, w, source, args.folds, args.train_ratio, args.min_recall,
|
||||||
|
active_params_path=_active_params_path,
|
||||||
)
|
)
|
||||||
bl_prec = baseline_details.get("mean_precision", 0.0)
|
bl_prec = baseline_details.get("mean_precision", 0.0)
|
||||||
bl_auc = baseline_details.get("mean_auc", 0.0)
|
bl_auc = baseline_details.get("mean_auc", 0.0)
|
||||||
@@ -593,15 +611,27 @@ def main():
|
|||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
|
|
||||||
# 4. 결과 저장 및 출력
|
# 4. 결과 저장 및 출력
|
||||||
|
import shutil
|
||||||
output_path = save_results(
|
output_path = save_results(
|
||||||
study, baseline_score, baseline_details, elapsed, args.data, args.min_recall,
|
study, baseline_score, baseline_details, elapsed, args.data, args.min_recall,
|
||||||
)
|
)
|
||||||
|
# --symbol 모드: 결과 파일을 심볼별 디렉토리로 이동
|
||||||
|
if args.symbol:
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
sym_model_dir = Path(f"models/{sym_lower}")
|
||||||
|
sym_model_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
dest = sym_model_dir / output_path.name
|
||||||
|
shutil.move(str(output_path), str(dest))
|
||||||
|
output_path = dest
|
||||||
print_report(
|
print_report(
|
||||||
study, baseline_score, baseline_details, elapsed, output_path, args.min_recall,
|
study, baseline_score, baseline_details, elapsed, output_path, args.min_recall,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 5. 성능 개선 시 active 파일 자동 갱신
|
# 5. 성능 개선 시 active 파일 자동 갱신
|
||||||
import shutil
|
if args.symbol:
|
||||||
|
sym_lower = args.symbol.lower()
|
||||||
|
active_path = Path(f"models/{sym_lower}/active_lgbm_params.json")
|
||||||
|
else:
|
||||||
active_path = Path("models/active_lgbm_params.json")
|
active_path = Path("models/active_lgbm_params.json")
|
||||||
if not args.no_baseline and study.best_value > baseline_score:
|
if not args.no_baseline and study.best_value > baseline_score:
|
||||||
shutil.copy(output_path, active_path)
|
shutil.copy(output_path, active_path)
|
||||||
|
|||||||
Reference in New Issue
Block a user