# TradeLock Manager API Guide

**Audience:** Strategy managers and integration engineers  
**Use case:** Server-to-server strategy management and trade submission  
**Version:** 0.6  
**Last updated:** 2026-04-22

This guide is a production-focused API reference for TradeLock manager workflows.

## 1) Base URLs

- Production: `https://tradelock.net/api`
- Local emulator (Hosting): `http://127.0.0.1:5003/api`

All examples below use production URLs.

## 2) Authentication

TradeLock supports two auth methods on manager endpoints:

1. API key (recommended for servers)
- Header: `X-Trader-Api-Key: <API_KEY>`

2. Firebase ID token
- Header: `Authorization: Bearer <FIREBASE_ID_TOKEN>`

### API key lifecycle endpoints

These endpoints require **Firebase ID token** auth (not API key auth):

- `POST /generate-api-key`
- `GET /list-api-keys`
- `POST /delete-api-key`
- `POST /revoke-api-key` (same behavior as delete)

### API key scopes

`generate-api-key` supports key scopes:

- `full` (default): can access all strategies
- `sandbox`: can only submit to strategies marked as sandbox/test

A sandbox key calling a non-sandbox strategy returns:

- `403` with `code: "SANDBOX_SCOPE_RESTRICTION"`

## 3) Recommended Submission Modes (copy-paste first)

Use this order for manager signal ingestion:
1. `POST /set-target-portfolio` (primary)
2. `POST /logTrade` with `quantity` + explicit side
3. `POST /logTrade` with single-symbol `allocation_percent` and no side

Default examples below are intentionally prefilled for sandbox testing:
- `strategy`: `Sandbox Strategy`
- API key header: `X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>`

### 3.1 Primary: set full target portfolio

Behavior:
- Full-portfolio scope by default.
- If a current holding symbol is omitted from `targets`, its target is `0%`.

```bash
curl -X POST "https://tradelock.net/api/set-target-portfolio" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "strategy": "Sandbox Strategy",
    "targets": { "SPY": 33, "TLT": 33, "EEM": 10, "GSY": 15 },
    "execution_anchor": "next_us_equity_open",
    "open_delay_minutes": 2,
    "idempotency_key": "signal-portfolio-20260308-001"
  }'
```

### 3.2 Quantity mode: explicit buy/sell units

```bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "trade_type": "buy",
    "quantity": 10,
    "idempotency_key": "signal-qty-buy-20260308-001"
  }'
```

### 3.3 Single-symbol target allocation (leave others unchanged)

```bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "allocation_percent": 30,
    "idempotency_key": "signal-single-target-20260308-001"
  }'
```

## 4) Request Conventions

### Strategy identity

A strategy is keyed by `strategy_name` in the manager workspace.

- Create is effectively an upsert on `strategy_name`
- Renaming an existing strategy name is not supported on update

### Symbol/asset format

Trade assets are expected as uppercase symbols, with optional exchange hint:

- `AAPL`
- `AAPL:NASDAQ`
- `BTC-USD`

### Time and timestamps

Responses may contain:

- ISO timestamps (string)
- Unix epoch milliseconds (number)

### Idempotency (write safety)

Use idempotency on all write operations.

Accepted forms:

- Header: `Idempotency-Key: <key>`
- Body: `idempotency_key`

For `quick-trade`, `client_order_id` is accepted and mapped to `idempotency_key`.

Validation and behavior:

- Length must be `8..200` chars
- Same key + same payload: replay prior response
- Same key + different payload: `409` conflict
- Same key while first request is processing: `409`

Note for browser clients: pass idempotency in the JSON body. CORS allow-list does not include `Idempotency-Key`.

### Fill policy and delay

For `quick-trade` and `logTrade`:

- `fill_policy`: `immediate_if_open` (default) or `next_open_with_delay`
- `open_delay_minutes`: non-negative number

Delay precedence:

1. `open_delay_minutes` in request
2. client default delay
3. global fallback (`5` minutes)

### Live vs CSV-import fields

For live trades (`import_source` omitted or `live`), do **not** send:

- `reported_date`
- `user_reported_price`
- `reference_price`
- `allocation_reference_price`

These are only valid for `import_source: "csv_import"`.

## 5) Error Model

Most errors use:

```json
{ "error": "Human-readable message", "code": "OPTIONAL_MACHINE_CODE" }
```

Common statuses:

