비트코인 · 자동매매 봇 · 슈퍼트렌드 · 실측 재현 페이지

비트코인 슈퍼트렌드 봇,
1시간봉에 걸까 4시간봉에 걸까

트레이딩뷰 슈퍼트렌드(ATR 14 · 배수 2)에 일봉 50일선 조건과 변동성 비중을 붙인 봇 하나를, 설정은 그대로 두고 차트 시간봉만 바꿔 돌렸습니다. 바이낸스 비트코인 현물 실제 가격 · 2017년 12월 6일 ~ 2026년 10월 18일(UTC) · 1,000만원 · 사고팔 때마다 편도 0.1%. 업비트 가격으로도 같이 돌렸고, 영상의 숫자는 덜 나온 바이낸스 쪽입니다.

1억 1,066만원4시간봉에 건 봇의 끝 잔고 (연 31.1% · 가장 깊은 골짜기 -28.8%)
1,956만원같은 설정을 1시간봉에 건 봇 (연 7.9% · -47.0%)
3.6배1시간봉이 더 자주 사고판 횟수(1626번 대 456번)

오늘 드리는 것 — 설정 그대로

항목값
차트비트코인 4시간봉 (6·8시간봉도 비슷 · 1·2시간봉으로 내리지 않는다)
슈퍼트렌드ATR 길이 14 · 배수 2 — 초록(상승)일 때만 산다
조건어제까지 끝난 일봉 종가가 일봉 50일선 위일 때만
판다슈퍼트렌드가 빨강으로 바뀌거나, 일봉 종가가 50일선 밑으로 내려가면
얼마나 사나살 때 한 번 — 계좌의 0.5 ÷ 최근 7일 연율 변동성(최대 100%). 팔 때까지 수량 그대로
어디서현물. 선물·공매도는 켜지 않는다

트레이딩뷰에 바로 붙여 넣는 파인 스크립트: btc_supertrend_vol.pine (변동성 창 42봉 = 4시간봉 7일 — 차트를 4시간봉으로 둘 것)

차트 시간봉만 바꾼다 — 설정은 그대로

차트바이낸스 1,000만원 →연가장 깊은 골짜기사고판 횟수업비트 1,000만원 →연골짜기
1시간봉1,956만원7.9%-47.0%1626번2,346만원10.1%-46.3%
2시간봉4,868만원19.5%-39.5%860번1억 5,120만원35.8%-25.0%
4시간봉1억 1,066만원31.1%-28.8%456번2억 8,610만원46.0%-27.7%
6시간봉1억 2,816만원33.3%-31.2%350번4억 3,122만원52.9%-22.9%
8시간봉1억 1,398만원31.6%-33.5%302번3억 6,135만원49.9%-27.5%
12시간봉2억 1,626만원41.5%-40.7%222번2억 5,130만원43.9%-32.6%
일봉7,366만원25.3%-47.4%166번1억 2,586만원33.1%-52.4%
그냥 보유6,580만원23.7%-83.9%—6,856만원24.3%-87.0%

왜 1시간봉은 덜 남았나 — 수수료를 바꿔 보면 (바이낸스)

편도 수수료1시간봉 연4시간봉 연(끝 잔고)
0.00%26.3%37.1% (1억 6,421만원)
0.05%16.7%34.1% (1억 3,481만원)
0.10%7.9%31.1% (1억 1,066만원)
0.12%4.5%30.0% (1억 225만원)
0.15%-0.3%28.3% (9,083만원)
0.25%-14.9%22.7% (6,117만원)

수수료를 0 으로 놓아도 1시간봉이 낮습니다 — 짧은 봉의 헛신호 몫입니다. 수수료가 오르면 1시간봉은 훨씬 빨리 무너집니다.

덜 넣어서 덜 떨어진 것 아닌가 — 같은 평균 비중을 늘 들고 있었다면

봇평균 비중봇 (연 · 골짜기)같은 비중 고정 (연 · 골짜기)
1시간봉 바이낸스24%연 7.9% · -47.0%연 9.9% · -28.6%
1시간봉 업비트25%연 10.1% · -46.3%연 9.6% · -35.5%
4시간봉 바이낸스27%연 31.1% · -28.8%연 10.6% · -33.6%
4시간봉 업비트28%연 46.0% · -27.7%연 10.1% · -39.1%
6시간봉 바이낸스29%연 33.3% · -31.2%연 11.0% · -35.7%
6시간봉 업비트29%연 52.9% · -22.9%연 10.4% · -40.1%
8시간봉 바이낸스30%연 31.6% · -33.5%연 11.2% · -35.8%
8시간봉 업비트30%연 49.9% · -27.5%연 10.6% · -41.8%

