quantplay 2.0.151__tar.gz → 2.0.160__tar.gz

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.
Files changed (70) hide show
  1. {quantplay-2.0.151 → quantplay-2.0.160}/PKG-INFO +1 -1
  2. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/icici_direct.py +32 -21
  3. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/kotak.py +111 -17
  4. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay.egg-info/PKG-INFO +1 -1
  5. {quantplay-2.0.151 → quantplay-2.0.160}/setup.py +1 -1
  6. {quantplay-2.0.151 → quantplay-2.0.160}/README.md +0 -0
  7. {quantplay-2.0.151 → quantplay-2.0.160}/pyproject.toml +0 -0
  8. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/__init__.py +0 -0
  9. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/__init__.py +0 -0
  10. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/aliceblue.py +0 -0
  11. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/angelone.py +0 -0
  12. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/auto_login/__init__.py +0 -0
  13. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/auto_login/aliceblue.py +0 -0
  14. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/breeze/__init__.py +0 -0
  15. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/breeze/breeze_utils.py +0 -0
  16. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/broker_factory.py +0 -0
  17. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/dhan.py +0 -0
  18. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/finvasia_utils/__init__.py +0 -0
  19. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
  20. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/five_paisa.py +0 -0
  21. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/flattrade.py +0 -0
  22. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/ft_utils/__init__.py +0 -0
  23. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
  24. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/ft_utils/ft_noren.py +0 -0
  25. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/generics/__init__.py +0 -0
  26. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/generics/broker.py +0 -0
  27. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/iifl_xts.py +0 -0
  28. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/jainam_xts.py +0 -0
  29. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/kite_utils.py +0 -0
  30. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/motilal.py +0 -0
  31. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/noren.py +0 -0
  32. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/shoonya.py +0 -0
  33. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/uplink/__init__.py +0 -0
  34. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/uplink/uplink_utils.py +0 -0
  35. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/upstox.py +0 -0
  36. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/xts.py +0 -0
  37. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/xts_utils/Connect.py +0 -0
  38. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/xts_utils/Exception.py +0 -0
  39. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
  40. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/xts_utils/__init__.py +0 -0
  41. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/broker/zerodha.py +0 -0
  42. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/exception/__init__.py +0 -0
  43. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/exception/exceptions.py +0 -0
  44. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/__init__.py +0 -0
  45. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/broker.py +0 -0
  46. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/broker_response.py +0 -0
  47. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/generics.py +0 -0
  48. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/instrument_data.py +0 -0
  49. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/model/order_event.py +0 -0
  50. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/py.typed +0 -0
  51. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/__init__.py +0 -0
  52. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/caching.py +0 -0
  53. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/constant.py +0 -0
  54. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/exchange.py +0 -0
  55. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/number_utils.py +0 -0
  56. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/pickle_utils.py +0 -0
  57. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/utils/selenium_utils.py +0 -0
  58. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/wrapper/__init__.py +0 -0
  59. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/wrapper/aws/__init__.py +0 -0
  60. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay/wrapper/aws/s3.py +0 -0
  61. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay.egg-info/SOURCES.txt +0 -0
  62. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay.egg-info/dependency_links.txt +0 -0
  63. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay.egg-info/requires.txt +0 -0
  64. {quantplay-2.0.151 → quantplay-2.0.160}/quantplay.egg-info/top_level.txt +0 -0
  65. {quantplay-2.0.151 → quantplay-2.0.160}/setup.cfg +0 -0
  66. {quantplay-2.0.151 → quantplay-2.0.160}/tests/__init__.py +0 -0
  67. {quantplay-2.0.151 → quantplay-2.0.160}/tests/conftest.py +0 -0
  68. {quantplay-2.0.151 → quantplay-2.0.160}/tests/wrapper/__init__.py +0 -0
  69. {quantplay-2.0.151 → quantplay-2.0.160}/tests/wrapper/aws/__init__.py +0 -0
  70. {quantplay-2.0.151 → quantplay-2.0.160}/tests/wrapper/aws/s3_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.151
3
+ Version: 2.0.160
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -146,12 +146,12 @@ class ICICI(Broker):
146
146
 
