Tektii just went live. We're shipping fixes daily — spot a bug? Let us know

Write Workflow

This workflow runs the full backtest loop: create a strategy, push your Docker image as a version, then configure, run, and read a backtest against it.

Overview

The write workflow follows these steps:

  1. Create a strategy - Set up a named container for your trading logic
  2. Prepare your Dockerfile - Ensure your strategy builds for the target platform
  3. Upload a version - Build and push your Docker image to the platform
  4. Write a scenario configuration - Describe the market data and window to test
  5. Run the backtest - Kick off the run with scenario run
  6. Read the results - Pull the headline metrics (return, Sharpe, drawdown)

Prerequisites

Before starting, ensure you have:

  • The Tektii CLI installed (Installation)
  • Your API key configured (Authentication)
  • Docker installed and running
  • A Dockerfile for your strategy

Need a strategy to upload? Your First Backtest has a complete paste-and-run strategy.py and Dockerfile, and Writing a Strategy covers the strategy shape — the event loop, order submission, position sizing, and history warm-up. Write the strategy to read the instrument and timeframe off the events it receives rather than hard-coding them; one uploaded image then runs against anything a scenario points it at.

Step 1: Create a Strategy

If you don't have a strategy yet, create one:

tektii strategy create --name "my-strategy"

This displays the newly created strategy as a field/value table:

✓ Strategy created successfully!

┌──────────────┬──────────────────────────────────────┐
│ Field        │ Value                                │
├──────────────┼──────────────────────────────────────┤
│ Strategy ID  │ a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d  │
│ Name         │ my-strategy                          │
│ Owner ID     │ user_...                              │
│ Created By   │ user_...                              │
│ Created At   │ 2024-01-15T10:30:00Z                  │
│ Updated At   │ 2024-01-15T10:30:00Z                  │
└──────────────┴──────────────────────────────────────┘

Note the strategy ID (a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d) - you'll need it for the next step.

If You Already Have a Strategy

List your existing strategies to find the ID:

tektii strategy list

Step 2: Prepare Your Dockerfile

Your strategy needs a Dockerfile so the platform can run it as a container.

Example Dockerfile

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt

COPY . .
CMD ["python", "strategy.py"]

Step 3: Upload a Version

Navigate to your strategy repository and run:

cd /path/to/my-strategy
tektii version upload a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d

The CLI will:

  1. Validate inputs (including that your Dockerfile exists)
  2. Auto-detect your Git SHA (or generate a timestamp fallback)
  3. Create a version entry in the API
  4. Build the Docker image
  5. Verify the built image targets linux/amd64
  6. Push the image to the registry

Custom Paths

If your Dockerfile isn't in the default location, specify custom paths:

tektii version upload a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d \
  --dockerfile ./docker/Dockerfile.prod \
  --context ./src

Flags

FlagDefaultDescription
--dockerfile <PATH>./DockerfilePath to your Dockerfile
--context <PATH>.Docker build context directory
--git-sha <SHA>Auto-detectedGit commit SHA for this version
--parent <VERSION_ID>-Parent version ID when branching from an existing version
--template <TEMPLATE>-Create the version from a platform template (ma-crossover, rsi-momentum) and skip the Docker build

Git SHA Detection

The CLI automatically detects your current Git commit SHA. If you're not in a Git repository, it generates a timestamp-based identifier like ts-1234567890.

To specify a SHA manually:

tektii version upload a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d --git-sha abc1234def

Version provenance & branching

Each version records two things that tie it back to your work:

  • Git SHA — the traceability link back to your source. Tektii stores the built image and the commit SHA, not your source code. To see what changed between two versions, run git diff <shaA> <shaB> in your own repository.
  • Parent version — lineage. --parent records which version this one descends from, organising your experiments as a branching tree. You can branch a new experiment from any version, not just the latest — point --parent at the version you want to fork from.

Apple Silicon (M1/M2/M3) Users

If you're on an Apple Silicon Mac, Docker builds natively target darwin/arm64, which the platform's scenario runner cannot run. You have two options:

  1. Open Docker Desktop
  2. Go to Settings > General
  3. Enable "Use Rosetta for x86/amd64 emulation"
  4. Restart Docker Desktop

Option 2: Use Docker Buildx

Create a buildx builder that can cross-compile:

# Create and use a buildx builder
docker buildx create --use

# Build with explicit platform
docker buildx build --platform linux/amd64 -t my-image .

Complete Example

Here's the full workflow from start to finish:

# Step 1: Create a strategy (if you don't have one)
tektii strategy create --name "momentum-v1"
# Note: Save the strategy ID from the output

# Step 2: Navigate to your strategy code
cd /path/to/momentum-strategy

# Step 3: Verify Dockerfile exists
ls Dockerfile

# Step 4: Upload the version
tektii version upload a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d

# The CLI will build and push your Docker image
# On success, you'll see the new version ID

Example Output

Validating inputs...
→ Detected version: abc1234
Creating version in API...
✓ Created version: b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e
Connecting to Docker...
Building image ...
Verifying image platform...
Pushing image to ...

✓ Version uploaded successfully!

  Version ID: b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e
  Git SHA: abc1234
  Image: ...

  Use this version ID when creating scenarios

Verify Your Upload

List versions for your strategy to confirm the upload:

tektii version list a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d

Get details for a specific version:

tektii version get a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e

Configure & run a backtest

Uploading a version doesn't run anything on its own — it just makes your strategy available. To actually test it, you run a backtest scenario: you describe what market data to feed the strategy and over what window, the platform replays that data through your image, and you read the results.

A config is a reusable saved preset and a scenario is a run of one against a strategy version — see config vs scenario in the commands reference. The steps below pass the config inline with scenario run --file; to save it for reuse, see Re-run a saved configuration.

