# A축 4편 실행 기록 — freqtrade/freqtrade

실행일 **2026-09-21** (KST 21:03~21:29 / UTC 12:03~12:29).
환경: macOS 26.6.2 · arm64 · uv 0.10.12 · CPython 3.12.13 · freqtrade 2026.8 · CCXT 4.5.81.

> **계정·키 없음.** 거래소 계정을 만들지 않았고 API 키를 발급받지 않았다.
> `config.json` 의 `exchange.key` · `exchange.secret` 은 빈 문자열이다.
> 거래소에는 **공개 시세 조회만** 했다 (`download-data`, `test-pairlist`, 바이낸스 공개 klines/ticker).
> **실주문 0건.** 모든 주문은 freqtrade 자체 Dry-Run 이고 주문 번호가 `dry_run_buy_…` / `dry_run_sell_…` 로 시작한다.
> 전용 venv: `/private/tmp/tut-venvs/freqtrade`. 작업 디렉터리는 세션 스크래치패드(`…/scratchpad/ft`).
>
> **건드리지 않은 것**: 엔진 pid 11535 · 웹 pid 9827 (B축 1회차 슬롯 4개). 실행 전후로 생존 확인했다.
> 죽인 프로세스는 내가 띄운 freqtrade 봇뿐이다 (pid 33408 → `kill -9`, S8 용).
> freqtrade 의 API 서버는 **8080** 에 띄웠다 — 8741 은 weltiq 웹(9827)이 쓰고 있어서다.

## 파일

| 파일 | 내용 |
|---|---|
| `install.log` | 설치 실측 — 콜드 캐시 **5.01초 / 83패키지** |
| `config.json` | freqtrade 설정 (비밀번호·jwt 마스킹) |
| `pairlist-overlay.json` | S4 용 동적 페어리스트 오버레이 |
| `tight-stop.json` | S6·S7 용 손절폭 오버레이 (`-0.0005`) |
| `manual_strategy.py` | 자동 진입 신호 없는 전략 (S5~S8 용) |
| `cheating_strategy.py` | **일부러 미래를 참조하는 전략** (시나리오 D 용) |
| `dryrun-phase1.log` | 봇 기동 · S5 ETH 왕복 · SOL 진입 |
| `dryrun-phase1-restart.log` | **S8** — `kill -9` 뒤 재기동, `Found open trade: Trade(id=2, …)` |
| `dryrun-phase2.log` | 손절 −0.2% — 10분간 미발동 (S6 의 "고치기 전") |
| `dryrun-phase3.log` | 손절 −0.05% — **91초 만에 `Reason: stop_loss`** |
| `weltiq-btc-1d.json` | weltiq `/v1/bars` BTCUSDT 1d 30봉 (5절 대조 원자료) |
| `freqtrade-mcp-README.md` · `freqtrade-mcp-main.py` | kukapay/freqtrade-mcp 원본 (2026-09-21 받음) |
| `freqtrade-mcp-probe.py` | MCP stdio 로 툴 17개 전수 호출한 점검 스크립트 |
| `github-meta.json` | 두 저장소 메타 (2026-09-21 관측) |
| `readme-head.txt` | freqtrade README 앞 40줄 — Dry-Run 문장 원문 |

## 핵심 실측값

| | 값 |
|---|---|
| 콜드 설치 | **5.01초**, 83패키지 (Resolved 1.44s / Prepared 2.63s / Installed 140ms) |
| 캐시 설치 | 0.04초 |
| 봉 내려받기 | **26.70초** — 3종목 × (1d 90봉 + 5m 26,065봉) = 78,465봉, **키 없음** |
| 백테스트 (SampleStrategy, 5m, 90일) | **4.12초**, 66건, 수익률 4.07%, `Market change` **56.16%** |
| `test-pairlist` 5단계 | 9.04초 — **493 → 100 → 97 → 88 → 86 → 78** |
| `lookahead-analysis` | SampleStrategy **5.28초 / has_bias=No** · CheatingStrategy **3.10초 / has_bias=Yes** |
| 치팅 전략 백테스트 | 51일 **44.11%**, 승률 92.8%, 최대 낙폭 **0.06%** (미래 참조로 만든 가짜 결과) |
| 지원 거래소 | 78 (`-a` 포함 104) — 전부 크립토 |
| `freqtrade-client` 메서드 | 43 |

