비트코인 · 자동매매 봇 · 익절 · 실측 재현 페이지

비트코인 봇 익절,
10%에 걸까 그냥 둘까

같은 비트코인 봇(트레이딩뷰 슈퍼트렌드 기본값 + 일봉 30일 평균 조건)에 익절만 다르게 걸고 돌렸습니다. 바이낸스 비트코인 현물 실제 가격 · 2017년 12월 2일 ~ 2026년 10월 28일(UTC) · 1,000만원 · 사고팔 때마다 편도 0.1%. 업비트 가격으로도 같이 돌렸고, 영상의 숫자는 덜 나온 바이낸스 쪽입니다.

1억 5,550만원익절 없이 봇이 팔 때까지 둔 계좌 (가장 깊은 하락 -36.7%)
2,396만원10% 오르면 전부 파는 익절을 건 계좌 (-32.4%)
28 / 4210%를 넘긴 거래 중 익절 없는 봇이 익절 계좌가 판 값보다 더 받은 거래

봇 규칙 그대로

항목값
차트비트코인 4시간봉
슈퍼트렌드트레이딩뷰 기본값 — ATR 길이 10 · 배수 3. 초록(상승)일 때만 산다
조건4시간봉 종가가 어제까지 끝난 일봉(UTC) 30일 평균보다 높을 때만
판다슈퍼트렌드가 빨강으로 바뀌거나, 종가가 그 30일 평균 밑으로 내려가면
얼마나 사나살 때 계좌 전부(100%) · 현물 · 공매도 없음
체결4시간봉이 끝날 때 판정 → 다음 봉 시가에 체결 · 편도 수수료 0.1%
10% 익절 계좌위 규칙에 하나 더: 산 값(신호 봉 종가)보다 4시간봉 종가가 10% 이상이면 다음 봉 시가에 전부 판다 → 신호가 꺼졌다 다시 켜질 때까지 쉰다

비트코인 봇 — 익절만 바꾼다

익절바이낸스 1,000만원 →가장 깊은 하락업비트 1,000만원 →가장 깊은 하락
익절 없음1억 5,550만원-36.7%8억 1,151만원-32.0%
10% 전부 익절2,396만원-32.4%9,490만원-25.9%
25% 전부 익절1억 1,971만원-35.5%3억 7,840만원-31.1%
50% 전부 익절1억 6,103만원-36.6%8억 549만원-32.0%
25%에서 절반1억 4,105만원-35.5%5억 8,376만원-31.1%
50%에서 절반1억 5,952만원-36.6%8억 2,156만원-32.0%

바이낸스 금액은 달러 가격의 배수를 1,000만원에 곱한 값입니다(환율 변화 미반영 — 두 계좌 비교에는 영향 없음). 10% 익절 계좌가 가장 크게 빠졌을 때의 하락 폭은 조금 작았습니다 — 그 대신 남은 돈이 6분의 1도 안 됩니다.

10%를 넘긴 뒤에는 어떻게 됐나 (바이낸스 · 익절 없는 봇의 거래)

거래 249번 중 번 거래 117번 · 잃은 거래 132번 — 봇은 절반 넘게 조금씩 잃고, 크게 오르는 몇 번이 메웁니다. 10%를 넘긴 거래 42번을 익절 계좌가 실제로 판 값(10%를 넘긴 다음 봉 시가)과 견주면 28번은 익절 없는 봇이 더 받았고 14번은 익절 계좌가 더 받았습니다. 거래마다 더 받은 몫을 모으면 봇 쪽 463.5%포인트 대 익절 쪽 41.8%포인트입니다. 이 42번 중 봇이 손실로 판 거래는 0번입니다(10% 선과 견주면 31번이 10%보다 높게, 11번이 10% 아래에서 팔렸습니다). 가장 크게 번 거래는 2020-12-13 진입 거래(98.2%)였고, 10% 익절 계좌는 그때도 10% 남짓에서 팔았습니다. 10%를 넘긴 거래 중 가운데: 2018-04-12T20:00 진입 → 8일 뒤 2018-04-20 10% 넘음 → 봇은 15.3% 에 팜.

덜 넣어서 덜 번 것 아닌가 — 보유 비중을 똑같이 맞추면

10% 익절 계좌는 팔고 쉬는 날이 많아 평균 보유 비중이 25.8%였습니다. 익절 없는 봇의 살 때 비중을 0.674로 줄여 평균 보유 비중을 똑같이 맞추면 바이낸스 비트코인 7,675만원(10% 익절 2,396만원) — 12개 중 12/12에서 익절 없는 쪽이 더 남았습니다(공개 코드 출력의 「똑같이 맞추면」 줄).

