Skip to content

Tutorial: watch a price and swap

A common agentic pattern: watch a price and act when it crosses a line. This builds a small, safe polling loop that buys once the spot price of an AMM token drops below a threshold, then stops. It's deliberately simple — the point is the shape, with the guardrails in the right places.

Testnet first

Run this on testnet with a dedicated wallet until it behaves exactly as you expect. It places a real swap when the condition is met.

The script

import time
from decimal import Decimal
from primedelta import PrimeDelta, SwapSide, AccountStatus

RPC = "https://chain.testnet.primedelta.io"
SYMBOL = "AMMT1"
BUY_BELOW = Decimal("9.50")    # spot price threshold
SPEND = Decimal("10")          # dUSD to spend when it triggers
MAX_TRADE_USD = Decimal("25")  # your cap
SLIPPAGE_BPS = 100
POLL_SECONDS = 15

pd = PrimeDelta(private_key="0x…", web3_provider_url=RPC, network="testnet")
pd.login()

if pd.get_account_status() != AccountStatus.DID_MINTED:
    raise SystemExit("wallet has no minted DID — verify it first")
if SPEND > MAX_TRADE_USD:
    raise SystemExit(f"{SPEND} dUSD exceeds your cap of {MAX_TRADE_USD}")

print(f"watching {SYMBOL}; will buy {SPEND} dUSD when spot < {BUY_BELOW}")

while True:
    try:
        price = pd.spot_price(SYMBOL)
    except Exception as exc:            # transient RPC / pricing hiccup
        print("price check failed, retrying:", exc)
        time.sleep(POLL_SECONDS)
        continue

    print(f"spot {SYMBOL} = {price}")
    if price < BUY_BELOW:
        quote = pd.quote_swap(SYMBOL, SwapSide.STABLECOIN_TO_STOCK, SPEND)
        min_out = pd.min_out_from_quote(quote, slippage_bps=SLIPPAGE_BPS)
        tx = pd.swap_exact_input(
            SYMBOL, SwapSide.STABLECOIN_TO_STOCK,
            amount_in=SPEND, min_amount_out=min_out,
        )
        print("bought — tx:", tx)
        break                            # one-shot: stop after the fill

    time.sleep(POLL_SECONDS)

Why it's shaped this way

  • The checks that don't change are done once, up front — login, the DID gate, and your cap. No point re-checking them every tick.
  • The price read is wrapped in try/except so a transient RPC blip retries instead of crashing the loop. Only pricing is retried; a real trade failure should stop you.
  • The trade still quotes fresh at the moment it fires and derives a positive min_amount_out — the threshold decides whether to trade, the quote decides the floor.
  • It's one-shot (break after the fill). A loop that keeps buying needs a real budget and a daily cap — see below.

Turning it into a budgeted loop

If you want it to keep running, don't rely on a counter — track spend and stop at a daily budget:

spent_today = Decimal("0")
DAILY_BUDGET = Decimal("100")

# inside the loop, before swapping:
if spent_today + SPEND > DAILY_BUDGET:
    print("daily budget reached — stopping")
    break
# after a successful swap:
spent_today += SPEND

When you run this through the MCP server instead of a raw script, the server enforces PRIMEDELTA_MCP_MAX_DAILY_USD for you — off the model — so the budget can't be bypassed. See What the guardrails do.

Don't do this

  • No zero minimum-out, ever — a watcher that fires on a price move is exactly when a bad floor gets sandwiched.
  • Don't poll a tokenized stock's spot price — oracle stocks have no AMM spot; use the oracle price and check market hours.
  • Don't hammer the RPC — 10–30s polling is plenty; tighter loops just add load and rate-limit risk.

Back to the guarded swap · SDK reference