mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-10 08:48:07 +00:00
更新文档,添加和修改多个API接口的描述,确保文档内容的准确性和完整性,提升开发者使用体验。
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
Placeholder until adapter implementation
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { GrvtClient, GrvtWsClient, EGrvtEnvironment } from "@grvt/sdk";
|
||||
|
||||
export interface GrvtCredentials {
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
env: EGrvtEnvironment;
|
||||
subAccountId: string;
|
||||
instrument: string;
|
||||
}
|
||||
|
||||
export class GrvtGateway {
|
||||
private readonly grvtClient: GrvtClient;
|
||||
private readonly wsClient: GrvtWsClient;
|
||||
|
||||
constructor(private readonly credentials: GrvtCredentials) {
|
||||
this.grvtClient = new GrvtClient({
|
||||
apiKey: credentials.apiKey,
|
||||
apiSecret: credentials.apiSecret,
|
||||
env: credentials.env,
|
||||
});
|
||||
this.wsClient = new GrvtWsClient({ apiKey: credentials.apiKey, env: credentials.env });
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.wsClient.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
import { GrvtClient, GrvtWsClient, EGrvtEnvironment } from "@grvt/sdk";
|
||||
import { TDG } from "@grvt/client";
|
||||
|
||||
import type {
|
||||
GrvtAccountSnapshot,
|
||||
GrvtOrder,
|
||||
GrvtOrderUpdateFeed,
|
||||
GrvtPosition,
|
||||
GrvtPositionUpdateFeed,
|
||||
} from "../types";
|
||||
|
||||
export interface GrvtGatewayOptions {
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
subAccountId: string;
|
||||
instrument: string;
|
||||
env?: EGrvtEnvironment;
|
||||
}
|
||||
|
||||
export class GrvtGateway {
|
||||
private readonly client: GrvtClient;
|
||||
private readonly ws: GrvtWsClient;
|
||||
private readonly tdg: TDG;
|
||||
|
||||
private accountSnapshot: GrvtAccountSnapshot | null = null;
|
||||
private openOrders: GrvtOrder[] = [];
|
||||
private positions: GrvtPosition[] = [];
|
||||
private readonly instrument: string;
|
||||
private snapshotListeners = new Set<(snapshot: GrvtAccountSnapshot) => void>();
|
||||
private ordersListeners = new Set<(orders: GrvtOrder[]) => void>();
|
||||
private positionsListeners = new Set<(positions: GrvtPosition[]) => void>();
|
||||
|
||||
constructor(private readonly options: GrvtGatewayOptions) {
|
||||
const environment = options.env ?? (process.env.GRVT_ENV as EGrvtEnvironment) ?? EGrvtEnvironment.TESTNET;
|
||||
this.client = new GrvtClient({
|
||||
apiKey: options.apiKey,
|
||||
apiSecret: options.apiSecret,
|
||||
env: environment,
|
||||
});
|
||||
this.ws = new GrvtWsClient({ apiKey: options.apiKey, env: environment });
|
||||
this.tdg = new TDG({ host: `https://trades.${this.client["domain"]}/lite/v1` });
|
||||
this.tdg = new TDG({ host: `https://trades.${this.client["domain"]}` });
|
||||
this.instrument = options.instrument;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.refreshAccountSnapshot();
|
||||
await this.refreshOpenOrders();
|
||||
await this.refreshPositions();
|
||||
await this.ws.connect();
|
||||
this.subscribeStreams();
|
||||
}
|
||||
|
||||
onAccount(listener: (snapshot: GrvtAccountSnapshot) => void): () => void {
|
||||
this.snapshotListeners.add(listener);
|
||||
if (this.accountSnapshot) listener(this.accountSnapshot);
|
||||
return () => {
|
||||
this.snapshotListeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
onOrders(listener: (orders: GrvtOrder[]) => void): () => void {
|
||||
this.ordersListeners.add(listener);
|
||||
if (this.openOrders.length) listener([...this.openOrders]);
|
||||
return () => {
|
||||
this.ordersListeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
onPositions(listener: (positions: GrvtPosition[]) => void): () => void {
|
||||
this.positionsListeners.add(listener);
|
||||
if (this.positions.length) listener([...this.positions]);
|
||||
return () => {
|
||||
this.positionsListeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
getAccountSnapshot(): GrvtAccountSnapshot | null {
|
||||
return this.accountSnapshot ? { ...this.accountSnapshot } : null;
|
||||
}
|
||||
|
||||
getOpenOrders(): GrvtOrder[] {
|
||||
return [...this.openOrders];
|
||||
}
|
||||
|
||||
getPositions(): GrvtPosition[] {
|
||||
return [...this.positions];
|
||||
}
|
||||
|
||||
private async refreshAccountSnapshot(): Promise<void> {
|
||||
const response = await this.client.getSubAccountSummary({ sub_account_id: this.options.subAccountId });
|
||||
const snapshot = mapAccountSnapshot(response);
|
||||
this.accountSnapshot = snapshot;
|
||||
this.emitAccount(snapshot);
|
||||
}
|
||||
|
||||
private async refreshOpenOrders(): Promise<void> {
|
||||
const response = await this.client.tdgClient.openOrders({ sub_account_id: this.options.subAccountId });
|
||||
const orders = mapOpenOrders(response);
|
||||
this.openOrders = orders;
|
||||
this.emitOrders(orders);
|
||||
}
|
||||
|
||||
private async refreshPositions(): Promise<void> {
|
||||
const response = await this.client.tdgClient.positions({ sub_account_id: this.options.subAccountId });
|
||||
const positions = mapPositions(response);
|
||||
this.positions = positions.filter((position) => position.instrument === this.instrument);
|
||||
this.emitPositions(this.positions);
|
||||
}
|
||||
|
||||
private subscribeStreams(): void {
|
||||
const orderSubscription: IWSTdgOrderRequest = {
|
||||
stream: `${EStream.ORDER}`,
|
||||
params: {
|
||||
sub_account_id: this.options.subAccountId,
|
||||
instrument: this.instrument,
|
||||
},
|
||||
onData: (message) => this.handleOrderFeed(message),
|
||||
};
|
||||
|
||||
const orderStateSubscription: IWSTdgOrderStateRequest = {
|
||||
stream: `${EStream.STATE}`,
|
||||
params: {
|
||||
sub_account_id: this.options.subAccountId,
|
||||
instrument: this.instrument,
|
||||
},
|
||||
onData: (message) => this.handleOrderStateFeed(message),
|
||||
};
|
||||
|
||||
const positionSubscription: IWSTdgPositionRequest = {
|
||||
stream: `${EStream.POSITION}`,
|
||||
params: {
|
||||
sub_account_id: this.options.subAccountId,
|
||||
instrument: this.instrument,
|
||||
},
|
||||
onData: (message) => this.handlePositionFeed(message),
|
||||
};
|
||||
|
||||
this.ws.subscribe(orderSubscription);
|
||||
this.ws.subscribe(orderStateSubscription);
|
||||
this.ws.subscribe(positionSubscription);
|
||||
}
|
||||
|
||||
private handleOrderFeed(feed: GrvtOrder): void {
|
||||
this.mergeOrder(feed);
|
||||
}
|
||||
|
||||
private handleOrderStateFeed(feed: GrvtOrderUpdateFeed): void {
|
||||
this.mergeOrderState(feed);
|
||||
}
|
||||
|
||||
private handlePositionFeed(feed: GrvtPositionUpdateFeed): void {
|
||||
this.mergePosition(feed);
|
||||
}
|
||||
|
||||
private mergeOrder(order: GrvtOrder): void {
|
||||
const existingIndex = this.openOrders.findIndex((item) => item.order_id === order.order_id);
|
||||
if (existingIndex >= 0) {
|
||||
this.openOrders[existingIndex] = { ...this.openOrders[existingIndex], ...order };
|
||||
} else {
|
||||
this.openOrders.push(order);
|
||||
}
|
||||
this.emitOrders([...this.openOrders]);
|
||||
}
|
||||
|
||||
private mergeOrderState(update: GrvtOrderUpdateFeed): void {
|
||||
const existing = this.openOrders.find((item) => item.order_id === update.order_id);
|
||||
if (existing) {
|
||||
existing.state = update.state;
|
||||
this.emitOrders([...this.openOrders]);
|
||||
}
|
||||
}
|
||||
|
||||
private mergePosition(update: GrvtPositionUpdateFeed): void {
|
||||
const idx = this.positions.findIndex((item) => item.instrument === update.instrument);
|
||||
if (idx >= 0) {
|
||||
this.positions[idx] = {
|
||||
...this.positions[idx],
|
||||
...update,
|
||||
};
|
||||
} else {
|
||||
this.positions.push(update);
|
||||
}
|
||||
this.emitPositions([...this.positions]);
|
||||
}
|
||||
|
||||
private emitAccount(snapshot: GrvtAccountSnapshot): void {
|
||||
this.snapshotListeners.forEach((listener) => {
|
||||
try {
|
||||
listener({ ...snapshot });
|
||||
} catch (error) {
|
||||
console.error("[GrvtGateway] account listener failed", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private emitOrders(orders: GrvtOrder[]): void {
|
||||
this.ordersListeners.forEach((listener) => {
|
||||
try {
|
||||
listener([...orders]);
|
||||
} catch (error) {
|
||||
console.error("[GrvtGateway] orders listener failed", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private emitPositions(positions: GrvtPosition[]): void {
|
||||
this.positionsListeners.forEach((listener) => {
|
||||
try {
|
||||
listener([...positions]);
|
||||
} catch (error) {
|
||||
console.error("[GrvtGateway] positions listener failed", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function mapAccountSnapshot(response: IApiSubAccountSummaryResponse): GrvtAccountSnapshot {
|
||||
const result = response.result;
|
||||
if (!result) {
|
||||
return {
|
||||
total_unrealized_pnl: "0",
|
||||
positions: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
total_unrealized_pnl: result.unrealized_pnl,
|
||||
positions: (result.positions ?? []).map(mapPositionEntry),
|
||||
settle_currency: result.settle_currency,
|
||||
available_balance: result.available_balance,
|
||||
};
|
||||
}
|
||||
|
||||
function mapOpenOrders(response: IApiOpenOrdersResponse): GrvtOrder[] {
|
||||
return (response.result ?? []).map((order) => ({
|
||||
order_id: order.order_id ?? "",
|
||||
client_order_id: order.metadata?.client_order_id,
|
||||
sub_account_id: order.sub_account_id,
|
||||
is_market: order.is_market,
|
||||
time_in_force: order.time_in_force,
|
||||
post_only: order.post_only,
|
||||
reduce_only: order.reduce_only,
|
||||
legs: order.legs?.map((leg) => ({
|
||||
instrument: leg.instrument ?? "",
|
||||
size: leg.size ?? "0",
|
||||
limit_price: leg.limit_price ?? undefined,
|
||||
is_buying_asset: leg.is_buying_asset,
|
||||
})),
|
||||
metadata: order.metadata,
|
||||
state: order.state,
|
||||
}));
|
||||
}
|
||||
|
||||
function mapPositions(response: IApiPositionsResponse): GrvtPosition[] {
|
||||
return (response.result ?? []).map(mapPositionEntry);
|
||||
}
|
||||
|
||||
function mapPositionEntry(entry: any): GrvtPosition {
|
||||
return {
|
||||
instrument: entry.instrument ?? "",
|
||||
size: entry.size ?? "0",
|
||||
entry_price: entry.entry_price,
|
||||
mark_price: entry.mark_price,
|
||||
unrealized_pnl: entry.unrealized_pnl,
|
||||
sub_account_id: entry.sub_account_id,
|
||||
update_time: entry.event_time,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,157 @@ export interface AsterAccountPosition {
|
||||
markPrice?: string;
|
||||
}
|
||||
|
||||
export interface GrvtOrderLeg {
|
||||
instrument: string;
|
||||
size: string;
|
||||
limit_price?: string;
|
||||
is_buying_asset?: boolean;
|
||||
}
|
||||
|
||||
export interface GrvtOrderMetadata {
|
||||
client_order_id?: string;
|
||||
create_time?: string;
|
||||
broker?: string | null;
|
||||
}
|
||||
|
||||
export interface GrvtOrderState {
|
||||
status?: string;
|
||||
reject_reason?: string | null;
|
||||
book_size?: string[];
|
||||
traded_size?: string[];
|
||||
update_time?: string;
|
||||
avg_fill_price?: string[];
|
||||
}
|
||||
|
||||
export interface GrvtOrder {
|
||||
order_id: string;
|
||||
client_order_id?: string;
|
||||
sub_account_id?: string;
|
||||
is_market?: boolean;
|
||||
time_in_force?: string;
|
||||
post_only?: boolean;
|
||||
reduce_only?: boolean;
|
||||
legs?: GrvtOrderLeg[];
|
||||
metadata?: GrvtOrderMetadata;
|
||||
state?: GrvtOrderState;
|
||||
instrument?: string;
|
||||
}
|
||||
|
||||
export interface GrvtTrade {
|
||||
price: string;
|
||||
size: string;
|
||||
taker_side: "BUY" | "SELL";
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface GrvtTradeHistoryResponse {
|
||||
result?: GrvtTrade[];
|
||||
}
|
||||
|
||||
export interface GrvtWebsocketMessage<T> {
|
||||
stream: string;
|
||||
selector: string;
|
||||
sequence_number?: string;
|
||||
feed: T;
|
||||
}
|
||||
|
||||
export interface GrvtOrderUpdateFeed {
|
||||
order_id: string;
|
||||
client_order_id?: string;
|
||||
sub_account_id?: string;
|
||||
state?: GrvtOrderState;
|
||||
traded_size?: string[];
|
||||
update_time?: string;
|
||||
}
|
||||
|
||||
export interface GrvtPositionUpdateFeed {
|
||||
instrument: string;
|
||||
size: string;
|
||||
entry_price?: string;
|
||||
mark_price?: string;
|
||||
unrealized_pnl?: string;
|
||||
sub_account_id?: string;
|
||||
update_time?: string;
|
||||
}
|
||||
|
||||
export interface GrvtDepthUpdateFeed {
|
||||
instrument: string;
|
||||
bids: GrvtDepthLevel[];
|
||||
asks: GrvtDepthLevel[];
|
||||
event_time?: string;
|
||||
}
|
||||
|
||||
export interface GrvtTickerUpdateFeed {
|
||||
instrument: string;
|
||||
mark_price?: string;
|
||||
last_trade_price?: string;
|
||||
best_bid_price?: string;
|
||||
best_ask_price?: string;
|
||||
volume_24h?: string;
|
||||
}
|
||||
|
||||
export interface GrvtOpenOrdersResponse {
|
||||
result?: GrvtOrder[];
|
||||
}
|
||||
|
||||
export interface GrvtPositionsResponse {
|
||||
result?: GrvtPosition[];
|
||||
}
|
||||
|
||||
export interface GrvtPosition {
|
||||
instrument: string;
|
||||
size: string;
|
||||
entry_price?: string;
|
||||
mark_price?: string;
|
||||
unrealized_pnl?: string;
|
||||
}
|
||||
|
||||
export interface GrvtAccountSnapshot {
|
||||
total_unrealized_pnl?: string;
|
||||
positions: GrvtPosition[];
|
||||
settle_currency?: string;
|
||||
available_balance?: string;
|
||||
}
|
||||
|
||||
export interface GrvtBalancesResponse {
|
||||
result?: {
|
||||
total_unrealized_pnl?: string;
|
||||
positions?: GrvtPosition[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GrvtDepthLevel {
|
||||
price: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
export interface GrvtDepth {
|
||||
instrument: string;
|
||||
event_time?: string;
|
||||
bids: GrvtDepthLevel[];
|
||||
asks: GrvtDepthLevel[];
|
||||
}
|
||||
|
||||
export interface GrvtTicker {
|
||||
instrument: string;
|
||||
mark_price?: string;
|
||||
last_trade_price?: string;
|
||||
best_bid_price?: string;
|
||||
best_ask_price?: string;
|
||||
volume_24h?: string;
|
||||
}
|
||||
|
||||
export interface GrvtKline {
|
||||
open_time: number;
|
||||
close_time: number;
|
||||
open: string;
|
||||
high: string;
|
||||
low: string;
|
||||
close: string;
|
||||
volume: string;
|
||||
number_of_trades?: number;
|
||||
}
|
||||
|
||||
export interface AsterAccountAsset {
|
||||
asset: string;
|
||||
walletBalance: string;
|
||||
|
||||
Reference in New Issue
Block a user