Skip to content

Create#

POST https://api.pro.anboto.xyz/api/v2/strategies/campaigns/create

A delta-neutral long+short pair on Papertrade, repeated until stopped. 409 while the team already runs a campaign (one at a time per account), or while one in error still holds legs. ApiKeyAuth required.

Body params#

type string required
Campaign type; today only paper_farm
Default: paper_farm
exchange_id string required
The venue to farm on; today only papertrade
Default: papertrade
symbol string required
Anboto symbol, BTC/USDC:USDC or ETH/USDC:USDC
Example: BTC/USDC:USDC
leverage integer required
Leverage of each leg, 1-1000 (the leverage guard caps it well below 1000 in practice)
margin_per_leg number required
Margin to post per leg at the start of a cycle, in USDC (>= 10)
target_move_bps integer required
Winning-leg move, in basis points, that closes a cycle (50-100)
max_cycles integer
Stop after this many cycles; omit or null for unlimited
budget_usdc number
Stop once this much USDC has been spent; omit or null for no cap
Stop conditions the campaign watches on its own, besides an explicit stop/kill

Headers#

Header
X-API-KEY string required
X-TIMESTAMP int64 ms required
X-SIGN string required
X-RECV-WINDOW int, default 5000

Responses#

201 The campaign, started
400 Invalid parameter, or the leverage guard rejected the combination, {"code", "error", "errors"?}
401 Unauthorized: missing or unknown API key
403 Forbidden: the API key is read-only, or the paper_farm_bot feature is not enabled for this team, {"code", "error", "errors"?}
409 The team already has an active campaign, {"code", "error", "errors"?}
429 Rate limit exceeded, retry later, {"code", "error", "errors"?}
503 The strategies service is unavailable, {"code", "error", "errors"?}

Response fields — StrategyCampaign#

id string
Campaign id (UUID)
type string
Campaign type; today always paper_farm
status enum
running | paused | stopped | done | error — running, paused, stopped, done, error
stopping boolean
A graceful stop or a hard kill is under way. Independent of status: can be true while status stays error (the open legs are being closed before the campaign reaches stopped)
phase string
Cycle phase the runner is in: idle, opening, open, closing or settling
stop_reason string
Why a stopped/done campaign ended: cycles_done, budget_spent, session_key_expiring, queued_share, user_stop or user_kill. Empty while still running
cycles_done integer
Cycles completed so far
cycles_planned integer
max_cycles from create, or null for unlimited
paper_minted number
Total PAPER minted by this campaign's losing legs
usdc_converted number
Total USDC spent converting minted PAPER (haircut + fee)
queued_usdc number
Profit queued to fund a future cycle's margin instead of being paid out
haircut_paid number
Total haircut paid converting PAPER to USDC
fees_paid number
Total trading fees paid by this campaign
current_legs array[StrategyCampaignLeg]
Open legs of the current cycle (0 while idle, up to 2 while open)
current_legs[].position_id string
The venue's position id, or null while the open is still unconfirmed
current_legs[].side string
long or short
current_legs[].entry number
Entry (mark) price the leg opened at
current_legs[].move_pct number
Signed move since entry, in percent, from this leg's point of view
current_legs[].bust number
This leg's liquidation (bust) price
current_legs[].size number
Position size
current_legs[].margin number
Margin posted for this leg, in USDC
current_legs[].leverage integer
Leverage of this leg
current_legs[].mark_price number
Latest mark price used to watch this leg
current_legs[].parent_order_id string
The order id that opened this leg
current_legs[].opened_at date-time
UTC time the leg opened, ISO 8601
The campaign's most recently closed cycle
last_cycle.pnl_usdc number
Net result of the cycle, in USDC (can be negative)
last_cycle.paper number
PAPER minted by the losing leg of this cycle
last_cycle.closed_at date-time
UTC time both legs were closed, ISO 8601
last_cycle.multileg_order_id string
The multileg (or paired single) order id the close went through, when known
last_error string
Why the campaign is in error; empty otherwise
symbol string
Anboto symbol being farmed, e.g. BTC/USDC:USDC
leverage integer
Leverage of each leg
margin_per_leg number
Margin posted per leg at the start of a cycle, in USDC
target_move_bps integer
Winning-leg move, in basis points, that closes the cycle
budget_usdc number
budget_usdc from create, or null for no spend cap
Stop conditions the campaign watches on its own, besides an explicit stop/kill
stop.queued_share_pct number
Stop once this percent (0-100) of a new cycle's margin would come from queued profit rather than fresh budget. Null or omitted: off
stop.stop_before_key_expiry boolean
Stop gracefully once the Papertrade session key has under an hour left, rather than risk a cycle stuck open past expiry
Default: true
stop.hard_kill_on_stop boolean
If a stop rule fires mid-cycle, close both legs at market now instead of waiting for the target
Default: false
created_at date-time
UTC time the campaign was created, ISO 8601
started_at date-time
UTC time the campaign started, ISO 8601
stopped_at date-time
UTC time the campaign stopped, ISO 8601, or null while still running
body='{
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
}'

