코딩을 모르는 초보자가 AI에게 "유명한 자동매매 전략 5개를 짜 달라"고 하면 받게 되는 그 전략들(RSI 반등·골든크로스·MACD·볼린저밴드·변동성 돌파)을, AI가 준 기본값 그대로 업비트 비트코인 4시간봉 8.4년에 넣고 실측했습니다. 비용은 편도 0.1%, 신호는 봉 종가 확정 후 다음 봉 반영입니다. 아래 코드로 전부 재현됩니다.
v1 = AI가 준 그대로 · v2 = AI의 개선안(종가 > 200일 이동평균선일 때만 매매) 적용
| 전략 (1000만원 시작) | 최종 잔고 | 연복리 | 최대낙폭 | 검증 연복리 (2024~) | 검증 낙폭 | 매매 수 |
|---|---|---|---|---|---|---|
| 그냥 보유 | 1억 3,048만원 | 35.9% | -74.8% | 27.2% | -50.3% | 1 |
| v1 골든크로스 | 5억 7,653만원 | 62.2% | -53.4% | 44.8% | -25.0% | 550 |
| v2 골든크로스+필터 | 3억 414만원 | 50.3% | -30.9% | 32.6% | -24.4% | 306 |
| v1 MACD | 5,023만원 | 21.2% | -56.9% | 15.3% | -37.8% | 671 |
| v2 MACD+필터 | 7,276만원 | 26.7% | -33.1% | 18.0% | -24.3% | 374 |
| v1 RSI 반등 | 55만원 | -29.2% | -95.4% | -15.9% | -50.1% | 194 |
| v1 볼린저 역추세 | 50만원 | -30.0% | -95.3% | -19.7% | -48.4% | 185 |
| v1 변동성 돌파 | 12,000원 | -55.0% | -99.9% | -55.2% | -89.3% | 2,966 |
| 전략 (1000만원 시작) | 최종 잔고 | 연복리 | 최대낙폭 | 검증 연복리 (2024~) | 검증 낙폭 | 매매 수 |
|---|---|---|---|---|---|---|
| 그냥 보유 | 6,189만원 | 24.3% | -89.8% | 3.7% | -65.2% | 1 |
| v1 골든크로스 | 15억 8,101만원 | 83.0% | -54.6% | 39.7% | -52.3% | 538 |
| v2 골든크로스+필터 | 3억 5,048만원 | 52.9% | -40.4% | 35.0% | -23.6% | 305 |
| v1 MACD | 2억 300만원 | 43.2% | -64.8% | 29.0% | -46.7% | 661 |
| v2 MACD+필터 | 2억 6,466만원 | 47.8% | -37.4% | 27.6% | -23.7% | 355 |
| v1 RSI 반등 | 31만원 | -33.9% | -97.8% | -24.7% | -63.5% | 209 |
| v1 볼린저 역추세 | 41만원 | -31.7% | -97.1% | -23.4% | -63.8% | 204 |
| v1 변동성 돌파 | 22,000원 | -51.8% | -99.8% | -49.0% | -84.4% | 2,932 |
대본을 쓰기 전에 결론을 공격하는 것이 이 채널의 규칙입니다.
업비트 공개 API로 데이터를 받아 위 표를 그대로 다시 계산합니다. 기준일이 2026.8.31로 고정되어 있어 영상·페이지와 같은 숫자가 나옵니다.
# -*- coding: utf-8 -*-
"""AI가 짜 준 자동매매 봇 5개 — 업비트 4시간봉 백테스트 재현 코드
영상: 「같은 AI가 짜준 자동매매 봇인데 5.8억과 1.2만원」 (AlgoLab, 2026-09)
필요한 것: Python 3.10+ · requests · numpy (pip install requests numpy)
실행: python ai_bot_backtest.py
업비트 공개 API에서 4시간봉을 내려받아(약 2분) 표를 출력합니다.
ASOF: 데이터를 2026-08-31 까지로 자릅니다 — 영상과 같은 숫자가 나오게 하기 위한
기준일입니다. 최신까지 보려면 ASOF = None 으로 바꾸세요(숫자는 영상과 달라집니다).
업비트 캔들 페이지 경계 때문에 '그냥 보유' 등 일부 값이 1% 미만으로 다를 수 있습니다.
규칙(영상과 동일):
· 전략 5개는 AI(클로드)가 초보자 프롬프트에 준 것 그대로, 기본 파라미터 그대로.
· 신호는 봉 종가에서 확정 → 다음 봉부터 반영(미래참조 방지). 숏 없음, 현금 무수익.
· 비용: 포지션이 바뀔 때마다 변화분 × 편도 0.1%.
· v2 = 같은 신호 × 추세 필터(종가 > 1200봉 이동평균 ≈ 200일선) — AI가 준 개선안.
한계: 과거 데이터 백테스트라 미래를 보장하지 않습니다. 슬리피지·세금 미반영.
"""
import datetime
import time
import numpy as np
import requests
ASOF = datetime.date(2026, 8, 31) # 영상 기준일. None 이면 오늘까지
COST = 0.001 # 편도 0.1%
FILTER_N = 1200 # 4시간봉 1200개 = 200일
START = 1000 # 만원
# ── 업비트 4시간봉 내려받기 (공개 API, 키 불필요) ───────────────────────────
def fetch_upbit_4h(market):
url = "https://api.upbit.com/v1/candles/minutes/240"
to = None
rows = []
while True:
params = {"market": market, "count": 200}
if to:
params["to"] = to
r = requests.get(url, params=params, timeout=10)
r.raise_for_status()
batch = r.json()
if not batch:
break
rows += batch
to = batch[-1]["candle_date_time_utc"] + "Z"
time.sleep(0.11) # 공개 API 예의
if len(batch) < 200:
break
rows.sort(key=lambda x: x["candle_date_time_kst"])
o = np.array([x["opening_price"] for x in rows], float)
h = np.array([x["high_price"] for x in rows], float)
l = np.array([x["low_price"] for x in rows], float)
c = np.array([x["trade_price"] for x in rows], float)
d = [x["candle_date_time_kst"][:10] for x in rows]
if ASOF: # 캐시 유무와 무관하게 항상 같은 기준으로 자른다
keep = [i for i, x in enumerate(d) if x <= ASOF.isoformat()]
o, h, l, c = o[keep], h[keep], l[keep], c[keep]
d = [d[i] for i in keep]
return d, o, h, l, c
# ── 지표 ────────────────────────────────────────────────────────────────────
def sma(x, n):
out = np.full(len(x), np.nan)
if len(x) >= n:
cs = np.cumsum(np.insert(x, 0, 0.0))
out[n - 1:] = (cs[n:] - cs[:-n]) / n
return out
def ema(x, n):
a = 2.0 / (n + 1)
out = np.empty(len(x))
out[0] = x[0]
for i in range(1, len(x)):
out[i] = a * x[i] + (1 - a) * out[i - 1]
return out
def rstd(x, n):
out = np.full(len(x), np.nan)
if len(x) >= n:
from numpy.lib.stride_tricks import sliding_window_view as swv
out[n - 1:] = swv(x, n).std(axis=1)
return out
def ffill_signal(buy, sell, warm, n):
s = np.full(n, np.nan)
s[sell] = 0.0
s[buy] = 1.0
s[:warm] = 0.0
idx = np.arange(n)
has = ~np.isnan(s)
last = np.maximum.accumulate(np.where(has, idx, 0))
out = s[last]
out[np.isnan(out)] = 0.0
return out
# ── AI가 준 전략 5개 (기본값 그대로) ────────────────────────────────────────
def ai_rsi(o, h, l, c, n=14, lo=30, hi=70):
d = np.diff(c, prepend=c[0])
up = np.where(d > 0, d, 0.0)
dn = np.where(d < 0, -d, 0.0)
au, ad = ema(up, n), ema(dn, n)
rsi = 100 - 100 / (1 + au / np.where(ad == 0, 1e-9, ad))
return ffill_signal(rsi < lo, rsi > hi, n + 1, len(c))
def ai_goldencross(o, h, l, c, fast=5, slow=20):
f, s = sma(c, fast), sma(c, slow)
out = np.zeros(len(c))
m = ~(np.isnan(f) | np.isnan(s))
out[m] = (f[m] > s[m]).astype(float)
return out
def ai_macd(o, h, l, c, fast=12, slow=26, sig=9):
m = ema(c, fast) - ema(c, slow)
return (m > ema(m, sig)).astype(float)
def ai_bollinger(o, h, l, c, n=20, k=2.0):
ma, sd = sma(c, n), rstd(c, n)
buy = (c < ma - k * sd) & ~np.isnan(sd)
sell = (c > ma + k * sd) & ~np.isnan(sd)
return ffill_signal(buy, sell, n + 1, len(c))
def ai_volbreak(o, h, l, c, k=0.5):
rng = np.r_[np.nan, h[:-1] - l[:-1]]
s = np.zeros(len(c))
m = ~np.isnan(rng)
s[m] = (c[m] > o[m] + k * rng[m]).astype(float)
return s
STRATS = {"RSI 반등": ai_rsi, "골든크로스": ai_goldencross, "MACD": ai_macd,
"볼린저 역추세": ai_bollinger, "변동성 돌파": ai_volbreak}
# ── 평가 ────────────────────────────────────────────────────────────────────
def evaluate(sig, ret, d, ppy, warm):
pos = np.r_[0.0, sig[:-1]]
g = (1 + pos * ret) * (1 - COST * np.abs(np.diff(np.r_[0.0, pos])))
gg = g[warm:]
eq = np.cumprod(gg)
yrs = len(gg) / ppy
cagr = eq[-1] ** (1 / yrs) - 1 if eq[-1] > 0 else -1.0
mdd = float((eq / np.maximum.accumulate(eq) - 1).min())
return eq[-1], cagr, mdd
def main():
for market, label in [("KRW-BTC", "비트코인"), ("KRW-ETH", "이더리움")]:
print(f"\n{label} ({market}) 4시간봉 내려받는 중...")
d, o, h, l, c = fetch_upbit_4h(market)
n = len(c)
ret = np.zeros(n)
ret[1:] = c[1:] / c[:-1] - 1
yrs_all = (datetime.date.fromisoformat(d[-1]) -
datetime.date.fromisoformat(d[0])).days / 365.25
ppy = n / yrs_all
warm = FILTER_N + 1
filt = np.zeros(n)
ma = sma(c, FILTER_N)
m = ~np.isnan(ma)
filt[m] = (c[m] > ma[m]).astype(float)
print(f" {n:,}봉 · {d[warm]} ~ {d[-1]}")
print(f" {'전략':<14}{'최종(만원)':>12}{'연복리':>9}{'최대낙폭':>9}")
mult, cagr, mdd = evaluate(np.ones(n), ret, d, ppy, warm)
print(f" {'그냥 보유':<14}{START*mult:>12,.0f}{cagr:>9.1%}{mdd:>9.1%}")
for name, fn in STRATS.items():
sig = fn(o, h, l, c)
for tag, s in [("v1 " + name, sig), ("v2 " + name + "+필터", sig * filt)]:
mult, cagr, mdd = evaluate(s, ret, d, ppy, warm)
print(f" {tag:<14}{START*mult:>12,.1f}{cagr:>9.1%}{mdd:>9.1%}")
if __name__ == "__main__":
main()
AI는 봇을 잘 짜 줍니다. 문제는 어느 봇에 돈을 맡겨도 되는지는 짜 주지 않는다는 것입니다. 알고랩은 이런 검증을 포함해 전략 구현부터 자동매매 운영까지 대신해 드립니다.
본 페이지의 모든 수치는 과거 데이터 기반 시뮬레이션이며 투자 권유가 아닙니다. 특정 종목·전략의 미래 수익을 보장하지 않습니다. 투자 판단과 책임은 투자자 본인에게 있습니다.