Tutorial: Order Trigger Bot
Introduction
Order Trigger Bots (Trigger Bots) are responsible for marking orders that satisfy the trigger condition, including:
-
Trigger Market Orders - Stop Market and Take Profit
-
Trigger Limit Orders - Stop Limit and Take Profit Limit
Trigger Bots receive a small compensation for each successfully marked order.
See Keepers & Decentralised Orderbook for a technical explanation of how the decentralised orderbook (DLOB) and matching incentives work.
Trigger Bots are similar to Tutorial: Order Matching Bot in that they:
-
also maintain a local copy of the Keepers & Decentralised Orderbook;
-
do not require the operator to manage collateral; and
-
receive a small reward for performing their duties.
Getting Started
The reference implementation of a Trigger Bot is available here in the velocity-v1 monorepo.
Follow Keeper Bots to set required environment variables and initialize a Velocity user account.
Start the Trigger Bot:
bun run dev:triggerTechnical Explanation
The trigger bot polls the DLOB for orders whose trigger conditions are now met (e.g. oracle price crossed a stop-loss level) and submits a transaction to mark them as triggered, earning a small keeper reward.
Get nodes from the DLOB that are ready to be triggered
The DLOB tracks conditional orders (stop market, stop limit, take profit, take profit limit) and their trigger prices. findNodesToTrigger returns orders where the trigger price has crossed the trigger threshold, these are ready to be marked on-chain.
The trigger price is not always the raw oracle price. When the state account’s MedianTriggerPrice feature bit is on (it is on mainnet today), the program (and getTriggerPrice) triggers off a median of the oracle price, the last fill price, and a funding-basis price instead. Read the bit from the state account rather than assuming either mode. A bot that passes the raw oracle price directly can both miss real triggers and submit triggers that revert.
const market = this.velocityClient.getPerpMarketAccount(marketIndex)!;
const oraclePriceData = this.velocityClient.getOracleDataForPerpMarket(marketIndex);
const triggerPrice = getTriggerPrice(
market,
oraclePriceData.price,
new BN(Date.now() / 1000),
useMedianTriggerPrice(this.velocityClient.getStateAccount())
);
const nodesToTrigger = this.dlob.findNodesToTrigger(
marketIndex,
this.slotSubscriber.getSlot(),
triggerPrice,
MarketType.PERP,
this.velocityClient.getStateAccount()
);Call getTriggerOrderIx on VelocityClient
Submit the trigger transaction for each eligible node. Once triggered on-chain, the order becomes a regular order available for matching bots to fill. The transaction can still revert, for example if the market’s fill operation is paused. Expect competition from other trigger bots, and handle errors gracefully and continue to the next node.
const user = this.userMap.get(nodeToTrigger.node.userAccount.toString());
const triggerIx = await this.velocityClient.getTriggerOrderIx(
nodeToTrigger.node.userAccount,
user.getUserAccount(),
nodeToTrigger.node.order
);