Step 4: Write a scenario configuration

A scenario is defined by a JSON configuration file. tektii scenario run reads this file with --file:

{
  "strategyVersionId": "b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e",
  "subscriptions": [
    { "instrument": "C:BTCUSD", "events": ["candle_1m"] }
  ],
  "startTime": "2024-01-01T00:00:00Z",
  "endTime": "2024-04-01T00:00:00Z",
  "initialCapital": 100000,
  "positionMode": "netting",
  "env": { "MA_SHORT": "20", "MA_LONG": "50" }
}

Each subscription pairs one instrument — a platform symbol like C:BTCUSD or F:EURUSD, from the Available Instruments catalog — with the events delivered to your strategy, such as candle_1m. The full field reference — every key, the valid events, the candle pattern grammar, and optional transaction costs — is Scenario Configuration.

Two plan caps apply to every run — how far back startTime can reach, and how long the window can span. Size the window against Plan Limits before you launch.

Step 5: Run the backtest

Pass the config file to scenario run. Launching the scenario starts the run — the version comes from the file's strategyVersionId, so there's no --version-id on this path:

tektii scenario run \
  --strategy-id a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d \
  --file ./backtest-config.json

The command returns a scenario ID at QUEUED — submission is asynchronous — then the run moves through PENDING and RUNNING to a terminal state; note the success state is COMPLETE, not COMPLETED. See Scenario states for the full state machine.

Step 6: Read the results

Poll the scenario until it completes, then read its headline metrics with scenario get:

tektii scenario get a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f

That prints a summary table in your terminal. For a machine-readable object, add the global --output json flag:

tektii --output json scenario get a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f

For a completed scenario, the response carries the top-line performance fields:

{
  "data": {
    "scenarioId": "c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f",
    "state": "COMPLETE",
    "totalReturn": 12.45,
    "sharpeRatio": 1.32,
    "maxDrawdownPct": 8.10,
    "totalTrades": 142,
    "winRatePct": 54.2
  }
}

totalReturn, maxDrawdownPct, and winRatePct are percentages (e.g. 12.45 = 12.45%). sharpeRatio and the other risk metrics appear only once a completed run has enough data to compute them. For a precise definition of each field — including what window Sharpe is computed over and how drawdown is measured — see the Results Reference.

You can also open the scenario in the web app to see the equity curve and metrics rendered as charts. For the downloadable result types — the full timeseries and the per-trade log, and every field they contain — continue to the Read Workflow and the Results Reference.

Strategy parameters: the env override map

A scenario config controls more than the market data and window. Your strategy parameters (indicator windows, stop-loss / take-profit, and so on) come from environment variables, and the image's ENV lines set the defaults — but they're not the only way to set them. The env field on a scenario config, and the per-run env you pass at launch, override the baked ENV for that run, so you can vary parameters against one image with no rebuild.

{
  "strategyVersionId": "b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e",
  "subscriptions": [{ "instrument": "C:BTCUSD", "events": ["candle_1m"] }],
  "startTime": "2024-01-01T00:00:00Z",
  "endTime": "2024-04-01T00:00:00Z",
  "env": { "MA_SHORT": "20", "MA_LONG": "50" }
}

The per-run env wins over the baked ENV, so a parameter sweep is N configs (or N runs) against the same version, not N Docker builds.

Layering, lowest to highest precedence:

  1. The image's baked ENV (defaults).
  2. The saved config's env (per-config default override).
  3. The per-run env supplied at launch (wins).

To sweep a grid of parameter sets in one call, AI agents can use the MCP batch_run_scenarios tool with an env_grid — see Per-run env overrides & sweeps.

Re-run a saved configuration

Passing --file ./backtest-config.json re-supplies the same file on every run. If you'll reuse a window, save the configuration once and run it later by its id — no file, and no re-typing the subscriptions, window, or capital.

Save the configuration

The file you wrote in Step 4 is an inline config — it carries strategyVersionId, and config create rejects that key, because a saved preset does not pin a version. Copy the file, delete strategyVersionId, and save the copy:

tektii config create --name "btc-q1-2024" --file ./saved-config.json

This stores the config under your account and returns a config ID. Saved configs are user-scoped — list them again anytime with tektii config list. The two file shapes differ only at the ends — see The two config schemas.

Run a backtest from the saved id

Run the saved config against a strategy version with scenario run, passing the config ID to --config:

tektii scenario run \
  --strategy-id a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d \
  --config d4e5f6a7-b8c9-4d4e-8f5a-6b7c8d9e0f1a \
  --version-id b2c3d4e5-f6a7-4b2c-8d3e-4f5a6b7c8d9e

The subscriptions, window, and capital all come from the saved config, so the only things you supply are which strategy and version to run it against. A saved config doesn't pin a version, which is why --version-id is required here — unlike the --file path in Step 5, where the version comes from the file and the flag is rejected.

This enqueues the run and returns a scenario ID at QUEUED — read it back with scenario get exactly as in Step 6. See scenario run for the full argument reference.

Common Issues

Docker Not Running

Error: Docker not running

Solution: Start Docker Desktop or the Docker daemon.

Platform Mismatch

Error: Image platform mismatch (expected linux/amd64, got darwin/arm64)

Solution: See the Apple Silicon section above.

Git SHA Not Detected

Warning: Failed to detect git SHA, using timestamp fallback

This is not an error. If you're not in a Git repository, the CLI uses a timestamp-based identifier. To specify a SHA manually, use --git-sha.

Dockerfile Not Found

Error: Dockerfile not found at ./Dockerfile

Solution: Use --dockerfile to specify the correct path:

tektii version upload a1b2c3d4-e5f6-4a1b-9c2d-3e4f5a6b7c8d --dockerfile ./docker/Dockerfile

Next Steps