## Dry-Run 기조 — 코드 근거

| 근거 | 위치 |
|---|---|
| `MINIMAL_CONFIG` 에 `"dry_run": True` | `freqtrade/constants.py:196` |
| 키가 없으면 기본이 DRY_RUN — `config.get("dry_run", True)` | `freqtrade/configuration/configuration.py:472` |
| `new-config` 첫 질문이 Dry-Run, 기본 예 | `freqtrade/configuration/deploy_config.py:54` |
| **Dry-Run 이면 API 키를 묻지 않음** — `when: lambda x: not x["dry_run"]` | `freqtrade/configuration/deploy_config.py:142` |
| 유틸 명령은 Dry-Run 강제 | `freqtrade/configuration/config_setup.py:27` |
| DB 파일명이 갈림 | `tradesv3.dryrun.sqlite` vs `tradesv3.sqlite` |
| 응답이 모의라고 밝힘 | `balance.note = "Simulated balances"` · `show_config.runmode = "dry_run"` |
| README 원문 | "Always start by running a trading bot in Dry-Run and do not engage money…" (`Disclaimer` 절) |

## Dry-Run 타임라인 (KST)

| 시각 | 일 |
|---|---|
| 21:08:31 | 봇 기동 (pid 33408) — `Dry run is enabled. All trades are simulated.` |
| 21:09:15 | **S5** ETH/USDT 진입 0.0366 @ 2728.80 · `dry_run_buy_ETH/USDT_9e909c38…` |
| 21:09:26 | SOL/USDT 진입 0.857 @ 116.63 (S8 용으로 열어 둠) |
| 21:09:49 | **S5** ETH 강제 청산 @ 2725.35 → **−0.32589189 USDT** (수수료 0.0999 + 0.0997) |
| 21:10:0x | **S8** `kill -9 33408` — API 응답 없음 확인 |
| 21:10:26 | **S8** 재기동 → `Found open trade: Trade(id=2, pair=SOL/USDT, amount=0.857, open_rate=116.63…)` |
| 21:11:56 | SOL 강제 청산 @ 116.69 → −0.14853524 USDT |
| 21:12:27 | **S6** 손절 −0.2% 로 BTC 진입 @ 85037.82 (stop 84867.75) |
| 21:12~21:22 | 10분 관찰 — 최저 표시 손익 −0.24%, **미발동** (표시 손익에 왕복 수수료 0.2% 포함이라 −0.4% 근처여야 걸림) |
| 21:23:50 | **S6** 규칙 수정 후 재주문 — 손절 −0.05%, BTC 진입 @ 85198.18 (stop 85155.59) |
| 21:25:21 | **S7** `Exit for BTC/USDT detected. Reason: stop_loss` → 체결 **85145.81** (손절선보다 9.78 아래, 약 1.1bp) |
| 21:29 | 봇 종료. 총 4건 전부 청산, `profit_closed_coin = -0.76038468 USDT` |

## 시나리오 D — lookahead-analysis

| | SampleStrategy | CheatingStrategy |
|---|---|---|
| `has_bias` | **No** | **Yes** |
| `total_signals` | 20 | 20 |
| `biased_entry_signals` | 0 | **20** |
| `biased_exit_signals` | 0 | **20** |
| `biased_indicators` | (없음) | **`future_close`, `enter_long`, `exit_long`** |

`cheating_strategy.py` 는 `dataframe["close"].shift(-12)` 로 12봉(=1시간) 뒤 종가를 끌어온다.
검사기가 **내가 심어 둔 컬럼 이름(`future_close`)을 그대로 지목**했다.

## 5절 대조 — weltiq `/v1/bars` vs freqtrade 다운로드

```
BTC/USDT 1d, 겹치는 30봉 중 OHLC 네 값 모두 일치 30 · 다름 0
```

- 라벨 규약이 다르다: freqtrade = **봉 시작 시각 UTC**, weltiq = **봉 마감 시각**(`db/schema.sql:25` — "거래소 이벤트 시각 (봉 마감)"). 하루 밀어 맞춘 뒤 30/30.
- 둘 다 **미완성 당일 봉을 뺀다.**
- weltiq `/v1/bars` 는 `hours` 파라미터 상한 **720시간(30일)** — `weltiq/api/server.py:749`. 90일치 백테스트 자료용으로는 맞지 않는다.

