sgcWebSockets + Binance로 Delphi에서 실시간 트레이딩 봇 구축

· 기능

우리가 빌드할 것

이 튜토리얼의 끝에서 Binance에서 라이브 거래 및 주문서 데이터를 스트리밍하고, 간단한 브레이크아웃 전략을 실행하고, REST API를 통해 실제 주문을 배치하고, 위험 제어(최대 포지션 크기, 일일 손실 한도, 킬 스위치)를 적용하는 작동하는 Delphi VCL 애플리케이션을 갖게 될 거예요. 동일한 배관이 sgcWebSockets가 지원하는 모든 거래소 — Coinbase, Kraken, OKX, Bybit, Bitfinex — 에 대해 작동해요. 자격 증명과 심볼 매핑만 변경하면 돼요.

이것은 참조 sgcTrader 샘플에서 사용된 동일한 아키텍처예요. 전체 UI, 차트 및 다중 거래소 라우팅이 있는 더 큰 시작점을 원하면 그것을 잡으세요. 아래 워크스루는 약 300줄의 코드에서 후드 아래에서 무엇이 일어나고 있는지 보여줘요.

시작하기 전에 두 가지 전제 조건. 첫째, Binance API 키를 받으세요(계정 > API 관리). 개발의 경우 "Enable Reading" 및 "Enable Spot Trading"만 있는 키를 생성하고 IP를 화이트리스트에 추가하세요. 출금 가능 키를 코드에 절대 넣지 마세요. 둘째, 먼저 Binance의 테스트넷(testnet.binance.vision)에서 모든 것을 하세요. 엔드포인트, 메시지 형식 및 서명 알고리즘은 프로덕션과 동일하지만 자금은 가짜예요. "내 전략이 정확하다고 확신해요"라고 정확히 테스트넷에서 먼저 테스트하지 않은 횟수만큼 실제 돈을 잃었어요.

하나의 다이어그램의 아키텍처

세 스레드, 두 컴포넌트, 하나의 위험 게이트키퍼:

컴포넌트 역할 스레드
TsgcWSAPI_Binance WebSocket 스트림: 거래, 깊이, 캔들, 사용자 데이터 I/O 워커
TsgcHTTP_API_Binance REST: 주문 배치, 취소, 계정 스냅샷 트레이더 워커
전략 큐 분리: 시장 이벤트 → 결정 → 주문 전략 워커
위험 게이트 각 주문 차단 / 축소 / 허용 트레이더 내 인라인

1단계: 시장 데이터 스트리밍

폼에 TsgcWSAPI_Binance를 드롭하세요. 이미 Binance 결합 스트림 프로토콜을 사용해요 — 원하는 채널에 구독만 하세요.

uses
  sgcWSAPI_Binance;

procedure TForm1.FormCreate(Sender: TObject);
begin
  oBinance := TsgcWSAPI_Binance.Create(Self);
  oBinance.WatchDog.Enabled  := True;
  oBinance.WatchDog.Interval := 5;
  oBinance.HeartBeat.Enabled := True;

  oBinance.OnBinanceMessage := DoStream;
  oBinance.OnDisconnect     := DoDisconnect;

  // Subscribe to 1-minute klines and aggregated trades for BTCUSDT
  oBinance.Streams.Add('btcusdt@kline_1m');
  oBinance.Streams.Add('btcusdt@aggTrade');

  oBinance.Active := True;
end;

procedure TForm1.DoStream(Sender: TObject; const aStream, aData: string);
begin
  // Fire-and-forget: push to the strategy queue
  oStrategyQueue.Push(TMarketEvent.Create(aStream, aData));
end;

그것이 전체 시장 데이터 수집 계층이에요. 드롭 시 재연결, 죽은 링크를 감지하기 위한 하트비트, 전략 큐로의 비차단 푸시.