- `200` OK
- `201` Created (strategy created)
- `202` Accepted (trade queued)
- `400` Validation error
- `401` Authentication failed/missing
- `403` Authenticated but not allowed
- `404` Not found
- `405` Method not allowed
- `409` Idempotency conflict/in-progress
- `500` Internal error
- `502` Upstream provider failure (symbol search)

## 6) Endpoint Reference

### 6.1 API Keys

### `POST /generate-api-key`

Generate a new manager API key.

Auth:

- `Authorization: Bearer <FIREBASE_ID_TOKEN>`

Request body:

| Field | Type | Required | Notes |
|---|---|---|---|
| `key_name` | string | No | Default: `"My API Key"` |
| `client_id` | string | No | Default: current user UID |
| `key_scope` | string | No | `full` (default) or `sandbox` |

Success (`200`):

```json
{
  "api_key": "uuid",
  "key_id": "uuid",
  "scope": "full",
  "message": "API Key generated successfully"
}
```

### `GET /list-api-keys`

List API keys for authenticated user.

Auth:

- `Authorization: Bearer <FIREBASE_ID_TOKEN>`

Success (`200`):

```json
[
  {
    "id": "key-id",
    "name": "Webhook Prod",
    "created_at": "2026-02-20T15:00:00.000Z",
    "last_used": "2026-02-27T10:11:12.000Z",
    "status": "active",
    "scope": "full"
  }
]
```

### `POST /delete-api-key` (or `POST /revoke-api-key`)

Delete/revoke an API key.

Auth:

- `Authorization: Bearer <FIREBASE_ID_TOKEN>`

Request body:

| Field | Type | Required |
|---|---|---|
| `api_key_id` | string | Yes |

Success (`200`):

```json
{ "message": "API Key deleted successfully" }
```

### 6.2 Strategy Management

### `GET /strategies`

List strategies for authenticated manager.

Auth:

- API key or ID token

Query params:

| Param | Type | Required | Notes |
|---|---|---|---|
| `limit` | number | No | `1..100`, default `50` |
| `page_token` | string | No | Last returned strategy ID |
| `include_archived` | boolean-like | No | `true/1/yes` to include soft-deleted |

Success (`200`):

```json
{
  "strategies": [
    {
      "id": "Momentum Core",
      "strategy_name": "Momentum Core",
      "is_public": false,
      "tags": ["momentum"],
      "asset_types": ["equity"],
      "initial_capital_usd": 100000
    }
  ],
  "next_page_token": null
}
```

Notes:

- When OpenTimestamps verification anchors are enabled and the current account is eligible to see them, strategies may also include `verification_anchor_latest_v1`.
- `verification_anchor_latest_v1` is the latest per-strategy anchor summary and includes:
  - `provider`: `opentimestamps`
  - `snapshot_date`
  - `status`: `pending`, `attested`, or `error`
  - `anchor_path` and `proof_path`
  - `anchor_sha256`
  - `stamped_at`, `upgraded_at`, `attested_at`, `last_error`

Example:

```json
{
  "verification_anchor_latest_v1": {
    "provider": "opentimestamps",
    "snapshot_date": "2026-04-04",
    "status": "pending",
    "anchor_sha256": "f8f8...",
    "anchor_path": "verification/ots/.../anchor.json",
    "proof_path": "verification/ots/.../anchor.json.ots"
  }
}
```

### `GET /verification-anchor-file`

Download the latest OpenTimestamps anchor artifact for one of your strategies.

Auth:

- ID token

Query params:

| Param | Type | Required | Notes |
|---|---|---|---|
| `strategyName` or `strategy_name` | string | Yes | Target strategy |
| `kind` | string | Yes | `anchor` or `proof` |

Behavior:

- `kind=anchor` returns the latest `anchor.json`.
- `kind=proof` returns the latest detached `anchor.json.ots` proof.
- Returns `404` when the strategy has no anchor yet, the feature is disabled, or the current account is not eligible to view anchor files.

Success:

- `200` with an attachment response
- Content type is `application/json` for `anchor`
- Content type is `application/octet-stream` for `proof`

### `POST /createStrategy`

Create or update a strategy by `strategy_name`.

Auth:

- API key or ID token

Request body:

