블로그

규칙으로 보는 미국·한국 주식

매매 기법

내 화면이 내 규칙과 다른 말을 하고 있었다

매매 기법내 화면이 내 규칙과 다른 말을 하고 있었다

내 화면이 내 규칙과 다른 말을 하고 있었다

규칙 기반 매매 시스템을 만들었다면, 확인해야 할 게 하나 있다. 화면에 뜨는 숫자가 정말 그 규칙에서 나온 숫자인가.

내 시스템에서 세 개가 아니었다. 그리고 그건 에러를 내지 않았다.

첫 번째: 존재하지 않는 익절가

보유 종목 대시보드에 "익절목표" 칸이 있었다. 계산식은 이랬다.

```

목표가 = 현재가 + 2 × (현재가 − 20일선)

그리고 손실 중이면 → 매입가 × 1.1 로 대체

```

문제가 네 겹이었다.

하나, 검증된 규칙에는 익절가라는 개념 자체가 없다. 청산은 추세 트레일링이고 고정 목표를 두지 않는다. 화면에만 있는 숫자였다.

, 손절선과 기준이 달랐다. 규칙의 손절은 `min(직전 5봉 저점, 20일선×0.98)`인데, 이 목표가는 `현재가 − 20일선`을 1R로 썼다. 같은 "2R"이라는 말을 쓰면서 R의 정의가 서로 달랐다.

, 진입가가 아니라 현재가 기준이라 매일 움직였다. 주가가 오르면 목표도 따라 올라가서 영원히 닿지 않는다.

, 이게 제일 나쁘다. `손실 중이면 매입가 × 1.1`. 물려 있으면 목표를 "최소 10% 회복"으로 바꿔치기하는 로직이다. 이건 규칙이 아니라 손실 회피 심리를 코드로 굳힌 것이다. 감정을 배제하려고 만든 시스템에 감정이 들어가 있었다.

같은 코드가 세 군데 더 있었다. 종목 분석 화면에는 `종가 + 2R`이, 텔레그램 브리핑에는 `여력 +N%`가 있었다. 전부 백테스트로 검증된 적 없는 숫자였다.

화면을 보고 파는 사람과 규칙대로 파는 사람이 서로 다른 매매를 하게 된다. 혼자 쓰는 시스템에서도 이건 치명적이다. 오늘의 나와 3주 전의 내가 다른 규칙으로 매매하게 되니까.

두 번째: 조용히 무시되던 파라미터

청산 채널을 10일에서 15일로 바꿔서 백테스트를 돌렸다. 결과가 나왔다.

```

채널 10 → 8,347건, 기대값 0.093, MDD −131.6R, 보유 11일

채널 15 → 8,347건, 기대값 0.093, MDD −131.6R, 보유 11일

```

완전히 똑같았다. 건수도, 소수점 셋째 자리까지도.

우연일 리가 없어서 한 종목만 떼어 청산 사유를 찍어봤다.

```

channel=10 → trail 14건, supportBreak 0건

channel=15 → trail 4건, supportBreak 10건

channel=30 → trail 0건, supportBreak 14건

```

원인이 나왔다. 청산 경로가 두 개였다.

```ts

// 경로 1 — 파라미터를 따름

trailLevelAt → swingLow(low, i, profile.trail.channel)

// 경로 2 — sellFlags 안에, 10이 하드코딩

if (want.has("supportBreak") && c < swingLow(low, i, 10)) ...

```

둘 다 "10일 저점 이탈"이라 완전히 중복이었다. 그래서 채널을 10보다 넓히면 하드코딩된 쪽이 먼저 걸려서 파라미터가 무시된다. 숫자를 바꿔도 아무 일도 일어나지 않았던 것이다.

이게 왜 중요하냐면, 이 버그 때문에 "청산은 10일이 최적"이라는 결론 자체가 검증된 적이 없었다. 넓혀서 비교해보려 해도 애초에 비교가 안 됐으니까. 중복을 제거하고 다시 재보니 채널 45가 10보다 50% 나았다.

세 번째: 브리핑이 같은 목록을 두 번 보내고 있었다

아침에는 한국 시장 브리핑, 저녁에는 미국 시장 브리핑을 텔레그램으로 보낸다. `?market=kr`, `?market=us` 파라미터로 구분한다.