147
147
  return self.quantplay_symbol_map[symbol]
148
148
 
149
- def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
149
+ def orders_by_exchange(self, exchange: ExchangeType):
150
150
  to_date = datetime.now().isoformat()[:19] + ".000Z"
151
151
  from_date = (datetime.now() - timedelta(hours=10)).isoformat()[:19] + ".000Z"
152
152
 
153
153
  orders: Dict[str, Any] = self.wrapper.get_order_list( # type:ignore
154
- exchange_code="NFO",
154
+ exchange_code=exchange,
155
155
  from_date=from_date,
156
156
  to_date=to_date,
157
157
  )
@@ -230,20 +230,21 @@ class ICICI(Broker):
230
230
 
231
231
  return orders_df
232
232
 
233
+ def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
234
+ nfo_orders = self.orders_by_exchange("NFO")
235
+ bfo_orders = self.orders_by_exchange("BFO")
236
+
237
+ return pl.concat([nfo_orders, bfo_orders])
238
+
233
239
  def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
234
- to_date = datetime.now().isoformat()[:19] + ".000Z"
235
- from_date = (datetime.now() - timedelta(hours=10)).isoformat()[:19] + ".000Z"
236
- positions: Dict[str, Any] = self.wrapper.get_portfolio_holdings( # type:ignore
237
- exchange_code="NFO",
238
- from_date=from_date,
239
- to_date=to_date,
240
- stock_code="",
241
- portfolio_type="",
242
- )
240
+ positions: Dict[str, Any] = self.wrapper.get_portfolio_positions() # type:ignore
241
+
243
242
  if "Success" in positions and positions["Success"] is None:
244
243
  return pl.DataFrame(schema=self.positions_schema)
245
244
  positions_df = pl.DataFrame(positions["Success"])
246
-
245
+ if "realized_profit" not in positions_df:
246
+ positions_df = positions_df.with_columns(pl.lit(0.0).alias("realized_profit"))
247
+ positions_df = positions_df.filter(pl.col("exchange_code").is_in(["NFO", "BFO"]))
247
248
  positions_df = positions_df.rename(
248
249
  {
249
250
  "exchange_code": "exchange",
@@ -252,6 +253,9 @@ class ICICI(Broker):
252
253
  "product_type": "product",
253
254
  }
254
255
  )
256
+ positions_df = positions_df.with_columns(
257
+ pl.col("quantity").cast(pl.Int32).alias("quantity")
258
+ )
255
259
  positions_df = positions_df.with_columns(
256
260
  pl.col("strike").cast(pl.Float32).alias("strike"),
257
261
  pl.col("expiry").str.strptime(pl.Date, format="%d-%b-%Y").alias("expiry"),
@@ -260,11 +264,11 @@ class ICICI(Broker):
260
264
  .when(pl.col("right") == "Put")
261
265
  .then(pl.lit("PE"))
262
266
  .alias("instrument_type"),
263
- (
264
- pl.col("unrealized_profit").cast(pl.Float32)
265
- + pl.col("realized_profit").cast(pl.Float32)
266
- ).alias("pnl"),
267
- pl.col("quantity").cast(pl.Int32).alias("quantity"),
267
+ pl.when(pl.col("action") == "Sell")
268
+ .then(abs(pl.col("quantity").cast(pl.Float32)) * -1)
269
+ .otherwise(abs(pl.col("quantity").cast(pl.Float32)))
270
+ .alias("quantity"),
271
+ pl.lit(0).alias("pnl"),
268
272
  pl.col("average_price").cast(pl.Float32).alias("average_price"),
269
273
  )
270
274
 
@@ -341,9 +345,14 @@ class ICICI(Broker):
341
345
  if tag is None:
342
346
  tag = ""
343
347
  icici_tradingsymbol = self.get_symbol(tradingsymbol)
