brunch

You can make anything
by writing

C.S.Lewis

by 엔데 Oct 25. 2021

2. 코인 자동매매 봇 만들기(1)

첫 번째로 고민했던 것은 대략적인 코드의 흐름이었다. 그전부터 코딩을 조금조금씩은 할 줄 알았지만, '24시간 돌아가게' 설계하는 건 전혀 다른 문제였다. 얼마나 빈번하게 거래할 것이냐도 문제고, 오류처리도 걱정해야 했다. 일단 아래 그림은 코드의 대략적인 흐름이다.

굉장히 심플하지만 전체 코드를 이해할 때 도움을 줄 것이다.

먼저, 코드는 계속해서 끊임없이 돌아간다. 그리고 설정한 시간이 되면 모든 코인에 대해 반복하는(약 100번 정도) 코드가 실행된다. 먼저 OHLCV(Open/High/Low/Close/Volume) 데이터를 파이썬 데이터 프레임으로 받는다. 그리고 그 데이터 프레임에 로직에 필요한 보조지표를 추가한다. 이때 해당 코인의 잔고가 없으면 Buy logic을 적용시키고, 잔고가 있다면 Sell logic을 적용시킨다. Buy logic에서 사기로 결정했다면 산 가격에서의 Stoploss(이 선 아래로 내려가면 판다)와 target 금액을 설정한다. 또한 Sell logic에서는 목표한 Target 가격에 올라갔거나, 가격이 Stoploss 아래로 내려간 경우 Sell 주문을 한다. 결과는 별도의 데이터 프레임에 저장되어 다음 실행에서 사용된다.

* 데이터 프레임: 파이썬에서 쉽게 정보를 저장, 처리할 수 있는 저장공간. 엑셀 스프레드시트와 비슷하다.

이렇게 해놓은 이유는 가장 큰 이유는 코드를 고치기 쉽게 하기 위해서였다. 만약 코드를 수정할 일이 있다면, 다른 건 건드리지 않고 로직만 수정하면 된다. 보조지표만 수정하고 싶다면 보조지표 부분만 수정한다. 


이제 실제로 코드 부분으로 넘어가 보자

코드의 가장 바깥에 위치하는 파라미터 부분이다.

먼저, 캔들차트의 어느 봉(시간 프레임)을 사용할지 설정한다. 여기선 15분 봉을 사용할 예정이다. 

pyupbit에서 모든 코인의 티커(이름)를 받는다. 이때 티커는 리스트로 반환된다. 

df_master는 코인 거래에 필요한 모든 정보를 담고 있다. 열은 코인 별로 분류되어 있고, 인덱스에는 원하는 정보들을 넣어놓을 수 있다. 위의 사진은 현재 돌리고 있는 보조지표를 보여주고 있다. 특히, 사고 팔 고의 주문 정보나 open position( 산 다음에 파는 형태로 변하는 것), stoploss나 target 금액 등을 저장해 놓는다. 매번 갱신된다.

access와 secret 은 업비트에서 제공하는 api를 사용하여 매매할 때 필요한 아이디와 비번 같은 것이다. 업비트 홈페이지에서 발급받을 수 있다.

참고로 df_master는 이렇게 생겼다.

코인이 총 105개나 된다.....


이렇게 파라메터 설정이 끝나면, 이제 끊임없이 계속 돌아갈 봇의 뼈대를 세우는 작업을 할 차례다. 솔직히 말해서 이것보다 좀 더 우아하게 짤 수 있다고 생각이 되는데, 나는 개코도 모르겠다. 며칠 머리를 싸맨 결과가 아래와 같다. 빨간색 동그라미 숫자의 순서로 설명할 것이다.

첫 번째로, 먼저 while에 True를 적용해서 계속해서 코드를 돌릴 것이다. 

그런데 너무 빨리 돌아가면 오류를 일으키니, 한번 코드가 끝나면 8초간 쉬어줄 것이다.

Try-catch를 써서 오류를 잡아준다. 그리고 그냥 지나간다. 코드를 돌리다 보면 JSON 에러가 많이 발생하는데, 이때마다 멈추면 개빡친다. 처음엔 내가 코딩 잘못한 줄 알았는데, 그냥 JSON error는 업비트 잘못이다.

