# A축 6편 jesse — 실행 기록 (2026-09-22)

글: [`docs/tutorials/a/06-jesse.md`](../../../docs/tutorials/a/06-jesse.md)

## 환경

| | |
|---|---|
| OS | macOS 26.6.2 · arm64 |
| 파이썬 | CPython 3.12.13 (전용 venv `/private/tmp/tut-venvs/jesse`) |
| 설치 도구 | uv 0.10.12 |
| jesse | **3.2.1** (+ `jesse-rust` 1.2.0) |
| PostgreSQL | 16.13 (Homebrew) · DB `jesse_tut` |
| Redis | 8.6.2 · 포트 **9011** (이 작업용으로 따로 띄움) |
| jesse 대시보드 | 포트 **9010** |
| jesse LSP / MCP | 포트 **9012** / **9013** |
| 프로젝트 디렉터리 | `/private/tmp/jesse-tut` |

실행 시각대: 2026-09-22 01:41 ~ 02:12 KST.

## GitHub 관측값 (2026-09-22, GitHub API)

```
full_name        jesse-ai/jesse
stargazers_count 8564
forks_count      1242
subscribers      209
open_issues      16
created_at       2018-11-09T10:38:44Z
pushed_at        2026-09-21T16:01:10Z
archived         False
license          MIT
```

## 시간 실측

| 단계 | 실측 |
|---|---|
| `uv venv -p 3.12` | 0.05초 |
| `uv pip install jesse` | **7.94초** · 패키지 111개 · venv 866MB |
| 봉 import ①: BTC-USDT 1m, 2026-06-24~09-20 | **약 91초** · 128,160봉 (01:45:39→01:47:10) |
| 봉 import ②: 같은 심볼 2026-03-01부터 | **117초** · 165,600봉 추가 (01:52:15→01:54:12) |
| DB 크기 (봉 293,760개) | 91MB |
| 백테스트 (1h, 2026-03-15~09-20, fast_mode=false) | **0.41초** |
| 몬테카를로 매매 셔플 500회 | **5초** (17:04:08→17:04:13 UTC) |
| 몬테카를로 봉 재표본 500회 | **4초** (17:04:13→17:04:17 UTC) |

## 만든 것

- 전략 `SmaCrossTutorial` — [`SmaCrossTutorial.py`](SmaCrossTutorial.py). SMA 20/50 교차, 스팟 롱 전용, ATR×2 손절.
- 백테스트 세션 `b5af22f5-c4f4-4398-ba29-de8388db1598` — 결과 [`bt2_result.json`](bt2_result.json)
- 몬테카를로 세션 `7aa5319c-cbd0-4bcb-b106-952ce1ba2d60` — [`mc_result.json`](mc_result.json) · 원본 신뢰구간 [`mc_trades_results.json`](mc_trades_results.json) [`mc_candles_results.json`](mc_candles_results.json)
- 실패 재현 세션 `ea58273b-c5f9-450d-8c8c-58223e10b00b` (워밍업 봉 부족)
- MCP 최소 클라이언트 [`mcpcall.py`](mcpcall.py)
- 화면 캡처 스크립트 [`shoot.py`](shoot.py) (playwright, 1600×1000 @2x)
- 5절 스크립트 [`wq_trades_monte_carlo.py`](wq_trades_monte_carlo.py) · 그림 [`wq_plot.py`](wq_plot.py)

## 백테스트 결과 (실측)

```
Total Closed Trades   48
Total Net Profit      1217.59 (12.18%)
Start => Finish       10000 => 11215.45
Open Trades           1
Total Paid Fees       918.38
Max Drawdown          -14.52%
Max Underwater Period 157 days
Annual Return         24.8%
Win-rate              27.08%   (13승 35패)
Sharpe / Calmar / Sortino   0.98 / 1.71 / 1.99
Largest Winning / Losing    1891.09 / -233.95
```

## 몬테카를로 (500 × 2, 실측)

매매 순서 셔플 (trades):

| METRIC | ORIGINAL | WORST 5% | MEDIAN | BEST 5% | p |
|---|---|---|---|---|---|
| Return | 12.1759% | 12.1759% | 12.1759% | 12.1759% | 0.004 |
| Max Drawdown | −15.39% | −18.50% | −11.96% | −8.19% | 0.154 |
| Sharpe | 0.963 | 0.792 | 0.838 | 0.882 | 0.0 |
| Calmar | 1.611 | 1.343 | 2.077 | 3.033 | 0.848 |

봉 재표본 (candles, `moving_block_bootstrap`, batch 10080):

| METRIC | ORIGINAL | WORST 5% | MEDIAN | BEST 5% | p |
|---|---|---|---|---|---|
| Net Profit % | 12.176 | −18.132 | 15.339 | 56.380 | 0.570 |
| Max Drawdown | −15.391 | −30.408 | −15.996 | −8.143 | 0.530 |
| Sharpe | 0.963 | −1.606 | 1.189 | 3.286 | 0.578 |
| Win-rate | 0.2708 | 0.1707 | 0.2727 | 0.3903 | 0.532 |
| 매매 건수 | 48 | 39 | 46 | 53 | 0.384 |
| Annual Return | 24.80 | −32.03 | 31.68 | 136.95 | 0.570 |
| Calmar | 1.611 | −1.339 | 2.041 | 14.617 | 0.550 |

`significant_metrics_5pct: 0 / 7` — 봉 재표본 기준으로 유의한 지표는 하나도 없었다.

## 5절 — weltiq 슬롯 기록을 jesse 셔플에 태우기

원본: 슬롯 `ct_80a7c7a17e34156b` (binance-spot / BTCUSDT / tf=1d, `doc_hash` `d3088b7c…`), 이미 돌려 둔 뒤 정지된 슬롯의 `GET /v1/containers/{cid}/export`. 체결 60건 = 왕복 30건, 수수료 37.37548391 USDT.

```
원본      : 수익률 -0.3807%  최대낙폭 -0.5432%
우리 쪽 성능표의 최대낙폭: -0.5432%   ← 두 계산이 소수 넷째 자리까지 일치

[total_return]  원본 -0.3807   5th -0.3807 | 50th -0.3807 | 95th -0.3807  (p=1.0)
[max_drawdown]  원본 -0.5432   5th -0.6560 | 50th -0.4990 | 95th -0.3807  (p=0.276)
[sharpe_ratio]  원본 -3.2205   5th -3.1758 | 50th -3.1712 | 95th -3.1669  (p=1.0)
[calmar_ratio]  원본 -2.8900   5th -4.1229 | 50th -3.1457 | 95th -2.3930  (p=0.276)

셔플 500회, 시드 20260922
```

셔플·재구성·신뢰구간은 jesse 의 `jesse.research.monte_carlo.monte_carlo_trades` 내부 함수를 그대로 불렀다(직접 구현하지 않음). 결과 [`wq_mc_result.json`](wq_mc_result.json), 원본 내보내기 [`wq_export.json`](wq_export.json).

## 막힌 것과 그 이유 (전부 재현)

1. **`jesse make-project` 가 없다.** 3.2.1 의 `jesse --help` 는 `install-live` · `run` 둘뿐인데, 프로젝트가 아닌 곳에서 실행하면 나오는 에러 메시지는 여전히 `jesse make-project {name}` 을 안내한다. 실제로는 `strategies/` 와 `storage/` 디렉터리 두 개만 있으면 프로젝트로 인식한다(`jesse/helpers.py:1026`).
2. **MCP `run_backtest` 는 계정이 필요하다.** `{"status":"limit_reached", "is_guest": true, "limit": 0}`. `jesse/mcp/USAGE_LIMITS.md` 에 "guests = 0" 로 명시돼 있다. **계정을 만들지 않았으므로 MCP 로는 실행하지 않았고**, 실행은 전부 로컬 HTTP API(`POST /backtest`, `POST /monte-carlo`)로 했다. 드래프트 생성·봉 import·세션 조회 등 나머지 MCP 툴은 계정 없이 동작했다.
3. **설정 `config` 의 모양이 두 가지다.** 백테스트 러너는 `config['exchanges']`(복수, 대시보드가 보내는 모양)를, 몬테카를로 러너는 `config['exchange']`(단수)를 읽는다. MCP 드래프트는 단수로 만들어 준다. 모양이 안 맞으면 **워커가 종료 코드 1로 조용히 죽고 세션은 `running` 인 채로 남는다** — 같은 종류의 사고에 대한 주석이 `jesse/config.py:158` 에 남아 있다. 이 글에서는 양쪽 키를 모두 넣어 해결했다.
4. **워밍업 봉 210개.** 90일치만 받아 놓고 그 시작일부터 백테스트를 걸면 거절된다. 이건 조용히 죽지 않고 정확한 메시지가 나온다 — `03-warmup-error.png`.
5. **스팟에서는 `go_long()` 안에 `self.stop_loss` 를 못 쓴다.** `InvalidStrategy: ... Try setting it in self.on_open_position() instead.` 세션 상태가 `stopped` + 예외 문구로 정확히 올라왔다.
6. **라이브·페이퍼는 유료 플러그인.** `jesse install-live` 가 `.env` 의 `LICENSE_API_TOKEN` 을 요구한다(`jesse/services/installer.py:25`). **결제하지 않았으므로 S5~S8 은 돌리지 않았다.**

## 안 한 것

- 거래소 주문은 **한 건도** 내지 않았다. 거래소에는 **공개 봉 조회만** 했다(Binance Spot 공개 klines).
- jesse.trade 계정 생성·로그인·유료 플러그인 구매 **안 함**.
- 커뮤니티 전략 마켓의 PREMIUM 전략 구매 **안 함**.
- 최적화(Optimization), Rule Significance Test: **안 돌렸다 — 미확인**.
- weltiq 웹 화면 캡처: 페이지가 쿠키 세션 로그인을 요구해 **찍지 않았다**. 5절은 웹 화면이 아니라 내보낸 원장 파일을 다루므로 그 결과 그림(`08-weltiq-shuffle.png`)으로 대신했다.
- 돌고 있던 슬롯 12개와 엔진·웹·감시 프로세스는 **건드리지 않았다**. 5절에서 읽은 `ct_80a7c7a17e34156b` 는 이미 정지된 슬롯이고, 읽기(export)만 했다.

## 화면 캡처

| 파일 | 무엇 |
|---|---|
| `shots/01-first-run.png` | 첫 기동 대시보드 — `Jesse Environment 3.2.1` |
| `shots/02-import-candles.png` | Manage Candles — 받아 둔 구간 |
| `shots/03-warmup-error.png` | 워밍업 봉 부족 실패 화면 |
| `shots/04-backtest-result.png` | 백테스트 결과 + Performance 패널 |
| `shots/05-trade-chart.png` | **Trade chart — 봉 위에 실행된 주문 48건** |
| `shots/06-trade-chart-one-trade.png` | 매매 한 건 확대 + EXECUTIONS 2 orders |
| `shots/07-monte-carlo.png` | **몬테카를로 500×2 결과** |
| `shots/08-weltiq-shuffle.png` | weltiq 슬롯 30건 셔플 낙폭 분포 |

캡처는 playwright(chromium, 1600×1000, DPR 2)로 `http://127.0.0.1:9010` 를 열어 찍었다. 로그인 토큰은 `localStorage` 에 넣어 화면에 노출되지 않는다.

## 대시보드 기본 포트 (BLOG_GUIDE §3.3 "대시보드는 띄우는 법까지" 용 확인값)

