SPENDING_LIMIT + RATE_LIMIT: Double Protection for High-Frequency Trading Bots
Trading bots operate in an environment where milliseconds matter and a single misconfigured parameter can drain a wallet before you even notice. If you're building an automated trading system — arbitrage, MEV, algorithmic strategies — you need risk controls that are granular enough to survive real market conditions without slowing down your execution pipeline.
Why Risk Controls Break Most Trading Bots
Here's the brutal truth about wallet risk management in production trading systems: most developers bolt it on as an afterthought. The bot logic gets all the attention — the signal detection, the routing logic, the slippage math — and the wallet layer is treated as a dumb key-signer. That works fine in testing. It fails catastrophically when:
- A bug in your signal logic triggers 200 transactions in 60 seconds
- Gas spikes and your bot keeps executing at 10x the expected cost
- A compromised session token gives an attacker access to your entire balance
- A miscalculated position size pushes a single transaction through for $50,000 instead of $500
The stakes are high because trading bots, by design, move money without human oversight. That's their entire value proposition. But it also means every failure mode compounds faster than you can intervene. You need controls baked into the infrastructure layer, not the application layer — because application-layer guards can have bugs too.
Two Policies, One Wallet — The Double Protection Model
WAIaaS provides a policy engine with 21 policy types and 4 security tiers (INSTANT, NOTIFY, DELAY, APPROVAL). For high-frequency trading bots specifically, two policies work together to create layered protection that doesn't compromise execution speed for normal operations while catching runaway behavior before it causes serious damage.
SPENDING_LIMIT protects you against individual transaction size anomalies. A single transaction that exceeds your expected position size gets routed through a higher security tier — delayed or held for approval — before it executes.
RATE_LIMIT protects you against frequency anomalies. A bug that tries to fire 500 transactions in an hour gets cut off before it empties your wallet.
These two failure modes are independent. A compromised session token might try one very large transaction (caught by SPENDING_LIMIT) or many small ones (caught by RATE_LIMIT). Deploying both means you're covered in either scenario.
Setting Up SPENDING_LIMIT for a Trading Bot
The SPENDING_LIMIT policy uses WAIaaS's 4-tier security model. Transactions are categorized automatically based on USD value:
- INSTANT: Execute immediately, no notification
- NOTIFY: Execute immediately, send notification to owner
- DELAY: Queue for N seconds, then execute (cancellable)
- APPROVAL: Require human approval via WalletConnect, Telegram, or Push
For a trading bot, you want normal-sized trades to hit INSTANT (zero friction), larger positions to trigger NOTIFY (you want to know), and anything anomalously large to hit DELAY or APPROVAL (stop and wait).
curl -X POST http://127.0.0.1:3100/v1/policies \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{
"walletId": "<wallet-uuid>",
"type": "SPENDING_LIMIT",
"rules": {
"instant_max_usd": 100,
"notify_max_usd": 500,
"delay_max_usd": 2000,
"delay_seconds": 900,
"daily_limit_usd": 5000
}
}'
What this configuration does in practice:
- Trades under $100 execute instantly — your arb bot doesn't miss the window
- Trades between $100-$500 execute but you get notified — useful for monitoring position sizes
- Trades between $500-$2000 get queued for 15 minutes — you have time to cancel if something looks wrong
- Anything above $2000 requires your explicit approval — a hard stop on anomalous behavior
- Total daily exposure is capped at $5,000 — even if everything else goes wrong, this is your backstop
The tier assignment is automatic. Your bot submits a transaction exactly the same way regardless of size — WAIaaS evaluates it against the policy and routes it to the appropriate tier. Your bot doesn't need any awareness of the policy logic.
Setting Up RATE_LIMIT for Frequency Control
RATE_LIMIT enforces a maximum number of transactions within a time period. For a trading bot that's expected to execute a bounded number of trades per hour, this is your protection against runaway loops.
curl -X POST http://127.0.0.1:3100/v1/policies \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{
"walletId": "<wallet-uuid>",
"type": "RATE_LIMIT",
"rules": {
"maxTransactions": 10,
"period": "hourly"
}
}'
Set maxTransactions to something comfortably above your expected normal trading frequency, but below the volume a runaway bug would generate. If your strategy normally fires 2-3 trades per hour, setting this to 10 gives you headroom while still catching a loop that tries to execute 100.
When the rate limit is hit, WAIaaS returns a structured error:
{
"error": {
"code": "POLICY_DENIED",
"message": "Transaction denied by RATE_LIMIT policy",
"domain": "POLICY",
"retryable": false
}
}
Your bot should handle this error explicitly — log it, alert on it, and halt further execution until you can investigate. A POLICY_DENIED response from RATE_LIMIT is a signal that something unusual is happening, not a transient error to retry through.
The 7-Stage Pipeline Under the Hood
Understanding how WAIaaS processes transactions helps you reason about latency. Every transaction goes through a 7-stage pipeline: validate → auth → policy → wait → execute → confirm. The policy stage is where SPENDING_LIMIT and RATE_LIMIT are evaluated.
For INSTANT-tier transactions (your normal trading range), the wait stage is essentially zero — the pipeline moves straight through to execution. For DELAY-tier transactions, the wait stage holds for delay_seconds before continuing. This means your bot's normal operations have minimal overhead from the policy system, while anomalous transactions are caught without any application-level code on your end.
Combining with Other Risk Policies
For a production trading bot, SPENDING_LIMIT and RATE_LIMIT are the core of your risk stack, but they work best alongside other policy types. Three that matter most for trading bots:
CONTRACT_WHITELIST — default-deny contract calls. Your bot should only be able to call contracts you've explicitly approved. If your strategy only uses Jupiter and Drift, whitelist those contracts. Any transaction targeting an unrecognized contract is blocked automatically.
ALLOWED_TOKENS — default-deny token transfers. Only tokens you've explicitly listed can be transferred. This prevents a compromised session from exfiltrating arbitrary tokens.
PERP_MAX_LEVERAGE — if your bot trades perpetual futures on Hyperliquid or Drift, cap the leverage it can use. A bug that miscalculates position sizing with 50x leverage instead of 5x can be catastrophic. Set a hard ceiling at the infrastructure level.
The policy engine enforces all active policies simultaneously. A transaction has to pass every applicable policy to execute.
Dry-Run Before You Deploy
Before putting real capital on a new strategy, use the dry-run API to simulate transactions against your actual policy configuration. This lets you verify that your expected trade sizes hit the right security tier, and that edge-case positions are handled the way you intend.
curl -X POST http://127.0.0.1:3100/v1/transactions/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer wai_sess_<token>" \
-d '{
"type": "TRANSFER",
"to": "recipient-address",
"amount": "0.1",
"dryRun": true
}'
The dry-run evaluates the full policy pipeline without broadcasting the transaction. You can verify tier assignment, check that whitelist policies are satisfied, and confirm error behavior for out-of-bounds values — all without touching mainnet.
Deploying the Bot Infrastructure
The complete setup takes a few minutes. Start with Docker:
git clone https://github.com/minhoyoo-iotrust/WAIaaS.git
cd WAIaaS
docker compose up -d
The daemon runs on 127.0.0.1:3100 by default (DOCKER-02). Create a wallet for your trading bot:
curl -X POST http://127.0.0.1:3100/v1/wallets \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"name": "trading-wallet", "chain": "solana", "environment": "mainnet"}'
Then create a session token for the bot — this is what your trading code uses to authenticate:
curl -X POST http://127.0.0.1:3100/v1/sessions \
-H "Content-Type: application/json" \
-H "X-Master-Password: my-secret-password" \
-d '{"walletId": "<wallet-uuid>"}'
Apply your SPENDING_LIMIT and RATE_LIMIT policies using the examples above. Then connect your bot code using the TypeScript SDK:
import { WAIaaSClient, WAIaaSError } from '@waiaas/sdk';
const client = new WAIaaSClient({
baseUrl: process.env['WAIAAS_BASE_URL'] ?? 'http://localhost:3100',
sessionToken: process.env['WAIAAS_SESSION_TOKEN'],
});
// Your bot's execution loop
async function executeTrade(to: string, amount: string) {
try {
const result = await client.sendToken({
type: 'TRANSFER',
to,
amount,
});
console.log(`Trade submitted: ${result.id} (status: ${result.status})`);
// Poll for confirmation
const POLL_TIMEOUT_MS = 60_000;
const startTime = Date.now();
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
const tx = await client.getTransaction(result.id);
if (tx.status === 'COMPLETED') {
console.log(`Trade confirmed: ${tx.txHash}`);
return tx;
}
if (tx.status === 'FAILED') {
console.error(`Trade failed: ${tx.error}`);
return null;
}
await new Promise(resolve => setTimeout(resolve, 1000));
}
} catch (error) {
if (error instanceof WAIaaSError) {
if (error.code === 'POLICY_DENIED') {
// Rate limit or spending limit hit — halt and alert
console.error(`Policy blocked trade: ${error.message}`);
// Trigger your alerting system here
process.exit(1);
}
console.error(`API error [${error.code}]: ${error.message}`);
}
throw error;
}
}
The critical piece in the error handling: when you catch POLICY_DENIED, don't retry. Alert on it and halt. That response means your bot is doing something outside its expected operating parameters, and you need a human to look at it before it continues.
DeFi Protocol Access in the Same Wallet
One advantage of routing your trading bot through WAIaaS is that all 15 integrated DeFi protocols are accessible through the same session token and wallet. A cross-venue strategy that swaps on Jupiter, hedges on Drift, and bridges via LI.FI or Across can execute all of those from a single wallet context:
# Execute a Jupiter swap
curl -X POST http://127.0.0.1:3100/v1/actions/jupiter-swap/swap \
-H "Content-Type: application/json" \
-H "Authorization: Bearer wai_sess_<token>" \
-d '{
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"amount": "1000000000"
}'
All of these protocol actions go through the same 7-stage pipeline, which means your SPENDING_LIMIT and RATE_LIMIT policies apply to DeFi actions as well as raw transfers. Your policy configuration covers your entire trading surface.
Quick Start Checklist
Getting this running for a real trading bot:
-
Deploy the daemon —
docker compose up -dfrom the WAIaaS repo - Create a dedicated wallet — one wallet per strategy, so policies are isolated
- Apply SPENDING_LIMIT — calibrate instant/notify/delay thresholds to your expected position sizes
-
Apply RATE_LIMIT — set
maxTransactionsto 2-3x your expected hourly trade frequency - Apply CONTRACT_WHITELIST and ALLOWED_TOKENS — whitelist only what your strategy needs
- Dry-run your edge cases — simulate your maximum expected position and verify the tier assignment before going live
- Handle POLICY_DENIED explicitly — treat it as a halt signal, not a retriable error
What's Next
If you want to go deeper on WAIaaS's policy system, the full OpenAPI reference is available at http://127.0.0.1:3100/reference once your daemon is running — it documents all 21 policy types with request schemas. For monitoring your bot's transaction history and DeFi positions, the Admin Web UI at /admin gives you a live dashboard without writing any code.
The source is on GitHub and everything discussed here is verifiable in the codebase:
- GitHub: https://github.com/minhoyoo-iotrust/WAIaaS
- Official site: https://waiaas.ai
Build the bot. Let the policy engine be your circuit breaker.
Top comments (0)