Trigger orders
Not just TP/SL!
Last updated
Not just TP/SL!
Last updated
Zeta supports trigger orders which can be permissionlessly executed based on a set condition - either a price or a timestamp. You'll see these as "Take-profit/Stop-loss" on dex.zeta.markets, but you can make more complicated triggers yourself in the SDK too.
When a trigger order is placed using the place_trigger_order
instruction, a new TriggerOrder
account is created. This account contains all necessary information about the order, and is deleted when the order is executed.
The relevant TriggerOrder
accounts for a given CrossMarginAccount
can be found by checking the trigger_order_bits
of the CrossMarginAccount
. If a bit is 1, then the corresponding TriggerOrder
account exists (see utils.getTriggerOrder()
to see how the seeds work). One account can hold up to 128 triggers, as the bits value stored is a u128.
Besides regular TP/SL, trigger orders allow you to set any price+direction or timestamp as the trigger. Just be careful with margining, as margin requirements are not checked at the time you place a trigger, but only when it executes! Think of it as simply placing an order in the future.
You can do anything that a regular PlaceOrder allows you to do, including different order types, reduceOnly and setting your own order price.
You can also edit trigger orders before they execute using the edit_trigger_order
instruction.
When either the price trigger or timestamp trigger is valid, a trigger order can be executed by calling the execute_trigger_order
instruction. This is permissionless, so anyone can execute anyone's trigger orders! If the condition has not been met yet, a TriggerOrderCannotBeExecuted
error will be returned.
As mentioned earlier, margin requirements are only checked when the trigger order executes. This is generally not an issue for the standard TP/SL offered on dex.zeta.markets as reduceOnly is set to true, but you might want to be careful if you're making your own triggers that increase your position.
TP/SL on dex.zeta.markets uses a FillOrKill order type for market, and Limit order type for limit, and sets order_price to be 5% away from trigger_price by default (you can edit this slippage in the Settings page). Therefore the slippage should be enough to cross the spread in volatile conditions.
There is extra control given to the State.trigger_admin
account, which is set to our crank's wallet. This account has the power to delete active trigger orders, which it does in certain conditions:
If a trigger order fails too many times (currently 300, which corresponds to approx 50 mins of failed execution)
If a TP/SL trigger is left hanging after a user closes their position, to ensure frontend UX remains fluid.