Skip to main content
Orders are the basic instructions a strategy sends to the exchange emulator to open, close, or modify positions. In Indie, an Order is a lightweight object that describes the desired side, size, and other order parameters. The results of order execution are reflected in its status. The trading interface provides special functions for managing orders: place_order, amend_order, and cancel_order. These order management methods implement the builder pattern and are used like this:
# Place order:
order = (
    self.trading.place_order(side=order_side.BUY, size=1.0).
    limit(price=120000).
    stop(price=119000.0).
    take_profit(stop=135000, limit=134000).
    stop_loss(stop=115000).
    submit()
)

# Update order:
self.trading.amend_order(order.id).stop_loss(114000).submit()

# Cancel order:
self.trading.cancel_order(order.id)
Note that cancel_order does not require an explicit submit call, as it has no settings. More detailed information about these methods can be found in library reference.

Order types, parameters, and execution logic

Market

A market order is an instruction from an investor to a broker to buy or sell shares, bonds, or other assets at the best price currently available in the market. For most investors and most situations, this is the standard way to place a trade. When the asset is highly liquid — such as a major large-cap stock or a widely traded ETF — there are typically many buyers and sellers active at any given moment. As a result, a market order is usually executed almost immediately (subject to processing delays), and the final price is generally very close to the most recent quote visible to the investor. This is the simplest type of order. To place one, you only need to specify the order’s direction and size:
  • side (indie.strategies.order_side): BUY or SELL;
  • size: positive absolute quantity of asset to buy or sell;
Example of a strategy using market orders:
# indie:lang_version = 5
from indie import strategy, param, Var
from indie.strategies import order_side


@strategy('Consecutive Up/Down Strategy', overlay_main_pane=True)
@param.int('consecutive_bars_up', default=3, title='Consecutive bars up', min=1)
@param.int('consecutive_bars_down', default=3, title='Consecutive bars down', min=1)
def Main(self, consecutive_bars_up, consecutive_bars_down):
    price = self.close
    order_size = 1

    ups = Var[int].new(init=0)
    downs = Var[int].new(init=0)
    if price[0] > price[1]:
        ups.set(ups.get() + 1)
    else:
        ups.set(0)
    if price[0] < price[1]:
        downs.set(downs.get() + 1)
    else:
        downs.set(0)

    pos_size = self.trading.position.size
    if ups.get() >= consecutive_bars_up and pos_size <= 0:
        # reverse previous position if exists or open a new one
        self.trading.place_order(order_side.BUY, size=(order_size + abs(pos_size))).submit()
    elif downs.get() >= consecutive_bars_down and pos_size >= 0:
        self.trading.place_order(order_side.SELL, size=(order_size + abs(pos_size))).submit()

Limit

Limit orders allow you to control the exact price at which you buy or sell a stock. A buy limit order sets the maximum price you’re willing to pay, while a sell limit order defines the minimum price you’re willing to accept. These orders are especially useful when trading:
  • Highly volatile stocks with large price swings
  • Low-liquid stocks with wide bid-ask spreads
  • Periods of market uncertainty or rapid price changes
  • Situations where achieving a specific price is more important than immediate execution
The key trade-off with limit orders is between price certainty and execution certainty: you are guaranteed not to pay more or sell for less than your chosen price, but the order may never fill if the market does not reach that level. To place a limit order, you must provide one additional parameter:
  • limit: limit price, the order will execute at this price or better;
Example of a strategy using limit orders:
# indie:lang_version = 5
from indie import strategy, MainStrategyContext, Optional
from indie.strategies import order_side, Order


@strategy('Limit order demo', overlay_main_pane=True)
class Main(MainStrategyContext):
    def __init__(self):
        self._order: Optional[Order] = None

    def calc(self):
        pos_size = self.trading.position.size
        if self.close[0] > self.open[0] and pos_size == 0 and self._order is None:
            self._order = (
                self.trading.place_order(order_side.BUY, size=1.0).
                limit(price=self.close[0] + self.info.tick_size).
                submit()
            )
        elif self.close[0] < self.open[0] and pos_size > 0:
            (
                self.trading.place_order(order_side.SELL, size=pos_size).
                limit(price=self.close[0] - self.info.tick_size).
                submit()
            )
            self._order = None

Stop

A stop order is used either to limit potential losses or to initiate a trade when the market reaches a specified price level. A buy stop order is typically used when a trader wants to buy an asset at a price higher than the current market value. Traders use buy stop orders when they expect the price to continue rising after breaking through a resistance level. A buy stop order allows traders to automatically open a long position once the price exceeds a specified level, potentially capturing further upward movement. A sell stop order is used when a trader wants to sell an asset at a price lower than the current market value. Traders typically use sell stop orders to protect against further losses if the price breaks below a support level. When triggered, a sell stop order automatically exits the position to reduce potential losses and manage risk. To create a stop order, you must specify the stop price:
  • stop: the stop (trigger) price. A buy stop triggers when the market reaches this price or higher; a sell stop triggers when the market reaches this price or lower.
  • direction (optional): the trigger direction for the stop price. Possible values:
    • trigger_direction.AUTO (default): the system automatically determines the trigger direction based on the current market price and the specified stop price. If the stop price is above the current market price, it triggers when the price rises to it; if below, it triggers when the price falls to it.
    • trigger_direction.RISES_TO: the stop order triggers when the market price rises to or above the stop price.
    • trigger_direction.FALLS_TO: the stop order triggers when the market price falls to or below the stop price.
