Skip to content

tradingmachine.orders.plan_parts.top_up_execution

The top_up execution of a plan: a new broker order for whatever a join's growing target is missing, never a resized one.

It is meant for an order sized by a join, such as the second order of a ThenPart under each_fill that grows with every fill of the first. Each time the target grows, including after earlier orders have filled, one new broker order is sent for the quantity neither traded nor resting, so every broker order keeps the price it was given and its place in the queue; this is how UBI's attached_hedge and legged_spread presets grow their second leg. A target that shrinks cuts resting orders, newest first. A cancelled or rejected order stops it until the target next grows, so an IOC order the exchange cancels is not sent again at once, and a rejection stops it for good. A caller's change to the quantity of one of its orders is kept rather than modified back.

It takes no settings and does not nest.

Typical usage example:

execution = top_up_execution.TopUpExecution() part = order_part.OrderPart(execution=execution) document = part.document()

TopUpExecution

Bases: PlanPart

An execution that sends one new broker order each time a join raises the order's target.

document

document()

Builds the top_up execution object UBI reads.

Returns:

Type Description
dict

A dict with the single key top_up, whose value is an empty dict, because the execution takes no settings.

Examples:

Print the execution on its own:

from tradingmachine.orders.plan_parts import top_up_execution

execution = top_up_execution.TopUpExecution()
print(execution.document())

Print an entry followed by an exit that grows with each fill, a new order being sent for every addition rather than the resting one being resized:

from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import then_part
from tradingmachine.orders.plan_parts import top_up_execution

plan = then_part.ThenPart(
    first=order_part.OrderPart(),
    each_fill=order_part.OrderPart(
        side="protect",
        pricing=fixed_pricing.FixedPricing(
            price=1020.0,
            order_type="LIMIT",
        ),
        execution=top_up_execution.TopUpExecution(),
    ),
)
print(plan.document())
Source code in src/tradingmachine/orders/plan_parts/top_up_execution.py
def document(self) -> dict:
    """Builds the `top_up` execution object UBI reads.

    Returns:
        A dict with the single key `top_up`, whose value is an empty dict, because the execution takes no settings.

    Raises:
        Nothing.

    Examples:
        Print the execution on its own:

        ```python
        from tradingmachine.orders.plan_parts import top_up_execution

        execution = top_up_execution.TopUpExecution()
        print(execution.document())
        ```

        Print an entry followed by an exit that grows with each fill, a new order being sent for every addition rather than the resting one being resized:

        ```python
        from tradingmachine.orders.plan_parts import fixed_pricing
        from tradingmachine.orders.plan_parts import order_part
        from tradingmachine.orders.plan_parts import then_part
        from tradingmachine.orders.plan_parts import top_up_execution

        plan = then_part.ThenPart(
            first=order_part.OrderPart(),
            each_fill=order_part.OrderPart(
                side="protect",
                pricing=fixed_pricing.FixedPricing(
                    price=1020.0,
                    order_type="LIMIT",
                ),
                execution=top_up_execution.TopUpExecution(),
            ),
        )
        print(plan.document())
        ```
    """
    return {
        "top_up": {},
    }

Example programs

Build a NIFTY calendar spread legged in, the far month sold for whatever the near month has bought

Run it with .venv/bin/python examples/orders/plan_parts/top_up_execution/top_up_execution/calendar_spread_legged_in.py.

