@michaleffffff/mcp-trading-server 2.5.0 → 2.5.1
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/services/tradeService.js +50 -17
- package/dist/utils/units.js +10 -2
- package/package.json +1 -1
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
import { Direction, OrderType, TriggerType } from "@myx-trade/sdk";
|
|
2
|
-
import { parseUnits } from "ethers";
|
|
3
2
|
import { getChainId, getQuoteToken } from "../auth/resolveClient.js";
|
|
4
3
|
import { ensureUnits } from "../utils/units.js";
|
|
5
4
|
import { normalizeAddress } from "../utils/address.js";
|
|
6
5
|
import { normalizeSlippagePct4dp } from "../utils/slippage.js";
|
|
7
|
-
/**
|
|
8
|
-
* 将人类可读数值转为指定精度的整数字串
|
|
9
|
-
* e.g. toUnits("100", 6) => "100000000"
|
|
10
|
-
*/
|
|
11
|
-
export function toUnits(human, decimals) {
|
|
12
|
-
return parseUnits(human, decimals).toString();
|
|
13
|
-
}
|
|
14
6
|
function resolveDirection(direction) {
|
|
15
7
|
if (direction !== 0 && direction !== 1) {
|
|
16
8
|
throw new Error("direction must be 0 (LONG) or 1 (SHORT).");
|
|
17
9
|
}
|
|
18
10
|
return direction === 0 ? Direction.LONG : Direction.SHORT;
|
|
19
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* 自动推断开启订单的触发类型 (Limit/Stop)
|
|
14
|
+
*/
|
|
15
|
+
function resolveTriggerType(orderType, direction, triggerType) {
|
|
16
|
+
if (triggerType !== undefined && triggerType !== null && triggerType !== 0) {
|
|
17
|
+
return triggerType;
|
|
18
|
+
}
|
|
19
|
+
// LIMIT LONG: LTE(2), LIMIT SHORT: GTE(1)
|
|
20
|
+
if (orderType === OrderType.LIMIT) {
|
|
21
|
+
return direction === 0 ? 2 : 1;
|
|
22
|
+
}
|
|
23
|
+
// STOP LONG: GTE(1), STOP SHORT: LTE(2)
|
|
24
|
+
if (orderType === OrderType.STOP) {
|
|
25
|
+
return direction === 0 ? 1 : 2;
|
|
26
|
+
}
|
|
27
|
+
return 0; // MARKET order typically uses 0
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 自动推断已有仓位的止盈止损触发类型
|
|
31
|
+
*/
|
|
32
|
+
function resolveTpSlTriggerType(isTp, direction, triggerType) {
|
|
33
|
+
if (triggerType !== undefined && triggerType !== null && triggerType !== 0) {
|
|
34
|
+
return triggerType;
|
|
35
|
+
}
|
|
36
|
+
// TP: LONG -> GTE(1), SHORT -> LTE(2)
|
|
37
|
+
// SL: LONG -> LTE(2), SHORT -> GTE(1)
|
|
38
|
+
if (isTp) {
|
|
39
|
+
return direction === 0 ? 1 : 2;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return direction === 0 ? 2 : 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
20
45
|
/**
|
|
21
46
|
* 开仓 / 加仓
|
|
22
47
|
*/
|
|
@@ -39,7 +64,7 @@ export async function openPosition(client, address, args) {
|
|
|
39
64
|
poolId: args.poolId,
|
|
40
65
|
positionId: args.positionId,
|
|
41
66
|
orderType: args.orderType,
|
|
42
|
-
triggerType: args.triggerType,
|
|
67
|
+
triggerType: resolveTriggerType(args.orderType, args.direction, args.triggerType),
|
|
43
68
|
direction: dir,
|
|
44
69
|
collateralAmount: ensureUnits(args.collateralAmount, quoteDecimals, "collateralAmount"),
|
|
45
70
|
size: ensureUnits(args.size, baseDecimals, "size"),
|
|
@@ -58,7 +83,8 @@ export async function openPosition(client, address, args) {
|
|
|
58
83
|
orderParams.slSize = ensureUnits(args.slSize, baseDecimals, "slSize");
|
|
59
84
|
if (args.slPrice)
|
|
60
85
|
orderParams.slPrice = ensureUnits(args.slPrice, 30, "slPrice");
|
|
61
|
-
|
|
86
|
+
const tradingFeeRaw = ensureUnits(args.tradingFee, quoteDecimals, "tradingFee");
|
|
87
|
+
return client.order.createIncreaseOrder(orderParams, tradingFeeRaw, args.marketId);
|
|
62
88
|
}
|
|
63
89
|
/**
|
|
64
90
|
* 平仓 / 减仓
|
|
@@ -85,7 +111,7 @@ export async function closePosition(client, address, args) {
|
|
|
85
111
|
poolId: args.poolId,
|
|
86
112
|
positionId: args.positionId,
|
|
87
113
|
orderType: args.orderType,
|
|
88
|
-
triggerType: args.triggerType,
|
|
114
|
+
triggerType: resolveTriggerType(args.orderType, args.direction, args.triggerType),
|
|
89
115
|
direction: dir,
|
|
90
116
|
collateralAmount: ensureUnits(args.collateralAmount, quoteDecimals, "collateralAmount"),
|
|
91
117
|
size: ensureUnits(args.size, baseDecimals, "size"),
|
|
@@ -107,6 +133,13 @@ export async function setPositionTpSl(client, address, args) {
|
|
|
107
133
|
throw new Error("At least one of tpPrice or slPrice must be provided.");
|
|
108
134
|
}
|
|
109
135
|
const dir = resolveDirection(args.direction);
|
|
136
|
+
// Fetch pool detail for decimals
|
|
137
|
+
const poolResponse = await client.markets.getMarketDetail({ chainId, poolId: args.poolId });
|
|
138
|
+
const poolData = poolResponse?.data || (poolResponse?.marketId ? poolResponse : null);
|
|
139
|
+
if (!poolData) {
|
|
140
|
+
throw new Error(`Could not find pool metadata for ID: ${args.poolId}`);
|
|
141
|
+
}
|
|
142
|
+
const baseDecimals = poolData.baseDecimals || 18;
|
|
110
143
|
const params = {
|
|
111
144
|
chainId,
|
|
112
145
|
address,
|
|
@@ -115,18 +148,18 @@ export async function setPositionTpSl(client, address, args) {
|
|
|
115
148
|
direction: dir,
|
|
116
149
|
leverage: args.leverage,
|
|
117
150
|
executionFeeToken,
|
|
118
|
-
tpTriggerType: args.tpTriggerType,
|
|
119
|
-
slTriggerType: args.slTriggerType,
|
|
151
|
+
tpTriggerType: resolveTpSlTriggerType(true, args.direction, args.tpTriggerType),
|
|
152
|
+
slTriggerType: resolveTpSlTriggerType(false, args.direction, args.slTriggerType),
|
|
120
153
|
slippagePct: normalizeSlippagePct4dp(args.slippagePct),
|
|
121
154
|
};
|
|
122
155
|
if (args.tpPrice)
|
|
123
|
-
params.tpPrice = args.tpPrice;
|
|
156
|
+
params.tpPrice = ensureUnits(args.tpPrice, 30, "tpPrice");
|
|
124
157
|
if (args.tpSize)
|
|
125
|
-
params.tpSize = args.tpSize;
|
|
158
|
+
params.tpSize = ensureUnits(args.tpSize, baseDecimals, "tpSize");
|
|
126
159
|
if (args.slPrice)
|
|
127
|
-
params.slPrice = args.slPrice;
|
|
160
|
+
params.slPrice = ensureUnits(args.slPrice, 30, "slPrice");
|
|
128
161
|
if (args.slSize)
|
|
129
|
-
params.slSize = args.slSize;
|
|
162
|
+
params.slSize = ensureUnits(args.slSize, baseDecimals, "slSize");
|
|
130
163
|
return client.order.createPositionTpSlOrder(params);
|
|
131
164
|
}
|
|
132
165
|
/**
|
package/dist/utils/units.js
CHANGED
|
@@ -23,10 +23,18 @@ export function ensureUnits(value, decimals, label = "value") {
|
|
|
23
23
|
throw new Error(`${label} is required.`);
|
|
24
24
|
if (!DECIMAL_RE.test(str))
|
|
25
25
|
throw new Error(`${label} must be a numeric string.`);
|
|
26
|
-
|
|
26
|
+
// If it's already a very large integer (e.g. > 12 digits or > decimals digits),
|
|
27
|
+
// assume it's already in the smallest unit (Wei/Raw).
|
|
28
|
+
if (!str.includes(".") && (str.length > 12 || str.length > decimals)) {
|
|
29
|
+
return str;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
27
32
|
return parseUnits(str, decimals).toString();
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
catch (e) {
|
|
35
|
+
console.warn(`[ensureUnits] parseUnits failed for ${label}: ${str}. Returning raw.`);
|
|
36
|
+
return str;
|
|
37
|
+
}
|
|
30
38
|
}
|
|
31
39
|
export function parseHumanUnits(value, decimals, label = "value") {
|
|
32
40
|
const str = String(value).trim();
|