9.7 KiB
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
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 단독 실행
# 솔루션 루트에서
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 실행 (권장)
# 솔루션 루트에서
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. 라이선스 / 기여
사내 운영용 단일 사용자 서버입니다. 기여 정책 및 라이선스는 별도 내부 문서를 따릅니다.