mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
209 lines
6.2 KiB
TypeScript
209 lines
6.2 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import {
|
|
buildOndoperpsAuthHeaders,
|
|
createOndoperpsWsSignature,
|
|
mapOndoperpsAccountSnapshot,
|
|
mapOndoperpsOrder,
|
|
normalizeOndoperpsSymbol,
|
|
OndoperpsGateway,
|
|
} from "../src/exchanges/ondoperps/gateway";
|
|
|
|
describe("Ondo Perps gateway", () => {
|
|
it("normalizes common symbols to the official market format", () => {
|
|
expect(normalizeOndoperpsSymbol("")).toBe("BTC-USD.P");
|
|
expect(normalizeOndoperpsSymbol("xau")).toBe("XAU-USD.P");
|
|
expect(normalizeOndoperpsSymbol("NVDA/USD")).toBe("NVDA-USD.P");
|
|
expect(normalizeOndoperpsSymbol("AAPL-USD.P")).toBe("AAPL-USD.P");
|
|
});
|
|
|
|
it("builds REST and WebSocket HMAC signatures from the documented payload", () => {
|
|
const body = JSON.stringify({ market: "XAU-USD.P", side: "buy", size: "1" });
|
|
const headers = buildOndoperpsAuthHeaders({
|
|
apiKeyId: "ondoKeyId_test",
|
|
apiSecret: "ondoApiSecret_secret",
|
|
timestamp: "1700000000000",
|
|
method: "POST",
|
|
requestPath: "/v1/perps/orders?market=XAU-USD.P",
|
|
body,
|
|
});
|
|
|
|
expect(headers).toEqual({
|
|
"ONDO-KEY-ID": "ondoKeyId_test",
|
|
"ONDO-TIMESTAMP": "1700000000000",
|
|
"ONDO-SIGN": "3cf0628da79a19225d79fd17ab4767ee1ecae0219a52e1b1fc1b0c1a8bc7a880",
|
|
});
|
|
expect(createOndoperpsWsSignature("ondoApiSecret_secret", "1700000000000")).toBe(
|
|
"eb87f259c41c7cba728b1ce5baf0a385e6e5c1b1031424d73206cacbaebea742",
|
|
);
|
|
expect(createOndoperpsWsSignature("ondoApiSecret_secret", "1700000000000", true)).toBe(
|
|
"46ca7822ee8444eccd96feaa596e30072c8428aa8ec2cedbe0fdc6744bc21ec7",
|
|
);
|
|
});
|
|
|
|
it("maps Ondo order fields into the shared exchange contract", () => {
|
|
const order = mapOndoperpsOrder(
|
|
{
|
|
orderId: "order-1",
|
|
clientOrderId: "client-1",
|
|
side: "sell",
|
|
price: "201.25",
|
|
size: "2",
|
|
market: "NVDA-USD.P",
|
|
filledSize: "0.5",
|
|
filledCost: "100.625",
|
|
status: "open",
|
|
createdAt: "2026-07-11T00:00:00Z",
|
|
type: "limit",
|
|
timeInForce: "GTC",
|
|
reduceOnly: true,
|
|
},
|
|
"NVDA-USD.P",
|
|
);
|
|
|
|
expect(order).toMatchObject({
|
|
orderId: "order-1",
|
|
clientOrderId: "client-1",
|
|
symbol: "NVDA-USD.P",
|
|
side: "SELL",
|
|
type: "LIMIT",
|
|
status: "NEW",
|
|
origQty: "2",
|
|
executedQty: "0.5",
|
|
avgPrice: "201.25",
|
|
reduceOnly: true,
|
|
});
|
|
});
|
|
|
|
it("creates post-only orders with signed REST headers", async () => {
|
|
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
|
const fetchFn = vi.fn(async (input: string | URL | Request, init?: RequestInit) => {
|
|
const url = String(input);
|
|
requests.push({ url, init });
|
|
if (url.endsWith("/v1/markets")) {
|
|
return new Response(JSON.stringify({
|
|
success: true,
|
|
result: {
|
|
perps: {
|
|
tradingPairs: [
|
|
{ market: "XAU-USD.P", baseIncrement: "0.01", quoteIncrement: "0.1" },
|
|
],
|
|
},
|
|
},
|
|
}));
|
|
}
|
|
return new Response(JSON.stringify({
|
|
success: true,
|
|
result: {
|
|
orderId: "order-2",
|
|
clientOrderId: "client-2",
|
|
side: "buy",
|
|
price: "2500",
|
|
size: "0.5",
|
|
market: "XAU-USD.P",
|
|
filledSize: "0",
|
|
status: "open",
|
|
createdAt: "2026-07-11T00:00:00Z",
|
|
type: "limit",
|
|
timeInForce: "GTC",
|
|
reduceOnly: false,
|
|
},
|
|
}));
|
|
});
|
|
const fakeWebSocket = {
|
|
readyState: 0,
|
|
addEventListener: vi.fn(),
|
|
send: vi.fn(),
|
|
} as unknown as WebSocket;
|
|
const gateway = new OndoperpsGateway({
|
|
apiKeyId: "ondoKeyId_test",
|
|
apiSecret: "ondoApiSecret_secret",
|
|
symbol: "XAU-USD.P",
|
|
fetchFn: fetchFn as unknown as typeof fetch,
|
|
webSocketFactory: () => fakeWebSocket,
|
|
now: () => 1_700_000_000_000,
|
|
});
|
|
|
|
const order = await gateway.createOrder({
|
|
symbol: "XAU-USD.P",
|
|
side: "BUY",
|
|
type: "LIMIT",
|
|
quantity: 0.5,
|
|
price: 2500,
|
|
timeInForce: "GTX",
|
|
clientOrderId: "client-2",
|
|
});
|
|
|
|
const request = requests[1];
|
|
expect(request?.url).toBe("https://api.ondoperps.xyz/v1/perps/orders");
|
|
expect(JSON.parse(String(request?.init?.body))).toMatchObject({
|
|
market: "XAU-USD.P",
|
|
type: "limit",
|
|
side: "buy",
|
|
size: "0.5",
|
|
price: "2500",
|
|
timeInForce: "GTC",
|
|
postOnly: true,
|
|
clientOrderId: "client-2",
|
|
});
|
|
expect(request?.init?.headers).toMatchObject({
|
|
"ONDO-KEY-ID": "ondoKeyId_test",
|
|
"ONDO-TIMESTAMP": "1700000000000",
|
|
});
|
|
expect(order).toMatchObject({ orderId: "order-2", status: "NEW", type: "LIMIT" });
|
|
});
|
|
|
|
it("maps short positions and USDC margin fields into an account snapshot", () => {
|
|
const snapshot = mapOndoperpsAccountSnapshot(
|
|
{
|
|
walletBalance: "5000",
|
|
realizedPnl: "10",
|
|
unrealizedPnl: "-25",
|
|
marginBalance: "4975",
|
|
usedMargin: "1000",
|
|
availableMargin: "3975",
|
|
withdrawableMargin: "3975",
|
|
maintenanceMarginRequirement: "50",
|
|
totalMaintenanceMargin: "80",
|
|
marginRatio: "0.016",
|
|
leverage: "1.5",
|
|
underLiquidation: false,
|
|
totalFundingPayments: "-1",
|
|
totalTradingFees: "2",
|
|
totalPnL: "-18",
|
|
},
|
|
[
|
|
{
|
|
market: "NVDA-USD.P",
|
|
direction: "short",
|
|
netQuantity: "3",
|
|
averageEntryPrice: "200",
|
|
usedMargin: "1000",
|
|
unrealizedPnl: "-25",
|
|
markPrice: "208.33",
|
|
liquidationPrice: "260",
|
|
bankruptcyPrice: "275",
|
|
maintenanceMargin: "50",
|
|
notionalValue: "625",
|
|
leverage: "1.5",
|
|
netFundingSinceNeutral: "-1",
|
|
returnOnEquity: "-0.025",
|
|
},
|
|
],
|
|
"NVDA-USD.P",
|
|
"NVDA-USD.P",
|
|
1234,
|
|
);
|
|
|
|
expect(snapshot.totalWalletBalance).toBe("5000");
|
|
expect(snapshot.availableBalance).toBe("3975");
|
|
expect(snapshot.assets[0]?.asset).toBe("USDC");
|
|
expect(snapshot.positions[0]).toMatchObject({
|
|
symbol: "NVDA-USD.P",
|
|
positionAmt: "-3",
|
|
entryPrice: "200",
|
|
markPrice: "208.33",
|
|
positionSide: "BOTH",
|
|
});
|
|
});
|
|
});
|