# Orders, positions, and trades



An **Order** is a request; a **Trade** is one filled lot; **Position** summarizes all open lots. Obtain them from `self.buy/sell`, `self.trades`, and `self.position`. Do not construct these handles yourself.

[Order](https://docs.openbacktesting.com/reference/trading/#openbacktesting-engine-order) · [Position](https://docs.openbacktesting.com/reference/trading/#openbacktesting-engine-position) · [Trade](https://docs.openbacktesting.com/reference/trading/#openbacktesting-engine-trade) · [OrderRequest](https://docs.openbacktesting.com/reference/trading/#openbacktesting-broker-orderrequest) · [OrderGroup](https://docs.openbacktesting.com/reference/trading/#openbacktesting-broker-ordergroup) · [OrderStatus](https://docs.openbacktesting.com/reference/trading/#openbacktesting-broker-orderstatus) · [Commission](https://docs.openbacktesting.com/reference/trading/#openbacktesting-broker-commission) · [Slippage](https://docs.openbacktesting.com/reference/trading/#openbacktesting-broker-slippage) · [fixed_quantity](https://docs.openbacktesting.com/reference/trading/#openbacktesting-strategy-sizing-fixed-quantity) · [equity_fraction](https://docs.openbacktesting.com/reference/trading/#openbacktesting-strategy-sizing-equity-fraction) · [stop_risk](https://docs.openbacktesting.com/reference/trading/#openbacktesting-strategy-sizing-stop-risk) · [SizingDecision](https://docs.openbacktesting.com/reference/trading/#openbacktesting-strategy-sizing-sizingdecision)

## Submit an order

Place these calls inside `Strategy.next()`:

```python
price = float(self.close[-1])
order = self.buy(qty=1, limit=price * 0.99, sl=price * 0.95,
                 tp=price * 1.05, valid_bars=3, tag="pullback")
self.log.info(f"Submitted {order.order_id}: {order.status}")
```

| Argument to `buy()` / `sell()` | Meaning |
| --- | --- |
| `qty` | Positive asset units; must match the instrument quantity step. `qty=0.1` means one tenth of a unit. |
| `amount` | Positive notional budget in settlement currency, excluding fees. |
| `percent` | Percentage of request-time equity in `(0, 100]`; `20` means 20%. |
| `limit` | Absolute limit price: a buy cannot execute above it; a sell cannot execute below it. |
| `stop` | Absolute trigger price. With `limit`, creates a stop-limit order. |
| `sl`, `tp` | Absolute stop-loss and take-profit prices attached to the filled lot. For a long entry, SL is below entry and TP above it; reversed for shorts. |
| `tag` | Optional JSON-compatible label/data copied into trading records. |
| `reduce_only` | Only reduce existing exposure; requires explicit `qty`. |
| `valid_bars` | Allow execution on the next `n` bars, then expire; positive integer. |
| `expires_at` | Alternative timezone-aware expiry timestamp; the data index must also be aware. |

Choose exactly one sizing argument, or omit all to use a configured default. `None` is not a sizing value. `valid_bars` and `expires_at` cannot be combined. Without an expiry, an order remains active until filled, canceled, rejected, or the run ends.

Amounts and percentages convert to quantity using Close/equity at submission. They do not reserve cash; gaps and fees may make an order fail at execution. A returned handle confirms submission, not execution. The default timing fills eligible market orders at the next open.

## Cancel, exit, or change protection

```python
# Inside next(): iterate over a snapshot while canceling.
for order in self.orders:
    if order.tag == "pullback":
        order.cancel()

for trade in self.trades:
    if trade.is_long:
        trade.sl = float(self.close[-1]) * 0.95
        trade.tp = None  # Remove take-profit protection.

# Submit exits for every open lot.
exits = self.position.close()
```

`Trade.close(qty=1)` exits an explicit quantity. `Trade.close(portion=0.5)` exits half, rounded toward zero to the quantity step; rounding to zero raises an error. Omit both to exit all. `Position.close(portion=...)` and `self.close_position(portion=...)` return tuples of exit orders, empty when flat. They submit requests, so exposure remains until execution.

For a native trailing stop, call `trade.set_trailing_stop(amount=5)` or `trade.set_trailing_stop(percent=0.05)`; **here 0.05 means 5%**. Choose one positive distance. Favorable extremes update protection for subsequent bars; a stop does not guarantee the exit price after a gap.

## Target a position

| Method | Example and unit |
| --- | --- |
| `self.order_target_quantity(target=10)` | Target net holdings of 10 units. |
| `self.order_target_value(target=2000)` | Target net notional of 2,000 currency units. |
| `self.order_target_percent(target=0.2)` | Target net notional of **20%** of equity; this method uses a fraction, unlike `buy(percent=20)`. |

Targets may be negative for short exposure or zero to exit. They support the same price/expiry/tag conditions as ordinary orders, return `Order` or `None`, and require `hedging=False`. Identical pending targets are reused; changing a target replaces it based on actual holdings. Other pending entry/manual-exit orders conflict with target orders. Target weight is calculated at submission and is not continuously rebalanced.

## Submit one-cancels-other entries

```python
from openbacktesting.broker import OrderRequest

def next(self):
    if self.bar_index == 0:
        price = float(self.close[-1])
        self.entry_group = self.submit_oco(
            first=OrderRequest(side="buy", qty=1, stop=price * 1.02),
            second=OrderRequest(side="sell", qty=1, stop=price * 0.98),
        )
```

Use this `next()` in a strategy with `init()` defined. `OrderRequest` requires `side="buy"/"sell"` and one explicit `qty`/`amount`/`percent`. Its other arguments follow the ordinary-order table. Submitting the group validates both entries together. Completion or cancellation of either terminates its peer; if both trigger within a bar, `first` takes priority. `group.orders` reads the pair and `group.cancel()` cancels pending members.

## Interpret exposure and profit

`position.size` is signed net quantity. With hedging, equal long/short holdings have zero net size but still have exposure: use `bool(position)` or `position.has_exposure`. `position.snapshot` separates gross/long/short quantities and their average entry prices. `Trade.pl` and `Position.pl` include the remaining entry fees; `return_fraction` uses fractional units and may be `None` for flat or hedged aggregate exposure.

`self.account` gives balance, equity, margin, and cumulative fees. Balance includes realized gross P/L minus paid fees; equity adds unrealized gross P/L. `self.closed_trade_stats` counts closed fragments, so a partial exit can increase its count while a lot remains open.

## Costs

```python
from openbacktesting.broker import Commission, Slippage

commission = Commission(fixed=1, relative=0.001, per_unit=0.01)
slippage = Slippage(mode="bps", value=5)
```

Pass these to `Backtest(..., commission=commission, slippage=slippage)`. Commission adds a fixed fee, a fraction of absolute notional, and a fee per executed unit. Slippage adds either basis points (`bps`) or a fixed price distance (`fixed`) in the adverse direction. Fees apply on each actual execution, including exits.

OHLC bars cannot reveal intrabar price order. If a bar reaches both SL and TP, the framework chooses SL and records an ambiguity. Protection from a conditional or same-close entry starts on a later bar. See [execution rules](https://docs.openbacktesting.com/guides/trading/index.md) for timing and end-of-data behavior.

<a id="openbacktesting-engine-order"></a>



## Order

Stable order handle; request and snapshot are immutable values, and cancel is repeatable.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-engine-order-is-contingent"></a>`is_contingent` | Whether this is a lot protection exit. |
| <a id="openbacktesting-engine-order-is-long"></a>`is_long` | Whether this is a buy-side request. |
| <a id="openbacktesting-engine-order-is-short"></a>`is_short` | Whether this is a sell-side request. |
| <a id="openbacktesting-engine-order-limit"></a>`limit` | Immutable requested limit price. |
| <a id="openbacktesting-engine-order-parent-trade"></a>`parent_trade` | Associated active lot; None after closure. |
| <a id="openbacktesting-engine-order-size"></a>`size` | Original signed asset quantity. |
| <a id="openbacktesting-engine-order-sl"></a>`sl` | Initial requested stop-loss price. |
| <a id="openbacktesting-engine-order-stop"></a>`stop` | Currently effective trigger price; the original condition remains in request. |
| <a id="openbacktesting-engine-order-tag"></a>`tag` | Independent plain-value copy of the user tag. |
| <a id="openbacktesting-engine-order-tp"></a>`tp` | Initial requested take-profit price. |
| <a id="openbacktesting-engine-order-status"></a>`status` | Current state; query events for historical states. |
| <a id="openbacktesting-engine-order-request"></a>`request` | Return a frozen NormalizedOrderRequest with readable side, qty, limit, stop, sl, tp, reduce_only, trade_id, expires_at, valid_bars, tag_json, and tag. tag returns an independent JSON copy. This object observes a registered order; authors use OrderRequest to describe an entry awaiting submission. |
| <a id="openbacktesting-engine-order-snapshot"></a>`snapshot` | Latest frozen state, without exposing writable state. |
| <a id="openbacktesting-engine-order-group"></a>`group` | Associated OCO or protection group; None for ordinary standalone orders. |
| <a id="openbacktesting-engine-order-order-id"></a>`order_id` | Stable, read-only order identity within each run. |
| <a id="openbacktesting-engine-order-sizing"></a>`sizing` | Sizing basis frozen at submission; None for targets, lot exits, and internal protection orders. |

<a id="openbacktesting-engine-order-cancel"></a>



### Order.cancel

```python
Order.cancel()
```

Cancel an unfinished order; calls on terminal states are idempotent.

<a id="openbacktesting-engine-position"></a>



## Position

Aggregate active lots; truthiness depends on total exposure.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-engine-position-is-long"></a>`is_long` | Whether the net position is long. |
| <a id="openbacktesting-engine-position-is-short"></a>`is_short` | Whether the net position is short. |
| <a id="openbacktesting-engine-position-pl"></a>`pl` | Unrealized net profit or loss including remaining entry fees. |
| <a id="openbacktesting-engine-position-size"></a>`size` | Signed net quantity. |
| <a id="openbacktesting-engine-position-has-exposure"></a>`has_exposure` | Whether any lot is still held. |
| <a id="openbacktesting-engine-position-snapshot"></a>`snapshot` | Frozen snapshot of net position, gross exposure, and fees. |
| <a id="openbacktesting-engine-position-return-fraction"></a>`return_fraction` | Net return fraction; None when a hedged or flat position has no common cost basis. |

<a id="openbacktesting-engine-position-close"></a>



### Position.close

```python
Position.close(portion=None)
```

Submit reduce-only orders for each lot and return an ordered tuple.

<a id="openbacktesting-engine-trade"></a>



## Trade

Entry lot that can be exited and protected separately; partial exits preserve trade_id.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-engine-trade-entry-time"></a>`entry_time` | Market-data index value at entry (a timestamp when the data has a DatetimeIndex). |
| <a id="openbacktesting-engine-trade-exit-time"></a>`exit_time` | Market-data index value at exit, or None while open. |
| <a id="openbacktesting-engine-trade-is-long"></a>`is_long` | Whether this is a long lot. |
| <a id="openbacktesting-engine-trade-is-short"></a>`is_short` | Whether this is a short lot. |
| <a id="openbacktesting-engine-trade-pl"></a>`pl` | Net profit or loss including allocated fees; active lots include remaining entry fees. |
| <a id="openbacktesting-engine-trade-size"></a>`size` | Signed quantity of the remaining or closed portion. |
| <a id="openbacktesting-engine-trade-sl"></a>`sl` | Current stop-loss price; writes record cancellation and replacement events. |
| <a id="openbacktesting-engine-trade-tag"></a>`tag` | Copy of the entry tag. |
| <a id="openbacktesting-engine-trade-tp"></a>`tp` | Current take-profit price; None cancels protection. |
| <a id="openbacktesting-engine-trade-value"></a>`value` | Absolute notional value at the current or exit price. |
| <a id="openbacktesting-engine-trade-pl-gross"></a>`pl_gross` | Gross price profit or loss. |
| <a id="openbacktesting-engine-trade-return-fraction"></a>`return_fraction` | Net return fraction using remaining or allocated entry notional as the denominator. |
| <a id="openbacktesting-engine-trade-group"></a>`group` | Lot protection group, including historical replaced members. |

<a id="openbacktesting-engine-trade-close"></a>



### Trade.close

```python
Trade.close(portion=None, *, qty=None)
```

Reduce-only exit; qty and portion are mutually exclusive, and the default closes the entire lot.

<a id="openbacktesting-engine-trade-set-trailing-stop"></a>



### Trade.set_trailing_stop

```python
Trade.set_trailing_stop(*, amount=None, percent=None)
```

Create a native trailing stop, replacing the fixed SL; effective from the next eligible bar.

<a id="openbacktesting-engine-trade-entry-bar"></a>



**`entry_bar`** — Zero-based bar position at entry.

<a id="openbacktesting-engine-trade-entry-price"></a>



**`entry_price`** — Actual entry price.

<a id="openbacktesting-engine-trade-exit-bar"></a>



**`exit_bar`** — Zero-based exit bar, or None while the lot is open.

<a id="openbacktesting-engine-trade-exit-price"></a>



**`exit_price`** — Actual exit price, or None while the lot is open.

<a id="openbacktesting-broker-orderrequest"></a>



## OrderRequest

```python
broker.OrderRequest(
    *,
    side,
    qty=...,
    amount=...,
    percent=...,
    limit=None,
    stop=None,
    sl=None,
    tp=None,
    tag=None,
    expires_at=None,
    valid_bars=None,
)
```

Frozen entry request awaiting submission; construction does not register an order and is allowed during initialization.

| Attribute | Type | Meaning |
| --- | --- | --- |
| <a id="openbacktesting-broker-orderrequest-side"></a>`side` | `Literal['buy', 'sell']` | Buy or sell direction. |
| <a id="openbacktesting-broker-orderrequest-limit"></a>`limit` | `float \| None` | Absolute limit price, or None for no limit. |
| <a id="openbacktesting-broker-orderrequest-stop"></a>`stop` | `float \| None` | Absolute stop trigger, or None for no trigger. |
| <a id="openbacktesting-broker-orderrequest-sl"></a>`sl` | `float \| None` | Absolute stop-loss price, or None. |
| <a id="openbacktesting-broker-orderrequest-tp"></a>`tp` | `float \| None` | Absolute take-profit price, or None. |
| <a id="openbacktesting-broker-orderrequest-expires-at"></a>`expires_at` | `pd.Timestamp \| None` | Timezone-aware expiry instant, or None. |
| <a id="openbacktesting-broker-orderrequest-valid-bars"></a>`valid_bars` | `int \| None` | Maximum eligible future bars, or None for no bar expiry. |

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-broker-orderrequest-tag"></a>`tag` | Independent JSON tag copy; modifying it does not change the request. |
| <a id="openbacktesting-broker-orderrequest-qty"></a>`qty` | Explicit asset quantity; None in other modes. |
| <a id="openbacktesting-broker-orderrequest-amount"></a>`amount` | Explicit notional amount; converted at submission, None in other modes. |
| <a id="openbacktesting-broker-orderrequest-percent"></a>`percent` | Explicit equity percentage (20 means 20%); None in other modes. |

<a id="openbacktesting-broker-ordergroup"></a>



## OrderGroup

Queryable OCO or lot protection group; cancellation does not close positions.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-broker-ordergroup-orders"></a>`orders` | All orders in this group, including historical terminal orders. |

<a id="openbacktesting-broker-ordergroup-cancel"></a>



### OrderGroup.cancel

```python
OrderGroup.cancel()
```

Cancel all still-active members without automatically exiting positions.

<a id="openbacktesting-broker-orderstatus"></a>



## OrderStatus

Observable order states; terminal states cannot reopen.

| Member | Value |
| --- | --- |
| `OrderStatus.SUBMITTED` | `Submitted` |
| `OrderStatus.ACCEPTED` | `Accepted` |
| `OrderStatus.PARTIALLY_FILLED` | `PartiallyFilled` |
| `OrderStatus.FILLED` | `Filled` |
| `OrderStatus.CANCELED` | `Canceled` |
| `OrderStatus.EXPIRED` | `Expired` |
| `OrderStatus.REJECTED` | `Rejected` |

<a id="openbacktesting-broker-orderstatus-submitted"></a>


<a id="openbacktesting-broker-orderstatus-accepted"></a>


<a id="openbacktesting-broker-orderstatus-partially-filled"></a>


<a id="openbacktesting-broker-orderstatus-filled"></a>


<a id="openbacktesting-broker-orderstatus-canceled"></a>


<a id="openbacktesting-broker-orderstatus-expired"></a>


<a id="openbacktesting-broker-orderstatus-rejected"></a>



`order.status.terminal` is True after a fill, cancellation, expiry, or rejection. Terminal orders cannot reopen; `order.cancel()` is safe to repeat.

<a id="openbacktesting-broker-commission"></a>



## Commission

```python
broker.Commission(fixed=0.0, relative=0.0, per_unit=0.0)
```

Fixed fee per fill, notional rate, and fee per asset unit; a rate of 0.002 means 0.2%.

| Attribute | Type | Meaning |
| --- | --- | --- |
| <a id="openbacktesting-broker-commission-fixed"></a>`fixed` | `float` | Fixed fee per actual fill. |
| <a id="openbacktesting-broker-commission-relative"></a>`relative` | `float` | Fraction of execution notional; 0.002 means 0.2%. |
| <a id="openbacktesting-broker-commission-per-unit"></a>`per_unit` | `float` | Fee per executed asset unit. |

<a id="openbacktesting-broker-commission-snapshot"></a>



### Commission.snapshot

```python
Commission.snapshot()
```

Export independent plain values with a model version.

<a id="openbacktesting-broker-commission-from-snapshot"></a>



### Commission.from_snapshot

```python
Commission.from_snapshot(value)
```

Restore a complete snapshot strictly; reject unknown versions, fields, and implicit defaults.

<a id="openbacktesting-broker-slippage"></a>



## Slippage

```python
broker.Slippage(mode='none', value=0)
```

No slippage, fixed-price slippage, or basis-point slippage; apply once per fill.

| Attribute | Type | Meaning |
| --- | --- | --- |
| <a id="openbacktesting-broker-slippage-mode"></a>`mode` | `str` | none, fixed price distance, or bps (basis points). |
| <a id="openbacktesting-broker-slippage-value"></a>`value` | `float` | Nonnegative slippage amount in the selected mode. |

<a id="openbacktesting-broker-slippage-cost"></a>



### Slippage.cost

```python
Slippage.cost(price)
```

Return a nonnegative slippage distance per unit.

## Size an order from a budget or stop

The pure helpers in `openbacktesting.strategy.sizing` return a `SizingDecision`. Supply `reference_price`, usually `float(self.close[-1])`, and `instrument=self.instrument`. Budget-based helpers also take `account=self.account`. Submit only when `decision.qty > 0`; `decision.reason` explains a zero result.

```python
from openbacktesting.strategy.sizing import stop_risk

def next(self):
    if self.bar_index == 0:
        price = float(self.close[-1])
        stop = price * 0.95
        decision = stop_risk(0.01, account=self.account, reference_price=price,
                             stop_price=stop, side="buy", instrument=self.instrument)
        if decision.qty > 0:
            self.buy(qty=decision.qty, sl=stop)
```

`fixed_quantity(qty, ...)` rounds units toward zero; `equity_fraction(fraction, ...)` spends an equity fraction; `stop_risk(fraction, ..., stop_price, side)` budgets an equity fraction for the stop distance. `cost_per_unit` is your nonnegative estimate of costs, not a guarantee of actual fees or gap losses.

<a id="openbacktesting-strategy-sizing-fixed-quantity"></a>



## fixed_quantity

```python
strategy.sizing.fixed_quantity(qty, *, reference_price, instrument=None, cost_per_unit=0)
```

Round fixed asset quantities toward zero without inferring capital fractions.

<a id="openbacktesting-strategy-sizing-equity-fraction"></a>



## equity_fraction

```python
strategy.sizing.equity_fraction(
    fraction,
    *,
    account,
    reference_price,
    instrument=None,
    cost_per_unit=0,
)
```

Equity net-notional budget, with quantity costs charged to the same budget; margin fraction does not amplify it.

<a id="openbacktesting-strategy-sizing-stop-risk"></a>



## stop_risk

```python
strategy.sizing.stop_risk(
    fraction,
    *,
    account,
    reference_price,
    stop_price,
    side='buy',
    instrument=None,
    cost_per_unit=0,
)
```

Stop-loss risk budgets include estimated round-trip costs per unit; gaps may exceed the planned loss.

<a id="openbacktesting-strategy-sizing-sizingdecision"></a>



## SizingDecision

Budget result; estimated_cost estimates costs for the given quantity without guaranteeing fill prices.

| Attribute | Type | Meaning |
| --- | --- | --- |
| <a id="openbacktesting-strategy-sizing-sizingdecision-qty"></a>`qty` | `float` | Asset quantity in native units. |
| <a id="openbacktesting-strategy-sizing-sizingdecision-reference-price"></a>`reference_price` | `float` | Price used to calculate the requested quantity. |
| <a id="openbacktesting-strategy-sizing-sizingdecision-estimated-cost"></a>`estimated_cost` | `float` | Estimated costs for the calculated quantity. |
| <a id="openbacktesting-strategy-sizing-sizingdecision-reason"></a>`reason` | `str \| None` | Reason for the recorded decision or termination. |
| <a id="openbacktesting-strategy-sizing-sizingdecision-budget"></a>`budget` | `float \| None` | Notional or risk budget; None for a pure quantity request. |

## Account values and event callbacks

Read `self.account` for balance/equity/margin, `self.position.snapshot` for exposure, and `self.closed_trade_stats` for running closed-fragment statistics. [Account snapshots and events](https://docs.openbacktesting.com/reference/events/index.md) explains every returned field and shows `on_order`, `on_fill`, and `on_finish` in use.

[Strategy](https://docs.openbacktesting.com/reference/strategy/index.md) · [Results](https://docs.openbacktesting.com/guides/results/index.md) · [User manual](https://docs.openbacktesting.com/index.md)
