Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,11 @@ typings/

# next.js build output
.next

# Build artifacts from the REST v3 examples (compiled locally, never committed)
.build/
.dart_tool/
target/
.gradle/
rest-v3/dotnet/bin/
rest-v3/dotnet/obj/
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ For more information about the SDK, please refer to the [SDK documentation](http
- [Swift](https://github.com/foxbit-group/foxbit-api-samples/tree/main/rest-v3/swift)
- [WebSocket v2](https://github.com/foxbit-group/foxbit-api-samples/tree/main/websocket-v2)
- [JavaScript](https://github.com/foxbit-group/foxbit-api-samples/tree/main/websocket-v2/javascript)
- [WebSocket v3](https://github.com/foxbit-group/foxbit-api-samples/tree/main/websocket-v3)
- [JavaScript](https://github.com/foxbit-group/foxbit-api-samples/tree/main/websocket-v3/javascript)

## Getting Started

Expand All @@ -67,7 +69,9 @@ export FOXBIT_API_KEY=your_api_key_here
export FOXBIT_API_SECRET=your_api_secret_here
```

Make sure to replace `your_api_key_here` and `your_api_secret_here` with the actual values provided by Foxbit.
Make sure to replace `your_api_key_here` and `your_api_secret_here` with the actual values provided by Foxbit. Alternatively, keep them in a `.env` file (already git-ignored) and pass `--env-file .env` to `docker run`.

Every REST v3 example ships a pinned `Dockerfile`, so Docker is all you need to run any of them. See the [REST v3 guide](rest-v3/README.md) for the common example flow, how request signing works (including the two gotchas behind most `Invalid signature` errors), and troubleshooting tips.

## Support

Expand Down
12 changes: 12 additions & 0 deletions rest-v3-sdk/javascript/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Pinned to a full, existing multi-arch tag (do not use `latest`).
FROM node:24.18.0-alpine3.24

WORKDIR /app

# Install dependencies from the exact lockfile for reproducible builds.
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts

COPY examples.js ./

CMD ["node", "examples.js"]
49 changes: 33 additions & 16 deletions rest-v3-sdk/javascript/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,53 @@
# Foxbit API REST v3 JavaScript SDK Examples
# Foxbit REST API v3 JavaScript SDK Example

[![npm version](https://img.shields.io/npm/v/@foxbit-group/rest-api.svg?style=flat)](https://www.npmjs.com/package/@foxbit-group/rest-api)

> **SDK Oficial:** [@foxbit-group/rest-api](https://www.npmjs.com/package/@foxbit-group/rest-api)
A minimal Node.js example of the [Foxbit REST API v3](https://docs.foxbit.com.br/rest/v3/) built on the official SDK, [`@foxbit-group/rest-api`](https://www.npmjs.com/package/@foxbit-group/rest-api). It runs a complete flow in 7 steps:

This directory contains JavaScript examples demonstrating how to interact with the Foxbit API REST v3 using the official Foxbit SDK. These scripts cover a range of functionalities, from fetching market data to placing orders and managing your account, now leveraging the SDK for easier integration and improved reliability.
1. `GET /rest/v3/me` — authenticated request with no parameters.
2. `GET /rest/v3/markets/btcbrl/orderbook?depth=1` — public market data to read the best bid.
3. Compute a limit price at 50% of the best bid, floored to an integer (`btcbrl` uses `price_increment: 1.0`).
4. `POST /rest/v3/orders` — create a LIMIT BUY for 0.0001 BTC at the computed price.
5. Wait 2 seconds.
6. `GET /rest/v3/orders?market_symbol=btcbrl&state=ACTIVE` — list active orders.
7. `PUT /rest/v3/orders/cancel` — cancel the order created in step 4.

## Prerequisites
> **Warning:** this example creates a REAL order on your account (LIMIT BUY 0.0001 BTC at 50% of the market price — inside the exchange price band, but far too low to ever execute) and cancels it right after.

Before you begin, ensure you have the following prerequisites installed on your system:
## Requirements

- Node.js: These examples are written for Node.js, a JavaScript runtime built on Chrome's V8 JavaScript engine. Ensure you have the latest stable version installed.
- NPM (Node Package Manager): Comes with Node.js, used for managing dependencies.
- Docker (recommended), or
- Node.js >= 18 to run natively.

## Getting Started
## Credentials

1. **Install Dependencies**: Navigate to the JavaScript SDK examples directory in your terminal and run `npm install` to install the necessary dependencies, including the Foxbit SDK.
Create an API key at <https://app.foxbit.com.br/profile/api-key> and export it:

```bash
npm install
export FOXBIT_API_KEY="your-api-key"
export FOXBIT_API_SECRET="your-api-secret"
```

2. **Configure API Keys**: You must read the [main README file located at the root of the project](https://github.com/foxbit-group/foxbit-api-samples?tab=readme-ov-file#getting-started) for general information on setting up your environment, including configuring your API keys as environment variables.
Alternatively, put both variables in a `.env` file and use `--env-file .env` with Docker.

## Run with Docker

```bash
docker build -t foxbit-sample-sdk-javascript .
docker run --rm -e FOXBIT_API_KEY -e FOXBIT_API_SECRET foxbit-sample-sdk-javascript
# or: docker run --rm --env-file .env foxbit-sample-sdk-javascript
```

3. **Running the Examples**: To run the example, navigate to the project directory in the terminal and execute the following command:
## Run natively

```bash
node examples.js
npm install
npm start
# or: node examples.js
```

## Additional Notes
## How request signing works

These examples are meant to serve as a starting point and now utilize the official Foxbit SDK for all API interactions. It's recommended to review and test the code thoroughly before using it in a production environment.
Every authenticated request must be signed with HMAC-SHA256 and carry the headers `X-FB-ACCESS-KEY`, `X-FB-ACCESS-TIMESTAMP` (UNIX time in milliseconds) and `X-FB-ACCESS-SIGNATURE`.

For detailed API documentation, refer to the [Foxbit API Documentation](https://docs.foxbit.com.br/rest/v3/).
**The official SDK handles all of this for you.** When you build a `Configuration` with your `apiKey`/`apiSecret`, the SDK computes the prehash (`timestamp + method + path + queryString + rawBody`), signs it and attaches the headers on every call — so this example contains no manual signing code. If you need to implement signing yourself, see the dependency-free examples under [`rest-v3/`](../../rest-v3) and the full documentation at <https://docs.foxbit.com.br/rest/v3/>.
155 changes: 104 additions & 51 deletions rest-v3-sdk/javascript/examples.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,113 @@
"use strict";

/**
* Foxbit REST API v3 example using the official JavaScript SDK
* (@foxbit-group/rest-api). The SDK signs every authenticated request
* internally (HMAC-SHA256 headers), so no manual signing code is needed.
*
* Flow:
* 1. GET /rest/v3/me — current member info
* 2. GET /rest/v3/markets/btcbrl/orderbook?depth=1 — best bid (public data)
* 3. Compute a limit price at 50% of the best bid (floored to an integer)
* 4. POST /rest/v3/orders — LIMIT BUY at the computed price
* 5. Wait 2 seconds
* 6. GET /rest/v3/orders?market_symbol=btcbrl&state=ACTIVE
* 7. PUT /rest/v3/orders/cancel — cancel the created order
*/

const {
Configuration,
MarketDataApi,
MemberInfoApi,
TradingApi,
} = require("@foxbit-group/rest-api");

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
const MARKET_SYMBOL = "btcbrl";

// Fail fast if credentials are missing. Never print their values.
for (const name of ["FOXBIT_API_KEY", "FOXBIT_API_SECRET"]) {
if (!process.env[name]) {
console.error(`Missing required environment variable: ${name}`);
process.exit(1);
}
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function logStep(title, response) {
console.log("-".repeat(50));
console.log(title);
console.log(`Response (${response.status}): ${JSON.stringify(response.data)}`);
}

(async () => {
try {
console.log("FOXBIT_API_KEY:", process.env.FOXBIT_API_KEY);

const config = new Configuration({
apiKey: process.env.FOXBIT_API_KEY,
apiSecret: process.env.FOXBIT_API_SECRET,
});

// Create instance of the API clients
const memberApi = new MemberInfoApi(config);
const tradingApi = new TradingApi(config);

// Get the user information
const meResponse = await memberApi.currentMember();
console.log("Response:", meResponse.data);

// Request to create a new order
const orderResponse = await tradingApi.createOrder({
createOrderRequest: {
market_symbol: "btcbrl",
side: "BUY",
type: "LIMIT",
price: "500000.0",
quantity: "0.0001",
},
});
console.log("Response:", orderResponse.data);

await sleep(2000);

// Get active orders
const ordersResponse = await tradingApi.listOrders({
marketSymbol: "btcbrl",
state: "ACTIVE",
});
console.log("Response:", ordersResponse.data);

// Request to cancel the order
const cancelResponse = await tradingApi.cancelOrders({
cancelOrdersRequest: {
type: "ID",
id: orderResponse.data.id,
},
});
console.log("Response:", cancelResponse.data);
} catch (error) {
console.error("Failed to process request.", error.response.data);
// Print only safe error details (HTTP status + response body). Never dump the
// whole Axios error object: its request config carries the signed auth headers.
function fail(error) {
if (error.response) {
const body = JSON.stringify(error.response.data);
console.error(`Request failed (${error.response.status}): ${body}`);
} else {
console.error(`Request failed: ${error.message}`);
}
})();
process.exit(1);
}

async function main() {
const configuration = new Configuration({
apiKey: process.env.FOXBIT_API_KEY,
apiSecret: process.env.FOXBIT_API_SECRET,
});

const memberApi = new MemberInfoApi(configuration);
const marketDataApi = new MarketDataApi(configuration);
const tradingApi = new TradingApi(configuration);

// 1. Authenticated request: current member info.
const me = await memberApi.currentMember();
logStep("GET /rest/v3/me", me);

// 2. Public market data: fetch the top of the order book.
const orderbook = await marketDataApi.getOrderbook({
marketSymbol: MARKET_SYMBOL,
depth: 1,
});
logStep(`GET /rest/v3/markets/${MARKET_SYMBOL}/orderbook?depth=1`, orderbook);
const bestBid = Number(orderbook.data.bids[0][0]);

// 3. Price the order at 50% of the best bid, rounded to an integer
// (btcbrl has price_increment 1.0). This stays inside the exchange's
// accepted price band — a hardcoded value like 10.0 is rejected with
// 422 "Price out of range" — while being far too low to ever execute.
const price = String(Math.floor(bestBid * 0.5));

// 4. Create the order at the computed price.
const created = await tradingApi.createOrder({
createOrderRequest: {
market_symbol: MARKET_SYMBOL,
side: "BUY",
type: "LIMIT",
price,
quantity: "0.0001",
},
});
logStep("POST /rest/v3/orders", created);
const orderId = created.data.id;

// 5. Give the matching engine a moment before listing orders.
await sleep(2000);

// 6. List active orders — the order created above should be in the list.
const active = await tradingApi.listOrders({
marketSymbol: MARKET_SYMBOL,
state: "ACTIVE",
});
logStep(`GET /rest/v3/orders?market_symbol=${MARKET_SYMBOL}&state=ACTIVE`, active);

// 7. Cancel the order created in step 4 by its id.
const canceled = await tradingApi.cancelOrders({
cancelOrdersRequest: { type: "ID", id: orderId },
});
logStep("PUT /rest/v3/orders/cancel", canceled);
}

main().catch(fail);
22 changes: 12 additions & 10 deletions rest-v3-sdk/javascript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 9 additions & 11 deletions rest-v3-sdk/javascript/package.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
{
"name": "javascript",
"name": "foxbit-rest-v3-sdk-javascript-example",
"version": "1.0.0",
"description": "",
"private": true,
"description": "Foxbit REST API v3 example using the official JavaScript SDK (@foxbit-group/rest-api)",
"type": "commonjs",
"main": "examples.js",
"engines": {
"node": ">=18"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"start": "node examples.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@foxbit-group/rest-api": "^0.1.3"
},
"overrides": {
"axios": "^1.16.0",
"follow-redirects": "^1.16.0",
"form-data": "^4.0.6"
"@foxbit-group/rest-api": "0.1.3"
}
}
3 changes: 3 additions & 0 deletions rest-v3-sdk/typescript/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
dist
npm-debug.log
16 changes: 16 additions & 0 deletions rest-v3-sdk/typescript/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Build stage: install all dependencies and compile TypeScript to JavaScript.
FROM node:22.22.0-alpine3.23 AS build
WORKDIR /app
COPY package.json package-lock.json tsconfig.json ./
RUN npm ci
COPY index.ts ./
RUN npm run build

# Runtime stage: install only production dependencies and run the compiled app.
FROM node:22.22.0-alpine3.23 AS runtime
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
CMD ["node", "dist/index.js"]
Loading