그렇지 않으면 직접 작성해야 할 컴포넌트가 하는 한 가지: Binance 결합 스트림 URL은 /stream?streams=name1/name2/name3이며, 연결을 끊지 않고 스트림을 추가하거나 제거하려면 동일한 소켓을 통해 JSON-RPC 구독/구독 취소 메시지를 보내야 해요. TsgcWSAPI_Binance는 JSON-RPC 핸드셰이크를 처리하는 SubscribeStreamUnsubscribeStream 메서드를 노출해요. 사용자가 UI에서 새 티커를 선택할 때 유용해요 — 재연결 없음, 메시지 손실 없음.

또한: Binance는 IP당 및 스트림당 제한을 부과해요. 모든 USDT 쌍에 대한 전체 깊이의 경우 메시지 속도 제한에 빠르게 도달할 거예요. 실제로 필요한 것만 구독하고, 광범위한 시장 보기가 필요할 때 심볼별 스트림보다 집계된 스트림(!miniTicker@arr)을 선호하세요.

2단계: 최소 전략

전략은 자체 스레드에서 실행돼요. 1분 캔들 스트림에서 마지막 N개 종가의 롤링 윈도우를 유지하고 가격이 20기간 고가를 돌파할 때 매수해요. 순수한 일러스트레이션 — 실제 돈 앞에 놓지 마세요.

procedure TStrategyThread.Execute;
var
  oEvent : TMarketEvent;
  oJSON  : TsgcJSONObject;
  vClose : Double;
  vHigh  : Double;
begin
  while not Terminated do
  begin
    if not oStrategyQueue.Pop(oEvent, 100) then Continue;
    try
      oJSON := TsgcJSONObject.Parse(oEvent.Data);
      try
        if oEvent.Stream.EndsWith('@kline_1m') then
        begin
          vClose := oJSON.O['k'].F['c'];
          FCloses.Append(vClose);
          if FCloses.Count >= 21 then
          begin
            vHigh := FCloses.Max(20);                  // prior 20-bar high
            if (FPosition = 0) and (vClose > vHigh) then
              oOrderQueue.Push(TIntent.New(siBuy, 'BTCUSDT', 0.001))
            else if (FPosition > 0) and (vClose < FCloses.MA(20)) then
              oOrderQueue.Push(TIntent.New(siSell, 'BTCUSDT', FPosition));
          end;
        end;
      finally
        oJSON.Free;
      end;
    finally
      oEvent.Free;
    end;
  end;
end;

3단계: 위험 게이트

전략이 거래소와 직접 대화하도록 절대 두지 마세요. 한도를 아는 게이트를 통해 모든 의도를 깔때기에 부으세요.

function TRiskGate.Validate(const aIntent: TIntent;
  out aReason: string): Boolean;
begin
  Result := False;

  if FKillSwitch then
    Exit(False);

  if Abs(FDailyPnL) > FConfig.MaxDailyLoss then
  begin
    aReason := 'Daily loss limit hit';
    Exit;
  end;

  if (aIntent.Side = siBuy)
     and (FPositionUSD + aIntent.Qty * FLastPrice > FConfig.MaxPositionUSD) then
  begin
    aReason := 'Max position size';
    Exit;
  end;

  Result := True;
end;

4단계: REST를 통해 주문 배치

주문 워커는 검증된 의도를 가져와 요청에 서명하고 Binance에 게시해요.

procedure TTraderThread.Execute;
var
  oIntent  : TIntent;
  vReason  : string;
  oResponse: TsgcBinanceClass_Response_NewOrder;
begin
  while not Terminated do
  begin
    if not oOrderQueue.Pop(oIntent, 100) then Continue;
    try
      if not FRisk.Validate(oIntent, vReason) then
      begin
        Log(Format('REJECT %s %s qty=%.6f reason=%s',
          [SideName(oIntent.Side), oIntent.Symbol, oIntent.Qty, vReason]));
        Continue;
      end;

      oResponse := oHttp.NewOrder(
        oIntent.Symbol,
        IfThen(oIntent.Side = siBuy, 'BUY', 'SELL'),
        'MARKET',
        oIntent.Qty,
        0  // price ignored for MARKET
      );
      try
        Log(Format('FILL  %s qty=%.6f price=%.2f id=%d',
          [oIntent.Symbol, oResponse.ExecutedQty,
           oResponse.AvgPrice, oResponse.OrderId]));
        FRisk.OnFill(oIntent.Side, oResponse.ExecutedQty, oResponse.AvgPrice);
      finally
        oResponse.Free;
      end;
    finally
      oIntent.Free;
    end;
  end;
