목차
주문딜레이 시간 체크
- 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 |