## kukapay/freqtrade-mcp 점검

| | |
|---|---|
| 별 / 라이선스 | ★145 / MIT (2026-09-21 관측) |
| 마지막 푸시 | 2025-12-06 — **README 수정만** |
| **코드 마지막 변경** | **2025-04-05** |
| 커밋 총수 | 4 |

**결함 셋 (전부 실측):**

1. README 대로 설치 → `mcp` 2.2.0 이 깔림 → `ModuleNotFoundError: No module named 'mcp.server.fastmcp'`. (`pyproject.toml` 이 `mcp[cli]>=1.5.0` 으로 상한이 없음)
2. `mcp<2`(1.30.0)로 내려도 `AttributeError: 'FastMCP' object has no attribute 'info'` (`__main__.py:23`, `:28`). **하한인 mcp 1.5.0 에서도 `.info` 가 없음을 직접 확인** — 공개 상태로는 애초에 기동 불가.
3. `server.info(...)` 두 줄을 지우고 기동 → **툴 17개 중 동작 11 · 실패 6.**

| 실패한 툴 | 원인 (`freqtrade-client` 2026.8 기준) |
|---|---|
| `fetch_config` | `config` → 지금은 `show_config` |
| `place_trade` | `forcebuy()` 가 `stake_amount` 를 안 받음 · `forcesell` 은 삭제됨(`forceexit`) |
| `start_bot` / `stop_bot` | `start` / `stop` 으로 바뀜 |
| `add_blacklist` | `blacklist(*args)` 로 바뀜 |
| `delete_blacklist` | 해당 메서드 없음 |

부수 관찰: 툴들이 `ctx.info(...)` 를 `await` 없이 부른다 → `RuntimeWarning: coroutine 'Context.info' was never awaited`.

## 돌리지 않은 것

| | 왜 |
|---|---|
| S2 한국 종목 | **이 도구로는 안 된다** — 크립토 전용, 지원 거래소 78개 전부 암호화폐 |
| S3 내 실행 기록 복기 | 해당 없음 — 이 편에서 weltiq 슬롯을 쓰지 않았다 |
| S9 수수료 바꿔 재계산 | **미확인** — `fee` 설정으로 될 것으로 보이나 실제로 안 돌렸다 |
| S10 나란히 비교 | 이 편의 범위 밖 (별도 트랙) |
| hyperopt | **미확인** — 시간이 오래 걸려 이번에 안 돌렸다 |
| FreqUI 화면 | HTTP 200 · `<title>FreqUI</title>` 까지만 확인. 화면 조작은 안 했다 |

---

## 캡처 보강 세션 — 2026-09-22 (KST 02:25~02:38)

3.3절(화면 캡처·대시보드 설명) 개정에 맞춰 **같은 venv·같은 `config.json`·같은 sqlite** 로 다시 띄워 화면만 찍었다.
본문 숫자는 손대지 않았고, 새로 얻은 값은 캡처 안에만 있다.

> **건드리지 않은 것**: 엔진 pid 55526 · 웹 pid 48127 · 67039 · 71349 · 감시 pid 50367 · 3678, 그리고 돌고 있던 슬롯 12개.
> 실행 전후로 생존 확인했다. 죽인 프로세스는 내가 띄운 freqtrade 봇뿐이다 (pid 77536).
> **FreqUI 포트는 9160** — 기본 8080 을 피했다. 거래소 키 없음, 실주문 0건, 텔레그램 봇 미생성.

| 한 일 | 결과 |
|---|---|
| `freqtrade install-ui` | `UI already up-to-date, FreqUI Version 3.1.2` |
| `freqtrade trade -c config.json -c ui-port.json` | pid 77536 · `Starting HTTP Server at 127.0.0.1:9160` |
| FreqUI 로그인 | `config.json` 의 `api_server.username/password` 로 접속 |
| Dashboard 확인 | 어제 4건이 그대로 — `Closed Profit −0.08% (−0.76)` · `Balance 989.247 USDT(dry)` · `W/L 0 / 4` |
| `POST /api/v1/forceenter` BTC/USDT | trade_id **5** · 0.00116 @ **85868.2** · stake 99.607 · `dry_run_buy_BTC/USDT_bf761c30…` |
| `POST /api/v1/forceexit` trade 5 | `force_exit` · `profit_ratio −0.00160888` → 누적 `profit_closed_coin −0.92080094` |
| `compare_bars.py` (신규) | 저장해 둔 `weltiq-btc-1d.json` 과 `BTC_USDT-1d.feather` 재대조 — **30/30 일치, 다름 0** (본문 값 재현) |