end;

4b단계: REST 인증 및 서명

TsgcHTTP_API_Binance 컴포넌트가 API 시크릿을 사용하여 요청에 서명해요. 뒤에서는 표준 쿼리 문자열을 빌드하고, 시크릿으로 HMAC-SHA256을 계산하고, signature 매개변수로 추가해요. 시작 시 키와 시크릿을 한 번 제공해요.

oHttp := TsgcHTTP_API_Binance.Create(Self);
oHttp.BinanceOptions.ApiKey    := FConfig.ApiKey;
oHttp.BinanceOptions.ApiSecret := FConfig.ApiSecret;
oHttp.BinanceOptions.RecvWindow := 5000;   // ms tolerance for signed requests
// Test connectivity and confirm your IP whitelist
ShowMessage('Server time: ' + IntToStr(oHttp.GetServerTime));

테스트넷에 대해 실행하려면 BinanceOptions.Testnet := True를 설정하세요 — 컴포넌트가 REST 기본 URL과 WebSocket 호스트를 자동으로 전환해요. 테스트넷에 대해 빌드하고 테스트하고, 단일 플래그를 뒤집어 프로덕션에 배포하세요. Binance API 문서는 그 외에는 두 환경 간에 동일해요.

5단계: 사용자 데이터 스트림

동일한 WebSocket 컴포넌트는 개인 사용자 데이터 스트림 — 계정 업데이트, 주문 이벤트, 포지션 변경 — 에도 구독해요. 이것은 봇 외부에서 발생한 채움(웹 UI에서 수동 취소, 청산 등)을 조정하는 방법이에요.

oBinance.AuthOptions.ApiKey    := FConfig.ApiKey;
oBinance.AuthOptions.ApiSecret := FConfig.ApiSecret;
oBinance.Streams.Add('!userData');

procedure TForm1.DoStream(Sender: TObject; const aStream, aData: string);
var
  oJSON: TsgcJSONObject;
begin
  if aStream = '!userData' then
  begin
    oJSON := TsgcJSONObject.Parse(aData);
    try
      if oJSON.S['e'] = 'executionReport' then
        FRisk.ReconcileExternalFill(oJSON);
    finally
      oJSON.Free;
    end;
  end;
end;

백테스팅에 대한 메모

위의 어떤 것도 "이 전략이 실제로 수익성이 있나요?"에 답하지 않아요. 그것이 백테스팅이 하는 것이에요 — 동일한 전략을 과거 데이터에 대해 다시 실행하여 미래 성능을 추정. 위의 아키텍처는 이것을 거의 무료로 만들어요: 전략 스레드는 시장 이벤트가 라이브 WebSocket에서 오는지 CSV 리더에서 오는지 신경 쓰지 않아요. 디스크에서 캔들을 읽고 동일한 큐에 공급하는 합성 이벤트 소스를 빌드하면 전략 코드가 수년의 과거 데이터에 대해 변경 없이 실행돼요.

피해야 할 두 가지 함정. Look-ahead bias: 전략이 처리하는 타임스탬프에 사용할 수 없었던 데이터 포인트를 엿보지 않도록 하세요. 그리고 생존 편향: 오늘날까지 살아남은 "성공적인" 심볼의 큐레이션된 목록이 아니라 그 당시 존재했던 심볼의 우주에 대해 학습하고 테스트하세요. 둘 다 코딩 버그를 합친 것보다 더 많은 전략을 프로덕션에서 죽였어요.

운영 체크리스트

