AGENT SKILL

polyhub_copy

Manage copy-trading tasks, view signals, positions and trades on Polyhub using an API key.

License 许可
AGPL-3.0
最近更新
2026-11-12
Stars
★ 81
Forks
17

01 前言 Front Matter

name
polyhub_copy
description
Manage copy-trading tasks, view signals, positions and trades on Polyhub using an API key.

02 资源文件 2

03 SKILL.md 正文

Polyhub Copy Skill

Version: v0.3.3

When to use

Use this skill when the user asks about:

  • Listing, creating, updating or deleting copy-trading tasks
  • Viewing copy task positions or trades
  • Selling positions (single or all)
  • Batch updating or deleting copy tasks
  • Viewing total PnL across copy tasks
  • Viewing copy signals and signal stats
  • Managing take-profit / stop-loss (TPSL) rules

Requirements

  • POLYHUB_API_BASE_URL — Polyhub API server URL (e.g. https://api.polyhub.example.com)
  • POLYHUB_API_KEY — API key (must start with phub_)
  • curl must be available in the runtime environment.
  • jq is strongly recommended for building JSON payloads safely.

Safety rules

  • Never print POLYHUB_API_KEY in the output.
  • Treat all IDs and user-provided JSON fields as untrusted input.
  • For write actions (POST/PATCH/DELETE), repeat the action summary and wait for explicit user confirmation before calling the API.
  • Prefer building JSON with jq -n instead of interpolating raw shell strings.
  • Validation rules:
    • taskId must be a 24-char hex MongoDB ObjectID: ^[0-9a-fA-F]{24}$
    • ruleId must be non-empty.
    • taskIds in batch operations must all match the taskId format.
    • status for positions should only be passed when the user explicitly requests a filter.

Tools

Use the bash tool to call the API with curl.

Fast Path

For common intents, map user requests like this:

  • “列出任务” -> GET /api/v1/copy-tasks
  • “暂停/恢复/停止任务” -> PATCH /api/v1/copy-tasks/{taskId} with status
  • “改跟单模式/改单笔或总仓位限制” -> PATCH /api/v1/copy-tasks/{taskId} with taskConfig
  • “看持仓/成交” -> GET /positions or GET /trades
  • “卖出某个仓位/全部仓位” -> POST /sell or POST /sell-all
  • “看跟单信号/统计” -> GET /api/v1/copy-signals or /stats

Curl base setup

BASE="${POLYHUB_API_BASE_URL%/}"
AUTH=(-H "Authorization: Bearer $POLYHUB_API_KEY" -H "Content-Type: application/json")

Validate helpers

if [[ ! "$TASK_ID" =~ ^[0-9a-fA-F]{24}$ ]]; then echo "Invalid taskId"; exit 2; fi
if [[ -z "${RULE_ID:-}" ]]; then echo "Invalid ruleId"; exit 2; fi
for id in "${TASK_IDS[@]}"; do
  if [[ ! "$id" =~ ^[0-9a-fA-F]{24}$ ]]; then
    echo "Invalid taskId in batch: $id"
    exit 2
  fi
done

Copy Tasks

Action: List copy tasks

  • GET /api/v1/copy-tasks
  • Query params: includeDeleted=true|false (default: true)
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks?includeDeleted=true"

Guidance:

  • Use includeDeleted=true when the user wants a full history or is looking for a previously deleted task.
  • Use includeDeleted=false when the user only wants active/non-deleted tasks.

Action: Create copy task

  • POST /api/v1/copy-tasks
  • Required field: targetTrader (Polymarket address of the smart money)
  • Optional fields from domain.CreateCopyTaskPayload: targetUsername, taskConfig, filteredByTag, tpslRules

Before calling: ask the user for targetTrader at minimum. Summarize and confirm.

Minimum fields to ask:

  • Always ask: targetTrader
  • Ask only if needed: filteredByTag, targetUsername
  • If the user wants custom copy behavior: ask for the relevant taskConfig fields explicitly
  • If the user wants TPSL on creation: ask for tpslRules
PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  '{targetTrader: $targetTrader}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks" \
  -d "$PAYLOAD"

If the user wants advanced config, build taskConfig and tpslRules explicitly instead of accepting arbitrary JSON.

Action: Get copy task

  • GET /api/v1/copy-tasks/{taskId}
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID"

Action: Update copy task

  • PATCH /api/v1/copy-tasks/{taskId}
  • Body: partial JSON. Common fields:
    • status
    • taskConfig
    • filteredByTag
    • targetUsername
    • tpslRules using create / update / cancel

Before calling: validate taskId, summarize changes, and confirm.

Minimum fields to ask:

  • Always ask: taskId
  • Ask only the fields the user wants to change:
    • Task lifecycle: status
    • Copy behavior: taskConfig
    • Trader label/tag filters: targetUsername, filteredByTag
    • TPSL maintenance: tpslRules
PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  '{status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update taskConfig safely

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  --argjson maxBoughtPerMarket 50 \
  '{taskConfig: {buyOnly: $buyOnly, maxBoughtPerMarket: $maxBoughtPerMarket}}')"

Example: change copy mode to ONE_TO_ONE

PAYLOAD="$(jq -n \
  '{taskConfig: {copyMode: "ONE_TO_ONE"}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: change copy mode to FIXED_SIZE

FIXED_SIZE should include taskConfig.fixedAmount.

PAYLOAD="$(jq -n \
  --argjson fixedAmount 5 \
  '{taskConfig: {copyMode: "FIXED_SIZE", fixedAmount: $fixedAmount}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Copy mode guidance:

  • taskConfig.copyMode is the field that controls the task's copy mode.
  • Confirmed mode values in the current codebase: ONE_TO_ONE, FIXED_SIZE.
  • If switching to FIXED_SIZE, also ask for taskConfig.fixedAmount.
  • If switching to ONE_TO_ONE, omit fixedAmount unless the user explicitly wants to keep it stored.

TaskConfig field guidance

Use these fields when the user wants to change task-level copy behavior. Prefer changing only the fields the user explicitly asked for.

Common fields:

  • taskConfig.copyMode
    • Controls copy sizing mode.
    • Confirmed values: ONE_TO_ONE, FIXED_SIZE.
  • taskConfig.fixedAmount
    • Used when copyMode=FIXED_SIZE.
    • Represents a fixed notional amount for copied orders.
  • taskConfig.navMultiplier
    • Multiplies the computed order size.
    • Current default in backend user config is 1.
  • taskConfig.positionUtilization
    • Position utilization / max amount per copy-trade style control.
    • Backend default is 0, which means no filter.
  • taskConfig.buyOnly
    • If true, non-BUY signals are skipped.
  • taskConfig.buyLimitPerTradeMin
    • Minimum allowed copied buy amount per trade.
    • 0 effectively means no lower bound.
  • taskConfig.buyLimitPerTradeMax
    • Maximum allowed copied buy amount per trade.
    • 0 effectively means no upper bound.
  • taskConfig.maxBoughtPerCopyTask
    • Caps total bought amount at task level.
    • 0 effectively means no cap.
  • taskConfig.maxBoughtPerMarket
    • Caps total bought amount for a single market inside the task.
    • 0 effectively means no cap.
  • taskConfig.priceRangeMin
    • Minimum allowed execution price.
    • Default from user config is typically 0.01.
  • taskConfig.priceRangeMax
    • Maximum allowed execution price.
    • Default from user config is typically 0.99.
  • taskConfig.minLiquidity
    • Minimum market liquidity filter.
    • 0 effectively means no filter.
  • taskConfig.minFillSize
    • Minimum fill shares filter.
    • 0 effectively means no filter.
  • taskConfig.maxPriceDeviation
    • Maximum allowed price deviation percentage.
  • taskConfig.expiryHours
    • Order expiry configuration for copied orders.
    • Backend default from user config is typically 0.
  • taskConfig.convictionLevel
    • Conviction-based filter level.
    • Confirmed values: OFF, LOOSE, STANDARD, STRICT.
    • Backend default is STANDARD.