고정 비중 계좌는 매 봉 비중을 맞추고 비용을 0 으로 둔, 그 계좌에 유리한 계산입니다.

최근 2년만 (2024-10-18 ~)

거래소1시간봉 봇4시간봉 봇그냥 보유
바이낸스890만원 (-40.5%)1,440만원 (-20.6%)1,182만원 (-53.4%)
업비트806만원 (-37.3%)1,904만원 (-12.7%)1,175만원 (-50.3%)

봇이 실제로 사고판 기록 (바이낸스 4시간봉)

산 뒤 판 것 226번 중 계좌가 늘어난 것 97번 · 줄어든 것 129번(한 번에 가장 크게 줄어든 것 -6.1%). 계좌를 가장 많이 늘린 한 번은 2019-06-10T16:00:00 종가 7,946달러에 계좌의 69%로 사서 2019-06-27T04:00:00 종가 12,500달러에 판 거래(계좌 1.395배)입니다.

해봇그냥 보유
2018-17.2%-73.0%
2019+75.3%+94.3%
2020+144.5%+302.0%
2021+36.7%+59.8%
2022-22.6%-64.2%
2023+46.9%+155.6%
2024+75.4%+121.3%
2025+6.4%-6.3%
2026+2.4%-7.5%

크게 오른 해에는 그냥 보유보다 덜 법니다 — 봇은 대개 계좌의 일부만 사 둡니다.

다른 거래소·다른 시대 — 비트스탬프 2014-01 ~ 2017-09

차트연가장 깊은 골짜기사고판 횟수
1시간봉24.3%-31.4%757번
2시간봉69.7%-20.4%397번
4시간봉81.6%-20.9%211번
6시간봉58.6%-25.8%177번
8시간봉68.5%-23.0%143번
12시간봉88.1%-25.4%105번
일봉77.4%-25.3%71번
그냥 보유60.7%-82.3%—

한계 — 먼저 읽어 주세요

받아가기

돌리는 법: zip 을 풀고 python run_supertrend4h.py (1분 안쪽)

그대로 돌린 출력

비트코인 슈퍼트렌드 봇 · ATR 14 · 배수 2 · 일봉 50일선 · 살 때 비중 0.5÷7일 변동성 · 편도 0.1%
기간 2017-12-06T16:00:00 ~ 2026-10-18T16:00:00 (UTC) · 1,000만원

[1] 차트 시간봉만 바꾼다 — 설정은 그대로
  1시간봉   바이낸스 1,956만원 (연 7.9% · 가장 깊은 골짜기 -47.0% · 사고판 횟수 1626번) | 업비트 2,346만원 (연 10.1% · -46.3% · 1600번)
  2시간봉   바이낸스 4,868만원 (연 19.5% · 가장 깊은 골짜기 -39.5% · 사고판 횟수 860번) | 업비트 1억 5,120만원 (연 35.8% · -25.0% · 800번)
  4시간봉   바이낸스 1억 1,066만원 (연 31.1% · 가장 깊은 골짜기 -28.8% · 사고판 횟수 456번) | 업비트 2억 8,610만원 (연 46.0% · -27.7% · 436번)
  6시간봉   바이낸스 1억 2,816만원 (연 33.3% · 가장 깊은 골짜기 -31.2% · 사고판 횟수 350번) | 업비트 4억 3,122만원 (연 52.9% · -22.9% · 318번)
  8시간봉   바이낸스 1억 1,398만원 (연 31.6% · 가장 깊은 골짜기 -33.5% · 사고판 횟수 302번) | 업비트 3억 6,135만원 (연 49.9% · -27.5% · 278번)
  12시간봉  바이낸스 2억 1,626만원 (연 41.5% · 가장 깊은 골짜기 -40.7% · 사고판 횟수 222번) | 업비트 2억 5,130만원 (연 43.9% · -32.6% · 210번)
  일봉     바이낸스 7,366만원 (연 25.3% · 가장 깊은 골짜기 -47.4% · 사고판 횟수 166번) | 업비트 1억 2,586만원 (연 33.1% · -52.4% · 138번)
  그냥 보유 바이낸스 6,580만원 (연 23.7% · 가장 깊은 골짜기 -83.9%)
  그냥 보유 업비트 6,856만원 (연 24.3% · 가장 깊은 골짜기 -87.0%)
  1시간봉은 4시간봉보다 3.6배 자주 사고팔았다(바이낸스 1626번 대 456번)

