Docs / Concepts

Costs

Fees read the non-promotional tier, and cost is checked before anything else because most ideas die here.

Check costs first#

cost_model_refresh()
cost_breakeven(trades_per_day=20)

This is step one of the order of work, before data ingest and long before any statistics. The reason is arithmetic.

At the published Coinbase Exchange entry tier — 60 bps taker per side, non-promotional, so a 120 bps round trip — twenty trades a day costs:

120 bps × 20 trades/day × 365 days / 100 = 8,760% per year

No signal fixes that. An idea requiring 20 trades a day at the entry tier is dead before it is tested, and finding that out costs one tool call rather than a week of backtesting and a chunk of your trial budget.

The fee is named explicitly so the figure can be audited and recomputed against your own cost model rather than taken on trust.

The ladder is Coinbase's, and only Coinbase's#

Every fee figure on this page is Coinbase's published schedule. If you ingested data from another venue with source="url", these numbers do not describe it.

That is not an oversight to be smoothed over with a generic "crypto fee". Fees are venue- and tier-specific, and cost drag is the thing that kills most strategies before any statistic is reached — so a guessed cost model is exactly the kind of number this server exists to reject. A cost model always names the venue it came from; check it against your own venue's schedule before trusting a breakeven computed from it.

Where the numbers come from#

With Coinbase credentials configured the server reads your tier from the account. Without them it falls back to the published schedule, and every cost model says which it is:

{"pricing_tier": "$0-$10K", "taker_bps": 60.0,
 "source": "fallback_published_schedule", "is_live": false}

is_live: false means the entry-tier figures quoted throughout these docs — 60 bps taker per side, a 120 bps round trip — are the published rates rather than a reading of an account. They are the correct rates for a new account, which is what most readers have, but they are not personalised until a key is set.

Non-promotional tiers, on purpose#

Fees read fee_tier_without_promotion.current_tier, not the promotional fee_tier object.

Promotional rates expire. A backtest priced against a promotion is a backtest of a business relationship rather than of a strategy, and it will look worse the moment the promotion ends — which is exactly when you would be trading it live.

Prices and fees describe the same venue#

Candles come from Coinbase Advanced Trade (350 bars per request), not Coinbase Exchange. Earlier versions pulled candles from Exchange while reading Advanced Trade fees, which meant prices and fees described two different products. gate_check warns on a mismatch.

Staleness#

The cost model carries a one-hour staleness horizon. server_status reports age_seconds and source for every cost model, so an old model is visible as old.

A fallback ladder — the hardcoded table used when no API key is configured — reports its fetched_at as when the table was assembled, not when anything was retrieved. Refreshing a fallback returns the same table, so it is reported as old rather than as stale: "stale" means refresh this, and there is no refresh that would help.

Fee share of returns#

backtest_result reports gross and net side by side, plus the fee share of gross return. A strategy whose fee share is above 100% is not a marginal strategy; it is a fee-generation mechanism.

Breakeven hit rate#

stats_triple_barrier(...)

Triple-barrier labelling reports a breakeven hit rate — the win rate you would need for the strategy to break even after costs. When barriers are tighter than the round-trip fee, this exceeds 1.0, meaning no achievable hit rate is sufficient. That is a complete answer, and it arrives before any model is fitted.

Read this page as Markdown: /docs/concepts/costs.md