traderspost-webhook
TradersPost executes trades when it receives a JSON POST to your strategy's webhook URL. This skill covers the payload shapes, retry semantics, and how to drive webhooks on a cron from APScheduler.
When to use
Trigger this skill when the user wants to:
- POST a buy/sell/exit signal to TradersPost from a backend service
- Build a "Go Live" toggle on a strategy that fires webhooks on a schedule
- Test a webhook with a one-off payload from a dev UI
- Convert a strategy's
signal()function output into the right JSON shape
Webhook URL
Each TradersPost strategy has its own URL:
https://webhooks.traderspost.io/trading/webhook/<strategy-id>
Find it under the strategy's "Webhooks" tab in the TradersPost dashboard. Treat it like a secret — anyone with the URL can place orders.
Payload shapes
The webhook listens for JSON. Minimum required fields:
{ "action": "buy", "ticker": "AAPL" }
{ "action": "sell", "ticker": "AAPL" }
{ "action": "exit", "ticker": "AAPL" }
Optional fields:
{
"action": "buy",
"ticker": "AAPL",
"quantity": 10, // shares — omit to use strategy's default sizing
"price": 234.56, // limit price — omit for market
"sentiment": "bullish", // freeform; surfaces in TradersPost UI
"signalPrice": 234.50, // price at which the signal was generated
"stopLoss": { "type": "stop_loss", "stopPrice": 230.00 },
"takeProfit": { "limitPrice": 240.00 }
}
For crypto, use the dotted pair format TradersPost expects:
{ "action": "buy", "ticker": "BTC.USD" }
Posting from Python
import httpx
async def post_webhook(url: str, payload: dict) -> dict:
"""Returns {ok, status, body}. Raises on transport errors only."""
async with httpx.AsyncClient(timeout=10.0) as client:
r = await client.post(url, json=payload)
return {"ok": r.is_success, "status": r.status_code, "body": r.text[:500]}
def buy(ticker: str, quantity: float | None = None) -> dict:
out = {"action": "buy", "ticker": ticker}
if quantity is not None:
out["quantity"] = quantity
return out
def sell(ticker: str, quantity: float | None = None) -> dict:
out = {"action": "sell", "ticker": ticker}
if quantity is not None:
out["quantity"] = quantity
return out
def exit_(ticker: str) -> dict:
return {"action": "exit", "ticker": ticker}
TradersPost responds with 2xx on accepted signals. Treat non-2xx as a soft error and log — do not auto-retry (you'll double-fire on a flapping connection). The TradersPost dashboard shows the full audit trail of received webhooks.
Scheduling with APScheduler
For a "Go Live" toggle that runs a strategy on a cron:
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
import logging
log = logging.getLogger("scheduler")
scheduler = AsyncIOScheduler()
scheduler.start()
_jobs: dict[str, str] = {} # strategy_name → job_id
async def _tick(name: str, webhook: str, signal_fn) -> None:
payload = signal_fn() # your strategy returns a dict or None
if not payload:
return
result = await post_webhook(webhook, payload)
log.info("%s → %s", name, result)
def start_live(name: str, cron: str, webhook: str, signal_fn) -> str:
"""`cron` follows standard cron syntax — '*/5 9-15 * * 1-5' = every 5 min, weekdays, market hours."""
job = scheduler.add_job(
_tick, CronTrigger.from_crontab(cron),
kwargs={"name": name, "webhook": webhook, "signal_fn": signal_fn},
id=f"strategy-{name}", replace_existing=True,
)
_jobs[name] = job.id
return job.id
def stop_live(name: str) -> bool:
job_id = _jobs.pop(name, None)
if job_id:
scheduler.remove_job(job_id)
return True
return False
Cron tips:
- Market hours weekdays:
*/5 9-15 * * 1-5(every 5 min, 9am–3:55pm) - End of day:
30 15 * * 1-5(3:30pm weekdays) - Pre-market:
0 8 * * 1-5 - Crypto 24/7:
*/15 * * * *
APScheduler runs in process — if you restart the API, scheduled jobs are lost. For durable scheduling, persist the job specs to a SQLite/JSON store on start_live and re-register them on FastAPI startup.
Testing without firing real orders
Use webhook.site for a free disposable inbox during development. Set TRADERSPOST_WEBHOOK_URL to that URL, fire a test, and inspect the received JSON in the browser before pointing at a real strategy.
Reference
In command-dash:
apps/api/app/signals/traderspost.py— payload helpers +post_webhookapps/api/app/signals/scheduler.py— APScheduler + strategy loadingapps/api/app/signals/routes.py—POST /api/strategies/{name}/live,POST /api/strategies/test-webhook