Skip to content

d3x293/shark-exchange-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Shark Exchange SDK

TypeScript-first Node.js SDK for Shark Exchange REST and Socket.IO APIs.

This SDK is designed to be used as a complete trading integration layer, not as a thin API wrapper. It gives you typed resource clients, request signing, rate limiting, retry handling, validation, order builders, websocket helpers, listen-key management, and examples for common workflows.

Source reference: Shark API Reference Documentation.

Requirements

  • Node.js 20 or newer.
  • Node.js 22 or newer is recommended for current production apps.
  • Shark API key and secret are required for authenticated account, order, wallet, position, and private stream APIs.
  • Public market REST and public market streams do not require credentials.

The package ships ESM, CommonJS, and TypeScript declarations.

Install

npm install shark-exchange-sdk

Import

ESM or TypeScript:

import { SharkExchange } from "shark-exchange-sdk";

CommonJS:

const { SharkExchange } = require("shark-exchange-sdk");

Client Setup

import { SharkExchange } from "shark-exchange-sdk";

export const shark = new SharkExchange({
  apiKey: process.env.SHARK_API_KEY,
  apiSecret: process.env.SHARK_API_SECRET,
});

Advanced setup:

const shark = new SharkExchange({
  apiKey: process.env.SHARK_API_KEY,
  apiSecret: process.env.SHARK_API_SECRET,
  timeoutMs: 30_000,
  retry: {
    retries: 2,
    baseDelayMs: 250,
    maxDelayMs: 2_000,
  },
  rateLimit: {
    enabled: true,
  },
  userAgent: "my-trading-app/1.0.0",
});

Environment Variables

SHARK_API_KEY=your-api-key
SHARK_API_SECRET=your-api-secret

Keep live trading controls in your application, not inside examples. The examples that place or cancel orders are intentionally guarded with RUN_LIVE_TRADING=true.

What The SDK Handles

  • HMAC-SHA256 signing for authenticated REST endpoints.
  • Timestamps for authenticated REST requests.
  • Public vs authenticated endpoint routing.
  • Endpoint-aware rate limiting based on Shark documentation.
  • Retry and timeout behavior.
  • Runtime validation for common order mistakes.
  • Fluent order builders.
  • Typed REST resources grouped by workflow.
  • Socket.IO public and authenticated stream helpers.
  • Listen-key creation, keepalive, and deletion.
  • Typed resource response unwrapping for { data: ... } and { result: ... } envelopes.
  • Raw request escape hatch for newly added or undocumented endpoints.

Resource Map

Market Data

Public REST data:

await shark.market.ticker24Hr("BTCINR");
await shark.market.aggregateTrade("BTCINR");
await shark.market.depth("BTCINR");
await shark.market.klines({
  pair: "BTCINR",
  interval: "1m",
  priceType: "MARK_PRICE",
  limit: 100,
});

Exchange

Exchange metadata and account-level preferences:

await shark.exchange.info({ market: "INR" });

await shark.exchange.updatePreference({
  contractName: "BTCINR",
  marginMode: "ISOLATED",
  leverage: 10,
});

await shark.exchange.updateLeverage({
  contractName: "BTCINR",
  leverage: 10,
});

Orders

Order placement, lookup, editing, margin, and cancellation:

await shark.orders.placeMarket({
  placeType: "ORDER_FORM",
  quantity: 0.002,
  side: "BUY",
  symbol: "BTCINR",
  reduceOnly: false,
  marginAsset: "INR",
});

await shark.orders.placeLimit({
  placeType: "ORDER_FORM",
  quantity: 0.002,
  price: 5_000_000,
  side: "BUY",
  symbol: "BTCINR",
  reduceOnly: false,
  marginAsset: "INR",
});

await shark.orders.edit({
  clientOrderId: "client-order-id",
  quantity: 0.003,
  price: 5_050_000,
});

await shark.orders.open({ symbol: "BTCINR", pageSize: 50, sortOrder: "desc" });
await shark.orders.history({ symbol: "BTCINR", pageSize: 50, sortOrder: "desc" });
await shark.orders.get("client-order-id");
await shark.orders.getMultiple(["client-order-id-1", "client-order-id-2"]);
await shark.orders.linked("link-id");
await shark.orders.referenced("ref-id");
await shark.orders.splitTpSl({
  positionId: "position-id",
  splitTakeProfitOrders: [{ quantity: 0.001, price: 5_500_000 }],
  splitStopLossOrders: [{ quantity: 0.001, price: 4_750_000 }],
});
await shark.orders.marginHistory({ symbol: "BTCINR" });
await shark.orders.delete("client-order-id");
await shark.orders.cancelAll();