이 봇만 그런가 — 코인 2 × 봇 규칙 3 × 거래소 2

익절익절 없음보다 덜 남은 경우
10% 전부 익절12 / 12
25% 전부 익절11 / 12
50% 전부 익절5 / 12
25%에서 절반10 / 12
50%에서 절반4 / 12

50%에서 절반 익절은 봇에 따라 갈렸습니다 — 특히 이동평균 교차 봇에서 덜 남았습니다. 「꼭 걸고 싶다면 50%에서 절반」은 이 비트코인 슈퍼트렌드 봇에서 결과가 거의 같았다는 뜻이지, 어느 봇에서나 같다는 뜻은 아닙니다. 지정가 익절(봉 안에서 +10%에 닿으면 정확히 +10%에 체결)로 다시 재도 10% 익절은 12개 모두 덜 남았습니다(바이낸스 비트코인 1억 5,517만원 대 2,406만원 — 연구 기록).

코인 · 봇 규칙익절바이낸스하락업비트하락
BTC · 슈퍼트렌드(10·3)익절 없음1억 5,550만원-36.7%8억 1,151만원-32.0%
BTC · 슈퍼트렌드(10·3)10% 전부 익절2,396만원-32.4%9,490만원-25.9%
BTC · 슈퍼트렌드(10·3)25% 전부 익절1억 1,971만원-35.5%3억 7,840만원-31.1%
BTC · 슈퍼트렌드(10·3)50% 전부 익절1억 6,103만원-36.6%8억 549만원-32.0%
BTC · 슈퍼트렌드(10·3)25%에서 절반1억 4,105만원-35.5%5억 8,376만원-31.1%
BTC · 슈퍼트렌드(10·3)50%에서 절반1억 5,952만원-36.6%8억 2,156만원-32.0%
BTC · 이동평균 50/200 교차익절 없음1억 4,931만원-72.0%7억 4,210만원-48.5%
BTC · 이동평균 50/200 교차10% 전부 익절2,051만원-38.3%1,830만원-34.5%
BTC · 이동평균 50/200 교차25% 전부 익절3,925만원-56.5%9,901만원-27.6%
BTC · 이동평균 50/200 교차50% 전부 익절1억 2,416만원-56.5%2억 6,496만원-48.5%
BTC · 이동평균 50/200 교차25%에서 절반8,339만원-67.5%3억 1,687만원-36.6%
BTC · 이동평균 50/200 교차50%에서 절반1억 4,160만원-66.9%4억 7,513만원-48.5%
BTC · 켈트너 돌파(20·1.5)익절 없음7,992만원-40.3%7억 1,930만원-31.3%
BTC · 켈트너 돌파(20·1.5)10% 전부 익절5,024만원-33.3%1억 8,798만원-20.9%
BTC · 켈트너 돌파(20·1.5)25% 전부 익절8,476만원-36.7%7억 1,656만원-20.9%
BTC · 켈트너 돌파(20·1.5)50% 전부 익절9,882만원-36.7%10억 3,967만원-23.3%
BTC · 켈트너 돌파(20·1.5)25%에서 절반8,258만원-36.7%7억 2,181만원-21.2%
BTC · 켈트너 돌파(20·1.5)50%에서 절반8,914만원-36.7%8억 7,057만원-23.3%
ETH · 슈퍼트렌드(10·3)익절 없음10억 9,364만원-36.4%35억 8,005만원-30.9%
ETH · 슈퍼트렌드(10·3)10% 전부 익절3,919만원-42.9%9,832만원-24.4%
ETH · 슈퍼트렌드(10·3)25% 전부 익절4억 7,065만원-36.1%12억 2,710만원-28.7%
ETH · 슈퍼트렌드(10·3)50% 전부 익절15억 3,282만원-36.4%41억 228만원-28.7%
ETH · 슈퍼트렌드(10·3)25%에서 절반7억 5,092만원-36.1%22억 1,272만원-28.7%
ETH · 슈퍼트렌드(10·3)50%에서 절반13억 471만원-36.4%38억 6,952만원-28.7%
ETH · 이동평균 50/200 교차익절 없음10억 8,221만원-54.3%11억 9,166만원-57.3%
ETH · 이동평균 50/200 교차10% 전부 익절5,550만원-39.1%1,375만원-48.0%
ETH · 이동평균 50/200 교차25% 전부 익절9,123만원-37.3%1억 698만원-35.3%
ETH · 이동평균 50/200 교차50% 전부 익절4억 2,877만원-37.3%7억 2,658만원-39.8%
ETH · 이동평균 50/200 교차25%에서 절반3억 8,488만원-47.8%4억 3,929만원-52.7%
ETH · 이동평균 50/200 교차50%에서 절반7억 4,787만원-42.6%10억 3,254만원-46.5%
ETH · 켈트너 돌파(20·1.5)익절 없음6억 962만원-33.7%96억 9,678만원-29.8%
ETH · 켈트너 돌파(20·1.5)10% 전부 익절1억 3,161만원-33.7%5억 9,239만원-24.4%
ETH · 켈트너 돌파(20·1.5)25% 전부 익절5억 7,634만원-33.7%46억 3,993만원-29.8%
ETH · 켈트너 돌파(20·1.5)50% 전부 익절6억 8,990만원-33.7%106억 2,312만원-29.8%
ETH · 켈트너 돌파(20·1.5)25%에서 절반6억 702만원-33.7%69억 3,780만원-29.8%
ETH · 켈트너 돌파(20·1.5)50%에서 절반6억 4,923만원-33.7%101억 8,478만원-29.8%