관심사 처리 위치
Wi-Fi 드롭 시 재연결 WatchDog.Enabled := True
죽은 링크 감지 HeartBeat.Enabled := True
시간 동기화 (Binance가 왜곡된 서명 거부) OS의 NTP, 서버 시간 엔드포인트에 대한 일일 호출 포함
주문 멱등성 모든 주문에 newClientOrderId 사용
속도 제한 헤더 추적; 한도의 90% 이내일 때 백오프
킬 스위치 단일 부울, UI 또는 watchdog 프로세스에서 뒤집힘
감사 로그 모든 의도, 모든 채움, 모든 거부, 추가 전용

Binance를 넘어

TsgcWSAPI_BinanceTsgcWSAPI_Coinbase, TsgcWSAPI_Kraken 또는 다른 20+ 거래소 컴포넌트로 교체하세요. 전략, 위험 게이트 및 주문 워커는 변경되지 않아요 — 자격 증명 설정과 심볼 명명만요. 차트, 포지션 관리 및 주문 라우팅 UI가 있는 프로덕션 등급 다중 거래소 트레이더의 경우 sgcTrader 샘플을 살펴보세요.

실제 다중 거래소 시스템은 여기서 본 것 위에 한 계층을 더 추가해요: 심볼 정규화기. Binance는 BTCUSDT라고 부르고, Coinbase는 BTC-USD라고 부르고, Kraken은 XBT/USD라고 불러요. 표준 이름과 거래소별 별칭이 있는 내부 심볼 모델을 빌드하고 API 경계에서 번역하세요. 사전 5분 작업, 다운스트림에서 무한히 저장된 버그.

다중 거래소 운영을 위해 추가할 다른 것은 시작 시 시계 편차 검사예요. Binance, Coinbase 및 나머지는 서버에서 1000ms 이상 떨어진 타임스탬프가 있는 서명된 요청을 거부해요. NTP는 일반적으로 그 안에 잘 유지하지만 잘못 구성된 VPS는 한 시간에 몇 초 표류할 수 있어요. 시작 시 서버 시간 엔드포인트를 쿼리하고, 오프셋을 로깅하고, >500ms이면 거래를 거부하세요.

이것에 왜 Delphi인가요?

"왜 Python으로 작성하지 않나요?"는 명백한 질문이에요. 프로덕션에서 세 가지 답변. 첫째, JIT 워밍업과 GIL은 CPython을 저지연 이벤트 루프에 부적합하게 만들어요 — Delphi에서 0.8ms 중앙값 지연 시간에 도달하는 동일한 전략이 진지한 노력 없이 CPython에서 6ms 걸려요. 둘째, 배포 이야기가 더 간단해요: 하나의 서명된 exe vs 100개의 휠이 있는 virtualenv, 그 중 절반은 설치 시 C 컴파일러가 필요해요. 셋째, 기존 백 오피스가 Delphi에 있어요. 그 클래스 — 계정 원장, P&L 계산기, 저널, 감사 로그 — 를 다른 언어로 다시 구현하는 대신 새 봇에서 재사용하면 전체 조정 버그 카테고리가 제거돼요.

순수 연구 및 노트북 스타일 백테스트의 경우 Python이 쉽게 이겨요 — pandas, statsmodels, vectorbt 및 친구들의 생태계는 비교할 수 없어요. 대부분의 회사에 작동하는 분할: Python에서 연구, Delphi에서 프로덕션. 전략 로직을 작은 상태 머신으로 내보내고, 한 번 포팅하고, 실전 테스트된 Delphi 런타임에서 실행하세요. 두 반은 결과를 공유하기 위해 언어를 공유할 필요가 없어요.

다음에 읽을 것

VPS에서 이것을 24/7 실행할 계획이라면 다음으로 성능 튜닝을 읽으세요. 가장 일반적인 함정을 피하려면 10가지 일반적인 실수를 참조하세요. 그리고 아직 sgcWebSockets를 설치하지 않았다면 시작하기 허브가 5분 안에 라이브로 만들어 줄 거예요.

면책 조항: 이 게시물의 전략은 교육 목적이에요. 암호화폐 거래는 상당한 위험을 수반해요. 실제 자본으로 테스트되지 않은 코드를 배포하지 마세요.