344
- symbol_data: Dict[str, Any] = self.symbol_data[ # type:ignore
345
- f"{exchange}:{icici_tradingsymbol}"
346
- ]
348
+ try:
349
+ symbol_data: Dict[str, Any] = self.symbol_data[ # type:ignore
350
+ f"{exchange}:{icici_tradingsymbol}"
351
+ ]
352
+ except KeyError:
353
+ raise QuantplayOrderPlacementException(
354
+ f"Failed to find tradingsymbol {tradingsymbol}"
355
+ )
347
356
  icici_product = "cash"
348
357
  if exchange in ["NFO", "BFO"]:
349
358
  icici_product = "futures"
@@ -365,7 +374,7 @@ class ICICI(Broker):
365
374
  else:
366
375
  response: Dict[str, Any] = self.wrapper.place_order( # type:ignore
367
376
  stock_code=symbol_data["symbol_code"],
368
- exchange_code="NFO",
377
+ exchange_code=exchange,
369
378
  product=icici_product,
370
379
  action=transaction_type.lower(),
371
380
  order_type=order_type.lower(),
@@ -380,6 +389,8 @@ class ICICI(Broker):
380
389
  )
381
390
  if "Success" in response and "order_id" in response["Success"]:
382
391
  return response["Success"]["order_id"]
392
+ elif "Error" in response:
393
+ raise QuantplayOrderPlacementException(response["Error"])
383
394
  else:
