Skip to content

tradingmachine.orders.plan_parts.iceberg_execution

The iceberg execution of a plan: only part of the order shown at a time.

The order is sent as pieces of visible_quantity, the next only once the last has filled. Each piece may vary by up to randomise_percent either way, worked out from the parent's id and the number of pieces sent, so another trader cannot spot a repeating size; UBI's default is 0, no variation, and a varied piece is brought to the nearest whole number of lots, at least one. A piece that is cancelled or rejected rather than filled stops the iceberg.

An iceberg can nest on either side. As the outer execution it releases its pieces as slices for an inner one to work, and as the inner execution it shows each slice of a TwapExecution, VwapExecution, FrontLoadedExecution, ParticipationExecution or another iceberg a little at a time.

Typical usage example:

execution = iceberg_execution.IcebergExecution(visible_quantity=10) part = order_part.OrderPart(execution=execution) document = part.document()

IcebergExecution

IcebergExecution(*, visible_quantity, randomise_percent=None)

Bases: PlanPart

An execution that shows a small piece of the order at a time and sends the next once it has filled.

Attributes:

Name Type Description
visible_quantity

The int size of each piece before any variation, at least 1.

randomise_percent

The int percentage, 0 to 99, by which a piece may vary either way, or None for UBI's default of 0.

Initialises the execution with its piece size.

Parameters:

Name Type Description Default
visible_quantity int

The int size of each piece before any variation, at least 1.

required
randomise_percent int | None

The int percentage, 0 to 99, by which a piece may vary either way, or None for UBI's default of 0.

None
Source code in src/tradingmachine/orders/plan_parts/iceberg_execution.py
def __init__(
    self,
    *,
    visible_quantity: int,
    randomise_percent: int | None = None,
):
    """Initialises the execution with its piece size.

    Args:
        visible_quantity: The int size of each piece before any variation, at least 1.
        randomise_percent: The int percentage, 0 to 99, by which a piece may vary either way, or None for UBI's default of 0.

    Raises:
        Nothing.
    """
    self.visible_quantity = visible_quantity
    self.randomise_percent = randomise_percent

document

document()

Builds the iceberg execution object UBI reads.

Returns:

Type Description
dict

A dict with the single key iceberg, whose value holds visible_quantity and, when it is set, randomise_percent.

Examples:

Print an iceberg that shows ten units at a time:

from tradingmachine.orders.plan_parts import iceberg_execution

execution = iceberg_execution.IcebergExecution(visible_quantity=10)
print(execution.document())

Print an iceberg whose pieces vary by up to a fifth either way:

from tradingmachine.orders.plan_parts import iceberg_execution

execution = iceberg_execution.IcebergExecution(
    visible_quantity=50,
    randomise_percent=20,
)
print(execution.document())

Print an order sent as six TWAP slices over an hour, each slice shown as an iceberg of ten:

from tradingmachine.orders.plan_parts import iceberg_execution
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import twap_execution

part = order_part.OrderPart(
    execution=twap_execution.TwapExecution(
        slices=6,
        over_minutes=60,
    ),
    inner_execution=iceberg_execution.IcebergExecution(
        visible_quantity=10,
    ),
)
print(part.document())
Source code in src/tradingmachine/orders/plan_parts/iceberg_execution.py
def document(self) -> dict:
    """Builds the `iceberg` execution object UBI reads.

    Returns:
        A dict with the single key `iceberg`, whose value holds `visible_quantity` and, when it is set, `randomise_percent`.

    Raises:
        Nothing.

    Examples:
        Print an iceberg that shows ten units at a time:

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

        execution = iceberg_execution.IcebergExecution(visible_quantity=10)
        print(execution.document())
        ```

        Print an iceberg whose pieces vary by up to a fifth either way:

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

        execution = iceberg_execution.IcebergExecution(
            visible_quantity=50,
            randomise_percent=20,
        )
        print(execution.document())
        ```

        Print an order sent as six TWAP slices over an hour, each slice shown as an iceberg of ten:

        ```python
        from tradingmachine.orders.plan_parts import iceberg_execution
        from tradingmachine.orders.plan_parts import order_part
        from tradingmachine.orders.plan_parts import twap_execution

        part = order_part.OrderPart(
            execution=twap_execution.TwapExecution(
                slices=6,
                over_minutes=60,
            ),
            inner_execution=iceberg_execution.IcebergExecution(
                visible_quantity=10,
            ),
        )
        print(part.document())
        ```
    """
    settings = {}
    settings["visible_quantity"] = self.visible_quantity
    if self.randomise_percent is not None:
        settings["randomise_percent"] = self.randomise_percent
    return {
        "iceberg": settings,
    }