네 번째는 '원하는 시간'에 실행하기 위해 필요한 부분이다. get_time_info라는 함수는 내가 만든 함수이다. 뜻은 '15분 간격으로 시작하는데 15분 정각에서 840초만큼 지났을 때 실행할 겁니다'라고 하는 것이다. 굳이 이렇게 하는 이유는 조금 있다가 설명할 것이다.

if 문으로 현재 시각(clock)이 alt_start_time과 alt_end_time사이에 있으면 매매 코드를 실행한다. 이때 start와 end의 시간 차이는 10초이므로, 8초마다 쉬는 while구문은 한드시 한 번은 실행된다.(물론 때에 따라서 2번 실행될 수도 있다. 더 좋은 아이디어가 없을까?)

그 외의 시간에는 그냥 지나간다.

그다음부터는 모든 코인에 대해 for 문을 사용해서 알고리즘을 적용하는 일이다. 물론 지금 쓰는 버전에서는 이런 식으로 하지는 않지만, 이해를 위해 이런 식으로 표기했다.


이렇게 코드의 전체적인 뼈대가 정해진다.

그리고 get_time_info 함수는 다음과 같다.

get_time_info는 원하는 시각을 받는 부분과, 정각을 기준으로 얼마나 있다가 코드를 실행할지를 받아서 '시작 시각' , '끝나는 시각', '현재 시각'을 뱉어주는 함수다.

이게 좀 골 때리는데, 시각을 얻는 방법이 아무 코인(여기서는 비트코인을 사용)의 ohlcv데이터를 받은 다음, 거기서 가장 아래 있는 최근 행의 인덱스를 뽑아오는 방식으로 하는 것이다. 내 미천한 지식으로는 현재 시각을 기준으로 몇 분 주기로 실행할 수 있는 방법을 찾지 못했다..... 이 부분이 내가 한 코딩 중에서 가장 멍청하다고 생각한다.

여하튼, 이렇게 시각 데이터를 뽑아서, 원하는 시각만큼 코드 실행 시간을 뒤로 미뤄줘야 한다. 왜냐하면, 정각을 기준으로 계산되는 ohlcv데이터는 정각 부근에서 정확하지 않기 때문이다. 

좌) 정각+1분 에 출력한 ohlcv 정보                                       우) 정각+4분에 출력한 ohlcv정보  

위의 사진은 비트코인의 5분 봉 ohlcv데이터를 서로 다른 시간에 출력한 것이다. 왼쪽은 정각에 가깝게(22시 51분), 오른쪽은 정각 다음에 가깝게(22시 54분) 결과다. 이때 똑같은 최신 데이터인 22시 50분 값의 close값이 다른 걸 볼 수 있다. 또한 거래된 양인 volume도 왼쪽이 턱없이 작다. 그러니까 정보가 취합되는 도중인 가장 '최신'데이터는 가능하면 다음 정각에 가깝게 해야 더 알맞은 데이터를 찾을 수 있는 것이다. 그렇다고 너무 가까워서 코드가 실행되는 도중에 정각을 넘어가버리면 또 이상한 값을 받게 된다. 통상적으로 100개 모두 돌리는데 30초 정도 소요되므로, 1분 정도 여유 있게 시작한다. 

그러니까 15분 봉을 할 때는 (15-1) X60 = 840초 있다가 코드를 실행하고, 5분 봉일 때는 (5-1) X60 = 240초 를 기다렸다가 코드를 실행해야 하는 것이다.

(참고로 초기 코드에서 돈을 다 말아먹게 한 문제가 이것이었다. 엉뚱한 때 매수 신하고 자꾸 뜨길래 한참 골머리 앓다가, 우연히 발견했다. 분명 같은 ohlcv데이터를 사용했는데 다른 결과가 나온 것이다.)


여하튼, 이렇게 내가 만든 비루한 자동 매매 봇의 뼈대를 설명했다. 아무쪼록, 더 좋은 아이디어가 있으면 누구든 알려주시기 바란다. 다음에는 뼈대에 붙을 살코기(?!) 부분을 설명하도록 하겠다.


감사합니다.(아래는 전체 코드)




매거진의 이전글 1. 코인 자동 매매를 시작하다
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari