Jesse from install to live trading — the crypto strategy framework that shows you where each decision happened and separates skill from luck
An 8,500-star crypto strategy framework installed in 8 seconds, 290,000 candles imported, a backtest laid on top of the candles so every single order can be ins
Part 6. Jesse — the thing that shows you where a decision happened and separates skill from luckDone when: you can put your own backtest result through 1,000 Monte Carlo runs and get back a p-value telling you whether it was skill or luck.
| What it does | Imports crypto candles into a database, backtests a Python strategy, lays the result on top of the candles so you can zoom down to a single order, and measures with Monte Carlo whether that result was luck |
| Who it fits | People who write crypto strategies in Python and have at some point failed to trust their own backtest results. Anyone who wants to see "why did it buy there" on a chart rather than in a table |
| Who it doesn't fit | People who follow stocks (crypto only) · people who'd rather not stand up PostgreSQL and Redis · people who want to reach real-time orders for free (orders are paid) |
| What you end up with | One backtest (48 trades, +12.18%), those trades overlaid on the candles, and a 1,000-scenario distribution with p-values |
| What you need · cost | Code free (MIT) · Model none at all (no LLM) · Market data free, 0 API keys · weltiq only if you follow section 5. Note that live and paper trading are a paid plugin |
| Time · difficulty | 8 seconds to install, 91 seconds to import candles, 0.41 seconds to backtest, 9 seconds for Monte Carlo. Difficulty medium (you stand up PostgreSQL and Redis yourself) |
| Relationship to weltiq | It connects in the opposite direction. Jesse's order slot is paid, so weltiq has no place there — but a fill ledger exported from weltiq can be run through jesse's Monte Carlo |
| Test environment · date | macOS 26.6.2 · arm64 · CPython 3.12.13 · jesse 3.2.1 · PostgreSQL 16.13 · Redis 8.6.2 · 2026-09-22 |
| Disclosure | weltiq pays me to write this series. Everything said about the tool comes from runs I did myself |
1. What this is
jesse-ai/jesse is a crypto strategy framework written in Python. It imports candles into a database, you write strategies as Python classes, it runs backtests, and you view the results in a web dashboard. It started in 2018 and is in its eighth year.
If part 4's freqtrade, another crypto bot, is terminal-first with the web UI as a side dish, jesse is dashboard-first. The single line jesse run is the web server starting, and importing candles, editing strategies, backtesting and viewing results all happen inside the browser.
There are two things to see in this part. One, you inspect on the chart where the decision happened — most tools hand you a table like "win rate 27%, drawdown −14.5%", but which candle produced that −14.5% is not in the table. Two, Monte Carlo is in the product menu — it answers "skill or luck?" in 9 seconds. That is the main event.
| Item | Value |
|---|---|
| URL | github.com/jesse-ai/jesse |
| License | MIT |
| Stars | ★8,564 · 1,242 forks (observed 2026-09-22) |
| Last push | 2026-09-21 (the day before I wrote this) |
| Created | 2018-11-09 · 16 open issues · not archived |
| Version | PyPI jesse 3.2.1 (+ jesse-rust 1.2.0) |
| Candle sources | 10 exchanges (Apex, Binance, Bitfinex, Bybit, Coinbase, Gate, Hyperliquid, Kraken, KuCoin, Lighter) |
| Markets in config | 38 (testnets included) |
The first five rows come from the GitHub API, the version from jesse --version, and the last two from the installed candle-import drivers and jesse.info.exchange_info.
jesse run also brings up an MCP server (67 tools) alongside the dashboard. But the four tools that actually run the heavy things require a jesse.trade account, and a guest gets 0 runs per day, so I ran everything without an account through the dashboard and the local API (the raw response is in the run log).
2. Installing it
You need four things — Python 3.12 (a dedicated virtualenv), PostgreSQL (candles and sessions all live here), Redis (dashboard progress and worker management), and 0 API keys. Importing candles and backtesting work without one.
There is a stretch that costs money. The core (backtesting, Monte Carlo, the dashboard) is entirely free under MIT, but the live plugin that places real-time orders is paid, and
jesse install-livewill not proceed withoutLICENSE_API_TOKENin.env. The community strategy marketplace also carries strategies tagged PREMIUM. I paid for nothing in this article.
Use a dedicated virtualenv — it pulls 111 packages with heavyweights like ray, scikit-learn and matplotlib in the mix, 866MB once it's all in.
uv venv -p 3.12 /private/tmp/tut-venvs/jesse
uv pip install --python /private/tmp/tut-venvs/jesse/bin/python jesse
It took 7.94 seconds, and once jesse --version prints jesse, version 3.2.1 you're in.
Then comes the trap. The jesse make-project command the error message points you at does not exist in 3.2.1 — jesse --help has only install-live and run. The project check in the source looks like this.
# jesse/helpers.py:1026
def is_jesse_project():
ls = os.listdir('.')
return 'strategies' in ls and 'storage' in ls
Two directories are all it takes. So you make the folders, drop in a .env, and bring up the DB and Redis.
mkdir -p /private/tmp/jesse-tut/{strategies,storage}
cd /private/tmp/jesse-tut
cp env.example .env # PASSWORD, APP_PORT, POSTGRES_*, REDIS_*
createdb jesse_tut
redis-server --port 9011 --daemonize yes
jesse run
The full .env is at env.example; the only things to fill in are PASSWORD (a dashboard password you choose) and the PostgreSQL account. At the end of the startup log a Dashboard is available at http://localhost:9010 box appears. Open it, enter that password, and this is the screen.
The jesse dashboard on first start
The first-start screen. Look at Jesse Environment 3.2.1 in the System Monitor panel in the middle — get this far and the install is done. Backtest and Monte Carlo in the left menu are what this article looks at.
Where you get stuck
| Symptom | Cause | Way around |
|---|---|---|
The jesse make-project command you were pointed at doesn't exist |
The command was removed in 3.2.1 but the guidance text stayed | Create the two folders strategies/ and storage/ — that folder is the project |
.env is missing or empty |
You ran it outside the project root | cd to the folder holding those two directories and run again |
You forgot to set the PASSWORD in your .env file |
An empty value blocks startup entirely | Put anything in it. It is the dashboard login |
Database wasn't ready followed by a 10-second wait |
PostgreSQL isn't up | Check with pg_isready first |
| Redis connection refused | The REDIS_PORT in .env differs from the actual port |
Default is 6379. If you moved it, move .env too |
| Port conflict | There are three ports — defaults are dashboard 9000, language server 9001, MCP 9002 | Move only the dashboard and the other two get blocked silently. Move all three together |
3. The first result
Bringing the dashboard up
Every screen in this article comes from jesse's own dashboard. Starting it is one line, jesse run in the project folder, and the address is http://localhost:9010 (default 9000; I moved mine in .env). A password prompt appears, and it is the PASSWORD= value from .env — a local lock needing no sign-up and no internet, separate from the jesse.trade account in section 1. You can see everything up to section 4 of this article for free, with no account.
The first time you open it the screen is empty, and the three boxes under Your First Steps — Import Data → Create Strategy → Run Backtest — are exactly what this section does. The overlay screen from section 4 (Trade chart) only appears as a button on the right of the result screen once a backtest has finished.
Importing candles — 91 seconds
In Import Candles you pick an exchange, a symbol and a start date. I used Binance Spot · BTC-USDT · 2026-06-24.
Successfully processed 128160 observed candles since "2026-06-24" through "2026-09-20".
Existing rows were retained.
128,160 candles, about 91 seconds. 128,160 = 89 days × 1,440 — jesse always imports 1-minute candles and synthesizes longer timeframes. The DB grows fast (204 days and 290,000 candles came to 91MB).
The Manage Candles screen
The Import Candles → Manage screen. Each imported window is summarized with Start Date · End Date. Look at Update · Copy · Export · Delete on the right — you can copy candles you already imported under a different exchange name, or export them to CSV.
Write a strategy and backtest — and get blocked once
Strategies go in strategies/<name>/__init__.py as a Python class. I used the dullest thing I could think of — SMA 20/50 crossover, spot, long only, ATR×2 stop. It is not a trading opinion; it is an example whose job is to produce a "trade list" to feed into Monte Carlo.
class SmaCrossTutorial(Strategy):
@property
def fast(self): return ta.sma(self.candles, 20, sequential=True)
@property
def slow(self): return ta.sma(self.candles, 50, sequential=True)
def should_long(self) -> bool:
return utils.crossed(self.fast, self.slow, direction='above')
def go_long(self):
entry = self.price
self.stop_price = entry - ta.atr(self.candles) * 2
self.buy = utils.risk_to_qty(self.available_margin, 3, entry,
self.stop_price, fee_rate=self.fee_rate), entry
def on_open_position(self, order):
# On spot the stop must be set here, not in go_long().
self.stop_loss = self.position.qty, self.stop_price
def update_position(self):
if utils.crossed(self.fast, self.slow, direction='below'):
self.liquidate()
The full file is at SmaCrossTutorial.py. I then ran a backtest over the 90-day window I had just imported, and it was refused.
The warm-up candle shortage failure screen
The failure screen. Look at the SESSION STOPPED text in the red box — "this run needs data from 2026-02-11 (including 210 warm-up candles before the start date). Import candles from 2026-02-11 and run again." What is missing and how to fix it are in one sentence.**
Computing an indicator requires candles from before the start date. Jesse demands 210 by default, and rather than quietly computing over a shorter window when they are missing it refuses outright — a better attitude than running a backtest with odd values. I re-imported from 2026-03-01 (117 seconds, 165,600 more candles) and set the start date to 2026-03-15, which worked. 189 days, 1-hour candles, 0.41 seconds.
The backtest result screen
The finished backtest. In Cumulative Returns vs Benchmark on the left, the blue line is the strategy and the yellow dotted line is simply holding BTC — you can see at a glance that it trailed until August and caught up in September. In the Performance panel on the right, what to look at is Total Paid Fees 918.38: three quarters of the 1,217.59 net profit went out in fees.
Total Closed Trades 48
Total Net Profit 1217.59 (12.18%)
Start ⇒ Finish Balance 10000 ⇒ 11215.45
Total Paid Fees 918.38
Max Drawdown -14.52%
Win-rate 27.08% (13 wins, 35 losses)
Sharpe / Calmar / Sortino 0.98 / 1.71 / 1.99
This is what success looks like — the trade count and the fee total are printed, and once a Trade chart button appears on the right you can move on to section 4.
| Symptom | Cause | Way around |
|---|---|---|
Missing candles ... includes 210 warm-up candles |
Not enough candles before the start date | Re-import from the date printed in the message |
The session never leaves running |
The config has two shapes — backtest reads exchanges (plural), Monte Carlo reads exchange (singular), and when they disagree the worker dies silently and nothing at all appears on screen |
You won't hit it filling in the dashboard form. Posting via the API, put both keys in |
InvalidStrategy: Setting self.stop_loss in the go_long() method is not supported for spot trading |
A spot restriction | Set the stop in on_open_position() |
| The strategy doesn't appear in the list | The folder name and the class name differ | class SmaCrossTutorial inside strategies/SmaCrossTutorial/__init__.py |
4. Getting the most out of it — asking whether +12.18% was skill or luck
The backtest returned +12.18%. But that number is one thing that happened once — 48 trades occurred in that order and price moved along that path. What if the order had differed? The feature that shakes exactly that is in the product menu.
| What it shuffles | What it asks | |
|---|---|---|
| Monte Carlo trades | the order of the 48 trades | was the drawdown I lived through a matter of sequencing luck |
| Monte Carlo candles | resamples the candles themselves and re-runs the strategy | would it have worked if the market had been a little different |
500 of each, 1,000 scenarios in total — 5 seconds for the trade shuffle, 4 seconds for the candle resample (4 CPU cores, parallelized with ray).
The Monte Carlo result screen
Above is the 500-run trade-order shuffle, below the 500-run candle resample. The blue tangle is the 500 scenarios and the yellow line is the one that actually happened. The upper tangle converges at the right edge to a single point (11215.45) while the lower one stays spread all the way out — that difference is what this whole section is about.
Shuffle the trade order and the return stays put while only the drawdown moves.
| METRIC | ORIGINAL | WORST 5% | MEDIAN | BEST 5% |
|---|---|---|---|---|
| Return | 12.2% | 12.2% | 12.2% | 12.2% |
| Max Drawdown | −15.4% | −18.5% | −12.0% | −8.2% |
| Sharpe Ratio | 0.96 | 0.79 | 0.84 | 0.88 |
| Calmar Ratio | 1.61 | 1.34 | 2.08 | 3.03 |
Of course the return is identical — add the same P/Ls in a different order and the sum is the same. But maximum drawdown spreads from −8.2% to −18.5%. That is the value of the feature. "The worst I would have lived through had I actually run it" may have been closer to −18.5% than to −15.4%, and −15.4% itself was in no way special among the 500 shuffles (p=0.154). Sharpe, by contrast: the actual value of 0.963 is higher than the maximum of 0.915 across the 500 shuffles (p=0.0) — not because the strategy is good, but because the order the trades happened to arrive in was itself lucky.
Resample the candles and nothing was significant.
| METRIC | ORIGINAL | WORST 5% | MEDIAN | BEST 5% |
|---|---|---|---|---|
| Net Profit % | 12.18 | −18.13 | 15.34 | 56.38 |
| Max Drawdown | −15.39 | −30.41 | −16.00 | −8.14 |
| Sharpe Ratio | 0.96 | −1.61 | 1.19 | 3.29 |
| Win-rate | 0.271 | 0.171 | 0.273 | 0.390 |
| Trade count | 48 | 39 | 46 | 53 |
+12.18% was one point between −18.13% and +56.38%, below the median (+15.34%). The p-value is 0.570 and the summary reads significant_metrics_5pct: 0 / total_metrics: 7 — not one of the seven metrics is significant. This result is no evidence at all that "the SMA 20/50 crossover worked"; it is a value you could get out of any resampled market. Looking only at the backtest table you would have stopped at "+12% over 189 days, not bad", and one nine-second calculation overturns that reading completely.
Know two things before you run it. The Monte Carlo draft's default fee (0.0006) differs from the backtest side (0.001) — without matching them, "original" and "scenarios" are compared on top of different fees. And turn fast_mode on for the candle resample (on synthesized candles, if ATR collapses momentarily, sizing blows up and scenarios die — a failure mode written into jesse's own tool description). The full commands, and why the two screens show drawdowns of −15.4% and −14.52%, are in the run log.
What it can't do: this calculation attaches only to jesse's own backtest results (there is no entrance for external records, which is why I worked around it in section 5). The resampling defaults to one method, moving_block_bootstrap — so if that assumption is wrong, the distribution is wrong too.
Inspecting on the chart where a decision happened
Click Trade chart on the right of the backtest result screen.
The full Trade chart
On the left, the 48 completed trades lined up with entry price, quantity, return, direction and holding time; on the right, the same period's 1-hour candles with every executed order overlaid (blue BUY · LONG, red SELL · LONG). Look at that vertical run-up in late August — the strategy did not buy there. As a table it was "win rate 27%"; as a chart it is "missed the biggest rally and then traded in and out above it".
Pick one trade from the list and it zooms to that spot.
One trade zoomed in
The first trade opened up (entered 15 March at 22:00, held 71 hours, +1.67%). Look at the EXECUTIONS 2 orders panel on the left — 71610.33 BUY MARKET · Executed 0.14, 72952.85 SELL MARKET · Executed −0.14. It goes down to the order. The chart on the right joins the entry candle and the exit candle with a dotted line, and you can see the price falling vertically from the candle after the yellow SELL · LONG marker.
One caveat: indicator lines are not overlaid by default — my SMA 20/50 lines did not appear. It looks like you have to register them in the strategy code, but I didn't try, so it is unverified.
There are plenty of backtest frameworks, but putting a device that doubts its own results inside the product's own screen is rare. If part 4's freqtrade lookahead-analysis asks "did my code peek at the future", jesse's Monte Carlo asks "was my result luck".
5. Carrying it over to weltiq — an integration experiment (the direction is reversed)
Jesse has no place where it connects directly to weltiq's execution. The order-placing side is a paid plugin, and it attaches to exchanges with its own connectors. But in the other direction it does connect. The Monte Carlo from section 4 runs on nothing more than a "trade list", and a weltiq strategy container exports its fill ledger to a file. I used a container that had already run and been stopped — binance-spot / BTCUSDT / daily, 60 fills = 30 round trips, total fees 37.37548391 USDT.
- Sign up for weltiq ({{CTA_URL}}) → My → Connections → issue an API key.
- Take the export from the stopped container (fills, fees and the performance table are all in one file).
- First check that both are measuring with the same ruler — pair the fills into round-trip trades, put them into jesse's metrics calculator, and see whether the maximum drawdown matches weltiq's performance table. In my run both read −0.5432%, identical to four decimal places. If the numbers don't agree, plotting a distribution is pointless.
- Shuffle those 30 trades 500 times. The shuffling, the equity reconstruction and the confidence intervals all called jesse's own functions (if I reimplemented them, it wouldn't be jesse doing the measuring). The script is
wq_trades_monte_carlo.py. - Read the distribution.
The shuffle result for the 30 trades from the weltiq strategy container
The maximum-drawdown distribution from shuffling the 30 round-trip trades 500 times. The blue bars are the quantiles of the shuffle distribution and the orange vertical line is the −0.5432% that actually happened. Note that the actual value sits between the 25th (−0.5542%) and the median (−0.4990%), right in the middle of the distribution.
[max_drawdown] original -0.5432 5th -0.6560 | 50th -0.4990 | 95th -0.3807 (p=0.276)
[calmar_ratio] original -2.8900 5th -4.1229 | 50th -3.1457 | 95th -2.3930 (p=0.276)
500 shuffles, seed 20260922
How to read it. The −0.5432% drawdown this container lived through was neither bad sequencing luck nor good (p=0.276). Had the same 30 trades arrived in a different order, it could have been as bad as −0.656% or as good as −0.381%. So when setting a stop condition, "the worst so far was −0.54%, so let's set −0.6%" is dangerous — with the same strategy and the same number of trades, −0.66% comes up perfectly routinely.
What this combination does not do. The shuffle leaves each trade's P/L alone and changes only the order. A world with different fill prices, a world with a rejection, a world with larger slippage — none of those are produced this way. That is the candle resampling side from section 4, and it has to re-run a jesse strategy, so it cannot be attached to external records.
Where this sits among the four stages. Jesse's home ground is ② systematizing and deploying a strategy, and ③ paper trading is a paid plugin. The one place it joins weltiq is ④ trade analysis — and that one place was the most useful thing in this article. The stages before and after have their own articles: a tool with only the order slot empty (part 1, alpaca-mcp-server) and a tool that only decides and never executes (part 3, LLM-Auto-Trader).
6. After using it
What was good
- Monte Carlo is inside the product. Shaking a result in two directions takes 9 seconds, and p-values and quantiles land on the screen. Frameworks that ask "was my result luck" from the default menu are not common.
- You inspect decision points on the chart. All 48 trades overlay the candles, picking one zooms to that spot, and the two orders that made up the trade unfold with their fill status.
- When you're wrong, it tells you precisely. Short on warm-up candles and you get "import from 2026-02-11 and run again"; set a spot stop in the wrong place and you get "set it in
on_open_position()". And it won't force a run on insufficient data. - It's fast, and it keeps a record. 0.41 seconds for a 189-day hourly backtest, 9 seconds for 1,000 scenarios. Each session snapshots the strategy code, so there is no wondering a month later which version produced a result.
What fell short
- Live and paper trading are paid. So this article can say nothing about jesse's order path — unverified. It contrasts with part 4's freqtrade making Dry-Run the default, though less as better-or-worse than as products of a different character.
- You can't run anything through MCP. A thing built on the assumption that an AI will drive it, down to auto-generating a conventions document, requiring an account for the actual running felt inconsistent (the rule itself is published transparently in the repository).
- It's heavy, and crypto only. PostgreSQL + Redis are mandatory, the venv is 866MB, one 90-day import is 91 seconds. Fixed to 1-minute candles, so you import every minute even if you only need 4-hour bars. No equities, no traditional futures.
- The strategy marketplace puts returns and win rates on the front. Every card in the
Strategiesmenu carries PnL, max drawdown, Sharpe and win rate in large type, with paid PREMIUM strategies mixed in. I downloaded nothing for this article.
Cost — code and market data free (MIT, 0 API keys), model none (no LLM in this tool). The only paid parts are the live/paper plugin and the marketplace's PREMIUM strategies. Time: 8 seconds to install · 91 seconds for 90 days of candles · 0.41 seconds to backtest · 9 seconds for 1,000 scenarios.
7. What to do next
Run one backtest on your own strategy and put the result straight into the Monte Carlo menu. Nine seconds. If zero metrics are significant, that strategy is not yet evidence — and the fact that finding this out costs nine seconds is the reason to use this tool.
Next is alsk1992/CloddsBot (★2.8k). Where jesse is a tool in which a human writes rules in Python and those rules are verified deterministically, CloddsBot hangs 121 skills off Claude and lets it decide — the rules come out of conversation rather than code, so it gets stuck in exactly the opposite places.
weltiq setup — 3 lines 1. Sign up: {{CTA_URL}} 2. My → Connections → issue an API key 3.
claude mcp add weltiq --header "Authorization: Bearer <key>" https://weltiq.ai/mcpYou can take an exported fill ledger and run it through jesse's Monte Carlo exactly as in section 5.
Every command, output and number in this article came from runs actually performed on 2026-09-22. I created no exchange account and no API key, asked the exchange only for public market data, and not one order went out. None of it is a trading opinion. The trial and error, raw logs and full commands not written up here are in the run log.