2014년 9월부터 2026년 8월까지, 비트코인에 1000만원을 넣고 마틴게일 봇을 돌렸습니다. 345번 익절에 실패 0번 — 승률 100%입니다. 그런데 최종 잔고는 1742만원, 같은 돈을 그냥 들고 있었다면 17억 183만원이었습니다. 같은 수학이 물타기라는 이름으로 주식 계좌에도 있습니다. 전부 실데이터로 검증했습니다.
지면 배팅을 2배로 올려서 다시 겁니다. 1만원부터 10번 걸 돈(1023만원)을 들고 가면 한 세트를 지는 경우는 10연패뿐이라, 세트 승률이 99.87%가 됩니다. 그런데—
| 값 | 뜻 | |
|---|---|---|
| 한 판 승률 | 48.6% | 유럽식 룰렛 · 빨간색 |
| 세트 승률 | 99.87% | 10연패에만 진다 |
| 1000세트 안에 10연패 | 72% | 대부분 절벽을 만난다 |
| 세트당 기대값 | -0.31단위 | 이길 때 +1 · 질 때 -1023 |
승률은 배팅 방식으로 얼마든지 올라갑니다. 기대값은 그 방식으로 바뀌지 않습니다.
시중 마틴게일 봇의 전형적인 설정: 1000만원을 2배 등비 7단계로 나누고, 마지막 매수가 -5%마다 다음 단계를 사고, 평균단가 +3%면 전량 익절. 일봉 종가 판정 · 수수료 왕복 0.1%.
| 전적 | 봇 최종 | 그냥 보유 | 최장 물림 | |
|---|---|---|---|---|
| 비트코인 (2014~2026) | 345승 0패 | 1742만원 | 17억 183만원 (170배) | 793일 · 평가손 -68% |
| 이더리움 (2017~2026) | 197승 0패 | 1766만원 | 7612만원 | 722일 |
승률 100%의 비밀 — 이 봇은 손해를 확정하지 않습니다. 패배는 기록 대신 계좌 안의 미실현 평가손으로 쌓입니다. 2021년 12월부터 2024년 2월까지 793일 동안 돈이 전부 잠긴 채, 기록상으로는 무패였습니다.
떨어질 때마다 더 사서 평균 단가를 낮추고, 본전이 오면 판다 — 지면 올리고 이기면 리셋하는 마틴게일과 같은 구조입니다. 규칙으로 만들어 세 지수의 역사적 고점부터 돌렸습니다: 1000만원을 250만원씩, 평균단가 -10%마다 매수, 평균단가 +5%면 전량 매도 후 재진입.
| 시작 | 물타기+본전탈출 | 탈출만 끔 | 그냥 보유 | 물타기 승률 |
|---|---|---|---|---|
| 닛케이 1989 고점 (36.7년) | 1385만원 · 물림 33년 10개월 | 1933만원 | 1699만원 | 100% |
| 코스피 2007 고점 (18.8년) | 1975만원 | 3879만원 | 3346만원 | 100% |
| S&P500 2000 고점 (26.4년) | 2012만원 | 9130만원 | 7989만원 | 100% |
세 지수 모두 승률 100% — 그리고 세 지수 모두 꼴찌. 매수 규칙은 그대로 두고 본전에 파는 규칙 하나만 껐더니 S&P500에서 2012만원이 9130만원이 됐습니다. 범인은 분할 매수가 아니라 '본전 탈출'입니다.
· 전부 시뮬레이션입니다. 물타기는 지수, 봇은 청산 없는 현물 기준 — 레버리지를 쓰는 실전 봇이나
0원까지 갈 수 있는 개별 종목의 물타기는 이보다 위험합니다.
· 수수료는 왕복 0.1%만 반영했고 세금·호가 스프레드는 뺐습니다. 현금이자 연 2%를 줘도 순위는 유지됩니다.
· 파라미터 민감도(추가매수 트리거·익절폭·조각 수 6조합)와 시작일 민감도(6개)에서 결론의 부호가 유지됨을 확인했습니다.
· 과거 데이터는 미래를 보장하지 않습니다. 계산 기준일은 2026-08-28로 고정했습니다.
파이썬 표준 라이브러리만 씁니다. python martingale_backtest.py 한 줄로
영상의 모든 숫자가 재현됩니다. martingale_backtest.py 내려받기
# -*- coding: utf-8 -*-
"""물타기 = 마틴게일 — 영상 수치 재현 코드 (AlgoLab)
영상: 「자동매매 345번 다 이겼는데 16억을 놓쳤습니다」 (2026-08-31)
이 파일 하나로 영상의 모든 수치가 재현됩니다. 표준 라이브러리만 씁니다.
python martingale_backtest.py
① 룰렛 마틴게일 수학 — 세트 승률 99.87% · 1000세트 내 10연패 확률 72%
② 비트코인/이더리움 마틴게일 봇 — 345승 0패 → 1742만원 (보유 17억 183만원)
③ 지수 물타기 (닛케이/코스피/S&P500) — 승률 100% · 전부 꼴찌
④ 본전 탈출만 끄면 — S&P500 2012만원 → 9130만원
데이터: 야후 파이낸스 v8 chart API (adjclose = 배당 재투자 총수익)
"""
import csv
import datetime
import io
import json
import os
import random
import urllib.parse
import urllib.request
# 영상을 만든 날 기준으로 고정 — 최신 데이터로 보려면 None 으로 바꾸세요.
# (고정하지 않으면 그날그날 데이터가 늘어나 영상과 숫자가 달라집니다)
ASOF = datetime.date(2026, 8, 28)
FEE = 0.0005 # 편도 0.05% (왕복 0.1%)
CAP = 1000.0 # 만원
# ──────────────────────────────────────────────────────────────────────────
# 데이터
# ──────────────────────────────────────────────────────────────────────────
def fetch(sym):
"""야후 일봉 (date, adjclose). ASOF 이후는 버린다. 같은 폴더에 CSV 캐시."""
cache = f"_cache_{sym.replace('^', '')}.csv"
if os.path.exists(cache):
with io.open(cache, encoding="utf-8") as f:
return [(r["date"], float(r["adjclose"])) for r in csv.DictReader(f)]
p2 = int(datetime.datetime(ASOF.year, ASOF.month, ASOF.day).timestamp()) + 86400
u = (f"https://query1.finance.yahoo.com/v8/finance/chart/{urllib.parse.quote(sym)}"
f"?period1=0&period2={p2}&interval=1d")
req = urllib.request.Request(u, headers={"User-Agent": "Mozilla/5.0"})
j = json.loads(urllib.request.urlopen(req, timeout=60).read())
r = j["chart"]["result"][0]
ts = r["timestamp"]
close = r["indicators"]["quote"][0]["close"]
adj = (r["indicators"].get("adjclose") or [{}])[0].get("adjclose") or close
rows = []
for t, c, a in zip(ts, close, adj):
if c is None or a is None:
continue
d = datetime.date.fromtimestamp(t)
if ASOF and d > ASOF:
continue
rows.append((d.isoformat(), a))
with io.open(cache, "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["date", "adjclose"])
w.writerows((d, f"{a:.6f}") for d, a in rows)
return rows
# ──────────────────────────────────────────────────────────────────────────
# ① 룰렛 마틴게일 (순수 수학)
# ──────────────────────────────────────────────────────────────────────────
def roulette():
p = 18 / 37 # 유럽식 룰렛, 빨간색
levels, capital = 10, 2 ** 10 - 1 # 1+2+...+512 = 1023단위
q = (1 - p) ** levels # 한 세트가 10연패로 끝날 확률
print("① 룰렛 마틴게일")
print(f" 한 판 승률 {p * 100:.1f}%")
print(f" 세트 승률 {(1 - q) * 100:.2f}%")
print(f" 1000세트 내 10연패 {(1 - (1 - q) ** 1000) * 100:.0f}%")
print(f" 세트당 기대값 {(1 - q) * 1 - q * capital:+.2f}단위 (이길 때 +1, 질 때 -{capital})")
# ──────────────────────────────────────────────────────────────────────────
# ② 코인 마틴게일 봇 — 7레벨 2배 등비 · -5% 추가 · 평단 +3% 익절 · 일봉 종가
# ──────────────────────────────────────────────────────────────────────────
def run_bot(rows, levels=7, drop=0.05, take=0.03, mult=2.0):
weights = [mult ** i for i in range(levels)]
unit = CAP / sum(weights)
cash, qty, invested, level = CAP, 0.0, 0.0, 0
last_buy = None
cycles = wins = 0
stuck_from, longest = None, 0
worst_unreal = 0.0
for d, px in rows:
if qty == 0:
spend = min(unit * weights[0], cash)
qty = spend * (1 - FEE) / px
cash -= spend
invested, level, last_buy = spend, 1, px
continue
avg = invested / qty
if px >= avg * (1 + take):
cash += qty * px * (1 - FEE)
qty, invested, level, last_buy = 0.0, 0.0, 0, None
cycles += 1
wins += 1
if stuck_from:
span = (datetime.date.fromisoformat(d)
- datetime.date.fromisoformat(stuck_from)).days
longest = max(longest, span)
stuck_from = None
elif level < levels and px <= last_buy * (1 - drop):
spend = min(unit * weights[level], cash)
if spend > 0:
qty += spend * (1 - FEE) / px
cash -= spend
invested += spend
level += 1
last_buy = px
if level >= levels and stuck_from is None:
stuck_from = d
elif level >= levels:
if stuck_from is None:
stuck_from = d
worst_unreal = min(worst_unreal, (px - avg) / avg * 100)
final = cash + qty * rows[-1][1]
hold = rows[-1][1] / rows[0][1]
return cycles, wins, final, hold, longest, worst_unreal
def bots():
print("\n② 코인 마틴게일 봇 (1000만원 · 7단계 2배 등비 · -5% 추가 · 평단 +3% 익절)")
for sym, name in (("BTC-USD", "비트코인"), ("ETH-USD", "이더리움")):
rows = fetch(sym)
cycles, wins, final, hold, longest, worst = run_bot(rows)
print(f" {name} {rows[0][0]} ~ {rows[-1][0]}")
print(f" {wins}승 {cycles - wins}패 · 최종 {final:,.0f}만원"
f" · 그냥 보유 {hold * CAP:,.0f}만원 ({hold:.0f}배)")
print(f" 최장 물림 {longest}일 · 물림 중 최악 평가손 {worst:.0f}%")
# ──────────────────────────────────────────────────────────────────────────
# ③④ 지수 물타기 — 1/4 진입 · 평단 -10%마다 1/4 추가 · 평단 +5% 전량 매도
# sell=False 면 같은 매수에 "본전 탈출"만 끈 것 (팔지 않고 모은다)
# ──────────────────────────────────────────────────────────────────────────
def sim_water(rows, parts=4, drop=0.10, take=0.05, sell=True):
part = CAP / parts
cash, qty, invested, used = CAP, 0.0, 0.0, 0
cycles = wins = 0
for d, px in rows:
avg = invested / qty if qty > 0 else None
if qty == 0:
spend = min(part, cash)
qty = spend * (1 - FEE) / px
cash -= spend
invested, used = spend, 1
elif sell and px >= avg * (1 + take):
cash += qty * px * (1 - FEE)
qty, invested, used = 0.0, 0.0, 0
cycles += 1
wins += 1
elif used < parts and px <= avg * (1 - drop):
spend = min(part, cash)
if spend > 0:
qty += spend * (1 - FEE) / px
cash -= spend
invested += spend
used += 1
final = cash + qty * rows[-1][1]
return final, cycles, wins
def water():
print("\n③ 지수 물타기 (1000만원 · 250만원씩 · 평단 -10%마다 매수 · 평단 +5% 전량 매도)")
scen = (("^N225", "1989-12-29", "닛케이 1989 고점"),
("^KS11", "2007-10-31", "코스피 2007 고점"),
("SPY", "2000-03-24", "S&P500 2000 고점"))
for sym, start, label in scen:
rows = [r for r in fetch(sym) if r[0] >= start]
w, c, win = sim_water(rows, sell=True)
p, _, _ = sim_water(rows, sell=False)
hold = rows[-1][1] / rows[0][1] * CAP * (1 - FEE)
print(f" {label} ({start} ~ {rows[-1][0]})")
print(f" 물타기+본전탈출 {w:,.0f}만원 · {win}/{c} 전승")
print(f" 탈출만 끔 {p:,.0f}만원")
print(f" 그냥 보유 {hold:,.0f}만원")
if __name__ == "__main__":
print(f"기준일 {ASOF} 고정 (최신으로 보려면 ASOF = None)\n")
roulette()
bots()
water()
print("\n전체 검증 스크립트·차트 코드: https://algolab.co.kr/mart")
※ 본 페이지의 수치는 공개된 시장 데이터를 과거 기준으로 검증한 교육·검증 목적의 자료이며, 특정인에 대한 투자권유나 매수·매도 권유가 아닙니다. 과거 백테스트 성과가 미래 수익을 보장하지 않습니다. 시뮬레이션 가정(수수료·판정 기준·기준일)은 본문과 코드에 명시되어 있으며, 실제 거래 조건은 거래소·증권사·상품에 따라 다를 수 있습니다. 모든 투자 판단과 그에 따른 책임은 이용자 본인에게 있습니다.