Example programs

Build icebergs that rest a little below the market, with fixed and with varied piece sizes

Run it with .venv/bin/python examples/orders/plan_parts/iceberg_execution/iceberg_execution/hidden_size_below_the_market.py.

"""Build icebergs that rest a little below the market, with fixed and with varied piece sizes.

The program reads Vodafone Idea's last price and tick size and builds two orders for 2000 shares limited two ticks below the last price, one showing 200 at a time and one whose pieces vary by up to a quarter either way so other traders cannot spot a repeating size. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/iceberg_execution/iceberg_execution/hidden_size_below_the_market.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import iceberg_execution
from tradingmachine.orders.plan_parts import order_part


class HiddenSizeBelowTheMarket:
    """Two icebergs for Vodafone Idea, one regular and one randomised.

    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 both orders' objects.

        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}")
        tick_size = 0.01
        if self.share.tick_size is not None:
            tick_size = float(self.share.tick_size)
        limit_price = round(last_price - 2 * tick_size, 2)
        executions = {
            "200 at a time": iceberg_execution.IcebergExecution(
                visible_quantity=200,
            ),
            "about 200 at a time, varied by up to 25%": iceberg_execution.IcebergExecution(
                visible_quantity=200,
                randomise_percent=25,
            ),
        }
        print(f"Last price of {self.share.symbol}: {last_price}")
        for description, execution in executions.items():
            part = order_part.OrderPart(
                instrument=self.share,
                transaction_type="buy",
                quantity=2000,
                product="cnc",
                pricing=fixed_pricing.FixedPricing(
                    price=limit_price,
                    order_type="LIMIT",
                ),
                execution=execution,
            )
            print(f"An iceberg showing {description}:")
            print(json.dumps(part.document(), indent=2))


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

Build a large buy of Vodafone Idea sent as TWAP slices, each shown as an iceberg

Run it with .venv/bin/python examples/orders/plan_parts/iceberg_execution/iceberg_execution/twap_slices_shown_as_icebergs.py.

"""Build a large buy of Vodafone Idea sent as TWAP slices, each shown as an iceberg.

The program reads Vodafone Idea's last price and builds an order for 6000 shares, limited one percent above that price, sent as six TWAP slices over an hour with each slice of 1000 shown 250 at a time and varied by up to a tenth. This is a nested execution: the `TwapExecution` passed as `execution` splits the order into slices, and the `IcebergExecution` passed as `inner_execution` works each slice. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/iceberg_execution/iceberg_execution/twap_slices_shown_as_icebergs.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import iceberg_execution
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import twap_execution


class TwapOfIcebergs:
    """A buy of Vodafone Idea worked as TWAP slices, each slice an iceberg.

    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 order'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}")
        limit_price = round(last_price * 1.01, 2)
        part = order_part.OrderPart(
            instrument=self.share,
            transaction_type="buy",
            quantity=6000,
            product="cnc",
            pricing=fixed_pricing.FixedPricing(
                price=limit_price,
                order_type="LIMIT",
            ),
            execution=twap_execution.TwapExecution(
                slices=6,
                over_minutes=60,
            ),
            inner_execution=iceberg_execution.IcebergExecution(
                visible_quantity=250,
                randomise_percent=10,
            ),
        )
        print(f"Last price of {self.share.symbol}: {last_price}")
        print(json.dumps(part.document(), indent=2))


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