| Field | Type | Required | Notes |
|---|---|---|---|
| `strategy_name` | string | Yes | Strategy ID in workspace |
| `description` | string | No | |
| `public_summary` | string | No | Defaults to description if missing |
| `is_public` | boolean | No | Default `false` |
| `public_slug` | string | No | Applied when `is_public=true` |
| `tags` | string[] | No | Up to 25 entries after normalization |
| `asset_types` | string[] | No | Up to 25 entries after normalization |
| `metadata` | object | No | Primitive/object values only |
| `initial_capital_usd` | number | No | Must be positive if provided |
| `trade_hide_days` | number | No | Days of recent trades hidden from public viewers. Allowed: `0` (show all), `30`, `60`, `90`, `9999` (hide all). Default: `0` |
| `min_rebalance_move_pct` | number | No | Drift filter for `set-target-portfolio`. Legs with a notional move below this % of running capital are skipped as no-ops. Range `0`–`100`. Default: `1` |

Success:

- `201` when created
- `200` when existing strategy is updated

```json
{
  "message": "Strategy created successfully",
  "created": true,
  "strategy": {
    "id": "Momentum Core",
    "strategy_name": "Momentum Core",
    "is_deleted": false
  }
}
```

### `POST|PUT|PATCH /update-strategy` (alias: `/updateStrategy`)

Patch strategy fields.

Auth:

- API key or ID token

Request body:

| Field | Type | Required | Notes |
|---|---|---|---|
| `strategy_id` or `strategy_name` | string | Yes | Target strategy |
| `description` | string | No | |
| `public_summary` | string | No | |
| `is_public` | boolean | No | |
| `public_slug` | string | No | Regenerated/sanitized when needed |
| `tags` | string[] | No | |
| `asset_types` | string[] | No | |
| `metadata` | object | No | |
| `is_deleted` | boolean | No | Soft-delete toggle |
| `initial_capital_usd` | number | No | Must be positive |
| `trade_hide_days` | number | No | Days of recent trades hidden from public viewers. Allowed: `0`, `30`, `60`, `90`, `9999`. |
| `min_rebalance_move_pct` | number | No | Drift filter threshold `0`–`100`. |

Constraints:

- Renaming strategy name is not supported (`400`)
- `verification_rate` is server-managed and cannot be set

Success (`200`):

```json
{
  "message": "Strategy updated successfully",
  "strategy": {
    "id": "Momentum Core",
    "strategy_name": "Momentum Core"
  }
}
```

### `POST|DELETE /delete-strategy` (alias: `/deleteStrategy`)

Soft-delete (archive) a strategy. Archiving also cancels active pending trades for the same strategy before the archive is applied. Archived strategies are preserved indefinitely and can be restored or permanently deleted from the owner's account.

Auth:

- API key or ID token

Request body:

| Field | Type | Required |
|---|---|---|
| `strategy_id` or `strategy_name` | string | Yes |

Success (`200`):

```json
{
  "message": "Strategy archived successfully",
  "strategy_id": "Momentum Core",
  "deleted": true,
  "pending_trade_cancellation": {
    "matched_pending_trades": 1,
    "cancelled_count": 1,
    "already_cancelled_count": 0,
    "processing_count": 0,
    "not_cancellable_count": 0,
    "failed_count": 0
  }
}
```

Conflict (`409`):

- Returned as `ARCHIVE_BLOCKED_BY_PENDING_TRADES` if one or more matching pending trades are already processing or could not be cancelled safely.

### `POST|DELETE /permanent-delete-strategy` (alias: `/permanentDeleteStrategy`)

Permanently delete an archived strategy and its strategy-owned data. This removes the strategy document tree, matching pending trades, immutable trade storage files, verification snapshot files, and OpenTimestamps anchor/proof files tied to that strategy.

Auth:

- ID token only

Request body:

| Field | Type | Required |
|---|---|---|
| `strategy_id` or `strategy_name` | string | Yes |

Success (`200`):

```json
{
  "message": "Strategy permanently deleted",
  "strategy_id": "Momentum Core",
  "deleted": true,
  "permanent": true
}
```

Conflict (`409`):

- Returned as `STRATEGY_NOT_ARCHIVED` if the strategy has not been archived first.

### 6.3 Trade Validation and Submission

### `POST /validate-trade` (alias: `POST /dry-run-trade`)

Validate and normalize a trade request without creating a trade.

Auth:

- API key or ID token

Key inputs:

- Strategy: `strategy` or `strategy_name` or `user_strategy_name` (optional if user has exactly one strategy)
- Asset: `ticker` or `symbol` or `asset`
- Side: `trade_type` or `side` or `action` (`buy|sell`)
- Size: exactly one of `quantity` or allocation aliases