Order Builder

Use the builder when you want to avoid assembling payloads by hand:

const payload = shark
  .order()
  .symbol("BTCINR")
  .buy()
  .marginAsset("INR")
  .limit(0.002, 5_000_000)
  .takeProfit(5_500_000)
  .stopLoss(4_750_000)
  .build();

await shark.orders.place(payload);

Stop orders:

const stopLimit = shark
  .order()
  .symbol("BTCINR")
  .sell()
  .marginAsset("INR")
  .stopLimit(0.002, 4_900_000, 4_890_000)
  .reduceOnly()
  .build();

Positions

await shark.positions.list("OPEN", { symbol: "BTCINR", pageSize: 100 });
await shark.positions.get("position-id");
await shark.positions.closeAll();

Wallet

await shark.wallet.futures({ marginAsset: "INR" });
await shark.wallet.funding({ marginAsset: "INR" });

User Data

await shark.userData.tradeHistory({
  symbol: "BTCINR",
  pageSize: 100,
  sortOrder: "desc",
});

await shark.userData.transactionHistory({
  symbol: "BTCINR",
  positionId: "position-id",
  pageSize: 100,
});

Listen Keys

const { listenKey } = await shark.listenKeys.create();
await shark.listenKeys.keepAlive();
await shark.listenKeys.delete();

Public Streams

Shark realtime uses Socket.IO. The SDK uses socket.io-client for the correct protocol.

import { SharkExchange, streamTopics } from "shark-exchange-sdk";

const shark = new SharkExchange();
const stream = shark.streams.public({ autoConnect: false });

stream.on("connect", () => {
  stream.subscribe([
    streamTopics.depth("BTCINR", "0.1"),
    streamTopics.kline("BTCINR", "1m"),
    streamTopics.markPrice("BTCINR"),
    streamTopics.aggregateTrade("BTCINR"),
    streamTopics.ticker("BTCINR"),
    streamTopics.marketInfo("BTCINR"),
    streamTopics.markPriceArray(),
    streamTopics.tickerArray(),
    streamTopics.allContractDetails(),
  ]);
});

stream.on("depthUpdate", console.log);
stream.on("kline", console.log);
stream.on("markPriceUpdate", console.log);
stream.on("aggTrade", console.log);
stream.on("24hrTicker", console.log);
stream.on("marketInfo", console.log);

stream.connect();

Live Data Pattern

For bots and dashboards, use REST for the initial snapshot and public streams for updates:

const symbols = ["BTCINR", "ETHINR"];
const snapshots = new Map<string, { lastPrice: number; bestBid: number; bestAsk: number }>();

for (const symbol of symbols) {
  const [ticker, depth] = await Promise.all([
    shark.market.ticker24Hr(symbol),
    shark.market.depth(symbol),
  ]);

  snapshots.set(symbol, {
    lastPrice: Number(ticker.c),
    bestBid: Number(depth.b?.[0]?.[0]),
    bestAsk: Number(depth.a?.[0]?.[0]),
  });
}

const stream = shark.streams.public({ autoConnect: false });
stream.on("connect", () => {
  stream.subscribe(symbols.flatMap((symbol) => [
    streamTopics.ticker(symbol),
    streamTopics.markPrice(symbol),
    streamTopics.depth(symbol, "0.1"),
  ]));
});
stream.on("24hrTicker", console.log);
stream.on("markPriceUpdate", console.log);
stream.on("depthUpdate", console.log);
stream.connect();

For a complete live-data cache with depth grouping, staleness checks, reconnect resubscription, and graceful shutdown, see examples/08-live-data-feed.ts.

Authenticated Streams

Managed authenticated streams create a listen key and refresh it automatically:

const stream = await shark.streams.managedAuthenticated({
  autoConnect: false,
  autoKeepAlive: true,
  keepAliveIntervalMs: 30 * 60_000,
});

