Skip to content

tradingmachine.orders.plan_parts.from_fill_pricing

The from_fill pricing rule of a plan: an exit placed a distance from the price its position was opened at.

UBI prices the exit from the average fill of the orders that opened the position, in the direction that suits the side it is sent on, so one setting suits a position opened either way. When both sides of a two-sided entry filled, only the fills on the side the position is held on count, so a short opened at 990 and partly bought back at 1010 keeps its exits measured from 990. With stop_distance the exit is a native stop-limit that far beyond the fill against the position, with its limit stop_limit_offset further on; with target_distance it is a limit that far beyond the fill in the position's favour. Give the stop pair or the target, not both. Prices are rounded to the tick, and nothing is sent until the opening order has filled. The order must sit under a ThenPart's each_fill or on_complete child, or UBI refuses the plan with from_fill_needs_then.

Typical usage example:

pricing = from_fill_pricing.FromFillPricing( stop_distance=10.0, stop_limit_offset=1.0, ) document = pricing.document()

FromFillPricing

FromFillPricing(*, stop_distance=None, stop_limit_offset=None, target_distance=None)

Bases: PlanPart

A pricing rule for an exit set a distance from the fill that opened its position.

Attributes:

Name Type Description
stop_distance

The float distance in rupees from the fill to the stop's trigger, or None for a target.

stop_limit_offset

The float distance in rupees past the stop's trigger to its limit, or None for a target.

target_distance

The float distance in rupees from the fill to the target's limit, or None for a stop.

Initialises the rule as a stop or as a target.

Parameters:

Name Type Description Default
stop_distance float | None

The float distance in rupees from the fill to the stop's trigger, given with stop_limit_offset, or None for a target.

None
stop_limit_offset float | None

The float distance in rupees past the stop's trigger to its limit, given with stop_distance, or None for a target.

None
target_distance float | None

The float distance in rupees from the fill to the target's limit, or None for a stop.

None
Source code in src/tradingmachine/orders/plan_parts/from_fill_pricing.py
def __init__(
    self,
    *,
    stop_distance: float | None = None,
    stop_limit_offset: float | None = None,
    target_distance: float | None = None,
):
    """Initialises the rule as a stop or as a target.

    Args:
        stop_distance: The float distance in rupees from the fill to the stop's trigger, given with `stop_limit_offset`, or None for a target.
        stop_limit_offset: The float distance in rupees past the stop's trigger to its limit, given with `stop_distance`, or None for a target.
        target_distance: The float distance in rupees from the fill to the target's limit, or None for a stop.

    Raises:
        Nothing.
    """
    self.stop_distance = stop_distance
    self.stop_limit_offset = stop_limit_offset
    self.target_distance = target_distance

document

document()

Builds the from_fill pricing object UBI reads, holding every setting that is not None.

Returns:

Type Description
dict

A dict with the single key from_fill, whose value holds stop_distance, stop_limit_offset and target_distance when each is set.

Examples:

Print a stop ten rupees from the fill with its limit one rupee past the trigger:

from tradingmachine.orders.plan_parts import from_fill_pricing

pricing = from_fill_pricing.FromFillPricing(
    stop_distance=10.0,
    stop_limit_offset=1.0,
)
print(pricing.document())

Print an entry followed by a stop and a target measured from its fill, which share the position so a fill on one reduces the other:

from tradingmachine.orders.plan_parts import either_part
from tradingmachine.orders.plan_parts import from_fill_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import then_part

