Files
BitGet_API/README.md

195 lines
9.7 KiB
Markdown
Raw Permalink Normal View History

2026-04-20 20:27:42 +09:00
# 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. 라이선스 / 기여
사내 운영용 단일 사용자 서버입니다. 기여 정책 및 라이선스는 별도 내부 문서를 따릅니다.