Recommended clarification rules before updating:

  • If the user says “改成固定金额跟单”, ask for fixedAmount.
  • If the user says “放大/缩小仓位”, clarify whether they mean navMultiplier or fixedAmount.
  • If the user says “限制单笔金额”, clarify whether they mean buyLimitPerTradeMin, buyLimitPerTradeMax, or both.
  • If the user says “限制总仓位”, clarify whether they mean maxBoughtPerCopyTask or maxBoughtPerMarket.
  • If the user says “限制价格区间”, ask for both priceRangeMin and priceRangeMax.
  • If the user says “调严格一点/调宽松一点”, clarify whether they mean convictionLevel.
  • If the user says “恢复跟单/暂停跟单/停止任务”, clarify whether they mean task status or a taskConfig change.

Example: update navMultiplier

PAYLOAD="$(jq -n \
  --argjson navMultiplier 1.5 \
  '{taskConfig: {navMultiplier: $navMultiplier}}')"

Example: enable buy-only mode

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  '{taskConfig: {buyOnly: $buyOnly}}')"

Example: limit per-trade buy amount

PAYLOAD="$(jq -n \
  --argjson min 2 \
  --argjson max 10 \
  '{taskConfig: {buyLimitPerTradeMin: $min, buyLimitPerTradeMax: $max}}')"

Example: limit total bought amount

PAYLOAD="$(jq -n \
  --argjson maxBoughtPerCopyTask 100 \
  --argjson maxBoughtPerMarket 25 \
  '{taskConfig: {maxBoughtPerCopyTask: $maxBoughtPerCopyTask, maxBoughtPerMarket: $maxBoughtPerMarket}}')"

Example: restrict execution price range

PAYLOAD="$(jq -n \
  --argjson min 0.1 \
  --argjson max 0.9 \
  '{taskConfig: {priceRangeMin: $min, priceRangeMax: $max}}')"

Example: combine multiple taskConfig changes