Allocation aliases:

- `allocation_percent`
- `allocation_pct`
- `allocationPercentage`
- `allocation%`

Success (`200`):

```json
{
  "valid": true,
  "mode": "validate_only",
  "key_scope": "full",
  "normalized_payload": {
    "user_strategy_name": "Momentum Core",
    "asset": "AAPL",
    "trade_type": "buy",
    "allocation_percent": 10,
    "fill_policy": "immediate_if_open"
  },
  "notes": [
    "Allocation mode with trade_type sizes this order from strategy running capital."
  ]
}
```

### `POST /quick-trade`

Manager-friendly trade submission with broad aliases.

Recommendation:
- Keep this for backwards compatibility or low-code adapters.
- For manager-driven portfolio instructions, prefer `POST /set-target-portfolio`.

Auth:

- API key or ID token

Request fields:

| Canonical field | Accepted input keys | Required | Notes |
|---|---|---|---|
| strategy | `strategy`, `strategy_name`, `user_strategy_name` | Conditionally | Optional only if exactly one strategy exists |
| asset | `ticker`, `symbol`, `asset` | Yes | `SYMBOL` or `SYMBOL:EXCHANGE` |
| trade side | `trade_type`, `side`, `action` | Depends | Required when using `quantity` |
| quantity | `quantity`, `qty` | Depends | Mutually exclusive with allocation |
| allocation | `allocation_percent`, `allocation_pct`, `allocationPercentage`, `allocation%` | Depends | Mutually exclusive with quantity |
| idempotency | `idempotency_key`, `client_order_id` | Recommended | Use in production |
| execution | `fill_policy`, `open_delay_minutes` | No | |
| tags | `tags` | No | string[] |

Behavior:

- Calls `logTrade` internally after normalization
- Returns immediate fill (`200`) or queued (`202`)
- Pending replacement rules for same `strategy + asset`:
  - `target_allocation` mode (send `allocation_percent` and omit side) replaces the existing active pending target-allocation intent for that symbol.
  - `quantity` mode and side-based `allocation_percent` mode append as separate pending orders (FIFO).

### `POST /set-target-portfolio`

Set an authoritative target portfolio for a strategy.

- Full-portfolio semantics are default: symbols with open positions that are not in `targets` are auto-targeted to `0%`.
- Before submitting new legs, the endpoint cancels all cancellable pending trades for the same strategy by default.
- Trade legs are submitted sequentially (sells first, then buys).
- Each leg is submitted as target-allocation logic (`allocation_percent` without side).
- Targets do not need to sum to `100`. Totals below `100` leave residual cash uninvested. For example, an `80` total target is valid and leaves `20%` in cash.
- Legs whose notional move is smaller than `min_rebalance_move_pct` (set on the strategy, default `1%`) are silently skipped. The response marks them with `drift_filtered: true` and `drift_pct`.

Pricing and sizing behavior:

- `set-target-portfolio` sizing uses last-price lookup (`getLastPrice`) for each leg, not Alpaca execution quotes.
- Provider order for last-price lookup:
  - Crypto-like symbols: `coinbase` -> `twelve-data` -> `yahoo-finance2` -> `alpha-vantage`
  - Futures-like symbols: `twelve-data` -> `yahoo-finance2` -> `alpha-vantage` -> `coinbase`
  - Other symbols: configured default provider first, then remaining providers.
- Configured default provider:
  - `twelve-data` when `TWELVEDATA_API_KEY` or `TWELVE_DATA_API_KEY` is present
  - else `alpha-vantage` when `ALPHA_VANTAGE_API_KEY` is present
  - else `yahoo-finance2`
- If no provider returns a usable quote, the endpoint returns `400` with a sizing error per symbol.

Auth:

- API key or ID token

Request fields:

| Field | Type | Required | Notes |
|---|---|---|---|
| `strategy` / `strategy_name` / `user_strategy_name` | string | Conditionally | Optional only if exactly one strategy exists |
| `targets` | array or object map | Yes | Array: `[{ "symbol": "SPY", "allocation_percent": 33 }]` or map: `{ "SPY": 33 }` |
| `execution_anchor` | enum | No | `now` (default) or `next_us_equity_open` |
| `execute_on_stock_open` | boolean | No | Alias for `execution_anchor=next_us_equity_open` |
| `open_delay_minutes` | number | No | Applied per leg |
| `cancel_pending_trades_first` | boolean | No | Default `true`; set `false` to preserve the current pending queue |
| `dry_run` | boolean | No | If `true`, returns plan only (no submissions) |
| `idempotency_key` / `client_order_id` | string | Recommended | Used for request-level replay and deterministic per-leg idempotency keys |
| `tags` | string[] | No | Applied to each submitted leg |

