테일샷의 시그널 감지 로직을 전면 개편했다. 핵심은 하나다. 이분법적 필터를 연속적인 점수 체계로 바꿨다.
기존 로직은 칼로 자르듯 판단했다. EMA가 완전 정배열(FULL)이 아니면? 탈락. 직전 3봉에 반대방향 대량 캔들이 있으면? 탈락. 통과하거나, 죽거나.
문제는 "거의 정배열"인데 탈락하는 시그널이 너무 많았다는 것이다. EMA 10이 20보다 살짝 아래에 있을 뿐인데, PARTIAL이라는 이유로 아예 보지도 않았다. 충돌 캔들도 마찬가지. 거래량 2.01배짜리 약한 충돌 하나 때문에 꼬리 5배 + 거래량 8배짜리 좋은 시그널이 버려졌다.
세상은 0과 1이 아니라 0에서 100 사이 어딘가에 있다. 점수 체계는 그 어딘가를 표현할 수 있다.
기존 로직 vs 개선 로직
| 항목 | 기존 | 개선 |
|---|---|---|
| 방향 결정 | EMA 배열 기반 (역배열 → LONG) | 캔들 꼬리 비교 (아랫꼬리 > 윗꼬리 → LONG) |
| EMA 배열 | FULL만 허용. 나머지 전부 탈락 | FULL/PARTIAL/NONE 전부 감지. 점수로 차등 |
| 충돌 캔들 | 하드필터. 1건이라도 있으면 SKIP | 카운트만 기록. 점수로 감점 |
| EMA 스프레드 | 계산 안 함 | 계산하여 점수 반영 |
| 판단 방식 | 통과/탈락 이분법 | 100점 만점 종합 점수 |
방향 결정의 변화
기존에는 EMA 배열로 방향을 정했다. EMA 역배열이면 LONG, 정배열이면 SHORT. 추세의 반대 방향으로 반전 진입하는 논리다.
개선 후에는 캔들 자체가 말하게 했다. 아랫꼬리가 윗꼬리보다 길면 LONG(매수세가 밀어올렸다), 반대면 SHORT. 가격 행동이 직접 방향을 알려준다.
100점 만점 점수 체계
네 가지 항목으로 종합 점수를 매긴다.
| 항목 | 배점 | 근거 |
|---|---|---|
| 꼬리 비율 | 30점 | 백테스트 승률 기반. 승률 70% 이상이면 30점, 40% 미만이면 13점 |
| 거래량 | 30점 | 실제 트레이드 피드백 승률 기반. 데이터 없으면 거래량 배수로 대체 |
| EMA | 20점 | 배열 유형(FULL 10점, PARTIAL 8점, NONE 5점) + 스프레드(3%+ 10점, <1% 3점) |
| 충돌 | 20점 | 0건 20점, 1건(약) 15점, 1건(강) 10점, 2건+ 5점 |
70점 이상만 AI 분석으로 넘어가고, AI가 6점 이상을 줘야 최종 진입한다. 이중 관문이다.
백테스트가 딴 세상에 살고 있었다
Scanner를 고치고 나서 깨달았다. 백테스트가 아직 기존 로직으로 돌고 있다.
비유하면 이런 상황이다. 시험장에서는 5과목 성적을 종합해서 합격 여부를 판단하는데, 모의고사에서는 수학 한 과목만 보고 떨어뜨리고 있었다. 모의고사 성적(백테스트 승률)으로 본시험(실전) 합격률을 예측하고 있었으니, 그 예측이 정확할 리 없다.
Scanner가 "PARTIAL EMA에서의 승률"을 조회해도 백테스트에 데이터가 없다. 데이터가 없으면 기본값(21점)이 들어간다. 실제 승률이 30%든 70%든 전부 21점. 점수 체계를 아무리 정교하게 만들어도, 원본 데이터가 틀리면 의미가 없다.
백테스트와 실전의 로직이 다르면, 백테스트 승률은 거짓말이다.
동기화 작업
1. 백테스트 워크플로우 (v2 → v3)
Scanner의 Analyze_TailShot 노드와 동일한 로직으로 전면 교체했다.
- 방향 결정: EMA 배열 기반 → 캔들 꼬리 비교
- EMA 하드필터 제거: PARTIAL, NONE도 시그널로 감지
- 충돌 하드필터 제거: 충돌이 있어도 시그널 감지, 데이터만 기록
- 새 데이터 수집:
ema_align_type,ema_spread,conflict_count,max_conflict_vol - 스캔 범위 확대: 200봉(8일) → 1000봉(41일)
2. DB 스키마 확장
alt2_backtest_signals 테이블에 4개 컬럼 추가:
ema_align_type ENUM('FULL','PARTIAL','NONE') -- EMA 배열 유형
ema_spread DECIMAL(6,2) -- EMA 스프레드 (%)
conflict_count INT -- 충돌 캔들 수
max_conflict_vol DECIMAL(6,2) -- 최대 충돌 거래량 배수
이 데이터가 쌓이면 "EMA FULL일 때 승률 vs PARTIAL일 때 승률", "충돌 0건 vs 1건" 같은 조건별 분석이 가능해진다. 점수 체계를 더 정교하게 튜닝할 수 있는 원재료다.
3. API + 데이터 재수집
backtest.php의 INSERT 문에 새 컬럼 4개 반영. 기존 데이터는 구버전 로직이므로 전량 삭제 후 재수집.
코인: 549/549개 처리
새 시그널: 3,397건 (PENDING)
기간: 약 41일치
기존 200봉(8일)에서는 528건이었다. 하드필터 제거 + 스캔 범위 확대로 시그널이 6배 이상 늘었다. 데이터가 많을수록 승률의 신뢰도가 올라간다.
---왜 백테스트에 점수를 저장하지 않는가
의도적인 설계다. 백테스트는 "이 조건에서 TP가 나왔냐 SL이 나왔냐"라는 사실만 기록한다. 점수는 Scanner가 실시간으로 계산한다.
백테스트는 시험 성적표 원본이다. "3월 15일, ETHUSDT, LONG, 꼬리 3.2배, EMA PARTIAL, 충돌 0건, TP 달성" — 이게 원본 데이터. Scanner는 이 성적표를 읽어서 "꼬리 3배대 LONG의 승률이 68%니까 28점" 하고 점수를 매긴다.
성적표에 입시 점수를 미리 적어둘 필요는 없다. 채점 기준이 바뀔 수도 있으니까.
부수적인 개선
텔레그램 사전점수 표시
진입 알림에 100점 만점 점수와 항목별 내역이 표시된다.
📋 사전점수: 82/100 (꼬리25 거래량28 EMA17 충돌12)
📏 꼬리비율: 3.2x
📊 거래량: 4.1x
🤖 AI: 7/10 (A)
대시보드 달력 마일스톤
오늘 날짜(3/26)에 "로직 개선" 마일스톤 추가. 나중에 성과를 비교할 때 기준점이 된다.
---돌아보면
EP.11에서 전략을 하나로 깎았다. 이번엔 그 하나의 전략 안에서 판단 체계를 바꿨다.
이분법에서 점수 체계로의 전환은 단순한 기술적 변경이 아니다. "좋은 시그널은 어떤 시그널인가"에 대한 사고방식의 변화다. 조건을 전부 통과한 시그널만 좋은 게 아니다. 약점이 있어도 강점이 압도적이면 좋은 시그널일 수 있다. 점수 체계는 그 판단을 가능하게 한다.
그리고 그 점수를 신뢰하려면, 점수의 근거가 되는 백테스트가 같은 눈으로 세상을 봐야 한다. 오늘 그걸 맞췄다.
시스템의 신뢰성은 가장 약한 고리에서 결정된다. 오늘의 약한 고리는 "실전과 다른 기준으로 돌아가는 백테스트"였다.