캡처 8장은 `shots/` 에 있다. 도구 화면 7장(freqtrade 터미널 2 · FreqUI 5) · 우리 쪽 1장.

| 파일 | 무엇 |
|---|---|
| `01-install.png` | `freqtrade --version` + `install-ui` |
| `02-terminal-dryrun.png` | 봇 기동 로그 — `Dry run is enabled` · `HTTP Server at 127.0.0.1:9160` |
| `03-frequi-login.png` | FreqUI 로그인 창 (비밀번호는 마스킹된 상태) |
| `04-frequi-first-open.png` | 처음 연 화면 — `No bot selected` |
| `05-frequi-dashboard.png` | Dashboard — `Dry` 뱃지 · 누적 손익 · Cumulative Profit |
| `06-frequi-trade-chart.png` | Trade — BTC/USDT 1분봉 + 종목 목록의 실시간 손익 |
| `07-frequi-open-trade.png` | Open Trades — 모의 포지션 한 건 (`−0.18%`, 수수료 때문) |
| `08-weltiq-bars-compare.png` | `compare_bars.py` 출력 — 30/30 일치 |

---

## 독자판(v2) 재편 시 본문에서 내린 원문 — 2026-09-24

2026-09-24 에 `docs/tutorials/a/04-freqtrade.md` 를 독자판(≤3,000어절)으로 재편하면서
본문에서 뺀 서술·출력을 **여기에 그대로 옮겨 둔다.** 숫자는 손대지 않았다.
(위쪽 기록은 2026-09-21/22 실행 당시의 것이고, 아래는 그때 본문에만 있던 것을 보존한 것이다.)

### 설치 — 작업 디렉터리와 설정 확인

```bash
freqtrade create-userdir --userdir ./user_data
```

생기는 것: `backtest_results` · `data` · `freqaimodels` · `hyperopt_results` · `hyperopts` ·
`logs` · `notebooks` · `plot` · `strategies`. `strategies/sample_strategy.py` 가 예제로 하나 들어온다.

`freqtrade new-config` 마법사는 **대화형이라 스크립트에서 못 쓴다.** `config.json` 을 손으로 썼다.
`freqtrade show-config -c config.json` 은 **비밀번호를 `REDACTED` 로 가려서** 출력한다.

### 봉 내려받기 — 원문 로그

```
INFO - Instance is running with dry_run enabled
INFO - Using CCXT 4.5.81
INFO - About to download pairs: ['BTC/USDT', 'ETH/USDT', 'SOL/USDT'], intervals: ['1d', '5m']
INFO - Downloaded data for BTC/USDT, 1d, spot with length 90.
INFO - Downloaded data for BTC/USDT, 5m, spot with length 26065.
INFO - Downloaded data for ETH/USDT, 1d, spot with length 90.
INFO - Downloaded data for SOL/USDT, 5m, spot with length 26065.
real 26.70
```

```
      Found 6 pair / timeframe combinations.
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃             Pair ┃         Timeframe ┃    Type ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│         BTC/USDT │            5m, 1d │    spot │
│         ETH/USDT │            5m, 1d │    spot │
│         SOL/USDT │            5m, 1d │    spot │
└──────────────────┴───────────────────┴─────────┘
```

명령:

```bash
freqtrade download-data -c config.json --userdir ./user_data --timeframes 1d 5m --days 90
freqtrade list-data -c config.json --userdir ./user_data
```

### 백테스트 — 요약 표 전체 (SampleStrategy)

```
│ Backtesting from                │ 2026-06-23 16:40:00 │
│ Backtesting to                  │ 2026-09-21 12:00:00 │
│ Total/Daily Avg Trades          │ 66 / 0.74           │
│ Starting balance                │ 1000 USDT           │
│ Final balance                   │ 1040.739 USDT       │
│ Total profit %                  │ 4.07%               │
│ Market change                   │ 56.16%              │
│ Win%                            │ 93.9%               │
│ Absolute drawdown               │ 10.184 USDT (0.97%) │
real 4.12
```