stream.on("orderFilled", console.log);
stream.on("orderPartiallyFilled", console.log);
stream.on("orderCancelled", console.log);
stream.on("orderFailed", console.log);
stream.on("newOrder", console.log);
stream.on("updateOrder", console.log);
stream.on("newPosition", console.log);
stream.on("updatePosition", console.log);
stream.on("closePosition", console.log);
stream.on("balanceUpdate", console.log);
stream.on("newTrade", console.log);
stream.on("sessionExpired", console.log);

stream.connect();

If you already have a listen key:

const stream = shark.streams.authenticated("existing-listen-key");

Response Envelopes

Typed resource methods return the useful payload directly.

For example, if Shark returns:

{
  "data": {
    "s": "BTCINR",
    "c": "5271387.66"
  }
}

Then:

const ticker = await shark.market.ticker24Hr("BTCINR");
console.log(ticker.s);

Use raw.request() when you need the exact server response:

const raw = await shark.raw.request("GET", "/v1/market/ticker24Hr/BTCINR", {
  auth: false,
});

Compatibility Modes

The Shark docs currently show both path-style and query-style examples for a few public market endpoints. The SDK defaults to the documented JavaScript path style and keeps query mode available.

await shark.market.ticker24Hr("BTCINR");
await shark.market.ticker24Hr("BTCINR", { mode: "query" });

await shark.market.depth("BTCINR");
await shark.market.depth("BTCINR", { mode: "query" });

await shark.market.aggregateTrade("BTCINR");
await shark.market.aggregateTrade("BTCINR", { mode: "query" });

Order lookup methods also expose compatibility modes where the docs differ. Live testing showed single-order lookup should use query mode by default; path mode remains available when you need to match a path-style integration.

await shark.orders.linked("link-id", { mode: "query" });
await shark.orders.referenced("ref-id", { mode: "path" });
await shark.orders.get("client-order-id");
await shark.orders.get("client-order-id", { mode: "path" });

Error Handling

import {
  SharkApiError,
  SharkAuthError,
  SharkTimeoutError,
  SharkValidationError,
} from "shark-exchange-sdk";

try {
  await shark.orders.placeMarket({
    placeType: "ORDER_FORM",
    quantity: 0,
    side: "BUY",
    symbol: "BTCINR",
    reduceOnly: false,
    marginAsset: "INR",
  });
} catch (error) {
  if (error instanceof SharkValidationError) {
    console.error("Bad SDK input:", error.message);
  } else if (error instanceof SharkAuthError) {
    console.error("Missing or invalid local credentials:", error.message);
  } else if (error instanceof SharkTimeoutError) {
    console.error("Request timed out:", error.message);
  } else if (error instanceof SharkApiError) {
    console.error("Shark API error:", error.status, error.response);
  } else {
    throw error;
  }
}

Pagination Helper

Some Shark endpoints expose cursor-like timestamps. The SDK provides a generic helper so your app can keep its resource call readable.

for await (const order of shark.paginate((cursor) =>
  shark.orders.history({
    symbol: "BTCINR",
    pageSize: 100,
    endTimestamp: cursor,
  })
)) {
  console.log(order.clientOrderId);
}

If the API shape differs, pass getItems and getNextTimestamp.

Examples

The examples/ directory is designed as a user guide in code:

  • examples/01-public-market-data.ts: public market REST data.
  • examples/02-orders-and-builders.ts: market, limit, stop, edit, lookup, margin, and cancel flows.
  • examples/03-account-positions-wallet.ts: positions, wallet, user-data, exchange preferences.
  • examples/04-public-stream.ts: public Socket.IO market streams.
  • examples/05-authenticated-stream.ts: listen-key authenticated account stream.
  • examples/06-configuration-and-errors.ts: retry, timeout, rate limit, errors, abort signals.
  • examples/07-commonjs.js: CommonJS usage.
  • examples/08-live-data-feed.ts: REST bootstrap plus live Socket.IO market updates for bots and dashboards.

Run TypeScript examples with a runner such as tsx:

npx tsx examples/01-public-market-data.ts

Run CommonJS examples with Node:

node examples/07-commonjs.js

Scripts

npm run typecheck
npm test
npm run build

Publishing Checklist

npm run typecheck
npm test
npm run build
npm pack --dry-run

If your local npm cache has permission issues, use a temporary cache:

npm pack --dry-run --cache /private/tmp/npm-cache

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors