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.
service.py ADDED
@@ -0,0 +1,531 @@
1
+ import logging
2
+ from typing import Dict, Optional
3
+ from pybit.unified_trading import HTTP
4
+
5
+ from config import Config
6
+ # import pandas as pd # Removed as it was only used for talib
7
+ # from talib import abstract # Removed as talib is no longer used
8
+
9
+ # Logging configuration
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
13
+ datefmt='%Y-%m-%d %H:%M:%S'
14
+ )
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class BybitService:
20
+ """A wrapper class for interacting with the Bybit Unified Trading API.
21
+
22
+ Provides methods to call various Bybit API v5 endpoints for market data,
23
+ account management, and order execution.
24
+ Handles initialization of the pybit HTTP client.
25
+ """
26
+
27
+ def __init__(self):
28
+ """
29
+ Initialize BybitService
30
+ """
31
+ logger.info(f"Initializing Bybit Service - Testnet: {Config.TESTNET}, API Key: {Config.ACCESS_KEY}")
32
+ self.client = HTTP(
33
+ testnet=Config.TESTNET,
34
+ api_key=Config.ACCESS_KEY,
35
+ api_secret=Config.SECRET_KEY
36
+ )
37
+
38
+ # Market data related methods
39
+ def get_orderbook(self, category: str, symbol: str, limit: int = 50) -> Dict:
40
+ """
41
+ Get orderbook data
42
+
43
+ Args:
44
+ category (str): Category (spot, linear, inverse, etc.)
45
+ symbol (str): Symbol (e.g., BTCUSDT)
46
+ limit (int): Number of orderbook entries to retrieve
47
+
48
+ Returns:
49
+ Dict: Orderbook data
50
+ """
51
+ return self.client.get_orderbook(
52
+ category=category,
53
+ symbol=symbol,
54
+ limit=limit
55
+ )
56
+
57
+ def get_kline(self, category: str, symbol: str, interval: str,
58
+ start: Optional[int] = None, end: Optional[int] = None,
59
+ limit: int = 200) -> Dict:
60
+ """
61
+ Get K-line data
62
+
63
+ Args:
64
+ category: Category (spot, linear, inverse, etc.)
65
+ symbol: Symbol (e.g., BTCUSDT)
66
+ interval: Time interval (1, 3, 5, 15, 30, 60, 120, 240, 360, 720, D, W, M)
67
+ start: Start time (millisecond timestamp)
68
+ end: End time (millisecond timestamp)
69
+ limit: Number of records to retrieve
70
+
71
+ Returns:
72
+ Dict: K-line data
73
+ """
74
+ try:
75
+ params = {
76
+ "category": category,
77
+ "symbol": symbol,
78
+ "interval": interval,
79
+ "limit": limit
80
+ }
81
+
82
+ if start:
83
+ params["start"] = start
84
+ if end:
85
+ params["end"] = end
86
+
87
+ response = self.client.get_kline(**params)
88
+ return response
89
+
90
+ except Exception as e:
91
+ logger.error(f"Failed to get K-line data: {str(e)}")
92
+ return {"error": str(e)}
93
+
94
+ def get_tickers(self, category: str, symbol: str) -> Dict:
95
+ """
96
+ Get ticker information
97
+
98
+ Args:
99
+ category (str): Category (spot, linear, inverse, etc.)
100
+ symbol (str): Symbol (e.g., BTCUSDT)
101
+
102
+ Returns:
103
+ Dict: Ticker information
104
+ """
105
+ return self.client.get_tickers(
106
+ category=category,
107
+ symbol=symbol
108
+ )
109
+
110
+ # Account related methods
111
+ def get_wallet_balance(self, accountType: str, coin: Optional[str] = None) -> Dict:
112
+ """
113
+ Get wallet balance
114
+
115
+ Args:
116
+ accountType (str): Account type (UNIFIED, CONTRACT, SPOT)
117
+ coin (Optional[str]): Coin symbol
118
+
119
+ Returns:
120
+ Dict: Wallet balance information
121
+ """
122
+ return self.client.get_wallet_balance(
123
+ accountType=accountType,
124
+ coin=coin
125
+ )
126
+
127
+ def get_positions(self, category: str, symbol: Optional[str] = None) -> Dict:
128
+ """
129
+ Get position information
130
+
131
+ Args:
132
+ category (str): Category (spot, linear, inverse, etc.)
133
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
134
+
135
+ Returns:
136
+ Dict: Position information
137
+ """
138
+ return self.client.get_positions(
139
+ category=category,
140
+ symbol=symbol
141
+ )
142
+
143
+ # Order related methods
144
+ def place_order(self, category: str, symbol: str, side: str, orderType: str,
145
+ qty: str, price: Optional[str] = None,
146
+ timeInForce: Optional[str] = None, orderLinkId: Optional[str] = None,
147
+ isLeverage: Optional[int] = None, orderFilter: Optional[str] = None,
148
+ triggerPrice: Optional[str] = None, triggerBy: Optional[str] = None,
149
+ orderIv: Optional[str] = None, positionIdx: Optional[int] = None,
150
+ takeProfit: Optional[str] = None, stopLoss: Optional[str] = None,
151
+ tpTriggerBy: Optional[str] = None, slTriggerBy: Optional[str] = None,
152
+ tpLimitPrice: Optional[str] = None, slLimitPrice: Optional[str] = None,
153
+ tpOrderType: Optional[str] = None, slOrderType: Optional[str] = None) -> Dict:
154
+ """
155
+ Execute order
156
+
157
+ Args:
158
+ category (str): Category
159
+ - spot: Spot trading
160
+ * Minimum order quantity: 0.000011 BTC (up to 6 decimal places)
161
+ * Minimum order amount: 5 USDT
162
+ * If buying at market price, qty should be input in USDT units (e.g., "10" = 10 USDT)
163
+ * If selling at market price, qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC)
164
+ * If placing a limit order, qty should be input in BTC units
165
+ * positionIdx is not used
166
+ - linear: Futures trading (USDT margin)
167
+ * positionIdx is required (1: Long, 2: Short)
168
+ - inverse: Futures trading (coin margin)
169
+ * positionIdx is required (1: Long, 2: Short)
170
+ symbol (str): Symbol (e.g., BTCUSDT)
171
+ side (str): Order direction (Buy, Sell)
172
+ orderType (str): Order type (Market, Limit)
173
+ qty (str): Order quantity
174
+ - Market Buy: qty should be input in USDT units (e.g., "10" = 10 USDT)
175
+ - Market Sell: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC, up to 6 decimal places)
176
+ - Limit: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC, up to 6 decimal places)
177
+ price (Optional[str]): Order price (for limit order)
178
+ timeInForce (Optional[str]): Order validity period
179
+ - GTC: Good Till Cancel (default, for limit order)
180
+ - IOC: Immediate or Cancel (market order)
181
+ - FOK: Fill or Kill
182
+ - PostOnly: Post Only
183
+ orderLinkId (Optional[str]): Order link ID (unique value)
184
+ isLeverage (Optional[int]): Use leverage (0: No use, 1: Use)
185
+ orderFilter (Optional[str]): Order filter
186
+ - Order: General order (default)
187
+ - tpslOrder: TP/SL order
188
+ - StopOrder: Stop order
189
+ triggerPrice (Optional[str]): Trigger price
190
+ triggerBy (Optional[str]): Trigger basis
191
+ orderIv (Optional[str]): Order volatility
192
+ positionIdx (Optional[int]): Position index
193
+ - Required for futures (linear/inverse) trading
194
+ - 1: Long position
195
+ - 2: Short position
196
+ - positionIdx is not used for spot trading
197
+ takeProfit (Optional[str]): Take profit price
198
+ stopLoss (Optional[str]): Stop loss price
199
+ tpTriggerBy (Optional[str]): Take profit trigger basis
200
+ slTriggerBy (Optional[str]): Stop loss trigger basis
201
+ tpLimitPrice (Optional[str]): Take profit limit price
202
+ slLimitPrice (Optional[str]): Stop loss limit price
203
+ tpOrderType (Optional[str]): Take profit order type (Market, Limit)
204
+ slOrderType (Optional[str]): Stop loss order type (Market, Limit)
205
+
206
+ Returns:
207
+ Dict: Order result
208
+
209
+ Example:
210
+ # Spot trading (SPOT account balance required)
211
+ place_order("spot", "BTCUSDT", "Buy", "Market", "10") # Buy market price for 10 USDT
212
+ place_order("spot", "BTCUSDT", "Sell", "Market", "0.000100") # Sell market price for 0.0001 BTC
213
+ place_order("spot", "BTCUSDT", "Buy", "Limit", "0.000100", price="50000") # Buy limit order for 0.0001 BTC
214
+
215
+ # Spot trading - limit order + TP/SL
216
+ place_order("spot", "BTCUSDT", "Buy", "Limit", "0.000100", price="50000",
217
+ takeProfit="55000", stopLoss="45000", # TP/SL setting
218
+ tpOrderType="Market", slOrderType="Market") # Execute TP/SL as market order
219
+
220
+ # Futures trading
221
+ place_order("linear", "BTCUSDT", "Buy", "Market", "0.001", positionIdx=1) # Buy market price for long position
222
+ place_order("linear", "BTCUSDT", "Sell", "Market", "0.001", positionIdx=2) # Sell market price for short position
223
+
224
+ Notes:
225
+ 1. Spot trading order quantity restrictions:
226
+ - Minimum order quantity: 0.000011 BTC
227
+ - Minimum order amount: 5 USDT
228
+ - BTC quantity is only allowed up to 6 decimal places (e.g., 0.000100 O, 0.0001234 X)
229
+ 2. Pay attention to unit when buying/selling at market price:
230
+ - Buying: qty should be input in USDT units (e.g., "10" = 10 USDT)
231
+ - Selling: qty should be input in BTC units (e.g., "0.000100" = 0.0001 BTC)
232
+ 3. Futures trading requires positionIdx:
233
+ - Long position: positionIdx=1
234
+ - Short position: positionIdx=2
235
+ 4. positionIdx is not used for spot trading
236
+
237
+ Reference site:
238
+ https://bybit-exchange.github.io/docs/v5/order/create-order
239
+ """
240
+ try:
241
+ # Default settings
242
+ if timeInForce is None:
243
+ timeInForce = "IOC" if orderType == "Market" else "GTC"
244
+ if orderFilter is None:
245
+ orderFilter = "Order"
246
+ if isLeverage is None:
247
+ isLeverage = 0
248
+
249
+ # Check positionIdx for futures trading
250
+ if category in ["linear", "inverse"]:
251
+ if not positionIdx or positionIdx not in ["1", "2"]:
252
+ return {"error": "positionIdx is required for futures trading (1: Long position, 2: Short position)"}
253
+
254
+ # Ignore positionIdx for spot trading
255
+ if category == "spot":
256
+ positionIdx = None
257
+
258
+ # Prepare request data
259
+ request_data = {
260
+ "category": category,
261
+ "symbol": symbol,
262
+ "side": side,
263
+ "orderType": orderType,
264
+ "qty": qty,
265
+ "timeInForce": timeInForce,
266
+ "orderFilter": orderFilter,
267
+ "isLeverage": isLeverage
268
+ }
269
+
270
+ # Add optional parameters
271
+ if price is not None:
272
+ request_data["price"] = price
273
+ if orderLinkId is not None:
274
+ request_data["orderLinkId"] = orderLinkId
275
+ if triggerPrice is not None:
276
+ request_data["triggerPrice"] = triggerPrice
277
+ if triggerBy is not None:
278
+ request_data["triggerBy"] = triggerBy
279
+ if orderIv is not None:
280
+ request_data["orderIv"] = orderIv
281
+ if positionIdx is not None:
282
+ request_data["positionIdx"] = positionIdx
283
+ if takeProfit is not None:
284
+ request_data["takeProfit"] = takeProfit
285
+ if stopLoss is not None:
286
+ request_data["stopLoss"] = stopLoss
287
+ if tpTriggerBy is not None:
288
+ request_data["tpTriggerBy"] = tpTriggerBy
289
+ if slTriggerBy is not None:
290
+ request_data["slTriggerBy"] = slTriggerBy
291
+ if tpLimitPrice is not None:
292
+ request_data["tpLimitPrice"] = tpLimitPrice
293
+ if slLimitPrice is not None:
294
+ request_data["slLimitPrice"] = slLimitPrice
295
+ if tpOrderType is not None:
296
+ request_data["tpOrderType"] = tpOrderType
297
+ if slOrderType is not None:
298
+ request_data["slOrderType"] = slOrderType
299
+
300
+ # Execute order
301
+ result = self.client.place_order(**request_data)
302
+
303
+ # Check minimum order quantity/amount
304
+ if isinstance(result, dict) and "error" in result:
305
+ if "min_qty" in result and "min_amt" in result:
306
+ # Minimum order quantity/amount verification failed
307
+ logger.error(f"Order execution failed: {result['error']}")
308
+ return {
309
+ "error": f"{result['error']} (Minimum order quantity: {result['min_qty']} {symbol.replace('USDT', '')}, Minimum order amount: {result['min_amt']} USDT)"
310
+ }
311
+ else:
312
+ logger.error(f"Order execution failed: {result['error']}")
313
+ return {"error": result['error']}
314
+ elif result.get("retCode") != 0:
315
+ logger.error(f"Order execution failed: {result.get('retMsg')}")
316
+ return {"error": result.get("retMsg")}
317
+ return result
318
+ except Exception as e:
319
+ logger.error(f"Order execution failed: {e}", exc_info=True)
320
+ return {"error": str(e)}
321
+
322
+ def cancel_order(self, category: str, symbol: str, orderId: Optional[str] = None,
323
+ orderLinkId: Optional[str] = None, orderFilter: Optional[str] = None) -> Dict:
324
+ """
325
+ Cancel order
326
+
327
+ Args:
328
+ category (str): Category (spot, linear, inverse, etc.)
329
+ symbol (str): Symbol (e.g., BTCUSDT)
330
+ orderId (Optional[str]): Order ID
331
+ orderLinkId (Optional[str]): Order link ID
332
+ orderFilter (Optional[str]): Order filter
333
+
334
+ Returns:
335
+ Dict: Cancel result
336
+ """
337
+ return self.client.cancel_order(
338
+ category=category,
339
+ symbol=symbol,
340
+ orderId=orderId,
341
+ orderLinkId=orderLinkId,
342
+ orderFilter=orderFilter
343
+ )
344
+
345
+ def get_order_history(self, category: str, symbol: Optional[str] = None,
346
+ orderId: Optional[str] = None, orderLinkId: Optional[str] = None,
347
+ orderFilter: Optional[str] = None, orderStatus: Optional[str] = None,
348
+ startTime: Optional[int] = None, endTime: Optional[int] = None,
349
+ limit: int = 50) -> Dict:
350
+ """
351
+ Get order history
352
+
353
+ Args:
354
+ category (str): Category (spot, linear, inverse, etc.)
355
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
356
+ orderId (Optional[str]): Order ID
357
+ orderLinkId (Optional[str]): Order link ID
358
+ orderFilter (Optional[str]): Order filter
359
+ orderStatus (Optional[str]): Order status
360
+ startTime (Optional[int]): Start time in milliseconds
361
+ endTime (Optional[int]): End time in milliseconds
362
+ limit (int): Number of orders to retrieve
363
+
364
+ Returns:
365
+ Dict: Order history
366
+ """
367
+ return self.client.get_order_history(
368
+ category=category,
369
+ symbol=symbol,
370
+ orderId=orderId,
371
+ orderLinkId=orderLinkId,
372
+ orderFilter=orderFilter,
373
+ orderStatus=orderStatus,
374
+ startTime=startTime,
375
+ endTime=endTime,
376
+ limit=limit
377
+ )
378
+
379
+ def get_open_orders(self, category: str, symbol: Optional[str] = None,
380
+ orderId: Optional[str] = None, orderLinkId: Optional[str] = None,
381
+ orderFilter: Optional[str] = None, limit: int = 50) -> Dict:
382
+ """
383
+ Get open orders
384
+
385
+ Args:
386
+ category (str): Category (spot, linear, inverse, etc.)
387
+ symbol (Optional[str]): Symbol (e.g., BTCUSDT)
388
+ orderId (Optional[str]): Order ID
389
+ orderLinkId (Optional[str]): Order link ID
390
+ orderFilter (Optional[str]): Order filter
391
+ limit (int): Number of orders to retrieve
392
+
393
+ Returns:
394
+ Dict: Open orders
395
+ """
396
+ return self.client.get_open_orders(
397
+ category=category,
398
+ symbol=symbol,
399
+ orderId=orderId,
400
+ orderLinkId=orderLinkId,
401
+ orderFilter=orderFilter,
402
+ limit=limit
403
+ )
404
+
405
+ # Leverage related methods
406
+ def set_leverage(self, category: str, symbol: str, buyLeverage: str,
407
+ sellLeverage: str) -> Dict:
408
+ """
409
+ Set leverage
410
+
411
+ Args:
412
+ category (str): Category (spot, linear, inverse, etc.)
413
+ symbol (str): Symbol (e.g., BTCUSDT)
414
+ buyLeverage (str): Buy leverage
415
+ sellLeverage (str): Sell leverage
416
+
417
+ Returns:
418
+ Dict: Setting result
419
+ """
420
+ return self.client.set_leverage(
421
+ category=category,
422
+ symbol=symbol,
423
+ buyLeverage=buyLeverage,
424
+ sellLeverage=sellLeverage
425
+ )
426
+
427
+ def set_trading_stop(self, category: str, symbol: str,
428
+ takeProfit: Optional[str] = None,
429
+ stopLoss: Optional[str] = None,
430
+ trailingStop: Optional[str] = None,
431
+ positionIdx: Optional[int] = None) -> Dict:
432
+ """
433
+ Set trading stop
434
+
435
+ Args:
436
+ category (str): Category (spot, linear, inverse, etc.)
437
+ symbol (str): Symbol (e.g., BTCUSDT)
438
+ takeProfit (Optional[str]): Take profit price
439
+ stopLoss (Optional[str]): Stop loss price
440
+ trailingStop (Optional[str]): Trailing stop
441
+ positionIdx (Optional[int]): Position index
442
+
443
+ Returns:
444
+ Dict: Setting result
445
+ """
446
+ return self.client.set_trading_stop(
447
+ category=category,
448
+ symbol=symbol,
449
+ takeProfit=takeProfit,
450
+ stopLoss=stopLoss,
451
+ trailingStop=trailingStop,
452
+ positionIdx=positionIdx
453
+ )
454
+
455
+ def set_margin_mode(self, category: str, symbol: str,
456
+ tradeMode: int, buyLeverage: str,
457
+ sellLeverage: str) -> Dict:
458
+ """
459
+ Set margin mode
460
+
461
+ Args:
462
+ category (str): Category (spot, linear, inverse, etc.)
463
+ symbol (str): Symbol (e.g., BTCUSDT)
464
+ tradeMode (int): Trading mode (0: Isolated, 1: Cross)
465
+ buyLeverage (str): Buying leverage
466
+ sellLeverage (str): Selling leverage
467
+
468
+ Returns:
469
+ Dict: Setting result
470
+ """
471
+ return self.client.set_margin_mode(
472
+ category=category,
473
+ symbol=symbol,
474
+ tradeMode=tradeMode,
475
+ buyLeverage=buyLeverage,
476
+ sellLeverage=sellLeverage
477
+ )
478
+
479
+ # Utility methods
480
+ def get_api_key_information(self) -> Dict:
481
+ """
482
+ Get API key information
483
+
484
+ Returns:
485
+ Dict: API key information
486
+ """
487
+ return self.client.get_api_key_information()
488
+
489
+ def get_instruments_info(self, category: str, symbol: str,
490
+ status: Optional[str] = None, baseCoin: Optional[str] = None) -> Dict:
491
+ """
492
+ Get exchange information
493
+
494
+ Args:
495
+ category (str): Category (spot, linear, inverse, etc.)
496
+ symbol (str): Symbol (e.g., BTCUSDT)
497
+ status (Optional[str]): Status
498
+ baseCoin (Optional[str]): Base coin
499
+
500
+ Returns:
501
+ Dict: Exchange information
502
+ """
503
+ return self.client.get_instruments_info(
504
+ category=category,
505
+ symbol=symbol,
506
+ status=status,
507
+ baseCoin=baseCoin
508
+ )
509
+
510
+
511
+ if __name__ == "__main__":
512
+ # Example usage
513
+ bybit_service = BybitService()
514
+ # Example: Get K-line data
515
+ kline_data = bybit_service.get_kline(
516
+ category='spot',
517
+ symbol='BTCUSDT',
518
+ interval='1',
519
+ limit=10
520
+ )
521
+ print("K-line Data:")
522
+ print(kline_data)
523
+
524
+ # Example: Get Orderbook
525
+ # orderbook = bybit_service.get_orderbook(category='spot', symbol='BTCUSDT', limit=5)
526
+ # print("\nOrderbook Data:")
527
+ # print(orderbook)
528
+
529
+ # Removed get_talib_kline example usage
530
+ # data = bybit_service.get_talib_kline(...)
531
+ # print(data)