# Rule sets

> Define and version the criteria an account is judged against, and read the rule catalogue your tier unlocks.

Source: https://www.propexecutor.com/docs/rule-sets
Whole reference in one file: https://www.propexecutor.com/doc.md

A rule set is the criteria an account is judged by: daily drawdown, maximum drawdown, profit target, minimum trading days, plus whatever else you build from the rule catalogue. It is versioned, and versions are immutable.

## Versions are append-only

> **This is the single most important thing on this page**
> Editing a rule set **appends a new version**. It never changes an existing one, and there is no endpoint that can. A trading account freezes the version current at its creation and is judged by that version for the rest of its life.
> 
> So tightening your rules today cannot retroactively breach an account somebody bought last month. That is deliberate and not configurable — it is what makes a challenge you sold defensible.

## List rule sets

`GET /v1/rule-sets` · **Planned** · scope `accounts:read`

## Create a rule set

`POST /v1/rule-sets` · **Planned** · scope `accounts:write`

## List versions

`GET /v1/rule-sets/{id}/versions` · **Planned** · scope `accounts:read`

Newest first. Exactly one has `is_current: true` — that is the one a new account will freeze.

## Append a version

`POST /v1/rule-sets/{id}/versions` · **Planned** · scope `accounts:write`

<!-- curl -->
```bash
curl -X POST https://api.propexecutor.com/v1/rule-sets/7c4a…/versions \
  -H "Authorization: Bearer $PFX_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "daily_drawdown_pct": 5,
    "max_drawdown_pct": 10,
    "profit_target_pct": 8,
    "min_trading_days": 3,
    "allowed_instruments": ["EURUSD", "GBPUSD", "XAUUSD"],
    "rules": [
      { "key": "max_open_positions", "params": { "count": 3 }, "action": "reject_order" },
      { "key": "max_lots",           "params": { "lots": 5 },  "action": "reject_order" }
    ]
  }'
```

| Field | Type | Description |
| --- | --- | --- |
| `daily_drawdown_pct` | number \| null | Measured against the day's starting equity, UTC midnight. Null means no daily limit. |
| `max_drawdown_pct` | number \| null | Trailing, measured from the account's all-time equity high. |
| `profit_target_pct` | number \| null | Null is valid and means **the account never passes** — that is how instant-funding products are modelled. |
| `min_trading_days` | integer | Distinct days with at least one trade. 0 for none. |
| `allowed_instruments` | string[] | Empty means everything your feed carries. |
| `rules` | object[] | Typed rules from the catalogue. Each has a `key`, `params` and an `action` of `breach`, `reject_order` or `flag`. |

## What an action does

| action | Effect |
| --- | --- |
| `breach` | Ends the challenge the moment the rule fires. |
| `reject_order` | Refuses the order that would have violated it. Checked inside the transaction that writes the order, so it holds under concurrent orders. |
| `flag` | Records it for your review and lets trading continue. Read them from [rule flags](https://www.propexecutor.com/docs/accounts). |

## The rule catalogue

`GET /v1/rule-catalog` · **Planned** · scope `accounts:read`

Every rule you can build with, its parameters, and whether your plan tier unlocks it. Read this rather than hard-coding a list — it is the same source the rule builder in the panel renders from, and the server validates against it.

> **Time-based rules are not available**
> Rules that would need a clock rather than an event — a news blackout window, an inactivity timeout — are listed in the catalogue as unavailable and the API refuses them. Rules are evaluated on price ticks, fills and orders, and adding a scheduler to make a rule fire on a timer would put the breach decision behind a delay. That is the one thing the rule engine will not do.
