Tools · finance

Strategy backtest (crypto, daily closes)

Backtest a stated trading rule on a crypto asset's daily USD closes (Coinbase Exchange) and compare it with buying and holding: SMA crossover, RSI mean reversion, DCA or buy-and-hold, 30-365 days, with fees. Returns total return, drawdown, Sharpe, win rate, exposure, every trade and the equity curve; the rules are published in every answer. Send {} for BTC, SMA 20/50, 180 days. A test of the past, not advice. Pay-per-call via x402.

$0.009 per call, paid in USDC on Base or Solana with x402 — no account, no API key. POST https://fiatdock.com/v1/market/backtest (or GET with the same fields as query parameters) · MCP tool strategy_backtest. An answer the tool cannot produce is refused with 4xx/5xx and is not charged.

Inputs

FieldTypeDescriptionExample
assetstringCrypto asset ticker with a USD market on Coinbase Exchange (BTC, ETH, SOL, …). Default BTC"BTC"
strategyone of sma_cross, rsi_reversion, dca, buy_and_holdsma_cross (long while the fast moving average is above the slow one), rsi_reversion (buy when RSI falls below rsiBuy, sell when it rises above rsiSell), dca (buy a fixed amount every dcaEveryDays), buy_and_hold. Default sma_cross"sma_cross"
daysintegerLength of the tested window in days, 30-365 (default 180)180
fastintegersma_cross: fast moving-average length in days, 2-100 (default 20)20
slowintegersma_cross: slow moving-average length in days, 3-200, longer than fast (default 50)50
rsiPeriodintegerrsi_reversion: RSI length in days, 2-50 (default 14)14
rsiBuynumberrsi_reversion: buy when RSI falls below this, 1-50 (default 30)30
rsiSellnumberrsi_reversion: sell when RSI rises above this, 50-99 (default 70)70
dcaEveryDaysintegerdca: days between purchases, 1-30 (default 7)7
feeBpsnumberFee on every trade in basis points, 0-100 (default 10 = 0.1%)10
capitalUsdnumberStarting capital in USD, 10-10000000 (default 1000)1000

Example

Request body

{"asset":"BTC","strategy":"sma_cross","days":180,"fast":20,"slow":50}

Answer (the published example)

{"asset":"BTC","market":"BTC-USD","strategy":"sma_cross","params":{"days":180,"feeBps":10,"capitalUsd":1000,"fast":20,"slow":50},"period":{"from":"2026-04-19","to":"2026-10-15","days":180},"startPriceUsd":73823.14,"endPriceUsd":82131.46,"finalEquityUsd":1226.92,"totalReturnPct":22.69,"buyAndHoldReturnPct":11.25,"maxDrawdownPct":15.71,"sharpe":1.54,"exposurePct":71.7,"trades":3,"winRatePct":0,"tradeList":[{"date":"2026-04-19","side":"buy","priceUsd":73823.14},{"date":"2026-05-31","side":"sell","priceUsd":73575.17},{"date":"2026-07-21","side":"buy","priceUsd":66516.18}],"equityCurve":[{"date":"2026-04-19","equityUsd":999,"priceUsd":73823.14},{"date":"2026-05-25","equityUsd":1045.36,"priceUsd":77249.07},{"date":"2026-06-30","equityUsd":994.65,"priceUsd":58523.93},{"date":"2026-08-05","equityUsd":965.07,"priceUsd":64603.03},{"date":"2026-09-10","equityUsd":1143.34,"priceUsd":76536.55},{"date":"2026-10-15","equityUsd":1226.92,"priceUsd":82131.46}],"method":"Daily USD closes of completed UTC days (Coinbase Exchange). A signal formed at a day's close is executed at the next day's close (no look-ahead); buy_and_hold buys at the first close and dca buys every dcaEveryDays from the first close. Long or flat only, no leverage, no slippage or funding; a fee of feeBps on every trade's notional. sma_cross: long while the fast simple moving average is above the slow one. rsi_reversion: Wilder RSI, seeded with the simple average of the first rsiPeriod changes of a warm-up of 10 x rsiPeriod days before the window, with the long/flat state carried through the warm-up; buy when it falls below rsiBuy, sell when it rises above rsiSell. dca: capitalUsd split into equal purchases, one every dcaEveryDays from the first close (floor((days - 1) / dcaEveryDays) + 1 of them). sma_cross reads the slow average's length of history before the window. A window with a missing day, or a market with no close in the last 3 days, is refused, never filled in. totalReturnPct = final equity / capital - 1; buyAndHoldReturnPct = last close / first close - 1 (no fee); maxDrawdownPct = the largest peak-to-trough fall of the equity curve; sharpe = mean / standard deviation of daily equity returns x sqrt(365), risk-free rate 0; winRatePct over closed round trips; exposurePct = share of days holding the asset.","source":"Coinbase Exchange daily candles (api.exchange.coinbase.com)","asOf":"2026-10-16T13:21:41.862Z","note":"A test of a stated rule on past prices — not a forecast and not investment advice. Results ignore slippage, taxes and funding; a different window can give the opposite result."}

Call it

curl

# 1) ask: an unpaid call answers 402 with the exact price (free)
curl -i -X POST https://fiatdock.com/v1/market/backtest -H 'content-type: application/json' -d '{"asset":"BTC","strategy":"sma_cross","days":180,"fast":20,"slow":50}'
# 2) pay: any x402 client signs that 402 and resends — see Node or Python below

Node

// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const pay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:*", client: new ExactEvmScheme(privateKeyToAccount(process.env.AGENT_PRIVATE_KEY)) }],
});
const r = await pay("https://fiatdock.com/v1/market/backtest", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({"asset":"BTC","strategy":"sma_cross","days":180,"fast":20,"slow":50}),
});
console.log(r.status, await r.json());

Python

# pip install "x402[httpx,evm]"
import asyncio, json, os
from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm.exact import register_exact_evm_client

async def main():
    client = register_exact_evm_client(x402Client(), Account.from_key(os.environ["AGENT_PRIVATE_KEY"]))
    async with x402HttpxClient(client) as http:
        r = await http.post("https://fiatdock.com/v1/market/backtest", json=json.loads("{\"asset\":\"BTC\",\"strategy\":\"sma_cross\",\"days\":180,\"fast\":20,\"slow\":50}"))
        print(r.status_code, r.json())

asyncio.run(main())

MCP (Claude, Cursor, any MCP client)

{"mcpServers":{"fiatdock":{"command":"npx","args":["-y","fiatdock-mcp"],"env":{"AGENT_PRIVATE_KEY":"0x<your agent wallet key>"}}}}
// then call the tool "strategy_backtest" — the package pays each call from that wallet

The wallet needs USDC on Base. Prefer no install? The remote server https://fiatdock.com/mcp answers the 402 as tool output for an agent that signs its own payments.

Related tools

Machine-readable: OpenAPI · tools.json · llms.txt · /.well-known/x402