const res = await fetch('https://app.trdrs.co/api/trading/exits', {
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${process.env.TRDRS_API_KEY}`,
},
body: JSON.stringify({
"instrument": "ESU6",
"clientOrderId": "protect-esu6-1787581500",
"exits": {
"takeProfits": [
{
"quantity": 1,
"price": 6485.5
}
],
"stopLosses": [
{
"quantity": 2,
"price": 6477.5
}
]
}
}),
})
const data = await res.json()curl -X POST 'https://app.trdrs.co/api/trading/exits' \
-H "Authorization: Bearer $TRDRS_API_KEY" \
-H 'content-type: application/json' \
-d '{"instrument":"ESU6","clientOrderId":"protect-esu6-1787581500","exits":{"takeProfits":[{"quantity":1,"price":6485.5}],"stopLosses":[{"quantity":2,"price":6477.5}]}}'import requests
url = "https://app.trdrs.co/api/trading/exits"
payload = {
"instrument": "ESU6",
"clientOrderId": "protect-esu6-1787581500",
"exits": {
"takeProfits": [
{
"quantity": 1,
"price": 6485.5
}
],
"stopLosses": [
{
"quantity": 2,
"price": 6477.5
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.trdrs.co/api/trading/exits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'instrument' => 'ESU6',
'clientOrderId' => 'protect-esu6-1787581500',
'exits' => [
'takeProfits' => [
[
'quantity' => 1,
'price' => 6485.5
]
],
'stopLosses' => [
[
'quantity' => 2,
'price' => 6477.5
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.trdrs.co/api/trading/exits"
payload := strings.NewReader("{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.trdrs.co/api/trading/exits")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.trdrs.co/api/trading/exits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"bracket": {
"id": "b41e7d2a-9c58-4f03-a6b1-2e8d5c7f0a94",
"state": "active"
},
"refused": null,
"stuck": 0
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}Protect a position
Puts a bracket on an open position: up to three take profits and three stop losses, laid out as pairs along the position, nearest the entry first, each pair’s take profit and stop loss cancelling each other. Write it as exits. Distances are measured from the position’s average entry, and every price must clear the market on the side it protects, since a stop already past the market would close the position at once. The bracket protects qty of the position, or all of it that no other bracket protects. Send positionId to protect one position alone, on an account that names its positions. A stop loss that moves itself, to breakeven or trailing, is moved by trdrs on every provider that holds a bracket. No hold on the account refuses a bracket on a position: it only closes the position it protects and cannot add exposure.
Send entryOrderId in place of a position to protect a resting entry before it fills: a working order of yours on the instrument that can open a position and has filled nothing. The bracket covers all of the entry and waits on it, as a bracket placed with its entry does, and each pair rests once the fill that opens its part of the position arrives. Its prices are measured from the entry’s own price (a limit’s limit, a stop’s or a stop-limit’s trigger) and lie on their side of it, and none is held to the market, since nothing is open yet. On the paper book, which holds a waiting bracket inside its entry, the entry is replaced with itself carrying the pairs in the same step that records the bracket, and rests afterwards under your clientOrderId: the answer’s entryOrderId names it. A hold that refuses new entries refuses that replacement too.
Required key: Trading API key. A Connect app’s backend can also send it with the app’s API key, and a venue’s backend with its Venue key, which carries trader:trade, each naming one of its own traders in x-trdrs-trader: it then runs on that trader’s own accounts.
const res = await fetch('https://app.trdrs.co/api/trading/exits', {
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${process.env.TRDRS_API_KEY}`,
},
body: JSON.stringify({
"instrument": "ESU6",
"clientOrderId": "protect-esu6-1787581500",
"exits": {
"takeProfits": [
{
"quantity": 1,
"price": 6485.5
}
],
"stopLosses": [
{
"quantity": 2,
"price": 6477.5
}
]
}
}),
})
const data = await res.json()curl -X POST 'https://app.trdrs.co/api/trading/exits' \
-H "Authorization: Bearer $TRDRS_API_KEY" \
-H 'content-type: application/json' \
-d '{"instrument":"ESU6","clientOrderId":"protect-esu6-1787581500","exits":{"takeProfits":[{"quantity":1,"price":6485.5}],"stopLosses":[{"quantity":2,"price":6477.5}]}}'import requests
url = "https://app.trdrs.co/api/trading/exits"
payload = {
"instrument": "ESU6",
"clientOrderId": "protect-esu6-1787581500",
"exits": {
"takeProfits": [
{
"quantity": 1,
"price": 6485.5
}
],
"stopLosses": [
{
"quantity": 2,
"price": 6477.5
}
]
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.trdrs.co/api/trading/exits",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'instrument' => 'ESU6',
'clientOrderId' => 'protect-esu6-1787581500',
'exits' => [
'takeProfits' => [
[
'quantity' => 1,
'price' => 6485.5
]
],
'stopLosses' => [
[
'quantity' => 2,
'price' => 6477.5
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.trdrs.co/api/trading/exits"
payload := strings.NewReader("{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.trdrs.co/api/trading/exits")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.trdrs.co/api/trading/exits")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"instrument\": \"ESU6\",\n \"clientOrderId\": \"protect-esu6-1787581500\",\n \"exits\": {\n \"takeProfits\": [\n {\n \"quantity\": 1,\n \"price\": 6485.5\n }\n ],\n \"stopLosses\": [\n {\n \"quantity\": 2,\n \"price\": 6477.5\n }\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"bracket": {
"id": "b41e7d2a-9c58-4f03-a6b1-2e8d5c7f0a94",
"state": "active"
},
"refused": null,
"stuck": 0
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}Authorizations
The Trading API key (trdrs_sk_…), for market data, orders and account state on the accounts its owner holds. Use it server-to-server, and keep it on your server. Its scope is named trading in the API.
Query Parameters
The provider the account is at, such as rithmic or paper. Omit it to use your default provider. An unknown value is refused with 400, never replaced with another provider.
The account number at that provider. It must be one of your own accounts, or the call is refused with 400.
Body
A bracket to put on an open position, or on a resting entry: its take profits and stop losses.
The instrument.
Your own id for this protection, and its idempotency key. On the paper book a resting entry rests under this id once the bracket is on it.
The take profits and stop losses. Distances are measured from the position's average entry, or from a resting entry's own price.
Show child attributes
Show child attributes
{
"takeProfits": [{ "quantity": 1, "price": 6485.5 }],
"stopLosses": [{ "quantity": 2, "price": 6477.5 }]
}
How much of the position to protect. Omit it to protect all of it that no other bracket protects. A resting entry is protected whole, so a protection that names one carries no qty.
The one position to protect, by the id the account snapshot gives it: a ticket on an account that holds separate tickets, or the net position. Only where the account names its positions; anywhere else the request is refused, rather than applied to the instrument.
A resting entry to protect before it fills, by its providerOrderId, in place of a position: a working order of yours on the instrument that can open a position and has filled nothing. The bracket covers all of the entry and waits on it, and each pair rests once the fill that opens its part of the position arrives. Send it without positionId and qty.
"234992311"
Response
The bracket rests, or on a resting entry waits on it (pending_entry), with entryOrderId naming the order the entry rests as. refused is the provider's reason where it refused a pair, whose part of the position the bracket then shows as unprotected.
A bracket after a protection, a change or a withdrawal.
The bracket.
Show child attributes
Show child attributes
{
"id": "b41e7d2a-9c58-4f03-a6b1-2e8d5c7f0a94",
"state": "active"
}
The provider's reason where it refused a pair, or null. A change of a bracket the provider refuses leaves the bracket as it was. On a protection, the refused pair's part of the position shows as unprotected and the other pairs rest.
How many orders the change meant to cancel that would not cancel and still rest. The bracket keeps them, so the account stream shows them.
On a protection of a resting entry, and on a change or a withdrawal of a bracket waiting on one: the order the entry rests as afterwards. The paper book replaces the entry with itself to change the pairs it carries, so there the entry rests under a new id. Every other provider keeps the entry's id. Absent on every other answer.
"protect-entry-esu6-1787581560"