iflow-mcp_dlwjdtn535-mcp-bybit-server 0.1.4__py3-none-any.whl

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.
server.py ADDED
@@ -0,0 +1,683 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from typing import Dict, Optional, Any
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+ from pydantic import Field
8
+
9
+ from service import BybitService
10
+
11
+ # Logging configuration
12
+ logging.basicConfig(
13
+ level=logging.INFO, # Change logging level to DEBUG
14
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
15
+ )
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # Log environment variables
19
+ logger.debug("Current environment variables:")
20
+ for key, value in os.environ.items():
21
+ if key in ["MEMBER_ID", "ACCESS_KEY", "SECRET_KEY", "TESTNET"]:
22
+ logger.debug(f"{key}: {value}")
23
+
24
+ # Create BybitService instance
25
+ bybit_service = BybitService()
26
+
27
+ # Register MCP tools
28
+ mcp = FastMCP()
29
+
30
+ @mcp.tool()
31
+ def get_secret_key(
32
+ ) -> str:
33
+ """
34
+ Get secret key from environment variables
35
+ :return: Secret key
36
+ """
37
+ return os.getenv("SECRET_KEY")
38
+
39
+ @mcp.tool()
40
+ def get_access_key(
41
+ ) -> str:
42
+ """
43
+ Get access key from environment variables
44
+ :return: Access key
45
+ """
46
+ return os.getenv("ACCESS_KEY")
47
+
48
+ # Register MCP tools
49
+ @mcp.tool()
50
+ def get_orderbook(
51
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
52
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
53
+ limit: int = Field(default=50, description="Number of orderbook entries to retrieve")
54
+ ) -> Dict:
55
+ """
56
+ Get orderbook data
57
+ :parameter
58
+ symbol: Symbol (e.g., BTCUSDT)
59
+ limit: Number of orderbook entries to retrieve
60
+ category: Category (spot, linear, inverse, etc.)
61
+
62
+ Args:
63
+ category: Category (spot, linear, inverse, etc.)
64
+ symbol (str): Symbol (e.g., BTCUSDT)
65
+ limit (int): Number of orderbook entries to retrieve
66
+
67
+ Returns:
68
+ Dict: Orderbook data
69
+
70
+ Example:
71
+ get_orderbook("spot", "BTCUSDT", 10)
72
+
73
+ Reference:
74
+ https://bybit-exchange.github.io/docs/v5/market/orderbook
75
+ """
76
+ try:
77
+ result = bybit_service.get_orderbook(category, symbol, limit)
78
+ if result.get("retCode") != 0:
79
+ logger.error(f"Failed to get orderbook: {result.get('retMsg')}")
80
+ return {"error": result.get("retMsg")}
81
+ return result
82
+ except Exception as e:
83
+ logger.error(f"Failed to get orderbook: {e}", exc_info=True)
84
+ return {"error": str(e)}
85
+
86
+
87
+ @mcp.tool()
88
+ def get_kline(
89
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
90
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
91
+ interval: str = Field(description="Time interval (1, 3, 5, 15, 30, 60, 120, 240, 360, 720, D, W, M)"),
92
+ start: Optional[int] = Field(default=None, description="Start time in milliseconds"),
93
+ end: Optional[int] = Field(default=None, description="End time in milliseconds"),
94
+ limit: int = Field(default=200, description="Number of records to retrieve")
95
+ ) -> Dict:
96
+ """
97
+ Get K-line (candlestick) data
98
+
99
+ Args:
100
+ category (str): Category (spot, linear, inverse, etc.)
101
+ symbol (str): Symbol (e.g., BTCUSDT)
102
+ interval (str): Time interval (1, 3, 5, 15, 30, 60, 120, 240, 360, 720, D, W, M)
103
+ start (Optional[int]): Start time in milliseconds
104
+ end (Optional[int]): End time in milliseconds
105
+ limit (int): Number of records to retrieve
106
+
107
+ Returns:
108
+ Dict: K-line data
109
+
110
+ Example:
111
+ get_kline("spot", "BTCUSDT", "1h", 1625097600000, 1625184000000, 100)
112
+
113
+ Reference:
114
+ https://bybit-exchange.github.io/docs/v5/market/kline
115
+ """
116
+ try:
117
+ result = bybit_service.get_kline(category, symbol, interval, start, end, limit)
118
+ if result.get("retCode") != 0:
119
+ logger.error(f"Failed to get K-line data: {result.get('retMsg')}")
120
+ return {"error": result.get("retMsg")}
121
+ return result
122
+ except Exception as e:
123
+ logger.error(f"Failed to get K-line data: {e}", exc_info=True)
124
+ return {"error": str(e)}
125
+
126
+
127
+ @mcp.tool()
128
+ def get_tickers(
129
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
130
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)")
131
+ ) -> Dict:
132
+ """
133
+ Get ticker information
134
+
135
+ Args:
136
+ category (str): Category (spot, linear, inverse, etc.)
137
+ symbol (str): Symbol (e.g., BTCUSDT)
138
+
139
+ Returns:
140
+ Dict: Ticker information
141
+
142
+ Example:
143
+ get_tickers("spot", "BTCUSDT")
144
+
145
+ Reference:
146
+ https://bybit-exchange.github.io/docs/v5/market/tickers
147
+ """
148
+ try:
149
+ result = bybit_service.get_tickers(category, symbol)
150
+ if result.get("retCode") != 0:
151
+ logger.error(f"Failed to get ticker information: {result.get('retMsg')}")
152
+ return {"error": result.get("retMsg")}
153
+ return result
154
+ except Exception as e:
155
+ logger.error(f"Failed to get ticker information: {e}", exc_info=True)
156
+ return {"error": str(e)}
157
+
158
+
159
+ @mcp.tool()
160
+ def get_wallet_balance(
161
+ accountType: str = Field(description="Account type (UNIFIED, CONTRACT, SPOT)"),
162
+ coin: Optional[str] = Field(default=None, description="Coin symbol")
163
+ ) -> Dict:
164
+ """
165
+ Get wallet balance
166
+
167
+ Args:
168
+ accountType (str): Account type (UNIFIED, CONTRACT, SPOT)
169
+ coin (Optional[str]): Coin symbol
170
+
171
+ Returns:
172
+ Dict: Wallet balance information
173
+
174
+ Example:
175
+ get_wallet_balance("UNIFIED", "BTC")
176
+
177
+ Reference:
178
+ https://bybit-exchange.github.io/docs/v5/account/wallet-balance
179
+ """
180
+ try:
181
+ result = bybit_service.get_wallet_balance(accountType, coin)
182
+ if result.get("retCode") != 0:
183
+ logger.error(f"Failed to get wallet balance: {result.get('retMsg')}")
184
+ return {"error": result.get("retMsg")}
185
+ return result
186
+ except Exception as e:
187
+ logger.error(f"Failed to get wallet balance: {e}", exc_info=True)
188
+ return {"error": str(e)}
189
+
190
+
191
+ @mcp.tool()
192
+ def get_positions(
193
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
194
+ symbol: Optional[str] = Field(default=None, description="Symbol (e.g., BTCUSDT)")
195
+ ) -> Dict:
196
+ """
197
+ Get position information
198
+
199
+ Args:
200
+ category (str): Category (spot, linear, inverse, etc.)
201
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
202
+
203
+ Returns:
204
+ Dict: Position information
205
+
206
+ Example:
207
+ get_positions("spot", "BTCUSDT")
208
+
209
+ Reference:
210
+ https://bybit-exchange.github.io/docs/v5/position
211
+ """
212
+ try:
213
+ result = bybit_service.get_positions(category, symbol)
214
+ if result.get("retCode") != 0:
215
+ logger.error(f"Failed to get position information: {result.get('retMsg')}")
216
+ return {"error": result.get("retMsg")}
217
+ return result
218
+ except Exception as e:
219
+ logger.error(f"Failed to get position information: {e}", exc_info=True)
220
+ return {"error": str(e)}
221
+
222
+
223
+ @mcp.tool()
224
+ def place_order(
225
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
226
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
227
+ side: str = Field(description="Order direction (Buy, Sell)"),
228
+ orderType: str = Field(description="Order type (Market, Limit)"),
229
+ qty: str = Field(description="Order quantity"),
230
+ price: Optional[str] = Field(default=None, description="Order price (for limit orders)"),
231
+ positionIdx: Optional[str] = Field(default=None, description="Position index (1: Long, 2: Short)"),
232
+ timeInForce: Optional[str] = Field(default=None, description="Time in force (GTC, IOC, FOK, PostOnly)"),
233
+ orderLinkId: Optional[str] = Field(default=None, description="Order link ID"),
234
+ isLeverage: Optional[int] = Field(default=None, description="Use leverage (0: No, 1: Yes)"),
235
+ orderFilter: Optional[str] = Field(default=None, description="Order filter (Order, tpslOrder, StopOrder)"),
236
+ triggerPrice: Optional[str] = Field(default=None, description="Trigger price"),
237
+ triggerBy: Optional[str] = Field(default=None, description="Trigger basis"),
238
+ orderIv: Optional[str] = Field(default=None, description="Order volatility"),
239
+ takeProfit: Optional[str] = Field(default=None, description="Take profit price"),
240
+ stopLoss: Optional[str] = Field(default=None, description="Stop loss price"),
241
+ tpTriggerBy: Optional[str] = Field(default=None, description="Take profit trigger basis"),
242
+ slTriggerBy: Optional[str] = Field(default=None, description="Stop loss trigger basis"),
243
+ tpLimitPrice: Optional[str] = Field(default=None, description="Take profit limit price"),
244
+ slLimitPrice: Optional[str] = Field(default=None, description="Stop loss limit price"),
245
+ tpOrderType: Optional[str] = Field(default=None, description="Take profit order type (Market, Limit)"),
246
+ slOrderType: Optional[str] = Field(default=None, description="Stop loss order type (Market, Limit)")
247
+ ) -> Dict:
248
+ """
249
+ Execute order
250
+
251
+ Args:
252
+ category (str): Category
253
+ - spot: Spot trading
254
+ * Minimum order quantity: 0.000011 BTC (up to 6 decimal places)
255
+ * Minimum order amount: 5 USDT
256
+ * If buying at market price, qty should be input in USDT units (e.g., "10" = 10 USDT)
257
+ * If selling at market price, qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC)
258
+ * If placing a limit order, qty should be input in BTC units
259
+ * positionIdx is not used
260
+ - linear: Futures trading (USDT margin)
261
+ * positionIdx is required (1: Long, 2: Short)
262
+ - inverse: Futures trading (coin margin)
263
+ * positionIdx is required (1: Long, 2: Short)
264
+ symbol (str): Symbol (e.g., BTCUSDT)
265
+ side (str): Order direction (Buy, Sell)
266
+ orderType (str): Order type (Market, Limit)
267
+ qty (str): Order quantity
268
+ - Market Buy: qty should be input in USDT units (e.g., "10" = 10 USDT)
269
+ - Market Sell: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC, up to 6 decimal places)
270
+ - Limit: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC, up to 6 decimal places)
271
+ price (Optional[str]): Order price (for limit orders)
272
+ positionIdx (Optional[str]): Position index
273
+ - Required for futures (linear/inverse) trading
274
+ - "1": Long position
275
+ - "2": Short position
276
+ - Not used for spot trading
277
+ timeInForce (Optional[str]): Order validity period
278
+ - GTC: Good Till Cancel (default, for limit orders)
279
+ - IOC: Immediate or Cancel (market order)
280
+ - FOK: Fill or Kill
281
+ - PostOnly: Post Only
282
+ orderLinkId (Optional[str]): Order link ID (unique value)
283
+ isLeverage (Optional[int]): Use leverage (0: No, 1: Yes)
284
+ orderFilter (Optional[str]): Order filter
285
+ - Order: Regular order (default)
286
+ - tpslOrder: TP/SL order
287
+ - StopOrder: Stop order
288
+ triggerPrice (Optional[str]): Trigger price
289
+ triggerBy (Optional[str]): Trigger basis
290
+ orderIv (Optional[str]): Order volatility
291
+ takeProfit (Optional[str]): Take profit price
292
+ stopLoss (Optional[str]): Stop loss price
293
+ tpTriggerBy (Optional[str]): Take profit trigger basis
294
+ slTriggerBy (Optional[str]): Stop loss trigger basis
295
+ tpLimitPrice (Optional[str]): Take profit limit price
296
+ slLimitPrice (Optional[str]): Stop loss limit price
297
+ tpOrderType (Optional[str]): Take profit order type (Market, Limit)
298
+ slOrderType (Optional[str]): Stop loss order type (Market, Limit)
299
+
300
+ Returns:
301
+ Dict: Order result
302
+
303
+ Example:
304
+ # Spot trading (SPOT account balance required)
305
+ place_order("spot", "BTCUSDT", "Buy", "Market", "10") # Buy market price for 10 USDT
306
+ place_order("spot", "BTCUSDT", "Sell", "Market", "0.000100") # Sell market price for 0.0001 BTC
307
+ place_order("spot", "BTCUSDT", "Buy", "Limit", "0.000100", price="50000") # Buy limit order for 0.0001 BTC
308
+
309
+ # Spot trading - limit order + TP/SL
310
+ place_order("spot", "BTCUSDT", "Buy", "Limit", "0.000100", price="50000",
311
+ takeProfit="55000", stopLoss="45000", # TP/SL setting
312
+ tpOrderType="Market", slOrderType="Market") # Execute TP/SL as market order
313
+
314
+ # Futures trading
315
+ place_order("linear", "BTCUSDT", "Buy", "Market", "0.001", positionIdx="1") # Buy market price for long position
316
+ place_order("linear", "BTCUSDT", "Sell", "Market", "0.001", positionIdx="2") # Sell market price for short position
317
+
318
+ Notes:
319
+ 1. Spot trading order quantity restrictions:
320
+ - Minimum order quantity: 0.000011 BTC
321
+ - Minimum order amount: 5 USDT
322
+ - BTC quantity is only allowed up to 6 decimal places (e.g., 0.000100 O, 0.0001234 X)
323
+ 2. Pay attention to unit when buying/selling at market price:
324
+ - Buying: qty should be input in USDT units (e.g., "10" = 10 USDT)
325
+ - Selling: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC)
326
+ 3. Futures trading requires positionIdx:
327
+ - Long position: positionIdx="1"
328
+ - Short position: positionIdx="2"
329
+ 4. positionIdx is not used for spot trading
330
+
331
+ Reference:
332
+ https://bybit-exchange.github.io/docs/v5/order/create-order
333
+ """
334
+ try:
335
+ result = bybit_service.place_order(
336
+ category, symbol, side, orderType, qty, price, positionIdx,
337
+ timeInForce, orderLinkId, isLeverage, orderFilter,
338
+ triggerPrice, triggerBy, orderIv, takeProfit,
339
+ stopLoss, tpTriggerBy, slTriggerBy, tpLimitPrice,
340
+ slLimitPrice, tpOrderType, slOrderType
341
+ )
342
+ if result.get("retCode") != 0:
343
+ logger.error(f"Failed to place order: {result.get('retMsg')}")
344
+ return {"error": result.get("retMsg")}
345
+ return result
346
+ except Exception as e:
347
+ logger.error(f"Failed to place order: {e}", exc_info=True)
348
+ return {"error": str(e)}
349
+
350
+
351
+ @mcp.tool()
352
+ def cancel_order(
353
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
354
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
355
+ orderId: Optional[str] = Field(default=None, description="Order ID"),
356
+ orderLinkId: Optional[str] = Field(default=None, description="Order link ID"),
357
+ orderFilter: Optional[str] = Field(default=None, description="Order filter")
358
+ ) -> Dict:
359
+ """
360
+ Cancel order
361
+
362
+ Args:
363
+ category (str): Category (spot, linear, inverse, etc.)
364
+ symbol (str): Symbol (e.g., BTCUSDT)
365
+ orderId (Optional[str]): Order ID
366
+ orderLinkId (Optional[str]): Order link ID
367
+ orderFilter (Optional[str]): Order filter
368
+
369
+ Returns:
370
+ Dict: Cancel result
371
+
372
+ Example:
373
+ cancel_order("spot", "BTCUSDT", "123456789")
374
+
375
+ Reference:
376
+ https://bybit-exchange.github.io/docs/v5/order/cancel-order
377
+ """
378
+ try:
379
+ result = bybit_service.cancel_order(category, symbol, orderId, orderLinkId, orderFilter)
380
+ if result.get("retCode") != 0:
381
+ logger.error(f"Failed to cancel order: {result.get('retMsg')}")
382
+ return {"error": result.get("retMsg")}
383
+ return result
384
+ except Exception as e:
385
+ logger.error(f"Failed to cancel order: {e}", exc_info=True)
386
+ return {"error": str(e)}
387
+
388
+
389
+ @mcp.tool()
390
+ def get_order_history(
391
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
392
+ symbol: Optional[str] = Field(default=None, description="Symbol (e.g., BTCUSDT)"),
393
+ orderId: Optional[str] = Field(default=None, description="Order ID"),
394
+ orderLinkId: Optional[str] = Field(default=None, description="Order link ID"),
395
+ orderFilter: Optional[str] = Field(default=None, description="Order filter"),
396
+ orderStatus: Optional[str] = Field(default=None, description="Order status"),
397
+ startTime: Optional[int] = Field(default=None, description="Start time in milliseconds"),
398
+ endTime: Optional[int] = Field(default=None, description="End time in milliseconds"),
399
+ limit: int = Field(default=50, description="Number of orders to retrieve")
400
+ ) -> Dict:
401
+ """
402
+ Get order history
403
+
404
+ Args:
405
+ category (str): Category (spot, linear, inverse, etc.)
406
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
407
+ orderId (Optional[str]): Order ID
408
+ orderLinkId (Optional[str]): Order link ID
409
+ orderFilter (Optional[str]): Order filter
410
+ orderStatus (Optional[str]): Order status
411
+ startTime (Optional[int]): Start time in milliseconds
412
+ endTime (Optional[int]): End time in milliseconds
413
+ limit (int): Number of orders to retrieve
414
+
415
+ Returns:
416
+ Dict: Order history
417
+
418
+ Example:
419
+ get_order_history("spot", "BTCUSDT", "123456789", "link123", "Order", "Created", 1625097600000, 1625184000000, 10)
420
+
421
+ Reference:
422
+ https://bybit-exchange.github.io/docs/v5/order/order-list
423
+ """
424
+ try:
425
+ result = bybit_service.get_order_history(
426
+ category, symbol, orderId, orderLinkId,
427
+ orderFilter, orderStatus, startTime, endTime, limit
428
+ )
429
+ if result.get("retCode") != 0:
430
+ logger.error(f"Failed to get order history: {result.get('retMsg')}")
431
+ return {"error": result.get("retMsg")}
432
+ return result
433
+ except Exception as e:
434
+ logger.error(f"Failed to get order history: {e}", exc_info=True)
435
+ return {"error": str(e)}
436
+
437
+
438
+ @mcp.tool()
439
+ def get_open_orders(
440
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
441
+ symbol: Optional[str] = Field(default=None, description="Symbol (e.g., BTCUSDT)"),
442
+ orderId: Optional[str] = Field(default=None, description="Order ID"),
443
+ orderLinkId: Optional[str] = Field(default=None, description="Order link ID"),
444
+ orderFilter: Optional[str] = Field(default=None, description="Order filter"),
445
+ limit: int = Field(default=50, description="Number of orders to retrieve")
446
+ ) -> Dict:
447
+ """
448
+ Get open orders
449
+
450
+ Args:
451
+ category (str): Category (spot, linear, inverse, etc.)
452
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
453
+ orderId (Optional[str]): Order ID
454
+ orderLinkId (Optional[str]): Order link ID
455
+ orderFilter (Optional[str]): Order filter
456
+ limit (int): Number of orders to retrieve
457
+
458
+ Returns:
459
+ Dict: Open orders
460
+
461
+ Example:
462
+ get_open_orders("spot", "BTCUSDT", "123456789", "link123", "Order", 10)
463
+
464
+ Reference:
465
+ https://bybit-exchange.github.io/docs/v5/order/open-order
466
+ """
467
+ try:
468
+ result = bybit_service.get_open_orders(
469
+ category, symbol, orderId, orderLinkId, orderFilter, limit
470
+ )
471
+ if result.get("retCode") != 0:
472
+ logger.error(f"Failed to get open orders: {result.get('retMsg')}")
473
+ return {"error": result.get("retMsg")}
474
+ return result
475
+ except Exception as e:
476
+ logger.error(f"Failed to get open orders: {e}", exc_info=True)
477
+ return {"error": str(e)}
478
+
479
+
480
+ @mcp.tool()
481
+ def set_trading_stop(
482
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
483
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
484
+ takeProfit: Optional[str] = Field(default=None, description="Take profit price"),
485
+ stopLoss: Optional[str] = Field(default=None, description="Stop loss price"),
486
+ trailingStop: Optional[str] = Field(default=None, description="Trailing stop"),
487
+ positionIdx: Optional[int] = Field(default=None, description="Position index")
488
+ ) -> Dict:
489
+ """
490
+ Set trading stop
491
+
492
+ Args:
493
+ category (str): Category (spot, linear, inverse, etc.)
494
+ symbol (str): Symbol (e.g., BTCUSDT)
495
+ takeProfit (Optional[str]): Take profit price
496
+ stopLoss (Optional[str]): Stop loss price
497
+ trailingStop (Optional[str]): Trailing stop
498
+ positionIdx (Optional[int]): Position index
499
+
500
+ Returns:
501
+ Dict: Setting result
502
+
503
+ Example:
504
+ set_trading_stop("spot", "BTCUSDT", "55000", "45000", "1000", 0)
505
+
506
+ Reference:
507
+ https://bybit-exchange.github.io/docs/v5/position/trading-stop
508
+ """
509
+ try:
510
+ result = bybit_service.set_trading_stop(
511
+ category, symbol, takeProfit, stopLoss, trailingStop, positionIdx
512
+ )
513
+ if result.get("retCode") != 0:
514
+ logger.error(f"Failed to set trading stop: {result.get('retMsg')}")
515
+ return {"error": result.get("retMsg")}
516
+ return result
517
+ except Exception as e:
518
+ logger.error(f"Failed to set trading stop: {e}", exc_info=True)
519
+ return {"error": str(e)}
520
+
521
+
522
+ @mcp.tool()
523
+ def set_margin_mode(
524
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
525
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
526
+ tradeMode: int = Field(description="Trading mode (0: Isolated, 1: Cross)"),
527
+ buyLeverage: str = Field(description="Buying leverage"),
528
+ sellLeverage: str = Field(description="Selling leverage")
529
+ ) -> Dict:
530
+ """
531
+ Set margin mode
532
+
533
+ Args:
534
+ category (str): Category (spot, linear, inverse, etc.)
535
+ symbol (str): Symbol (e.g., BTCUSDT)
536
+ tradeMode (int): Trading mode (0: Isolated, 1: Cross)
537
+ buyLeverage (str): Buying leverage
538
+ sellLeverage (str): Selling leverage
539
+
540
+ Returns:
541
+ Dict: Setting result
542
+
543
+ Example:
544
+ set_margin_mode("spot", "BTCUSDT", 0, "10", "10")
545
+
546
+ Reference:
547
+ https://bybit-exchange.github.io/docs/v5/account/set-margin-mode
548
+ """
549
+ try:
550
+ result = bybit_service.set_margin_mode(
551
+ category, symbol, tradeMode, buyLeverage, sellLeverage
552
+ )
553
+ if result.get("retCode") != 0:
554
+ logger.error(f"Failed to set margin mode: {result.get('retMsg')}")
555
+ return {"error": result.get("retMsg")}
556
+ return result
557
+ except Exception as e:
558
+ logger.error(f"Failed to set margin mode: {e}", exc_info=True)
559
+ return {"error": str(e)}
560
+
561
+
562
+ @mcp.tool()
563
+ def get_api_key_information() -> Dict:
564
+ """
565
+ Get API key information
566
+
567
+ Returns:
568
+ Dict: API key information
569
+
570
+ Example:
571
+ get_api_key_information()
572
+
573
+ Reference:
574
+ https://bybit-exchange.github.io/docs/v5/user/apikey-info
575
+ """
576
+ try:
577
+ result = bybit_service.get_api_key_information()
578
+ if result.get("retCode") != 0:
579
+ logger.error(f"Failed to get API key information: {result.get('retMsg')}")
580
+ return {"error": result.get("retMsg")}
581
+ return result
582
+ except Exception as e:
583
+ logger.error(f"Failed to get API key information: {e}", exc_info=True)
584
+ return {"error": str(e)}
585
+
586
+
587
+ @mcp.tool()
588
+ def get_instruments_info(
589
+ category: str = Field(description="Category (spot, linear, inverse, etc.)"),
590
+ symbol: str = Field(description="Symbol (e.g., BTCUSDT)"),
591
+ status: Optional[str] = Field(default=None, description="Status"),
592
+ baseCoin: Optional[str] = Field(default=None, description="Base coin")
593
+ ) -> Dict:
594
+ """
595
+ Get exchange information
596
+
597
+ Args:
598
+ category (str): Category (spot, linear, inverse, etc.)
599
+ symbol (str): Symbol (e.g., BTCUSDT)
600
+ status (Optional[str]): Status
601
+ baseCoin (Optional[str]): Base coin
602
+
603
+ Returns:
604
+ Dict: Exchange information
605
+
606
+ Example:
607
+ get_instruments_info("spot", "BTCUSDT", "Trading", "BTC")
608
+
609
+ Reference:
610
+ https://bybit-exchange.github.io/docs/v5/market/instrument
611
+ """
612
+ try:
613
+ result = bybit_service.get_instruments_info(category, symbol, status, baseCoin)
614
+ if result.get("retCode") != 0:
615
+ logger.error(f"Failed to get instruments information: {result.get('retMsg')}")
616
+ return {"error": result.get("retMsg")}
617
+ return result
618
+ except Exception as e:
619
+ logger.error(f"Failed to get instruments information: {e}", exc_info=True)
620
+ return {"error": str(e)}
621
+
622
+
623
+ @mcp.prompt()
624
+ def prompt(message: str) -> str:
625
+ return f"""
626
+ You are an AI assistant providing access to Bybit API functionalities through available tools.
627
+ Analyze user requests and utilize the appropriate tools to fetch data, manage account information, or execute/manage orders as requested.
628
+
629
+ Available tools:
630
+ - get_orderbook(category, symbol, limit) - Get orderbook: Retrieve orderbook information for a specific category and symbol. limit parameter can be used to specify the number of orderbook entries to retrieve.
631
+ - get_kline(category, symbol, interval, start, end, limit) - Get K-line data: Retrieve K-line data for a specific category and symbol. interval, start, end, and limit parameters can be used to specify the retrieval range and number of records.
632
+ - get_tickers(category, symbol) - Get ticker information: Retrieve ticker information for a specific category and symbol.
633
+ - get_trades(category, symbol, limit) - Get recent trade history: Retrieve recent trade history for a specific category and symbol. limit parameter can be used to specify the number of trades to retrieve.
634
+ - get_wallet_balance(accountType, coin) - Get wallet balance: Retrieve wallet balance information for a specific account type and coin.
635
+ - get_positions(category, symbol) - Get position information: Retrieve position information for a specific category and symbol.
636
+ - place_order(category, symbol, side, orderType, qty, price, timeInForce, orderLinkId, isLeverage, orderFilter, triggerPrice, triggerBy, orderIv, positionIdx) - Execute order: Execute an order. Various parameters can be used to specify the details of the order.
637
+ - cancel_order(category, symbol, orderId, orderLinkId, orderFilter) - Cancel order: Cancel a specific order. orderId, orderLinkId, and orderFilter parameters can be used to specify the order to cancel.
638
+ - get_order_history(category, symbol, orderId, orderLinkId, orderFilter, orderStatus, startTime, endTime, limit) - Get order history: Retrieve order history. Various parameters can be used to specify the retrieval range and conditions.
639
+ - get_open_orders(category, symbol, orderId, orderLinkId, orderFilter, limit) - Get open orders: Retrieve open orders. limit parameter can be used to specify the number of orders to retrieve.
640
+ - get_leverage_info(category, symbol) - Get leverage information: Retrieve leverage information for a specific category and symbol.
641
+ - set_trading_stop(category, symbol, takeProfit, stopLoss, trailingStop, positionIdx) - Set trading stop: Set trading stop. takeProfit, stopLoss, trailingStop, and positionIdx parameters can be used to specify the settings.
642
+ - set_margin_mode(category, symbol, tradeMode, buyLeverage, sellLeverage) - Set margin mode: Set margin mode. tradeMode, buyLeverage, and sellLeverage parameters can be used to specify the settings.
643
+ - get_api_key_information() - Get API key information: Retrieve API key information.
644
+ - get_instruments_info(category, symbol, status, baseCoin) - Get exchange information: Retrieve exchange information. status and baseCoin parameters can be used to specify the retrieval conditions.
645
+
646
+ Note: This tool executes a backtest simulation, it does not interact with the live Bybit API for trading.
647
+ Invokes the `run_strategy` function from `backtest.py`.
648
+
649
+ Args:
650
+ start_time: Start time for the backtest period (millisecond timestamp).
651
+ end_time: End time for the backtest period (millisecond timestamp).
652
+ strategy_vars: A dictionary containing the strategy definition.
653
+ Refer to the `run_strategy` function in `backtest.py` for the expected structure.
654
+ This includes initial balance, indicator settings, buy/sell conditions, and position settings.
655
+
656
+ Returns:
657
+ Dict: The results of the backtest, including performance metrics and trade history.
658
+ Returns an error dictionary if the backtest fails.
659
+
660
+ User message: {message}
661
+ """
662
+
663
+
664
+ def main():
665
+ try:
666
+ logger.info("MCP server starting...")
667
+ print("MCP server starting...", file=sys.stderr)
668
+
669
+ # Log environment variables again
670
+ logger.debug("Server start environment variables:")
671
+ for key, value in os.environ.items():
672
+ if key in ["MEMBER_ID", "ACCESS_KEY", "SECRET_KEY", "TESTNET"]:
673
+ logger.debug(f"{key}: {value}")
674
+
675
+ mcp.run(transport="stdio")
676
+ except Exception as e:
677
+ logger.error(e)
678
+ print(f"Server execution failed: {str(e)}", file=sys.stderr)
679
+ sys.exit(1)
680
+
681
+
682
+ if __name__ == "__main__":
683
+ main()