비트코인 · 터틀 트레이딩 · 돌파 매매 · 실측 재현 페이지
비트코인 일봉 종가가 직전 20일 최고가를 넘으면 사고, 직전 10일 최저가 아래로 내려가면 파는 규칙 — '터틀 트레이딩'으로 알려진 돌파 규칙입니다. 같은 신호로 1,000만원 계좌 둘을 2018년 1월 1일부터 2026년 10월 16일까지 굴렸습니다. ① 한 번에 다 — 신호가 나면 전액 산다 ② 나눠 사기 — 처음엔 조금만 사고, 오를 때마다 같은 양을 더 산다(터틀 원조의 사이징·더 사기·손절 그대로). 종가로 판단해 다음 날 시가에 체결했고, 편도 수수료 0.1%를 뗐습니다.
| 항목 | 값 |
|---|---|
| 사는 신호 | 일봉 종가 > 직전 20일 최고가 → 다음 날 시가에 산다 |
| 파는 신호 | 종가 < 직전 10일 최저가 → 다음 날 시가에 전부 판다 |
| 하루 움직임 N | 지난 20일 평균 변동폭(와일더 ATR 20) |
| 처음 한 번에 사는 양 | 계좌 평가액 × 1% ÷ N 개 — 값이 N 만큼 떨어지면 계좌가 1% 줄어드는 양(1%어치가 아닙니다). 2026-10-16 바이낸스 종가로 1,000만원 계좌라면 약 3,648,000원어치 |
| 더 사기 | 종가 ≥ 마지막으로 산 값 + N 의 절반 → 같은 양을 한 번 더 · 처음 산 것까지 최대 4번 |
| 손절 | 종가 < 마지막으로 산 값 − 2N → 다음 날 시가에 전부 판다(10일 최저가를 기다리지 않음) |
| 현금 | 모자라면 남은 현금만큼만 산다(빚 없음 · 계좌 100% 까지) |
| 계좌 | 연 수익 | 가장 크게 줄어든 폭 | 1,000만원 → | 2024-01 부터 |
|---|---|---|---|---|
| 나눠 사기 | 연 33.31% | -30.67% | 1억 2,513만원 | 연 7.82% / -27.80% |
| 한 번에 다 | 연 34.97% | -53.95% | 1억 3,952만원 | 연 11.93% / -38.02% |
| 같은 비중 | 연 11.21% | -35.71% | 2,544만원 | 연 9.44% / -19.40% |
| 그냥 보유 | 연 22.84% | -81.18% | 6,100만원 | 연 24.61% / -52.97% |
나눠 사기 거래 46번 중 손해 32번 · 가장 큰 거래 2020-10-11 → 2021-03-25 +353.13% · 처음 한 번에 산 양이 계좌의 3분의 1 미만이었던 거래 34번 / 46번
| 계좌 | 연 수익 | 가장 크게 줄어든 폭 | 1,000만원 → | 2024-01 부터 |
|---|---|---|---|---|
| 나눠 사기 | 연 48.96% | -30.27% | 3억 3,183만원 | 연 24.59% / -23.48% |
| 한 번에 다 | 연 35.17% | -72.48% | 1억 4,137만원 | 연 15.82% / -32.30% |
| 같은 비중 | 연 11.88% | -43.29% | 2,683만원 | 연 10.08% / -18.77% |
| 그냥 보유 | 연 22.25% | -86.81% | 5,847만원 | 연 25.25% / -49.94% |
돌리는 법: zip 을 풀고 python run_turtle.py
구간 2018-01-01 ~ 2026-10-16 · 편도 수수료 0.1% · 원금 1,000만원(달러 계좌는 같은 배수로 환산) == 비트코인 · 바이낸스 (마지막 봉 2026-10-16) 나눠 사기 연 33.31% · 가장 크게 줄어든 폭 -30.67% · 1,000만원 → 1억 2,513만원 · 2024-01~ 연 7.82% / -27.80% · 거래 46번 · 평균 비중 30.4% 한 번에 다 연 34.97% · 가장 크게 줄어든 폭 -53.95% · 1,000만원 → 1억 3,952만원 · 2024-01~ 연 11.93% / -38.02% · 거래 42번 같은 비중 연 11.21% · 가장 크게 줄어든 폭 -35.71% · 1,000만원 → 2,544만원 · 2024-01~ 연 9.44% / -19.40% 그냥 보유 연 22.84% · 가장 크게 줄어든 폭 -81.18% · 1,000만원 → 6,100만원 · 2024-01~ 연 24.61% / -52.97% 나눠 사기 거래 46번 중 손해 32번 · 가장 큰 손해 -7.20% · 가장 큰 거래 2020-10-11 → 2021-03-25 +353.13% 처음 한 번에 산 양이 계좌의 3분의 1 미만이었던 거래 34번 / 46번 2026-10-16 종가 기준 1,000만원 계좌의 처음 한 번 = 약 3,648,000원어치 (N = 종가의 2.74%) (원조 느린 규칙 55일 돌파·20일 청산) 나눠 사기 연 20.73% · -44.86% / 한 번에 다 연 25.97% · -47.12% == 비트코인 · 업비트 (마지막 봉 2026-10-16) 나눠 사기 연 48.96% · 가장 크게 줄어든 폭 -30.27% · 1,000만원 → 3억 3,183만원 · 2024-01~ 연 24.59% / -23.48% · 거래 46번 · 평균 비중 32.0% 한 번에 다 연 35.17% · 가장 크게 줄어든 폭 -72.48% · 1,000만원 → 1억 4,137만원 · 2024-01~ 연 15.82% / -32.30% · 거래 45번 같은 비중 연 11.88% · 가장 크게 줄어든 폭 -43.29% · 1,000만원 → 2,683만원 · 2024-01~ 연 10.08% / -18.77% 그냥 보유 연 22.25% · 가장 크게 줄어든 폭 -86.81% · 1,000만원 → 5,847만원 · 2024-01~ 연 25.25% / -49.94% 나눠 사기 거래 46번 중 손해 28번 · 가장 큰 손해 -7.88% · 가장 큰 거래 2019-03-21 → 2019-07-15 +170.02% 처음 한 번에 산 양이 계좌의 3분의 1 미만이었던 거래 25번 / 46번 2026-10-16 종가 기준 1,000만원 계좌의 처음 한 번 = 약 4,462,000원어치 (N = 종가의 2.24%) (원조 느린 규칙 55일 돌파·20일 청산) 나눠 사기 연 43.59% · -32.76% / 한 번에 다 연 28.50% · -67.71% == 이더리움 · 바이낸스 (마지막 봉 2026-10-16) 나눠 사기 연 16.41% · 가장 크게 줄어든 폭 -44.15% · 1,000만원 → 3,803만원 · 2024-01~ 연 -7.42% / -44.15% · 거래 50번 · 평균 비중 22.7% 한 번에 다 연 21.99% · 가장 크게 줄어든 폭 -67.83% · 1,000만원 → 5,738만원 · 2024-01~ 연 -18.59% / -62.46% · 거래 47번 같은 비중 연 10.27% · 가장 크게 줄어든 폭 -40.63% · 1,000만원 → 2,362만원 · 2024-01~ 연 4.59% / -21.23% 그냥 보유 연 14.19% · 가장 크게 줄어든 폭 -93.97% · 1,000만원 → 3,210만원 · 2024-01~ 연 1.08% / -67.55% == 이더리움 · 업비트 (마지막 봉 2026-10-16) 나눠 사기 연 34.20% · 가장 크게 줄어든 폭 -37.04% · 1,000만원 → 1억 3,263만원 · 2024-01~ 연 10.33% / -29.57% · 거래 45번 · 평균 비중 28.1% 한 번에 다 연 43.07% · 가장 크게 줄어든 폭 -65.81% · 1,000만원 → 2억 3,277만원 · 2024-01~ 연 3.50% / -53.32% · 거래 44번 같은 비중 연 11.98% · 가장 크게 줄어든 폭 -53.82% · 1,000만원 → 2,703만원 · 2024-01~ 연 5.39% / -23.71% 그냥 보유 연 13.74% · 가장 크게 줄어든 폭 -95.59% · 1,000만원 → 3,100만원 · 2024-01~ 연 1.59% / -64.30% 끝.
# -*- coding: utf-8 -*-
"""비트코인 돌파 매매 — 한 번에 다 사기 vs 오를 때 나눠 사기(터틀 사이징) · AlgoLab 재현 코드
표준 라이브러리만 씁니다. 같은 폴더의 data_turtle/ 에 얼려 둔 일봉(2026-10-16 까지)을 읽습니다.
python run_turtle.py
규칙(일봉 · 종가로 판단 · 다음 날 시가에 체결 · 편도 수수료 0.1%)
사는 신호 종가 > 직전 20일 최고가
파는 신호 종가 < 직전 10일 최저가 → 전부 판다
N 지난 20일 평균 변동폭(와일더 ATR 20)
한 번에 계좌 평가액 × 1% ÷ N 개 (값이 N 만큼 떨어지면 계좌가 1% 줄어드는 양)
더 사기 종가 ≥ 마지막으로 산 값 + 0.5N 이면 같은 양을 한 번 더 · 처음 산 것까지 최대 4번
손절 종가 < 마지막으로 산 값 − 2N → 전부 판다
현금이 모자라면 남은 현금만큼만 산다(빚 없음)
비교
한 번에 다 같은 사는·파는 신호, 신호가 나면 전액(더 사기·손절 없음)
같은 비중 나눠 사기 계좌의 평균 비트코인 비중을 늘 들고 있는 계좌(매달 첫날 시가에 맞춤)
그냥 보유 2018-01-01 종가부터 들고 있기
"""
import csv
import datetime
import os
HERE = os.path.dirname(os.path.abspath(__file__))
DATA = os.path.join(HERE, "data_turtle")
FEE = 0.001
START, END, RECENT = "2018-01-01", "2026-10-16", "2024-01-01"
SEED = 10_000_000
FILES = [("비트코인", "바이낸스", "BTCUSDT_1d.csv"), ("비트코인", "업비트", "KRW-BTC_1d.csv"),
("이더리움", "바이낸스", "ETHUSDT_1d.csv"), ("이더리움", "업비트", "KRW-ETH_1d.csv")]
def load(name):
d, o, h, l, c = [], [], [], [], []
with open(os.path.join(DATA, name), encoding="utf-8") as f:
r = csv.reader(f)
next(r)
for row in r:
if row[0][:10] > END:
continue
d.append(row[0][:10]); o.append(float(row[1])); h.append(float(row[2]))
l.append(float(row[3])); c.append(float(row[4]))
return d, o, h, l, c
def atr20(h, l, c):
n = len(c)
tr = [h[0] - l[0]] + [max(h[t] - l[t], abs(h[t] - c[t - 1]), abs(l[t] - c[t - 1])) for t in range(1, n)]
N = [None] * n
N[19] = sum(tr[:20]) / 20
for t in range(20, n):
N[t] = (19 * N[t - 1] + tr[t]) / 20
return N
def channel(h, l, n_in, n_out):
n = len(h)
hi = [max(h[t - n_in:t]) if t >= n_in else None for t in range(n)]
lo = [min(l[t - n_out:t]) if t >= n_out else None for t in range(n)]
return hi, lo
def run(D, n_in=20, n_out=10, risk=0.01, max_u=4, stop=True, full=False):
d, o, c, N = D["d"], D["o"], D["c"], D["N"]
hi, lo = channel(D["h"], D["l"], n_in, n_out)
i0 = D["i0"]
cash, qty, units, last_fill, n_ent = 1.0, 0.0, 0, 0.0, 0.0
order, cur, trades, eq, expo = None, None, [], [], []
for t in range(i0, len(c)):
if order is not None:
px = o[t]
if order[0] == "sell":
cash += qty * px * (1 - FEE)
cur.update(out=d[t], why=order[1], pnl=cash / cur["e0"] - 1)
trades.append(cur)
qty, units, cur = 0.0, 0, None
else:
afford = cash / (px * (1 + FEE))
q = afford if order[1] is None else min(order[1], afford)
if q > 0:
if cur is None:
cur = dict(inn=d[t], e0=cash + qty * px, buys=[])
cash -= q * px * (1 + FEE)
qty += q
units += 1
last_fill, n_ent = px, order[2]
cur["buys"].append(qty * px / (cash + qty * px))
order = None
e = cash + qty * c[t]
eq.append(e)
expo.append(qty * c[t] / e if e > 0 else 0.0)
if t == len(c) - 1 or hi[t] is None or lo[t] is None or N[t] is None:
continue
if units > 0:
if c[t] < lo[t]:
order = ("sell", "10일 최저가")
elif not full and stop and c[t] < last_fill - 2 * n_ent:
order = ("sell", "2N 손절")
elif not full and units < max_u and c[t] >= last_fill + 0.5 * n_ent:
order = ("buy", risk * e / N[t], N[t])
elif c[t] > hi[t]:
order = ("buy", None if full else risk * e / N[t], N[t])
return eq, expo, trades
def same_weight(D, w):
d, o, c, i0 = D["d"], D["o"], D["c"], D["i0"]
cash, qty, eq = 1.0, 0.0, []
for t in range(i0, len(c)):
if t == i0 or d[t][:7] != d[t - 1][:7]:
e = cash + qty * o[t]
tq = w * e / o[t]
dq = tq - qty
cash -= dq * o[t] + abs(dq) * o[t] * FEE
qty = tq
eq.append(cash + qty * c[t])
return eq
def stats(eq, dates):
yrs = (datetime.date.fromisoformat(dates[-1]) - datetime.date.fromisoformat(dates[0])).days / 365.25
pk, mdd = eq[0], 0.0
for v in eq:
pk = max(pk, v)
mdd = min(mdd, v / pk - 1)
return (eq[-1] / eq[0]) ** (1 / yrs) - 1, mdd, eq[-1] / eq[0]
def won(x):
v = int(round(SEED * x, -4)) // 10000
return (f"{v // 10000}억 {v % 10000:,}만원" if v % 10000 else f"{v // 10000}억원") if v >= 10000 else f"{v:,}만원"
def line(name, eq, dates):
g, m, x = stats(eq, dates)
j = dates.index(next(x for x in dates if x >= RECENT))
g2, m2, _ = stats(eq[j:], dates[j:])
return f" {name:8s} 연 {g * 100:.2f}% · 가장 크게 줄어든 폭 {m * 100:.2f}% · 1,000만원 → {won(x)} · 2024-01~ 연 {g2 * 100:.2f}% / {m2 * 100:.2f}%"
def main():
print(f"구간 {START} ~ {END} · 편도 수수료 {FEE * 100:.1f}% · 원금 1,000만원(달러 계좌는 같은 배수로 환산)\n")
for coin, ex, f in FILES:
d, o, h, l, c = load(f)
i0 = next(i for i, x in enumerate(d) if x >= START)
D = dict(d=d, o=o, h=h, l=l, c=c, N=atr20(h, l, c), i0=i0)
dd = d[i0:]
eq_t, ex_t, tr_t = run(D)
eq_f, _, tr_f = run(D, full=True)
w = sum(ex_t) / len(ex_t)
eq_w = same_weight(D, w)
bh = [x / c[i0] for x in c[i0:]]
print(f"== {coin} · {ex} (마지막 봉 {d[-1]})")
print(line("나눠 사기", eq_t, dd) + f" · 거래 {len(tr_t)}번 · 평균 비중 {w * 100:.1f}%")
print(line("한 번에 다", eq_f, dd) + f" · 거래 {len(tr_f)}번")
print(line("같은 비중", eq_w, dd))
print(line("그냥 보유", bh, dd))
if coin == "비트코인":
lose = sum(t["pnl"] <= 0 for t in tr_t)
small = sum(t["buys"][0] < 1 / 3 for t in tr_t)
worst = min(t["pnl"] for t in tr_t)
best = max(tr_t, key=lambda t: t["pnl"])
print(f" 나눠 사기 거래 {len(tr_t)}번 중 손해 {lose}번 · 가장 큰 손해 {worst * 100:.2f}% · "
f"가장 큰 거래 {best['inn']} → {best['out']} {best['pnl'] * 100:+.2f}%")
print(f" 처음 한 번에 산 양이 계좌의 3분의 1 미만이었던 거래 {small}번 / {len(tr_t)}번")
t = len(c) - 1
print(f" {d[t]} 종가 기준 1,000만원 계좌의 처음 한 번 = 약 {round(SEED * 0.01 / D['N'][t] * c[t], -3):,.0f}원어치 "
f"(N = 종가의 {D['N'][t] / c[t] * 100:.2f}%)")
eq_s2, _, _ = run(D, n_in=55, n_out=20)
g, m, _ = stats(eq_s2, dd)
eq_s2f, _, _ = run(D, n_in=55, n_out=20, full=True)
g2, m2, _ = stats(eq_s2f, dd)
print(f" (원조 느린 규칙 55일 돌파·20일 청산) 나눠 사기 연 {g * 100:.2f}% · {m * 100:.2f}% / 한 번에 다 연 {g2 * 100:.2f}% · {m2 * 100:.2f}%")
print()
print("끝.")
if __name__ == "__main__":
main()
알고랩 Strategy Lab — 전략을 검증하고, 검증한 것만 자동매매로 옮깁니다. 매일 종가를 보고 하루 움직임을 다시 재고, 살 양을 계산하는 일은 사람보다 봇이 잘합니다. 알고랩 자동매매 살펴보기 · 전략 검증 전체 보기
과거 데이터를 돌려 본 결과이며 미래 수익을 보장하지 않습니다. 투자 판단과 책임은 본인에게 있습니다.