백테스트에서 막히는 자리(본문 표에서 뺀 것 포함):

| 증상 | 원인 | 해결 |
|---|---|---|
| `No data found. Terminating.` | 봉을 안 받았거나 `--timerange` 가 데이터 밖 | `list-data` 로 먼저 확인 |
| 타임프레임 불일치 | 전략의 `timeframe` 과 받아 둔 봉이 다름 | `--timeframe` 으로 덮어쓰거나 맞는 봉을 받기 |
| `data ends at …` 경고 | 마지막 봉이 안 끝나서 빠짐 | **정상.** 미완성 봉을 빼는 게 맞다 |
| 결과가 매번 조금씩 다름 | 받아 둔 봉의 끝이 계속 밀림 | `--timerange` 로 구간을 못 박는다 |

### FreqUI — 본문에서 줄인 네 가지

1. **띄우는 명령**: 별도 명령 없음. `freqtrade trade -c config.json --userdir ./user_data` 로 봇을 띄우면
   `api_server.enabled: true` 일 때 화면도 같이 뜬다. 매매 없이 화면만 보려면 `freqtrade webserver`.
2. **주소**: 기본 `http://127.0.0.1:8080`. `api_server.listen_port` 로 변경(이번엔 9160).
   `listen_ip_address` 를 `0.0.0.0` 으로 바꾸면 기동 로그에
   `SECURITY WARNING - Local Rest Server listening to external connections` 가 뜬다. 밖으로 열지 말 것.
3. **로그인**: 계정 생성이 아니라 `api_server.username`/`password`. 비우면
   `SECURITY WARNING - No password for local REST Server defined.` 가 뜬다. `jwt_secret_key` 기본값도 경고.
   `new-config` 마법사는 jwt 를 매번 난수로 채운다. 기본 아이디는 `freqtrader`.
   FreqUI 로그인 창의 네 칸 = API Url · Username · Password · Bot Name(별명, 봇 여러 개 등록용).
4. **처음 열면**: 로봇 그림과 "Welcome to the FreqtradeUI" 만. 위쪽에 `No bot selected`.
   오른쪽 위 Login 을 먼저 눌러야 채워진다. **봇이 안 떠 있어도 이 화면까지는 나온다.**

### 텔레그램 — 문서·코드 확인만, 실측 아님 (미확인)

**이번에 텔레그램 봇을 실제로 만들지 않았다.** 아래는 설정과 코드로 확인한 내용이다.

- 띄우는 명령 없음 — `telegram.enabled: true` 면 봇 기동 시 같이 붙는다.
- 주소 대신 `telegram.token` · `telegram.chat_id` 두 개(토큰은 @BotFather).
- 인증은 채팅 ID 가 곧 인증. 등록 안 된 곳의 메시지는 `Rejected unauthorized message from: …` 로 버려진다.
- 채팅 ID 를 모르면 `chat_id` 에 아무 값을 넣고 봇을 띄운 뒤 그 방에서 **`/tg_info`**.
  이 명령 하나만 인증을 안 거치게 돼 있고, 복사해 붙일 수 있는 JSON 조각으로 답한다.
- 기동 메시지 뒤 `/status` `/profit` `/balance` `/forceexit` `/stop` 등이 듣는다.

### 종목 좁히기 — 필터 단계 라벨 (9.04초)

```
전체 USDT 스팟 시장: 493
필터 1단계 (거래대금 상위 100)        → 100
필터 2단계 (+ 상장 30일 이상)         →  97
필터 3단계 (+ 가격 0.01 이상)         →  88
필터 4단계 (+ 스프레드 0.2% 이내)     →  86
필터 5단계 (+ 10일 변동폭 3% 이상)    →  78
```

```bash
freqtrade test-pairlist -c config.json -c pairlist-overlay.json --userdir ./user_data --quote USDT
```

종목명은 기사에 싣지 않았다(추천으로 읽힐 수 있어서). 이 필터는 **호가·거래대금·변동성 같은
시장 구조 지표만** 본다 — 재무·뉴스·펀더멘털 필터는 없다. 크립토 전용이라 주식에는 못 쓴다.

### 모의 주문 한 번 왕복 — 원문

