Install
openclaw skills install vincent-trading-engineStrategy-driven automated trading for Polymarket and HyperLiquid. Use this skill when users want to create trading strategies, set stop-loss/take-profit/trai...
openclaw skills install vincent-trading-engineUse this skill to create and manage automated trading strategies for Polymarket prediction markets and HyperLiquid perpetuals/spot. The Trading Engine combines driver-based monitoring (web search, Twitter, newswire, price feeds) with a signal pipeline and LLM-powered decision-making to automatically trade based on your thesis. It also includes standalone stop-loss, take-profit, and trailing stop rules that work without the LLM.
All commands use the @vincentai/cli package.
The Trading Engine is a unified system with two modes:
Architecture:
/api/skills/polymarket/strategies/.../api/skills/polymarket/rules/...venue: "hyperliquid" and route through the HL adapterpolymarketSkill.placeBet() or hyperliquidSkill.trade()) which enforces spending limits, approval thresholds, and allowlistsconfig.tools settings. If canTrade: false, the trade tool is not providedid, type (stock, perp, swap, binary, option), venue, and optional constraints (leverage, margin, liquidity, fees).estimate (target price/value), direction (long/short/neutral), confidence (0–1), and reasoning.weight, direction (bullish/bearish/contextual), and monitoring config (entities, keywords, embedding anchor, sources, polling interval).signalScoreThreshold (minimum score to batch), highConfidenceThreshold (score that triggers immediate wake), maxWakeFrequency (e.g. "1 per 15m"), batchWindow (e.g. "5m").Strategies process information through a 6-layer pipeline:
Strategies follow a versioned lifecycle: DRAFT → ACTIVE → PAUSED → ARCHIVED
To iterate on a strategy, duplicate it as a new version (creates a new DRAFT with incremented version number and the same config).
npx @vincentai/cli@latest trading-engine create-strategy \
--key-id <KEY_ID> \
--name "BTC Momentum" \
--config '{
"instruments": [
{ "id": "btc-usd-perp", "type": "perp", "venue": "polymarket" },
{ "id": "BTC", "type": "perp", "venue": "hyperliquid" }
],
"thesis": {
"estimate": 105000,
"direction": "long",
"confidence": 0.7,
"reasoning": "ETF inflows accelerating, halving supply shock imminent"
},
"drivers": [
{
"name": "ETF Flow Monitor",
"weight": 2.0,
"direction": "bullish",
"monitoring": {
"entities": ["BlackRock", "Fidelity"],
"keywords": ["bitcoin ETF", "BTC inflow"],
"embeddingAnchor": "Bitcoin ETF institutional inflows",
"sources": ["web_search", "newswire"]
}
},
{
"name": "Crypto Twitter",
"weight": 1.0,
"direction": "contextual",
"monitoring": {
"entities": ["@BitcoinMagazine", "@saborskycnbc"],
"keywords": ["bitcoin", "BTC"],
"sources": ["twitter"]
}
}
],
"escalation": {
"signalScoreThreshold": 0.3,
"highConfidenceThreshold": 0.8,
"maxWakeFrequency": "1 per 15m",
"batchWindow": "5m"
},
"tradeRules": {
"entry": { "minEdge": 0.05, "orderType": "limit", "limitOffset": 0.01 },
"autoActions": { "stopLoss": -0.10, "takeProfit": 0.25, "trailingStop": -0.05 },
"exit": { "thesisInvalidation": ["ETF outflows exceed $500M/week"] },
"sizing": {
"method": "edgeScaled",
"maxPosition": 500,
"maxPortfolioPct": 20,
"maxTradesPerDay": 5,
"minTimeBetweenTrades": "30m"
}
},
"notifications": {
"onTrade": true,
"onThesisChange": true,
"channel": "none"
}
}'
Parameters:
--name: Strategy name--config: Full strategy config JSON (see Core Concepts above for structure)--data-source-secret-id: Optional DATA_SOURCES secret ID for driver monitoring API calls--poll-interval: Polling interval in minutes for driver monitoring (default: 15)npx @vincentai/cli@latest trading-engine list-strategies --key-id <KEY_ID>
npx @vincentai/cli@latest trading-engine get-strategy --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Update a DRAFT strategy. Pass only the fields you want to change — config is a partial object.
npx @vincentai/cli@latest trading-engine update-strategy --key-id <KEY_ID> --strategy-id <STRATEGY_ID> \
--name "Updated Name" --config '{ "thesis": { "confidence": 0.8, "reasoning": "Updated reasoning" } }'
Parameters:
--strategy-id: Strategy ID (required)--name: New strategy name--config: Partial strategy config JSON — only include fields to update--data-source-secret-id: DATA_SOURCES secret ID--poll-interval: New polling interval in minutesStarts driver monitoring and signal pipeline processing. Strategy must be in DRAFT status.
npx @vincentai/cli@latest trading-engine activate --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Stops monitoring. Strategy must be ACTIVE.
npx @vincentai/cli@latest trading-engine pause --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Resumes monitoring. Strategy must be PAUSED.
npx @vincentai/cli@latest trading-engine resume --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Permanently stops a strategy. Cannot be undone.
npx @vincentai/cli@latest trading-engine archive --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Creates a new DRAFT with the same config, incremented version number, and a link to the parent version.
npx @vincentai/cli@latest trading-engine duplicate-strategy --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
See all versions of a strategy lineage.
npx @vincentai/cli@latest trading-engine versions --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
See the LLM decision log for a strategy — what data triggered it, what the LLM decided, what actions were taken, and the cost.
npx @vincentai/cli@latest trading-engine invocations --key-id <KEY_ID> --strategy-id <STRATEGY_ID> --limit 20
See aggregate LLM costs for all strategies under a secret.
npx @vincentai/cli@latest trading-engine costs --key-id <KEY_ID>
See performance metrics for a strategy: P&L, win rate, trade count, and per-instrument breakdown.
npx @vincentai/cli@latest trading-engine performance --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
Add a driver with "sources": ["web_search"]. The engine periodically searches Brave for the driver's keywords and triggers the signal pipeline when new results appear.
{
"name": "AI News Monitor",
"weight": 1.5,
"direction": "bullish",
"monitoring": {
"keywords": ["AI tokens", "GPU shortage", "prediction market regulation"],
"embeddingAnchor": "AI technology investment trends",
"sources": ["web_search"]
}
}
Each keyword is searched independently. Results are deduplicated — the same URLs won't trigger the pipeline twice.
Add a driver with "sources": ["twitter"]. The engine periodically checks the specified entities for new tweets.
{
"name": "Crypto Twitter",
"weight": 1.0,
"direction": "contextual",
"monitoring": {
"entities": ["@DeepSeek", "@nvidia", "@OpenAI"],
"keywords": ["AI", "GPU"],
"sources": ["twitter"]
}
}
Tweets are deduplicated by tweet ID — only genuinely new tweets trigger the pipeline.
Add a driver with "sources": ["newswire"]. The engine periodically polls Finnhub's market news API and triggers the pipeline when new headlines matching your keywords appear.
{
"name": "Market News",
"weight": 1.5,
"direction": "contextual",
"monitoring": {
"keywords": ["artificial intelligence", "GPU shortage", "semiconductor"],
"sources": ["newswire"]
}
}
Headlines and summaries are matched case-insensitively. Articles are deduplicated by headline hash with a sliding window.
Note: Requires a FINNHUB_API_KEY env var on the server. Finnhub's free tier allows 60 API calls/min. No per-call credit deduction.
Price triggers are evaluated in real-time via the Polymarket WebSocket feed. When a price condition is met, the signal pipeline is invoked with the price data.
Trigger types:
ABOVE — triggers when price exceeds a thresholdBELOW — triggers when price drops below a thresholdCHANGE_PCT — triggers on a percentage change from reference pricePrice triggers are one-shot: once fired, they're marked as consumed. The LLM can create new triggers if needed.
The thesis is your structured directional view. Good theses include:
tradeRules.exit.thesisInvalidation to define what would break your thesisWhen the LLM is invoked, it can use these tools (depending on strategy config):
| Tool | Description | Requires |
|---|---|---|
place_trade | Buy or sell a position | canTrade: true in trade rules |
set_stop_loss | Set a stop-loss rule on a position | canSetRules: true in trade rules |
set_take_profit | Set a take-profit rule | canSetRules: true in trade rules |
set_trailing_stop | Set a trailing stop | canSetRules: true in trade rules |
alert_user | Send an alert without trading | Always available |
no_action | Do nothing (with reasoning) | Always available |
Every LLM invocation is metered:
dataSourceCreditUsd)Typical LLM invocation cost: $0.05–$0.30 depending on context size.
Trade rules execute automatically when price conditions are met — no LLM involved. These are stop-loss, take-profit, and trailing stop rules that protect your positions.
npx @vincentai/cli@latest trading-engine status --key-id <KEY_ID>
# Returns: worker status, active rules count, last sync time, circuit breaker state
Automatically sell a position if price drops below a threshold:
# Polymarket — triggerPrice is 0–1 (outcome token price)
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--market-id 0x123... --token-id 456789 \
--rule-type STOP_LOSS --trigger-price 0.40
# HyperLiquid — triggerPrice is absolute USD price, marketId and tokenId are the coin name
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--venue hyperliquid --market-id BTC --token-id BTC \
--rule-type STOP_LOSS --trigger-price 95000
Parameters:
--venue: polymarket (default) or hyperliquid--market-id: Polymarket condition ID, or coin name for HyperLiquid (e.g. BTC, ETH)--token-id: Polymarket outcome token ID, or coin name for HyperLiquid--rule-type: STOP_LOSS (sells if price <= trigger), TAKE_PROFIT (sells if price >= trigger), or TRAILING_STOP--trigger-price: Price threshold — 0 to 1 for Polymarket, absolute USD price for HyperLiquidAutomatically sell a position if price rises above a threshold:
# Polymarket
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--market-id 0x123... --token-id 456789 \
--rule-type TAKE_PROFIT --trigger-price 0.75
# HyperLiquid
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--venue hyperliquid --market-id ETH --token-id ETH \
--rule-type TAKE_PROFIT --trigger-price 4500
A trailing stop moves the stop price up as the price rises:
# Polymarket
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--market-id 0x123... --token-id 456789 \
--rule-type TRAILING_STOP --trigger-price 0.45 --trailing-percent 5
# HyperLiquid
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--venue hyperliquid --market-id SOL --token-id SOL \
--rule-type TRAILING_STOP --trigger-price 170 --trailing-percent 5
Trailing stop behavior:
--trailing-percent is percent points (e.g. 5 = 5%)candidateStop = currentPrice * (1 - trailingPercent/100)candidateStop > current triggerPrice, updates triggerPricetriggerPrice never moves downcurrentPrice <= triggerPrice# All rules
npx @vincentai/cli@latest trading-engine list-rules --key-id <KEY_ID>
# Filter by status
npx @vincentai/cli@latest trading-engine list-rules --key-id <KEY_ID> --status ACTIVE
npx @vincentai/cli@latest trading-engine update-rule --key-id <KEY_ID> --rule-id <RULE_ID> --trigger-price 0.45
npx @vincentai/cli@latest trading-engine delete-rule --key-id <KEY_ID> --rule-id <RULE_ID>
npx @vincentai/cli@latest trading-engine positions --key-id <KEY_ID>
# All events
npx @vincentai/cli@latest trading-engine events --key-id <KEY_ID>
# Events for specific rule
npx @vincentai/cli@latest trading-engine events --key-id <KEY_ID> --rule-id <RULE_ID>
# Paginated
npx @vincentai/cli@latest trading-engine events --key-id <KEY_ID> --limit 50 --offset 100
Event types:
RULE_CREATED — Rule was createdRULE_TRAILING_UPDATED — Trailing stop moved triggerPrice upwardRULE_EVALUATED — Worker checked the rule against current priceRULE_TRIGGERED — Trigger condition was metACTION_PENDING_APPROVAL — Trade requires human approval, rule pausedACTION_EXECUTED — Trade executed successfullyACTION_FAILED — Trade execution failedRULE_CANCELED — Rule was manually canceledACTIVE — Rule is live and being monitoredTRIGGERED — Condition was met, trade executedPENDING_APPROVAL — Trade requires human approval; rule pausedCANCELED — Manually canceled before triggeringFAILED — Triggered but trade execution failednpx @vincentai/cli@latest polymarket bet --key-id <KEY_ID> --token-id 123456789 --side BUY --amount 10 --price 0.55
npx @vincentai/cli@latest trading-engine create-strategy --key-id <KEY_ID> \
--name "Bitcoin Bull Thesis" \
--config '{
"instruments": [
{ "id": "123456789", "type": "binary", "venue": "polymarket" }
],
"thesis": {
"estimate": 0.85,
"direction": "long",
"confidence": 0.7,
"reasoning": "Bitcoin is likely to break $100k on ETF inflows"
},
"drivers": [
{
"name": "ETF News",
"weight": 2.0,
"direction": "bullish",
"monitoring": {
"keywords": ["bitcoin ETF inflows", "bitcoin institutional"],
"sources": ["web_search", "newswire"]
}
},
{
"name": "Crypto Twitter",
"weight": 1.0,
"direction": "contextual",
"monitoring": {
"entities": ["@BitcoinMagazine", "@saborskycnbc"],
"sources": ["twitter"]
}
}
],
"escalation": {
"signalScoreThreshold": 0.3,
"highConfidenceThreshold": 0.8,
"maxWakeFrequency": "1 per 15m",
"batchWindow": "5m"
},
"tradeRules": {
"entry": { "minEdge": 0.05 },
"autoActions": { "stopLoss": -0.15, "takeProfit": 0.30, "trailingStop": -0.05 },
"exit": { "thesisInvalidation": ["ETF outflows accelerate above $500M/week"] },
"sizing": { "method": "edgeScaled", "maxPosition": 100, "maxPortfolioPct": 20, "maxTradesPerDay": 5 }
}
}' \
--poll-interval 10
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--market-id 0xabc... --token-id 123456789 \
--rule-type STOP_LOSS --trigger-price 0.40
npx @vincentai/cli@latest trading-engine activate --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
# Check strategy invocations
npx @vincentai/cli@latest trading-engine invocations --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
# Check trade rule events
npx @vincentai/cli@latest trading-engine events --key-id <KEY_ID>
# Check costs
npx @vincentai/cli@latest trading-engine costs --key-id <KEY_ID>
# Check performance
npx @vincentai/cli@latest trading-engine performance --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
npx @vincentai/cli@latest hyperliquid trade --key-id <KEY_ID> \
--coin BTC --is-buy true --sz 0.001 --limit-px 106000 --order-type market
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--venue hyperliquid --market-id BTC --token-id BTC \
--rule-type STOP_LOSS --trigger-price 95000
npx @vincentai/cli@latest trading-engine create-rule --key-id <KEY_ID> \
--venue hyperliquid --market-id BTC --token-id BTC \
--rule-type TAKE_PROFIT --trigger-price 115000
npx @vincentai/cli@latest trading-engine create-strategy --key-id <KEY_ID> \
--name "BTC Perp Momentum" \
--config '{
"instruments": [
{ "id": "BTC", "type": "perp", "venue": "hyperliquid" }
],
"thesis": {
"estimate": 115000,
"direction": "long",
"confidence": 0.7,
"reasoning": "ETF inflows accelerating, halving supply shock imminent"
},
"drivers": [
{
"name": "ETF News",
"weight": 2.0,
"direction": "bullish",
"monitoring": {
"keywords": ["bitcoin ETF inflows", "bitcoin institutional"],
"sources": ["web_search", "newswire"]
}
}
],
"escalation": {
"signalScoreThreshold": 0.3,
"highConfidenceThreshold": 0.8,
"maxWakeFrequency": "1 per 15m",
"batchWindow": "5m"
},
"tradeRules": {
"entry": { "minEdge": 0.05 },
"autoActions": { "stopLoss": -0.10, "takeProfit": 0.25, "trailingStop": -0.05 },
"sizing": { "method": "edgeScaled", "maxPosition": 500, "maxPortfolioPct": 20, "maxTradesPerDay": 5 }
}
}' \
--poll-interval 10
npx @vincentai/cli@latest trading-engine activate --key-id <KEY_ID> --strategy-id <STRATEGY_ID>
npx @vincentai/cli@latest trading-engine events --key-id <KEY_ID>
The Trading Engine runs two independent background workers:
Circuit Breaker: Both workers use a circuit breaker pattern. If a venue API fails 5+ consecutive times, the worker pauses and resumes after a cooldown. Check status with:
npx @vincentai/cli@latest trading-engine status --key-id <KEY_ID>
confidence: 0.5 and let the LLM adjust — avoid overconfidence in the initial thesisweight: 3.0 has 3x the signal score contribution of weight: 1.0edgeScaled sizing for adaptive position sizes based on thesis confidence and edgemaxPortfolioPct to limit exposure — even high-confidence strategies shouldn't risk the entire portfolioautoActions in the config or standalone rules)thesisInvalidation exit rules to define explicit conditions that should trigger position exitsWhen a user says:
Strategy creation:
{
"strategyId": "strat-123",
"name": "BTC Momentum",
"status": "DRAFT",
"version": 1
}
Rule creation:
{
"ruleId": "rule-456",
"ruleType": "STOP_LOSS",
"triggerPrice": 0.4,
"status": "ACTIVE"
}
LLM invocation log entries:
{
"invocationId": "inv-789",
"strategyId": "strat-123",
"trigger": "web_search",
"actions": ["place_trade"],
"costUsd": 0.12,
"createdAt": "2026-02-26T12:00:00.000Z"
}
| Error | Cause | Resolution |
|---|---|---|
401 Unauthorized | Invalid or missing API key | Check that the key-id is correct; re-link if needed |
403 Policy Violation | Trade blocked by server-side policy | User must adjust policies at heyvincent.ai |
402 Insufficient Credit | Not enough credit for LLM invocation | User must add credit at heyvincent.ai |
INVALID_STATUS_TRANSITION | Strategy can't transition to requested state | Check current status (e.g., only DRAFT can activate) |
CIRCUIT_BREAKER_OPEN | Polymarket API failures triggered circuit breaker | Wait for cooldown; check status command |
429 Rate Limited | Too many requests or concurrent LLM invocations | Wait and retry with backoff |
Key not found | API key was revoked or never created | Re-link with a new token from the wallet owner |
localhost:19000 — only accessible from the same VPS