[2] 같은 평균 비중을 늘 들고 있었다면(매 봉 맞춤 · 비용 0 — 이 계좌에 유리한 계산)
  1시간봉 바이낸스 봇 평균 비중 24% → 고정 비중 계좌 연 9.9% · 가장 깊은 골짜기 -28.6% (봇 연 7.9% · -47.0%)
  1시간봉 업비트 봇 평균 비중 25% → 고정 비중 계좌 연 9.6% · 가장 깊은 골짜기 -35.5% (봇 연 10.1% · -46.3%)
  4시간봉 바이낸스 봇 평균 비중 27% → 고정 비중 계좌 연 10.6% · 가장 깊은 골짜기 -33.6% (봇 연 31.1% · -28.8%)
  4시간봉 업비트 봇 평균 비중 28% → 고정 비중 계좌 연 10.1% · 가장 깊은 골짜기 -39.1% (봇 연 46.0% · -27.7%)
  6시간봉 바이낸스 봇 평균 비중 29% → 고정 비중 계좌 연 11.0% · 가장 깊은 골짜기 -35.7% (봇 연 33.3% · -31.2%)
  6시간봉 업비트 봇 평균 비중 29% → 고정 비중 계좌 연 10.4% · 가장 깊은 골짜기 -40.1% (봇 연 52.9% · -22.9%)
  8시간봉 바이낸스 봇 평균 비중 30% → 고정 비중 계좌 연 11.2% · 가장 깊은 골짜기 -35.8% (봇 연 31.6% · -33.5%)
  8시간봉 업비트 봇 평균 비중 30% → 고정 비중 계좌 연 10.6% · 가장 깊은 골짜기 -41.8% (봇 연 49.9% · -27.5%)

[3] 수수료를 바꾸면 (바이낸스)
  편도 0.00%  1시간봉 연 26.3% · 4시간봉 연 37.1% (1억 6,421만원)
  편도 0.05%  1시간봉 연 16.7% · 4시간봉 연 34.1% (1억 3,481만원)
  편도 0.10%  1시간봉 연 7.9% · 4시간봉 연 31.1% (1억 1,066만원)
  편도 0.12%  1시간봉 연 4.5% · 4시간봉 연 30.0% (1억 225만원)
  편도 0.15%  1시간봉 연 -0.3% · 4시간봉 연 28.3% (9,083만원)
  편도 0.25%  1시간봉 연 -14.9% · 4시간봉 연 22.7% (6,117만원)

[4] 최근 2년만 (2024-10-18 16:00 UTC ~) — 같은 계좌를 그날부터 다시 잰다
  바이낸스 1시간봉 봇 890만원(골짜기 -40.5%) · 4시간봉 봇 1,440만원(골짜기 -20.6%) · 그냥 보유 1,182만원(골짜기 -53.4%)
  업비트 1시간봉 봇 806만원(골짜기 -37.3%) · 4시간봉 봇 1,904만원(골짜기 -12.7%) · 그냥 보유 1,175만원(골짜기 -50.3%)

[5] 바이낸스 4시간봉 봇이 사고판 기록
  산 뒤 판 것 226번 · 계좌가 늘어난 것 97번 · 줄어든 것 129번(한 번에 가장 크게 줄어든 것 -6.1%)
  계좌를 가장 많이 늘린 한 번: 2019-06-10T16:00:00 종가 7,946달러에 계좌의 69%로 사서 → 2019-06-27T04:00:00 종가 12,500달러에 팔았다 · 계좌 1.395배
  해마다(바이낸스 4시간봉 봇 대 그냥 보유):
    2018  봇 -17.2%  그냥 보유 -73.0%
    2019  봇 +75.3%  그냥 보유 +94.3%
    2020  봇 +144.5%  그냥 보유 +302.0%
    2021  봇 +36.7%  그냥 보유 +59.8%
    2022  봇 -22.6%  그냥 보유 -64.2%
    2023  봇 +46.9%  그냥 보유 +155.6%
    2024  봇 +75.4%  그냥 보유 +121.3%
    2025  봇 +6.4%  그냥 보유 -6.3%
    2026  봇 +2.4%  그냥 보유 -7.5%