```
봇 기동 21:08:31 — "Dry run is enabled. All trades are simulated."

진입 21:09:15  ETH/USDT  0.0366 @ 2728.80  stake 99.874 USDT
  order_id = dry_run_buy_ETH/USDT_9e909c38-3468-465a-bb69-40ede45b0abc
청산 21:09:49  ETH/USDT  0.0366 @ 2725.35  (force_exit)
  order_id = dry_run_sell_ETH/USDT_e4513410-11c9-4e41-8e7c-7d3e0d2a404f

fee_open_cost  0.09987408 USDT
fee_close_cost 0.09974781 USDT
profit_abs     -0.32589189 USDT  (-0.33%)
```

```bash
freqtrade trade -c config.json --userdir ./user_data &

curl -s --user 'tony:…' -H 'Content-Type: application/json' \
  -X POST -d '{"pair":"ETH/USDT","side":"long"}' \
  http://127.0.0.1:8080/api/v1/forceenter

curl -s --user 'tony:…' -H 'Content-Type: application/json' \
  -X POST -d '{"tradeid":"1"}' \
  http://127.0.0.1:8080/api/v1/forceexit
```

`force_entry_enable: true` 가 설정에 있어야 `forceenter` 가 열린다.

본문에서 뺀 세 관찰:
1. 주문 번호가 `dry_run_buy_` 로 시작 — 기록만 봐도 모의임을 알 수 있다.
2. 수수료가 제대로 물린다. 진입 0.0999 + 청산 0.0997 ≈ 0.2 USDT. 같은 값에 사서 들고 있으면 손익이
   정확히 −0.2%. **가격이 안 움직여도 마이너스인 상태**를 보여 준다.
3. API 응답이 자기가 모의라고 밝힌다 — `{"note": "Simulated balances", "runmode": "dry_run", "dry_run": true}`.
   `GET /api/v1/balance` 와 `GET /api/v1/show_config`. 외부 도구가 프로그램으로 확인할 수 있는 신호다.

안 되는 것: 호가 잔량을 층층이 소진하는 체결이 아니다. Dry-Run 은 호가창 맨 위 한 호가로 채운다.
큰 주문의 슬리피지는 이걸로 못 잰다(freqtrade 문서도 이 한계를 적어 두고 있다).

### 손절 미발동 10분 관찰 (15초 간격 40회 중 일부)

```
[ 9] BTC/USDT 85013.47  손익 -0.23%  stop= 84867.75
[12] BTC/USDT 85000.00  손익 -0.24%  stop= 84867.75
[21] BTC/USDT 85120.13  손익 -0.10%  stop= 84867.75
[35] BTC/USDT 85312.47  손익 +0.12%  stop= 84867.75
[40] BTC/USDT 85063.27  손익 -0.17%  stop= 84867.75
→ 10분 동안 손절 안 걸림
```

규칙이 잘못된 게 아니라 **계산이 잘못돼 있었다.** 진입가 85,037.82 에 −0.2% 면 손절선 84,867.75.
그런데 화면 손익률(−0.24%)에는 **왕복 수수료 0.2% 가 이미 들어가 있다.**
손절이 걸리려면 표시 손익이 −0.4% 근처까지 가야 하는데 10분 동안 제일 나빴던 게 −0.24% 였다.

고쳐서 재주문:

```bash
echo '{"stoploss": -0.0005}' > tight-stop.json
freqtrade trade -c config.json -c tight-stop.json --userdir ./user_data
```

```
INFO - Override strategy 'stoploss' with value from the configuration: -0.0005
21:23:50  BTC/USDT 진입 @ 85198.18  stop = 85155.59 (-0.05%)
21:25:21  Exit for BTC/USDT detected. Reason: stop_loss
```

안 되는 것: **손절폭을 바꾸려면 봇을 재시작해야 한다.** `reload_config` 는 설정 파일을 다시 읽지만
이미 열린 포지션의 손절선은 그대로다. 돌고 있는 포지션의 규칙을 무중단으로 갈아 끼우는 통로는 없다.

### 발동한 손절의 거래 레코드 전체

```
trade_id            4
pair                BTC/USDT
open_rate           85198.18       (21:23:50)
stop_loss_abs       85155.59       (-0.05%)
close_rate          85145.81       (21:25:21)
min_rate            85145.81
max_rate            85268.46
exit_reason         stop_loss
profit_abs          -0.26057537 USDT  (-0.26%)
```