| | 기본값 | 이 글에서 쓴 값 | 출처 |
|---|---|---|---|
| 대시보드 | **9000** | 9010 | `jesse/cli.py` `PORT = 9000` |
| 파이썬 언어 서버 (LSP) | **9001** | 9012 | `jesse/services/lsp.py` `LSP_DEFAULT_PORT = 9001` |
| MCP 서버 | **9002** | 9013 | `jesse/mcp/mcp_config.py` `MCP_PORT = 9002` |
| 바인드 호스트 | **0.0.0.0** | 127.0.0.1 | `jesse/cli.py` `HOST = "0.0.0.0"` |

전부 `.env` 의 `APP_PORT` / `LSP_PORT` / `MCP_PORT` / `APP_HOST` 로 덮어쓴다.

대시보드 로그인은 `.env` 의 `PASSWORD` 를 sha256 한 값이 곧 토큰이다(`jesse/services/auth.py`). **jesse.trade 계정과는 무관하며, 대시보드에서 누르는 백테스트·Trade chart·몬테카를로는 계정 없이 전부 동작했다.** 계정이 필요한 것은 MCP 의 실행 툴 4개뿐이다.

---

## 부록 — 독자판 재편(2026-09-24)에서 기사 밖으로 내린 것

독자판 `docs/tutorials/a/06-jesse.md` 를 ≤3,000어절로 줄이면서 본문에서 뺀 실측·관측을 여기 옮겨 적는다. 숫자는 손대지 않았다.

### MCP 서버 실측

- `jesse run` 이 대시보드(9010)와 함께 MCP 서버(9013)를 같이 띄운다. **툴 67개**를 세었다. 전략 생성·봉 import·백테스트 드래프트 생성·세션 결과 조회까지 전부 MCP 툴로 있다.
- 무거운 실행 툴 **4개** (`run_backtest` · `run_monte_carlo` · `run_optimization` · `run_significance_test`) 만 jesse.trade 계정을 요구한다. 게스트 응답 원문:

```
{"status": "limit_reached",
 "message": "Running research through the AI assistant — backtests, Monte Carlo,
             optimizations, and significance tests — requires a Jesse account.
             Sign up for free at jesse.trade to get started.",
 "used": 0, "limit": 0, "is_guest": true}
```

  `jesse/mcp/USAGE_LIMITS.md` 에 규칙이 그대로 적혀 있다 — **게스트 0회 / 무료 계정 100회/일 / 유료 무제한**. 계정을 만들지 않았으므로 MCP 로는 실행하지 않았고, 실행은 전부 로컬 HTTP API 로 했다.
- `jesse run` 은 프로젝트 폴더에 **`AGENTS.md` 를 534줄**로 써 놓는다. "너는 Jesse 전략 에이전트다, 모든 매매 관련 동작은 Jesse MCP 툴로 해라" 류의 규약이 들어 있다. AI 에게 시켜 쓸 것을 전제로 만든 물건이라는 뜻이다.

### 직접 친 명령 (기사에서는 접어 둔 부분)

봉 받기 — MCP 툴로도 된다(계정 불필요):

```bash
python mcpcall.py call import_candles \
  '{"exchange":"Binance Spot","symbol":"BTC-USDT","start_date":"2026-06-24"}'
```

진행률은 `get_candle_import_status(import_id)`:

```
{'current': 50.4, 'estimated_remaining_seconds': 95.1, 'current_date': '2026-06-10'}
```

**`current_date` 가 거꾸로 내려간다** — 최근 봉부터 과거로 받는다.

백테스트 — 드래프트는 MCP(계정 불필요), 실행은 로컬 API:

```bash
python mcpcall.py call create_backtest_draft '{
  "exchange":"Binance Spot",
  "routes":"[{\"exchange\":\"Binance Spot\",\"strategy\":\"SmaCrossTutorial\",\"symbol\":\"BTC-USDT\",\"timeframe\":\"1h\"}]",
  "start_date":"2026-03-15","finish_date":"2026-09-20",
  "export_chart":true,"fast_mode":false,"benchmark":true}'

curl -X POST http://127.0.0.1:9010/backtest \
  -H "Authorization: $TOKEN" -H 'Content-Type: application/json' -d @payload.json
```