PAYLOAD="$(jq -n \
  --argjson buyOnly true \
  --argjson navMultiplier 1.2 \
  --argjson maxBoughtPerMarket 20 \
  '{
    taskConfig: {
      buyOnly: $buyOnly,
      navMultiplier: $navMultiplier,
      maxBoughtPerMarket: $maxBoughtPerMarket
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update convictionLevel

PAYLOAD="$(jq -n \
  --arg convictionLevel "STRICT" \
  '{taskConfig: {convictionLevel: $convictionLevel}}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Conviction level guidance:

  • OFF: disable conviction filter.
  • LOOSE: lower threshold, more signals can pass.
  • STANDARD: default mode.
  • STRICT: higher threshold, fewer signals can pass.
  • If the user gives an unknown value, normalize to one of the four explicit options before calling the API.

Example: update taskConfig and status together

PAYLOAD="$(jq -n \
  --arg status "RUNNING" \
  --argjson buyOnly true \
  '{
    status: $status,
    taskConfig: {
      buyOnly: $buyOnly
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Example: update TPSL rules safely

PAYLOAD="$(jq -n \
  --arg ruleId "$RULE_ID" \
  --argjson takeProfitPct 20 \
  '{tpslRules: {update: [{id: $ruleId, takeProfitPct: $takeProfitPct}]}}')"

Example: update task status

PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  '{status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X PATCH "$BASE/api/v1/copy-tasks/$TASK_ID" \
  -d "$PAYLOAD"

Status guidance:

  • Valid task status values for update are: RUNNING, PAUSED, STOPPED.
  • Use PAUSED when the user wants to temporarily stop following new trades.
  • Use RUNNING when the user wants to resume an active task.
  • Use STOPPED only when the user explicitly wants the task stopped rather than just paused.
  • Do not use DELETED in update payloads; deletion should use DELETE /api/v1/copy-tasks/{taskId}.
  • Prefer uppercase status values in payloads.

Action: Delete copy task

  • DELETE /api/v1/copy-tasks/{taskId}

Before calling: validate taskId and confirm.

Minimum fields to ask:

  • Always ask: taskId
curl -sS --fail-with-body "${AUTH[@]}" -X DELETE "$BASE/api/v1/copy-tasks/$TASK_ID"

Action: Get total PnL

  • GET /api/v1/copy-tasks/total-pnl
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/total-pnl"

Positions & Trades

Action: List positions

  • GET /api/v1/copy-tasks/{taskId}/positions
  • Query params: status (optional)
    • Confirmed values in current code: active, closed
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/positions?status=active"

Guidance:

  • active returns positions with remaining amount above the close threshold.
  • closed returns positions whose remaining amount is effectively zero.
  • If status is omitted or unknown, the backend currently returns all positions.
  • Prefer active / closed rather than open / closed.

Action: List trades

  • GET /api/v1/copy-tasks/{taskId}/trades
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/trades"

Action: Sell position

  • POST /api/v1/copy-tasks/{taskId}/sell
  • Required field: marketId (preferred) or conditionId
  • Optional field: amount (partial sell; omit to sell full position)

Before calling: validate taskId, summarize (which market, amount), and confirm.

Minimum fields to ask:

  • Always ask: taskId
  • Always ask one of: marketId or conditionId
  • Ask amount only when the user wants a partial sell
PAYLOAD="$(jq -n \
  --arg marketId "..." \
  --argjson amount 10 \
  '{marketId: $marketId, amount: $amount}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell" \
  -d "$PAYLOAD"

Action: Sell all positions

  • POST /api/v1/copy-tasks/{taskId}/sell-all

Before calling: validate taskId and confirm — this sells ALL positions for the task.

Minimum fields to ask:

  • Always ask: taskId
curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/sell-all"

Batch Operations

Action: Batch update tasks

  • POST /api/v1/copy-tasks/batch-update
  • Required: taskIds (array of task IDs)
  • Optional: status (string), taskConfig (object)

Before calling: summarize which tasks and what changes, and confirm.

Minimum fields to ask:

  • Always ask: taskIds
  • Ask at least one of: status, taskConfig
TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --arg status "PAUSED" \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{taskIds: $taskIds, status: $status}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-update" \
  -d "$PAYLOAD"

Example: batch update taskConfig.copyMode

TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{
    taskIds: $taskIds,
    taskConfig: {
      copyMode: "ONE_TO_ONE"
    }
  }')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-update" \
  -d "$PAYLOAD"

Batch update guidance:

  • status uses the same allowed values as single-task update: RUNNING, PAUSED, STOPPED.
  • taskConfig replaces the task config object sent in the batch request, so only send fields you intentionally want to set.
  • If neither status nor taskConfig is provided, the backend currently just returns the selected tasks.
  • Use batch update only when the same change should apply to all listed tasks.

Action: Batch delete tasks

  • POST /api/v1/copy-tasks/batch-delete
  • Required: taskIds (array of task IDs)

Before calling: confirm deletion.

Minimum fields to ask:

  • Always ask: taskIds
TASK_IDS=("64f0c7e7b8e4f8c1a1b2c3d4" "64f0c7e7b8e4f8c1a1b2c3d5")
PAYLOAD="$(jq -n \
  --argjson taskIds "$(printf '%s\n' "${TASK_IDS[@]}" | jq -R . | jq -s .)" \
  '{taskIds: $taskIds}')"

curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/batch-delete" \
  -d "$PAYLOAD"

Copy Signals

Action: List copy signals

  • GET /api/v1/copy-signals
  • Query params:
    • limit (int, default 50)
    • cursor (int, offset for pagination)
    • since (RFC3339 timestamp)
    • sinceCreatedAt (RFC3339 timestamp)
    • action (one of: COPIED, SKIPPED, FAILED, RECEIVED)
    • trader (address filter)
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-signals?limit=20&action=COPIED"

Prefer composing the query from user intent. Only pass filters the user asked for.

Validation:

  • limit must be a positive integer.
  • cursor must be a non-negative integer.
  • since and sinceCreatedAt must be RFC3339 or RFC3339Nano timestamps.
  • Valid action values are: COPIED, SKIPPED, FAILED, RECEIVED.

Action guidance:

  • RECEIVED: the signal was ingested.
  • COPIED: the copy order was placed successfully.
  • SKIPPED: the signal was intentionally not copied because checks or limits blocked it.
  • FAILED: the system attempted processing but failed.

Example: fetch signals since a specific time

curl -sS --fail-with-body "${AUTH[@]}" \
  "$BASE/api/v1/copy-signals?since=2026-03-01T00:00:00Z&limit=50"

Example: fetch next page using cursor

curl -sS --fail-with-body "${AUTH[@]}" \
  "$BASE/api/v1/copy-signals?cursor=50&limit=50"

Action: Stream copy signals

  • GET /api/v1/copy-signals/stream
  • Response type: Server-Sent Events (SSE)
curl -sS -N "${AUTH[@]}" "$BASE/api/v1/copy-signals/stream"

Stream guidance:

  • Use this only when the user explicitly wants real-time signal updates.
  • Expect events such as ready and copy_signal.
  • Keep the connection open; this is not a one-shot request.

Action: Get signal stats

  • GET /api/v1/copy-signals/stats

Returns signal counts grouped by action and by trader.

curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-signals/stats"

TPSL Rules (Take-Profit / Stop-Loss)

Action: Get TPSL rule

  • GET /api/v1/copy-tasks/{taskId}/tpsl-rules/{ruleId}

Minimum fields to ask:

  • Always ask: taskId, ruleId
curl -sS --fail-with-body "${AUTH[@]}" "$BASE/api/v1/copy-tasks/$TASK_ID/tpsl-rules/$RULE_ID"

Action: Simulate TPSL rule

  • POST /api/v1/copy-tasks/{taskId}/tpsl-rules/{ruleId}/simulate

Minimum fields to ask:

  • Always ask: taskId, ruleId
curl -sS --fail-with-body "${AUTH[@]}" -X POST "$BASE/api/v1/copy-tasks/$TASK_ID/tpsl-rules/$RULE_ID/simulate"

TPSL inline payload guidance

When creating a task, tpslRules items may include:

  • sourceOrderId, sourceType
  • Optional entry info: conditionId, tokenId, assetId, entryPrice, entrySize, side
  • Optional risk config: takeProfitPct, takeProfitProb, stopLossPct, limitPriceOffset, orderTimeout, fallbackToMarket

When updating a task, tpslRules should be wrapped as:

  • create: array of new inline rules
  • update: array of {id, ...patchFields}
  • cancel: array of ruleId

Example: create a task with one TPSL rule

PAYLOAD="$(jq -n \
  --arg targetTrader "0x..." \
  --arg sourceOrderId "order_123" \
  --arg sourceType "copy_trade" \
  --argjson takeProfitPct 20 \
  --argjson stopLossPct 10 \
  '{
    targetTrader: $targetTrader,
    tpslRules: [
      {
        sourceOrderId: $sourceOrderId,
        sourceType: $sourceType,
        takeProfitPct: $takeProfitPct,
        stopLossPct: $stopLossPct
      }
    ]
  }')"

Error handling

  • 401: API key missing/invalid/expired/disabled. Ask user to check or regenerate key.
  • 404: Task or resource not found. Ask user to verify taskId.
  • 409: Task already exists (duplicate targetTrader).
  • 422: Copy task limit exceeded.
  • 400: Invalid payload or invalid query param such as includeDeleted.
  • 5xx: Server error. Retry once with backoff; if still failing, report response body.

04 同仓库更多 Skills