어쩌다 올초에 비트코인에 투자를 했다. 처음에 재미삼아 1비트를 사봤는데 계속 떨어져서 물타기를 하다가 금액이 커져서 그냥 냅두게 됐었다. 그게 4, 5월 엄청 폭등하는 바람에 수익을 좀 봤다. 이런 사실을 아는 출판사 다니는 친구 녀석이 자기 회사에서 주식 자동매매 책이 잘 팔린다고 암호화폐 자동매매 책을 써 볼 생각이 없냐는 제안을 했다. 그렇게 어쩌다보니 봇 거래를 해보게 됐다.
사실 전업 프로그래머 입장에서 투자 전략이야 천차만별이지만 시스템 트레이딩 코드를 만드는 건 너무 쉽다. 게다가 포인터를 다뤄야 하는 C/C++도 아니고 파이썬이면 충분하기 때문에 뻥 좀 보태서 한 시간이면 봇을 만들 수 있다. 요는 코딩이 어려운 건 아니고 전략이 어렵다는 의미다. 어쨌든 30분 정도 REST API를 훓어보고 봇으로 거래를 해보게 됐다. 당연한 얘기지만 된다. 거기에 내가 생각한 한 가지 아이디어를 넣어서 거래를 해보게 했더니 수익이 발생하는게 아닌가? 긴 추석 연휴 기간 동안 봇을 조금 다듬었다. 그렇게 25일이 됐고 봇이 나에게 월급을 줘야 하는 시점에 봇의 성적표는 다음과 같다. 각각 날짜, 비트코인봇 수익, 비트코인봇 거래회수, 이더리움봇 수익, 이더리움봇 거래회수, 토탈 수익, 토탈 거래회수, 누적 수익이다.
bot result
생각보다 많을 수도 있고, 적을 수도 있는데 나는 이 봇의 이름을 최저임금봇이라 명명했다. 시급 만원 정도를 추종하도록 설계됐다. 물론 시장 상황이 좋으면 더 벌기도 하고, 나쁘면 덜 벌기도 하는데 대략 그 정도 수준에서 24시간 돌면서 거래를 한다. 재미난 건 상수 값 몇 개 바꾸면 저 단위가 10배가 될 수도 있다는 점이다. 물론 내가 쪽빡찰 확률은 100배가 되겠지만 말이다. 여기까지만 쓰면 단순 자랑질 이상도 이하도 아닐 것이다. 그래서 트레이딩 봇을 만들면서 알게된 점 몇 가지를 정리해 본다. 분명 이런 시스템을 만드려는 금융 쪽 종사자가 아닌 프로그래머가 있다면 도움이 되리라 확신한다.
삼위일체
처음에 봇을 만들고 가동시키자 봇이 엉뚱한 실수를 계속 했다. 분명 50원에 샀으면 100원에 팔라고 코딩을 해놨는데 계속 40원에 팔면서 수익을 내고 있다고 착각하고 있는 것이었다. 나같은 프로페셔널한 프로그래머가 if문 따위를 잘못 코딩할리도 없는데 말이다. 부랴부랴 Ctrl + C를 눌러서 봇을 껐지만 제법 손실이 난 후였다. 이 버그 아닌 버그를 찾는데 가장 많은 시간이 들었는데 원인은 다름아닌 트랜잭션에 있었다.
내가 상상한 퐌타지한 세계는 그랬다. 거래소에서 나에게 제공하는 모든 데이터는 수직 동기화가 되어 있을 줄 알았다. 즉 그 사이게 시간 차가 존재할 줄 몰랐다는 의미다. 하지만 현실 세계는 그렇지 않았다. KRW 잔고가 먼저 반영되기도 하고, BTC 잔고가 그 다음에 움직이기도 하고, 거래 트랜잭션 데이터는 몇 십초가 있다가 확정되기도 했다. 즉 이 사이에는 어느 정도 시간적인 불일치가 있었다. 이걸 한쪽 데이터만 보고 판단해서 거래를 하면 나처럼 낭패를 볼 수 밖에 없다. 검산을 해야 한다. KRW 잔고와 BTC 잔고와 거래 내역이 모두 일치하는 삼위일체의 상태에 있을 때에 수익을 계산하고 베팅을 해야 한다.
부동소수점?
이 오류가 정확하게 부동소수점 때문인지 아니면 내 계산 방식 때문인지는 모르겠지만 위 삼위일체 검산 로직을 추가해도 정확하게 데이터가 일치하지 않았다. 나는 그냥 편하게 부동소수점 때문이라고 판단했는데 1사토시 근처에서 오류가 있는 경우가 많았다. 그래서 그냥 0.00000001 정도의 차이는 동일하다고 간주했다.
하락장을 고려해야 한다
상승만 한다면 바보 같은 봇들도 모두 수익을 낼 수 있다. 그리고 어렵게 봇을 만들 필요도 없다. 사두고 장기 보유하면 되기 때문이다. 하지만 시장은 항상 그렇듯이 오르기도 하고 내리기도 한다. 암호화폐 시장은 그 변동성이 더 크다. 그래서 봇 거래가 수익을 내기 좋은 환경이기도 하다. 어쨌든 하락장을 고려해야 한다.
처음 만든 봇은 그저 오른다는 가정하에 만들었고 시장 상황이 계속 올라서 참 쉬웠다. 그러다 하락장이 왔는데 봇은 어쩔줄을 몰라 했다. 출구전략이 필요했다. 그런데 이론적으로 주구장창 하락한다면 일반 거래로 수익을 얻을 방법은 없다. 여기서 일반 거래란 공매도 같은 장치가 없음을 의미한다. 따라서 출구 전략은 단순할 수 밖에 없다. 더 사서 물타기를 할지 아니면 손절을 할지를 판단하는게 전부다. 여러가지 전략이 있을 수 있는데 나도 5가지 정도의 전략을 테스트 해보았다. 갈수록 산수에서 수학으로 가는 내용들이 도입됐고 코드도 복잡해졌다. 그런데 아니러니 하게도 수익률은 두번째 방법이 가장 좋았다. 그래서 그냥 그걸로 적용해 두었다.
인간은 개입하지 말지어다
위에서 말한 하락장에서 봇의 손실이 제법 커졌다. 사람은 당연히 불안해질 수 밖에 없다. 봇은 사지도 팔지도 못하고 있었다. 그러다 코인 가격이 반등되어 적당한 가격이 왔다. 봇은 아직 팔 때가 아니라서 거래를 하고 있지 않았다. 사람인 나는 조급해질 수 밖에 없었다. 봇이 사들인 코인의 대부분을 이 시점에 손절해 버렸다. 봇은 회복할 수 없는 타격을 입었다. 봇은 내가 저지른 만행을 복구할 수가 없는 지경이 됐다. 결국 나는 봇의 수익률 추종치를 리밸런싱할 수 밖에 없었다. 이게 비트코인 봇인데 원래 비트코인 봇이 이더리움 봇 보다는 수익률이 월등했는데 이 당시의 내 개입으로 인해서 구려졌다.
봇이 어쩔 줄 몰라 하면 봇을 고쳐서 대응하면 된다. 절대 수동 트레이드로 개입할 필요가 없다. 왜냐하면 내가 느끼는 감성적인 수치와 봇이 정확하게 계산한 수치 사이에는 항상 간극이 존재할 수 밖에 없기 때문이다. 감정적인 대응의 결과는 손실 밖에 없다.
거래 수수료 트레이프오프
봇 트레이딩의 가장 큰 리스크는 다름 아닌 거래 수수료다. 봇이라서 엄청나게 많은 회수의 거래를 하는데 그때마다 수수료가 발생하기 때문이다. 상당수는 거래 볼륨으로 커버가 가능한데 대부분의 거래소가 거래 볼륨이 커질 수록 수수료가 낮아지도록 책정되어 있기 때문이다. 거래소에 따라서 일정 수준을 넘어서면 수수료 없이 거래하는 것도 가능하다. 그런데 문제는 그 수수료가 한 가지가 아니라 두 가지란 점에 있다. 모든 거래소 시스템이 비슷한 방식의 수수료를 책정하고 있는데 일명 메이커, 테이커 비용이다.
시장에 유동성을 공급하는 거래에는 메이커, 유동성을 없애는 거래에는 테이커 비용을 물린다. 이게 거의 수수료가 없는 쪽은 메이커 비용이고, 테이커 비용은 수수료가 높다. 테이커 비용에 물리게 되면 사실상 단타 매매로 수익을 내기가 굉장히 어려워진다. 문제는 이 테이커 비용을 최대한 덜 내게 하는 방법인데 이게 쉽지가 않다. 그런 API가 존재하면 좋겠지만 거래소 수익 때문인지 그런 API를 제공하지 않는다. 내가 주문을 낼 때에는 멀쩡한 메이커 주문이었는데 그게 거래소 트랜잭션 시스템에서 직렬화 되면서 테이커 비용을 물게 되는 경우가 왕왕있다.
당연한 얘기겠지만 되도록 이 테이커 비용을 피하면서 거래를 성사시키는 것이 중요하다. 그런데 문제가 그렇게 간단하지는 않은 것이 거래를 성립시키려면 테이커 비용을 무는 쪽으로 다가 가야 하기 때문이다. 즉, 이 사이에는 트레이드 오프가 있다. 거래를 잘 성립하게 만들다보면 테이커 비용을 무는 경우가 많고, 그렇다고 완전 회피하려면 유동성만 공급할 뿐 거래가 성사되지 않기 때문이다. 즉, 최대한 원활하게 거래를 성립시키면서 테이커 비용은 최대한 피하는 그런 방법을 만드는 것이 중요하다.
두려움을 넘어서
이 모든 봇 거래의 가장 큰 어려움은 두려움이다. 이 단순한 봇도 내 전재산의 무시 못할 수준의 금액을 가지고 도박을 한다. 내가 자는 동안에 위에서 말한 삼위일체 문제 같은게 발생한다면 거지되는 것이다. 그 두려움이 처음 3일 정도는 있었던 것 같다. 이제는 어느 정도 봇을 믿게 됐다. 특히 내가 한 수동 거래가 병신짓으로 판명난 이후에는 봇을 더 신뢰하게 됐다. 어쨌든 자신이 만든 버그가 현실 세계에서 어떻게 발현될 수 있는지 확인해 보고 싶은 프로그래머라면 도전해 볼 만한 영역인 것 같다. 물론 수익은 덤이다. 재미삼아 시작했는데 결과가 나쁘지 않아 누적 수익이 0이 될 때까지는 봇 실험을 계속해 볼 생각이다.