tradingmachine.orders.plan_parts.sequence_part
¶
The sequence join of a plan: two to twenty-five plans run one after another.
Each child starts only once the one before it is done, whether it filled or ended, so a sequence is how a plan waits for one order to finish before the next is even considered. A sequence join cannot be a then join's child, because that child is sized to the first plan's fills.
Typical usage example:
part = sequence_part.SequencePart( children=[ order_part.OrderPart(trigger=time_at.TimeAt("10:00")), order_part.OrderPart(trigger=time_at.TimeAt("14:00")), ], ) document = part.document()
SequencePart
¶
Bases: PlanPart
Several plans run one after another, each starting once the one before is done.
Attributes:
| Name | Type | Description |
|---|---|---|
children |
The list of plan_part.PlanPart nodes, in the order they run. |
Initialises the join with its children.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
children
|
Sequence[PlanPart]
|
A sequence of two to twenty-five plan_part.PlanPart nodes, each an |
required |
Source code in src/tradingmachine/orders/plan_parts/sequence_part.py
document
¶
Builds the sequence node UBI reads.
Returns:
| Type | Description |
|---|---|
dict
|
A dict with the single key |
Examples:
Print two buys where the second is considered only once the first is done:
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import price_crosses
from tradingmachine.orders.plan_parts import sequence_part
part = sequence_part.SequencePart(
children=[
order_part.OrderPart(
trigger=price_crosses.PriceCrosses(level=995.0),
),
order_part.OrderPart(
trigger=price_crosses.PriceCrosses(level=990.0),
),
],
)
print(part.document())
Print an entry with its protecting stop, followed once both are done by a second entry later in the day:
from tradingmachine.orders.plan_parts import native_stop_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import sequence_part
from tradingmachine.orders.plan_parts import then_part
from tradingmachine.orders.plan_parts import time_at
part = sequence_part.SequencePart(
children=[
then_part.ThenPart(
first=order_part.OrderPart(),
each_fill=order_part.OrderPart(
side="protect",
pricing=native_stop_pricing.NativeStopPricing(
trigger_price=990.0,
limit_price=988.0,
),
),
),
order_part.OrderPart(trigger=time_at.TimeAt("14:00")),
],
)
print(part.document())
Source code in src/tradingmachine/orders/plan_parts/sequence_part.py
Example programs¶
Build a protected trade in Vodafone Idea followed, once it is over, by a second entry after two in the afternoon¶
Run it with .venv/bin/python examples/orders/plan_parts/sequence_part/sequence_part/protected_trade_then_afternoon_reentry.py.
"""Build a protected trade in Vodafone Idea followed, once it is over, by a second entry after two in the afternoon.
The first child of the sequence is a then join: a buy whose every fill is protected by a stop and a target that reduce each other. The second child, a buy at two in the afternoon, is only considered once the whole first trade is done, which shows that a sequence can hold joins as well as orders. It prints the plan. Nothing is sent to UBI's order routes.
Typical usage example:
.venv/bin/python examples/orders/plan_parts/sequence_part/sequence_part/protected_trade_then_afternoon_reentry.py
"""
import json
from tradingmachine.assets import equities
from tradingmachine.orders.plan_parts import either_part
from tradingmachine.orders.plan_parts import fixed_pricing
from tradingmachine.orders.plan_parts import marketable_pricing
from tradingmachine.orders.plan_parts import native_stop_pricing
from tradingmachine.orders.plan_parts import order_part
from tradingmachine.orders.plan_parts import sequence_part
from tradingmachine.orders.plan_parts import then_part
from tradingmachine.orders.plan_parts import time_at
class TradeThenReentry:
"""A bracketed trade, and a second entry started only after it.
Attributes:
share: The tradingmachine.assets.equities.Equity for Vodafone Idea on the NSE.
"""
def __init__(self):
"""Looks the share up.
Raises:
tradingmachine.assets.exceptions.InstrumentError: The share could not be found in UBI.
"""
self.share = equities.Equity(exchange="nse", symbol="IDEA")
def protected_trade(self, last_price: float) -> then_part.ThenPart:
"""Builds the first trade, a buy protected by a stop 3% below and a target 3% above.
Args:
last_price: The float last price the levels are measured from.
Returns:
The then_part.ThenPart holding the entry and its two exits.
Raises:
Nothing.
"""
stop_trigger = round(last_price * 0.97, 2)
target_price = round(last_price * 1.03, 2)
return then_part.ThenPart(
first=order_part.OrderPart(
pricing=marketable_pricing.MarketablePricing(),
),
each_fill=either_part.EitherPart(
children=[
order_part.OrderPart(
side="protect",
pricing=native_stop_pricing.NativeStopPricing(
trigger_price=stop_trigger,
limit_price=round(stop_trigger - 0.05, 2),
),
),
order_part.OrderPart(
side="protect",
pricing=fixed_pricing.FixedPricing(
price=target_price,
order_type="LIMIT",
),
),
],
sibling_rule="reduce",
),
)
def run(self) -> None:
"""Prints the plan.
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}")
plan = sequence_part.SequencePart(
children=[
self.protected_trade(last_price),
order_part.OrderPart(
trigger=time_at.TimeAt("14:00"),
pricing=marketable_pricing.MarketablePricing(),
),
],
)
print(f"Last price of {self.share.symbol}: {last_price}")
print(json.dumps(plan.document(), indent=2))
if __name__ == "__main__":
TradeThenReentry().run()
Build a scale-in: three buys of Vodafone Idea at successively lower levels, each considered only once the one before is done¶
Run it with .venv/bin/python examples/orders/plan_parts/sequence_part/sequence_part/scale_in_on_three_dips.py.
"""Build a scale-in: three buys of Vodafone Idea at successively lower levels, each considered only once the one before is done.
The program reads the share's last price and builds three buys waiting for dips of 1%, 2% and 3%. In a sequence join the second buy does not start watching until the first is done, so a single sharp fall fills one buy at a time rather than all three at once. It prints the plan. Nothing is sent to UBI's order routes.
Typical usage example:
.venv/bin/python examples/orders/plan_parts/sequence_part/sequence_part/scale_in_on_three_dips.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 price_crosses
from tradingmachine.orders.plan_parts import sequence_part
class ThreeStepScaleIn:
"""Three dip buys run one after another.
Attributes:
share: The tradingmachine.assets.equities.Equity for Vodafone Idea on the NSE.
dip_percents: The list of float percentages below the last price each buy waits for.
"""
def __init__(self):
"""Looks the share up and sets the dips.
Raises:
tradingmachine.assets.exceptions.InstrumentError: The share could not be found in UBI.
"""
self.share = equities.Equity(exchange="nse", symbol="IDEA")
self.dip_percents = [
1.0,
2.0,
3.0,
]
def run(self) -> None:
"""Prints the plan.
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}")
buys = []
for dip_percent in self.dip_percents:
level = round(last_price * (1 - dip_percent / 100), 2)
buys.append(
order_part.OrderPart(
trigger=price_crosses.PriceCrosses(level=level),
pricing=marketable_pricing.MarketablePricing(buffer_ticks=2),
quantity=500,
),
)
plan = sequence_part.SequencePart(children=buys)
print(f"Last price of {self.share.symbol}: {last_price}")
print(json.dumps(plan.document(), indent=2))
if __name__ == "__main__":
ThreeStepScaleIn().run()