본문 바로가기
Algorithm Trading/ComDon 프로그램 개발이야기

5. 주문딜레이 시간 체크 / 비동기 처리의 장점

by 컴돈AI 2024. 1. 9.

목차

주문딜레이 시간 체크

  • TradingView를 이용하여 자동매매를 구현하게 되면 다음과 같은 방법으로 주문 체결이 일어나게 됩니다.
    • TradingView -> Alert 발생 -> Comdon 프로그램 -> 바이낸스 API 호출 -> 바이낸스 거래 체결
  • 따라서 딜레이가 발생할 수 있습니다. 딜레이가 어느 정도 발생하는지를 체크를 해보도록 하겠습니다.
  • 또한 비동기의 장점을 살펴보기 위해서 동기 방식과 비동기 방식의 시간을 비교하도록 하겠습니다.
  • 파인스크립트 코드
    • 한번에 많은 주문을 발생시키기 위해 pyramiding을 1000으로 설정하고, calc_on_every_tick = true로 설정하였습니다.
    • //@version=5
      //@strategy_alert_message {{strategy.order.alert_message}}
      strategy("돌파 체크", overlay = true, calc_on_every_tick = true, pyramiding = 1000)
      
      int length = input.int(15, "Length")
      
      float highest = ta.highest(high, length)[1]
      float lowest  = ta.lowest(low, length)[1]
      
      if close[0] >= highest
          strategy.entry("Buy", strategy.long,alert_message = "[hieghest]  현재가 : "+ str.tostring(close[0]))
      if close[0] <= lowest
          strategy.entry("Sell", strategy.short,alert_message = "[lowest]  현재가 : "+ str.tostring(close[0]))
      
      var realTimeStart = timenow
      
      bgcolor(time_close >= realTimeStart ? color.new(color.orange, 80) : na)
      
      plot(highest, "Highest", color = color.lime)
      plot(lowest, "Lowest", color = color.red)
    • entry 신호가 발생하면 alert_message에 있는 내용에 Comdon 프로그램으로 전송되게 됩니다.

동기 방식

  • 동기 코드
    • from fastapi import FastAPI, Request, HTTPException
      from contextlib import asynccontextmanager
      from datetime import datetime
      import uvicorn
      import ccxt
      
      
      @asynccontextmanager
      async def lifespan(app: FastAPI):
          print("서버접속")
          yield
          print("서버종료")
      
      
      app = FastAPI(lifespan=lifespan)
      
      
      ALLOWED_IPS = {"52.89.214.238", "34.212.75.30", "54.218.53.128", "52.32.178.7"}
      
      
      @app.middleware("http")
      async def ip_filter_middleware(request: Request, call_next):
          client_ip = request.client.host
          if client_ip not in ALLOWED_IPS:
              raise HTTPException(status_code=403, detail="IP Address not allowed")
          response = await call_next(request)
          return response
      
      
      @app.post("/webhook")
      async def webhook(request: Request):
          alert_message = await request.body()
          alert_message_str = alert_message.decode("utf-8")
          alert_time = datetime.now()
          print(alert_message_str)
      
          exchange = ccxt.binanceusdm(
              {
                  "apiKey": "",
                  "secret": "",
                  "enableRateLimit": True,
              }
          )
      
          # buy, sell
          order = exchange.create_order("ETH/USDT", "MARKET", "buy", 0.009)
          print(f"alert_time : {alert_time}, complete time : {datetime.now()} ")
      
          return {"message": "Received"}
      
      
      if __name__ == "__main__":
          uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
      • async와 await을 사용했지만, 실질적으로 주문을 하는 코드인 exchange.create_order 부분이 동기 코드이기 때문에 주문이 체결되는 시간 동안 여기서 전체 프로그램이 대기하게 됩니다. 즉, 동기적으로 코드가 작동하게 됩니다.
  • 결과
    • 얼러트 로그
    • 출력 결과
    • 시간을 보게되면 4시 31분 31초에 한 번에 많은 주문(alert_message)이 들어온것을 볼 수 있습니다. 하지만 이제 동기적으로 코드를 짠 경우 하나의 alert 에 대해서 체결이 완료되어야지만 다음 alert 가 체결되기때문에 31초에 발생한 alert가 주문이 밀려 35초에 체결된것을 확인할 수 있습니다.
      • 즉, 동기적으로 코드를 작성한 경우 delay가 심하게 발생할 수 있습니다. (초단타는 어려울 수 도 있습니다.)

