mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
* add docs * Add Binance exchange support - Updated the environment configuration to include Binance as a selectable exchange option. - Enhanced the README documentation to reflect the addition of Binance. - Implemented the Binance exchange adapter and integrated it into the existing exchange framework. - Modified the basis arbitrage strategy to support Binance alongside existing exchanges. - Added tests to ensure proper functionality and integration of Binance within the trading system. * Enhance README with detailed Binance exchange configuration - Added comprehensive instructions for setting up Binance as an exchange option. - Included environment variable specifications for API keys, market types, and trading symbols. - Provided examples for both perpetual and spot trading strategies. - Clarified the use of WebSocket and REST for the Binance adapter. * Enhance exchange support and testing framework - Added a new test suite for exchange contracts to ensure consistency and functionality across supported exchanges. - Refactored exchange ID handling to utilize a centralized list of supported exchanges, improving maintainability. - Updated CLI argument parsing and help documentation to reflect the new exchange structure. - Introduced utility functions for validating supported exchanges and their display names. - Enhanced the BasisApp and strategy runner to leverage the new exchange validation logic. - Added a new test command for running exchange-related tests. * Refactor exchange contract tests and update CLI commands - Removed the trailing supported exchanges set and simplified the logic for trailing stop support in the exchange contract tests. - Updated the test command for exchange contracts to exclude unnecessary tests, streamlining the testing process. - Enhanced test descriptions for clarity and improved understanding of the functionality being tested.
129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import type { ExchangeAdapter } from "../src/exchanges/adapter";
|
|
import type {
|
|
AsterAccountSnapshot,
|
|
AsterDepth,
|
|
AsterKline,
|
|
AsterOrder,
|
|
AsterTicker,
|
|
} from "../src/exchanges/types";
|
|
import { BasisArbEngine } from "../src/strategy/basis-arb-engine";
|
|
|
|
class StubAdapter implements ExchangeAdapter {
|
|
id = "aster";
|
|
private depthHandler: ((depth: AsterDepth) => void) | null = null;
|
|
|
|
supportsTrailingStops(): boolean {
|
|
return false;
|
|
}
|
|
|
|
watchAccount(_cb: (snapshot: AsterAccountSnapshot) => void): void {
|
|
// not required for this test
|
|
}
|
|
|
|
watchOrders(_cb: (orders: AsterOrder[]) => void): void {
|
|
// not required for this test
|
|
}
|
|
|
|
watchDepth(_symbol: string, cb: (depth: AsterDepth) => void): void {
|
|
this.depthHandler = cb;
|
|
}
|
|
|
|
emitDepth(depth: AsterDepth): void {
|
|
this.depthHandler?.(depth);
|
|
}
|
|
|
|
watchTicker(_symbol: string, _cb: (ticker: AsterTicker) => void): void {
|
|
// not required for this test
|
|
}
|
|
|
|
watchKlines(_symbol: string, _interval: string, _cb: (klines: AsterKline[]) => void): void {
|
|
// not required for this test
|
|
}
|
|
|
|
createOrder(): Promise<AsterOrder> {
|
|
throw new Error("not implemented");
|
|
}
|
|
|
|
cancelOrder(_params: { symbol: string; orderId: number | string }): Promise<void> {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
cancelOrders(_params: { symbol: string; orderIdList: Array<number | string> }): Promise<void> {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
cancelAllOrders(_params: { symbol: string }): Promise<void> {
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
|
|
describe("BasisArbEngine", () => {
|
|
it("computes spreads after receiving futures depth and spot quotes", async () => {
|
|
const adapter = new StubAdapter();
|
|
const spotClient = {
|
|
getBookTicker: vi.fn().mockResolvedValue({
|
|
symbol: "ASTERUSDT",
|
|
bidPrice: "1.0000",
|
|
bidQty: "1",
|
|
askPrice: "1.0500",
|
|
askQty: "1",
|
|
time: 2_000,
|
|
}),
|
|
};
|
|
const futuresClient = {
|
|
getPremiumIndex: vi.fn().mockResolvedValue({
|
|
fundingRate: "0.0001",
|
|
nextFundingTime: 3_600_000,
|
|
time: 2_000,
|
|
}),
|
|
};
|
|
|
|
const engine = new BasisArbEngine(
|
|
{
|
|
futuresSymbol: "ASTERUSDT",
|
|
spotSymbol: "ASTERUSDT",
|
|
refreshIntervalMs: 1_000,
|
|
maxLogEntries: 10,
|
|
takerFeeRate: 0.0004,
|
|
arbAmount: 1,
|
|
},
|
|
adapter,
|
|
{
|
|
spotClient,
|
|
futuresClient,
|
|
now: () => 1_000,
|
|
}
|
|
);
|
|
|
|
engine.start();
|
|
|
|
adapter.emitDepth({
|
|
lastUpdateId: 1,
|
|
bids: [["1.0400", "1"]],
|
|
asks: [["1.0600", "1"]],
|
|
eventTime: 1_500,
|
|
});
|
|
|
|
await vi.waitFor(() => {
|
|
expect(spotClient.getBookTicker).toHaveBeenCalled();
|
|
const snap = engine.getSnapshot();
|
|
expect(snap.spotBid).not.toBeNull();
|
|
expect(snap.futuresBid).not.toBeNull();
|
|
});
|
|
|
|
const snapshot = engine.getSnapshot();
|
|
expect(snapshot.spread).toBeCloseTo(1.04 - 1.05, 6);
|
|
expect(snapshot.spreadBps).toBeCloseTo(((1.04 - 1.05) / 1.05) * 10_000, 6);
|
|
const fee = 0.0004;
|
|
const effectiveFee = fee * 2;
|
|
const expectedNet = 1.04 * (1 - effectiveFee) - 1.05 * (1 + effectiveFee);
|
|
expect(snapshot.netSpread).toBeCloseTo(expectedNet, 6);
|
|
expect(snapshot.netSpreadBps).toBeCloseTo((expectedNet / 1.05) * 10_000, 6);
|
|
expect(snapshot.feedStatus).toEqual({ futures: true, spot: true, funding: true });
|
|
expect(snapshot.opportunity).toBe(expectedNet >= 0);
|
|
|
|
engine.stop();
|
|
});
|
|
});
|