@michaleffffff/mcp-trading-server 3.1.1 → 3.1.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.1.2 - 2026-03-23
4
+ ### Changed
5
+ - Added local preflight validation for opening `LIMIT` / `STOP` orders so `open_position_simple` and `execute_trade` now fail fast with clear guidance when:
6
+ - `triggerType` conflicts with the selected open-order semantics
7
+ - the target price is on the wrong side of the current oracle price for the chosen `LONG` / `SHORT` + `LIMIT` / `STOP` combination
8
+
3
9
  ## 3.1.1 - 2026-03-23
4
10
  ### Changed
5
11
  - Upgraded the active SDK baseline to `@myx-trade/sdk@^1.0.4`.
@@ -121,6 +121,68 @@ function computeRecommendedSizeRaw(targetQuoteRaw, priceRaw30, baseDecimals, quo
121
121
  const denominator = priceRaw30 * (10n ** BigInt(quoteDecimals));
122
122
  return numerator / denominator;
123
123
  }
124
+ function getOrderTypeLabel(orderType) {
125
+ if (orderType === OrderType.MARKET)
126
+ return "MARKET";
127
+ if (orderType === OrderType.LIMIT)
128
+ return "LIMIT";
129
+ if (orderType === OrderType.STOP)
130
+ return "STOP";
131
+ return `ORDER_TYPE_${String(orderType)}`;
132
+ }
133
+ function getDirectionLabel(direction) {
134
+ return direction === 0 ? "LONG" : "SHORT";
135
+ }
136
+ function getTriggerTypeLabel(triggerType) {
137
+ if (triggerType === TriggerType.NONE)
138
+ return "NONE";
139
+ if (triggerType === TriggerType.GTE)
140
+ return "GTE";
141
+ if (triggerType === TriggerType.LTE)
142
+ return "LTE";
143
+ return `TRIGGER_TYPE_${String(triggerType)}`;
144
+ }
145
+ async function validateIncreaseOrderTriggerSemantics(client, args, chainId) {
146
+ const directionIndex = resolveDirectionIndex(args.direction);
147
+ const orderType = Number(args.orderType);
148
+ const explicitTriggerType = args.triggerType;
149
+ if (orderType === OrderType.MARKET) {
150
+ if (explicitTriggerType !== undefined && explicitTriggerType !== null && Number(explicitTriggerType) !== TriggerType.NONE) {
151
+ throw new Error("Invalid triggerType for MARKET open order: MARKET orders must use triggerType=0/NONE.");
152
+ }
153
+ return;
154
+ }
155
+ if (orderType !== OrderType.LIMIT && orderType !== OrderType.STOP) {
156
+ return;
157
+ }
158
+ const expectedTriggerType = resolveTriggerType(orderType, directionIndex, false);
159
+ const effectiveTriggerType = explicitTriggerType === undefined || explicitTriggerType === null
160
+ ? expectedTriggerType
161
+ : Number(explicitTriggerType);
162
+ if (effectiveTriggerType !== expectedTriggerType) {
163
+ throw new Error(`Invalid triggerType for opening ${getDirectionLabel(directionIndex)} ${getOrderTypeLabel(orderType)} order: ` +
164
+ `expected ${getTriggerTypeLabel(expectedTriggerType)}, got ${getTriggerTypeLabel(effectiveTriggerType)}.`);
165
+ }
166
+ const oracleData = await getFreshOraclePrice(client, args.poolId, chainId);
167
+ const currentPriceRaw = ensureUnits(oracleData.price, 30, "oracle price", { allowImplicitRaw: false });
168
+ const targetPriceRaw = BigInt(args.priceRaw30);
169
+ const currentPriceRawBig = BigInt(currentPriceRaw);
170
+ const currentPriceHuman = formatUnits(currentPriceRawBig, 30);
171
+ const targetPriceHuman = formatUnits(targetPriceRaw, 30);
172
+ const orderLabel = `${getDirectionLabel(directionIndex)} ${getOrderTypeLabel(orderType)}`;
173
+ const shouldBeBelowCurrent = (orderType === OrderType.LIMIT && directionIndex === 0) ||
174
+ (orderType === OrderType.STOP && directionIndex === 1);
175
+ const shouldBeAboveCurrent = (orderType === OrderType.STOP && directionIndex === 0) ||
176
+ (orderType === OrderType.LIMIT && directionIndex === 1);
177
+ if (shouldBeBelowCurrent && targetPriceRaw >= currentPriceRawBig) {
178
+ throw new Error(`Invalid ${orderLabel} price: target price ${targetPriceHuman} must be below current oracle price ${currentPriceHuman}. ` +
179
+ `If you want to trade above current price, use ${directionIndex === 0 ? "STOP LONG" : "LIMIT SHORT"} instead.`);
180
+ }
181
+ if (shouldBeAboveCurrent && targetPriceRaw <= currentPriceRawBig) {
182
+ throw new Error(`Invalid ${orderLabel} price: target price ${targetPriceHuman} must be above current oracle price ${currentPriceHuman}. ` +
183
+ `If you want to trade below current price, use ${directionIndex === 0 ? "LIMIT LONG" : "STOP SHORT"} instead.`);
184
+ }
185
+ }
124
186
  function validateIncreaseOrderEconomics(args) {
125
187
  const collateralRawBig = BigInt(args.collateralRaw);
126
188
  const sizeRawBig = BigInt(args.sizeRaw);
@@ -250,6 +312,13 @@ export async function openPosition(client, address, args) {
250
312
  baseDecimals,
251
313
  quoteDecimals,
252
314
  });
315
+ await validateIncreaseOrderTriggerSemantics(client, {
316
+ poolId: args.poolId,
317
+ orderType: Number(args.orderType),
318
+ direction: args.direction,
319
+ triggerType: args.triggerType,
320
+ priceRaw30: priceRaw,
321
+ }, chainId);
253
322
  const timeInForce = mapTimeInForce(args.timeInForce);
254
323
  const orderParams = {
255
324
  chainId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michaleffffff/mcp-trading-server",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "myx-mcp": "dist/server.js"