정직 검증 · 물타기 = 마틴게일

345번 다 이긴 봇이
그냥 보유의 98분의 1을 벌었습니다.

2014년 9월부터 2026년 8월까지, 비트코인에 1000만원을 넣고 마틴게일 봇을 돌렸습니다. 345번 익절에 실패 0번 — 승률 100%입니다. 그런데 최종 잔고는 1742만원, 같은 돈을 그냥 들고 있었다면 17억 183만원이었습니다. 같은 수학이 물타기라는 이름으로 주식 계좌에도 있습니다. 전부 실데이터로 검증했습니다.

재현 코드 받기 이 계산의 한계 →
345승 0패
마틴게일 봇 · 비트코인 12년
1742만원
봇의 최종 잔고 (보유 17억 183만원)
33년 10개월
닛케이 물타기의 최장 물림

1. 승률은 만들 수 있습니다 — 룰렛의 수학

지면 배팅을 2배로 올려서 다시 겁니다. 1만원부터 10번 걸 돈(1023만원)을 들고 가면 한 세트를 지는 경우는 10연패뿐이라, 세트 승률이 99.87%가 됩니다. 그런데—

한 판 승률48.6%유럽식 룰렛 · 빨간색
세트 승률99.87%10연패에만 진다
1000세트 안에 10연패72%대부분 절벽을 만난다
세트당 기대값-0.31단위이길 때 +1 · 질 때 -1023

승률은 배팅 방식으로 얼마든지 올라갑니다. 기대값은 그 방식으로 바뀌지 않습니다.

2. 진짜 코인에 돌렸습니다 — 12년 실측

시중 마틴게일 봇의 전형적인 설정: 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일 동안 돈이 전부 잠긴 채, 기록상으로는 무패였습니다.

3. 같은 수학이 계좌에 있습니다 — 물타기

떨어질 때마다 더 사서 평균 단가를 낮추고, 본전이 오면 판다 — 지면 올리고 이기면 리셋하는 마틴게일과 같은 구조입니다. 규칙으로 만들어 세 지수의 역사적 고점부터 돌렸습니다: 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만원이 됐습니다. 범인은 분할 매수가 아니라 '본전 탈출'입니다.

4. 처방

① 총액·횟수를 먼저
1000만원을 4번에 — 정하는 순간 마틴게일이 계획된 분할 매수가 됩니다
② 본전에 팔지 않기
본전 탈출은 이길 때 조금 벌고 물릴 때 수십 년 잠기는 구조를 만듭니다
③ 승률이 아니라 기대값
승률 100%는 손실을 평가손으로 미뤄 둔 착시일 수 있습니다

이 계산의 한계

· 전부 시뮬레이션입니다. 물타기는 지수, 봇은 청산 없는 현물 기준 — 레버리지를 쓰는 실전 봇이나 0원까지 갈 수 있는 개별 종목의 물타기는 이보다 위험합니다.
· 수수료는 왕복 0.1%만 반영했고 세금·호가 스프레드는 뺐습니다. 현금이자 연 2%를 줘도 순위는 유지됩니다.
· 파라미터 민감도(추가매수 트리거·익절폭·조각 수 6조합)와 시작일 민감도(6개)에서 결론의 부호가 유지됨을 확인했습니다.
· 과거 데이터는 미래를 보장하지 않습니다. 계산 기준일은 2026-08-28로 고정했습니다.

재현 코드 — 믿지 말고 직접 돌려보세요

파이썬 표준 라이브러리만 씁니다. python martingale_backtest.py 한 줄로 영상의 모든 숫자가 재현됩니다. 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")
Strategy Lab · 이 검증 더 보기
내 전략도 같은 잣대로 검증해드립니다 — 제작 전 백테스트 상담 → 그리드봇 8.7년 — 하락 41전 41승, 최종 254만원같은 수학 폭락에 팔지 않는 규칙 — −55%를 −23%로물타기 대신 전략 검증 전체 보기 →

※ 본 페이지의 수치는 공개된 시장 데이터를 과거 기준으로 검증한 교육·검증 목적의 자료이며, 특정인에 대한 투자권유나 매수·매도 권유가 아닙니다. 과거 백테스트 성과가 미래 수익을 보장하지 않습니다. 시뮬레이션 가정(수수료·판정 기준·기준일)은 본문과 코드에 명시되어 있으며, 실제 거래 조건은 거래소·증권사·상품에 따라 다를 수 있습니다. 모든 투자 판단과 그에 따른 책임은 이용자 본인에게 있습니다.