Example:

```json
{
  "strategy": "Sandbox Strategy",
  "targets": {
    "SPY": 33,
    "TLT": 33,
    "EEM": 10,
    "GSY": 15
  },
  "execution_anchor": "next_us_equity_open",
  "open_delay_minutes": 2,
  "idempotency_key": "stp-20260308-001"
}
```

Success (`200`) includes rebalance summary and per-leg results.

- When `cancel_pending_trades_first` is enabled, responses also include `preflight_pending_trade_reset`.
- If a pending trade is already `processing`, or any cancellation fails during the preflight reset, the endpoint returns `409` before submitting new legs.

Example response fragment:

```json
{
  "message": "Target portfolio submitted successfully.",
  "rebalance_id": "stp_1773763355292_ganlirnm",
  "preflight_pending_trade_reset": {
    "matchedPendingTrades": 3,
    "cancelledCount": 2,
    "alreadyCancelledCount": 0,
    "processingCount": 1,
    "notCancellableCount": 0,
    "failedCount": 0
  }
}
```

### `POST /logTrade`

Advanced trade endpoint.

Auth:

- API key or ID token

Core fields:

| Field | Type | Required | Notes |
|---|---|---|---|
| `user_strategy_name` / `strategy_name` / `strategy` | string | Conditionally | Optional only if exactly one strategy exists |
| `asset` | string | Yes | `SYMBOL` or `SYMBOL:EXCHANGE` |
| `trade_type` / `side` / `action` | `buy\|sell` | Depends | Required for direct quantity mode |
| `quantity` / `qty` | number | Depends | Required unless allocation is provided |
| `allocation_percent` and aliases | number | Depends | Required unless quantity is provided |
| `fill_policy` | enum | No | `immediate_if_open` (default) or `next_open_with_delay` |
| `open_delay_minutes` | number | No | non-negative |
| `idempotency_key` | string | Recommended | or `Idempotency-Key` header |
| `tags` | string[] | No | |

Sizing modes:

1. Quantity mode
- send `quantity` + explicit side

2. Allocation-percent mode
- send `allocation_percent` + side
- quantity is computed from strategy running capital

3. Target-allocation mode
- send `allocation_percent` and omit side
- endpoint computes target position delta and direction
- queued submissions in this mode replace prior active pending target-allocation intent for the same strategy+asset

CSV import-only fields (`import_source: "csv_import"`):

| Field | Type | Notes |
|---|---|---|
| `import_source` | string | Must equal `csv_import` |
| `reported_date` | ISO-8601 string | Optional |
| `user_reported_price` | number | Required and must be > 0 |
| `reference_price` | number | Optional (sizing reference) |
| `allocation_reference_price` | number | Optional (sizing reference) |

Optional enrichment fields:

- `rankable_strategy_id`, `isin`, `tick_size`, `multiplier`, `point_value`, `margin`, `commission`

Live execution quote behavior:

- `logTrade` uses execution quote lookup (`getExecutionQuote`) when processing non-CSV trades.
- For non-crypto and non-futures symbols, if `ALPACA_EQUITY_ENABLED=true`, execution quote tries Alpaca first.
- Alpaca quote uses top-of-book side pricing: buy -> ask, sell -> bid. Stale/crossed/missing side quotes are rejected.
- If Alpaca quote fails, execution falls back to last-price providers.
- Fallback executions are marked with `execution_basis: "last"` and include `quote_fallback_reason`.
- If no quote is available (or market is not open), the trade is queued (`202`) for delayed processing; failed delayed attempts can move to `error_processing`.

Success patterns:

Immediate/logged (`200`):

```json
{
  "message": "Trade logged successfully!",
  "tradeId": "abc123",
  "execution": {
    "fill_policy": "immediate_if_open",
    "resolved_open_delay_minutes": 5
  },
  "sizing": {
    "mode": "allocation_percent",
    "allocation_percent": 10,
    "quantity": 42
  },
  "hybrid": {
    "verified": true,
    "storageRef": "strategies/.../snapshot.json",
    "hash": "..."
  },
  "price_data": {
    "price": 201.12,
    "market_state": "REGULAR"
  }
}
```

