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.
- __init__.py +0 -0
- config.py +23 -0
- iflow_mcp_dlwjdtn535_mcp_bybit_server-0.1.4.dist-info/METADATA +246 -0
- iflow_mcp_dlwjdtn535_mcp_bybit_server-0.1.4.dist-info/RECORD +9 -0
- iflow_mcp_dlwjdtn535_mcp_bybit_server-0.1.4.dist-info/WHEEL +4 -0
- iflow_mcp_dlwjdtn535_mcp_bybit_server-0.1.4.dist-info/entry_points.txt +2 -0
- iflow_mcp_dlwjdtn535_mcp_bybit_server-0.1.4.dist-info/licenses/LICENSE +21 -0
- server.py +683 -0
- service.py +531 -0
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()
|