@yuants/vendor-aster 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-spot.js +47 -0
- package/dist/account-spot.js.map +1 -0
- package/dist/api.js +6 -28
- package/dist/api.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/order-spot.js +37 -0
- package/dist/order-spot.js.map +1 -0
- package/dist/sapi.js +48 -0
- package/dist/sapi.js.map +1 -0
- package/dist/utils.js +28 -0
- package/dist/utils.js.map +1 -0
- package/lib/account-spot.d.ts +2 -0
- package/lib/account-spot.d.ts.map +1 -0
- package/lib/account-spot.js +50 -0
- package/lib/account-spot.js.map +1 -0
- package/lib/api.d.ts +1 -1
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +7 -29
- package/lib/api.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/order-spot.d.ts +2 -0
- package/lib/order-spot.d.ts.map +1 -0
- package/lib/order-spot.js +39 -0
- package/lib/order-spot.js.map +1 -0
- package/lib/sapi.d.ts +37 -0
- package/lib/sapi.d.ts.map +1 -0
- package/lib/sapi.js +51 -0
- package/lib/sapi.js.map +1 -0
- package/lib/utils.d.ts +3 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/utils.js +33 -0
- package/lib/utils.js.map +1 -0
- package/package.json +1 -1
- package/temp/package-deps.json +9 -5
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { provideAccountInfoService } from '@yuants/data-account';
|
|
2
|
+
import { Terminal } from '@yuants/protocol';
|
|
3
|
+
import { getApiV1Account, getApiV1TickerPrice } from './sapi';
|
|
4
|
+
const ADDRESS = process.env.ADDRESS;
|
|
5
|
+
export const SPOT_ACCOUNT_ID = `ASTER/${ADDRESS}/SPOT`;
|
|
6
|
+
provideAccountInfoService(Terminal.fromNodeEnv(), SPOT_ACCOUNT_ID, async () => {
|
|
7
|
+
const [x, prices] = await Promise.all([getApiV1Account({}), getApiV1TickerPrice({})]);
|
|
8
|
+
const positions = x.balances.map((b) => {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const thePrice = b.asset === 'USDT' ? 1 : (_b = (_a = prices.find((p) => p.symbol === b.asset + 'USDT')) === null || _a === void 0 ? void 0 : _a.price) !== null && _b !== void 0 ? _b : 0;
|
|
11
|
+
const volume = +b.free + +b.locked;
|
|
12
|
+
const position_price = +thePrice;
|
|
13
|
+
const closable_price = +thePrice;
|
|
14
|
+
const valuation = volume * closable_price;
|
|
15
|
+
const floating_profit = 0;
|
|
16
|
+
return {
|
|
17
|
+
position_id: b.asset,
|
|
18
|
+
datasource_id: 'ASTER',
|
|
19
|
+
product_id: b.asset,
|
|
20
|
+
direction: 'LONG',
|
|
21
|
+
volume,
|
|
22
|
+
free_volume: +b.free,
|
|
23
|
+
position_price,
|
|
24
|
+
closable_price,
|
|
25
|
+
floating_profit,
|
|
26
|
+
valuation,
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const usdtAsset = x.balances.find((b) => b.asset === 'USDT');
|
|
30
|
+
const equity = positions.reduce((a, b) => a + b.valuation, 0);
|
|
31
|
+
const free = usdtAsset ? +usdtAsset.free : 0;
|
|
32
|
+
const balance = usdtAsset ? +usdtAsset.free + +usdtAsset.locked : 0;
|
|
33
|
+
const profit = equity - balance;
|
|
34
|
+
const used = equity - free;
|
|
35
|
+
return {
|
|
36
|
+
money: {
|
|
37
|
+
currency: 'USDT',
|
|
38
|
+
equity,
|
|
39
|
+
balance,
|
|
40
|
+
profit,
|
|
41
|
+
free,
|
|
42
|
+
used,
|
|
43
|
+
},
|
|
44
|
+
positions,
|
|
45
|
+
};
|
|
46
|
+
}, { auto_refresh_interval: 1000 });
|
|
47
|
+
//# sourceMappingURL=account-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-spot.js","sourceRoot":"","sources":["../src/account-spot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE9D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,OAAO,OAAO,CAAC;AAEvD,yBAAyB,CACvB,QAAQ,CAAC,WAAW,EAAE,EACtB,eAAe,EACf,KAAK,IAAI,EAAE;IACT,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEtF,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAa,EAAE;;QAChD,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAA,MAAA,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,0CAAE,KAAK,mCAAI,CAAC,CAAC;QAExG,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAEnC,MAAM,cAAc,GAAG,CAAC,QAAQ,CAAC;QACjC,MAAM,cAAc,GAAG,CAAC,QAAQ,CAAC;QACjC,MAAM,SAAS,GAAG,MAAM,GAAG,cAAc,CAAC;QAC1C,MAAM,eAAe,GAAG,CAAC,CAAC;QAE1B,OAAO;YACL,WAAW,EAAE,CAAC,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO;YACtB,UAAU,EAAE,CAAC,CAAC,KAAK;YACnB,SAAS,EAAE,MAAM;YACjB,MAAM;YACN,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;YACpB,cAAc;YACd,cAAc;YACd,eAAe;YACf,SAAS;SACV,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAE3B,OAAO;QACL,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,MAAM;YACN,OAAO;YACP,MAAM;YACN,IAAI;YACJ,IAAI;SACL;QACD,SAAS;KACV,CAAC;AACJ,CAAC,EACD,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAChC,CAAC","sourcesContent":["import { IPosition, provideAccountInfoService } from '@yuants/data-account';\nimport { Terminal } from '@yuants/protocol';\nimport { getApiV1Account, getApiV1TickerPrice } from './sapi';\n\nconst ADDRESS = process.env.ADDRESS!;\nexport const SPOT_ACCOUNT_ID = `ASTER/${ADDRESS}/SPOT`;\n\nprovideAccountInfoService(\n Terminal.fromNodeEnv(),\n SPOT_ACCOUNT_ID,\n async () => {\n const [x, prices] = await Promise.all([getApiV1Account({}), getApiV1TickerPrice({})]);\n\n const positions = x.balances.map((b): IPosition => {\n const thePrice = b.asset === 'USDT' ? 1 : prices.find((p) => p.symbol === b.asset + 'USDT')?.price ?? 0;\n\n const volume = +b.free + +b.locked;\n\n const position_price = +thePrice;\n const closable_price = +thePrice;\n const valuation = volume * closable_price;\n const floating_profit = 0;\n\n return {\n position_id: b.asset,\n datasource_id: 'ASTER',\n product_id: b.asset,\n direction: 'LONG',\n volume,\n free_volume: +b.free,\n position_price,\n closable_price,\n floating_profit,\n valuation,\n };\n });\n\n const usdtAsset = x.balances.find((b) => b.asset === 'USDT');\n const equity = positions.reduce((a, b) => a + b.valuation, 0);\n const free = usdtAsset ? +usdtAsset.free : 0;\n const balance = usdtAsset ? +usdtAsset.free + +usdtAsset.locked : 0;\n const profit = equity - balance;\n const used = equity - free;\n\n return {\n money: {\n currency: 'USDT',\n equity,\n balance,\n profit,\n free,\n used,\n },\n positions,\n };\n },\n { auto_refresh_interval: 1000 },\n);\n"]}
|
package/dist/api.js
CHANGED
|
@@ -1,33 +1,7 @@
|
|
|
1
|
+
import { opensslEquivalentHMAC } from './utils';
|
|
1
2
|
const API_KEY = process.env.API_KEY;
|
|
2
3
|
const SECRET_KEY = process.env.SECRET_KEY;
|
|
3
4
|
const BASE_URL = 'https://fapi.asterdex.com';
|
|
4
|
-
function arrayBufferToHex(buffer) {
|
|
5
|
-
const bytes = new Uint8Array(buffer);
|
|
6
|
-
const hexArray = [];
|
|
7
|
-
for (const byte of bytes) {
|
|
8
|
-
const hex = byte.toString(16).padStart(2, '0');
|
|
9
|
-
hexArray.push(hex);
|
|
10
|
-
}
|
|
11
|
-
return hexArray.join('');
|
|
12
|
-
}
|
|
13
|
-
async function opensslEquivalentHMAC(message, secretKey) {
|
|
14
|
-
try {
|
|
15
|
-
const keyBuffer = new TextEncoder().encode(secretKey);
|
|
16
|
-
const messageBuffer = new TextEncoder().encode(message);
|
|
17
|
-
// 导入密钥
|
|
18
|
-
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, {
|
|
19
|
-
name: 'HMAC',
|
|
20
|
-
hash: { name: 'SHA-256' },
|
|
21
|
-
}, false, ['sign']);
|
|
22
|
-
// 进行 HMAC-SHA256 签名
|
|
23
|
-
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
|
|
24
|
-
// 转换为16进制小写(与 openssl 输出格式一致)
|
|
25
|
-
return arrayBufferToHex(signature);
|
|
26
|
-
}
|
|
27
|
-
catch (error) {
|
|
28
|
-
throw new Error(`HMAC-SHA256 签名失败: ${error}`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
5
|
const request = async (type, method, endpoint, params = {}) => {
|
|
32
6
|
const needApiKey = type !== 'NONE';
|
|
33
7
|
const needSign = type === 'TRADE' || type === 'USER_DATA';
|
|
@@ -45,7 +19,7 @@ const request = async (type, method, endpoint, params = {}) => {
|
|
|
45
19
|
url.searchParams.set('signature', signature);
|
|
46
20
|
}
|
|
47
21
|
console.info(url.toString());
|
|
48
|
-
|
|
22
|
+
const res = await fetch(url.toString(), {
|
|
49
23
|
method,
|
|
50
24
|
headers: needApiKey
|
|
51
25
|
? {
|
|
@@ -53,6 +27,10 @@ const request = async (type, method, endpoint, params = {}) => {
|
|
|
53
27
|
}
|
|
54
28
|
: {},
|
|
55
29
|
}).then((response) => response.json());
|
|
30
|
+
if (res.code && res.code !== 0) {
|
|
31
|
+
throw JSON.stringify(res);
|
|
32
|
+
}
|
|
33
|
+
return res;
|
|
56
34
|
};
|
|
57
35
|
const createApi = (type, method, endpoint) => (params) => request(type, method, endpoint, params);
|
|
58
36
|
/**
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAW,CAAC;AAE3C,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,OAAO,GAAG,KAAK,EACnB,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,SAAc,EAAE,EACJ,EAAE;IACd,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,WAAW,CAAC;IAE1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;KACvC;IAED,IAAI,QAAQ,EAAE;QACZ,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;KAC9C;IAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM;QACN,OAAO,EAAE,UAAU;YACjB,CAAC,CAAC;gBACE,cAAc,EAAE,OAAO;aACxB;YACH,CAAC,CAAC,EAAE;KACP,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE;QAC9B,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;KAC3B;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,SAAS,GACb,CACE,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,EAAE,CACJ,CAAC,MAAY,EAAE,EAAE,CACf,OAAO,CAAO,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAqDvC,WAAW,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;AAE1C,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAmBtC,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { opensslEquivalentHMAC } from './utils';\n\nconst API_KEY = process.env.API_KEY!;\nconst SECRET_KEY = process.env.SECRET_KEY!;\n\nconst BASE_URL = 'https://fapi.asterdex.com';\n\nconst request = async <T>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n params: any = {},\n): Promise<T> => {\n const needApiKey = type !== 'NONE';\n const needSign = type === 'TRADE' || type === 'USER_DATA';\n\n const url = new URL(BASE_URL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n if (needSign) {\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);\n url.searchParams.set('signature', signature);\n }\n\n console.info(url.toString());\n const res = await fetch(url.toString(), {\n method,\n headers: needApiKey\n ? {\n 'X-MBX-APIKEY': API_KEY,\n }\n : {},\n }).then((response) => response.json());\n if (res.code && res.code !== 0) {\n throw JSON.stringify(res);\n }\n return res;\n};\n\nconst createApi =\n <TReq, TRes>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n ) =>\n (params: TReq) =>\n request<TRes>(type, method, endpoint, params);\n\n/**\n * 获取账户信息\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AFv4-user_data\n */\nexport const getFApiV4Account = createApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n updateTime: number;\n totalInitialMargin: string;\n totalMaintMargin: string;\n totalWalletBalance: string;\n totalUnrealizedProfit: string;\n totalMarginBalance: string;\n totalPositionInitialMargin: string;\n totalOpenOrderInitialMargin: string;\n totalCrossWalletBalance: string;\n totalCrossUnPnl: string;\n availableBalance: string;\n maxWithdrawAmount: string;\n assets: {\n asset: string;\n walletBalance: string;\n unrealizedProfit: string;\n marginBalance: string;\n maintMargin: string;\n initialMargin: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n maxWithdrawAmount: string;\n crossWalletBalance: string;\n crossUnPnl: string;\n availableBalance: string;\n marginAvailable: boolean;\n updateTime: number;\n }[];\n positions: {\n symbol: string;\n initialMargin: string;\n maintMargin: string;\n unrealizedProfit: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n leverage: string;\n isolated: boolean;\n entryPrice: string;\n maxNotional: string;\n positionSide: 'BOTH' | 'LONG' | 'SHORT';\n positionAmt: string;\n notional: string;\n isolatedWallet: string;\n updateTime: number;\n }[];\n }\n>('USER_DATA', 'GET', '/fapi/v4/account');\n\nexport const postFApiV1Order = createApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n type:\n | 'MARKET'\n | 'LIMIT'\n | 'STOP'\n | 'STOP_MARKET'\n | 'TAKE_PROFIT'\n | 'TAKE_PROFIT_MARKET'\n | 'TRAILING_STOP_MARKET';\n reduceOnly?: 'true' | 'false';\n quantity?: number;\n price?: number;\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX' | 'HIDDEN';\n },\n {}\n>('TRADE', 'POST', '/fapi/v1/order');\n"]}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AACnB,OAAO,SAAS,CAAC","sourcesContent":["import './account';\nimport './order';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AACnB,OAAO,gBAAgB,CAAC;AACxB,OAAO,SAAS,CAAC;AACjB,OAAO,cAAc,CAAC","sourcesContent":["import './account';\nimport './account-spot';\nimport './order';\nimport './order-spot';\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Terminal } from '@yuants/protocol';
|
|
2
|
+
import { SPOT_ACCOUNT_ID } from './account-spot';
|
|
3
|
+
import { getApiV1TickerPrice, postApiV1Order } from './sapi';
|
|
4
|
+
Terminal.fromNodeEnv().server.provideService('SubmitOrder', { required: ['account_id'], properties: { account_id: { type: 'string', const: SPOT_ACCOUNT_ID } } }, async (msg) => {
|
|
5
|
+
var _a;
|
|
6
|
+
const order = msg.req;
|
|
7
|
+
const symbol = order.product_id;
|
|
8
|
+
const type = { MARKET: 'MARKET', LIMIT: 'LIMIT', MAKER: 'LIMIT' }[order.order_type];
|
|
9
|
+
if (!type)
|
|
10
|
+
throw new Error(`Unsupported order_type: ${order.order_type}`);
|
|
11
|
+
const side = { OPEN_LONG: 'BUY', OPEN_SHORT: 'SELL', CLOSE_LONG: 'SELL', CLOSE_SHORT: 'BUY' }[order.order_direction];
|
|
12
|
+
if (!side)
|
|
13
|
+
throw new Error(`Unsupported order_direction: ${order.order_direction}`);
|
|
14
|
+
const timeInForce = order.order_type === 'MAKER' ? 'GTX' : order.order_type === 'LIMIT' ? 'GTC' : undefined;
|
|
15
|
+
const price = order.price;
|
|
16
|
+
let quantity = order.volume;
|
|
17
|
+
let quoteOrderQty;
|
|
18
|
+
if (type === 'MARKET' && side === 'BUY') {
|
|
19
|
+
const spotPrice = await getApiV1TickerPrice({});
|
|
20
|
+
const thePrice = (_a = spotPrice.find((x) => x.symbol === symbol)) === null || _a === void 0 ? void 0 : _a.price;
|
|
21
|
+
if (!thePrice)
|
|
22
|
+
throw new Error(`Cannot get price for symbol ${symbol}`);
|
|
23
|
+
quantity = undefined;
|
|
24
|
+
quoteOrderQty = order.volume * +thePrice;
|
|
25
|
+
}
|
|
26
|
+
await postApiV1Order({
|
|
27
|
+
symbol,
|
|
28
|
+
type,
|
|
29
|
+
side,
|
|
30
|
+
timeInForce,
|
|
31
|
+
price,
|
|
32
|
+
quantity,
|
|
33
|
+
quoteOrderQty,
|
|
34
|
+
});
|
|
35
|
+
return { res: { code: 0, message: 'OK' } };
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=order-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order-spot.js","sourceRoot":"","sources":["../src/order-spot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7D,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,cAAc,CAC1C,aAAa,EACb,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,EAAE,EACpG,KAAK,EAAE,GAAG,EAAE,EAAE;;IACZ,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;IACtB,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC;IAEhC,MAAM,IAAI,GAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAY,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;IAChG,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAE1E,MAAM,IAAI,GAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAY,CACtG,KAAK,CAAC,eAAgB,CACvB,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;IAEpF,MAAM,WAAW,GACf,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAE1F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAE1B,IAAI,QAAQ,GAAuB,KAAK,CAAC,MAAM,CAAC;IAChD,IAAI,aAAiC,CAAC;IAEtC,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,EAAE;QACvC,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,EAAE,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,MAAA,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,0CAAE,KAAK,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;QACxE,QAAQ,GAAG,SAAS,CAAC;QACrB,aAAa,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC;KAC1C;IAED,MAAM,cAAc,CAAC;QACnB,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,WAAW;QACX,KAAK;QACL,QAAQ;QACR,aAAa;KACd,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;AAC7C,CAAC,CACF,CAAC","sourcesContent":["import { IOrder } from '@yuants/data-order';\nimport { Terminal } from '@yuants/protocol';\nimport { SPOT_ACCOUNT_ID } from './account-spot';\nimport { getApiV1TickerPrice, postApiV1Order } from './sapi';\n\nTerminal.fromNodeEnv().server.provideService<IOrder>(\n 'SubmitOrder',\n { required: ['account_id'], properties: { account_id: { type: 'string', const: SPOT_ACCOUNT_ID } } },\n async (msg) => {\n const order = msg.req;\n const symbol = order.product_id;\n\n const type = ({ MARKET: 'MARKET', LIMIT: 'LIMIT', MAKER: 'LIMIT' } as const)[order.order_type!];\n if (!type) throw new Error(`Unsupported order_type: ${order.order_type}`);\n\n const side = ({ OPEN_LONG: 'BUY', OPEN_SHORT: 'SELL', CLOSE_LONG: 'SELL', CLOSE_SHORT: 'BUY' } as const)[\n order.order_direction!\n ];\n if (!side) throw new Error(`Unsupported order_direction: ${order.order_direction}`);\n\n const timeInForce =\n order.order_type === 'MAKER' ? 'GTX' : order.order_type === 'LIMIT' ? 'GTC' : undefined;\n\n const price = order.price;\n\n let quantity: number | undefined = order.volume;\n let quoteOrderQty: number | undefined;\n\n if (type === 'MARKET' && side === 'BUY') {\n const spotPrice = await getApiV1TickerPrice({});\n const thePrice = spotPrice.find((x) => x.symbol === symbol)?.price;\n if (!thePrice) throw new Error(`Cannot get price for symbol ${symbol}`);\n quantity = undefined;\n quoteOrderQty = order.volume * +thePrice;\n }\n\n await postApiV1Order({\n symbol,\n type,\n side,\n timeInForce,\n price,\n quantity,\n quoteOrderQty,\n });\n\n return { res: { code: 0, message: 'OK' } };\n },\n);\n"]}
|
package/dist/sapi.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { opensslEquivalentHMAC } from './utils';
|
|
2
|
+
const API_KEY = process.env.API_KEY;
|
|
3
|
+
const SECRET_KEY = process.env.SECRET_KEY;
|
|
4
|
+
const BASE_URL = 'https://sapi.asterdex.com';
|
|
5
|
+
const request = async (type, method, endpoint, params = {}) => {
|
|
6
|
+
const needApiKey = type !== 'NONE';
|
|
7
|
+
const needSign = type === 'TRADE' || type === 'USER_DATA';
|
|
8
|
+
const url = new URL(BASE_URL);
|
|
9
|
+
url.pathname = endpoint;
|
|
10
|
+
for (const [key, value] of Object.entries(params)) {
|
|
11
|
+
if (value === undefined)
|
|
12
|
+
continue;
|
|
13
|
+
url.searchParams.set(key, `${value}`);
|
|
14
|
+
}
|
|
15
|
+
if (needSign) {
|
|
16
|
+
url.searchParams.set('timestamp', `${Date.now()}`);
|
|
17
|
+
const msg = url.search.slice(1); // 去掉开头的 '?'
|
|
18
|
+
const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);
|
|
19
|
+
url.searchParams.set('signature', signature);
|
|
20
|
+
}
|
|
21
|
+
console.info(url.toString());
|
|
22
|
+
const response = await fetch(url.toString(), {
|
|
23
|
+
method,
|
|
24
|
+
headers: needApiKey
|
|
25
|
+
? {
|
|
26
|
+
'X-MBX-APIKEY': API_KEY,
|
|
27
|
+
}
|
|
28
|
+
: {},
|
|
29
|
+
}).then((response) => response.json());
|
|
30
|
+
if (response.code && response.code !== 0) {
|
|
31
|
+
throw JSON.stringify(response);
|
|
32
|
+
}
|
|
33
|
+
return response;
|
|
34
|
+
};
|
|
35
|
+
const createApi = (type, method, endpoint) => (params) => request(type, method, endpoint, params);
|
|
36
|
+
/**
|
|
37
|
+
* 获取账户信息 (现货)
|
|
38
|
+
*
|
|
39
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data
|
|
40
|
+
*/
|
|
41
|
+
export const getApiV1Account = createApi('USER_DATA', 'GET', '/api/v1/account');
|
|
42
|
+
/**
|
|
43
|
+
* 获取最新价格
|
|
44
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC
|
|
45
|
+
*/
|
|
46
|
+
export const getApiV1TickerPrice = createApi('MARKET_DATA', 'GET', '/api/v1/ticker/price');
|
|
47
|
+
export const postApiV1Order = createApi('TRADE', 'POST', '/api/v1/order');
|
|
48
|
+
//# sourceMappingURL=sapi.js.map
|
package/dist/sapi.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sapi.js","sourceRoot":"","sources":["../src/sapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAW,CAAC;AAE3C,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,OAAO,GAAG,KAAK,EACnB,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,SAAc,EAAE,EACJ,EAAE;IACd,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,WAAW,CAAC;IAE1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;KACvC;IAED,IAAI,QAAQ,EAAE;QACZ,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;QAC7C,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;KAC9C;IAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE7B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QAC3C,MAAM;QACN,OAAO,EAAE,UAAU;YACjB,CAAC,CAAC;gBACE,cAAc,EAAE,OAAO;aACxB;YACH,CAAC,CAAC,EAAE;KACP,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAS,CAAC,CAAC;IAE9C,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE;QACxC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;KAChC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,SAAS,GACb,CACE,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,EAAE,CACJ,CAAC,MAAY,EAAE,EAAE,CACf,OAAO,CAAO,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAetC,WAAW,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAO1C,aAAa,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC;AAEhD,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAWrC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC","sourcesContent":["import { opensslEquivalentHMAC } from './utils';\n\nconst API_KEY = process.env.API_KEY!;\nconst SECRET_KEY = process.env.SECRET_KEY!;\n\nconst BASE_URL = 'https://sapi.asterdex.com';\n\nconst request = async <T>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n params: any = {},\n): Promise<T> => {\n const needApiKey = type !== 'NONE';\n const needSign = type === 'TRADE' || type === 'USER_DATA';\n\n const url = new URL(BASE_URL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n if (needSign) {\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);\n url.searchParams.set('signature', signature);\n }\n\n console.info(url.toString());\n\n const response = await fetch(url.toString(), {\n method,\n headers: needApiKey\n ? {\n 'X-MBX-APIKEY': API_KEY,\n }\n : {},\n }).then((response) => response.json() as any);\n\n if (response.code && response.code !== 0) {\n throw JSON.stringify(response);\n }\n\n return response;\n};\n\nconst createApi =\n <TReq, TRes>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n ) =>\n (params: TReq) =>\n request<TRes>(type, method, endpoint, params);\n\n/**\n * 获取账户信息 (现货)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data\n */\nexport const getApiV1Account = createApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n canBurnAsset: boolean;\n updateTime: number;\n balances: {\n asset: string;\n free: string;\n locked: string;\n }[];\n }\n>('USER_DATA', 'GET', '/api/v1/account');\n\n/**\n * 获取最新价格\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getApiV1TickerPrice = createApi<\n {},\n {\n symbol: string;\n price: string;\n time: number;\n }[]\n>('MARKET_DATA', 'GET', '/api/v1/ticker/price');\n\nexport const postApiV1Order = createApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET';\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX';\n quantity?: number;\n quoteOrderQty?: number;\n price?: number;\n },\n {}\n>('TRADE', 'POST', '/api/v1/order');\n"]}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function arrayBufferToHex(buffer) {
|
|
2
|
+
const bytes = new Uint8Array(buffer);
|
|
3
|
+
const hexArray = [];
|
|
4
|
+
for (const byte of bytes) {
|
|
5
|
+
const hex = byte.toString(16).padStart(2, '0');
|
|
6
|
+
hexArray.push(hex);
|
|
7
|
+
}
|
|
8
|
+
return hexArray.join('');
|
|
9
|
+
}
|
|
10
|
+
export async function opensslEquivalentHMAC(message, secretKey) {
|
|
11
|
+
try {
|
|
12
|
+
const keyBuffer = new TextEncoder().encode(secretKey);
|
|
13
|
+
const messageBuffer = new TextEncoder().encode(message);
|
|
14
|
+
// 导入密钥
|
|
15
|
+
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, {
|
|
16
|
+
name: 'HMAC',
|
|
17
|
+
hash: { name: 'SHA-256' },
|
|
18
|
+
}, false, ['sign']);
|
|
19
|
+
// 进行 HMAC-SHA256 签名
|
|
20
|
+
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
|
|
21
|
+
// 转换为16进制小写(与 openssl 输出格式一致)
|
|
22
|
+
return arrayBufferToHex(signature);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new Error(`HMAC-SHA256 签名失败: ${error}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,gBAAgB,CAAC,MAAmB;IAClD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/C,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KACpB;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAiB;IAC5E,IAAI;QACF,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,aAAa,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAExD,OAAO;QACP,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC7C,KAAK,EACL,SAAS,EACT;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC1B,EACD,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;QAEF,oBAAoB;QACpB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QAE7E,8BAA8B;QAC9B,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC;KACpC;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;KAC/C;AACH,CAAC","sourcesContent":["export function arrayBufferToHex(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n const hexArray: string[] = [];\n\n for (const byte of bytes) {\n const hex = byte.toString(16).padStart(2, '0');\n hexArray.push(hex);\n }\n\n return hexArray.join('');\n}\n\nexport async function opensslEquivalentHMAC(message: string, secretKey: string): Promise<string> {\n try {\n const keyBuffer = new TextEncoder().encode(secretKey);\n const messageBuffer = new TextEncoder().encode(message);\n\n // 导入密钥\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n keyBuffer,\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n },\n false,\n ['sign'],\n );\n\n // 进行 HMAC-SHA256 签名\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);\n\n // 转换为16进制小写(与 openssl 输出格式一致)\n return arrayBufferToHex(signature);\n } catch (error) {\n throw new Error(`HMAC-SHA256 签名失败: ${error}`);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-spot.d.ts","sourceRoot":"","sources":["../src/account-spot.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe,QAA0B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SPOT_ACCOUNT_ID = void 0;
|
|
4
|
+
const data_account_1 = require("@yuants/data-account");
|
|
5
|
+
const protocol_1 = require("@yuants/protocol");
|
|
6
|
+
const sapi_1 = require("./sapi");
|
|
7
|
+
const ADDRESS = process.env.ADDRESS;
|
|
8
|
+
exports.SPOT_ACCOUNT_ID = `ASTER/${ADDRESS}/SPOT`;
|
|
9
|
+
(0, data_account_1.provideAccountInfoService)(protocol_1.Terminal.fromNodeEnv(), exports.SPOT_ACCOUNT_ID, async () => {
|
|
10
|
+
const [x, prices] = await Promise.all([(0, sapi_1.getApiV1Account)({}), (0, sapi_1.getApiV1TickerPrice)({})]);
|
|
11
|
+
const positions = x.balances.map((b) => {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
const thePrice = b.asset === 'USDT' ? 1 : (_b = (_a = prices.find((p) => p.symbol === b.asset + 'USDT')) === null || _a === void 0 ? void 0 : _a.price) !== null && _b !== void 0 ? _b : 0;
|
|
14
|
+
const volume = +b.free + +b.locked;
|
|
15
|
+
const position_price = +thePrice;
|
|
16
|
+
const closable_price = +thePrice;
|
|
17
|
+
const valuation = volume * closable_price;
|
|
18
|
+
const floating_profit = 0;
|
|
19
|
+
return {
|
|
20
|
+
position_id: b.asset,
|
|
21
|
+
datasource_id: 'ASTER',
|
|
22
|
+
product_id: b.asset,
|
|
23
|
+
direction: 'LONG',
|
|
24
|
+
volume,
|
|
25
|
+
free_volume: +b.free,
|
|
26
|
+
position_price,
|
|
27
|
+
closable_price,
|
|
28
|
+
floating_profit,
|
|
29
|
+
valuation,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
const usdtAsset = x.balances.find((b) => b.asset === 'USDT');
|
|
33
|
+
const equity = positions.reduce((a, b) => a + b.valuation, 0);
|
|
34
|
+
const free = usdtAsset ? +usdtAsset.free : 0;
|
|
35
|
+
const balance = usdtAsset ? +usdtAsset.free + +usdtAsset.locked : 0;
|
|
36
|
+
const profit = equity - balance;
|
|
37
|
+
const used = equity - free;
|
|
38
|
+
return {
|
|
39
|
+
money: {
|
|
40
|
+
currency: 'USDT',
|
|
41
|
+
equity,
|
|
42
|
+
balance,
|
|
43
|
+
profit,
|
|
44
|
+
free,
|
|
45
|
+
used,
|
|
46
|
+
},
|
|
47
|
+
positions,
|
|
48
|
+
};
|
|
49
|
+
}, { auto_refresh_interval: 1000 });
|
|
50
|
+
//# sourceMappingURL=account-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-spot.js","sourceRoot":"","sources":["../src/account-spot.ts"],"names":[],"mappings":";;;AAAA,uDAA4E;AAC5E,+CAA4C;AAC5C,iCAA8D;AAE9D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACxB,QAAA,eAAe,GAAG,SAAS,OAAO,OAAO,CAAC;AAEvD,IAAA,wCAAyB,EACvB,mBAAQ,CAAC,WAAW,EAAE,EACtB,uBAAe,EACf,KAAK,IAAI,EAAE;IACT,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAA,sBAAe,EAAC,EAAE,CAAC,EAAE,IAAA,0BAAmB,EAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEtF,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAa,EAAE;;QAChD,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAA,MAAA,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,KAAK,GAAG,MAAM,CAAC,0CAAE,KAAK,mCAAI,CAAC,CAAC;QAExG,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;QAEnC,MAAM,cAAc,GAAG,CAAC,QAAQ,CAAC;QACjC,MAAM,cAAc,GAAG,CAAC,QAAQ,CAAC;QACjC,MAAM,SAAS,GAAG,MAAM,GAAG,cAAc,CAAC;QAC1C,MAAM,eAAe,GAAG,CAAC,CAAC;QAE1B,OAAO;YACL,WAAW,EAAE,CAAC,CAAC,KAAK;YACpB,aAAa,EAAE,OAAO;YACtB,UAAU,EAAE,CAAC,CAAC,KAAK;YACnB,SAAS,EAAE,MAAM;YACjB,MAAM;YACN,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI;YACpB,cAAc;YACd,cAAc;YACd,eAAe;YACf,SAAS;SACV,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAChC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAE3B,OAAO;QACL,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,MAAM;YACN,OAAO;YACP,MAAM;YACN,IAAI;YACJ,IAAI;SACL;QACD,SAAS;KACV,CAAC;AACJ,CAAC,EACD,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAChC,CAAC","sourcesContent":["import { IPosition, provideAccountInfoService } from '@yuants/data-account';\nimport { Terminal } from '@yuants/protocol';\nimport { getApiV1Account, getApiV1TickerPrice } from './sapi';\n\nconst ADDRESS = process.env.ADDRESS!;\nexport const SPOT_ACCOUNT_ID = `ASTER/${ADDRESS}/SPOT`;\n\nprovideAccountInfoService(\n Terminal.fromNodeEnv(),\n SPOT_ACCOUNT_ID,\n async () => {\n const [x, prices] = await Promise.all([getApiV1Account({}), getApiV1TickerPrice({})]);\n\n const positions = x.balances.map((b): IPosition => {\n const thePrice = b.asset === 'USDT' ? 1 : prices.find((p) => p.symbol === b.asset + 'USDT')?.price ?? 0;\n\n const volume = +b.free + +b.locked;\n\n const position_price = +thePrice;\n const closable_price = +thePrice;\n const valuation = volume * closable_price;\n const floating_profit = 0;\n\n return {\n position_id: b.asset,\n datasource_id: 'ASTER',\n product_id: b.asset,\n direction: 'LONG',\n volume,\n free_volume: +b.free,\n position_price,\n closable_price,\n floating_profit,\n valuation,\n };\n });\n\n const usdtAsset = x.balances.find((b) => b.asset === 'USDT');\n const equity = positions.reduce((a, b) => a + b.valuation, 0);\n const free = usdtAsset ? +usdtAsset.free : 0;\n const balance = usdtAsset ? +usdtAsset.free + +usdtAsset.locked : 0;\n const profit = equity - balance;\n const used = equity - free;\n\n return {\n money: {\n currency: 'USDT',\n equity,\n balance,\n profit,\n free,\n used,\n },\n positions,\n };\n },\n { auto_refresh_interval: 1000 },\n);\n"]}
|
package/lib/api.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export declare const getFApiV4Account: (params: {}) => Promise<{
|
|
|
57
57
|
export declare const postFApiV1Order: (params: {
|
|
58
58
|
symbol: string;
|
|
59
59
|
side: 'BUY' | 'SELL';
|
|
60
|
-
positionSide?: "
|
|
60
|
+
positionSide?: "LONG" | "BOTH" | "SHORT" | undefined;
|
|
61
61
|
type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET' | 'TRAILING_STOP_MARKET';
|
|
62
62
|
reduceOnly?: "true" | "false" | undefined;
|
|
63
63
|
quantity?: number | undefined;
|
package/lib/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAsDA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;aAGhB,MAAM;cACL,OAAO;gBACL,OAAO;iBACN,OAAO;gBACR,MAAM;wBACE,MAAM;sBACR,MAAM;wBACJ,MAAM;2BACH,MAAM;wBACT,MAAM;gCACE,MAAM;iCACL,MAAM;6BACV,MAAM;qBACd,MAAM;sBACL,MAAM;uBACL,MAAM;YACjB;QACN,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,CAAC;QACzB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,OAAO,CAAC;QACzB,UAAU,EAAE,MAAM,CAAC;KACpB,EAAE;eACQ;QACT,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,gBAAgB,EAAE,MAAM,CAAC;QACzB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QACxC,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;KACpB,EAAE;EAEkC,CAAC;AAE1C,eAAO,MAAM,eAAe;YAEhB,MAAM;UACR,KAAK,GAAG,MAAM;;UAGhB,QAAQ,GACR,OAAO,GACP,MAAM,GACN,aAAa,GACb,aAAa,GACb,oBAAoB,GACpB,sBAAsB;;;;;iBAOM,CAAC"}
|
package/lib/api.js
CHANGED
|
@@ -1,36 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.postFApiV1Order = exports.getFApiV4Account = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
4
5
|
const API_KEY = process.env.API_KEY;
|
|
5
6
|
const SECRET_KEY = process.env.SECRET_KEY;
|
|
6
7
|
const BASE_URL = 'https://fapi.asterdex.com';
|
|
7
|
-
function arrayBufferToHex(buffer) {
|
|
8
|
-
const bytes = new Uint8Array(buffer);
|
|
9
|
-
const hexArray = [];
|
|
10
|
-
for (const byte of bytes) {
|
|
11
|
-
const hex = byte.toString(16).padStart(2, '0');
|
|
12
|
-
hexArray.push(hex);
|
|
13
|
-
}
|
|
14
|
-
return hexArray.join('');
|
|
15
|
-
}
|
|
16
|
-
async function opensslEquivalentHMAC(message, secretKey) {
|
|
17
|
-
try {
|
|
18
|
-
const keyBuffer = new TextEncoder().encode(secretKey);
|
|
19
|
-
const messageBuffer = new TextEncoder().encode(message);
|
|
20
|
-
// 导入密钥
|
|
21
|
-
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, {
|
|
22
|
-
name: 'HMAC',
|
|
23
|
-
hash: { name: 'SHA-256' },
|
|
24
|
-
}, false, ['sign']);
|
|
25
|
-
// 进行 HMAC-SHA256 签名
|
|
26
|
-
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
|
|
27
|
-
// 转换为16进制小写(与 openssl 输出格式一致)
|
|
28
|
-
return arrayBufferToHex(signature);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
throw new Error(`HMAC-SHA256 签名失败: ${error}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
8
|
const request = async (type, method, endpoint, params = {}) => {
|
|
35
9
|
const needApiKey = type !== 'NONE';
|
|
36
10
|
const needSign = type === 'TRADE' || type === 'USER_DATA';
|
|
@@ -44,11 +18,11 @@ const request = async (type, method, endpoint, params = {}) => {
|
|
|
44
18
|
if (needSign) {
|
|
45
19
|
url.searchParams.set('timestamp', `${Date.now()}`);
|
|
46
20
|
const msg = url.search.slice(1); // 去掉开头的 '?'
|
|
47
|
-
const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);
|
|
21
|
+
const signature = await (0, utils_1.opensslEquivalentHMAC)(msg, SECRET_KEY);
|
|
48
22
|
url.searchParams.set('signature', signature);
|
|
49
23
|
}
|
|
50
24
|
console.info(url.toString());
|
|
51
|
-
|
|
25
|
+
const res = await fetch(url.toString(), {
|
|
52
26
|
method,
|
|
53
27
|
headers: needApiKey
|
|
54
28
|
? {
|
|
@@ -56,6 +30,10 @@ const request = async (type, method, endpoint, params = {}) => {
|
|
|
56
30
|
}
|
|
57
31
|
: {},
|
|
58
32
|
}).then((response) => response.json());
|
|
33
|
+
if (res.code && res.code !== 0) {
|
|
34
|
+
throw JSON.stringify(res);
|
|
35
|
+
}
|
|
36
|
+
return res;
|
|
59
37
|
};
|
|
60
38
|
const createApi = (type, method, endpoint) => (params) => request(type, method, endpoint, params);
|
|
61
39
|
/**
|
package/lib/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":";;;AAAA,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAW,CAAC;AAE3C,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":";;;AAAA,mCAAgD;AAEhD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAW,CAAC;AAE3C,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,OAAO,GAAG,KAAK,EACnB,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,SAAc,EAAE,EACJ,EAAE;IACd,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,WAAW,CAAC;IAE1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;KACvC;IAED,IAAI,QAAQ,EAAE;QACZ,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAA,6BAAqB,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;KAC9C;IAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QACtC,MAAM;QACN,OAAO,EAAE,UAAU;YACjB,CAAC,CAAC;gBACE,cAAc,EAAE,OAAO;aACxB;YACH,CAAC,CAAC,EAAE;KACP,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE;QAC9B,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;KAC3B;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,SAAS,GACb,CACE,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,EAAE,CACJ,CAAC,MAAY,EAAE,EAAE,CACf,OAAO,CAAO,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD;;;;GAIG;AACU,QAAA,gBAAgB,GAAG,SAAS,CAqDvC,WAAW,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;AAE7B,QAAA,eAAe,GAAG,SAAS,CAmBtC,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,CAAC","sourcesContent":["import { opensslEquivalentHMAC } from './utils';\n\nconst API_KEY = process.env.API_KEY!;\nconst SECRET_KEY = process.env.SECRET_KEY!;\n\nconst BASE_URL = 'https://fapi.asterdex.com';\n\nconst request = async <T>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n params: any = {},\n): Promise<T> => {\n const needApiKey = type !== 'NONE';\n const needSign = type === 'TRADE' || type === 'USER_DATA';\n\n const url = new URL(BASE_URL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n if (needSign) {\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);\n url.searchParams.set('signature', signature);\n }\n\n console.info(url.toString());\n const res = await fetch(url.toString(), {\n method,\n headers: needApiKey\n ? {\n 'X-MBX-APIKEY': API_KEY,\n }\n : {},\n }).then((response) => response.json());\n if (res.code && res.code !== 0) {\n throw JSON.stringify(res);\n }\n return res;\n};\n\nconst createApi =\n <TReq, TRes>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n ) =>\n (params: TReq) =>\n request<TRes>(type, method, endpoint, params);\n\n/**\n * 获取账户信息\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-futures-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AFv4-user_data\n */\nexport const getFApiV4Account = createApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n updateTime: number;\n totalInitialMargin: string;\n totalMaintMargin: string;\n totalWalletBalance: string;\n totalUnrealizedProfit: string;\n totalMarginBalance: string;\n totalPositionInitialMargin: string;\n totalOpenOrderInitialMargin: string;\n totalCrossWalletBalance: string;\n totalCrossUnPnl: string;\n availableBalance: string;\n maxWithdrawAmount: string;\n assets: {\n asset: string;\n walletBalance: string;\n unrealizedProfit: string;\n marginBalance: string;\n maintMargin: string;\n initialMargin: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n maxWithdrawAmount: string;\n crossWalletBalance: string;\n crossUnPnl: string;\n availableBalance: string;\n marginAvailable: boolean;\n updateTime: number;\n }[];\n positions: {\n symbol: string;\n initialMargin: string;\n maintMargin: string;\n unrealizedProfit: string;\n positionInitialMargin: string;\n openOrderInitialMargin: string;\n leverage: string;\n isolated: boolean;\n entryPrice: string;\n maxNotional: string;\n positionSide: 'BOTH' | 'LONG' | 'SHORT';\n positionAmt: string;\n notional: string;\n isolatedWallet: string;\n updateTime: number;\n }[];\n }\n>('USER_DATA', 'GET', '/fapi/v4/account');\n\nexport const postFApiV1Order = createApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n positionSide?: 'BOTH' | 'LONG' | 'SHORT';\n type:\n | 'MARKET'\n | 'LIMIT'\n | 'STOP'\n | 'STOP_MARKET'\n | 'TAKE_PROFIT'\n | 'TAKE_PROFIT_MARKET'\n | 'TRAILING_STOP_MARKET';\n reduceOnly?: 'true' | 'false';\n quantity?: number;\n price?: number;\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX' | 'HIDDEN';\n },\n {}\n>('TRADE', 'POST', '/fapi/v1/order');\n"]}
|
package/lib/index.d.ts
CHANGED
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AACnB,OAAO,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,CAAC;AACnB,OAAO,gBAAgB,CAAC;AACxB,OAAO,SAAS,CAAC;AACjB,OAAO,cAAc,CAAC"}
|
package/lib/index.js
CHANGED
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,qBAAmB;AACnB,mBAAiB","sourcesContent":["import './account';\nimport './order';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,qBAAmB;AACnB,0BAAwB;AACxB,mBAAiB;AACjB,wBAAsB","sourcesContent":["import './account';\nimport './account-spot';\nimport './order';\nimport './order-spot';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order-spot.d.ts","sourceRoot":"","sources":["../src/order-spot.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const protocol_1 = require("@yuants/protocol");
|
|
4
|
+
const account_spot_1 = require("./account-spot");
|
|
5
|
+
const sapi_1 = require("./sapi");
|
|
6
|
+
protocol_1.Terminal.fromNodeEnv().server.provideService('SubmitOrder', { required: ['account_id'], properties: { account_id: { type: 'string', const: account_spot_1.SPOT_ACCOUNT_ID } } }, async (msg) => {
|
|
7
|
+
var _a;
|
|
8
|
+
const order = msg.req;
|
|
9
|
+
const symbol = order.product_id;
|
|
10
|
+
const type = { MARKET: 'MARKET', LIMIT: 'LIMIT', MAKER: 'LIMIT' }[order.order_type];
|
|
11
|
+
if (!type)
|
|
12
|
+
throw new Error(`Unsupported order_type: ${order.order_type}`);
|
|
13
|
+
const side = { OPEN_LONG: 'BUY', OPEN_SHORT: 'SELL', CLOSE_LONG: 'SELL', CLOSE_SHORT: 'BUY' }[order.order_direction];
|
|
14
|
+
if (!side)
|
|
15
|
+
throw new Error(`Unsupported order_direction: ${order.order_direction}`);
|
|
16
|
+
const timeInForce = order.order_type === 'MAKER' ? 'GTX' : order.order_type === 'LIMIT' ? 'GTC' : undefined;
|
|
17
|
+
const price = order.price;
|
|
18
|
+
let quantity = order.volume;
|
|
19
|
+
let quoteOrderQty;
|
|
20
|
+
if (type === 'MARKET' && side === 'BUY') {
|
|
21
|
+
const spotPrice = await (0, sapi_1.getApiV1TickerPrice)({});
|
|
22
|
+
const thePrice = (_a = spotPrice.find((x) => x.symbol === symbol)) === null || _a === void 0 ? void 0 : _a.price;
|
|
23
|
+
if (!thePrice)
|
|
24
|
+
throw new Error(`Cannot get price for symbol ${symbol}`);
|
|
25
|
+
quantity = undefined;
|
|
26
|
+
quoteOrderQty = order.volume * +thePrice;
|
|
27
|
+
}
|
|
28
|
+
await (0, sapi_1.postApiV1Order)({
|
|
29
|
+
symbol,
|
|
30
|
+
type,
|
|
31
|
+
side,
|
|
32
|
+
timeInForce,
|
|
33
|
+
price,
|
|
34
|
+
quantity,
|
|
35
|
+
quoteOrderQty,
|
|
36
|
+
});
|
|
37
|
+
return { res: { code: 0, message: 'OK' } };
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=order-spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"order-spot.js","sourceRoot":"","sources":["../src/order-spot.ts"],"names":[],"mappings":";;AACA,+CAA4C;AAC5C,iDAAiD;AACjD,iCAA6D;AAE7D,mBAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC,cAAc,CAC1C,aAAa,EACb,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,8BAAe,EAAE,EAAE,EAAE,EACpG,KAAK,EAAE,GAAG,EAAE,EAAE;;IACZ,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC;IACtB,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC;IAEhC,MAAM,IAAI,GAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAY,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;IAChG,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAE1E,MAAM,IAAI,GAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAY,CACtG,KAAK,CAAC,eAAgB,CACvB,CAAC;IACF,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;IAEpF,MAAM,WAAW,GACf,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAE1F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAE1B,IAAI,QAAQ,GAAuB,KAAK,CAAC,MAAM,CAAC;IAChD,IAAI,aAAiC,CAAC;IAEtC,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,EAAE;QACvC,MAAM,SAAS,GAAG,MAAM,IAAA,0BAAmB,EAAC,EAAE,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,MAAA,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,0CAAE,KAAK,CAAC;QACnE,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;QACxE,QAAQ,GAAG,SAAS,CAAC;QACrB,aAAa,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC;KAC1C;IAED,MAAM,IAAA,qBAAc,EAAC;QACnB,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,WAAW;QACX,KAAK;QACL,QAAQ;QACR,aAAa;KACd,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;AAC7C,CAAC,CACF,CAAC","sourcesContent":["import { IOrder } from '@yuants/data-order';\nimport { Terminal } from '@yuants/protocol';\nimport { SPOT_ACCOUNT_ID } from './account-spot';\nimport { getApiV1TickerPrice, postApiV1Order } from './sapi';\n\nTerminal.fromNodeEnv().server.provideService<IOrder>(\n 'SubmitOrder',\n { required: ['account_id'], properties: { account_id: { type: 'string', const: SPOT_ACCOUNT_ID } } },\n async (msg) => {\n const order = msg.req;\n const symbol = order.product_id;\n\n const type = ({ MARKET: 'MARKET', LIMIT: 'LIMIT', MAKER: 'LIMIT' } as const)[order.order_type!];\n if (!type) throw new Error(`Unsupported order_type: ${order.order_type}`);\n\n const side = ({ OPEN_LONG: 'BUY', OPEN_SHORT: 'SELL', CLOSE_LONG: 'SELL', CLOSE_SHORT: 'BUY' } as const)[\n order.order_direction!\n ];\n if (!side) throw new Error(`Unsupported order_direction: ${order.order_direction}`);\n\n const timeInForce =\n order.order_type === 'MAKER' ? 'GTX' : order.order_type === 'LIMIT' ? 'GTC' : undefined;\n\n const price = order.price;\n\n let quantity: number | undefined = order.volume;\n let quoteOrderQty: number | undefined;\n\n if (type === 'MARKET' && side === 'BUY') {\n const spotPrice = await getApiV1TickerPrice({});\n const thePrice = spotPrice.find((x) => x.symbol === symbol)?.price;\n if (!thePrice) throw new Error(`Cannot get price for symbol ${symbol}`);\n quantity = undefined;\n quoteOrderQty = order.volume * +thePrice;\n }\n\n await postApiV1Order({\n symbol,\n type,\n side,\n timeInForce,\n price,\n quantity,\n quoteOrderQty,\n });\n\n return { res: { code: 0, message: 'OK' } };\n },\n);\n"]}
|
package/lib/sapi.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 获取账户信息 (现货)
|
|
3
|
+
*
|
|
4
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data
|
|
5
|
+
*/
|
|
6
|
+
export declare const getApiV1Account: (params: {}) => Promise<{
|
|
7
|
+
feeTier: number;
|
|
8
|
+
canTrade: boolean;
|
|
9
|
+
canDeposit: boolean;
|
|
10
|
+
canWithdraw: boolean;
|
|
11
|
+
canBurnAsset: boolean;
|
|
12
|
+
updateTime: number;
|
|
13
|
+
balances: {
|
|
14
|
+
asset: string;
|
|
15
|
+
free: string;
|
|
16
|
+
locked: string;
|
|
17
|
+
}[];
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* 获取最新价格
|
|
21
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC
|
|
22
|
+
*/
|
|
23
|
+
export declare const getApiV1TickerPrice: (params: {}) => Promise<{
|
|
24
|
+
symbol: string;
|
|
25
|
+
price: string;
|
|
26
|
+
time: number;
|
|
27
|
+
}[]>;
|
|
28
|
+
export declare const postApiV1Order: (params: {
|
|
29
|
+
symbol: string;
|
|
30
|
+
side: 'BUY' | 'SELL';
|
|
31
|
+
type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET';
|
|
32
|
+
timeInForce?: "GTC" | "IOC" | "FOK" | "GTX" | undefined;
|
|
33
|
+
quantity?: number | undefined;
|
|
34
|
+
quoteOrderQty?: number | undefined;
|
|
35
|
+
price?: number | undefined;
|
|
36
|
+
}) => Promise<{}>;
|
|
37
|
+
//# sourceMappingURL=sapi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sapi.d.ts","sourceRoot":"","sources":["../src/sapi.ts"],"names":[],"mappings":"AAyDA;;;;GAIG;AACH,eAAO,MAAM,eAAe;aAGf,MAAM;cACL,OAAO;gBACL,OAAO;iBACN,OAAO;kBACN,OAAO;gBACT,MAAM;cACR;QACR,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,EAAE;EAEiC,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,mBAAmB;YAGpB,MAAM;WACP,MAAM;UACP,MAAM;IAE+B,CAAC;AAEhD,eAAO,MAAM,cAAc;YAEf,MAAM;UACR,KAAK,GAAG,MAAM;UACd,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,GAAG,aAAa,GAAG,oBAAoB;;;;;iBAOzD,CAAC"}
|
package/lib/sapi.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.postApiV1Order = exports.getApiV1TickerPrice = exports.getApiV1Account = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
const API_KEY = process.env.API_KEY;
|
|
6
|
+
const SECRET_KEY = process.env.SECRET_KEY;
|
|
7
|
+
const BASE_URL = 'https://sapi.asterdex.com';
|
|
8
|
+
const request = async (type, method, endpoint, params = {}) => {
|
|
9
|
+
const needApiKey = type !== 'NONE';
|
|
10
|
+
const needSign = type === 'TRADE' || type === 'USER_DATA';
|
|
11
|
+
const url = new URL(BASE_URL);
|
|
12
|
+
url.pathname = endpoint;
|
|
13
|
+
for (const [key, value] of Object.entries(params)) {
|
|
14
|
+
if (value === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
url.searchParams.set(key, `${value}`);
|
|
17
|
+
}
|
|
18
|
+
if (needSign) {
|
|
19
|
+
url.searchParams.set('timestamp', `${Date.now()}`);
|
|
20
|
+
const msg = url.search.slice(1); // 去掉开头的 '?'
|
|
21
|
+
const signature = await (0, utils_1.opensslEquivalentHMAC)(msg, SECRET_KEY);
|
|
22
|
+
url.searchParams.set('signature', signature);
|
|
23
|
+
}
|
|
24
|
+
console.info(url.toString());
|
|
25
|
+
const response = await fetch(url.toString(), {
|
|
26
|
+
method,
|
|
27
|
+
headers: needApiKey
|
|
28
|
+
? {
|
|
29
|
+
'X-MBX-APIKEY': API_KEY,
|
|
30
|
+
}
|
|
31
|
+
: {},
|
|
32
|
+
}).then((response) => response.json());
|
|
33
|
+
if (response.code && response.code !== 0) {
|
|
34
|
+
throw JSON.stringify(response);
|
|
35
|
+
}
|
|
36
|
+
return response;
|
|
37
|
+
};
|
|
38
|
+
const createApi = (type, method, endpoint) => (params) => request(type, method, endpoint, params);
|
|
39
|
+
/**
|
|
40
|
+
* 获取账户信息 (现货)
|
|
41
|
+
*
|
|
42
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data
|
|
43
|
+
*/
|
|
44
|
+
exports.getApiV1Account = createApi('USER_DATA', 'GET', '/api/v1/account');
|
|
45
|
+
/**
|
|
46
|
+
* 获取最新价格
|
|
47
|
+
* https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC
|
|
48
|
+
*/
|
|
49
|
+
exports.getApiV1TickerPrice = createApi('MARKET_DATA', 'GET', '/api/v1/ticker/price');
|
|
50
|
+
exports.postApiV1Order = createApi('TRADE', 'POST', '/api/v1/order');
|
|
51
|
+
//# sourceMappingURL=sapi.js.map
|
package/lib/sapi.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sapi.js","sourceRoot":"","sources":["../src/sapi.ts"],"names":[],"mappings":";;;AAAA,mCAAgD;AAEhD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,OAAQ,CAAC;AACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAW,CAAC;AAE3C,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,OAAO,GAAG,KAAK,EACnB,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,SAAc,EAAE,EACJ,EAAE;IACd,MAAM,UAAU,GAAG,IAAI,KAAK,MAAM,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,WAAW,CAAC;IAE1D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,GAAG,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;QACjD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;KACvC;IAED,IAAI,QAAQ,EAAE;QACZ,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;QAC7C,MAAM,SAAS,GAAG,MAAM,IAAA,6BAAqB,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAC/D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;KAC9C;IAED,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE7B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;QAC3C,MAAM;QACN,OAAO,EAAE,UAAU;YACjB,CAAC,CAAC;gBACE,cAAc,EAAE,OAAO;aACxB;YACH,CAAC,CAAC,EAAE;KACP,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAS,CAAC,CAAC;IAE9C,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE;QACxC,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;KAChC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,SAAS,GACb,CACE,IAAoE,EACpE,MAAc,EACd,QAAgB,EAChB,EAAE,CACJ,CAAC,MAAY,EAAE,EAAE,CACf,OAAO,CAAO,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD;;;;GAIG;AACU,QAAA,eAAe,GAAG,SAAS,CAetC,WAAW,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;AAEzC;;;GAGG;AACU,QAAA,mBAAmB,GAAG,SAAS,CAO1C,aAAa,EAAE,KAAK,EAAE,sBAAsB,CAAC,CAAC;AAEnC,QAAA,cAAc,GAAG,SAAS,CAWrC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC","sourcesContent":["import { opensslEquivalentHMAC } from './utils';\n\nconst API_KEY = process.env.API_KEY!;\nconst SECRET_KEY = process.env.SECRET_KEY!;\n\nconst BASE_URL = 'https://sapi.asterdex.com';\n\nconst request = async <T>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n params: any = {},\n): Promise<T> => {\n const needApiKey = type !== 'NONE';\n const needSign = type === 'TRADE' || type === 'USER_DATA';\n\n const url = new URL(BASE_URL);\n url.pathname = endpoint;\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n url.searchParams.set(key, `${value}`);\n }\n\n if (needSign) {\n url.searchParams.set('timestamp', `${Date.now()}`);\n const msg = url.search.slice(1); // 去掉开头的 '?'\n const signature = await opensslEquivalentHMAC(msg, SECRET_KEY);\n url.searchParams.set('signature', signature);\n }\n\n console.info(url.toString());\n\n const response = await fetch(url.toString(), {\n method,\n headers: needApiKey\n ? {\n 'X-MBX-APIKEY': API_KEY,\n }\n : {},\n }).then((response) => response.json() as any);\n\n if (response.code && response.code !== 0) {\n throw JSON.stringify(response);\n }\n\n return response;\n};\n\nconst createApi =\n <TReq, TRes>(\n type: 'NONE' | 'TRADE' | 'USER_DATA' | 'USER_STREAM' | 'MARKET_DATA',\n method: string,\n endpoint: string,\n ) =>\n (params: TReq) =>\n request<TRes>(type, method, endpoint, params);\n\n/**\n * 获取账户信息 (现货)\n *\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E8%B4%A6%E6%88%B7%E4%BF%A1%E6%81%AF-user_data\n */\nexport const getApiV1Account = createApi<\n {},\n {\n feeTier: number;\n canTrade: boolean;\n canDeposit: boolean;\n canWithdraw: boolean;\n canBurnAsset: boolean;\n updateTime: number;\n balances: {\n asset: string;\n free: string;\n locked: string;\n }[];\n }\n>('USER_DATA', 'GET', '/api/v1/account');\n\n/**\n * 获取最新价格\n * https://github.com/asterdex/api-docs/blob/master/aster-finance-spot-api_CN.md#%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC\n */\nexport const getApiV1TickerPrice = createApi<\n {},\n {\n symbol: string;\n price: string;\n time: number;\n }[]\n>('MARKET_DATA', 'GET', '/api/v1/ticker/price');\n\nexport const postApiV1Order = createApi<\n {\n symbol: string;\n side: 'BUY' | 'SELL';\n type: 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_MARKET' | 'TAKE_PROFIT' | 'TAKE_PROFIT_MARKET';\n timeInForce?: 'GTC' | 'IOC' | 'FOK' | 'GTX';\n quantity?: number;\n quoteOrderQty?: number;\n price?: number;\n },\n {}\n>('TRADE', 'POST', '/api/v1/order');\n"]}
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAU5D;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB/F"}
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.opensslEquivalentHMAC = exports.arrayBufferToHex = void 0;
|
|
4
|
+
function arrayBufferToHex(buffer) {
|
|
5
|
+
const bytes = new Uint8Array(buffer);
|
|
6
|
+
const hexArray = [];
|
|
7
|
+
for (const byte of bytes) {
|
|
8
|
+
const hex = byte.toString(16).padStart(2, '0');
|
|
9
|
+
hexArray.push(hex);
|
|
10
|
+
}
|
|
11
|
+
return hexArray.join('');
|
|
12
|
+
}
|
|
13
|
+
exports.arrayBufferToHex = arrayBufferToHex;
|
|
14
|
+
async function opensslEquivalentHMAC(message, secretKey) {
|
|
15
|
+
try {
|
|
16
|
+
const keyBuffer = new TextEncoder().encode(secretKey);
|
|
17
|
+
const messageBuffer = new TextEncoder().encode(message);
|
|
18
|
+
// 导入密钥
|
|
19
|
+
const cryptoKey = await crypto.subtle.importKey('raw', keyBuffer, {
|
|
20
|
+
name: 'HMAC',
|
|
21
|
+
hash: { name: 'SHA-256' },
|
|
22
|
+
}, false, ['sign']);
|
|
23
|
+
// 进行 HMAC-SHA256 签名
|
|
24
|
+
const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);
|
|
25
|
+
// 转换为16进制小写(与 openssl 输出格式一致)
|
|
26
|
+
return arrayBufferToHex(signature);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new Error(`HMAC-SHA256 签名失败: ${error}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.opensslEquivalentHMAC = opensslEquivalentHMAC;
|
|
33
|
+
//# sourceMappingURL=utils.js.map
|
package/lib/utils.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,SAAgB,gBAAgB,CAAC,MAAmB;IAClD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/C,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KACpB;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC3B,CAAC;AAVD,4CAUC;AAEM,KAAK,UAAU,qBAAqB,CAAC,OAAe,EAAE,SAAiB;IAC5E,IAAI;QACF,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,aAAa,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAExD,OAAO;QACP,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC7C,KAAK,EACL,SAAS,EACT;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;SAC1B,EACD,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;QAEF,oBAAoB;QACpB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QAE7E,8BAA8B;QAC9B,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC;KACpC;IAAC,OAAO,KAAK,EAAE;QACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC,CAAC;KAC/C;AACH,CAAC;AAzBD,sDAyBC","sourcesContent":["export function arrayBufferToHex(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n const hexArray: string[] = [];\n\n for (const byte of bytes) {\n const hex = byte.toString(16).padStart(2, '0');\n hexArray.push(hex);\n }\n\n return hexArray.join('');\n}\n\nexport async function opensslEquivalentHMAC(message: string, secretKey: string): Promise<string> {\n try {\n const keyBuffer = new TextEncoder().encode(secretKey);\n const messageBuffer = new TextEncoder().encode(message);\n\n // 导入密钥\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n keyBuffer,\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n },\n false,\n ['sign'],\n );\n\n // 进行 HMAC-SHA256 签名\n const signature = await crypto.subtle.sign('HMAC', cryptoKey, messageBuffer);\n\n // 转换为16进制小写(与 openssl 输出格式一致)\n return arrayBufferToHex(signature);\n } catch (error) {\n throw new Error(`HMAC-SHA256 签名失败: ${error}`);\n }\n}\n"]}
|
package/package.json
CHANGED
package/temp/package-deps.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"apps/vendor-aster/CHANGELOG.json": "
|
|
3
|
-
"apps/vendor-aster/CHANGELOG.md": "
|
|
2
|
+
"apps/vendor-aster/CHANGELOG.json": "d463c381a05bd1279d5aaa731a72ce713f1417ee",
|
|
3
|
+
"apps/vendor-aster/CHANGELOG.md": "3b786e2098915360ecedf4b5e06c20245c55060f",
|
|
4
4
|
"apps/vendor-aster/config/jest.config.json": "4bb17bde3ee911163a3edb36a6eb71491d80b1bd",
|
|
5
5
|
"apps/vendor-aster/config/rig.json": "f6c7b5537dc77a3170ba9f008bae3b6c3ee11956",
|
|
6
6
|
"apps/vendor-aster/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348",
|
|
7
|
-
"apps/vendor-aster/package.json": "
|
|
7
|
+
"apps/vendor-aster/package.json": "ad4f8a29e19d032a8da796fef6b545b3253e75a6",
|
|
8
|
+
"apps/vendor-aster/src/account-spot.ts": "f2d77bba520a13b124968aa1169bda0c58a06b18",
|
|
8
9
|
"apps/vendor-aster/src/account.ts": "dfef4baeb4a7f28726cd789e5a2e481e97d3a905",
|
|
9
|
-
"apps/vendor-aster/src/api.ts": "
|
|
10
|
+
"apps/vendor-aster/src/api.ts": "92a96387653a97e2643870e9ec6fb53938361b34",
|
|
10
11
|
"apps/vendor-aster/src/cli.ts": "9bf6b5559a6c6f33da20e74cc6c5d702c60ec891",
|
|
11
|
-
"apps/vendor-aster/src/index.ts": "
|
|
12
|
+
"apps/vendor-aster/src/index.ts": "bde17011380b14dbeaf830d4caaa081db7c65a8b",
|
|
13
|
+
"apps/vendor-aster/src/order-spot.ts": "933ff4138488894c31b052e2cef922dde022f6f7",
|
|
12
14
|
"apps/vendor-aster/src/order.ts": "546900e63e6e1c72d01086ea5eecc3c418ba7b67",
|
|
15
|
+
"apps/vendor-aster/src/sapi.ts": "40214de87500f9fb6647c11d3999e0e449a6fa0b",
|
|
16
|
+
"apps/vendor-aster/src/utils.ts": "f732922ea47951ddf0889bba9a70fd8e270cae2f",
|
|
13
17
|
"apps/vendor-aster/tsconfig.json": "81da8f78196974b5d15da0edb6b2d9f48641063c",
|
|
14
18
|
"apps/vendor-aster/.rush/temp/shrinkwrap-deps.json": "1f86f5175c8714c481b272c219e0d95bc6e72755",
|
|
15
19
|
"libraries/protocol/temp/package-deps.json": "3b8c7d38aeee5cc1b2858530c0bff8904d8326e4",
|