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
¶
Builds the top_up execution object UBI reads.
Returns:
| Type | Description |
|---|---|
dict
|
A dict with the single key |
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
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()