주식 자동매매 프로그램 만들기 - 매수조건 전략 때문에 매수 안됨 (고장 아님)

 

자동매매 봇 로그에 매수 없이 차단 기록만 이어지는 화면

조건검색까지 연동하고 나니, 이제 봇이 알아서 종목을 잡아왔다. 나는 내심 기대했다. 이제 신호가 뜨면 알아서 사겠지. 드디어 손을 뗄 수 있겠지.

그런데 그날, 봇은 하루 종일 단 한 주도 사지 않았다. 매수 로그를 아무리 뒤져도 orders=0. 종목은 잘만 잡아오는데 매수는 0건이었다. 나는 봇이 고장 났다고 확신했다. 하지만 원인은 고장이 아니라, 내가 걸어둔 매수 조건 필터에 있었다.

목차

분명히 종목은 잡히고 있었다

로그를 열어보면 봇은 전혀 놀고 있지 않았다. 조건에 맞는 종목이 실시간으로 계속 들어왔다. 새로 편입된 종목이 감시 목록에 붙고, 점수가 매겨지고, 상단으로 올라오고. 화면은 쉴 새 없이 움직였다.

✅ watchlist 추가(REAL_IN): 종목 편입 / total=150
🟣 [BREAKOUT_TRACK] 종목 돌파 감시 시작
📈 [SCAN_SUMMARY/60s] ticks=3034 codes=50 signals=0(dry=0) orders=0

1분에 수천 건씩 실시간 데이터(ticks)가 들어오고, 수십 개 종목(codes)이 감시되고 있었다. 봇은 초당 수십 번씩 판단을 하고 있었던 것이다. 그런데 그 마지막 숫자, signals=0, orders=0. 신호도 없고, 주문도 없다. 하루 종일 그랬다. 장이 열리고 닫힐 때까지 이 두 숫자는 단 한 번도 0을 벗어나지 않았다.

나는 봇이 고장 났다고 확신했다

솔직히 처음 든 생각은 하나였다. 아, 또 어디가 터졌구나.

이렇게 종목을 잘 잡아오는데 한 건도 안 산다는 게 말이 되나 싶었다. 매수 로직 어딘가에서 조용히 죽었거나, 조건을 잘못 넣어서 영영 참(true)이 안 나오는 상태일 거라고 생각했다. 그래서 나는 매수 함수부터 열어봤다. 조건식을 몇 번이나 다시 읽었다. 값을 찍어보고, 순서를 바꿔보고.

그런데 아무리 봐도 코드는 멀쩡했다. 죽은 흔적이 없었다. 봇은 분명히 살아서, 매 순간 뭔가를 판단하고 있었다. 나는 엉뚱한 데를 파고 있었던 것이다.

로그가 이미 답을 말하고 있었다

한참을 헤매다 나는 매수 로그가 아니라 매수가 막힌 로그를 보기 시작했다. 그러자 화면이 온통 눈사람 이모지로 뒤덮여 있었다.

🧭 [REGIME 변경] NEUTRAL → BEAR | breadth=0.0%
🧊 [매수차단] 종목 | market_block:market_not_ready
🧊 [매수차단] 종목 | cluster_block:heat=0 need=3 regime=BEAR
🧊 [매수차단] 종목 | market_block:KOSPI_ETF=NO | KOSDAQ_ETF=NO | mode=OR

봇은 죽은 게 아니었다. 사려다가, 매번 스스로 막고 있었던 것이다. 저 눈사람(🧊)은 전부 "이 종목 사려고 봤는데, 지금은 안 돼"라는 기록이었다. 하루치 로그에서 이 차단 기록만 수천 줄이었다.

사는 데는 여러 개의 문이 있었다

차단 로그를 읽고 나서야 나는 내 봇의 구조를 눈으로 다시 봤다. 봇이 종목 하나를 실제로 사기까지는, 통과해야 하는 문이 여러 개였다. 그날 로그에 찍힌 차단 사유만 봐도 몇 갈래였다.

