tradingmachine.orders.plan_parts.lifetime
¶
The lifetime of an order in a plan: when it stops, which part of its life that bounds, and what is done then.
A lifetime ends exactly one way: at_time, a time of day on the instrument's next trading day; after_minutes, counted from when the plan is placed; after_days, 1 to 365 days of 24 hours from then, which keeps the plan alive across trading days; or when, any trigger condition, checked on every tick. applies_to says whether the end bounds the wait for the trigger, the time the order works after it is sent, or both, which is UBI's default. An order still waiting when its end comes is done as expired. An order working then ends by on_end: cancel, UBI's default, cancels what rests and keeps what filled; marketable moves what rests two ticks past the other side's touch so it fills, which a stop cannot do; and close_filled cancels what rests and closes what filled at market, which only a plan that is this one order can do, and not an order that protects a position. A part ended by close_filled is done with the reason closed, or expired when nothing had filled.
Unlike the other parts, a lifetime is an entry of the order's lifetime list rather than a value under one key, so its document() holds the settings directly. A RepeatPart with until gives each copy a lifetime of its own, so an order repeated that way takes no Lifetime.
Typical usage example:
ending = lifetime.Lifetime(at_time="14:30", on_end="marketable") document = ending.document()
Lifetime
¶
Lifetime(*, at_time=None, after_minutes=None, after_days=None, when=None, applies_to=None, on_end=None)
Bases: PlanPart
When an order of a plan stops working, and what is done with it then.
Attributes:
| Name | Type | Description |
|---|---|---|
at_time |
The str time of day the order ends at, such as |
|
after_minutes |
The float minutes after placing that the order ends, or None. |
|
after_days |
The int days after placing that the order ends, from 1 to 365, or None. |
|
when |
The plan_part.PlanPart condition that ends the order when it holds, or None. |
|
applies_to |
The str part of the order's life the end bounds, |
|
on_end |
The str action taken on a working order, |
Initialises the lifetime with its end and settings.
Exactly one of at_time, after_minutes, after_days and when must be given; UBI refuses none or more than one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
at_time
|
str | None
|
The str time of day in India the order ends at, such as |
None
|
after_minutes
|
float | None
|
The float minutes after the plan is placed that the order ends, above zero, or None. UBI refuses minutes on a day the instrument does not trade. |
None
|
after_days
|
int | None
|
The int days of 24 hours after the plan is placed that the order ends, from 1 to 365, or None. |
None
|
when
|
PlanPart | None
|
A plan_part.PlanPart trigger condition, such as |
None
|
applies_to
|
str | None
|
The str part of the order's life the end bounds, |
None
|
on_end
|
str | None
|
The str action taken on an order still working, |
None
|
Source code in src/tradingmachine/orders/plan_parts/lifetime.py
document
¶
Builds the lifetime entry UBI reads, holding every setting that is not None.
Returns:
| Type | Description |
|---|---|
dict
|
A dict holding each setting that is set, with |
Examples:
Print a lifetime that ends at 14:30 and moves what still rests past the touch so it fills:
from tradingmachine.orders.plan_parts import lifetime
ending = lifetime.Lifetime(at_time="14:30", on_end="marketable")
print(ending.document())
Print an order whose wait ends when the price falls to 990 or when it is ten past three, whichever comes first:
from tradingmachine.orders.plan_parts import any_condition
from tradingmachine.orders.plan_parts import lifetime
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import price_crosses
from tradingmachine.orders.plan_parts import time_at
part = order_part.OrderPart(
trigger=price_crosses.PriceCrosses(level=1010.0),
lifetime=lifetime.Lifetime(
when=any_condition.AnyCondition(
[
price_crosses.PriceCrosses(
level=990.0,
direction="at_or_below",
),
time_at.TimeAt("15:10"),
]
),
applies_to="waiting",
on_end="cancel",
),
)
print(part.document())
Print an intraday entry that closes whatever filled thirty minutes after it is placed:
from tradingmachine.orders.plan_parts import lifetime
from tradingmachine.orders.plan_parts import order_part
part = order_part.OrderPart(
lifetime=lifetime.Lifetime(
after_minutes=30,
on_end="close_filled",
),
)
print(part.document())
Source code in src/tradingmachine/orders/plan_parts/lifetime.py
Example programs¶
Print one order ended each of the three ways UBI offers when its time comes¶
Run it with .venv/bin/python examples/orders/plan_parts/lifetime/lifetime/three_ways_to_end_an_order.py.
"""Print one order ended each of the three ways UBI offers when its time comes.
A limit entry can be cancelled at 14:30, made marketable at 14:30 so it fills, or have whatever filled closed at market thirty minutes after placing. The program builds the same limit entry at 995 with each lifetime and prints all three. Nothing is sent to UBI's order routes.
Typical usage example:
.venv/bin/python examples/orders/plan_parts/lifetime/lifetime/three_ways_to_end_an_order.py
"""
import json
from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import lifetime
from tradingmachine.orders.plan_parts import order_part
class ThreeEndings:
"""The same limit entry with three different lifetimes.
Attributes:
lifetimes: The dict of lifetime.Lifetime objects by the str name of how each ends.
"""
def __init__(self):
"""Builds the three lifetimes.
Raises:
Nothing.
"""
self.lifetimes = {
"cancel at 14:30": lifetime.Lifetime(at_time="14:30"),
"marketable at 14:30": lifetime.Lifetime(
at_time="14:30",
on_end="marketable",
),
"close what filled after 30 minutes": lifetime.Lifetime(
after_minutes=30,
on_end="close_filled",
),
}
def run(self) -> None:
"""Prints the entry's object with each lifetime.
Returns:
None.
Raises:
Nothing.
"""
for name, ending in self.lifetimes.items():
part = order_part.OrderPart(
pricing=fixed_pricing.FixedPricing(price=995.0),
lifetime=ending,
)
print(f"Ending by {name}:")
print(json.dumps(part.document(), indent=2))
if __name__ == "__main__":
ThreeEndings().run()
Build a dip entry whose wait ends if the index breaks down or the afternoon comes¶
Run it with .venv/bin/python examples/orders/plan_parts/lifetime/lifetime/wait_bounded_by_a_condition.py.
"""Build a dip entry whose wait ends if the index breaks down or the afternoon comes.
The program watches the NIFTY index and gives a dip entry on NSE IDEA a lifetime ending `when` either the index falls to 2 percent below its last value or it is 14:00, whichever comes first. The lifetime applies only to the wait, so an entry already sent is left to work. The market data read is read-only, and nothing is sent to UBI's order routes.
Typical usage example:
.venv/bin/python examples/orders/plan_parts/lifetime/lifetime/wait_bounded_by_a_condition.py
"""
import json
from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import any_condition
from tradingmachine.orders.plan_parts import lifetime
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import price_crosses
from tradingmachine.orders.plan_parts import time_at
class BoundedDipEntry:
"""A dip entry whose wait ends on an index breakdown or at a time.
Attributes:
share: The equities.Equity bought on the dip.
index: The equities.EquityIndex watched for a breakdown.
"""
def __init__(self):
"""Looks the share and the index up.
Raises:
Nothing.
"""
self.share = equities.Equity(exchange="nse", symbol="IDEA")
self.index = equities.EquityIndex(exchange="nse", symbol="NIFTY")
def run(self) -> None:
"""Prints the entry's object with the levels it uses.
Returns:
None.
Raises:
Nothing.
"""
entry_level = round(self.share.last_price * 0.98, 2)
breakdown_level = round(self.index.last_price * 0.98, 2)
ending = lifetime.Lifetime(
when=any_condition.AnyCondition(
[
price_crosses.PriceCrosses(
level=breakdown_level,
direction="at_or_below",
instrument=self.index,
),
time_at.TimeAt("14:00"),
]
),
applies_to="waiting",
)
part = order_part.OrderPart(
trigger=price_crosses.PriceCrosses(level=entry_level),
lifetime=ending,
)
print(
f"Entry at {entry_level}, given up if the index reaches {breakdown_level} or at 14:00:"
)
print(json.dumps(part.document(), indent=2))
if __name__ == "__main__":
BoundedDipEntry().run()