비동기 방식

  • 비동기 코드
    • from fastapi import FastAPI, Request, HTTPException
      from contextlib import asynccontextmanager
      from datetime import datetime
      import uvicorn
      import ccxt.async_support as ccxt
      
      
      @asynccontextmanager
      async def lifespan(app: FastAPI):
          print("서버접속")
          yield
          print("서버종료")
      
      
      app = FastAPI(lifespan=lifespan)
      
      
      ALLOWED_IPS = {"52.89.214.238", "34.212.75.30", "54.218.53.128", "52.32.178.7"}
      
      
      @app.middleware("http")
      async def ip_filter_middleware(request: Request, call_next):
          client_ip = request.client.host
          if client_ip not in ALLOWED_IPS:
              raise HTTPException(status_code=403, detail="IP Address not allowed")
          response = await call_next(request)
          return response
      
      
      @app.post("/webhook")
      async def webhook(request: Request):
          alert_message = await request.body()
          alert_message_str = alert_message.decode("utf-8")
          alert_time = datetime.now()
          print(alert_message_str)
      
          exchange = ccxt.binanceusdm(
              {
                  "apiKey": "",
                  "secret": "",
                  "enableRateLimit": True,
              }
          )
      
          # buy, sell
          order = await exchange.create_order("ETH/USDT", "MARKET", "buy", 0.009)
          print(f"alert_time : {alert_time}, complete time : {datetime.now()} ")
      
          await exchange.close()
      
          return {"message": "Received"}
      
      
      if __name__ == "__main__":
          uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
       
      • import ccxt.async_support as ccxt를 통해 손쉽게 ccxt라이브러리를 비동기 코드로 작성할 수 있습니다.
      • 또한 주문 체결 코드 앞에 await을 붙여줘야지만 주문체결을 대기하는 상태에서 다른 주문을 추가로 받을 수 있게 됩니다.
  • 결과
    • 얼러트 로그
    • 출력 결과
    • 시간을 살펴보게 되면 한 번에 아주 많은 alert 주문이 들어오더라도 많은 alert 주문이 비동기적으로 체결되는 것을 확인할 수 있습니다. (즉, 여러 주문이 들어와도, 주문 체결을 대기하는 동안 다른 주문이 밀리지 않고 다른 주문들도 접수가 됩니다.)
      • alert가 발생하고 alert가 발생한 주문에 대해 체결되지 않았지만, 또 들어온 alert에 대해서 주문 접수를 넣을 수 있습니다. (cf. 동기 코드에서는 alert가 발생하면 해당 alert가 발생한 주문이 체결될때까지 다른 들어온 alert에 대해서 주문 접수를 넣지 못하고 대기하고 있어야합니다.)

동기 vs 비동기 결과

  • 위 결과들을 살펴보면 하나의 alert가 발생한 뒤 실제로 체결되는데 약 0.5~0.7초 정도 소요되는 것을 확인할 수 있습니다. 
  • 만약 동기적으로 코드를 구현한다면, 한번에 100개의 주문이 동시에 들어오게 된다면 하나씩 처리해서 아무리 빨라도 모든 주문이 체결되는데 최소 50초 이상 걸릴 것입니다.
    • 하나의 주문을 처리하는데 0.5초라고 가정하면 동기적으로 0.5 * 100  = 50초 정도 소요됩니다.
  • 하지만 비동기적으로 코드를 구현할 경우, 거의 동시에 모든 주문을 접수할 수 있기 때문에 한 번에 100개의 주문이 들어오더라도 모든 주문이 거의 1 초정도안에 해결되게 될 것입니다.
  • 따라서 신호가 발생하자마자 체결하는 것이 중요한 자동매매에서는 비동기적으로 코드를 작성하는 것이 필수적입니다.

 

 

 

 

'Algorithm Trading > ComDon 프로그램 개발이야기' 카테고리의 다른 글

7. 이베스트투자증권 OPEN API  (0) 2024.01.22
6. .env 파일 작성  (0) 2024.01.21
4. CCXT 라이브러리  (0) 2023.12.30
3. webhook 연결(포트포워딩)  (1) 2023.12.29
2. TradingView PineScript  (0) 2023.12.23