@yuants/vendor-huobi 0.13.0 → 0.14.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-actions-with-credentials.js +30 -0
- package/dist/account-actions-with-credentials.js.map +1 -0
- package/dist/account-info.js +13 -173
- package/dist/account-info.js.map +1 -1
- package/dist/accounts/list.js +8 -0
- package/dist/accounts/list.js.map +1 -0
- package/dist/accounts/spot.js +20 -0
- package/dist/accounts/spot.js.map +1 -0
- package/dist/accounts/super-margin.js +74 -0
- package/dist/accounts/super-margin.js.map +1 -0
- package/dist/accounts/swap.js +87 -0
- package/dist/accounts/swap.js.map +1 -0
- package/dist/api/private-api.js +0 -8
- package/dist/api/private-api.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/orders/submitOrder.js +3 -2
- package/dist/orders/submitOrder.js.map +1 -1
- package/dist/uid.js +0 -1
- package/dist/uid.js.map +1 -1
- package/lib/account-actions-with-credentials.d.ts +2 -0
- package/lib/account-actions-with-credentials.d.ts.map +1 -0
- package/lib/account-actions-with-credentials.js +32 -0
- package/lib/account-actions-with-credentials.js.map +1 -0
- package/lib/account-info.d.ts +1 -1
- package/lib/account-info.d.ts.map +1 -1
- package/lib/account-info.js +11 -171
- package/lib/account-info.js.map +1 -1
- package/lib/accounts/list.d.ts +4 -0
- package/lib/accounts/list.d.ts.map +1 -0
- package/lib/accounts/list.js +12 -0
- package/lib/accounts/list.js.map +1 -0
- package/lib/accounts/spot.d.ts +4 -0
- package/lib/accounts/spot.d.ts.map +1 -0
- package/lib/accounts/spot.js +24 -0
- package/lib/accounts/spot.js.map +1 -0
- package/lib/accounts/super-margin.d.ts +7 -0
- package/lib/accounts/super-margin.d.ts.map +1 -0
- package/lib/accounts/super-margin.js +78 -0
- package/lib/accounts/super-margin.js.map +1 -0
- package/lib/accounts/swap.d.ts +4 -0
- package/lib/accounts/swap.d.ts.map +1 -0
- package/lib/accounts/swap.js +91 -0
- package/lib/accounts/swap.js.map +1 -0
- package/lib/api/private-api.d.ts +0 -13
- package/lib/api/private-api.d.ts.map +1 -1
- package/lib/api/private-api.js +1 -10
- package/lib/api/private-api.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/orders/submitOrder.d.ts.map +1 -1
- package/lib/orders/submitOrder.js +2 -1
- package/lib/orders/submitOrder.js.map +1 -1
- package/lib/uid.d.ts +0 -1
- package/lib/uid.d.ts.map +1 -1
- package/lib/uid.js +0 -1
- package/lib/uid.js.map +1 -1
- package/package.json +3 -3
- package/temp/package-deps.json +15 -10
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { provideAccountActionsWithCredential } from '@yuants/data-account';
|
|
2
|
+
import { Terminal } from '@yuants/protocol';
|
|
3
|
+
import { listAccounts } from './accounts/list';
|
|
4
|
+
import { getSpotAccountInfo } from './accounts/spot';
|
|
5
|
+
import { getSuperMarginAccountInfo } from './accounts/super-margin';
|
|
6
|
+
import { getSwapAccountInfo } from './accounts/swap';
|
|
7
|
+
import { getAccountIds } from './uid';
|
|
8
|
+
provideAccountActionsWithCredential(Terminal.fromNodeEnv(), 'HTX', {
|
|
9
|
+
type: 'object',
|
|
10
|
+
required: ['access_key', 'secret_key'],
|
|
11
|
+
properties: {
|
|
12
|
+
access_key: { type: 'string' },
|
|
13
|
+
secret_key: { type: 'string' },
|
|
14
|
+
},
|
|
15
|
+
}, {
|
|
16
|
+
listAccounts: listAccounts,
|
|
17
|
+
getAccountInfo: async (credential, account_id) => {
|
|
18
|
+
const accounts = await getAccountIds(JSON.stringify(credential));
|
|
19
|
+
switch (account_id) {
|
|
20
|
+
case accounts.spot:
|
|
21
|
+
return getSpotAccountInfo(credential, account_id);
|
|
22
|
+
case accounts.superMargin:
|
|
23
|
+
return getSuperMarginAccountInfo(credential, account_id);
|
|
24
|
+
case accounts.swap:
|
|
25
|
+
return getSwapAccountInfo(credential, account_id);
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Unsupported account_id: ${account_id}`);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=account-actions-with-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account-actions-with-credentials.js","sourceRoot":"","sources":["../src/account-actions-with-credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,mCAAmC,CACjC,QAAQ,CAAC,WAAW,EAAE,EACtB,KAAK,EACL;IACE,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;IACtC,UAAU,EAAE;QACV,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC9B,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC/B;CACF,EACD;IACE,YAAY,EAAE,YAAY;IAC1B,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE;QAC/C,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;QACjE,QAAQ,UAAU,EAAE;YAClB,KAAK,QAAQ,CAAC,IAAI;gBAChB,OAAO,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACpD,KAAK,QAAQ,CAAC,WAAW;gBACvB,OAAO,yBAAyB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YAC3D,KAAK,QAAQ,CAAC,IAAI;gBAChB,OAAO,kBAAkB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;SACrD;QACD,MAAM,IAAI,KAAK,CAAC,2BAA2B,UAAU,EAAE,CAAC,CAAC;IAC3D,CAAC;CACF,CACF,CAAC","sourcesContent":["import { provideAccountActionsWithCredential } from '@yuants/data-account';\nimport { Terminal } from '@yuants/protocol';\nimport { listAccounts } from './accounts/list';\nimport { getSpotAccountInfo } from './accounts/spot';\nimport { getSuperMarginAccountInfo } from './accounts/super-margin';\nimport { getSwapAccountInfo } from './accounts/swap';\nimport { getAccountIds } from './uid';\n\nprovideAccountActionsWithCredential(\n Terminal.fromNodeEnv(),\n 'HTX',\n {\n type: 'object',\n required: ['access_key', 'secret_key'],\n properties: {\n access_key: { type: 'string' },\n secret_key: { type: 'string' },\n },\n },\n {\n listAccounts: listAccounts,\n getAccountInfo: async (credential, account_id) => {\n const accounts = await getAccountIds(JSON.stringify(credential));\n switch (account_id) {\n case accounts.spot:\n return getSpotAccountInfo(credential, account_id);\n case accounts.superMargin:\n return getSuperMarginAccountInfo(credential, account_id);\n case accounts.swap:\n return getSwapAccountInfo(credential, account_id);\n }\n throw new Error(`Unsupported account_id: ${account_id}`);\n },\n },\n);\n"]}
|
package/dist/account-info.js
CHANGED
|
@@ -1,96 +1,18 @@
|
|
|
1
1
|
import { provideAccountInfoService } from '@yuants/data-account';
|
|
2
2
|
import { formatTime } from '@yuants/utils';
|
|
3
|
-
import { defer, distinct, filter,
|
|
3
|
+
import { defer, distinct, filter, from, map, mergeMap, repeat, retry, shareReplay, tap, toArray } from 'rxjs';
|
|
4
|
+
import { getSpotAccountInfo } from './accounts/spot';
|
|
5
|
+
import { getSuperMarginAccountInfo } from './accounts/super-margin';
|
|
6
|
+
import { getSwapAccountInfo } from './accounts/swap';
|
|
4
7
|
import { client } from './api';
|
|
5
|
-
import { getSpotAccountBalance
|
|
6
|
-
import { swapProductService } from './product';
|
|
8
|
+
import { getSpotAccountBalance } from './api/private-api';
|
|
7
9
|
/**
|
|
8
10
|
* 提供 SWAP 账户信息服务
|
|
9
11
|
*/
|
|
10
12
|
export const provideSwapAccountInfoService = (terminal, accountId, credential) => {
|
|
11
|
-
provideAccountInfoService(terminal, accountId, async () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (!balance.data) {
|
|
15
|
-
throw new Error('Failed to get unified account info');
|
|
16
|
-
}
|
|
17
|
-
const balanceData = balance.data.find((v) => v.margin_asset === 'USDT');
|
|
18
|
-
if (!balanceData) {
|
|
19
|
-
throw new Error('No USDT balance found in unified account');
|
|
20
|
-
}
|
|
21
|
-
const equity = balanceData.margin_balance;
|
|
22
|
-
const free = balanceData.withdraw_available;
|
|
23
|
-
// positions
|
|
24
|
-
const positionsRes = await getSwapCrossPositionInfo(credential);
|
|
25
|
-
const mapProductIdToPerpetualProduct = await firstValueFrom(swapProductService.mapProductIdToProduct$);
|
|
26
|
-
const positions = (positionsRes.data || []).map((v) => {
|
|
27
|
-
const product_id = v.contract_code;
|
|
28
|
-
const theProduct = mapProductIdToPerpetualProduct === null || mapProductIdToPerpetualProduct === void 0 ? void 0 : mapProductIdToPerpetualProduct.get(product_id);
|
|
29
|
-
const valuation = v.volume * v.last_price * ((theProduct === null || theProduct === void 0 ? void 0 : theProduct.value_scale) || 1);
|
|
30
|
-
return {
|
|
31
|
-
position_id: `${v.contract_code}/${v.contract_type}/${v.direction}/${v.margin_mode}`,
|
|
32
|
-
datasource_id: 'HUOBI-SWAP',
|
|
33
|
-
product_id,
|
|
34
|
-
direction: v.direction === 'buy' ? 'LONG' : 'SHORT',
|
|
35
|
-
volume: v.volume,
|
|
36
|
-
free_volume: v.available,
|
|
37
|
-
position_price: v.cost_hold,
|
|
38
|
-
closable_price: v.last_price,
|
|
39
|
-
floating_profit: v.profit_unreal,
|
|
40
|
-
valuation,
|
|
41
|
-
};
|
|
42
|
-
});
|
|
43
|
-
// orders
|
|
44
|
-
// const orders: IOrder[] = [];
|
|
45
|
-
// let page_index = 1;
|
|
46
|
-
// const page_size = 50;
|
|
47
|
-
// while (true) {
|
|
48
|
-
// const ordersRes = await client.getSwapOpenOrders({ page_index, page_size });
|
|
49
|
-
// if (!ordersRes.data?.orders || ordersRes.data.orders.length === 0) {
|
|
50
|
-
// break;
|
|
51
|
-
// }
|
|
52
|
-
// const pageOrders: IOrder[] = ordersRes.data.orders.map((v): IOrder => {
|
|
53
|
-
// return {
|
|
54
|
-
// order_id: v.order_id_str,
|
|
55
|
-
// account_id: SWAP_ACCOUNT_ID,
|
|
56
|
-
// product_id: v.contract_code,
|
|
57
|
-
// order_type: ['lightning'].includes(v.order_price_type)
|
|
58
|
-
// ? 'MARKET'
|
|
59
|
-
// : ['limit', 'opponent', 'post_only', 'optimal_5', 'optimal_10', 'optimal_20'].includes(
|
|
60
|
-
// v.order_price_type,
|
|
61
|
-
// )
|
|
62
|
-
// ? 'LIMIT'
|
|
63
|
-
// : ['fok'].includes(v.order_price_type)
|
|
64
|
-
// ? 'FOK'
|
|
65
|
-
// : v.order_price_type.includes('ioc')
|
|
66
|
-
// ? 'IOC'
|
|
67
|
-
// : 'STOP', // unreachable code
|
|
68
|
-
// order_direction:
|
|
69
|
-
// v.direction === 'open'
|
|
70
|
-
// ? v.offset === 'buy'
|
|
71
|
-
// ? 'OPEN_LONG'
|
|
72
|
-
// : 'OPEN_SHORT'
|
|
73
|
-
// : v.offset === 'buy'
|
|
74
|
-
// ? 'CLOSE_SHORT'
|
|
75
|
-
// : 'CLOSE_LONG',
|
|
76
|
-
// volume: v.volume,
|
|
77
|
-
// submit_at: v.created_at,
|
|
78
|
-
// price: v.price,
|
|
79
|
-
// traded_volume: v.trade_volume,
|
|
80
|
-
// };
|
|
81
|
-
// });
|
|
82
|
-
// orders.push(...pageOrders);
|
|
83
|
-
// page_index++;
|
|
84
|
-
// }
|
|
85
|
-
return {
|
|
86
|
-
money: {
|
|
87
|
-
currency: 'USDT',
|
|
88
|
-
equity,
|
|
89
|
-
free,
|
|
90
|
-
},
|
|
91
|
-
positions,
|
|
92
|
-
};
|
|
93
|
-
}, { auto_refresh_interval: 1000 });
|
|
13
|
+
provideAccountInfoService(terminal, accountId, async () => getSwapAccountInfo(credential, accountId), {
|
|
14
|
+
auto_refresh_interval: 1000,
|
|
15
|
+
});
|
|
94
16
|
};
|
|
95
17
|
/**
|
|
96
18
|
* 获取超级保证金账户余额流
|
|
@@ -139,97 +61,15 @@ export const setupSuperMarginWebSocketSubscriptions = (superMarginAccountBalance
|
|
|
139
61
|
export const provideSuperMarginAccountInfoService = (terminal, accountId, credential, superMarginAccountUid, subscriptions) => {
|
|
140
62
|
const superMarginAccountBalance$ = getSuperMarginAccountBalance$(credential, superMarginAccountUid);
|
|
141
63
|
setupSuperMarginWebSocketSubscriptions(superMarginAccountBalance$, subscriptions);
|
|
142
|
-
provideAccountInfoService(terminal, accountId, async () => {
|
|
143
|
-
var _a;
|
|
144
|
-
// get account balance
|
|
145
|
-
const accountBalance = await getSpotAccountBalance(credential, superMarginAccountUid);
|
|
146
|
-
const balanceList = ((_a = accountBalance.data) === null || _a === void 0 ? void 0 : _a.list) || [];
|
|
147
|
-
// calculate usdt balance
|
|
148
|
-
const usdtBalance = balanceList
|
|
149
|
-
.filter((v) => v.currency === 'usdt')
|
|
150
|
-
.reduce((acc, cur) => acc + +cur.balance, 0);
|
|
151
|
-
// get positions (non-usdt currencies)
|
|
152
|
-
const positions = [];
|
|
153
|
-
const nonUsdtCurrencies = balanceList
|
|
154
|
-
.filter((v) => v.currency !== 'usdt')
|
|
155
|
-
.reduce((acc, cur) => {
|
|
156
|
-
const existing = acc.find((item) => item.currency === cur.currency);
|
|
157
|
-
if (existing) {
|
|
158
|
-
existing.balance += +cur.balance;
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
acc.push({ currency: cur.currency, balance: +cur.balance });
|
|
162
|
-
}
|
|
163
|
-
return acc;
|
|
164
|
-
}, []);
|
|
165
|
-
// get prices and create positions
|
|
166
|
-
for (const currencyData of nonUsdtCurrencies) {
|
|
167
|
-
if (currencyData.balance > 0) {
|
|
168
|
-
try {
|
|
169
|
-
// get current price from websocket or fallback to REST API
|
|
170
|
-
let price;
|
|
171
|
-
try {
|
|
172
|
-
const tickPrice = await firstValueFrom(client.spot_ws.input$.pipe(
|
|
173
|
-
//
|
|
174
|
-
first((v) => { var _a, _b; return ((_a = v.ch) === null || _a === void 0 ? void 0 : _a.includes('ticker')) && ((_b = v.ch) === null || _b === void 0 ? void 0 : _b.includes(currencyData.currency)) && v.tick; }), map((v) => v.tick.bid), timeout(5000), tap({
|
|
175
|
-
error: (e) => {
|
|
176
|
-
subscriptions.clear();
|
|
177
|
-
},
|
|
178
|
-
})));
|
|
179
|
-
price = tickPrice;
|
|
180
|
-
}
|
|
181
|
-
catch (_b) {
|
|
182
|
-
// fallback to REST API
|
|
183
|
-
const tickerRes = await getSpotTick(credential, { symbol: `${currencyData.currency}usdt` });
|
|
184
|
-
price = tickerRes.tick.close;
|
|
185
|
-
}
|
|
186
|
-
positions.push({
|
|
187
|
-
position_id: `${currencyData.currency}/usdt/spot`,
|
|
188
|
-
product_id: `${currencyData.currency}usdt`,
|
|
189
|
-
direction: 'LONG',
|
|
190
|
-
volume: currencyData.balance,
|
|
191
|
-
free_volume: currencyData.balance,
|
|
192
|
-
position_price: price,
|
|
193
|
-
closable_price: price,
|
|
194
|
-
floating_profit: 0,
|
|
195
|
-
valuation: currencyData.balance * price,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
console.warn(formatTime(Date.now()), `Failed to get price for ${currencyData.currency}:`, error);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
// calculate equity
|
|
204
|
-
const equity = positions.reduce((acc, cur) => acc + cur.closable_price * cur.volume, 0) + usdtBalance;
|
|
205
|
-
return {
|
|
206
|
-
money: {
|
|
207
|
-
currency: 'USDT',
|
|
208
|
-
equity,
|
|
209
|
-
free: equity,
|
|
210
|
-
},
|
|
211
|
-
positions,
|
|
212
|
-
};
|
|
213
|
-
}, { auto_refresh_interval: 1000 });
|
|
64
|
+
provideAccountInfoService(terminal, accountId, async () => getSuperMarginAccountInfo(credential, accountId), { auto_refresh_interval: 1000 });
|
|
214
65
|
return superMarginAccountBalance$;
|
|
215
66
|
};
|
|
216
67
|
/**
|
|
217
68
|
* 提供 SPOT 账户信息服务
|
|
218
69
|
*/
|
|
219
|
-
export const provideSpotAccountInfoService = (terminal, accountId, credential
|
|
220
|
-
provideAccountInfoService(terminal, accountId, async () => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const equity = +((_b = (_a = spotBalance.data.list.find((v) => v.currency === 'usdt')) === null || _a === void 0 ? void 0 : _a.balance) !== null && _b !== void 0 ? _b : 0);
|
|
224
|
-
const free = equity;
|
|
225
|
-
return {
|
|
226
|
-
money: {
|
|
227
|
-
currency: 'USDT',
|
|
228
|
-
equity,
|
|
229
|
-
free,
|
|
230
|
-
},
|
|
231
|
-
positions: [],
|
|
232
|
-
};
|
|
233
|
-
}, { auto_refresh_interval: 1000 });
|
|
70
|
+
export const provideSpotAccountInfoService = (terminal, accountId, credential) => {
|
|
71
|
+
provideAccountInfoService(terminal, accountId, async () => getSpotAccountInfo(credential, accountId), {
|
|
72
|
+
auto_refresh_interval: 1000,
|
|
73
|
+
});
|
|
234
74
|
};
|
|
235
75
|
//# sourceMappingURL=account-info.js.map
|
package/dist/account-info.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-info.js","sourceRoot":"","sources":["../src/account-info.ts"],"names":[],"mappings":"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","sourcesContent":["import { IPosition, provideAccountInfoService } from '@yuants/data-account';\nimport { Terminal } from '@yuants/protocol';\nimport { formatTime } from '@yuants/utils';\nimport {\n defer,\n distinct,\n filter,\n first,\n firstValueFrom,\n from,\n map,\n mergeMap,\n repeat,\n retry,\n shareReplay,\n tap,\n timeout,\n toArray,\n} from 'rxjs';\nimport { client } from './api';\nimport {\n getSpotAccountBalance,\n getSpotTick,\n getSwapCrossPositionInfo,\n getUnifiedAccountInfo,\n ICredential,\n} from './api/private-api';\nimport { swapProductService } from './product';\n\n/**\n * 提供 SWAP 账户信息服务\n */\nexport const provideSwapAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n) => {\n provideAccountInfoService(\n terminal,\n accountId,\n async () => {\n // balance\n const balance = await getUnifiedAccountInfo(credential);\n if (!balance.data) {\n throw new Error('Failed to get unified account info');\n }\n const balanceData = balance.data.find((v) => v.margin_asset === 'USDT');\n if (!balanceData) {\n throw new Error('No USDT balance found in unified account');\n }\n const equity = balanceData.margin_balance;\n const free = balanceData.withdraw_available;\n\n // positions\n const positionsRes = await getSwapCrossPositionInfo(credential);\n const mapProductIdToPerpetualProduct = await firstValueFrom(swapProductService.mapProductIdToProduct$);\n const positions: IPosition[] = (positionsRes.data || []).map((v): IPosition => {\n const product_id = v.contract_code;\n const theProduct = mapProductIdToPerpetualProduct?.get(product_id);\n const valuation = v.volume * v.last_price * (theProduct?.value_scale || 1);\n return {\n position_id: `${v.contract_code}/${v.contract_type}/${v.direction}/${v.margin_mode}`,\n datasource_id: 'HUOBI-SWAP',\n product_id,\n direction: v.direction === 'buy' ? 'LONG' : 'SHORT',\n volume: v.volume,\n free_volume: v.available,\n position_price: v.cost_hold,\n closable_price: v.last_price,\n floating_profit: v.profit_unreal,\n valuation,\n };\n });\n\n // orders\n // const orders: IOrder[] = [];\n // let page_index = 1;\n // const page_size = 50;\n\n // while (true) {\n // const ordersRes = await client.getSwapOpenOrders({ page_index, page_size });\n // if (!ordersRes.data?.orders || ordersRes.data.orders.length === 0) {\n // break;\n // }\n\n // const pageOrders: IOrder[] = ordersRes.data.orders.map((v): IOrder => {\n // return {\n // order_id: v.order_id_str,\n // account_id: SWAP_ACCOUNT_ID,\n // product_id: v.contract_code,\n // order_type: ['lightning'].includes(v.order_price_type)\n // ? 'MARKET'\n // : ['limit', 'opponent', 'post_only', 'optimal_5', 'optimal_10', 'optimal_20'].includes(\n // v.order_price_type,\n // )\n // ? 'LIMIT'\n // : ['fok'].includes(v.order_price_type)\n // ? 'FOK'\n // : v.order_price_type.includes('ioc')\n // ? 'IOC'\n // : 'STOP', // unreachable code\n // order_direction:\n // v.direction === 'open'\n // ? v.offset === 'buy'\n // ? 'OPEN_LONG'\n // : 'OPEN_SHORT'\n // : v.offset === 'buy'\n // ? 'CLOSE_SHORT'\n // : 'CLOSE_LONG',\n // volume: v.volume,\n // submit_at: v.created_at,\n // price: v.price,\n // traded_volume: v.trade_volume,\n // };\n // });\n\n // orders.push(...pageOrders);\n // page_index++;\n // }\n\n return {\n money: {\n currency: 'USDT',\n equity,\n free,\n },\n positions,\n };\n },\n { auto_refresh_interval: 1000 },\n );\n};\n\n/**\n * 获取超级保证金账户余额流\n */\nexport const getSuperMarginAccountBalance$ = (credential: ICredential, superMarginAccountUid: number) => {\n return defer(() => getSpotAccountBalance(credential, superMarginAccountUid)).pipe(\n //\n map((res) => res.data),\n repeat({ delay: 1000 }),\n tap({\n error: (e) => {\n console.error(formatTime(Date.now()), 'unifiedRaw', e);\n },\n }),\n retry({ delay: 5000 }),\n shareReplay(1),\n );\n};\n\n/**\n * 设置超级保证金账户的 WebSocket 订阅\n */\nexport const setupSuperMarginWebSocketSubscriptions = (\n superMarginAccountBalance$: ReturnType<typeof getSuperMarginAccountBalance$>,\n subscriptions: Set<string>,\n) => {\n from(client.spot_ws.connection$).subscribe(() => {\n subscriptions.clear();\n });\n // subscribe the symbols of positions we held\n superMarginAccountBalance$\n .pipe(\n //\n mergeMap((res) =>\n from(res?.list || []).pipe(\n filter((v) => v.currency !== 'usdt'),\n map((v) => v.currency),\n distinct(),\n toArray(),\n map((v) => new Set(v)),\n ),\n ),\n )\n .subscribe((v: Set<string>) => {\n const toUnsubscribe = [...subscriptions].filter((x) => !v.has(x));\n const toSubscribe = [...v].filter((x) => !subscriptions.has(x));\n\n for (const symbol of toUnsubscribe) {\n client.spot_ws.output$.next({\n unsub: `market.${symbol}usdt.ticker`,\n });\n subscriptions.delete(symbol);\n }\n for (const symbol of toSubscribe) {\n client.spot_ws.output$.next({\n sub: `market.${symbol}usdt.ticker`,\n });\n subscriptions.add(symbol);\n }\n });\n};\n\n/**\n * 提供超级保证金账户信息服务\n */\nexport const provideSuperMarginAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n superMarginAccountUid: number,\n subscriptions: Set<string>,\n) => {\n const superMarginAccountBalance$ = getSuperMarginAccountBalance$(credential, superMarginAccountUid);\n setupSuperMarginWebSocketSubscriptions(superMarginAccountBalance$, subscriptions);\n\n provideAccountInfoService(\n terminal,\n accountId,\n async () => {\n // get account balance\n const accountBalance = await getSpotAccountBalance(credential, superMarginAccountUid);\n const balanceList = accountBalance.data?.list || [];\n\n // calculate usdt balance\n const usdtBalance = balanceList\n .filter((v) => v.currency === 'usdt')\n .reduce((acc, cur) => acc + +cur.balance, 0);\n\n // get positions (non-usdt currencies)\n const positions: IPosition[] = [];\n const nonUsdtCurrencies = balanceList\n .filter((v) => v.currency !== 'usdt')\n .reduce((acc, cur) => {\n const existing = acc.find((item) => item.currency === cur.currency);\n if (existing) {\n existing.balance += +cur.balance;\n } else {\n acc.push({ currency: cur.currency, balance: +cur.balance });\n }\n return acc;\n }, [] as { currency: string; balance: number }[]);\n\n // get prices and create positions\n for (const currencyData of nonUsdtCurrencies) {\n if (currencyData.balance > 0) {\n try {\n // get current price from websocket or fallback to REST API\n let price: number;\n try {\n const tickPrice = await firstValueFrom(\n client.spot_ws.input$.pipe(\n //\n first((v) => v.ch?.includes('ticker') && v.ch?.includes(currencyData.currency) && v.tick),\n map((v): number => v.tick.bid),\n timeout(5000),\n tap({\n error: (e) => {\n subscriptions.clear();\n },\n }),\n ),\n );\n price = tickPrice;\n } catch {\n // fallback to REST API\n const tickerRes = await getSpotTick(credential, { symbol: `${currencyData.currency}usdt` });\n price = tickerRes.tick.close;\n }\n\n positions.push({\n position_id: `${currencyData.currency}/usdt/spot`,\n product_id: `${currencyData.currency}usdt`,\n direction: 'LONG',\n volume: currencyData.balance,\n free_volume: currencyData.balance,\n position_price: price,\n closable_price: price,\n floating_profit: 0,\n valuation: currencyData.balance * price,\n });\n } catch (error) {\n console.warn(formatTime(Date.now()), `Failed to get price for ${currencyData.currency}:`, error);\n }\n }\n }\n\n // calculate equity\n const equity = positions.reduce((acc, cur) => acc + cur.closable_price * cur.volume, 0) + usdtBalance;\n\n return {\n money: {\n currency: 'USDT',\n equity,\n free: equity,\n },\n positions,\n };\n },\n { auto_refresh_interval: 1000 },\n );\n\n return superMarginAccountBalance$;\n};\n\n/**\n * 提供 SPOT 账户信息服务\n */\nexport const provideSpotAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n spotAccountUid: number,\n) => {\n provideAccountInfoService(\n terminal,\n accountId,\n async () => {\n const spotBalance = await getSpotAccountBalance(credential, spotAccountUid);\n\n const equity = +(spotBalance.data.list.find((v) => v.currency === 'usdt')?.balance ?? 0);\n const free = equity;\n return {\n money: {\n currency: 'USDT',\n equity,\n free,\n },\n positions: [],\n };\n },\n { auto_refresh_interval: 1000 },\n );\n};\n"]}
|
|
1
|
+
{"version":3,"file":"account-info.js","sourceRoot":"","sources":["../src/account-info.ts"],"names":[],"mappings":"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","sourcesContent":["import { provideAccountInfoService } from '@yuants/data-account';\nimport { Terminal } from '@yuants/protocol';\nimport { formatTime } from '@yuants/utils';\nimport { defer, distinct, filter, from, map, mergeMap, repeat, retry, shareReplay, tap, toArray } from 'rxjs';\nimport { getSpotAccountInfo } from './accounts/spot';\nimport { getSuperMarginAccountInfo } from './accounts/super-margin';\nimport { getSwapAccountInfo } from './accounts/swap';\nimport { client } from './api';\nimport { getSpotAccountBalance, ICredential } from './api/private-api';\n\n/**\n * 提供 SWAP 账户信息服务\n */\nexport const provideSwapAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n) => {\n provideAccountInfoService(terminal, accountId, async () => getSwapAccountInfo(credential, accountId), {\n auto_refresh_interval: 1000,\n });\n};\n\n/**\n * 获取超级保证金账户余额流\n */\nexport const getSuperMarginAccountBalance$ = (credential: ICredential, superMarginAccountUid: number) => {\n return defer(() => getSpotAccountBalance(credential, superMarginAccountUid)).pipe(\n //\n map((res) => res.data),\n repeat({ delay: 1000 }),\n tap({\n error: (e) => {\n console.error(formatTime(Date.now()), 'unifiedRaw', e);\n },\n }),\n retry({ delay: 5000 }),\n shareReplay(1),\n );\n};\n\n/**\n * 设置超级保证金账户的 WebSocket 订阅\n */\nexport const setupSuperMarginWebSocketSubscriptions = (\n superMarginAccountBalance$: ReturnType<typeof getSuperMarginAccountBalance$>,\n subscriptions: Set<string>,\n) => {\n from(client.spot_ws.connection$).subscribe(() => {\n subscriptions.clear();\n });\n // subscribe the symbols of positions we held\n superMarginAccountBalance$\n .pipe(\n //\n mergeMap((res) =>\n from(res?.list || []).pipe(\n filter((v) => v.currency !== 'usdt'),\n map((v) => v.currency),\n distinct(),\n toArray(),\n map((v) => new Set(v)),\n ),\n ),\n )\n .subscribe((v: Set<string>) => {\n const toUnsubscribe = [...subscriptions].filter((x) => !v.has(x));\n const toSubscribe = [...v].filter((x) => !subscriptions.has(x));\n\n for (const symbol of toUnsubscribe) {\n client.spot_ws.output$.next({\n unsub: `market.${symbol}usdt.ticker`,\n });\n subscriptions.delete(symbol);\n }\n for (const symbol of toSubscribe) {\n client.spot_ws.output$.next({\n sub: `market.${symbol}usdt.ticker`,\n });\n subscriptions.add(symbol);\n }\n });\n};\n\n/**\n * 提供超级保证金账户信息服务\n */\nexport const provideSuperMarginAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n superMarginAccountUid: number,\n subscriptions: Set<string>,\n) => {\n const superMarginAccountBalance$ = getSuperMarginAccountBalance$(credential, superMarginAccountUid);\n setupSuperMarginWebSocketSubscriptions(superMarginAccountBalance$, subscriptions);\n\n provideAccountInfoService(\n terminal,\n accountId,\n async () => getSuperMarginAccountInfo(credential, accountId),\n { auto_refresh_interval: 1000 },\n );\n\n return superMarginAccountBalance$;\n};\n\n/**\n * 提供 SPOT 账户信息服务\n */\nexport const provideSpotAccountInfoService = (\n terminal: Terminal,\n accountId: string,\n credential: ICredential,\n) => {\n provideAccountInfoService(terminal, accountId, async () => getSpotAccountInfo(credential, accountId), {\n auto_refresh_interval: 1000,\n });\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { getAccountIds } from '../uid';
|
|
2
|
+
export const listAccounts = async (credential) => {
|
|
3
|
+
const accounts = await getAccountIds(JSON.stringify(credential));
|
|
4
|
+
if (!accounts)
|
|
5
|
+
throw new Error('Failed to get Account IDs');
|
|
6
|
+
return [{ account_id: accounts.spot }, { account_id: accounts.superMargin }, { account_id: accounts.swap }];
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/accounts/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,MAAM,CAAC,MAAM,YAAY,GAA8C,KAAK,EAAE,UAAU,EAAE,EAAE;IAC1F,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAE5D,OAAO,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9G,CAAC,CAAC","sourcesContent":["import { IActionHandlerOfListAccounts } from '@yuants/data-account';\nimport { ICredential } from '../api/private-api';\nimport { getAccountIds } from '../uid';\n\nexport const listAccounts: IActionHandlerOfListAccounts<ICredential> = async (credential) => {\n const accounts = await getAccountIds(JSON.stringify(credential));\n if (!accounts) throw new Error('Failed to get Account IDs');\n\n return [{ account_id: accounts.spot }, { account_id: accounts.superMargin }, { account_id: accounts.swap }];\n};\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getSpotAccountBalance } from '../api/private-api';
|
|
2
|
+
import { spotAccountUidCache } from '../uid';
|
|
3
|
+
export const getSpotAccountInfo = async (credential) => {
|
|
4
|
+
var _a, _b;
|
|
5
|
+
const spotAccountUid = await spotAccountUidCache.query(JSON.stringify(credential));
|
|
6
|
+
if (!spotAccountUid)
|
|
7
|
+
throw new Error('Failed to get Spot Account UID');
|
|
8
|
+
const spotBalance = await getSpotAccountBalance(credential, spotAccountUid);
|
|
9
|
+
const equity = +((_b = (_a = spotBalance.data.list.find((v) => v.currency === 'usdt')) === null || _a === void 0 ? void 0 : _a.balance) !== null && _b !== void 0 ? _b : 0);
|
|
10
|
+
const free = equity;
|
|
11
|
+
return {
|
|
12
|
+
money: {
|
|
13
|
+
currency: 'USDT',
|
|
14
|
+
equity,
|
|
15
|
+
free,
|
|
16
|
+
},
|
|
17
|
+
positions: [],
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=spot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spot.js","sourceRoot":"","sources":["../../src/accounts/spot.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAe,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE7C,MAAM,CAAC,MAAM,kBAAkB,GAAgD,KAAK,EAAE,UAAU,EAAE,EAAE;;IAClG,MAAM,cAAc,GAAG,MAAM,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IACnF,IAAI,CAAC,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACvE,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAE5E,MAAM,MAAM,GAAG,CAAC,CAAC,MAAA,MAAA,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,0CAAE,OAAO,mCAAI,CAAC,CAAC,CAAC;IACzF,MAAM,IAAI,GAAG,MAAM,CAAC;IACpB,OAAO;QACL,KAAK,EAAE;YACL,QAAQ,EAAE,MAAM;YAChB,MAAM;YACN,IAAI;SACL;QACD,SAAS,EAAE,EAAE;KACd,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { IActionHandlerOfGetAccountInfo } from '@yuants/data-account';\nimport { getSpotAccountBalance, ICredential } from '../api/private-api';\nimport { spotAccountUidCache } from '../uid';\n\nexport const getSpotAccountInfo: IActionHandlerOfGetAccountInfo<ICredential> = async (credential) => {\n const spotAccountUid = await spotAccountUidCache.query(JSON.stringify(credential));\n if (!spotAccountUid) throw new Error('Failed to get Spot Account UID');\n const spotBalance = await getSpotAccountBalance(credential, spotAccountUid);\n\n const equity = +(spotBalance.data.list.find((v) => v.currency === 'usdt')?.balance ?? 0);\n const free = equity;\n return {\n money: {\n currency: 'USDT',\n equity,\n free,\n },\n positions: [],\n };\n};\n"]}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { createCache } from '@yuants/cache';
|
|
2
|
+
import { formatTime } from '@yuants/utils';
|
|
3
|
+
import { getSpotAccountBalance } from '../api/private-api';
|
|
4
|
+
import { getSpotTick } from '../api/public-api';
|
|
5
|
+
import { superMarginAccountUidCache } from '../uid';
|
|
6
|
+
const spotTickCache = createCache((currency) => getSpotTick({ symbol: `${currency}usdt` }), {
|
|
7
|
+
expire: 300000,
|
|
8
|
+
swrAfter: 10000, // 10 seconds
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* 全仓杠杆账户 (Super Margin Account)
|
|
12
|
+
*/
|
|
13
|
+
export const getSuperMarginAccountInfo = async (credential) => {
|
|
14
|
+
var _a;
|
|
15
|
+
// get account balance
|
|
16
|
+
const superMarginAccountUid = await superMarginAccountUidCache.query(JSON.stringify(credential));
|
|
17
|
+
if (!superMarginAccountUid)
|
|
18
|
+
throw new Error('Failed to get Super Margin Account UID');
|
|
19
|
+
const accountBalance = await getSpotAccountBalance(credential, superMarginAccountUid);
|
|
20
|
+
const balanceList = ((_a = accountBalance.data) === null || _a === void 0 ? void 0 : _a.list) || [];
|
|
21
|
+
// calculate usdt balance
|
|
22
|
+
const usdtBalance = balanceList
|
|
23
|
+
.filter((v) => v.currency === 'usdt')
|
|
24
|
+
.reduce((acc, cur) => acc + +cur.balance, 0);
|
|
25
|
+
// get positions (non-usdt currencies)
|
|
26
|
+
const positions = [];
|
|
27
|
+
const nonUsdtCurrencies = balanceList
|
|
28
|
+
.filter((v) => v.currency !== 'usdt')
|
|
29
|
+
.reduce((acc, cur) => {
|
|
30
|
+
const existing = acc.find((item) => item.currency === cur.currency);
|
|
31
|
+
if (existing) {
|
|
32
|
+
existing.balance += +cur.balance;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
acc.push({ currency: cur.currency, balance: +cur.balance });
|
|
36
|
+
}
|
|
37
|
+
return acc;
|
|
38
|
+
}, []);
|
|
39
|
+
// get prices and create positions
|
|
40
|
+
for (const currencyData of nonUsdtCurrencies) {
|
|
41
|
+
if (currencyData.balance > 0) {
|
|
42
|
+
const tickerRes = await spotTickCache.query(currencyData.currency);
|
|
43
|
+
const price = (tickerRes === null || tickerRes === void 0 ? void 0 : tickerRes.tick.close) || 0;
|
|
44
|
+
try {
|
|
45
|
+
// get current price from websocket or fallback to REST API
|
|
46
|
+
positions.push({
|
|
47
|
+
position_id: `${currencyData.currency}/usdt/spot`,
|
|
48
|
+
product_id: `${currencyData.currency}usdt`,
|
|
49
|
+
direction: 'LONG',
|
|
50
|
+
volume: currencyData.balance,
|
|
51
|
+
free_volume: currencyData.balance,
|
|
52
|
+
position_price: price,
|
|
53
|
+
closable_price: price,
|
|
54
|
+
floating_profit: 0,
|
|
55
|
+
valuation: currencyData.balance * price,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.warn(formatTime(Date.now()), `Failed to get price for ${currencyData.currency}:`, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// calculate equity
|
|
64
|
+
const equity = positions.reduce((acc, cur) => acc + cur.closable_price * cur.volume, 0) + usdtBalance;
|
|
65
|
+
return {
|
|
66
|
+
money: {
|
|
67
|
+
currency: 'USDT',
|
|
68
|
+
equity,
|
|
69
|
+
free: equity,
|
|
70
|
+
},
|
|
71
|
+
positions,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=super-margin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"super-margin.js","sourceRoot":"","sources":["../../src/accounts/super-margin.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { IActionHandlerOfGetAccountInfo, IPosition } from '@yuants/data-account';\nimport { formatTime } from '@yuants/utils';\nimport { getSpotAccountBalance, ICredential } from '../api/private-api';\nimport { getSpotTick } from '../api/public-api';\nimport { superMarginAccountUidCache } from '../uid';\n\nconst spotTickCache = createCache((currency) => getSpotTick({ symbol: `${currency}usdt` }), {\n expire: 300_000, // 5 minutes\n swrAfter: 10_000, // 10 seconds\n});\n\n/**\n * 全仓杠杆账户 (Super Margin Account)\n */\nexport const getSuperMarginAccountInfo: IActionHandlerOfGetAccountInfo<ICredential> = async (credential) => {\n // get account balance\n const superMarginAccountUid = await superMarginAccountUidCache.query(JSON.stringify(credential));\n if (!superMarginAccountUid) throw new Error('Failed to get Super Margin Account UID');\n const accountBalance = await getSpotAccountBalance(credential, superMarginAccountUid);\n const balanceList = accountBalance.data?.list || [];\n\n // calculate usdt balance\n const usdtBalance = balanceList\n .filter((v) => v.currency === 'usdt')\n .reduce((acc, cur) => acc + +cur.balance, 0);\n\n // get positions (non-usdt currencies)\n const positions: IPosition[] = [];\n const nonUsdtCurrencies = balanceList\n .filter((v) => v.currency !== 'usdt')\n .reduce((acc, cur) => {\n const existing = acc.find((item) => item.currency === cur.currency);\n if (existing) {\n existing.balance += +cur.balance;\n } else {\n acc.push({ currency: cur.currency, balance: +cur.balance });\n }\n return acc;\n }, [] as { currency: string; balance: number }[]);\n\n // get prices and create positions\n for (const currencyData of nonUsdtCurrencies) {\n if (currencyData.balance > 0) {\n const tickerRes = await spotTickCache.query(currencyData.currency);\n const price = tickerRes?.tick.close || 0;\n try {\n // get current price from websocket or fallback to REST API\n\n positions.push({\n position_id: `${currencyData.currency}/usdt/spot`,\n product_id: `${currencyData.currency}usdt`,\n direction: 'LONG',\n volume: currencyData.balance,\n free_volume: currencyData.balance,\n position_price: price,\n closable_price: price,\n floating_profit: 0,\n valuation: currencyData.balance * price,\n });\n } catch (error) {\n console.warn(formatTime(Date.now()), `Failed to get price for ${currencyData.currency}:`, error);\n }\n }\n }\n\n // calculate equity\n const equity = positions.reduce((acc, cur) => acc + cur.closable_price * cur.volume, 0) + usdtBalance;\n\n return {\n money: {\n currency: 'USDT',\n equity,\n free: equity,\n },\n positions,\n };\n};\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { firstValueFrom } from 'rxjs';
|
|
2
|
+
import { getSwapCrossPositionInfo, getUnifiedAccountInfo } from '../api/private-api';
|
|
3
|
+
import { swapProductService } from '../product';
|
|
4
|
+
export const getSwapAccountInfo = async (credential) => {
|
|
5
|
+
// balance
|
|
6
|
+
const balance = await getUnifiedAccountInfo(credential);
|
|
7
|
+
if (!balance.data) {
|
|
8
|
+
throw new Error('Failed to get unified account info');
|
|
9
|
+
}
|
|
10
|
+
const balanceData = balance.data.find((v) => v.margin_asset === 'USDT');
|
|
11
|
+
if (!balanceData) {
|
|
12
|
+
throw new Error('No USDT balance found in unified account');
|
|
13
|
+
}
|
|
14
|
+
const equity = balanceData.margin_balance;
|
|
15
|
+
const free = balanceData.withdraw_available;
|
|
16
|
+
// positions
|
|
17
|
+
const positionsRes = await getSwapCrossPositionInfo(credential);
|
|
18
|
+
const mapProductIdToPerpetualProduct = await firstValueFrom(swapProductService.mapProductIdToProduct$);
|
|
19
|
+
const positions = (positionsRes.data || []).map((v) => {
|
|
20
|
+
const product_id = v.contract_code;
|
|
21
|
+
const theProduct = mapProductIdToPerpetualProduct === null || mapProductIdToPerpetualProduct === void 0 ? void 0 : mapProductIdToPerpetualProduct.get(product_id);
|
|
22
|
+
const valuation = v.volume * v.last_price * ((theProduct === null || theProduct === void 0 ? void 0 : theProduct.value_scale) || 1);
|
|
23
|
+
return {
|
|
24
|
+
position_id: `${v.contract_code}/${v.contract_type}/${v.direction}/${v.margin_mode}`,
|
|
25
|
+
datasource_id: 'HUOBI-SWAP',
|
|
26
|
+
product_id,
|
|
27
|
+
direction: v.direction === 'buy' ? 'LONG' : 'SHORT',
|
|
28
|
+
volume: v.volume,
|
|
29
|
+
free_volume: v.available,
|
|
30
|
+
position_price: v.cost_hold,
|
|
31
|
+
closable_price: v.last_price,
|
|
32
|
+
floating_profit: v.profit_unreal,
|
|
33
|
+
valuation,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
// orders
|
|
37
|
+
// const orders: IOrder[] = [];
|
|
38
|
+
// let page_index = 1;
|
|
39
|
+
// const page_size = 50;
|
|
40
|
+
// while (true) {
|
|
41
|
+
// const ordersRes = await client.getSwapOpenOrders({ page_index, page_size });
|
|
42
|
+
// if (!ordersRes.data?.orders || ordersRes.data.orders.length === 0) {
|
|
43
|
+
// break;
|
|
44
|
+
// }
|
|
45
|
+
// const pageOrders: IOrder[] = ordersRes.data.orders.map((v): IOrder => {
|
|
46
|
+
// return {
|
|
47
|
+
// order_id: v.order_id_str,
|
|
48
|
+
// account_id: SWAP_ACCOUNT_ID,
|
|
49
|
+
// product_id: v.contract_code,
|
|
50
|
+
// order_type: ['lightning'].includes(v.order_price_type)
|
|
51
|
+
// ? 'MARKET'
|
|
52
|
+
// : ['limit', 'opponent', 'post_only', 'optimal_5', 'optimal_10', 'optimal_20'].includes(
|
|
53
|
+
// v.order_price_type,
|
|
54
|
+
// )
|
|
55
|
+
// ? 'LIMIT'
|
|
56
|
+
// : ['fok'].includes(v.order_price_type)
|
|
57
|
+
// ? 'FOK'
|
|
58
|
+
// : v.order_price_type.includes('ioc')
|
|
59
|
+
// ? 'IOC'
|
|
60
|
+
// : 'STOP', // unreachable code
|
|
61
|
+
// order_direction:
|
|
62
|
+
// v.direction === 'open'
|
|
63
|
+
// ? v.offset === 'buy'
|
|
64
|
+
// ? 'OPEN_LONG'
|
|
65
|
+
// : 'OPEN_SHORT'
|
|
66
|
+
// : v.offset === 'buy'
|
|
67
|
+
// ? 'CLOSE_SHORT'
|
|
68
|
+
// : 'CLOSE_LONG',
|
|
69
|
+
// volume: v.volume,
|
|
70
|
+
// submit_at: v.created_at,
|
|
71
|
+
// price: v.price,
|
|
72
|
+
// traded_volume: v.trade_volume,
|
|
73
|
+
// };
|
|
74
|
+
// });
|
|
75
|
+
// orders.push(...pageOrders);
|
|
76
|
+
// page_index++;
|
|
77
|
+
// }
|
|
78
|
+
return {
|
|
79
|
+
money: {
|
|
80
|
+
currency: 'USDT',
|
|
81
|
+
equity,
|
|
82
|
+
free,
|
|
83
|
+
},
|
|
84
|
+
positions,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=swap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swap.js","sourceRoot":"","sources":["../../src/accounts/swap.ts"],"names":[],"mappings":"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","sourcesContent":["import { IActionHandlerOfGetAccountInfo, IPosition } from '@yuants/data-account';\nimport { firstValueFrom } from 'rxjs';\nimport { getSwapCrossPositionInfo, getUnifiedAccountInfo, ICredential } from '../api/private-api';\nimport { swapProductService } from '../product';\n\nexport const getSwapAccountInfo: IActionHandlerOfGetAccountInfo<ICredential> = async (credential) => {\n // balance\n const balance = await getUnifiedAccountInfo(credential);\n if (!balance.data) {\n throw new Error('Failed to get unified account info');\n }\n const balanceData = balance.data.find((v) => v.margin_asset === 'USDT');\n if (!balanceData) {\n throw new Error('No USDT balance found in unified account');\n }\n const equity = balanceData.margin_balance;\n const free = balanceData.withdraw_available;\n\n // positions\n const positionsRes = await getSwapCrossPositionInfo(credential);\n const mapProductIdToPerpetualProduct = await firstValueFrom(swapProductService.mapProductIdToProduct$);\n const positions: IPosition[] = (positionsRes.data || []).map((v): IPosition => {\n const product_id = v.contract_code;\n const theProduct = mapProductIdToPerpetualProduct?.get(product_id);\n const valuation = v.volume * v.last_price * (theProduct?.value_scale || 1);\n return {\n position_id: `${v.contract_code}/${v.contract_type}/${v.direction}/${v.margin_mode}`,\n datasource_id: 'HUOBI-SWAP',\n product_id,\n direction: v.direction === 'buy' ? 'LONG' : 'SHORT',\n volume: v.volume,\n free_volume: v.available,\n position_price: v.cost_hold,\n closable_price: v.last_price,\n floating_profit: v.profit_unreal,\n valuation,\n };\n });\n\n // orders\n // const orders: IOrder[] = [];\n // let page_index = 1;\n // const page_size = 50;\n\n // while (true) {\n // const ordersRes = await client.getSwapOpenOrders({ page_index, page_size });\n // if (!ordersRes.data?.orders || ordersRes.data.orders.length === 0) {\n // break;\n // }\n\n // const pageOrders: IOrder[] = ordersRes.data.orders.map((v): IOrder => {\n // return {\n // order_id: v.order_id_str,\n // account_id: SWAP_ACCOUNT_ID,\n // product_id: v.contract_code,\n // order_type: ['lightning'].includes(v.order_price_type)\n // ? 'MARKET'\n // : ['limit', 'opponent', 'post_only', 'optimal_5', 'optimal_10', 'optimal_20'].includes(\n // v.order_price_type,\n // )\n // ? 'LIMIT'\n // : ['fok'].includes(v.order_price_type)\n // ? 'FOK'\n // : v.order_price_type.includes('ioc')\n // ? 'IOC'\n // : 'STOP', // unreachable code\n // order_direction:\n // v.direction === 'open'\n // ? v.offset === 'buy'\n // ? 'OPEN_LONG'\n // : 'OPEN_SHORT'\n // : v.offset === 'buy'\n // ? 'CLOSE_SHORT'\n // : 'CLOSE_LONG',\n // volume: v.volume,\n // submit_at: v.created_at,\n // price: v.price,\n // traded_volume: v.trade_volume,\n // };\n // });\n\n // orders.push(...pageOrders);\n // page_index++;\n // }\n\n return {\n money: {\n currency: 'USDT',\n equity,\n free,\n },\n positions,\n };\n};\n"]}
|
package/dist/api/private-api.js
CHANGED
|
@@ -89,14 +89,6 @@ export const getSpotAccountBalance = (credential, account_uid) => {
|
|
|
89
89
|
export const getCrossMarginLoanInfo = (credential) => {
|
|
90
90
|
return privateRequest(credential, 'GET', '/v1/cross-margin/loan-info', 'api.huobi.pro');
|
|
91
91
|
};
|
|
92
|
-
/**
|
|
93
|
-
* 获取现货行情
|
|
94
|
-
*
|
|
95
|
-
* https://www.htx.com/zh-cn/opend/newApiPages/?id=7ec3fc25-7773-11ed-9966-0242ac110003
|
|
96
|
-
*/
|
|
97
|
-
export const getSpotTick = (credential, params) => {
|
|
98
|
-
return privateRequest(credential, 'GET', `/market/detail/merged`, 'api.huobi.pro', params);
|
|
99
|
-
};
|
|
100
92
|
/**
|
|
101
93
|
* 现货下单
|
|
102
94
|
*
|