시장 전체가 힘이 없으면 막는 문(market_block), 여러 종목이 같이 강해질 때만 열리는 문(cluster), 그리고 그날의 시장 국면을 약세장으로 볼지 강세장으로 볼지 판정하는 문(regime). 그날은 아침부터 시장이 BEAR로 찍혔고, 그 밑에서 이 문들이 하나씩 종목을 돌려보내고 있었다. 후보는 넘쳐났지만, 이 문들을 다 통과한 종목이 하루 종일 하나도 없었던 것이다.

이 문 하나하나가 왜 거기 있는지, 내가 왜 굳이 이렇게까지 겹겹이 막아뒀는지는 이 글 한 편에 다 담기 어렵다. 그래서 앞으로 한 편씩, 이 문들을 하나하나 열어보려 한다. 오늘은 "문이 여러 개다"라는 것까지만.

안 사는 것도 봇이 일하는 것이다

여기서 나는 처음으로 내 봇의 핵심 원칙을 눈으로 확인했다. 내가 코드 맨 앞에 적어둔 한 줄이 있었다. SCAN은 BUY가 아니다. 후보를 찾는 것과 실제로 주문을 내는 것은 완전히 다른 일이라는 뜻이다.

봇이 종목을 잡는 건 그냥 "이건 봐둘 만하다"는 후보 선정일 뿐이다. 거기서 실제 매수까지 가려면 앞서 말한 여러 겹의 문을 다 통과해야 한다. 그날은 후보는 넘쳐났지만, 문을 통과한 종목이 하나도 없었을 뿐이다.

혹시 지금 자동매매 봇을 만들었는데 하루 종일 한 건도 안 사서 "이거 고장 난 거 아닌가" 불안한 사람이 있다면, 매수 로그가 아니라 매수가 차단된 로그부터 봐라. 대개 봇은 죽은 게 아니라, 안 사기로 결정한 것이다.

그래서 나는 이때부터 1분마다 딱 한 줄을 찍게 했다. 오늘 몇 개를 봤는지(codes), 신호가 몇 개 떴는지(signals), 주문이 몇 개 나갔는지(orders), 그리고 막혔다면 무슨 사유였는지(block reason). 위에서 본 [SCAN_SUMMARY]가 바로 그 한 줄이다. 이 줄 하나만 있으면 봇이 멈춘 건지, 살아서 안 사기로 한 건지를 5초 만에 구분할 수 있다. signals까지 0이면 애초에 신호가 안 뜬 것이고, signals는 있는데 orders가 0이면 사려다 문에서 막힌 것이다. 이 구분이 안 되면, 나처럼 멀쩡한 봇을 붙잡고 애먼 코드만 몇 시간씩 뜯게 된다.

이 단계에서 배운 것

손으로 사고팔 때 나는 하락장에서도 뭐라도 사야 할 것 같은 조바심에 자꾸 손이 나갔다. 그게 내 손가락의 문제였다. 봇에게 손을 넘긴 이유가 바로 이거였는데, 정작 봇이 아무것도 안 사자 이번엔 내가 그 봇을 의심하고 있었다.

돌아보면 그날 봇은 완벽하게 일한 것이다. 살 이유가 없는 날, 단 한 주도 사지 않는 것. 그게 내가 그토록 갖고 싶었던 규율이었다. 문제는 봇이 아니라, 그 규율을 못 믿고 코드를 뜯던 나였다.

후보는 넘쳐나는데 정작 왜 한 건도 통과가 안 됐는지, 그 문 하나하나가 어떤 기준으로 종목을 돌려보내는지. 그 첫 번째 문 이야기를 다음 글에서 풀어보려 한다.

이 글은 내가 직접 만든 프로그램의 개발 기록일 뿐, 특정 종목이나 매매 방식을 권하는 글이 아니다. 투자 판단과 그 결과는 각자의 몫이다.

Post a Comment

다음 이전