한계 — 먼저 읽어 주세요

받아가기

돌리는 법: zip 을 풀고 python run_takeprofit.py

그대로 돌린 출력

비트코인 봇에 10% 익절을 걸까, 봇이 팔 때까지 둘까 — 재현 코드 (알고랩 · 2026-10-29 영상)
기준일: 2026-10-28T16:00:00 UTC 봉까지

■ BTC · 슈퍼트렌드(10·3) · 2017-12-02 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 1억 5,550만원 (가장 깊은 하락 -36.7%) | 업비트 8억 1,151만원 (가장 깊은 하락 -32.0%)
  10% 전부 익절  바이낸스 2,396만원 (가장 깊은 하락 -32.4%) | 업비트 9,490만원 (가장 깊은 하락 -25.9%)
  25% 전부 익절  바이낸스 1억 1,971만원 (가장 깊은 하락 -35.5%) | 업비트 3억 7,840만원 (가장 깊은 하락 -31.1%)
  50% 전부 익절  바이낸스 1억 6,103만원 (가장 깊은 하락 -36.6%) | 업비트 8억 549만원 (가장 깊은 하락 -32.0%)
  25%에서 절반   바이낸스 1억 4,105만원 (가장 깊은 하락 -35.5%) | 업비트 5억 8,376만원 (가장 깊은 하락 -31.1%)
  50%에서 절반   바이낸스 1억 5,952만원 (가장 깊은 하락 -36.6%) | 업비트 8억 2,156만원 (가장 깊은 하락 -32.0%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.674 → 7,675만원 (10% 익절 2,396만원 · 평균 보유 비중 25.8%) | 업비트 살 때 비중 0.674 → 2억 3,855만원 (10% 익절 9,490만원 · 평균 보유 비중 25.2%)

■ BTC · 이동평균 50/200 교차 · 2017-12-02 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 1억 4,931만원 (가장 깊은 하락 -72.0%) | 업비트 7억 4,210만원 (가장 깊은 하락 -48.5%)
  10% 전부 익절  바이낸스 2,051만원 (가장 깊은 하락 -38.3%) | 업비트 1,830만원 (가장 깊은 하락 -34.5%)
  25% 전부 익절  바이낸스 3,925만원 (가장 깊은 하락 -56.5%) | 업비트 9,901만원 (가장 깊은 하락 -27.6%)
  50% 전부 익절  바이낸스 1억 2,416만원 (가장 깊은 하락 -56.5%) | 업비트 2억 6,496만원 (가장 깊은 하락 -48.5%)
  25%에서 절반   바이낸스 8,339만원 (가장 깊은 하락 -67.5%) | 업비트 3억 1,687만원 (가장 깊은 하락 -36.6%)
  50%에서 절반   바이낸스 1억 4,160만원 (가장 깊은 하락 -66.9%) | 업비트 4억 7,513만원 (가장 깊은 하락 -48.5%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.371 → 3,619만원 (10% 익절 2,051만원 · 평균 보유 비중 18.1%) | 업비트 살 때 비중 0.356 → 6,840만원 (10% 익절 1,830만원 · 평균 보유 비중 18.1%)

■ BTC · 켈트너 돌파(20·1.5) · 2017-12-02 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 7,992만원 (가장 깊은 하락 -40.3%) | 업비트 7억 1,930만원 (가장 깊은 하락 -31.3%)
  10% 전부 익절  바이낸스 5,024만원 (가장 깊은 하락 -33.3%) | 업비트 1억 8,798만원 (가장 깊은 하락 -20.9%)
  25% 전부 익절  바이낸스 8,476만원 (가장 깊은 하락 -36.7%) | 업비트 7억 1,656만원 (가장 깊은 하락 -20.9%)
  50% 전부 익절  바이낸스 9,882만원 (가장 깊은 하락 -36.7%) | 업비트 10억 3,967만원 (가장 깊은 하락 -23.3%)
  25%에서 절반   바이낸스 8,258만원 (가장 깊은 하락 -36.7%) | 업비트 7억 2,181만원 (가장 깊은 하락 -21.2%)
  50%에서 절반   바이낸스 8,914만원 (가장 깊은 하락 -36.7%) | 업비트 8억 7,057만원 (가장 깊은 하락 -23.3%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.796 → 5,657만원 (10% 익절 5,024만원 · 평균 보유 비중 19.4%) | 업비트 살 때 비중 0.756 → 2억 8,188만원 (10% 익절 1억 8,798만원 · 평균 보유 비중 18.4%)

■ ETH · 슈퍼트렌드(10·3) · 2017-12-05 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 10억 9,364만원 (가장 깊은 하락 -36.4%) | 업비트 35억 8,005만원 (가장 깊은 하락 -30.9%)
  10% 전부 익절  바이낸스 3,919만원 (가장 깊은 하락 -42.9%) | 업비트 9,832만원 (가장 깊은 하락 -24.4%)
  25% 전부 익절  바이낸스 4억 7,065만원 (가장 깊은 하락 -36.1%) | 업비트 12억 2,710만원 (가장 깊은 하락 -28.7%)
  50% 전부 익절  바이낸스 15억 3,282만원 (가장 깊은 하락 -36.4%) | 업비트 41억 228만원 (가장 깊은 하락 -28.7%)
  25%에서 절반   바이낸스 7억 5,092만원 (가장 깊은 하락 -36.1%) | 업비트 22억 1,272만원 (가장 깊은 하락 -28.7%)
  50%에서 절반   바이낸스 13억 471만원 (가장 깊은 하락 -36.4%) | 업비트 38억 6,952만원 (가장 깊은 하락 -28.7%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.561 → 1억 9,238만원 (10% 익절 3,919만원 · 평균 보유 비중 21.1%) | 업비트 살 때 비중 0.545 → 3억 4,427만원 (10% 익절 9,832만원 · 평균 보유 비중 19.9%)

■ ETH · 이동평균 50/200 교차 · 2017-12-05 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 10억 8,221만원 (가장 깊은 하락 -54.3%) | 업비트 11억 9,166만원 (가장 깊은 하락 -57.3%)
  10% 전부 익절  바이낸스 5,550만원 (가장 깊은 하락 -39.1%) | 업비트 1,375만원 (가장 깊은 하락 -48.0%)
  25% 전부 익절  바이낸스 9,123만원 (가장 깊은 하락 -37.3%) | 업비트 1억 698만원 (가장 깊은 하락 -35.3%)
  50% 전부 익절  바이낸스 4억 2,877만원 (가장 깊은 하락 -37.3%) | 업비트 7억 2,658만원 (가장 깊은 하락 -39.8%)
  25%에서 절반   바이낸스 3억 8,488만원 (가장 깊은 하락 -47.8%) | 업비트 4억 3,929만원 (가장 깊은 하락 -52.7%)
  50%에서 절반   바이낸스 7억 4,787만원 (가장 깊은 하락 -42.6%) | 업비트 10억 3,254만원 (가장 깊은 하락 -46.5%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.330 → 7,609만원 (10% 익절 5,550만원 · 평균 보유 비중 16.3%) | 업비트 살 때 비중 0.362 → 9,418만원 (10% 익절 1,375만원 · 평균 보유 비중 18.0%)

■ ETH · 켈트너 돌파(20·1.5) · 2017-12-05 ~ 2026-10-28 · 1,000만원
  익절 없음      바이낸스 6억 962만원 (가장 깊은 하락 -33.7%) | 업비트 96억 9,678만원 (가장 깊은 하락 -29.8%)
  10% 전부 익절  바이낸스 1억 3,161만원 (가장 깊은 하락 -33.7%) | 업비트 5억 9,239만원 (가장 깊은 하락 -24.4%)
  25% 전부 익절  바이낸스 5억 7,634만원 (가장 깊은 하락 -33.7%) | 업비트 46억 3,993만원 (가장 깊은 하락 -29.8%)
  50% 전부 익절  바이낸스 6억 8,990만원 (가장 깊은 하락 -33.7%) | 업비트 106억 2,312만원 (가장 깊은 하락 -29.8%)
  25%에서 절반   바이낸스 6억 702만원 (가장 깊은 하락 -33.7%) | 업비트 69억 3,780만원 (가장 깊은 하락 -29.8%)
  50%에서 절반   바이낸스 6억 4,923만원 (가장 깊은 하락 -33.7%) | 업비트 101억 8,478만원 (가장 깊은 하락 -29.8%)
  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: 바이낸스 살 때 비중 0.711 → 2억 1,813만원 (10% 익절 1억 3,161만원 · 평균 보유 비중 16.5%) | 업비트 살 때 비중 0.668 → 12억 3,335만원 (10% 익절 5억 9,239만원 · 평균 보유 비중 16.0%)

■ 익절 없음보다 돈이 적게 남은 경우 (12개 = 코인 2 × 봇 규칙 3 × 거래소 2)
  10% 전부 익절: 12/12
  25% 전부 익절: 11/12
  50% 전부 익절: 5/12
  25%에서 절반: 10/12
  50%에서 절반: 4/12
■ 10% 익절 계좌와 평균 보유 비중을 똑같이 맞춘 익절 없는 봇이 더 남긴 경우: 12/12

■ 비트코인 슈퍼트렌드 봇(익절 없음) 거래 하나하나 — 2017-12-02 이후 진입
  바이낸스: 거래 249번 · 번 거래 117번 · 잃은 거래 132번
     10% 를 넘긴 거래 42번 — 봇이 10% 보다 높은 값에 판 거래 31번 · 10% 아래로 판 거래 11번 · 손실로 끝난 거래 0번
     익절 계좌가 실제로 판 값과 견주면 — 익절 없는 봇이 더 받은 거래 28번 · 익절 계좌가 더 받은 거래 14번 · 더 받은 몫을 모으면 봇 쪽 463.5%포인트 대 익절 쪽 41.8%포인트
     가장 크게 번 거래: 2020-12-13 진입 · 98.2%
     10% 를 넘긴 거래 중 가운데: 2018-04-12T20:00 진입 → 8일 뒤 2018-04-20 10% 넘음 → 봇은 15.3% 에 팜
  업비트: 거래 224번 · 번 거래 118번 · 잃은 거래 106번
     10% 를 넘긴 거래 43번 — 봇이 10% 보다 높은 값에 판 거래 28번 · 10% 아래로 판 거래 15번 · 손실로 끝난 거래 0번
     익절 계좌가 실제로 판 값과 견주면 — 익절 없는 봇이 더 받은 거래 27번 · 익절 계좌가 더 받은 거래 16번 · 더 받은 몫을 모으면 봇 쪽 527.7%포인트 대 익절 쪽 51.8%포인트
     가장 크게 번 거래: 2020-12-13 진입 · 113.3%

끝.

재현 코드 전문

# -*- coding: utf-8 -*-
"""비트코인 봇에 10% 익절을 걸까, 봇이 팔 때까지 둘까 — 재현 코드 (알고랩 · 2026-10-29 영상)

파이썬 표준 라이브러리만 씁니다. 같은 폴더의 data/ 에 가격 자료(4시간봉 CSV)가 있어야 합니다.
    python run_takeprofit.py

봇 규칙
  · 4시간봉 슈퍼트렌드(트레이딩뷰 기본값 ATR 10 · 배수 3)가 상승(초록)이고
  · 4시간봉 종가가 '어제까지 끝난' 일봉(UTC) 30일 평균보다 높을 때 산다(계좌 전부 · 현물).
  · 둘 중 하나라도 꺼지면 판다. 봉 마감에 판정하고 다음 봉 시가에 체결. 편도 수수료 0.1%.
익절 계좌는 여기에 하나를 더한다
  · 산 값(신호 봉 종가)보다 4시간봉 종가가 p% 이상이면 다음 봉 시가에 판다(전부 또는 절반).
  · 전부 판 뒤에는 신호가 꺼졌다 다시 켜질 때까지 쉰다.
기준일 고정(ASOF): 2026-10-28 16:00 UTC 봉까지. 바이낸스 금액은 달러 가격의 배수를 1,000만원에 곱한 값."""
import csv, datetime, math, os

HERE = os.path.dirname(os.path.abspath(__file__))
ASOF = "2026-10-28T16:00:00"
COST = 0.001
WARM = 400
SEED = 1000  # 만원
NaN = float("nan")


def load(fn):
    d, o, h, l, c = [], [], [], [], []
    with open(os.path.join(HERE, "data", fn), encoding="utf-8") as f:
        r = csv.reader(f); next(r)
        for row in r:
            if row[0] > ASOF:
                break
            d.append(row[0]); o.append(float(row[1])); h.append(float(row[2])); l.append(float(row[3])); c.append(float(row[4]))
    return dict(d=d, o=o, h=h, l=l, c=c, n=len(c))


def sma(x, n):
    """누적합 차이로 계산한다(연구 코드 numpy 판과 같은 반올림)."""
    cs = [0.0]
    for v in x:
        cs.append(cs[-1] + v)
    return [NaN if i < n - 1 else (cs[i + 1] - cs[i + 1 - n]) / n for i in range(len(x))]


def ema(x, n):
    a = 2.0 / (n + 1); out = [x[0]]
    for v in x[1:]:
        out.append(a * v + (1 - a) * out[-1])
    return out


def atr(h, l, c, n):
    tr = [max(h[i] - l[i], abs(h[i] - (c[i - 1] if i else c[0])), abs(l[i] - (c[i - 1] if i else c[0]))) for i in range(len(c))]
    a = [NaN] * len(c)
    if len(c) >= n:
        a[n - 1] = sum(tr[:n]) / n
        for i in range(n, len(c)):
            a[i] = a[i - 1] + (tr[i] - a[i - 1]) / n
    return a


def supertrend(S, n=10, mult=3.0):
    h, l, c = S["h"], S["l"], S["c"]
    a = atr(h, l, c, n)
    up = [(h[i] + l[i]) / 2 - mult * a[i] for i in range(S["n"])]
    dn = [(h[i] + l[i]) / 2 + mult * a[i] for i in range(S["n"])]
    d = [1.0] * S["n"]
    for i in range(1, S["n"]):
        if math.isnan(a[i]):
            continue
        if c[i - 1] > up[i - 1]:
            up[i] = max(up[i], up[i - 1])
        if c[i - 1] < dn[i - 1]:
            dn[i] = min(dn[i], dn[i - 1])
        if c[i] > dn[i - 1]:
            d[i] = 1.0
        elif c[i] < up[i - 1]:
            d[i] = -1.0
        else:
            d[i] = d[i - 1]
    return [1.0 if x > 0 else 0.0 for x in d]


def ma_cross(S, fast=50, slow=200):
    f, s = sma(S["c"], fast), sma(S["c"], slow)
    return [1.0 if (not math.isnan(f[i]) and not math.isnan(s[i]) and f[i] > s[i]) else 0.0 for i in range(S["n"])]


def keltner(S, n=20, k=1.5):
    c = S["c"]; e = ema(c, n); a = atr(S["h"], S["l"], c, n)
    out, st = [], 0.0
    for i in range(S["n"]):
        buy = (not math.isnan(a[i])) and c[i] > e[i] + k * a[i]
        if i < n + 1:
            st = 0.0
        elif buy:
            st = 1.0
        elif c[i] < e[i]:
            st = 0.0
        out.append(st)
    return out


def daily_filter(S, n=30):
    days = [x[:10] for x in S["d"]]
    uniq, last = [], []
    for i, dd in enumerate(days):
        if not uniq or uniq[-1] != dd:
            uniq.append(dd); last.append(i)
        else:
            last[-1] = i
    dsma = sma([S["c"][i] for i in last], n)
    f, j = [], -1
    for i, dd in enumerate(days):
        if j < 0 or uniq[j] != dd:
            j += 1
        y = j - 1
        f.append(1.0 if (y >= 0 and not math.isnan(dsma[y]) and S["c"][i] > dsma[y]) else 0.0)
    return f


def take_profit(S, base, levels):
    """levels = ((수익률, 그때부터의 비중), …). 비중 0 = 전부 판다(신호가 꺼질 때까지 쉼)."""
    c = S["c"]; out = [0.0] * S["n"]; holding, armed, e, w = False, True, 0.0, 1.0
    for t in range(S["n"]):
        on = base[t] > 0
        if not holding:
            if not on:
                armed = True
            elif armed:
                holding, e, w = True, c[t], 1.0
        if holding:
            if not on:
                holding = False; out[t] = 0.0; continue
            for p, lw in levels:
                if c[t] / e - 1 >= p:
                    w = min(w, lw)
            out[t] = w
    return out


def run(S, w, want_pos=False):
    """수량 고정 엔진: 0→x 면 계좌 x 만큼 수량을 산다 · x→y 면 수량 × y/x · →0 이면 전부 판다. 다음 봉 시가 체결."""
    o, c, n = S["o"], S["c"], S["n"]
    cash, qty, eq, pos = 1.0, 0.0, [1.0] * n, [0.0] * n
    for t in range(1, n):
        wn, wp = w[t - 1], (w[t - 2] if t >= 2 else 0.0)
        if wn != wp:
            px = o[t]
            if wp == 0 and wn > 0:
                q = wn * (cash + qty * c[t - 1]) / c[t - 1]
            elif wn == 0:
                q = 0.0
            else:
                q = qty * wn / wp
            dq = q - qty
            cash -= dq * px + COST * abs(dq) * px
            qty = q
        eq[t] = cash + qty * c[t]
        pos[t] = qty * c[t] / eq[t] if eq[t] > 0 else 0.0
    return (eq, pos) if want_pos else eq


def window(series):
    day = lambda s: datetime.date.fromisoformat(s[:10])
    a = max(day(S["d"][WARM]) for S in series)
    b = min(day(S["d"][-1]) for S in series) + datetime.timedelta(days=1)
    return a, b


def stat(S, eq, a, b):
    ds = [x[:10] for x in S["d"]]
    i = next(k for k, x in enumerate(ds) if x >= a.isoformat())
    j = next((k for k, x in enumerate(ds) if x >= b.isoformat()), S["n"])
    e = [v / eq[i] for v in eq[i:j]]
    peak, mdd = e[0], 0.0
    for v in e:
        peak = max(peak, v); mdd = min(mdd, v / peak - 1)
    yrs = (datetime.date.fromisoformat(S["d"][j - 1][:10]) - datetime.date.fromisoformat(S["d"][i][:10])).days / 365.25
    return SEED * e[-1], e[-1] ** (1 / yrs) - 1, mdd, i, j


def exposure(S, w, i, j):
    """평균 보유 비중 — 봉 끝마다 (코인 평가액 ÷ 계좌) 의 평균."""
    _, pos = run(S, w, True)
    return sum(pos[i:j]) / (j - i)


def match_k(S, base, target, i, j):
    """익절 없는 봇의 살 때 비중 k 를 줄여 평균 보유 비중을 target 에 맞춘다(이분법 14번)."""
    lo, hi = 0.05, 1.0
    for _ in range(14):
        k = (lo + hi) / 2
        if exposure(S, [x * k for x in base], i, j) < target:
            lo = k
        else:
            hi = k
    return (lo + hi) / 2


def won(x):
    x = round(x)
    return f"{x // 10000}억 {x % 10000:,}만원" if x >= 10000 else f"{x:,}만원"


VARS = [("익절 없음", None), ("10% 전부 익절", ((0.10, 0.0),)), ("25% 전부 익절", ((0.25, 0.0),)),
        ("50% 전부 익절", ((0.50, 0.0),)), ("25%에서 절반", ((0.25, 0.5),)), ("50%에서 절반", ((0.50, 0.5),))]
RULES = [("슈퍼트렌드(10·3)", supertrend), ("이동평균 50/200 교차", ma_cross), ("켈트너 돌파(20·1.5)", keltner)]

print(__doc__.split("\n")[0]); print("기준일:", ASOF, "UTC 봉까지\n")
less = {v[0]: 0 for v in VARS[1:]}; total = 0; match_win = 0
for coin in ("BTC", "ETH"):
    mk = {"바이낸스": load(f"{coin}USDT_4h.csv"), "업비트": load(f"KRW-{coin}_4h.csv")}
    a, b = window(mk.values())
    filt = {m: daily_filter(S) for m, S in mk.items()}
    for rn, fn in RULES:
        print(f"■ {coin} · {rn} · {a} ~ {b - datetime.timedelta(days=1)} · 1,000만원")
        res = {}
        for m, S in mk.items():
            base = [x * y for x, y in zip(fn(S), filt[m])]
            for vn, lv in VARS:
                w = base if lv is None else take_profit(S, base, lv)
                res[(m, vn)] = stat(S, run(S, w), a, b)
        for vn, _ in VARS:
            print(f"  {vn:10s} " + " | ".join(f"{m} {won(res[(m, vn)][0])} (가장 깊은 하락 {res[(m, vn)][2] * 100:.1f}%)" for m in mk))
        mt = []
        for m, S in mk.items():
            base = [x * y for x, y in zip(fn(S), filt[m])]
            i, j = res[(m, "익절 없음")][3:5]
            tgt = exposure(S, take_profit(S, base, ((0.10, 0.0),)), i, j)
            k = match_k(S, base, tgt, i, j)
            mm = stat(S, run(S, [x * k for x in base]), a, b)[0]
            match_win += mm > res[(m, "10% 전부 익절")][0]
            mt.append(f"{m} 살 때 비중 {k:.3f} → {won(mm)} (10% 익절 {won(res[(m, '10% 전부 익절')][0])} · 평균 보유 비중 {tgt * 100:.1f}%)")
        print("  익절 없이 보유 비중만 10% 익절 계좌와 똑같이 맞추면: " + " | ".join(mt))
        for m in mk:
            total += 1
            for vn, _ in VARS[1:]:
                less[vn] += res[(m, vn)][0] < res[(m, "익절 없음")][0]
        if coin == "BTC" and rn.startswith("슈퍼트렌드"):
            keep = (mk, filt, fn)
        print()
print(f"■ 익절 없음보다 돈이 적게 남은 경우 ({total}개 = 코인 2 × 봇 규칙 3 × 거래소 2)")
for vn, k in less.items():
    print(f"  {vn}: {k}/{total}")
print(f"■ 10% 익절 계좌와 평균 보유 비중을 똑같이 맞춘 익절 없는 봇이 더 남긴 경우: {match_win}/{total}")

# 거래 하나하나 — 비트코인 슈퍼트렌드 봇(익절 없음)
mk, filt, fn = keep
print("\n■ 비트코인 슈퍼트렌드 봇(익절 없음) 거래 하나하나 — 2017-12-02 이후 진입")
for m, S in mk.items():
    base = [x * y for x, y in zip(fn(S), filt[m])]
    c, o, d, n = S["c"], S["o"], S["d"], S["n"]
    tr, t = [], 1
    while t < n:
        if base[t] > 0 and base[t - 1] == 0:
            e, u = c[t], t
            while u + 1 < n and base[u + 1] > 0:
                u += 1
            ein = o[t + 1] if t + 1 < n else c[t]
            xout = o[u + 1] if u + 1 < n else c[-1]
            best = max(c[t:u + 1]) / e - 1
            hit = next((k for k in range(t, u + 1) if c[k] / e - 1 >= 0.10), None)
            # 익절 계좌가 실제로 판 값: 종가가 +10% 를 처음 넘은 봉의 다음 봉 시가
            tp = o[hit + 1] / ein - 1 if (hit is not None and hit + 1 < n) else None
            tr.append(dict(d=d[t], end=xout / ein - 1, hit=hit is not None, hitd=d[hit] if hit is not None else "", tp=tp))
            t = u + 1
        else:
            t += 1
    tr = [x for x in tr if x["d"] >= "2017-12-02"]
    hit = [x for x in tr if x["hit"]]
    up = sum(x["end"] >= 0.10 for x in hit)
    print(f"  {m}: 거래 {len(tr)}번 · 번 거래 {sum(x['end'] > 0 for x in tr)}번 · 잃은 거래 {sum(x['end'] <= 0 for x in tr)}번")
    print(f"     10% 를 넘긴 거래 {len(hit)}번 — 봇이 10% 보다 높은 값에 판 거래 {up}번 · 10% 아래로 판 거래 {len(hit) - up}번 · 손실로 끝난 거래 {sum(x['end'] < 0 for x in hit)}번")
    more = [x for x in hit if x["end"] > x["tp"]]
    less_ = [x for x in hit if x["end"] < x["tp"]]
    gain = sum(x["end"] - x["tp"] for x in more) * 100
    loss = sum(x["tp"] - x["end"] for x in less_) * 100
    print(f"     익절 계좌가 실제로 판 값과 견주면 — 익절 없는 봇이 더 받은 거래 {len(more)}번 · 익절 계좌가 더 받은 거래 {len(less_)}번"
          f" · 더 받은 몫을 모으면 봇 쪽 {gain:.1f}%포인트 대 익절 쪽 {loss:.1f}%포인트")
    big = max(tr, key=lambda x: x["end"])
    print(f"     가장 크게 번 거래: {big['d'][:10]} 진입 · {big['end'] * 100:.1f}%")
    if m == "바이낸스":
        hs = sorted(hit, key=lambda x: x["end"]); mid = hs[len(hs) // 2]
        dd = (datetime.date.fromisoformat(mid["hitd"][:10]) - datetime.date.fromisoformat(mid["d"][:10])).days
        print(f"     10% 를 넘긴 거래 중 가운데: {mid['d'][:16]} 진입 → {dd}일 뒤 {mid['hitd'][:10]} 10% 넘음 → 봇은 {mid['end'] * 100:.1f}% 에 팜")
print("\n끝.")

알고랩 Strategy Lab — 전략을 검증하고, 검증한 것만 자동매매로 옮깁니다. 파는 때를 봇이 정하게 두는 일은 사람보다 봇이 잘 지킵니다. 알고랩 자동매매 살펴보기 · 전략 검증 전체 보기

과거 데이터를 돌려 본 결과이며 미래 수익을 보장하지 않습니다. 투자 판단과 책임은 본인에게 있습니다.