curl -X POST "$BASE/api/v2/strategies/campaigns/create" \
  -H "X-API-KEY: $API_KEY" -H "X-TIMESTAMP: $ts" \
  -H "X-SIGN: $sign" \
  -H "Content-Type: application/json" -d "$body"
payload = {
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
}

r = requests.post(f"{BASE}/api/v2/strategies/campaigns/create",
    data=json.dumps(payload)
    headers=signed_headers(payload))
print(r.json())
let payload = serde_json::json!({
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
});

let resp = client
    .post(format!("{BASE}/api/v2/strategies/campaigns/create"))
    .headers(signed_headers(&payload))
    .json(&payload)
    .send()?;
println!("{}", resp.text()?);
payload := map[string]any{
    "type": "string",
    "exchange_id": "string",
    "symbol": "BTC/USDC:USDC",
    "leverage": 0,
    "margin_per_leg": 0.0,
    "target_move_bps": 0,
    "max_cycles": 0,
    "budget_usdc": 0.0,
    "stop": map[string]any{
        "queued_share_pct": 0.0,
        "stop_before_key_expiry": true,
        "hard_kill_on_stop": true,
    },
}
body, _ := json.Marshal(payload)

req, _ := http.NewRequest("POST", BASE+"/api/v2/strategies/campaigns/create", bytes.NewReader(body))
for k, v := range signedHeaders(string(body)) {
    req.Header.Set(k, v)
}
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
String jsonBody = mapper.writeValueAsString(payload);
HttpResponse<String> resp = client.send(
    signedPost("/api/v2/strategies/campaigns/create", jsonBody),
    HttpResponse.BodyHandlers.ofString());
const payload: CreateStrategyCampaignRequest = {
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
};

const res = await fetch(`${BASE}/api/v2/strategies/campaigns/create`, {
  method: "POST",
  headers: signedHeaders(payload),
  body: JSON.stringify(payload),
});
const data: StrategyCampaign = await res.json();
val jsonBody = mapper.writeValueAsString(payload)
val request = signedPost("/api/v2/strategies/campaigns/create", jsonBody)
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
println(response.body())
std::string body = R"({
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
})";

cpr::Response r = cpr::Post(
    cpr::Url{BASE + "/api/v2/strategies/campaigns/create"},
    signedHeaders(body),
    cpr::Header{{"Content-Type", "application/json"}},
    cpr::Body{body});
std::cout << r.text << std::endl;
const payload = {
  "type": "string",
  "exchange_id": "string",
  "symbol": "BTC/USDC:USDC",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "max_cycles": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  }
};

const res = await fetch(`${BASE}/api/v2/strategies/campaigns/create`, {
  method: "POST",
  headers: signedHeaders(payload),
  body: JSON.stringify(payload),
});
console.log(await res.json());

Response 201

{
  "id": "string",
  "type": "string",
  "status": "running",
  "stopping": true,
  "phase": "string",
  "stop_reason": "string",
  "cycles_done": 0,
  "cycles_planned": 0,
  "paper_minted": 0.0,
  "usdc_converted": 0.0,
  "queued_usdc": 0.0,
  "haircut_paid": 0.0,
  "fees_paid": 0.0,
  "current_legs": [
    {
      "position_id": "string",
      "side": "string",
      "entry": 0.0,
      "move_pct": 0.0,
      "bust": 0.0,
      "size": 0.0,
      "margin": 0.0,
      "leverage": 0,
      "mark_price": 0.0,
      "parent_order_id": "string",
      "opened_at": "string"
    }
  ],
  "last_cycle": {
    "pnl_usdc": 0.0,
    "paper": 0.0,
    "closed_at": "string",
    "multileg_order_id": "string"
  },
  "last_error": "string",
  "symbol": "string",
  "leverage": 0,
  "margin_per_leg": 0.0,
  "target_move_bps": 0,
  "budget_usdc": 0.0,
  "stop": {
    "queued_share_pct": 0.0,
    "stop_before_key_expiry": true,
    "hard_kill_on_stop": true
  },
  "created_at": "string",
  "started_at": "string",
  "stopped_at": "string"
}