Queued (`202`):

```json
{
  "message": "Trade received and queued for processing when market opens.",
  "pendingTradeId": "pnd_456",
  "current_market_state": "CLOSED",
  "execution": {
    "fill_policy": "next_open_with_delay",
    "waiting_for_next_market_session": false,
    "resolved_open_delay_minutes": 5
  }
}
```

No-op target allocation (`200`):

```json
{
  "message": "No trade created. Strategy is already at the requested target allocation.",
  "no_op": true
}
```

### 6.4 Trade Status and History

### `GET /trade-status`

Get lifecycle/timeline for a trade or pending trade.

Auth:

- API key or ID token

Query params:

| Param | Required | Notes |
|---|---|---|
| `strategyName` or `strategy_name` | Yes | Strategy name |
| `tradeId` or `trade_id` | Yes | `tradeId` or `pendingTradeId` from submit response |

Success (`200`) returns timeline-style response with:

- `source`: `pending_trades` or `strategy_trade`
- `current_status`: e.g. `queued`, `pending_delay`, `filled`, `immutable_stored`, `verified`, `error_*`
- `snapshot_freshness`: `not_ready`, `fresh`, `stale`, `unknown`
- `execution`, `filled`, `timeline`

### `GET /strategy-trades`

Get strategy trade history (and optionally pending queue).

Auth:

- API key or ID token

Query params:

| Param | Required | Notes |
|---|---|---|
| `strategy_name` | Yes | Strategy name |
| `limit` | No | Default `100` |
| `include_pending` | No | `true/1/yes` to include active pending trades |

Success (`200`):

- Without pending: `{ "trades": [...] }`
- With pending: `{ "trades": [...], "pending_trades": [...] }`

### `POST /cancel-pending-trade`

Cancel one queued pending trade before execution.

Auth:

- API key or ID token

Request body:

| Field | Type | Required | Notes |
|---|---|---|---|
| `strategy_name` | string | Yes | Strategy name |
| `pending_trade_id` | string | Yes | ID from queued trade response |
| `reason` | string | No | Optional cancellation note |

Success (`200`):

```json
{
  "message": "Pending trade cancelled successfully.",
  "pending_trade_id": "pnd_456",
  "strategy_name": "Sandbox Strategy",
  "status": "cancelled"
}
```

Possible conflicts:

- `409` if order is already `processing` or in another non-cancellable status
- `404` if pending trade does not belong to caller/strategy

### `POST /cancel-all-pending-trades`

Cancel all cancellable pending trades for one strategy.  
`set-target-portfolio` now performs this queue reset automatically by default, so use this endpoint when you want to clear the queue explicitly without submitting a new target portfolio.

Auth:

- API key or ID token

Request body:

| Field | Type | Required | Notes |
|---|---|---|---|
| `strategy_name` | string | Yes | Strategy name to clear |
| `reason` | string | No | Optional cancellation note applied to all cancelled rows |

Success (`200`):

```json
{
  "message": "Cancelled 3 pending trade(s) for strategy 'Sandbox Strategy'.",
  "strategy_name": "Sandbox Strategy",
  "matched_pending_trades": 5,
  "cancelled_count": 3,
  "already_cancelled_count": 1,
  "processing_count": 1,
  "not_cancellable_count": 0,
  "failed_count": 0
}
```

### 6.5 Market Data Utilities

### `GET|POST /symbol-search`

Lookup symbols across providers.

Auth:

- Required
- Send either:
  - `X-Trader-Api-Key: <API_KEY>`
  - `Authorization: Bearer <FIREBASE_ID_TOKEN>`

Inputs:

| Field | Type | Required | Notes |
|---|---|---|---|
| `q` | string | Yes | query text |
| `provider` | string | No | `yahoo-finance2`, `alpha-vantage`, `twelve-data`, `coinbase` |
| `limit` | number | No | default `10`, max `25` |

Success (`200`):

```json
{
  "query": "AAPL",
  "count": 1,
  "provider_used": "twelve-data",
  "attempts": [
    { "provider": "twelve-data", "status": "success" }
  ],
  "results": [
    {
      "symbol": "AAPL",
      "name": "Apple Inc",
      "exchange": "NASDAQ",
      "type": "Common Stock",
      "provider": "twelve-data"
    }
  ]
}
```