안 되는 것: 이 손절은 **봇 프로세스가 살아 있어야** 동작한다. `stoploss_on_exchange` 를 켜면 거래소에
손절 주문을 올려 둘 수 있지만 그건 실거래에서만 의미가 있고 Dry-Run 에서는 결국 봇이 지킨다.

### 끊고 다시 붙이기 — 원문

```
죽이기 전  trade_id 2  SOL/USDT  0.857 @ 116.63  손익 -0.268 USDT
$ kill -9 33408
프로세스 종료됨 / API 응답 없음 (봇이 죽었습니다)

$ freqtrade trade -c config.json --userdir ./user_data   # 재기동
21:10:26 INFO - Found open trade: Trade(id=2, pair=SOL/USDT, amount=0.857,
                is_short=False, leverage=1, open_rate=116.63, open_since=2026-09-21…)

재시작 후  trade_id 2  SOL/USDT  0.857 @ 116.63  손익 -0.303 USDT  stop 104.97
```

상태가 메모리가 아니라 sqlite 에 있기 때문에 포지션·수량·진입가·손절선이 그대로 돌아왔다.
안 되는 것: **죽어 있는 동안은 아무 규칙도 안 돈다.** 위 실험에서 죽어 있던 20초 사이에 SOL 이
손절선을 뚫었다면 그냥 지나갔을 것이다. 복구는 "그동안의 일을 되돌린다"가 아니라
**"그 자리에서 다시 시작한다"** 다.

### 미래 참조 검사 — 치팅 전략 백테스트 전체

```
│ Total/Daily Avg Trades  │ 670 / 13.14         │
│ Starting balance        │ 1000 USDT           │
│ Final balance           │ 1441.085 USDT       │
│ Total profit %          │ 44.11%              │
│ Win%                    │ 92.8%               │
│ Drawdown                │ 0.697 USDT (0.06%)  │
```

정상 전략 검사 결과 (본문에서 표를 뺀 것):

```
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓
┃           filename ┃       strategy ┃ has_bias ┃ total_signals ┃ biased_entry_signals ┃ biased_indicators ┃
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩
│ sample_strategy.py │ SampleStrategy │       No │            20 │                    0 │                   │
└────────────────────┴────────────────┴──────────┴───────────────┴──────────────────────┴───────────────────┘
```

```bash
freqtrade lookahead-analysis -c config.json --userdir ./user_data \
  --strategy SampleStrategy --timerange 20260801- --timeframe 5m
```

`entry_pricing.price_side` 가 `same` 이면 `Market entry orders require entry_pricing.price_side = "other"`
로 거부된다. 이 명령은 시장가 주문을 전제하기 때문이다.

형제 명령 `recursive-analysis` — 지표가 **앞 봉을 몇 개나 필요로 하는지**를 재서
`startup_candle_count` 를 너무 짧게 잡아 지표 값이 매번 달라지는 문제를 잡아 준다. **이번에 안 돌렸다(미확인).**

안 되는 것: 이 검사는 **미래 참조만** 본다. 과최적화·생존 편향·유동성 무시는 못 잡는다.
그리고 `has_bias: No` 가 "이 전략이 좋다"는 뜻은 전혀 아니다 — 예제 전략은 `No` 를 받고도 시장에 52%p 뒤졌다.

### weltiq 쪽 — 본문에서 줄인 것

- weltiq `/v1/bars` 의 `hours` 파라미터 상한은 **720시간(30일)** 이고 코드 주석에
  "플랫폼 내 소비 전제 — 대량 추출은 요율 제한"이라고 적혀 있다. 90일치 백테스트 자료용으로는 안 맞는다.
- weltiq 의 `GET /v1/capabilities` 에는 `us-equity` · `krx` · `binance-spot` · `binance-usdm` ·
  `bybit-*` 가 같이 들어 있다. freqtrade 는 크립토 전용이라 이 목록의 주식 쪽으로 못 간다.
- 네 단계로 보면 freqtrade 는 **② 전략의 시스템화·장착과 ③ 페이퍼 트레이딩을 한 상자에 담은 물건**이다
  (크립토 한정). weltiq 와 겹치는 자리이지 이어지는 자리가 아니다.
  ①(분석)과 ④(거래 분석)는 어느 쪽을 쓰든 열려 있다.

### MCP 래퍼 — 툴 17개 전수 호출 원문