"""Build a NIFTY calendar spread legged in, the far month sold for whatever the near month has bought.

The program finds the two nearest NIFTY futures contracts and builds a then join: a buy of two lots of the near month two ticks past the offer, followed under `each_fill` by a sell of the next month on the same terms. The second leg uses `TopUpExecution`, as UBI's `attached_hedge` and `legged_spread` presets do, so each fill of the first sends a new sell for what is missing rather than resizing one resting order. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/top_up_execution/top_up_execution/calendar_spread_legged_in.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import marketable_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import then_part
from tradingmachine.orders.plan_parts import top_up_execution


class LeggedCalendarSpread:
    """A near-month NIFTY buy whose far-month sell is topped up with each fill.

    Attributes:
        near_month: The tradingmachine.assets.equities.EquityIndexFutures for the nearest NIFTY future.
        far_month: The tradingmachine.assets.equities.EquityIndexFutures for the next NIFTY future.
    """

    def __init__(self):
        """Finds the two nearest contracts.

        Raises:
            tradingmachine.assets.exceptions.InstrumentError: A contract could not be found in UBI.
        """
        expiries = equities.EquityIndexFutures.expiries(
            exchange="nse",
            underlying_symbol="NIFTY",
        )
        self.near_month = equities.EquityIndexFutures(
            exchange="nse",
            underlying_symbol="NIFTY",
            expiry_date=expiries[0],
        )
        self.far_month = equities.EquityIndexFutures(
            exchange="nse",
            underlying_symbol="NIFTY",
            expiry_date=expiries[1],
        )

    def run(self) -> None:
        """Prints the join's object.

        Returns:
            None.

        Raises:
            Nothing.
        """
        lot_size = int(self.near_month.lot_size)
        join = then_part.ThenPart(
            first=order_part.OrderPart(
                instrument=self.near_month,
                transaction_type="buy",
                quantity=2 * lot_size,
                product="nrml",
                pricing=marketable_pricing.MarketablePricing(buffer_ticks=2),
            ),
            each_fill=order_part.OrderPart(
                instrument=self.far_month,
                transaction_type="sell",
                product="nrml",
                pricing=marketable_pricing.MarketablePricing(buffer_ticks=2),
                execution=top_up_execution.TopUpExecution(),
            ),
        )
        print(
            f"Buying the {self.near_month.expiry_date} future, selling the {self.far_month.expiry_date} future"
        )
        print(json.dumps(join.document(), indent=2))


if __name__ == "__main__":
    LeggedCalendarSpread().run()

Build an entry whose profit target grows with every fill by new orders rather than by resizing one

Run it with .venv/bin/python examples/orders/plan_parts/top_up_execution/top_up_execution/target_that_grows_with_each_fill.py.

"""Build an entry whose profit target grows with every fill by new orders rather than by resizing one.

The program reads Vodafone Idea's last price and builds a then join: a limit buy of 10000 shares one percent below that price, followed under `each_fill` by a target limit sell three percent above it. The target uses `TopUpExecution`, so each fill sends a new order for the shares not yet covered and every order keeps its place in the queue, where the default execution would change one order's size. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/top_up_execution/top_up_execution/target_that_grows_with_each_fill.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import then_part
from tradingmachine.orders.plan_parts import top_up_execution


class GrowingTarget:
    """A buy of Vodafone Idea whose target is topped up with each fill.

    Attributes:
        share: The tradingmachine.assets.equities.Equity for Vodafone Idea on the NSE.
    """

    def __init__(self):
        """Looks up the share.

        Raises:
            tradingmachine.assets.exceptions.InstrumentError: The share could not be found in UBI.
        """
        self.share = equities.Equity(exchange="nse", symbol="IDEA")

    def run(self) -> None:
        """Prints the join's object.

        Returns:
            None.

        Raises:
            ValueError: UBI has no last price for the share.
        """
        last_price = self.share.last_price
        if last_price is None:
            raise ValueError(f"UBI has no last price for {self.share!r}")
        entry_price = round(last_price * 0.99, 2)
        target_price = round(last_price * 1.03, 2)
        join = then_part.ThenPart(
            first=order_part.OrderPart(
                instrument=self.share,
                transaction_type="buy",
                quantity=10000,
                product="cnc",
                pricing=fixed_pricing.FixedPricing(
                    price=entry_price,
                    order_type="LIMIT",
                ),
            ),
            each_fill=order_part.OrderPart(
                side="protect",
                pricing=fixed_pricing.FixedPricing(
                    price=target_price,
                    order_type="LIMIT",
                ),
                execution=top_up_execution.TopUpExecution(),
            ),
        )
        print(f"Last price of {self.share.symbol}: {last_price}")
        print(json.dumps(join.document(), indent=2))


if __name__ == "__main__":
    GrowingTarget().run()