[6] 1시간봉에 맞게 선을 고쳐 주면? — ATR 길이 56(4시간봉 14 × 4)
  1시간봉 ATR 56 · 배수 2  바이낸스 1,536만원 (연 5.0% · 골짜기 -45.0% · 1726번) | 업비트 1,940만원 (연 7.8% · 골짜기 -41.5% · 1700번)
  1시간봉 ATR 56 · 배수 4  바이낸스 6,443만원 (연 23.4% · 골짜기 -32.0% · 669번) | 업비트 1억 3,655만원 (연 34.3% · 골짜기 -32.1% · 632번)
  (비교: 4시간봉 ATR 14 · 배수 2  바이낸스 1억 1,066만원 · 업비트 2억 8,610만원)

[7] 다른 거래소·다른 시대 — 비트스탬프 2014-01 ~ 2017-09 (편도 0.1%)
  1시간봉   연 24.3% · 가장 깊은 골짜기 -31.4% · 사고판 횟수 757번
  2시간봉   연 69.7% · 가장 깊은 골짜기 -20.4% · 사고판 횟수 397번
  4시간봉   연 81.6% · 가장 깊은 골짜기 -20.9% · 사고판 횟수 211번
  6시간봉   연 58.6% · 가장 깊은 골짜기 -25.8% · 사고판 횟수 177번
  8시간봉   연 68.5% · 가장 깊은 골짜기 -23.0% · 사고판 횟수 143번
  12시간봉  연 88.1% · 가장 깊은 골짜기 -25.4% · 사고판 횟수 105번
  일봉     연 77.4% · 가장 깊은 골짜기 -25.3% · 사고판 횟수 71번
  그냥 보유(4시간봉 기준) 연 60.7% · 가장 깊은 골짜기 -82.3%

끝.

파인 스크립트 전문

//@version=5
// 비트코인 4시간봉 — 원조 슈퍼트렌드(ATR 14 · 배수 2) + 어제까지의 일봉 50일선 + 살 때만 변동성 비중
// 연구 근거: 연구/pool/트레이딩뷰-신상261019/sweep8.py (이 파일과 같은 규칙을 파이썬으로 옮긴 판 '4 파인판 · 살 때만 비중')
// ⚠ 2026-10-20 기준 트레이딩뷰 전략 테스터에서 아직 돌려 보지 않았다 — 파이썬 복제판 수치만 있다.
strategy("BTC 슈퍼트렌드 + 50일선 + 변동성 비중", overlay = true,
     initial_capital = 10000000, default_qty_type = strategy.cash,
     commission_type = strategy.commission.percent, commission_value = 0.1,
     process_orders_on_close = true, pyramiding = 0)

atrLen    = input.int(14, "ATR 길이")
factor    = input.float(2.0, "배수")
smaLen    = input.int(50, "일봉 이동평균")
volTarget = input.float(0.5, "목표 변동성(연, 0.5 = 50%)")
volWin    = input.int(42, "변동성 창(4시간봉 개수, 42 = 7일)")

[st, dir] = ta.supertrend(factor, atrLen)

// 어제까지 끝난 일봉 종가가 일봉 50일선 위인가 (lookahead_on + [1] = 다시 그려지지 않는 판)
dayUp = request.security(syminfo.tickerid, "D", close[1] > ta.sma(close, smaLen)[1], lookahead = barmerge.lookahead_on)

// 최근 42봉 수익률 표준편차(모집단) × √(6×365) = 연율 변동성
r   = close / close[1] - 1
vol = ta.stdev(r, volWin) * math.sqrt(6 * 365)
w   = vol > 0 ? math.min(1.0, volTarget / vol) : 1.0

want = dir < 0 and dayUp

if want and strategy.position_size == 0
    strategy.entry("L", strategy.long, qty = strategy.equity * w / close)
if not want and strategy.position_size > 0
    strategy.close("L")

plot(st, "슈퍼트렌드", color = dir < 0 ? color.green : color.red)

재현 코드 전문

# -*- coding: utf-8 -*-
"""비트코인 슈퍼트렌드 봇 — 1시간봉에 걸까, 4시간봉에 걸까 (AlgoLab 재현 코드 · 표준 라이브러리만)

같은 폴더의 data_btc/ 에 얼려 둔 1시간봉 세 개를 읽는다.
  BTCUSDT_1h.csv  바이낸스 현물(2017-08 ~ 2026-10-18)
  KRW-BTC_1h.csv  업비트(2017-09 ~ 2026-10-18)
  BTCUSD_1h.csv   비트스탬프(2013-06 ~ 2017-09) — 다른 거래소·다른 시대 확인용

규칙(트레이딩뷰 btc_supertrend_vol.pine 과 같은 규칙):
  · 1시간봉을 UTC 경계로 묶어 2·4·6·8·12시간봉·일봉을 만든다
  · 슈퍼트렌드(ATR 14 · 배수 2, 파인 ta.supertrend 와 같은 식)가 상승이고
    어제까지 끝난 일봉(UTC) 종가가 일봉 50일 이동평균 위일 때 산다. 둘 중 하나라도 꺼지면 판다
  · 살 때 한 번 비중 = min(1, 0.5 ÷ 최근 7일 연율 변동성) — 팔 때까지 수량 그대로
  · 봉이 끝날 때 판정하고 그 봉 종가에 체결 · 사고팔 때마다 편도 수수료(기본 0.1%)
  python run_supertrend4h.py
"""
import datetime
import math
import os

HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.join(HERE, "data_btc")
START, END = "2017-10-01", "2026-10-18T16:00:00"
SEED = 10_000_000
TFS = (1, 2, 4, 6, 8, 12, 24)


def load(name, a=START, b=END):
    d, o, h, l, c = [], [], [], [], []
    with open(os.path.join(DATA, name), encoding="utf-8") as f:
        next(f)
        for line in f:
            p = line.rstrip("\n").split(",")
            if len(p) < 5:
                continue
            t = p[0] if len(p[0]) > 10 else p[0] + "T00:00:00"   # 자정 봉은 날짜만 적혀 있다
            if t < a or t > b:
                continue
            d.append(t); o.append(float(p[1])); h.append(float(p[2])); l.append(float(p[3])); c.append(float(p[4]))
    return d, o, h, l, c


