> ## Documentation Index
> Fetch the complete documentation index at: https://takeprofit.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Strategy mechanics

This article describes how strategies execute — in both backtesting and live trading — and will help you write more accurate and reliable strategies.

## Backtesting vs live trading

Strategies run in two modes from the **same code**:

* **Backtesting** (including forward-testing on real-time bars) runs against a built-in **exchange emulator** that simulates order fills, fees, and margin on historical and live bars.
* **Live trading** sends real orders to a real exchange and reacts to real fills.

Most behavior is identical by design. But because one side is simulated and the other is a real venue, a few things necessarily differ. Use this section as the reference for what is the same, what is simulated only, and what is live only.

### Identical in both modes (strategy behavior)

These belong to the strategy itself, so they behave the same whether you backtest or trade live:

* **Order lifecycle and statuses** — the states an order goes through (see [Statuses and lifecycle](/docs/indie/Strategies/Orders#statuses-and-lifecycle)).
* **Execution delays** — placing, updating, or canceling an order takes effect on the next tick, never instantly (see [Order execution](#order-execution) below).
* **`intrabar_order_filter`** — `NO_FILTER` / `ON_BAR_CLOSE` / `FIRST_IN_BAR` / `LAST_IN_BAR` gate order placements **and** cancels/amends identically (see [Order filtering](#order-filtering-intrabar_order_filter-parameter)).
* **Capital, leverage, and margin** — the strategy keeps its **own books** on `initial_capital` and `leverage`. A **margin call** (cannot open or increase the position) and a **stop-out** (all orders canceled, position force-closed, strategy halted) can happen to the strategy based on its own equity, independently of the state of your overall exchange account (see [Margin trading](#margin-trading-leverage-parameter)).

### Simulated only (backtesting)

The emulator has to invent things that a real exchange decides for you:

* **Fill prices and timing** are simulated against each bar's OHLC (limit/stop trigger logic, intrabar replay). In live trading, fills are real, **asynchronous** events from the exchange, so the exact fill price and the tick on which a fill arrives will differ from the simulation.
* **`market_order_price`** (`MARKET_PRICE` / `ORDER_CREATION_PRICE` / `BAR_OPEN_PRICE`) affects **backtesting only** — it forces the simulated fill price. In live trading the fill price is whatever the exchange gives you and cannot be forced.
* **Commissions** use the `commission` model you configure. In live trading the **real exchange fees** from each execution apply instead, so cash and equity evolve slightly differently even for identical fills.

### Live only (real exchange)

* **Order execution is the exchange's job.** Fills, partial fills, and the triggering of stop/limit and take-profit/stop-loss orders follow the real exchange's rules and latency, not the emulator's.
* **Take-profit / stop-loss** are attached to the order and sent to the exchange. The position-level TP/SL behavior described in [Take-profit and stop-loss orders](/docs/indie/Strategies/Orders#take-profit-and-stop-loss-orders) is how the **emulator** models it (matching Bybit); other exchanges may handle brackets differently.

<Note>
  Live trading runs the same strategy code as backtesting, but a backtest is an **approximation** of live performance, not a guarantee. The differences above — simulated vs real fill prices, the configured commission vs real exchange fees, and exchange-side execution and latency — mean a live run can diverge from its backtest.
</Note>

## Order execution

When trading on a real exchange, your actions cannot be executed instantly. It takes time for a request to reach the exchange, for the order to be added to the order book, for it to be executed, and for the execution notification to return to you. When writing and backtesting strategies, traders often overlook these delays and fail to account for them. As a result, a strategy may behave very differently in live trading compared to backtesting.

To address this, our backtesting engine simulates these real-world delays. This means that actions in your code do not take effect immediately. Instead, they are applied with a delay, and the results of those actions become visible only on the next price tick, when the strategy is recalculated.

The following examples illustrate how these delays affect strategy behavior in different scenarios:

### Example 1: Order submission delay

When you create an order, it is not filled immediately. The order must first be sent to the exchange, accepted, and then executed. This example demonstrates that you cannot see the filled status immediately after calling `submit()`:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional, IndieError
from indie.strategies import order_side, Order, order_status

@strategy('Order Submission Delay')
class Main(MainStrategyContext):
    def __init__(self):
        self._order: Optional[Order] = None
        self._step = 0

    def calc(self):
        if self._step == 0:
            # Create an order
            self._order = self.trading.place_order(order_side.BUY, size=1).submit()
            # Check: order should NOT be filled immediately
            if self._order.value().status == order_status.FILLED:
                raise IndieError('Order should not be filled immediately after creation')
            self._step = 1
        elif self._step == 1:
            # On the next calc call, the order should be filled
            if self._order.value().status != order_status.FILLED:
                raise IndieError('Order should be filled by now')
            self._step = 0
```

### Example 2: Order update delay

When you update an order's parameters (e.g., limit price), the changes are not applied immediately. This example shows that the order properties reflect the update only on the next `calc()` call:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional, IndieError
from indie.strategies import order_side, Order, order_status

@strategy('Order Update Delay')
class Main(MainStrategyContext):
    def __init__(self):
        self._order: Optional[Order] = None
        self._step = 0

    def calc(self):
        if self._step == 0:
            # Create an order with a limit price that won't execute
            self._order = (
                self.trading.place_order(order_side.SELL, size=1).
                limit(self.high[0] * 10).
                submit()
            )
            self._step = 1
        elif self._step == 1:
            # Update the limit price to a value that will execute
            new_limit = self.low[0] / 10
            (
                self.trading.amend_order(self._order.value().id).
                limit(new_limit).
                submit()
            )
            # Check: the limit should NOT be updated immediately
            if self._order.value().limit.value() == new_limit:
                raise IndieError('Order cannot be updated immediately')
            self._step = 2
        elif self._step == 2:
            # On the next calc call, the new limit should be applied and order filled
            if self._order.value().limit.value() >= self.high[0]:
                raise IndieError('Order limit was not updated correctly')
            if self._order.value().status != order_status.FILLED:
                raise IndieError('Order should be filled by now')
            self._step = 0
```

### Example 3: Order cancellation delay

When you cancel an order, it is not canceled immediately. The cancellation request must be sent to the exchange and processed. This example demonstrates the delay:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional, IndieError
from indie.strategies import order_side, Order, order_status

@strategy('Order Cancellation Delay')
class Main(MainStrategyContext):
    def __init__(self):
        self._order: Optional[Order] = None
        self._step = 0

    def calc(self):
        if self._step == 0:
            # Create an order with a limit price that won't execute
            self._order = (
                self.trading.place_order(order_side.SELL, size=1).
                limit(self.high[0] * 10).
                submit()
            )
            self._step = 1
        elif self._step == 1:
            # Cancel the order
            self.trading.cancel_order(self._order.value().id)
            # Check: order should NOT be canceled immediately
            if self._order.value().status == order_status.CANCELED:
                raise IndieError('Order should not be cancelled immediately')
            self._step = 2
        elif self._step == 2:
            # On the next calc call, the order should be canceled
            if self._order.value().status != order_status.CANCELED:
                raise IndieError('Order should be cancelled by now')
            self._step = 0
```

### Example 4: Immediate update after creation

When you create and immediately update an order in the same `calc()` call, the system applies the update locally before sending the order to the exchange. This means the order is sent with the updated parameters:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional, IndieError
from indie.strategies import order_side, Order, order_status

@strategy('Immediate Update After Creation')
class Main(MainStrategyContext):
    def __init__(self):
        self._order: Optional[Order] = None
        self._step = 0

    def calc(self):
        if self._step == 0:
            # Create an order with a high limit price
            self._order = (
                self.trading.place_order(order_side.SELL, size=1).
                limit(self.high[0] * 10).
                submit()
            )
            # Immediately update it to a price that will execute
            new_limit = self.low[0] / 10
            (
                self.trading.amend_order(self._order.value().id).
                limit(new_limit).
                submit()
            )
            self._step = 1
        elif self._step == 1:
            # On the next calc call, the order should have the updated limit and be filled
            # NOTE: When you create and modify an order in the same calc call,
            # the update is applied locally before the order is sent to the exchange.
            # The exchange receives the order with the already updated parameters.
            if self._order.value().limit.value() >= self.high[0]:
                raise IndieError('Order limit was not updated correctly')
            if self._order.value().status != order_status.FILLED:
                raise IndieError('Order should be filled by now')
            self._step = 0
```

**Key takeaway**: Always account for execution delays when writing strategies. Order operations (creation, updates, cancellations) take effect on the next `calc()` call, not immediately. This behavior ensures your strategy works more consistently in both backtesting and live trading environments.

## Fail-safe order operations

Some operations performed by a strategy — such as canceling or updating an order — are handled safely. This means the strategy will not fail if you call cancel or amend on an order that has already been canceled or executed. In such cases, the invalid operation is simply ignored. This simplifies your code and removes the need for additional if statements in situations where you want to "cancel an order if it hasn't been executed or canceled yet" or "update an order if it's still active".

The example below demonstrates a strategy that places sell limit orders and then cancels or updates them based on price movement — without checking the order status before each operation:

```py expandable theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional
from indie.strategies import order_side, Order, order_status


@strategy('Fail-Safe Order Operations', overlay_main_pane=True)
class Main(MainStrategyContext):
    def __init__(self):
        self._sell_order: Optional[Order] = None

    def calc(self):
        if self.close[0] < self.close[1]:  # price starts falling -> place sell order
            if self._sell_order is None or self._sell_order.value().status == order_status.FILLED:
                limit_price = self.close[0] * 0.99
                self._sell_order = (
                    self.trading.place_order(order_side.SELL, size=1).
                    limit(limit_price).
                    submit()
                )
        elif self.close[0] > self.close[3]:  # price rising during three bars -> cancel sell order
            if self._sell_order is not None:
                # Note: we don't check if the order was already filled or canceled
                # The system will safely ignore the cancellation if the order is no longer active
                self.trading.cancel_order(self._sell_order.value().id)
                self._sell_order = None
        elif self.close[0] > self.close[1]:  # short price correction -> we can sell at a better price -> update order
            if self._sell_order is not None:
                # Note: we don't check if the order was already filled
                # The system will safely ignore the amend operation if the order is no longer active
                new_limit_price = self.close[0] * 0.99
                (
                    self.trading.amend_order(self._sell_order.value().id).
                    limit(new_limit_price).
                    submit()
                )

```

**Key takeaway**: In this example, we call both `cancel_order()` and `amend_order()` without explicitly checking the order status beforehand. If the order has already been filled, canceled, or rejected, these operations will be safely ignored without throwing an exception. This simplifies your code by removing the need for defensive status checks.

## Order filtering (`intrabar_order_filter` parameter)

Inside a bar, the price moves. While the current candle is not finished, the strategy receives updates for the current candle. The strategy does not remember all intermediate values; only the latest, most actual value of the current candle is available in the strategy. If the logic of the strategy is based on comparing the current candle with the previous closed candle, then you may get false positive triggers. They can be avoided by additional checks in the code or by using our pre-configured filter `intrabar_order_filter`.

Possible values for [`intrabar_order_filter`](/docs/indie/Library-reference/package-indie-strategies#enum_intrabar_order_filter):

* [`intrabar_order_filter.NO_FILTER`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_NO_FILTER): No intrabar filtering is applied. Orders can be placed or modified at any price update within a candle.
* [`intrabar_order_filter.ON_BAR_CLOSE`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_ON_BAR_CLOSE): Orders can be placed or modified only at the close of the candle. Other submits of `place_order` will be rejected with the `REJECTED` status.
* [`intrabar_order_filter.FIRST_IN_BAR`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_FIRST_IN_BAR): Only the first batch of orders within a bar is accepted. Orders can be placed on any price update, but once a `calc()` call places orders, all orders from subsequent `calc()` calls within the same bar are ignored (rejected with the `REJECTED` status).
* [`intrabar_order_filter.LAST_IN_BAR`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_LAST_IN_BAR): Only the last batch of orders within a bar is applied. All orders placed during a `calc()` call are accepted, but if a new `calc()` call places orders, the previous batch is replaced by the new one. The final batch is applied when the next bar starts.

Let's look in more detail with an example. Suppose the price changes like this:

```plain theme={null}
    candle1    |    candle2     |    candle3     |
41.0 41.4 41.8 | 42.2 42.6 43.0 | 43.4 43.8 44.2 |

# 3 price updates during each candle
```

We want to place an order when the price crosses the 42.0 level:

```plain theme={null}
    candle1    |    candle2     |    candle3     |
41.0 41.4 41.8 | 42.2 42.6 43.0 | 43.4 43.8 44.2 |
                  ^
      42.2 > 42.0 and 41.8 < 42.0
          crossing detected,
            order placing
```

We want to write code like this:

```py theme={null}
# current price is above the level and previous candle closed below it
if self.close[0] > 42.0 and self.close[1] < 42.0:
    self.trading.place_order(...).submit()
```

But the strategy’s `Main` function is called on each price update, not only on bar close. Since we compare the current price to the previous bar’s close, this will result in extra triggers:

```plain theme={null}
    candle1    |                 candle2                  |    candle3     |
41.0 41.4 41.8 | 42.2              42.6              43.0 | 43.4 43.8 44.2 |
                  ^                 ^                 ^
             42.2 > 42.0       42.6 > 42.0       43.0 > 42.0
             41.8 < 42.0       41.8 < 42.0       41.8 < 42.0
           cross detected    cross detected    cross detected
```

Therefore, we need to add extra logic to prevent duplicate orders. Depending on the specific situation, different approaches may be appropriate.

### Manual trigger filtering: previous price comparison

We can compare it not with the previous bar’s price but with the last received tick price, if you store it in a variable:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Manual Filter - Previous Price Comparison', intrabar_order_filter=intrabar_order_filter.NO_FILTER)
class Main(MainStrategyContext):
    def __init__(self):
        self._prev_price = 0.0

    def calc(self):
        if self.bar_index > 0:
            if self._prev_price < 42.0 and self.close[0] > 42.0:
                self.trading.place_order(...).submit()
        self._prev_price = self.close[0]

```

### Manual trigger filtering: trigger at bar close

We can detect the crossing and place an order only at bar close:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Manual Filter - Bar Close Trigger', intrabar_order_filter=intrabar_order_filter.NO_FILTER)
def Main(self):
    if self.is_closed_bar:
        if self.close[1] < 42.0 and self.close[0] > 42.0:
            self.trading.place_order(...).submit()

```

### Manual trigger filtering: only first trigger during bar

We can ensure that only one order is placed per bar (not necessarily at bar close):

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Manual Filter - First Trigger Only', intrabar_order_filter=intrabar_order_filter.NO_FILTER)
class Main(MainStrategyContext):
    def __init__(self):
        self._last_order_time = 0.0

    def calc(self):
        if self._last_order_time < self.time[0] and self.close[1] < 42.0 and self.close[0] > 42.0:
            self.trading.place_order(...).submit()
            self._last_order_time = self.time[0]

```

But all of these approaches require additional logic in the code. To avoid writing extra checks, we introduced several pre-configured filters for controlling when orders are placed.

### [`intrabar_order_filter.ON_BAR_CLOSE`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_ON_BAR_CLOSE)

To place orders only at bar close:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Auto Filter - On Bar Close', intrabar_order_filter=intrabar_order_filter.ON_BAR_CLOSE)
def Main(self):
    # not necessary to manually check if it is a closed bar
    if self.close[1] < 42.0 and self.close[0] > 42.0:
        self.trading.place_order(...).submit()

```

### [`intrabar_order_filter.FIRST_IN_BAR`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_FIRST_IN_BAR)

Only the first batch of orders within a bar is accepted. Orders can be placed on any price update, but once a `calc()` call places orders, all orders from subsequent `calc()` calls within the same bar are ignored until the next bar. You can place multiple orders in the same `calc()` call and they will all go through. The same rules apply to `cancel_order` and `amend_order` — see [below](#how-filtering-applies-to-cancel_order-and-amend_order).

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Auto Filter - First In Bar', intrabar_order_filter=intrabar_order_filter.FIRST_IN_BAR)
def Main(self):
    # not necessary to store the last order time
    if self.close[1] < 42.0 and self.close[0] > 42.0:
        self.trading.place_order(...).submit()

```

### [`intrabar_order_filter.LAST_IN_BAR`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_LAST_IN_BAR)

Only the last batch of orders within a bar is applied. You can place multiple orders in the same `calc()` call and they will all be accepted, but if a new `calc()` call places orders, the entire previous batch is replaced by the new one. The final batch is applied when the next bar starts. The same rules apply to `cancel_order` and `amend_order` — see [below](#how-filtering-applies-to-cancel_order-and-amend_order).

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import intrabar_order_filter

@strategy('Auto Filter - Last In Bar', intrabar_order_filter=intrabar_order_filter.LAST_IN_BAR)
def Main(self):
    # not necessary to store the last order time
    if self.close[1] < 42.0 and self.close[0] > 42.0:
        self.trading.place_order(...).submit()

```

Note that to determine the last batch of orders within the bar, the emulator waits for the bar to close and then applies the final batch before placing orders on the next bar.

### [`intrabar_order_filter.NO_FILTER`](/docs/indie/Library-reference/package-indie-strategies#const_intrabar_order_filter_NO_FILTER)

Finally, if none of these filters fit your needs and you want full control over when orders are placed, you can use the `NO_FILTER` setting, as shown in the examples above.

### How filtering applies to `cancel_order` and `amend_order`

The `intrabar_order_filter` parameter controls not only `place_order`, but also `cancel_order` and `amend_order` commands. The filtering rules are the same as for order placement:

* **`NO_FILTER`**: `cancel_order` and `amend_order` are accepted on any price update and applied on the next tick.
* **`ON_BAR_CLOSE`**: `cancel_order` and `amend_order` calls are only accepted at bar close. Calls during intrabar ticks are silently ignored.
* **`FIRST_IN_BAR`**: The first batch of `cancel_order`/`amend_order` calls in a bar is accepted. You can issue multiple commands in the same `calc()` call and they will all go through. Commands from subsequent `calc()` calls within the same bar are ignored.
* **`LAST_IN_BAR`**: All `cancel_order`/`amend_order` calls within a `calc()` call are accepted as a batch. If a new `calc()` call issues commands, the entire previous batch is replaced. The final batch is applied when the next bar starts.

For example, with `FIRST_IN_BAR` you can cancel two orders in a single `calc()` call and both will be canceled. But if you cancel one order on the first price update and try to cancel another on the next update, the second cancel will be ignored:

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional
from indie.strategies import order_side, Order, order_status, intrabar_order_filter


@strategy('Cancel/Amend with FIRST_IN_BAR', intrabar_order_filter=intrabar_order_filter.FIRST_IN_BAR)
class Main(MainStrategyContext):
    def __init__(self):
        self._order_a: Optional[Order] = None
        self._order_b: Optional[Order] = None

    def calc(self):
        if self._order_a is not None and self._order_b is not None:
            # Both cancels are in the same calc() call — both will be accepted
            self.trading.cancel_order(self._order_a.value().id)
            self.trading.cancel_order(self._order_b.value().id)
```

With `LAST_IN_BAR`, if you cancel an order on one price update and then cancel a different order on the next update, the first cancel is replaced by the second — only the second cancel will be applied when the next bar starts.

## Price for market orders (`market_order_price` parameter)

<Note>
  This parameter affects **backtesting only**. It forces the simulated fill price of market orders. In live trading the fill price is set by the real exchange and cannot be forced.
</Note>

By default, market orders are executed at the price the instrument has at the moment of execution. Note that the execution time of an order does not match the moment you place it or the moment you call `place_order()` (see the [Order execution](#order-execution) section above). This behavior reflects real-world execution, but during backtesting traders sometimes want to force a specific execution price for testing purposes. You can configure which value will be used as the execution price for market orders.

There are three available options:

* [`market_order_price.MARKET_PRICE`](/docs/indie/Library-reference/package-indie-strategies#const_market_order_price_MARKET_PRICE): the actual market price at the time of execution (default)
* [`market_order_price.ORDER_CREATION_PRICE`](/docs/indie/Library-reference/package-indie-strategies#const_market_order_price_ORDER_CREATION_PRICE): the price at which the order was created
* [`market_order_price.BAR_OPEN_PRICE`](/docs/indie/Library-reference/package-indie-strategies#const_market_order_price_BAR_OPEN_PRICE): the opening price of the bar in which the order was created

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import market_order_price

@strategy('Market Order at Creation Price', market_order_price=market_order_price.ORDER_CREATION_PRICE)
def Main(self):
    pass

```

This setting applies to the entire strategy run. It cannot be changed at runtime, so all market orders within a single run use the same configuration.

### Simulation of trading at market close

The configuration described above can be useful when you want to simulate trading at market close using a price close to the bar’s closing value. To achieve this, you should place an order right before the session ends, using the bar’s closing price. This can be done by combining the `market_order_price` and `intrabar_order_filter` parameters.

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import order_side, market_order_price, intrabar_order_filter


@strategy('Trading at Market Close',
          intrabar_order_filter=intrabar_order_filter.ON_BAR_CLOSE,
          market_order_price=market_order_price.ORDER_CREATION_PRICE)
def Main(self):
    # just buying at the bar closing price
    self.trading.place_order(order_side.BUY, size=42.0).submit()

```

### Simulation of trading at market open

Similarly, by combining the `market_order_price` and `intrabar_order_filter` parameters, you can execute an order at the current bar’s opening price to simulate trading at market open.

```py theme={null}
# indie:lang_version = 5
from indie import strategy, MainStrategyContext
from indie.strategies import order_side, market_order_price, intrabar_order_filter


@strategy('Trading at Market Open',
          intrabar_order_filter=intrabar_order_filter.FIRST_IN_BAR,
          market_order_price=market_order_price.BAR_OPEN_PRICE)
def Main(self):
    # just buying at the bar opening price
    self.trading.place_order(order_side.BUY, size=42.0).submit()

```

## Margin trading (`leverage` parameter)

<Note>
  Margin is the strategy's **own accounting** on `initial_capital` and `leverage`, so it works the same in backtesting and live trading. A **margin call** (the strategy can no longer open or increase a position) and a **stop-out** (all its orders are canceled, the position is force-closed, and the strategy stops trading) can happen to the strategy from its own equity — even if your overall exchange account is fine.
</Note>

Margin trading allows traders to borrow funds to increase their buying power, enabling them to open positions larger than their available capital. This is achieved by leveraging the trader's initial capital as collateral. If the `leverage` parameter is greater than 1.0, the strategy can open positions larger than the available capital.

* **Leverage**: The ratio of the trader's position size to their own capital. For example, a leverage of 2.0 means the trader can open positions twice the size of their capital.
* **Initial margin**: The minimum amount of the trader's own funds required to open leveraged positions. If the portfolio value falls below the initial margin, the trader must reduce exposure. The trader cannot open new or increase existing uncovered positions.
* **Maintenance Margin**: The amount of collateral at which positions are forcibly closed. The maintenance margin is usually 1/2 of the initial margin.
* **Equity**: The trader’s total capital, including unrealized profits and losses, minus any liabilities.
* **Margin Call**: A warning issued when equity falls below the initial margin, requiring the trader to add funds or reduce exposure to avoid liquidation.
* **Stop Out**: The forced closure of positions when equity falls below the maintenance margin level.

Example Scenario: imagine a trader with \$10,000 in capital and a max allowed leverage of 3.0. The trader opens a position worth \$20,000 (\$10,000 of their own and \$10,000 provided by the broker on loan, current leverage is 2x). If the market moves against the trader and their equity falls below the initial margin requirement (e.g., \$5,000), they will receive a margin call. If their equity continues to decline and falls below the maintenance margin requirement (e.g., \$2,500), the broker will close the trader's position to limit losses (stop-out).

Risks of Margin Trading:

* **Amplified Losses**: While leverage can increase profits, it also magnifies losses.
* **Liquidation Risk**: Failure to meet margin requirements can result in the forced closure of positions.
* **Interest Costs**: Borrowed funds may accrue interest, adding to the cost of trading.

Using leverage works best when it’s part of a clear plan: knowing how much risk you're willing to take, how much the position can move against you, and when you're ready to exit.

Example of a strategy using leverage:

```py expandable theme={null}
# indie:lang_version = 5
from indie import strategy
from indie.strategies import order_side


@strategy(
    'Leverage Example', overlay_main_pane=True,
    initial_capital=100000.0, leverage=10.0)  # allow margin trading with 10x leverage
def Main(self):
    pos_size = self.trading.position.size
    if pos_size == 0:
        current_cash = self.trading.cash
        allowed_size_to_buy_with_own_money = current_cash / self.close[0]
        # we can buy more than our own money but not more than 10x
        order_size = allowed_size_to_buy_with_own_money * 2.0  # 2x leverage is OK, because 2 < 10

        # open the position with leverage
        self.trading.place_order(order_side.BUY, size=order_size).submit()
    else:
        # close the position
        self.trading.place_order(order_side.SELL, size=pos_size).submit()

```