If all providers fail: `502` with provider attempt details.

### `GET|POST /fetch-price` (alias: `/fetchPrice`)

Get last/current price and market state.

Auth:

- Required
- Send either:
  - `X-Trader-Api-Key: <API_KEY>`
  - `Authorization: Bearer <FIREBASE_ID_TOKEN>`

Inputs:

| Field | Type | Required | Notes |
|---|---|---|---|
| `symbol` | string | Yes | e.g. `AAPL` |
| `provider` | string | No | provider hint |

When market is closed, endpoint can still return `200` with last available price and note.

Provider routing notes:

- Crypto symbol normalization for Coinbase:
  - `BTC/USD` is normalized to `BTC-USD`
  - bare symbols like `BTC` are normalized to `BTC-USD`
- Crypto symbols are routed to Coinbase first, then fallback providers.

## 7) cURL Quickstart

### 1. Set portfolio targets (primary)

```bash
curl -X POST "https://tradelock.net/api/set-target-portfolio" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "strategy": "Sandbox Strategy",
    "targets": {
      "SPY": 33,
      "TLT": 33,
      "EEM": 10,
      "GSY": 15
    },
    "execution_anchor": "next_us_equity_open",
    "open_delay_minutes": 2,
    "idempotency_key": "signal-portfolio-20260308-001"
  }'
```

### 2. Buy by quantity

```bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "trade_type": "buy",
    "quantity": 10,
    "idempotency_key": "signal-qty-buy-20260308-001"
  }'
```

### 3. Individual target allocation (single symbol)

```bash
curl -X POST "https://tradelock.net/api/logTrade" \
  -H "Content-Type: application/json" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>" \
  -d '{
    "user_strategy_name": "Sandbox Strategy",
    "asset": "AAPL:NASDAQ",
    "allocation_percent": 30,
    "idempotency_key": "signal-single-target-20260308-001"
  }'
```

### 4. Poll status

```bash
curl "https://tradelock.net/api/trade-status?strategyName=Sandbox%20Strategy&tradeId=<TRADE_OR_PENDING_ID>" \
  -H "X-Trader-Api-Key: <YOUR_SANDBOX_API_KEY>"
```

## 8) Integration Checklist

1. Use one API key per integration (webhook, backend service, ETL).
2. Always send `idempotency_key` on write requests.
3. Use `validate-trade` before enabling production automation.
4. Alert on repeated `4xx` responses.
5. Poll `trade-status` for queued trades.
6. Separate sandbox and production strategies/keys.

## 9) Changelog

- **0.6 (2026-04-22)**
- Added `trade_hide_days` field to `createStrategy` and `update-strategy`. Controls how many days of recent trades are hidden from public viewers (`0`, `30`, `60`, `90`, `9999`).
- Added `min_rebalance_move_pct` field to `createStrategy` and `update-strategy`. Legs in `set-target-portfolio` whose notional move is below this % of running capital are skipped. Default `1`.
- Documented drift filter response fields (`drift_filtered`, `drift_pct`) in `set-target-portfolio`.

- **0.5 (2026-03-17)**
- Documented `set-target-portfolio` preflight cancellation of existing pending trades.
- Documented `cancel_pending_trades_first=false` opt-out and `preflight_pending_trade_reset` response field.
- Clarified request-level replay behavior for `set-target-portfolio` idempotency retries.

- **0.4 (2026-03-08)**
- Added `POST /cancel-all-pending-trades` for strategy-scoped queue reset.
- Documented bulk-cancel response counters (`cancelled_count`, `processing_count`, etc.).

- **0.3 (2026-03-08)**
- Added clear recommended submission order with `set-target-portfolio` as primary.
- Added copy-paste sandbox defaults (`Sandbox Strategy`, `<YOUR_SANDBOX_API_KEY>`).
- Clarified three manager submission modes: full portfolio targets, quantity buy/sell, and single-symbol target allocation.

- **0.2 (2026-02-27)**
- Rewrote guide into endpoint-reference format.
- Corrected strategy endpoint requirements and status codes.
- Corrected auth requirements for utility endpoints.
- Added API key lifecycle endpoints and sandbox scope behavior.
- Clarified sizing modes, idempotency behavior, and queue semantics.

- **0.1 (2026-02-27)**
- Initial manager-focused API guide.