그런데 종목 목록을 뽑는 코드에 시장 필터가 없었다.

```ts

const setups = cache.setups.filter(s => s.tier === "BUY"); // 시장 구분 없음

```

`market` 파라미터는 지수 표시(코스피/나스닥)만 바꾸고 있었다. 종목 목록은 아침이나 저녁이나 똑같은 게 나갔다.

공통점

세 가지 모두 에러를 내지 않았다.

```

가짜 익절가 → 그럴듯한 숫자가 화면에 떴다

파라미터 무시 → 백테스트가 정상적으로 돌아 결과를 냈다

시장 필터 누락 → 브리핑이 매일 잘 발송됐다

```

프로그램이 멈추거나 빨간 글씨를 뱉었다면 바로 고쳤을 것이다. 아무 일 없이 잘 도는 게 문제였다.

찾아낸 계기도 전부 비슷했다. 숫자가 이상해서 파고들었다. 채널 10과 15가 완전히 같길래, 브리핑 내용이 아침저녁 똑같길래, 익절가가 매일 바뀌길래.

그래서 얻은 습관

결과가 나오면 개수부터 센다. 같은 값이 나오면 안 되는 자리에 같은 값이 나오는지, 건수가 예상 범위인지.

실제로 이 원칙이 그날 한 번 더 통했다. 20년치 데이터를 받는데 503종이 전부 "데이터 부족"으로 처리됐다. 확인해보니 야후에 `range=max`를 넣으면 일봉이 아니라 월봉을 돌려준다. 168봉짜리 월봉을 일봉인 줄 알고 백테스트를 돌릴 뻔했다. 이것도 에러는 없었다.

규칙 기반 매매의 전제는 "규칙대로 돌아간다"는 믿음이다. 그 믿음은 확인해야 유지된다.

핵심 요약

1. 화면의 익절가가 검증된 규칙에서 나온 값이 아니었다. 세 군데에 있었다.

2. 손실 중이면 목표를 "매입가 ×1.1"로 바꾸는 로직이 있었다. 감정을 배제하려는 시스템에 회피 심리가 코드로 들어가 있었다.

3. 청산 로직이 두 곳에 중복돼 파라미터가 무시되고 있었다. 채널 값을 바꿔도 결과가 같았다.

4. 브리핑에 시장 필터가 없어 같은 목록이 두 번 발송됐다.

5. 셋 다 에러를 내지 않았다. 잘 도는 게 문제였다.

자주 묻는 질문

백테스트 결과가 맞는지 어떻게 확인하나요?
파라미터를 바꿨을 때 결과가 바뀌는지 확인하는 것이 가장 간단하다. 값을 바꿨는데 결과가 동일하다면 그 파라미터는 어딘가에서 무시되고 있을 가능성이 크다. 또한 건수·기간·데이터 개수를 항상 함께 출력해두면 이상을 빨리 발견할 수 있다.
화면과 규칙이 다른지 어떻게 점검하나요?
화면에 표시되는 모든 숫자에 대해 "이 값이 어느 함수에서 나오는가"를 추적해보면 된다. 규칙 엔진이 아닌 곳에서 계산된 값이 있다면 그것이 불일치 후보다. 규칙 엔진의 값을 그대로 읽어 표시하도록 바꾸면, 이후 규칙이 변경돼도 화면이 자동으로 따라온다.
이런 문제를 미리 막는 방법이 있나요?
규칙 값은 한 곳에서만 정의하고 나머지는 그것을 참조하게 하는 것이 기본이다. 같은 조건이 두 군데에 하드코딩되면 언젠가 어긋난다. 실제로 이 프로젝트에서 문제가 된 세 가지 모두 "값이 두 곳에 존재"하는 구조에서 나왔다.

*이 글의 코드와 수치는 실제 프로젝트에서 발견하고 수정한 내용이다.*

이 원칙을 규칙으로 적용해보기

여기서 배운 관점을 실제 종목에 대입해, 컨플루언스·사이징·시나리오를 자동 계산해 볼 수 있습니다.

티커 분석 열기 →
규칙 기반 큐레이션 · 매매 판단은 본인 몫