Files
BitGet_API/README.md
2026-04-20 20:27:42 +09:00

195 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Bitget_Server
기존 WinForms 기반 `BitgetApi` 프로젝트(.NET Framework 4.7.2 + DevExpress)의 **거래·신호 비즈니스 로직**을 추출해 ASP.NET Core 8.0 REST API 서버로 이식한 단일 사용자용 백엔드입니다. 기존 UI 코드는 전부 제외되었고, 외부 Bitget REST/WebSocket 통신 규약과 신호 판정 로직은 원본과 동일하게 유지됩니다.
---
## 1. 기술 스택
- .NET 8.0 (ASP.NET Core Web API, `Microsoft.NET.Sdk.Web`)
- Kestrel HTTP 전용, 포트 **5000** (HTTPS 미사용)
- Serilog (Console + 일자 롤링 파일, `Sinks.Map` 으로 SourceContext 분리)
- Swashbuckle Swagger (환경 불문 항상 활성화, `/swagger`)
- Newtonsoft.Json (원본 직렬화 규약 유지)
- Docker (Ubuntu 24.04 noble 이미지 기반)
---
## 2. 아키텍처
Clean Layered 구조. 의존 방향은 `Api → Application → Domain`, 그리고 `Infrastructure → Domain` 만 허용.
```
Bitget_Server/
├── Api/ REST 진입점 (Controller + DTO)
│ ├── Controllers/
│ │ ├── SettingsController.cs /api/settings/*
│ │ ├── CoinsController.cs /api/coins
│ │ ├── SignalsController.cs /api/signals, /api/signals/conditions
│ │ ├── TradingController.cs /api/trading/*
│ │ ├── WebSocketController.cs /api/websocket/*
│ │ └── HealthController.cs /api/health
│ └── Dtos/
├── Application/
│ ├── Options/ BitgetOptions, GlobalOptions
│ ├── State/ MarketState (공유 가변 상태)
│ ├── Services/ SettingsService, BitgetTradingService,
│ │ SignalProcessService, PriceLevelTracker,
│ │ InitialCandleLoader
│ └── BackgroundServices/ MarketInfoBackgroundService,
│ WebSocketCalcBackgroundService,
│ SignalProcessBackgroundService
├── Domain/ 엔티티 / Enum / 상수 엔드포인트
├── Infrastructure/
│ ├── Bitget/ BitgetClient, BitgetWebSocketClient,
│ │ BitgetRateLimiter
│ └── Utils/ BaseSettings<T>, Convertor 등
├── Program.cs DI + 미들웨어 파이프라인
├── appsettings.json
├── appsettings.Development.json
└── Dockerfile
```
DI 수명 정책: 상태를 보유하거나 BackgroundService 에서 캡처되는 모든 서비스는 **Singleton** 으로 통일. Bitget 단일 엔드포인트를 공유하므로 `HttpClient` 와 `BitgetClient` 도 Singleton.
---
## 3. 실행 방법
### 3.1. 로컬 실행 (개발)
사전 요구: .NET SDK 8.0
```bash
cd Bitget_Server
dotnet restore
dotnet run
```
기동 후 브라우저에서 `http://localhost:5000/swagger` 로 진입하면 전체 API 명세를 확인할 수 있습니다. 루트 `http://localhost:5000/` 는 Swagger UI 로 리다이렉트됩니다.
개발 환경 설정은 `appsettings.Development.json` 이 덮어쓰며 기본으로 `LogDebugWrite: true` 입니다.
### 3.2. Docker 단독 실행
```bash
# 솔루션 루트에서
docker build -f Bitget_Server/Dockerfile -t bitget-server:latest .
docker run -d --name bitget-server \
-p 5000:5000 \
-v $(pwd)/data:/app/data \
-v $(pwd)/logs:/app/logs \
-v $(pwd)/appsettings.Production.json:/app/appsettings.Production.json:ro \
-e ASPNETCORE_ENVIRONMENT=Production \
-e TZ=Asia/Seoul \
bitget-server:latest
```
### 3.3. docker-compose 실행 (권장)
```bash
# 솔루션 루트에서
docker compose up -d --build
docker compose logs -f bitget-server
```
첫 실행 전에 반드시 `appsettings.Production.json` 의 `Bitget.ApiKeys[].AccessKey / SecretKey / PassphraseKey` 값을 실제 Bitget API 자격증명으로 교체해야 합니다. 해당 파일은 read-only 볼륨으로 마운트되므로 이미지에 시크릿이 포함되지 않습니다.
---
## 4. 설정 (`appsettings.json` → `Bitget` 섹션)
| 키 | 기본값 | 설명 |
|---|---|---|
| `DefaultApiId` | `0` | 최초 기동 시 GlobalOptions.SelectAPI 로 로드할 키의 Id |
| `RateLimitPerSecond` | `20` | 클라이언트 사이드 요청 한도 (0 또는 음수 시 무제한) |
| `DataDirectory` | `./data` | BaseSettings JSON 영속화 경로. Docker 에서는 `/app/data` |
| `LogDirectory` | `./logs` | Serilog 파일 싱크 경로. Docker 에서는 `/app/logs` |
| `LogDebugWrite` | `false` | 최소 로그 레벨을 Debug 로 낮출지 여부 |
| `MarketInfoUpdateIntervalMs` | `300` | 시세/계정 정보 폴링 주기 |
| `WebSocketCalcIntervalMs` | `1000` | WebSocket 계산 루프 주기 |
| `WebSocketPingIntervalMs` | `25000` | WebSocket Ping 전송 주기 |
| `ApiKeys[]` | — | 복수 API 키. 각 항목은 `Id / Name / AccessKey / SecretKey / PassphraseKey` 필드 구성 |
API 키 선택은 기동 직후 `ApiKeyBootstrapper` 가 `DefaultApiId` 로 자동 적용하며, 이후 런타임에는 `PUT /api/settings/api-key` 로 변경할 수 있습니다. 선택값은 `GlobalOptions` (BaseSettings JSON) 으로 영속화되어 재시작 시에도 유지됩니다.
---
## 5. REST API 요약
전체 계약 및 파라미터 스키마는 Swagger UI(`/swagger`) 를 참조하세요. 대략의 엔드포인트 구성은 다음과 같습니다.
**설정 (`/api/settings/*`)**: 캔들 주기, 선택 코인, 레버리지, 주문 수량, 선택 API 키의 GET/PUT. 선택 가능한 캔들 주기 목록은 `GET /api/settings/candle-interval/options`.
**마켓 (`/api/coins`)**: Bitget 선물 시장 전체 심볼 목록.
**신호 (`/api/signals`, `/api/signals/conditions`, `/api/signals/checked-markets`)**: 신호 조건 조회/갱신, 체크된 마켓 목록, 현재 산출된 롱/숏 진입·청산 신호.
**거래 (`/api/trading/*`)**: Bitget 사설 API 래퍼. 서버 timestamp, 선물 계정, 대기 주문, 주문 상세, 주문 실행(POST), 주문 취소(DELETE), 포지션 모드 변경(PUT).
**WebSocket 제어 (`/api/websocket/*`)**: 현재 연결 상태 조회, 수동 start/stop.
**헬스 (`/api/health`)**: 버전/타임스탬프 JSON. Docker healthcheck 가 호출.
오류 응답은 RFC 7807 ProblemDetails 로 통일되어 있습니다.
---
## 6. BackgroundService (WinForms 타이머 이식)
기존 WinForms 의 `Thread.Sleep` 루프가 다음 세 HostedService 로 대체되었습니다.
- **MarketInfoBackgroundService**: `MarketInfoUpdateIntervalMs` 주기로 Bitget 선물 시세/계정 스냅샷을 `MarketState` 에 기록
- **WebSocketCalcBackgroundService**: `WebSocketCalcIntervalMs` 주기로 WebSocket 으로부터 누적된 캔들 Tick 을 interval 별로 합산 계산
- **SignalProcessBackgroundService**: 200ms 주기로 `SignalProcessService.ProcessOnceAsync` 호출, 진입/청산 신호 리스트 갱신
HostedService 의 기동 순서는 DI 등록 순서를 따르며, `ApiKeyBootstrapper` 가 이들보다 먼저 실행되어 `SelectAPI` 초기화를 보장합니다.
---
## 7. Rate Limiter 동작
`BitgetRateLimiter` 는 1초 슬라이딩 윈도우 기반 클라이언트 사이드 스로틀입니다. 전 코드베이스에서 `HttpClient` 직접 사용은 `BitgetClient.SendWebServerAsync` 1곳으로 통일되어 있고, 이 메서드는 매 호출마다 `await _limiter.AcquireAsync(ct)` 로 슬롯을 확보합니다.
Bitget 서버가 429 TooManyRequests 를 반환하면 자동으로 500ms 대기 후 **1회에 한해** 재시도합니다. 재시도 후에도 429 면 일반 오류로 처리하고 null 을 반환합니다. `RateLimitPerSecond` 를 0 또는 음수로 설정하면 무제한 모드(즉시 통과)로 동작하고 기동 시 Warning 로그가 1회 출력됩니다.
---
## 8. 로그
Serilog Console 싱크와 `Sinks.Map` 을 통한 파일 싱크가 병렬 동작합니다. 파일은 `LogDirectory/bitget-YYYYMMDD.log` 형식의 일자 롤링이며 최대 30개 보관합니다. Docker 환경에서는 `json-file` 드라이버(10MB × 5 파일) 로 stdout 을 순환 보관합니다.
로그 레벨은 `Bitget.LogDebugWrite` 플래그로 Debug/Information 간을 전환합니다. `Microsoft.AspNetCore` 는 Warning 이상으로 고정 override 됩니다.
---
## 9. 원본 WinForms 대비 제외 사항
- DevExpress UI 컴포넌트 및 Form 계층 전체 (`Main.cs`, `*.Designer.cs`, `*.resx`)
- 화면 출력용 이벤트/델리게이트
- 개별 사용자 프로필 GUI (단일 사용자 서버로 통합)
제외되지 않은 이식 항목:
- HMAC-SHA256 서명 생성(`BitgetClient.GenerateSignString`)
- 전고점/전저점 dictionary state machine (`PriceLevelTracker`)
- 구름떼 / 영호조건식 상향·하향 돌파 판정 (`SignalProcessService`)
- 캔들 시계열 누적 및 WebSocket 통합 계산
- BaseSettings JSON 영속화 메커니즘
---
## 10. 문제 해결
| 증상 | 원인 및 조치 |
|---|---|
| `/api/health` 200 이지만 주문은 401/signature-invalid | `appsettings.Production.json` 의 `ApiKeys` 값이 자리표시자. 실제 키로 교체 후 컨테이너 재기동 |
| 컨테이너 기동 후 `/app/data` 내 json 이 재시작마다 초기화 | `./data` 볼륨 바인드가 누락되었거나 호스트 권한이 1654 사용자에게 없음. `chown` 또는 compose 재생성 |
| Debug 로그가 안 남음 | `Bitget.LogDebugWrite` 가 `false`. `appsettings.Development.json` 사용 또는 플래그 `true` 설정 |
| 잦은 throttle 로그 | `RateLimitPerSecond` 가 너무 낮거나 BackgroundService 주기가 과잉. 값 상향 또는 간격 늘리기 |
---
## 11. 라이선스 / 기여
사내 운영용 단일 사용자 서버입니다. 기여 정책 및 라이선스는 별도 내부 문서를 따릅니다.