```
ok    fetch_market_data  {'strategy': 'ManualStrategy', 'pair': 'BTC/USDT', …}
ok    fetch_bot_status   []
ok    fetch_profit       {'profit_closed_coin': -0.76038468, …}
ok    fetch_balance      {'currencies': [{'currency': 'USDT', …}], 'note': 'Simulated balances', …}
ok    fetch_performance  [{'profit_ratio': -0.001486067049644174, …}]
ok    fetch_whitelist    {'whitelist': ['BTC/USDT', 'ETH/USDT', 'SOL/USDT'], 'length': 3, …}
ok    fetch_blacklist    {'blacklist': [], …}
ok    fetch_trades       {'trades': [{'trade_id': 1, 'pair': 'ETH/USDT', …}]}
FAIL  fetch_config       Error: 'FtRestClient' object has no attribute 'config'
ok    fetch_locks        {'lock_count': 0, 'locks': []}
FAIL  place_trade        Error: FtRestClient.forcebuy() got an unexpected keyword argument 'stake_amount'
FAIL  start_bot          Error: 'FtRestClient' object has no attribute 'start_bot'
FAIL  stop_bot           Error: 'FtRestClient' object has no attribute 'stop_bot'
ok    reload_config      {'status': 'Reloading config ...'}
FAIL  add_blacklist      Error: 'FtRestClient' object has no attribute 'add_blacklist'
FAIL  delete_blacklist   Error: 'FtRestClient' object has no attribute 'delete_blacklist'
ok    delete_lock        {'lock_count': 0, 'locks': []}

총 17개 중 동작 11 · 실패 6
```

커밋 이력 네 줄:

```
2025-12-06  Update README      ← README 만 고침
2025-04-17  add discord badge
2025-04-05  update Python version requirement
2025-03-24  initial commit
```

기동 실패 원문:

```
ModuleNotFoundError: No module named 'mcp.server.fastmcp'.
This is mcp 2.x, where FastMCP was renamed to MCPServer …
```

```python
# __main__.py:23
if client.ping():
    server.info("Connected to Freqtrade API")
```

```
AttributeError: 'FastMCP' object has no attribute 'info'
```

**읽기는 거의 다 되고 쓰기는 거의 다 안 된다.** 실패 여섯은 전부 freqtrade 클라이언트 쪽 메서드
이름이 바뀌어서다 — `config` → `show_config`, `start_bot`/`stop_bot` → `start`/`stop`,
`add_blacklist` → `blacklist`. `place_trade` 가 쓰는 `forcesell` 은 **아예 없어졌다**(지금은 `forceexit`).
즉 **주문을 내는 툴 하나가 정확히 안 되는 상태**다. 포크하거나 freqtrade 공식 REST 클라이언트
(`freqtrade-client`, 메서드 43개)를 직접 부르는 쪽이 빠르다. 이 편의 주문 시나리오도 전부 그 API 를
`curl` 로 직접 불러서 했다.

freqtrade 의 잘못이 아니다. **본체가 계속 움직이는데 래퍼가 멈춰 있으면 생기는 일**이고,
서드파티 MCP 래퍼 전반에서 흔한 모양이다.

점검 스크립트는 `freqtrade-mcp-probe.py` — MCP stdio 로 붙어 `initialize` → `tools/list` →
툴 17개를 차례로 `tools/call` 한다.

### 본문에서 뺀 장단점 항목

장점 중 본문 4개에 안 남긴 것: 백테스트 요약에 `Market change` 가 기본으로 들어 있어 승률 93.9% 옆에
"그냥 들고 있었으면 56%" 가 나란히 놓인다 · 생태계(거래소 78, 포크 1.1만, 전략 저장소·커뮤니티) ·
문서가 자기 한계를 적는다(Dry-Run 이 유동성·대기열을 반영하지 않는다는 것).

단점 중 본문 4개에 안 남긴 것: DB 파일이 `--userdir` 을 안 따라가고 현재 디렉터리에 생긴다 ·
설정 파일이 크다(최소 설정도 40줄, 필드 하나 틀리면 JSON 스키마 오류가 스택트레이스로) ·
명령마다 전제가 다르다(`lookahead-analysis` 는 `price_side: "other"` 를 요구하는데 백테스트는 아니다) ·
전략을 파이썬으로 써야 한다(판다스 전제).
