feat: implement ML filter with LightGBM for trading signal validation
- Added MLFilter class to load and evaluate LightGBM model for trading signals. - Introduced retraining mechanism to update the model daily based on new data. - Created feature engineering and label building utilities for model training. - Updated bot logic to incorporate ML filter for signal validation. - Added scripts for data fetching and model training. Made-with: Cursor
This commit is contained in:
282
docs/plans/2026-03-01-dockerfile-and-docker-compose.md
Normal file
282
docs/plans/2026-03-01-dockerfile-and-docker-compose.md
Normal file
@@ -0,0 +1,282 @@
|
||||
# Dockerfile & docker-compose.yml 작성 및 Gitea 업로드 구현 계획
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** cointrader 프로젝트에 Dockerfile과 docker-compose.yml을 추가하고, 변경사항을 커밋하여 Gitea(10.1.10.28:3000)에 push한다.
|
||||
|
||||
**Architecture:** Python 3.11 slim 이미지 기반의 멀티스테이지 없는 단일 Dockerfile을 작성하고, docker-compose.yml로 환경변수(.env)를 주입하여 컨테이너를 실행한다. 로그 디렉토리는 볼륨으로 마운트하여 컨테이너 재시작 시에도 보존한다.
|
||||
|
||||
**Tech Stack:** Docker, docker-compose v2, Python 3.11-slim, python-dotenv
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Dockerfile 작성
|
||||
|
||||
**Files:**
|
||||
- Create: `Dockerfile`
|
||||
|
||||
**Step 1: Dockerfile 생성**
|
||||
|
||||
`/Users/gihyeon/github/cointrader/Dockerfile` 파일을 아래 내용으로 생성한다:
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN mkdir -p logs
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
CMD ["python", "main.py"]
|
||||
```
|
||||
|
||||
**Step 2: Dockerfile 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/Dockerfile
|
||||
```
|
||||
|
||||
Expected: 위 내용이 그대로 출력됨
|
||||
|
||||
**Step 3: Docker 빌드 테스트 (Docker가 설치된 경우)**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
docker build -t cointrader:test .
|
||||
```
|
||||
|
||||
Expected: `Successfully built <image_id>` 또는 `Successfully tagged cointrader:test`
|
||||
|
||||
> Docker가 설치되지 않은 환경이라면 이 단계는 건너뛴다.
|
||||
|
||||
---
|
||||
|
||||
## Task 2: docker-compose.yml 작성
|
||||
|
||||
**Files:**
|
||||
- Create: `docker-compose.yml`
|
||||
|
||||
**Step 1: docker-compose.yml 생성**
|
||||
|
||||
`/Users/gihyeon/github/cointrader/docker-compose.yml` 파일을 아래 내용으로 생성한다:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
cointrader:
|
||||
build: .
|
||||
container_name: cointrader
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
```
|
||||
|
||||
**Step 2: docker-compose.yml 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/docker-compose.yml
|
||||
```
|
||||
|
||||
Expected: 위 내용이 그대로 출력됨
|
||||
|
||||
**Step 3: docker-compose 문법 검증 (docker compose가 설치된 경우)**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
docker compose config
|
||||
```
|
||||
|
||||
Expected: 파싱된 YAML 설정이 오류 없이 출력됨
|
||||
|
||||
---
|
||||
|
||||
## Task 3: .dockerignore 작성
|
||||
|
||||
**Files:**
|
||||
- Create: `.dockerignore`
|
||||
|
||||
**Step 1: .dockerignore 생성**
|
||||
|
||||
`/Users/gihyeon/github/cointrader/.dockerignore` 파일을 아래 내용으로 생성한다:
|
||||
|
||||
```
|
||||
.env
|
||||
.venv
|
||||
__pycache__
|
||||
*.pyc
|
||||
*.pyo
|
||||
.pytest_cache
|
||||
logs/
|
||||
*.log
|
||||
.git
|
||||
docs/
|
||||
tests/
|
||||
```
|
||||
|
||||
> `.env`를 반드시 포함시켜 빌드 컨텍스트에서 제외한다. 이미지에 API 키가 포함되는 것을 방지한다.
|
||||
|
||||
**Step 2: .dockerignore 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/.dockerignore
|
||||
```
|
||||
|
||||
Expected: 위 내용이 그대로 출력됨
|
||||
|
||||
---
|
||||
|
||||
## Task 4: git 커밋
|
||||
|
||||
**Files:**
|
||||
- Modify: `Dockerfile` (신규)
|
||||
- Modify: `docker-compose.yml` (신규)
|
||||
- Modify: `.dockerignore` (신규)
|
||||
|
||||
**Step 1: git 상태 확인**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
git status
|
||||
```
|
||||
|
||||
Expected: `Dockerfile`, `docker-compose.yml`, `.dockerignore`가 untracked files로 표시됨
|
||||
|
||||
**Step 2: 스테이징**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
git add Dockerfile docker-compose.yml .dockerignore
|
||||
```
|
||||
|
||||
**Step 3: 스테이징 내용 검토 (`.env` 포함 여부 확인)**
|
||||
|
||||
```bash
|
||||
git diff --cached --name-only
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
docker-compose.yml
|
||||
```
|
||||
|
||||
`.env`가 목록에 **없어야** 한다. 만약 있다면 즉시 `git reset HEAD .env` 실행 후 중단.
|
||||
|
||||
**Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git commit -m "chore: add Dockerfile, docker-compose.yml, .dockerignore"
|
||||
```
|
||||
|
||||
Expected: `main` 브랜치에 새 커밋 생성
|
||||
|
||||
**Step 5: 커밋 확인**
|
||||
|
||||
```bash
|
||||
git log --oneline -3
|
||||
```
|
||||
|
||||
Expected: 방금 만든 커밋이 최상단에 표시됨
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Gitea push
|
||||
|
||||
> 이 Task는 Gitea 원격 저장소가 이미 설정되어 있다고 가정한다.
|
||||
> 아직 설정하지 않았다면 `docs/plans/2026-03-01-upload-to-gitea.md`의 Task 2~3을 먼저 완료한다.
|
||||
|
||||
**Step 1: 현재 원격 저장소 확인**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
git remote -v
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
origin http://10.1.10.28:3000/<사용자명>/cointrader.git (fetch)
|
||||
origin http://10.1.10.28:3000/<사용자명>/cointrader.git (push)
|
||||
```
|
||||
|
||||
origin이 없다면 아래 명령으로 추가 (`<사용자명>` 교체 필요):
|
||||
```bash
|
||||
git remote add origin http://10.1.10.28:3000/<사용자명>/cointrader.git
|
||||
```
|
||||
|
||||
**Step 2: push**
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
> Gitea 계정의 사용자명과 비밀번호(또는 액세스 토큰)를 입력하라는 프롬프트가 나타남
|
||||
|
||||
Expected:
|
||||
```
|
||||
Enumerating objects: ...
|
||||
Writing objects: 100% ...
|
||||
```
|
||||
|
||||
**Step 3: push 결과 확인**
|
||||
|
||||
```bash
|
||||
git log --oneline origin/main -3
|
||||
```
|
||||
|
||||
Expected: 로컬 커밋 히스토리와 동일하게 표시됨
|
||||
|
||||
**Step 4: Gitea 웹 UI에서 파일 확인**
|
||||
|
||||
브라우저에서 `http://10.1.10.28:3000/<사용자명>/cointrader` 접속 후 다음 파일이 있는지 확인:
|
||||
- `Dockerfile`
|
||||
- `docker-compose.yml`
|
||||
- `.dockerignore`
|
||||
|
||||
---
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
| 문제 | 원인 | 해결 |
|
||||
|------|------|------|
|
||||
| `docker build` 시 `gcc` 설치 실패 | 네트워크 문제 | `apt-get` 단계를 제거하고 빌드 재시도 (pandas-ta가 gcc 없이 설치되는지 확인) |
|
||||
| `docker compose config` 오류 | YAML 들여쓰기 오류 | 탭 대신 스페이스 2칸 사용 여부 확인 |
|
||||
| push 시 `Authentication failed` | 잘못된 계정 정보 | Gitea 웹 UI 로그인 테스트 후 동일 계정 사용 |
|
||||
| push 시 `non-fast-forward` | 원격에 이미 다른 커밋 존재 | `git pull --rebase origin main` 후 재시도 |
|
||||
| 컨테이너 실행 시 `.env` 없음 오류 | `.env` 파일 미생성 | `.env.example`을 복사하여 `.env` 생성 후 값 입력 |
|
||||
|
||||
---
|
||||
|
||||
## 참고: 컨테이너 실행 방법
|
||||
|
||||
```bash
|
||||
# .env 파일 준비
|
||||
cp .env.example .env
|
||||
# .env 파일에 실제 API 키와 Discord Webhook URL 입력
|
||||
|
||||
# 빌드 및 백그라운드 실행
|
||||
docker compose up -d --build
|
||||
|
||||
# 로그 확인
|
||||
docker compose logs -f
|
||||
|
||||
# 중지
|
||||
docker compose down
|
||||
```
|
||||
275
docs/plans/2026-03-01-fix-pandas-ta-python312.md
Normal file
275
docs/plans/2026-03-01-fix-pandas-ta-python312.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# pandas-ta Python 버전 호환성 수정 계획
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Jenkins CI에서 `pandas-ta==0.4.71b0`이 Python 3.11에서 설치 실패하는 문제를 해결한다.
|
||||
|
||||
**Architecture:** `pandas-ta==0.4.71b0`은 Python >=3.12를 요구하므로, Dockerfile의 베이스 이미지를 `python:3.11-slim`에서 `python:3.12-slim`으로 업그레이드한다. `requirements.txt`의 의존 패키지 버전도 Python 3.12와 호환되는 버전으로 정리한다.
|
||||
|
||||
**Tech Stack:** Docker, Python 3.12-slim, pandas-ta 0.4.71b0, python-binance 1.0.19
|
||||
|
||||
---
|
||||
|
||||
## 문제 분석
|
||||
|
||||
Jenkins 빌드 로그 오류:
|
||||
```
|
||||
ERROR: Ignored the following versions that require a different python version:
|
||||
0.4.67b0 Requires-Python >=3.12; 0.4.71b0 Requires-Python >=3.12
|
||||
ERROR: Could not find a version that satisfies the requirement pandas-ta==0.4.71b0
|
||||
```
|
||||
|
||||
**원인:** `requirements.txt`에 `pandas-ta==0.4.71b0`이 명시되어 있으나, Dockerfile 베이스 이미지가 `python:3.11-slim`이라 Python 3.12 이상을 요구하는 `pandas-ta`를 설치할 수 없다.
|
||||
|
||||
**해결 방향:** Dockerfile 베이스 이미지를 `python:3.12-slim`으로 변경한다.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Dockerfile 베이스 이미지 업그레이드
|
||||
|
||||
**Files:**
|
||||
- Modify: `Dockerfile:1`
|
||||
|
||||
**Step 1: Dockerfile 수정**
|
||||
|
||||
`Dockerfile` 1번째 줄을 다음과 같이 변경한다:
|
||||
|
||||
변경 전:
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
```
|
||||
|
||||
변경 후:
|
||||
```dockerfile
|
||||
FROM python:3.12-slim
|
||||
```
|
||||
|
||||
**Step 2: 변경 내용 확인**
|
||||
|
||||
```bash
|
||||
head -1 /Users/gihyeon/github/cointrader/Dockerfile
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
FROM python:3.12-slim
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: requirements.txt 의존성 호환성 검토 및 수정
|
||||
|
||||
**Files:**
|
||||
- Modify: `requirements.txt`
|
||||
|
||||
**Step 1: 현재 requirements.txt 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/requirements.txt
|
||||
```
|
||||
|
||||
Expected (현재 내용):
|
||||
```
|
||||
python-binance==1.0.19
|
||||
pandas>=2.2.0
|
||||
pandas-ta==0.4.71b0
|
||||
python-dotenv==1.0.0
|
||||
httpx>=0.27.0
|
||||
pytest>=8.1.0
|
||||
pytest-asyncio>=0.24.0
|
||||
aiohttp==3.9.3
|
||||
websockets==12.0
|
||||
loguru==0.7.2
|
||||
```
|
||||
|
||||
**Step 2: pandas-ta 0.4.71b0의 의존성 확인**
|
||||
|
||||
PyPI 정보에 따르면 `pandas-ta==0.4.71b0`은 다음을 요구한다:
|
||||
- `numba==0.61.2`
|
||||
- `numpy>=2.2.6`
|
||||
- `pandas>=2.3.2`
|
||||
|
||||
`requirements.txt`의 `pandas>=2.2.0`은 `pandas>=2.3.2`를 만족하므로 문제없다.
|
||||
단, `numba`가 명시되어 있지 않아 pandas-ta 설치 시 자동으로 설치된다.
|
||||
|
||||
**Step 3: requirements.txt 수정 (pandas 최소 버전 상향)**
|
||||
|
||||
`pandas>=2.2.0`을 `pandas>=2.3.2`로 변경하여 pandas-ta의 요구사항을 명시적으로 반영한다:
|
||||
|
||||
변경 전:
|
||||
```
|
||||
pandas>=2.2.0
|
||||
```
|
||||
|
||||
변경 후:
|
||||
```
|
||||
pandas>=2.3.2
|
||||
```
|
||||
|
||||
**Step 4: 변경 내용 확인**
|
||||
|
||||
```bash
|
||||
grep "pandas" /Users/gihyeon/github/cointrader/requirements.txt
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
pandas>=2.3.2
|
||||
pandas-ta==0.4.71b0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: 로컬 Docker 빌드 테스트
|
||||
|
||||
> Docker가 설치된 환경에서만 실행한다.
|
||||
|
||||
**Step 1: Docker 빌드**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
docker build -t cointrader:test .
|
||||
```
|
||||
|
||||
Expected: 빌드 성공 (`Successfully tagged cointrader:test` 또는 `#N DONE`)
|
||||
|
||||
**Step 2: 빌드된 이미지의 Python 버전 확인**
|
||||
|
||||
```bash
|
||||
docker run --rm cointrader:test python --version
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
Python 3.12.x
|
||||
```
|
||||
|
||||
**Step 3: pandas-ta import 확인**
|
||||
|
||||
```bash
|
||||
docker run --rm cointrader:test python -c "import pandas_ta; print(pandas_ta.__version__)"
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
0.4.71b0
|
||||
```
|
||||
|
||||
**Step 4: 테스트 이미지 정리**
|
||||
|
||||
```bash
|
||||
docker rmi cointrader:test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: git 커밋 및 Gitea push
|
||||
|
||||
**Files:**
|
||||
- Modify: `Dockerfile`
|
||||
- Modify: `requirements.txt`
|
||||
|
||||
**Step 1: git 상태 확인**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
git status
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
modified: Dockerfile
|
||||
modified: requirements.txt
|
||||
```
|
||||
|
||||
**Step 2: 변경 내용 검토**
|
||||
|
||||
```bash
|
||||
git diff Dockerfile requirements.txt
|
||||
```
|
||||
|
||||
Expected:
|
||||
- `Dockerfile`: `-FROM python:3.11-slim` → `+FROM python:3.12-slim`
|
||||
- `requirements.txt`: `-pandas>=2.2.0` → `+pandas>=2.3.2`
|
||||
|
||||
**Step 3: 스테이징**
|
||||
|
||||
```bash
|
||||
git add Dockerfile requirements.txt
|
||||
```
|
||||
|
||||
**Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git commit -m "fix: upgrade to Python 3.12 to support pandas-ta>=0.4.67b0"
|
||||
```
|
||||
|
||||
Expected: 커밋 성공
|
||||
|
||||
**Step 5: Gitea push**
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Expected: push 성공 후 Jenkins가 자동으로 새 빌드를 트리거함
|
||||
|
||||
**Step 6: 커밋 확인**
|
||||
|
||||
```bash
|
||||
git log --oneline -3
|
||||
```
|
||||
|
||||
Expected: 방금 만든 커밋이 최상단에 표시됨
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Jenkins 빌드 재실행 및 결과 확인
|
||||
|
||||
**Step 1: Jenkins 빌드 트리거**
|
||||
|
||||
Gitea push 후 Jenkins Webhook이 설정되어 있다면 자동으로 빌드가 트리거된다.
|
||||
수동으로 트리거하려면 Jenkins 웹 UI에서 `cointrader` 파이프라인 → `Build Now` 클릭.
|
||||
|
||||
**Step 2: 빌드 로그에서 성공 확인**
|
||||
|
||||
Jenkins 빌드 로그에서 다음 내용이 나타나야 한다:
|
||||
|
||||
```
|
||||
#9 [5/7] RUN pip install --no-cache-dir -r requirements.txt
|
||||
...
|
||||
Successfully installed pandas-ta-0.4.71b0 ...
|
||||
#9 DONE xx.xs
|
||||
```
|
||||
|
||||
오류 없이 `[Build Docker Image]` 스테이지가 완료되어야 한다.
|
||||
|
||||
**Step 3: 전체 파이프라인 성공 확인**
|
||||
|
||||
Jenkins 빌드 결과가 `SUCCESS`로 표시되어야 한다:
|
||||
```
|
||||
Finished: SUCCESS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
| 문제 | 원인 | 해결 |
|
||||
|------|------|------|
|
||||
| `python-binance==1.0.19` 설치 실패 | Python 3.12 비호환 | `python-binance>=1.0.19`로 변경하거나 최신 버전 확인 |
|
||||
| `aiohttp==3.9.3` 설치 실패 | Python 3.12 비호환 | `aiohttp>=3.9.3`으로 완화하거나 최신 버전으로 업그레이드 |
|
||||
| `numba` 설치 시간 초과 | numba 컴파일 시간 | 빌드 타임아웃 설정 증가 또는 `--timeout=300` 추가 |
|
||||
| Jenkins Webhook 미동작 | Gitea Webhook 미설정 | Gitea 저장소 설정 → Webhooks → Jenkins URL 추가 |
|
||||
|
||||
---
|
||||
|
||||
## 참고: Python 3.12 호환성 체크리스트
|
||||
|
||||
Python 3.11 → 3.12 주요 변경사항 중 이 프로젝트에 영향 가능한 항목:
|
||||
|
||||
- `asyncio` 동작 변경: `asyncio.get_event_loop()` deprecated → `asyncio.get_running_loop()` 권장
|
||||
- `typing` 모듈 일부 변경: `Union[X, Y]` → `X | Y` 문법 지원 강화
|
||||
- `datetime.utcnow()` deprecated → `datetime.now(timezone.utc)` 권장
|
||||
|
||||
현재 코드베이스(`src/`, `tests/`)에서 위 패턴 사용 여부를 확인하고 필요 시 수정한다.
|
||||
405
docs/plans/2026-03-01-jenkins-gitea-registry-cicd.md
Normal file
405
docs/plans/2026-03-01-jenkins-gitea-registry-cicd.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# Jenkins + Gitea 이미지 레지스트리 CI/CD 구현 계획
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Jenkins가 Gitea(10.1.10.28:3000)의 코드 변경을 감지하면 Docker 이미지를 빌드하여 Gitea Container Registry(10.1.10.28:5000 또는 Gitea 내장 패키지 레지스트리)에 push하고, docker-compose.yml이 해당 이미지를 pull해서 실행하도록 전체 CI/CD 파이프라인을 구성한다.
|
||||
|
||||
**Architecture:**
|
||||
- Jenkins는 Gitea webhook을 통해 main 브랜치 push 이벤트를 수신한다.
|
||||
- Jenkinsfile(파이프라인 스크립트)이 프로젝트 루트에 위치하며, `docker build → docker push → (선택) 원격 배포` 단계를 수행한다.
|
||||
- Gitea의 내장 Container Registry(Packages)를 이미지 저장소로 사용한다. 이미지 이름 형식: `10.1.10.28:3000/gihyeon/cointrader:<tag>`
|
||||
- docker-compose.yml은 `build: .` 대신 레지스트리 이미지를 직접 참조하도록 수정한다.
|
||||
|
||||
**Tech Stack:** Jenkins, Gitea Container Registry, Docker, docker-compose v2, Jenkinsfile(Declarative Pipeline)
|
||||
|
||||
---
|
||||
|
||||
## 사전 확인 사항
|
||||
|
||||
- Gitea 서버: `http://10.1.10.28:3000`
|
||||
- Gitea 저장소: `http://10.1.10.28:3000/gihyeon/cointrader.git`
|
||||
- Gitea Container Registry 주소: `10.1.10.28:3000` (HTTP 사용 시 Docker insecure-registries 설정 필요)
|
||||
- Jenkins 서버 주소: 별도 확인 필요 (아래 Task 1에서 확인)
|
||||
- 현재 Dockerfile: `FROM python:3.12-slim` 기반, `/app`에서 `python main.py` 실행
|
||||
|
||||
---
|
||||
|
||||
## Task 1: 환경 사전 점검
|
||||
|
||||
**Files:**
|
||||
- 확인: `Dockerfile`
|
||||
- 확인: `docker-compose.yml`
|
||||
|
||||
**Step 1: Gitea Container Registry(Packages) 활성화 확인**
|
||||
|
||||
브라우저에서 `http://10.1.10.28:3000/gihyeon/cointrader/packages` 접속.
|
||||
- 패키지 탭이 보이면 활성화된 것.
|
||||
- 안 보이면 Gitea 관리자 패널 → `Site Administration` → `Configuration` → `Enable Packages` 체크 필요.
|
||||
|
||||
**Step 2: Gitea Access Token 생성 (Jenkins용)**
|
||||
|
||||
`http://10.1.10.28:3000/user/settings/applications` 접속:
|
||||
- Token Name: `jenkins-cointrader`
|
||||
- 권한: `read:packages`, `write:packages` (또는 전체 권한)
|
||||
- `Generate Token` 클릭 후 **토큰 값을 반드시 복사** (다시 볼 수 없음)
|
||||
|
||||
**Step 3: Docker insecure-registries 설정 (HTTP 레지스트리 사용 시)**
|
||||
|
||||
Jenkins가 실행되는 서버(또는 로컬 Mac)에서:
|
||||
|
||||
```bash
|
||||
# /etc/docker/daemon.json 또는 Docker Desktop의 경우 Settings > Docker Engine
|
||||
cat /etc/docker/daemon.json
|
||||
```
|
||||
|
||||
아래 내용이 없으면 추가:
|
||||
```json
|
||||
{
|
||||
"insecure-registries": ["10.1.10.28:3000"]
|
||||
}
|
||||
```
|
||||
|
||||
Docker Desktop 사용 시: `Settings` → `Docker Engine` → JSON에 위 내용 병합 → `Apply & Restart`
|
||||
|
||||
**Step 4: Docker login 테스트**
|
||||
|
||||
```bash
|
||||
docker login 10.1.10.28:3000 -u gihyeon -p <위에서_생성한_토큰>
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
Login Succeeded
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Jenkinsfile 작성
|
||||
|
||||
**Files:**
|
||||
- Create: `Jenkinsfile`
|
||||
|
||||
**Step 1: Jenkinsfile 생성**
|
||||
|
||||
`/Users/gihyeon/github/cointrader/Jenkinsfile` 파일을 아래 내용으로 생성:
|
||||
|
||||
```groovy
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
REGISTRY = '10.1.10.28:3000'
|
||||
IMAGE_NAME = 'gihyeon/cointrader'
|
||||
IMAGE_TAG = "${env.BUILD_NUMBER}"
|
||||
FULL_IMAGE = "${REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
LATEST_IMAGE = "${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
GITEA_CREDS = credentials('gitea-registry-credentials')
|
||||
}
|
||||
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
steps {
|
||||
checkout scm
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build Image') {
|
||||
steps {
|
||||
sh "docker build -t ${FULL_IMAGE} -t ${LATEST_IMAGE} ."
|
||||
}
|
||||
}
|
||||
|
||||
stage('Push to Gitea Registry') {
|
||||
steps {
|
||||
sh """
|
||||
echo ${GITEA_CREDS_PSW} | docker login ${REGISTRY} -u ${GITEA_CREDS_USR} --password-stdin
|
||||
docker push ${FULL_IMAGE}
|
||||
docker push ${LATEST_IMAGE}
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Cleanup') {
|
||||
steps {
|
||||
sh """
|
||||
docker rmi ${FULL_IMAGE} || true
|
||||
docker rmi ${LATEST_IMAGE} || true
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
echo "Build #${env.BUILD_NUMBER} pushed: ${FULL_IMAGE}"
|
||||
}
|
||||
failure {
|
||||
echo "Build #${env.BUILD_NUMBER} FAILED"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **참고:**
|
||||
> - `GITEA_CREDS`는 Jenkins Credentials에 등록할 Username+Password 자격증명 ID다 (Task 3에서 등록).
|
||||
> - `IMAGE_TAG`는 Jenkins 빌드 번호를 사용한다. 태그 전략을 git 커밋 해시로 바꾸려면 `"${env.GIT_COMMIT[0..7]}"` 사용.
|
||||
> - `Cleanup` 스테이지는 Jenkins 서버 디스크 절약을 위해 빌드 후 로컬 이미지를 삭제한다.
|
||||
|
||||
**Step 2: Jenkinsfile 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/Jenkinsfile
|
||||
```
|
||||
|
||||
Expected: 위 내용이 출력됨
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Jenkins에 Gitea Credentials 등록
|
||||
|
||||
**Step 1: Jenkins 웹 UI 접속**
|
||||
|
||||
`http://<jenkins-서버-주소>:8080` 접속 (Jenkins 서버 주소 확인 필요)
|
||||
|
||||
**Step 2: Credentials 등록**
|
||||
|
||||
`Jenkins` → `Manage Jenkins` → `Credentials` → `System` → `Global credentials` → `Add Credentials`:
|
||||
|
||||
| 항목 | 값 |
|
||||
|------|----|
|
||||
| Kind | Username with password |
|
||||
| Scope | Global |
|
||||
| Username | `gihyeon` |
|
||||
| Password | Task 1 Step 2에서 생성한 Gitea Access Token |
|
||||
| ID | `gitea-registry-credentials` |
|
||||
| Description | Gitea Container Registry for cointrader |
|
||||
|
||||
`Create` 클릭
|
||||
|
||||
**Step 3: 등록 확인**
|
||||
|
||||
Credentials 목록에 `gitea-registry-credentials`가 표시되는지 확인
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Jenkins Pipeline Job 생성
|
||||
|
||||
**Step 1: 새 Pipeline Job 생성**
|
||||
|
||||
`Jenkins` → `New Item`:
|
||||
- Item name: `cointrader`
|
||||
- Type: `Pipeline`
|
||||
- `OK` 클릭
|
||||
|
||||
**Step 2: Pipeline 설정**
|
||||
|
||||
`Pipeline` 섹션에서:
|
||||
- Definition: `Pipeline script from SCM`
|
||||
- SCM: `Git`
|
||||
- Repository URL: `http://10.1.10.28:3000/gihyeon/cointrader.git`
|
||||
- Credentials: (Gitea 저장소 접근용 credentials 추가, 없으면 Task 3과 동일하게 추가)
|
||||
- Branch Specifier: `*/main`
|
||||
- Script Path: `Jenkinsfile`
|
||||
|
||||
`Save` 클릭
|
||||
|
||||
**Step 3: 수동 빌드 테스트**
|
||||
|
||||
`Build Now` 클릭 → Console Output 확인
|
||||
|
||||
Expected:
|
||||
```
|
||||
[Pipeline] stage: Build Image
|
||||
Successfully built ...
|
||||
[Pipeline] stage: Push to Gitea Registry
|
||||
Login Succeeded
|
||||
The push refers to repository [10.1.10.28:3000/gihyeon/cointrader]
|
||||
...
|
||||
latest: digest: sha256:... size: ...
|
||||
[Pipeline] stage: Cleanup
|
||||
Finished: SUCCESS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Gitea Webhook 설정 (자동 트리거)
|
||||
|
||||
**Step 1: Gitea 저장소 Webhook 추가**
|
||||
|
||||
`http://10.1.10.28:3000/gihyeon/cointrader/settings/hooks` 접속:
|
||||
- `Add Webhook` → `Gitea`
|
||||
- Target URL: `http://<jenkins-서버-주소>:8080/gitea-webhook/post`
|
||||
- Jenkins Gitea Plugin 사용 시 위 URL 형식
|
||||
- 일반 Generic Webhook 사용 시: `http://<jenkins-서버-주소>:8080/job/cointrader/build?token=<토큰>`
|
||||
- Trigger: `Push Events`
|
||||
- Branch filter: `main`
|
||||
- `Add Webhook` 클릭
|
||||
|
||||
**Step 2: Jenkins에 Gitea Plugin 설치 (미설치 시)**
|
||||
|
||||
`Manage Jenkins` → `Plugins` → `Available plugins` → `Gitea` 검색 → 설치 후 재시작
|
||||
|
||||
**Step 3: Webhook 테스트**
|
||||
|
||||
Gitea Webhook 설정 페이지에서 `Test Delivery` 클릭
|
||||
|
||||
Expected: Jenkins에서 새 빌드가 자동으로 시작됨
|
||||
|
||||
---
|
||||
|
||||
## Task 6: docker-compose.yml 수정
|
||||
|
||||
**Files:**
|
||||
- Modify: `docker-compose.yml`
|
||||
|
||||
현재 `docker-compose.yml`은 `build: .`으로 로컬 빌드를 사용한다. 이를 레지스트리 이미지를 pull해서 실행하도록 변경한다.
|
||||
|
||||
**Step 1: docker-compose.yml 수정**
|
||||
|
||||
`/Users/gihyeon/github/cointrader/docker-compose.yml`을 아래 내용으로 교체:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
cointrader:
|
||||
image: 10.1.10.28:3000/gihyeon/cointrader:latest
|
||||
container_name: cointrader
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
```
|
||||
|
||||
> **변경 사항:**
|
||||
> - `build: .` → `image: 10.1.10.28:3000/gihyeon/cointrader:latest`
|
||||
> - 이제 `docker compose up -d`를 실행하면 로컬 빌드 없이 레지스트리에서 이미지를 pull한다.
|
||||
> - 배포 서버에서 최신 이미지로 업데이트하려면: `docker compose pull && docker compose up -d`
|
||||
|
||||
**Step 2: 변경 내용 확인**
|
||||
|
||||
```bash
|
||||
cat /Users/gihyeon/github/cointrader/docker-compose.yml
|
||||
```
|
||||
|
||||
Expected: `image:` 필드가 레지스트리 주소를 가리킴
|
||||
|
||||
**Step 3: (선택) 로컬 개발용 docker-compose.override.yml 생성**
|
||||
|
||||
로컬에서 소스 코드를 직접 빌드해서 테스트하고 싶을 때를 위한 override 파일:
|
||||
|
||||
```yaml
|
||||
# docker-compose.override.yml (로컬 개발 전용, git에 포함하지 않아도 됨)
|
||||
services:
|
||||
cointrader:
|
||||
build: .
|
||||
image: cointrader:local
|
||||
```
|
||||
|
||||
이 파일이 있으면 `docker compose up -d`가 자동으로 `build: .`을 사용한다. 프로덕션 서버에는 이 파일을 두지 않는다.
|
||||
|
||||
---
|
||||
|
||||
## Task 7: 변경사항 커밋 및 Push
|
||||
|
||||
**Step 1: 변경 파일 확인**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
git status
|
||||
```
|
||||
|
||||
Expected: `Jenkinsfile`(new), `docker-compose.yml`(modified)이 표시됨
|
||||
|
||||
**Step 2: 스테이징**
|
||||
|
||||
```bash
|
||||
git add Jenkinsfile docker-compose.yml
|
||||
```
|
||||
|
||||
**Step 3: `.env` 미포함 확인**
|
||||
|
||||
```bash
|
||||
git diff --cached --name-only
|
||||
```
|
||||
|
||||
Expected: `Jenkinsfile`, `docker-compose.yml` 두 파일만 표시됨
|
||||
|
||||
**Step 4: 커밋**
|
||||
|
||||
```bash
|
||||
git commit -m "ci: Jenkins pipeline + Gitea registry CI/CD 설정"
|
||||
```
|
||||
|
||||
Expected: `main` 브랜치에 새 커밋 생성
|
||||
|
||||
**Step 5: Gitea에 Push**
|
||||
|
||||
```bash
|
||||
git push origin main
|
||||
```
|
||||
|
||||
Expected: Push 성공 + (Webhook 설정 완료 시) Jenkins 빌드 자동 시작
|
||||
|
||||
---
|
||||
|
||||
## Task 8: 엔드-투-엔드 검증
|
||||
|
||||
**Step 1: 코드 변경 후 push 테스트**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
# 아무 파일이나 사소하게 변경 (예: README 한 줄 추가)
|
||||
echo "# CI/CD test" >> README.md
|
||||
git add README.md
|
||||
git commit -m "test: CI/CD 파이프라인 검증용 더미 커밋"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Step 2: Jenkins 빌드 자동 시작 확인**
|
||||
|
||||
Jenkins UI에서 `cointrader` 잡의 빌드가 자동으로 시작되는지 확인 (30초 이내)
|
||||
|
||||
**Step 3: Gitea 레지스트리에 이미지 push 확인**
|
||||
|
||||
`http://10.1.10.28:3000/gihyeon/cointrader/packages` 접속 → `cointrader` 컨테이너 패키지에 새 태그가 생성되었는지 확인
|
||||
|
||||
**Step 4: 이미지 pull 테스트**
|
||||
|
||||
```bash
|
||||
docker pull 10.1.10.28:3000/gihyeon/cointrader:latest
|
||||
```
|
||||
|
||||
Expected:
|
||||
```
|
||||
latest: Pulling from gihyeon/cointrader
|
||||
...
|
||||
Status: Downloaded newer image for 10.1.10.28:3000/gihyeon/cointrader:latest
|
||||
```
|
||||
|
||||
**Step 5: docker compose로 실행 테스트**
|
||||
|
||||
```bash
|
||||
cd /Users/gihyeon/github/cointrader
|
||||
docker compose up -d
|
||||
docker compose logs -f --tail=20
|
||||
```
|
||||
|
||||
Expected: 컨테이너가 정상 시작되고 로그가 출력됨
|
||||
|
||||
---
|
||||
|
||||
## 트러블슈팅
|
||||
|
||||
| 문제 | 원인 | 해결 |
|
||||
|------|------|------|
|
||||
| `http: server gave HTTP response to HTTPS client` | Docker가 HTTPS로 레지스트리 접근 시도 | `daemon.json`에 `insecure-registries` 추가 후 Docker 재시작 |
|
||||
| `unauthorized: authentication required` | Credentials 미등록 또는 토큰 만료 | Task 1 Step 2에서 새 토큰 발급 후 Jenkins Credentials 업데이트 |
|
||||
| `connection refused` to Jenkins | Jenkins URL 오타 또는 방화벽 | Jenkins 서버 주소 재확인 |
|
||||
| Webhook이 Jenkins를 트리거하지 않음 | Jenkins URL이 Gitea 서버에서 접근 불가 | Jenkins가 Gitea 서버와 같은 네트워크에 있는지 확인, 방화벽 8080 포트 오픈 |
|
||||
| `image not found` on docker compose pull | 이미지가 아직 push되지 않음 | Jenkins 빌드 완료 후 재시도 |
|
||||
| Jenkins에서 `docker: command not found` | Jenkins 에이전트에 Docker 미설치 | Jenkins 서버에 Docker 설치 또는 Docker-in-Docker 설정 |
|
||||
102
docs/plans/2026-03-01-ml-filter-design.md
Normal file
102
docs/plans/2026-03-01-ml-filter-design.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# ML 필터 설계 문서
|
||||
|
||||
**날짜:** 2026-03-01
|
||||
|
||||
## 목적
|
||||
|
||||
기존 규칙 기반 신호(LONG/SHORT/HOLD)가 발생했을 때, LightGBM 모델이 해당 진입이 수익으로 끝날 확률을 계산하여 낮은 확률의 진입을 차단하는 보조 필터를 구현한다.
|
||||
|
||||
---
|
||||
|
||||
## 아키텍처 개요
|
||||
|
||||
```
|
||||
캔들 수신 → 기술 지표 계산 → 규칙 기반 신호(LONG/SHORT/HOLD)
|
||||
↓
|
||||
신호 != HOLD 일 때만
|
||||
↓
|
||||
[ML 필터] LightGBM.predict_proba()
|
||||
↓
|
||||
확률 >= 0.60 이면 진입 허용
|
||||
확률 < 0.60 이면 진입 차단
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 레이블 정의
|
||||
|
||||
- **1 (성공):** 진입 후 `take_profit` 가격에 먼저 도달
|
||||
- **0 (실패):** 진입 후 `stop_loss` 가격에 먼저 도달
|
||||
- TP/SL 계산은 기존 `Indicators.get_atr_stop()` 재사용 (ATR 기반)
|
||||
|
||||
---
|
||||
|
||||
## 피처 목록
|
||||
|
||||
| 피처 | 설명 |
|
||||
|---|---|
|
||||
| `rsi` | RSI(14) |
|
||||
| `macd_hist` | MACD 히스토그램 |
|
||||
| `bb_pct` | 볼린저밴드 내 가격 위치 (0~1) |
|
||||
| `ema_align` | EMA 정배열 여부 (1=정배열, -1=역배열, 0=혼재) |
|
||||
| `stoch_k` | Stochastic RSI K |
|
||||
| `stoch_d` | Stochastic RSI D |
|
||||
| `atr_pct` | ATR / 현재가 (변동성 비율) |
|
||||
| `vol_ratio` | 거래량 / vol_ma20 |
|
||||
| `ret_1` | 1캔들 전 대비 수익률 |
|
||||
| `ret_3` | 3캔들 전 대비 수익률 |
|
||||
| `ret_5` | 5캔들 전 대비 수익률 |
|
||||
| `signal_strength` | 규칙 기반 신호 강도 (long/short_signals 수) |
|
||||
| `side` | 신호 방향 (1=LONG, 0=SHORT) |
|
||||
|
||||
---
|
||||
|
||||
## 신규 컴포넌트
|
||||
|
||||
| 컴포넌트 | 파일 | 역할 |
|
||||
|---|---|---|
|
||||
| 피처 엔지니어링 | `src/ml_features.py` | 기술 지표 → ML 피처 변환 |
|
||||
| ML 필터 | `src/ml_filter.py` | 모델 로드 + 예측 + 폴백 |
|
||||
| 재학습 스케줄러 | `src/retrainer.py` | 매일 새벽 재학습 트리거 |
|
||||
| 데이터 수집 스크립트 | `scripts/fetch_history.py` | 바이낸스 과거 캔들 수집 |
|
||||
| 학습 스크립트 | `scripts/train_model.py` | LightGBM 학습 + 저장 |
|
||||
|
||||
---
|
||||
|
||||
## 재학습 스케줄
|
||||
|
||||
- **초기:** `scripts/fetch_history.py` + `scripts/train_model.py` 수동 실행
|
||||
- **이후:** 매일 새벽 3시 (KST) `retrainer.py`가 자동 실행
|
||||
- 새 모델 AUC > 기존 모델 AUC → 교체
|
||||
- 그렇지 않으면 기존 모델 유지 (롤백)
|
||||
- Discord 알림으로 결과 전송
|
||||
|
||||
---
|
||||
|
||||
## 모델 저장 구조
|
||||
|
||||
```
|
||||
models/
|
||||
├── lgbm_filter.pkl ← 현재 사용 중인 모델
|
||||
├── lgbm_filter_prev.pkl ← 롤백용 이전 모델
|
||||
└── training_log.json ← 재학습 이력 (날짜, AUC, 샘플 수)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 폴백 정책
|
||||
|
||||
`models/lgbm_filter.pkl` 파일이 없으면 ML 필터를 건너뛰고 기존 규칙 기반 신호 그대로 사용. 봇이 모델 없이도 정상 작동.
|
||||
|
||||
---
|
||||
|
||||
## bot.py 변경 범위
|
||||
|
||||
`process_candle()` 메서드에 3줄 추가:
|
||||
|
||||
```python
|
||||
if signal != "HOLD" and self.ml_filter.is_model_loaded():
|
||||
features = build_features(df_with_indicators, signal)
|
||||
if not self.ml_filter.should_enter(features):
|
||||
signal = "HOLD"
|
||||
```
|
||||
1124
docs/plans/2026-03-01-ml-filter-implementation.md
Normal file
1124
docs/plans/2026-03-01-ml-filter-implementation.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user