Example of creating stop orders:
from indie.strategies import order_side, trigger_direction

# Buy stop: triggers when price rises to 120,000 (using AUTO direction)
order = (
    self.trading.place_order(order_side.BUY, size=1.0).
    stop(price=120000).
    submit()
)

# Sell stop: triggers when price falls to 115,000 (using AUTO direction)
order = (
    self.trading.place_order(order_side.SELL, size=1.0).
    stop(price=115000).
    submit()
)

# Buy stop with explicit RISES_TO direction
order = (
    self.trading.place_order(order_side.BUY, size=1.0).
    stop(price=120000, direction=trigger_direction.RISES_TO).
    submit()
)

# Sell stop with explicit FALLS_TO direction
order = (
    self.trading.place_order(order_side.SELL, size=1.0).
    stop(price=115000, direction=trigger_direction.FALLS_TO).
    submit()
)
For a more comprehensive example of working with stop orders, including order management and updates, see the built-in Momentum Strategy.

Stop-limit

A stop-limit order is a conditional order that combines the features of a stop order and a limit order. It gives traders more control over execution by allowing them to specify both a stop price and a limit price. When the market reaches the stop price, the order is activated and converted into a limit order, which will execute at the specified limit price or better. The stop price in a stop-limit order serves as an activation point or trigger. For a sell stop-limit order, the trigger activates when the market reaches or falls below the stop price. For a buy stop-limit order, it activates when the market reaches or rises above the stop price. To create a stop-limit order, you must specify both the stop and limit prices:
  • stop: the stop (trigger) price. A buy stop triggers at this price or higher; a sell stop triggers at this price or lower;
  • limit: the limit price. After triggering, the order will execute at this price or better;
Example of creating a stop-limit order:
# Buy stop-limit: triggers when price reaches 120,000, then executes at 120,500 or better
order = (
    self.trading.place_order(order_side.BUY, size=1.0).
    stop(price=120000).
    limit(price=120500).
    submit()
)

# Sell stop-limit: triggers when price falls to 115,000, then executes at 114,500 or better
order = (
    self.trading.place_order(order_side.SELL, size=1.0).
    stop(price=115000).
    limit(price=114500).
    submit()
)

Take-profit and stop-loss orders

Take-profit (TP) and stop-loss (SL) orders are risk management tools that help traders automatically close positions to secure profits or limit losses. These orders are attached to entry orders and execute when the specified price levels are reached. When you attach TP and SL orders to an entry order, they work together as a bracket (also known as OCO - One-Cancels-Other): when one of them executes, the other is automatically canceled. This ensures that only one exit occurs per position. Take-profit and stop-loss orders apply to the order quantity they’re attached to. This allows you to place multiple TP/SL orders for different portions of your position. When the trigger price of an individual TP or SL order is reached, the corresponding order quantity is closed via either a market or limit order, and the opposite order (SL or TP) is automatically canceled. Additionally, you can modify the TP/SL prices before the parent order is filled. TP and SL orders always have the opposite side compared to their parent order. For example:
  • A buy order is protected by a sell take-profit order and a sell stop-loss order.
  • A sell order is protected by a buy take-profit order and a buy stop-loss order.
You can attach both TP and SL together (creating a full bracket), or use only one of them for partial risk management. To create orders with take-profit and stop-loss, you must specify the TP and/or SL prices. These prices serve as the stop price for the exit orders that will be placed after the parent order is executed. Additionally, you can set a limit price for TP/SL orders, which converts them into stop-limit orders. The usage syntax looks like this:
order = (
    self.trading.place_order(side=order_side.BUY, size=1.0).
    limit(price=120000).  # parent order will have limit type
    take_profit(stop=135000, limit=134000).  # take-profit will have stop-limit type
    stop_loss(stop=115000).  # stop-loss will have stop type
    submit()
)
NOTE: Currently, take-profit and stop-loss orders are only supported for limit and stop-limit orders but not for market orders.

Statuses and lifecycle

The order statuses are:
  • CREATED — created locally but not yet sent to the exchange emulator;
  • PLACED — accepted by the exchange and entered the order book;
  • PENDING_PLACED — waiting for a trigger condition to be met and has not yet entered the order book;
  • PARTIALLY_FILLED — partially filled and remains open;
  • FILLED — fully executed;
  • PARTIALLY_FILLED_CLOSED — partially filled and then closed without full execution;
  • REJECTED — rejected by the exchange or by margin checks;
  • CANCELED — canceled by user.
Typical lifecycle for market and limit orders: Typical lifecycle for stop and stop-limit orders: