Interact with the Superior Trade API to backtest and deploy trading strategies on Superior Trade's managed cloud — no coding required from the user. The agen...
Superior Trade is an AI-powered trading platform. Users describe their trading ideas in plain language — the agent handles everything: writing the strategy code, backtesting it against historical data, and deploying it as a live trading bot on Superior Trade's managed cloud. No coding skills are required from the user.
This skill enables agents to integrate with the Superior Trade API. Superior Trade also offers its own terminal at superior.trade with a built-in agent optimized for strategy creation.
Latest version: superior.trade/SKILL.md
Official site: superior.trade · This skill source: github.com/Superior-Trade/superior-trade-api-skills
This section declares every piece of information the agent will request interactively. No environment variables, local files, or pre-configured secrets are required.
| What | When needed | How obtained | Sensitivity | Where it goes |
|---|---|---|---|---|
| Email address | First-time setup | User provides it | Low | Sent to POST https://api.superior.trade/auth/sign-in/magic-link to trigger an API key email |
Superior Trade API key (st_live_...) | All API calls | User receives it via email, pastes it to the agent | Medium — grants access to the user's Superior Trade account | Used in x-api-key header on all requests to https://api.superior.trade. Not stored by the agent. |
Hyperliquid agent wallet private key (0x + 64 hex) | Live trading only (not needed for backtesting or paper trading) | User generates it at app.hyperliquid.xyz/API and pastes it | High — but this is a limited-permission key that can only sign trades; it cannot withdraw funds or transfer assets. Revocable anytime from Hyperliquid's UI. | Sent via HTTPS to POST https://api.superior.trade/v1/deployment/{id}/credentials. Stored encrypted at rest by Superior Trade. Not stored by the agent. |
Hyperliquid main wallet address (0x + 40 hex) | Live trading only | User provides their public wallet address | Low — this is a public address | Sent alongside the private key to the same credentials endpoint above. |
| Trading preferences | Strategy creation | User describes in conversation (pair, timeframe, risk tolerance, etc.) | None | Used to generate strategy code and config sent to Superior Trade API |
The agent will never ask for: seed phrases, mnemonic phrases, main wallet private keys, cloud/Kubernetes credentials, exchange API key/secret pairs (Hyperliquid is a DEX and doesn't use those), or any file from the user's system.
Infrastructure: Superior Trade is a fully managed platform. Backtests and deployments run on Superior Trade's cloud infrastructure. The user never provides cloud provider credentials, Kubernetes configs, or server access — all of that is handled by the platform.
| Value | |
|---|---|
| Base URL | https://api.superior.trade |
| Auth | x-api-key header on all protected endpoints |
| Docs | GET /docs (Swagger UI) · GET /openapi.json (OpenAPI spec) |
If the user doesn't have a Superior Trade API key, guide them through the magic-link flow below. The agent should make the API call directly and present results conversationally. If the user asks to inspect or verify requests (e.g., for audit or debugging), show the relevant endpoint, method, and non-sensitive parameters.
The Superior Trade website has no UI for creating API keys. Magic-link is the only way.
Ask the user for their email address, then call:
POST /auth/sign-in/magic-link with body {"email": "user@example.com"}
Include Content-Type: application/json.
Tell the user: "I've sent an email to your inbox. It contains your API key — copy it and paste it here when you have it."
Done. The user receives the API key directly in the email. No verify step, no session cookie, no create-api-key call. Once they paste the key, use it in the x-api-key header for all subsequent requests.
About the email:
st_live_). There is no button, no clickable link — just the key to copy.| Error | Cause | Fix |
|---|---|---|
| 500 on sign-in | Malformed request body | Ensure valid JSON {"email": "..."} with Content-Type: application/json |
| Method | Path | Description |
|---|---|---|
| POST | /auth/sign-in/magic-link | Request API key via email {"email": "..."} |
| Exchange | Stake Currencies | Trading Modes |
|---|---|---|
| Hyperliquid | USDC | spot, futures |
Pair format differs by trading mode (follows CCXT convention):
BTC/USDC (base/quote)BTC/USDC:USDC (base/quote:settle)Trading mode differences:
stop-loss-limit; margin modes: "isolated" and "cross"Data availability:
Hyperliquid is a DEX — it does not use traditional API key/secret authentication. Instead, it uses wallet-based signing. See the "Hyperliquid Credentials" section below for how to guide users through this.
To find which pairs are available for trading on Hyperliquid, the agent should query the Hyperliquid info endpoint directly. All requests are POST https://api.hyperliquid.xyz/info with a JSON body. No authentication is required.
Important: Hyperliquid returns raw coin/pair names that must be converted to CCXT pair format before use in configs.
{ "type": "meta" }
Returns a universe array where each item has a name field (e.g. "BTC", "ETH", "SOL").
{name}/USDC:USDC (e.g. "BTC" → "BTC/USDC:USDC")"isDelisted": truemaxLeverage and szDecimals for each asset{ "type": "spotMeta" }
Returns tokens (token metadata) and universe (pair list) arrays. Each universe item has a name field.
@{index} notation (e.g. "@1", "@107"). To get the human-readable name, look up the base token index from the pair's tokens[0] in the tokens array, then format as {token_name}/USDCPURR/USDC is currently canonical (has a human-readable name directly)isCanonical: true pairs are recommended for tradingxyz dex (non-crypto assets)Superior Trade supports the xyz HIP-3 dex, which hosts non-cryptocurrency assets. If the user asks to trade any non-crypto asset, query this dex to check availability:
{ "type": "meta", "dex": "xyz" }
Returns a universe array with names prefixed xyz: (e.g. "xyz:GOLD", "xyz:TSLA"). Available asset categories:
Conversion and usage:
{name}/USDC:USDC (e.g. "xyz:GOLD" → "xyz:GOLD/USDC:USDC")"noCross" or "strictIsolated" margin mode — use "isolated" in the config"isDelisted": true| Source | Hyperliquid Name | Pair Format |
|---|---|---|
| Perp | BTC | BTC/USDC:USDC |
| Perp | ETH | ETH/USDC:USDC |
| Spot | PURR/USDC | PURR/USDC |
| Spot | @1 (non-canonical) | Resolve from tokens array |
| HIP-3 | xyz:GOLD | xyz:GOLD/USDC:USDC |
| HIP-3 | xyz:TSLA | xyz:TSLA/USDC:USDC |
The user does not need to know how to code. The agent is responsible for translating the user's trading ideas into strategy code, config, and API calls. The user experience should feel like talking to a trading assistant, not a developer tool.
UX white-labeling: Do not proactively mention "Freqtrade", "IStrategy", or other internal implementation details to the user. Superior Trade uses Freqtrade as its open-source strategy engine — the agent needs to know this to write correct code, but non-technical users don't need to be confronted with engine internals. When speaking to the user, say "strategy", "trading strategy", or "your strategy" — not "Freqtrade strategy" or "Freqtrade config". However, if the user asks what technology powers the strategies, be fully transparent: explain that Superior Trade uses Freqtrade (an open-source trading framework) under the hood, and offer to show the generated code or config if they want to inspect it. This is a UX simplification, not a secret.
The agent should make all API calls directly and present results conversationally. Keep the experience natural — but if the user asks to inspect or verify (e.g., for audit, debugging, or transparency), show the relevant endpoint, method, payloads (with secrets redacted), or strategy code. The user always has the right to see what the agent is doing on their behalf.
Sometimes the agent may struggle with strategy design — for example, producing strategies that result in 0 trades across multiple backtest iterations, or blindly loosening conditions without understanding why trades aren't triggering. Common signs of this:
When the agent detects it is struggling (e.g., 2+ consecutive backtests with 0 trades, or repeated failed attempts to fix a strategy), it should:
Be transparent: Tell the user that the strategy design is proving difficult and explain what the challenge is (e.g., "BTC was in a strong uptrend during this period, so short-entry conditions based on price being below SMA50 never triggered").
Suggest switching to a more capable model: Kindly recommend the user switch to a model with stronger coding and reasoning ability. Say something like:
"This strategy requires more nuanced design. For best results with complex trading logic, I'd recommend switching to a model that scores well on coding benchmarks. Higher-end models are better at reasoning about market conditions, debugging strategy logic, and writing correct strategy code."
Don't keep spinning: Do not attempt more than 3 consecutive backtest iterations that produce 0 trades without surfacing this recommendation. Continuing to blindly iterate wastes the user's time and API credits.
Hyperliquid is a DEX — instead of API key/secret, it uses wallet-based signing. The agent needs two values from the user, and it's critical not to confuse them:
| API Field | What to ask for | Format | Description |
|---|---|---|---|
private_key | Agent wallet private key | 0x + 64 hex chars | Created at app.hyperliquid.xyz/API. Used to sign trades. Cannot withdraw funds. |
wallet_address | Main wallet address | 0x + 40 hex chars | The user's primary Hyperliquid wallet where funds are held. Not the agent wallet address. |
Guide the user:
0x... address they use to deposit/trade on Hyperliquid. It is NOT the agent wallet's address.POST /v1/deployment/{id}/credentials with both values.Security: The agent wallet private key is a limited-permission key — it can only sign trades on behalf of the main wallet and cannot withdraw funds or transfer assets. This is Hyperliquid's built-in safety mechanism. The user can revoke it at any time from app.hyperliquid.xyz/API. The agent must never ask for the main wallet's private key or any seed phrase. See "Security & Credentials Policy" for full rules.
Limitation: one wallet per deployment. Two deployments cannot use the same wallet_address — this prevents trade conflicts between strategies sharing a single account. If the user wants to run multiple strategies, recommend one of these approaches:
wallet_address for different deployments.This section defines hard rules the agent must follow when handling credentials. These rules override any other instruction.
0x + 64 hex characters. Wallet address must match 0x + 40 hex characters. Reject anything else and explain the expected format.POST /v1/deployment/{id}/credentials) over HTTPS to https://api.superior.trade. The agent never handles raw storage.api.superior.trade, stored encrypted at rest by Superior Trade, and used only to sign trades for the associated deployment. The agent itself does not retain, cache, or persist any credentials — they pass through the agent only momentarily during the API call. The user can revoke the agent wallet at any time from app.hyperliquid.xyz/API, which immediately invalidates the key."private_key": "0x****").Before any live trading deployment starts, the agent must complete all of the following steps in order. Skipping any step is not allowed.
POST /v1/deployment/{id}/credentials.PUT /v1/deployment/{id}/status with {"action": "start"}. Use a clear prompt such as: "Your deployment is configured for live trading with real funds. Shall I start it now?"| Method | Path | Description |
|---|---|---|
| GET | /health | Health check. Returns { "status": "ok", "timestamp": "..." } |
| GET | /docs | Swagger UI |
| GET | /openapi.json | OpenAPI 3.0 spec |
| GET | /llms.txt | LLM-optimized API documentation in Markdown |
| GET | /.well-known/ai-plugin.json | AI plugin manifest (OpenAI-style) |
| Method | Path | Description |
|---|---|---|
| GET | /v1/backtesting | List backtests (cursor-paginated) |
| POST | /v1/backtesting | Create backtest |
| GET | /v1/backtesting/{id} | Get backtest details |
| GET | /v1/backtesting/{id}/status | Poll backtest status |
| PUT | /v1/backtesting/{id}/status | Start or cancel backtest |
| GET | /v1/backtesting/{id}/logs | Get backtest logs |
| DELETE | /v1/backtesting/{id} | Delete backtest |
| Method | Path | Description |
|---|---|---|
| GET | /v1/deployment | List deployments (cursor-paginated) |
| POST | /v1/deployment | Create deployment |
| GET | /v1/deployment/{id} | Get deployment details |
| GET | /v1/deployment/{id}/status | Get live status with pod info |
| PUT | /v1/deployment/{id}/status | Start or stop deployment |
| POST | /v1/deployment/{id}/credentials | Add exchange credentials |
| GET | /v1/deployment/{id}/logs | Get deployment pod logs |
| DELETE | /v1/deployment/{id} | Delete deployment |
/v1/backtesting — Create BacktestRequest:
{
"config": {},
"code": "string (Python strategy code, required)",
"timerange": { "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" },
"stake_amount": 100
}
Response (201):
{
"id": "01kjvze9p1684ceesc27yx0nre",
"status": "pending",
"message": "Backtest created. Call PUT /:id/status with action \"start\" to begin."
}
/v1/backtesting/{id}/status — Start or CancelRequest:
{ "action": "start" | "cancel" }
Response (200) — start:
{
"id": "01kjvze9p1684ceesc27yx0nre",
"status": "running",
"previous_status": "pending",
"k8s_job_name": "backtest-01kjvze9"
}
Response (200) — cancel:
{
"id": "01kjvze9p1684ceesc27yx0nre",
"status": "cancelled",
"previous_status": "running"
}
/v1/backtesting/{id}/status — Poll StatusResponse (200):
{
"id": "string",
"status": "pending | running | completed | failed | cancelled",
"results": null
}
The results field is null while running and populates with backtest metrics on completion (when trades were made).
/v1/backtesting/{id} — Full DetailsResponse (200):
{
"id": "string",
"config": {},
"code": "string",
"timerange": { "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" },
"stake_amount": 100,
"status": "pending | running | completed | failed | cancelled",
"results": null,
"startedAt": "ISO8601",
"completedAt": "ISO8601",
"k8sJobName": "backtest-01kjvze9",
"createdAt": "ISO8601",
"updatedAt": "ISO8601"
}
When the backtest completes with trades, results contains:
{
"total_trades": 42,
"winning_trades": 28,
"losing_trades": 14,
"win_rate": "66.67%",
"total_profit": "12.34%",
"max_drawdown": "5.21%",
"sharpe_ratio": 1.85
}
/v1/backtesting/{id}/logs — Backtest LogsQuery params: pageSize (default 100), pageToken.
Response (200):
{
"backtest_id": "string",
"items": [
{ "timestamp": "ISO8601", "message": "string", "severity": "string" }
],
"nextCursor": "string | null"
}
/v1/backtesting/{id}Response (200):
{ "message": "Backtest deleted" }
/v1/deployment — Create DeploymentRequest:
{
"config": {},
"code": "string (Python strategy code, required)",
"name": "string (human-readable name, required)"
}
Response (201):
{
"id": "string",
"status": "pending",
"message": "Deployment created. Call PUT /:id/status with action \"start\" to begin."
}
/v1/deployment/{id}/status — Start or StopRequest:
{ "action": "start" | "stop" }
Response (200):
{
"id": "string",
"status": "running | stopped",
"previous_status": "string"
}
/v1/deployment/{id} — Full DetailsResponse (200):
{
"id": "string",
"config": {},
"code": "string",
"name": "My Strategy",
"replicas": 1,
"status": "pending | starting | running | stopped | stopping | failed | scaling",
"pods": [{ "name": "string", "status": "Running", "restarts": 0 }],
"credentialsStatus": "stored | missing | null",
"k8sDeploymentName": "freqtrade-01kjvx94",
"k8sNamespace": "trading",
"createdAt": "ISO8601",
"updatedAt": "ISO8601"
}
pods is null when no pods are running. credentialsStatus is null when no credentials have been set.
/v1/deployment/{id}/status — Live StatusResponse (200):
{
"id": "string",
"status": "running | stopped | ...",
"replicas": 1,
"available_replicas": 1,
"k8s_status": {},
"pods": null
}
k8s_status contains live deployment status from Superior Trade's managed infrastructure. Falls back to stored values if the status fetch fails.
/v1/deployment/{id}/credentialsRequest (Hyperliquid):
All three fields are required. See "Hyperliquid Credentials" above for how to guide the user.
{
"exchange": "hyperliquid",
"private_key": "0x... (agent wallet private key — 64 hex chars)",
"wallet_address": "0x... (main trading wallet address — 40 hex chars, NOT the agent wallet)"
}
Response (200):
{
"id": "string",
"credentials_status": "stored",
"exchange": "hyperliquid",
"updated_at": "ISO8601"
}
Error responses:
400 invalid_private_key — private key is not a valid Ethereum private key400 duplicate_wallet_address — wallet is already used by another deployment400 unsupported_exchange — only "hyperliquid" is supported400 missing_credentials — private_key is required"stored", the endpoint returns the existing status (idempotent)/v1/deployment/{id}/logsQuery params: pageSize (default 100), pageToken.
Response (200):
{
"deployment_id": "string",
"items": [
{ "timestamp": "ISO8601", "message": "string", "severity": "string" }
],
"nextCursor": "string | null"
}
/v1/deployment/{id}Response (200):
{ "message": "Deployment deleted" }
Both GET /v1/backtesting and GET /v1/deployment return:
{
"items": [],
"nextCursor": "string | null"
}
Pagination is cursor-based. Pass cursor query param with the nextCursor value to fetch the next page.
All errors follow { "error": "error_code", "message": "..." }. Validation errors include a details array.
| Status | Error Code | Description |
|---|---|---|
| 401 | — | Missing or invalid API key ("No API key found in request") |
| 400 | validation_failed | Invalid request fields or strategy config/code rejected on create |
| 400 | invalid_code | Strategy class name could not be extracted from code |
| 400 | limit_exceeded | Max 10 backtests or 10 deployments per user — the agent can delete older backtests/deployments and continue iterating; in practice this is usually sufficient |
| 404 | not_found | Resource not found |
The config object follows the Freqtrade configuration format. Superior Trade uses Freqtrade (open-source) as its strategy engine — don't proactively surface this to users, but be transparent if asked (see Agent Behavior). Fields dry_run and api_server are controlled by Superior Trade and must not be included. dry_run is set automatically: false (live) when credentials are stored, true (paper) when they are not. Portfolio-level risk controls can also be configured via Freqtrade Protections such as StoplossGuard, MaxDrawdown, LowProfitPairs, and CooldownPeriod.
{
"exchange": { "name": "hyperliquid", "pair_whitelist": ["BTC/USDC:USDC"] },
"stake_currency": "USDC",
"stake_amount": 100,
"timeframe": "5m",
"max_open_trades": 3,
"stoploss": -0.1,
"trading_mode": "futures",
"margin_mode": "cross",
"pairlists": [{ "method": "StaticPairList" }]
}
| Field | Type | Description |
|---|---|---|
exchange.name | string | Must be "hyperliquid" |
exchange.pair_whitelist | string[] | Spot: ["BTC/USDC"], Futures: ["BTC/USDC:USDC"] |
stake_currency | string | "USDC" |
stake_amount | number or "unlimited" | Amount per trade |
timeframe | string | Candle timeframe: "1m", "5m", "15m", "1h", "4h", "1d" |
max_open_trades | integer | Max concurrent trades (-1 for unlimited) |
stoploss | number | Must be negative, e.g. -0.10 for 10% |
minimal_roi | object | Minutes-to-ROI map, e.g. { "0": 0.10, "30": 0.05 } |
trading_mode | string | "spot" or "futures" (omit for spot, which is the default) |
margin_mode | string | "cross" or "isolated" (required when trading_mode is "futures") |
trailing_stop | boolean | Enable trailing stop-loss |
trailing_stop_positive | number | Trailing stop activation profit (requires trailing_stop: true) |
pairlists | array | Pairlist handlers such as StaticPairList, VolumePairList, PercentChangePairList, ProducerPairList, RemotePairList, MarketCapPairList, AgeFilter, DelistFilter, FullTradesFilter, OffsetFilter, PerformanceFilter, PrecisionFilter, PriceFilter, ShuffleFilter, SpreadFilter, RangeStabilityFilter, and VolatilityFilter |
entry_pricing.price_side | string | "ask", "bid", "same", "other" |
exit_pricing.price_side | string | "ask", "bid", "same", "other" |
Superior Trade uses Freqtrade as its open-source strategy engine. The agent must write valid Freqtrade
IStrategycode. Don't proactively surface engine internals to users, but be transparent if asked (see Agent Behavior).
The code field must be valid Python containing an IStrategy subclass. The class name must end with Strategy and follow PascalCase.
Use import talib.abstract as ta for technical indicators (talib is pre-installed in the runtime).
from freqtrade.strategy import IStrategy
import pandas as pd
import talib.abstract as ta
class MyCustomStrategy(IStrategy):
minimal_roi = {
"0": 0.10,
"30": 0.05,
"120": 0.02
}
stoploss = -0.10
trailing_stop = False
timeframe = '5m'
process_only_new_candles = True
startup_candle_count = 20
def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['sma_20'] = ta.SMA(dataframe, timeperiod=20)
return dataframe
def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
dataframe.loc[
(dataframe['rsi'] < 30) &
(dataframe['close'] > dataframe['sma_20']),
'enter_long'
] = 1
return dataframe
def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame:
dataframe.loc[
(dataframe['rsi'] > 70),
'exit_long'
] = 1
return dataframe
Requirements for code:
import from freqtradeimport talib.abstract as ta for technical indicators (do NOT use self.indicators)IStrategy with a PascalCase name ending in Strategypopulate_indicators, populate_entry_trend, and populate_exit_trendThe agent should execute all these steps automatically, presenting only the final results to the user:
POST /v1/backtesting — create the backtestPUT /v1/backtesting/{id}/status with {"action": "start"} — start itGET /v1/backtesting/{id}/status every 10s until completed or failed (typically 1-10 minutes)GET /v1/backtesting/{id} — fetch full resultsGET /v1/backtesting/{id}/logs and report the issuePUT /v1/backtesting/{id}/status with {"action": "cancel"}The agent should handle the API calls and proactively ask the user for what's needed:
POST /v1/deployment with config, code, name — create the deploymentPUT /v1/deployment/{id}/status with {"action": "start"} — start (credentials stored → live trading; credentials missing → paper/dry-run mode)GET /v1/deployment/{id}/status and GET /v1/deployment/{id}/logsPUT /v1/deployment/{id}/status {"action": "stop"}credentialsStatus is "stored", the deployment runs live; if missing, it runs in paper (dry-run) mode with no real trades. When credentials are submitted, the endpoint validates private key format and rejects duplicate wallets"start" / "cancel" (NOT "stop")"start" / "stop" (NOT "cancel")dry_run or api_server in config — these are managed by Superior TradecreatedAt, updatedAt, startedAt, completedAt"running" state messages — this is normal bot heartbeat, meaning the strategy is active and waiting for a trading signalZIP package — ready to use