384
395
  raise QuantplayOrderPlacementException(
385
396
  f"ICICI: order placement failed {response}"
@@ -6,6 +6,7 @@ from urllib.parse import urlencode
6
6
 
7
7
  import polars as pl
8
8
  import requests
9
+ import urllib
9
10
 
10
11
  from quantplay.broker.generics.broker import Broker
11
12
  from quantplay.model.broker import (
@@ -20,6 +21,7 @@ from quantplay.model.generics import (
20
21
  TransactionType,
21
22
  )
22
23
  from quantplay.model.order_event import OrderUpdateEvent
24
+ from quantplay.exception.exceptions import InvalidArgumentException
23
25
 
24
26
  PROD_BASE_URL = "https://gw-napi.kotaksecurities.com/"
25
27
  SESSION_PROD_BASE_URL = "https://napi.kotaksecurities.com/"
@@ -83,7 +85,7 @@ class Kotak(Broker):
83
85
  )
84
86
 
85
87
  if not session_init.ok:
86
- raise Exception("")
88
+ raise InvalidArgumentException(json.loads(session_init.content))
87
89
 
88
90
  json_resp = json.loads(session_init.text)
89
91
  self.configuration["bearer_token"] = json_resp.get("access_token")
@@ -146,6 +148,11 @@ class Kotak(Broker):
146
148
  Return the Token in a String Format
147
149
  """
148
150
 
151
+ def order_history(self, order_id: str):
152
+ body = {"nOrdNo": order_id}
153
+ order_history = self.request("order_history", body=body)["data"] # type:ignore
154
+ return pl.DataFrame(order_history)
155
+
149
156
  # **
150
157
  # ** GET Api's
151
158
  # **
@@ -187,6 +194,7 @@ class Kotak(Broker):
187
194
  pl.col("order_timestamp")
188
195
  .str.strptime(pl.Datetime(time_unit="ms"), format="%d-%b-%Y %H:%M:%S")
189
196
  .alias("order_timestamp"),
197
+ pl.col("status").str.to_uppercase().alias("status"),
190
198
  )
191
199
  orders_df = orders_df.with_columns(
192
200
  pl.col("order_timestamp").alias("update_timestamp"),
@@ -228,15 +236,79 @@ class Kotak(Broker):
228
236
  return orders_df
229
237
 
230
238
  def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
231
- positions_resp = self.request("order_book")
239
+ positions_resp = self.request("positions")
232
240
 
233
241
  if positions_resp["stat"] == "Not_Ok" and positions_resp["errMsg"] == "No Data":
234
242
  return pl.DataFrame(schema=self.positions_schema)
243
+ positions_df = pl.DataFrame(positions_resp["data"])
244
+
245
+ positions_df = positions_df.rename(
246
+ {
247
+ "trdSym": "tradingsymbol",
248
+ "buyAmt": "buy_value",
249
+ "sellAmt": "sell_value",
250
+ "prod": "product",
251
+ "tok": "token",
252
+ "exSeg": "exchange",
253
+ "optTp": "option_type",
254
+ }
255
+ )
256
+ positions_df = positions_df.with_columns(
257
+ (
258
+ pl.col("flBuyQty").fill_null(0).cast(pl.Int64)
259
+ + pl.col("cfBuyQty").fill_null(0).cast(pl.Int64)
260
+ ).alias("buy_quantity"),
261
+ (
262
+ pl.col("flSellQty").fill_null(0).cast(pl.Int64)
263
+ + pl.col("cfSellQty").fill_null(0).cast(pl.Int64)
264
+ ).alias("sell_quantity"),
265
+ pl.lit(None).alias("ltp"),
266
+ pl.lit(None).alias("pnl"),
267
+ pl.when(pl.col("exchange") == "nse_cm")
268
+ .then(pl.lit("NSE"))
269
+ .when(pl.col("exchange") == "bse_cm")
270
+ .then(pl.lit("BSE"))
271
+ .when(pl.col("exchange") == "nse_fo")
272
+ .then(pl.lit("NFO"))
273
+ .when(pl.col("exchange") == "bse_fo")
274
+ .then(pl.lit("BFO"))
275
+ .when(pl.col("exchange") == "cde_fo")
276
+ .then(pl.lit("CDS"))
277
+ .when(pl.col("exchange") == "bcs-fo")
278
+ .then(pl.lit("BCD"))
279
+ .when(pl.col("exchange") == "mcx")
280
+ .then(pl.lit("MCX"))
281
+ .otherwise(pl.col("exchange"))
282
+ .alias("exchange"),
283
+ pl.when(pl.col("option_type") == "XX")
284
+ .then(pl.lit(None))
285
+ .otherwise(pl.col("option_type"))
286
+ .alias("option_type"),
287
+ )
288
+ positions_df = positions_df.with_columns(
289
+ (pl.col("buy_quantity") - pl.col("sell_quantity")).alias("quantity")
290
+ )
291
+ positions_df = positions_df.with_columns(
292
+ (
293
+ (
294
+ pl.col("buy_value").cast(pl.Float32)
295
+ - pl.col("sell_value").cast(pl.Float32)
296
+ )
297
+ / pl.col("quantity")
298
+ ).alias("average_price")
299
+ )
300
+ positions_df = positions_df[list(self.positions_schema.keys())].cast(
301
+ self.positions_schema
302
+ )
303
+ return positions_df
304
+
305
+ def trades(self) -> pl.DataFrame:
306
+ self.request("trade_report")
235
307
 
236
308
  return pl.DataFrame(schema=self.positions_schema)
237
309
 
238
310
  def holdings(self) -> pl.DataFrame:
239
- holdings_resp = self.request("order_book")
311
+ holdings_resp = self.request("holdings")
240
312
 
241
313
  if holdings_resp["stat"] == "Not_Ok" and holdings_resp["errMsg"] == "No Data":
242
314
  return pl.DataFrame(schema=self.holidings_schema)
@@ -289,18 +361,21 @@ class Kotak(Broker):
289
361
  "qt": str(quantity),
290
362
  "rt": "DAY",
291
363
  "tp": str(trigger_price or "0"),
292
- "ts": tradingsymbol,
364
+ "ts": self.get_symbol(tradingsymbol, exchange),
293
365
  "tt": self.get_transaction_type(transaction_type),
294
366
  "ig": tag,
295
367
  }
296
368
 
297
- place_order_resp = self.request("place_order", body=place_order_body)
298
- print(place_order_resp)
369
+ place_order_resp = self.request( # type:ignore
370
+ "place_order", body=place_order_body
371
+ ) # type:ignore
299
372
 
300
373
  return ""
301
374
 
302
375
  def cancel_order(self, order_id: str, variety: str | None = None) -> None:
303
- return
376
+ body = {"on": order_id, "am": "No"}
377
+
378
+ self.request("cancel_order", body=body)
304
379
 
305
380
  def modify_order(self, order: ModifyOrderRequest) -> str:
306
381
  return ""
@@ -348,6 +423,21 @@ class Kotak(Broker):
348
423
 
349
424
  return product_map.get(product, product)
350
425
 
426
+ def get_symbol(self, symbol: str, exchange: ExchangeType | None = None):
427
+ if exchange == "NSE":
428
+ if symbol in ["NIFTY", "BANKNIFTY"]:
429
+ return symbol
430
+ if "-EQ" not in symbol:
431
+ return f"{symbol}-EQ"
432
+ else:
433
+ return symbol
434
+ if exchange == "BSE":
435
+ return symbol
436
+
437
+ if symbol not in self.quantplay_symbol_map:
438
+ return symbol
439
+ return self.quantplay_symbol_map[symbol]
440
+
351
441
  # **
352
442
  # ** Kotak Utils
353
443
  # **
@@ -375,7 +465,7 @@ class Kotak(Broker):
375
465
  "place_order": ("Orders/2.0/quick/order/rule/ms/place", "POST", False),
376
466
  "cancel_order": ("Orders/2.0/quick/order/cancel", "POST", False),
377
467
  "modify_order": ("Orders/2.0/quick/order/vr/modify", "POST", False),
378
- "order_history": ("Orders/2.0/quick/order/history", "GET", True),
468
+ "order_history": ("Orders/2.0/quick/order/history", "POST", False),
379
469
  "order_book": ("Orders/2.0/quick/user/orders", "GET", True),
380
470
  "trade_report": ("Orders/2.0/quick/user/trades", "GET", True),
381
471
  "positions": ("Orders/2.0/quick/user/positions", "GET", True),
@@ -406,23 +496,26 @@ class Kotak(Broker):
406
496
 
407
497
  query_params = {"sId": self.configuration["serverId"]}
408
498
  if item == "place_order":
409
- query_params["sId"] = "server4"
410
- request_headers["neo-fin-key"] = "neotradeapi"
499
+ request_headers.pop("accept", None)
411
500
 
412
501
  request_body = None
413
- request_params = None
414
502
 
415
503
  if params is not None:
416
504
  request_params = urlencode({**params, **query_params})
417
505
  else:
418
506
  request_params = urlencode(query_params)
419
507
 
508
+ request_body = {}
420
509
  if body is not None:
421
510
  if content_type == "application/json":
422
511
  request_body = json.dumps(body)
423
-
512
+ elif item in ["place_order"]:
513
+ request_body["jData"] = json.dumps(body)
424
514
  elif content_type == "application/x-www-form-urlencoded":
425
- request_body = {"jData": json.dumps(body)}
515
+ request_body = urllib.parse.urlencode( # type:ignore
516
+ {"jData": json.dumps(body)},
517
+ quote_via=urllib.parse.quote, # type:ignore
518
+ )
426
519
 
427
520
  resp = None
428
521
  resp_data = None
@@ -431,17 +524,18 @@ class Kotak(Broker):
431
524
  resp = requests.get(url=url, params=request_params, headers=request_headers)
432
525
 
433
526
  elif method == "POST":
527
+ url += "?" + urlencode(query_params)
528
+
434
529
  resp = requests.post(
435
- url=url,
530
+ url,
436
531
  headers=request_headers,
437
- data=request_body,
438
- params=request_params,
532
+ data=request_body, # type:ignore
439
533
  )
440
534
 
441
535
  if resp and resp.ok:
442
536
  resp_data = resp.json()
443
537
 
444
538
  if resp_data is None:
445
- raise Exception("")
539
+ raise Exception("Request Failed: No response from Kotak server")
446
540
 
447
541
  return resp_data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: quantplay
3
- Version: 2.0.151
3
+ Version: 2.0.160
4
4
  Summary: This python package will be stored in AWS CodeArtifact
5
5
  Home-page:
6
6
  Author:
@@ -21,7 +21,7 @@ requirements = [
21
21
  setup(
22
22
  name="quantplay",
23
23
  long_description=Path("README.md").read_text(),
24
- version="2.0.151",
24
+ version="2.0.160",
25
25
  setup_requires=["pytest-runner"],
26
26
  install_requires=requirements,
27
27
  tests_require=[],
File without changes
File without changes
File without changes