part = then_part.ThenPart(
    first=order_part.OrderPart(),
    each_fill=either_part.EitherPart(
        children=[
            order_part.OrderPart(
                side="protect",
                pricing=from_fill_pricing.FromFillPricing(
                    stop_distance=10.0,
                    stop_limit_offset=1.0,
                ),
            ),
            order_part.OrderPart(
                side="protect",
                pricing=from_fill_pricing.FromFillPricing(
                    target_distance=20.0,
                ),
            ),
        ],
        sibling_rule="reduce",
    ),
)
print(part.document())
Source code in src/tradingmachine/orders/plan_parts/from_fill_pricing.py
def document(self) -> dict:
    """Builds the `from_fill` pricing object UBI reads, holding every setting that is not None.

    Returns:
        A dict with the single key `from_fill`, whose value holds `stop_distance`, `stop_limit_offset` and `target_distance` when each is set.

    Raises:
        Nothing.

    Examples:
        Print a stop ten rupees from the fill with its limit one rupee past the trigger:

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

        pricing = from_fill_pricing.FromFillPricing(
            stop_distance=10.0,
            stop_limit_offset=1.0,
        )
        print(pricing.document())
        ```

        Print an entry followed by a stop and a target measured from its fill, which share the position so a fill on one reduces the other:

        ```python
        from tradingmachine.orders.plan_parts import either_part
        from tradingmachine.orders.plan_parts import from_fill_pricing
        from tradingmachine.orders.plan_parts import order_part
        from tradingmachine.orders.plan_parts import then_part

        part = then_part.ThenPart(
            first=order_part.OrderPart(),
            each_fill=either_part.EitherPart(
                children=[
                    order_part.OrderPart(
                        side="protect",
                        pricing=from_fill_pricing.FromFillPricing(
                            stop_distance=10.0,
                            stop_limit_offset=1.0,
                        ),
                    ),
                    order_part.OrderPart(
                        side="protect",
                        pricing=from_fill_pricing.FromFillPricing(
                            target_distance=20.0,
                        ),
                    ),
                ],
                sibling_rule="reduce",
            ),
        )
        print(part.document())
        ```
    """
    settings = {}
    if self.stop_distance is not None:
        settings["stop_distance"] = self.stop_distance
    if self.stop_limit_offset is not None:
        settings["stop_limit_offset"] = self.stop_limit_offset
    if self.target_distance is not None:
        settings["target_distance"] = self.target_distance
    return {
        "from_fill": settings,
    }

Example programs

Build a dip entry followed by a stop and a target measured from wherever the entry fills

Run it with .venv/bin/python examples/orders/plan_parts/from_fill_pricing/from_fill_pricing/stop_and_target_after_a_dip_entry.py.

"""Build a dip entry followed by a stop and a target measured from wherever the entry fills.

The program reads Vodafone Idea's last price and builds a Then join: a buy that waits for the price to fall 1%, and on each of its fills an Either join of a stop 2% of the price below the fill and a target 4% above it, which share the position so a fill on one reduces the other. The exits need no absolute prices, because UBI measures them from the entry's average fill. It prints the join's object as UBI would read it. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/from_fill_pricing/from_fill_pricing/stop_and_target_after_a_dip_entry.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import either_part
from tradingmachine.orders.plan_parts import from_fill_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import price_crosses
from tradingmachine.orders.plan_parts import then_part


class DipEntryWithExits:
    """A dip buy of Vodafone Idea with a stop and a target measured from its 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}")
        tick_size = 0.05
        if self.share.tick_size is not None:
            tick_size = float(self.share.tick_size)
        stop = order_part.OrderPart(
            side="protect",
            pricing=from_fill_pricing.FromFillPricing(
                stop_distance=round(last_price * 0.02, 2),
                stop_limit_offset=tick_size,
            ),
        )
        target = order_part.OrderPart(
            side="protect",
            pricing=from_fill_pricing.FromFillPricing(
                target_distance=round(last_price * 0.04, 2),
            ),
        )
        part = then_part.ThenPart(
            first=order_part.OrderPart(
                trigger=price_crosses.PriceCrosses(
                    level=round(last_price * 0.99, 2),
                ),
            ),
            each_fill=either_part.EitherPart(
                children=[
                    stop,
                    target,
                ],
                sibling_rule="reduce",
            ),
        )
        print(f"Last price of {self.share.symbol}: {last_price}")
        print(json.dumps(part.document(), indent=2))


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

Build an entry whose single target is sent only once the whole entry has filled, measured from its average fill

Run it with .venv/bin/python examples/orders/plan_parts/from_fill_pricing/from_fill_pricing/target_once_the_entry_completes.py.

"""Build an entry whose single target is sent only once the whole entry has filled, measured from its average fill.

The program builds a Then join for Vodafone Idea with `on_complete`, so the target waits until the buy is done, and prices the target half a rupee above the entry's average fill. The same exit works unchanged after a sale, where UBI places it below the fill instead. It prints the join's object as UBI would read it. Nothing is sent to UBI's order routes.

Typical usage example:

  .venv/bin/python examples/orders/plan_parts/from_fill_pricing/from_fill_pricing/target_once_the_entry_completes.py
"""

import json

from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import from_fill_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import then_part


class TargetAfterCompletion:
    """A buy of Vodafone Idea followed by a target once it has filled completely.

    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:
            Nothing.
        """
        part = then_part.ThenPart(
            first=order_part.OrderPart(
                instrument=self.share,
                transaction_type="buy",
                quantity=10,
            ),
            on_complete=order_part.OrderPart(
                side="protect",
                pricing=from_fill_pricing.FromFillPricing(target_distance=0.5),
            ),
        )
        print(f"Buy of {self.share.symbol} with a target half a rupee up:")
        print(json.dumps(part.document(), indent=2))


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