def resample(d, o, h, l, c, k):
    if k == 1:
        return d, o, h, l, c
    D, O, H, L, C = [], [], [], [], []
    last = None
    for i in range(len(d)):
        key = d[i][:11] + "%02d" % ((int(d[i][11:13]) // k) * k)
        if key != last:
            D.append(key + ":00:00"); O.append(o[i]); H.append(h[i]); L.append(l[i]); C.append(c[i]); last = key
        else:
            H[-1] = max(H[-1], h[i]); L[-1] = min(L[-1], l[i]); C[-1] = c[i]
    return D, O, H, L, C


def series(name, k, a=START, b=END):
    d, o, h, l, c = resample(*load(name, a, b), k)
    yrs = (datetime.datetime.fromisoformat(d[-1]) - datetime.datetime.fromisoformat(d[0])).total_seconds() / (365.25 * 86400)
    return dict(d=d, h=h, l=l, c=c, ppy=len(c) / yrs)


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


def supertrend_up(h, l, c, n=14, factor=2.0):
    """1 = 상승(초록) · 0 = 하락(빨강). 파인 ta.supertrend 와 같은 식."""
    a = atr(h, l, c, n)
    N = len(c)
    up = [(h[i] + l[i]) / 2 + factor * a[i] for i in range(N)]
    lo = [(h[i] + l[i]) / 2 - factor * a[i] for i in range(N)]
    is_upper = [True] * N
    res = [0.0] * N
    for i in range(1, N):
        if math.isnan(a[i]):
            continue
        pl = 0.0 if math.isnan(lo[i - 1]) else lo[i - 1]
        pu = 0.0 if math.isnan(up[i - 1]) else up[i - 1]
        lo[i] = lo[i] if (lo[i] > pl or c[i - 1] < pl) else pl
        up[i] = up[i] if (up[i] < pu or c[i - 1] > pu) else pu
        if math.isnan(a[i - 1]):
            dirn = 1
        elif is_upper[i - 1]:
            dirn = -1 if c[i] > up[i] else 1
        else:
            dirn = 1 if c[i] < lo[i] else -1
        is_upper[i] = dirn == 1
        res[i] = 1.0 if dirn == -1 else 0.0
    return res


def above_daily_sma(d, c, n=50):
    """그 봉의 '어제까지 끝난 일봉 종가 > 일봉 50일선' (일봉 종가 = 그날 마지막 봉 종가)."""
    days, dc = [], []
    for i in range(len(d)):
        if not days or d[i][:10] != days[-1]:
            days.append(d[i][:10]); dc.append(c[i])
        else:
            dc[-1] = c[i]
    up = []
    for k in range(len(dc)):
        if k + 1 < n:
            up.append(0.0)
        else:
            up.append(1.0 if dc[k] > sum(dc[k + 1 - n:k + 1]) / n else 0.0)
    pos = {x: k for k, x in enumerate(days)}
    return [up[pos[x[:10]] - 1] if pos[x[:10]] >= 1 else 0.0 for x in d]


def vol_weight(c, win, ann, target=0.5):
    r = [0.0] + [c[i] / c[i - 1] - 1 for i in range(1, len(c))]
    cs = [0.0]; cs2 = [0.0]
    for x in r:
        cs.append(cs[-1] + x); cs2.append(cs2[-1] + x * x)
    w = [1.0] * len(c)
    for t in range(win, len(c)):
        s = cs[t + 1] - cs[t + 1 - win]; s2 = cs2[t + 1] - cs2[t + 1 - win]
        v = math.sqrt(max(s2 / win - (s / win) ** 2, 0.0)) * math.sqrt(ann)
        w[t] = min(1.0, target / v) if v > 1e-9 else 1.0
    return w


def simulate(c, want, fee):
    """want[i] > 0 이면 들고 있고 싶다(값 = 살 때 비중). 산 뒤에는 수량 그대로, 0 이 되면 판다."""
    cash, units, n_tr = 1.0, 0.0, 0
    E, wts = [], []
    for i in range(len(c)):
        eq = cash + units * c[i]
        cur = units * c[i] / eq if eq > 0 else 0.0
        t = want[i]
        new = 0.0 if t <= 0 else (t if cur == 0 else cur)
        if abs(new - cur) > 1e-12:
            eq -= fee * abs(new - cur) * eq
            units = new * eq / c[i]; cash = eq - units * c[i]; n_tr += 1
        E.append(eq); wts.append(new)
    g = [1.0] + [E[i] / E[i - 1] for i in range(1, len(E))]
    return g, wts, n_tr


def stats(g, ppy):
    eq, peak, mdd = 1.0, 1.0, 0.0
    for x in g:
        eq *= x; peak = max(peak, eq); mdd = min(mdd, eq / peak - 1)
    return eq ** (ppy / len(g)) - 1, mdd, eq


def run(name, k, eval_start, fee=0.001, a=START, b=END, keep=False, atr_n=14, factor=2.0):
    S = series(name, k, a, b)
    d, h, l, c = S["d"], S["h"], S["l"], S["c"]
    bpd = 24 // k
    w = vol_weight(c, 7 * bpd, bpd * 365.0)
    st = supertrend_up(h, l, c, n=atr_n, factor=factor)
    fl = above_daily_sma(d, c)
    g, wts, n_tr = simulate(c, [st[i] * fl[i] * w[i] for i in range(len(c))], fee)
    W = next(i for i, x in enumerate(d) if x >= eval_start)
    cg, mdd, mult = stats(g[W:], S["ppy"])
    ret = [1.0] + [c[i] / c[i - 1] for i in range(1, len(c))]
    hc, hm, hmult = stats(ret[W:], S["ppy"])
    expo = sum(wts[W:]) / len(wts[W:])
    fc, fm, _ = stats([1 + expo * (x - 1) for x in ret[W:]], S["ppy"])
    r = dict(cagr=cg, mdd=mdd, end=mult * SEED, trades=n_tr, hold_cagr=hc, hold_mdd=hm, hold_end=hmult * SEED,
             expo=expo, fix_cagr=fc, fix_mdd=fm, start=d[W], last=d[-1])
    if keep:
        r.update(d=d[W:], g=g[W:], ret=ret[W:], wts=wts[W:], c=c[W:])
    return r


def won(x):
    """원 → '1억 1,066만원' · '1,956만원'"""
    m = int(round(x / 1e4))
    if m >= 10000:
        return "%d억 %s만원" % (m // 10000, format(m % 10000, ",")) if m % 10000 else "%d억원" % (m // 10000)
    return format(m, ",") + "만원"


def pct(x):
    return "%.1f%%" % (x * 100)


def main():
    eval_start = series("KRW-BTC_1h.csv", 4)["d"][400]      # 4시간봉 400봉 워밍업 뒤부터 잰다
    print("비트코인 슈퍼트렌드 봇 · ATR 14 · 배수 2 · 일봉 50일선 · 살 때 비중 0.5÷7일 변동성 · 편도 0.1%")
    print("기간 %s ~ %s (UTC) · 1,000만원\n" % (eval_start, END))
    print("[1] 차트 시간봉만 바꾼다 — 설정은 그대로")
    res = {}
    for k in TFS:
        for ex, f in (("바이낸스", "BTCUSDT_1h.csv"), ("업비트", "KRW-BTC_1h.csv")):
            res[(k, ex)] = r = run(f, k, eval_start, keep=(k in (1, 4)))
        b, u = res[(k, "바이낸스")], res[(k, "업비트")]
        lab = "%d시간봉" % k if k < 24 else "일봉"
        print("  %-6s 바이낸스 %s (연 %s · 가장 깊은 골짜기 %s · 사고판 횟수 %d번) | 업비트 %s (연 %s · %s · %d번)"
              % (lab, won(b["end"]), pct(b["cagr"]), pct(b["mdd"]), b["trades"], won(u["end"]), pct(u["cagr"]), pct(u["mdd"]), u["trades"]))
    for ex in ("바이낸스", "업비트"):
        r = res[(4, ex)]
        print("  그냥 보유 %s %s (연 %s · 가장 깊은 골짜기 %s)" % (ex, won(r["hold_end"]), pct(r["hold_cagr"]), pct(r["hold_mdd"])))
    b4, b1 = res[(4, "바이낸스")], res[(1, "바이낸스")]
    print("  1시간봉은 4시간봉보다 %.1f배 자주 사고팔았다(바이낸스 %d번 대 %d번)" % (b1["trades"] / b4["trades"], b1["trades"], b4["trades"]))

    print("\n[2] 같은 평균 비중을 늘 들고 있었다면(매 봉 맞춤 · 비용 0 — 이 계좌에 유리한 계산)")
    for k in (1, 4, 6, 8):
        for ex in ("바이낸스", "업비트"):
            r = res[(k, ex)]
            print("  %d시간봉 %s 봇 평균 비중 %.0f%% → 고정 비중 계좌 연 %s · 가장 깊은 골짜기 %s (봇 연 %s · %s)"
                  % (k, ex, r["expo"] * 100, pct(r["fix_cagr"]), pct(r["fix_mdd"]), pct(r["cagr"]), pct(r["mdd"])))

    print("\n[3] 수수료를 바꾸면 (바이낸스)")
    for fee in (0.0, 0.0005, 0.001, 0.0012, 0.0015, 0.0025):
        r1 = run("BTCUSDT_1h.csv", 1, eval_start, fee=fee); r4 = run("BTCUSDT_1h.csv", 4, eval_start, fee=fee)
        print("  편도 %.2f%%  1시간봉 연 %s · 4시간봉 연 %s (%s)" % (fee * 100, pct(r1["cagr"]), pct(r4["cagr"]), won(r4["end"])))

    print("\n[4] 최근 2년만 (2024-10-18 16:00 UTC ~) — 같은 계좌를 그날부터 다시 잰다")
    for ex in ("바이낸스", "업비트"):
        row = []
        for k in (1, 4):
            r = res[(k, ex)]
            i0 = next(i for i, x in enumerate(r["d"]) if x >= "2024-10-18T16:00:00")
            _, m1, e1 = stats(r["g"][i0 + 1:], 1); _, m2, e2 = stats(r["ret"][i0 + 1:], 1)
            row.append("%d시간봉 봇 %s(골짜기 %s)" % (k, won(e1 * SEED), pct(m1)))
        print("  %s %s · 그냥 보유 %s(골짜기 %s)" % (ex, " · ".join(row), won(e2 * SEED), pct(m2)))

    print("\n[5] 바이낸스 4시간봉 봇이 사고판 기록")
    r = res[(4, "바이낸스")]
    tr, i, eq = [], 0, 1.0
    eqs = []
    for x in r["g"]:
        eq *= x; eqs.append(eq)
    w = r["wts"]
    while i < len(w):
        if w[i] > 0 and (i == 0 or w[i - 1] == 0):
            j = i
            while j + 1 < len(w) and w[j + 1] > 0:
                j += 1
            ex = j + 1 if j + 1 < len(w) else j
            tr.append((r["d"][i], r["c"][i], w[i], r["d"][ex], r["c"][ex], eqs[ex] / (eqs[i - 1] if i > 0 else 1.0)))
            i = j + 1
        else:
            i += 1
    best = max(tr, key=lambda x: x[5])
    loss = sorted(x[5] - 1 for x in tr if x[5] <= 1)
    print("  산 뒤 판 것 %d번 · 계좌가 늘어난 것 %d번 · 줄어든 것 %d번(한 번에 가장 크게 줄어든 것 %s)"
          % (len(tr), sum(1 for x in tr if x[5] > 1), len(loss), pct(loss[0])))
    print("  계좌를 가장 많이 늘린 한 번: %s 종가 %s달러에 계좌의 %.0f%%로 사서 → %s 종가 %s달러에 팔았다 · 계좌 %.3f배"
          % (best[0], format(round(best[1]), ","), best[2] * 100, best[3], format(round(best[4]), ","), best[5]))
    print("  해마다(바이낸스 4시간봉 봇 대 그냥 보유):")
    for y in range(2018, 2027):
        idx = [k for k, x in enumerate(r["d"]) if x[:4] == str(y)]
        if not idx:
            continue
        b0 = idx[0] - 1 if idx[0] > 0 else 0
        bot = eqs[idx[-1]] / eqs[b0] - 1
        hold = r["c"][idx[-1]] / r["c"][b0] - 1
        print("    %d  봇 %+.1f%%  그냥 보유 %+.1f%%" % (y, bot * 100, hold * 100))

    print("\n[6] 1시간봉에 맞게 선을 고쳐 주면? — ATR 길이 56(4시간봉 14 × 4)")
    for fa in (2.0, 4.0):
        row = []
        for ex, f in (("바이낸스", "BTCUSDT_1h.csv"), ("업비트", "KRW-BTC_1h.csv")):
            r = run(f, 1, eval_start, atr_n=56, factor=fa)
            row.append("%s %s (연 %s · 골짜기 %s · %d번)" % (ex, won(r["end"]), pct(r["cagr"]), pct(r["mdd"]), r["trades"]))
        print("  1시간봉 ATR 56 · 배수 %d  %s" % (fa, " | ".join(row)))
    print("  (비교: 4시간봉 ATR 14 · 배수 2  바이낸스 %s · 업비트 %s)" % (won(res[(4, "바이낸스")]["end"]), won(res[(4, "업비트")]["end"])))

    print("\n[7] 다른 거래소·다른 시대 — 비트스탬프 2014-01 ~ 2017-09 (편도 0.1%)")
    bs = {}
    for k in TFS:
        bs[k] = r = run("BTCUSD_1h.csv", k, "2014-01-01", a="2013-06-01", b="2017-09-30T23:59:59")
        lab = "%d시간봉" % k if k < 24 else "일봉"
        print("  %-6s 연 %s · 가장 깊은 골짜기 %s · 사고판 횟수 %d번" % (lab, pct(r["cagr"]), pct(r["mdd"]), r["trades"]))
    print("  그냥 보유(4시간봉 기준) 연 %s · 가장 깊은 골짜기 %s" % (pct(bs[4]["hold_cagr"]), pct(bs[4]["hold_mdd"])))
    print("\n끝.")


if __name__ == "__main__":
    main()

알고랩 Strategy Lab — 전략을 검증하고, 검증한 것만 자동매매로 옮깁니다. 4시간마다 봉이 끝날 때 판정하고 그 자리에서 주문하는 일은 사람보다 봇이 잘합니다. 알고랩 자동매매 살펴보기 · 전략 검증 전체 보기

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