`$TOKEN` 은 `POST /auth/login` 에 `.env` 의 `PASSWORD` 를 넣으면 나온다.

몬테카를로 드래프트:

```bash
python mcpcall.py call create_monte_carlo_draft '{
  "exchange":"Binance Spot",
  "routes":"[{\"exchange\":\"Binance Spot\",\"strategy\":\"SmaCrossTutorial\",\"symbol\":\"BTC-USDT\",\"timeframe\":\"1h\"}]",
  "start_date":"2026-03-15","finish_date":"2026-09-20",
  "num_scenarios":500,"run_trades":true,"run_candles":true,
  "fast_mode":true,"cpu_cores":4}'
```

### 몬테카를로 실행 시 걸린 두 가지

1. **드래프트 기본 수수료가 백테스트와 다르다.** 몬테카를로 드래프트는 수수료 **0.0006** 으로 들어오고 백테스트 쪽은 **0.001** 이다. 이 글에서는 비교가 되도록 **0.001 로 맞춰서** 돌렸다. 안 맞추면 "원본"과 "시나리오"가 서로 다른 수수료 위에서 비교된다.
2. **봉 재표본은 `fast_mode: true` 로 돌려야 한다.** jesse 자신의 MCP 툴 docstring 이 이유를 적어 뒀다 — 합성된 봉에서 ATR 이 순간적으로 아주 작아지면 위험 기반 사이징이 포지션을 폭주시켜 시나리오가 `InsufficientMargin` 으로 죽는다. 자기 기능의 실패 모드를 툴 설명에 적어 둔 셈이다.

### 낙폭 값이 화면마다 다른 이유

몬테카를로 화면의 ORIGINAL 최대낙폭 **−15.4%** 는 백테스트 화면의 **−14.52%** 와 미세하게 다르다. 몬테카를로 쪽은 시나리오와 같은 자로 재려고 equity 곡선에서 자기 방식으로 다시 계산하기 때문이다. **비교는 같은 열 안에서** 해야 한다.

### 조용히 죽는 자리의 원문 주석

`config` 모양이 안 맞아 워커가 종료 코드 1로 죽는 사고에 대해, 같은 종류의 주석이 `jesse/config.py:158` 에 이미 달려 있다:

> *"a missing 'name' raised KeyError deep inside a spawned backtest worker, which then exited silently (the traceback was lost on the os._exit() that follows), leaving the MCP/dashboard session stuck."*

알고는 있는 문제다.

### 그 밖에 관측한 것

- **전략 코드 스냅샷.** 백테스트 결과 화면의 `Strategy snapshot` 에서 그때 돌린 전략 코드를 그대로 꺼내 볼 수 있다. 세션마다 통째로 저장된다.
- **차트에 지표가 기본으로 안 겹친다.** Trade chart 에 봉과 실행된 주문은 나오지만 이 글의 SMA 20/50 선은 나오지 않았다. 전략 코드에서 따로 등록해야 하는 것으로 보이나 **해 보지 않았다 — 미확인.**
- **전략 폴더 이름과 클래스 이름이 같아야** 목록에 뜬다 (`strategies/SmaCrossTutorial/__init__.py` 안에 `class SmaCrossTutorial`).
- **`Strategies` 메뉴는 커뮤니티 전략 장터**다. 카드마다 PnL·최대낙폭·샤프·승률이 앞면에 큼직하게 붙어 있고 PREMIUM 딱지가 붙은 유료 전략도 섞여 있다. **이 글에서는 아무것도 받지 않았고** 직접 쓴 `SmaCrossTutorial` 만 썼다.
- **봉은 1분봉 고정.** 더 긴 시간틀은 필요할 때 1분봉에서 합성한다. 90일치 하나가 128,160봉(= 89일 × 1,440)이고, 204일치 293,760봉이 DB 91MB 였다.
