wiz-trader 0.8.0__py3-none-any.whl → 0.10.0__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.
wiz_trader/__init__.py CHANGED
@@ -3,6 +3,6 @@
3
3
  from .quotes import QuotesClient
4
4
  from .apis import WizzerClient
5
5
 
6
- __version__ = "0.8.0"
6
+ __version__ = "0.10.0"
7
7
 
8
8
  __all__ = ["QuotesClient", "WizzerClient"]
wiz_trader/apis/client.py CHANGED
@@ -23,7 +23,77 @@ class WizzerClient:
23
23
  log_level (str): Logging level. Options: "error", "info", "debug".
24
24
  strategy_id (str): Default strategy ID to use if not provided in methods.
25
25
  """
26
+
27
+ # Constants
28
+ # Transaction types
29
+ TRANSACTION_TYPE_BUY = "BUY"
30
+ TRANSACTION_TYPE_SELL = "SELL"
31
+
32
+ # Product types
33
+ PRODUCT_CNC = "CNC" # Cash and Carry
34
+ PRODUCT_MIS = "MIS" # Margin Intraday Square-off
35
+ PRODUCT_NRML = "NRML" # Normal / Overnight Futures and Options
36
+
37
+ # Order types
38
+ ORDER_TYPE_MARKET = "MARKET"
39
+ ORDER_TYPE_LIMIT = "LIMIT"
40
+ ORDER_TYPE_SL = "SL" # Stop Loss
41
+ ORDER_TYPE_SLM = "SL-M" # Stop Loss Market
42
+
43
+ # Validity types
44
+ VALIDITY_DAY = "DAY"
45
+ VALIDITY_IOC = "IOC" # Immediate or Cancel
46
+ VALIDITY_GTT = "GTT" # Good Till Triggered
47
+
48
+ # Variety types
49
+ VARIETY_REGULAR = "REGULAR"
50
+ VARIETY_AMO = "AMO" # After Market Order
51
+ VARIETY_BO = "BO" # Bracket Order
52
+ VARIETY_CO = "CO" # Cover Order
53
+
54
+ # Exchanges
55
+ EXCHANGE_NSE = "NSE" # National Stock Exchange
56
+ EXCHANGE_BSE = "BSE" # Bombay Stock Exchange
57
+ EXCHANGE_WZR = "WZR" # Wizzer Exchange (for baskets)
58
+
59
+ # Segments
60
+ SEGMENT_NSE_CM = "NSECM" # NSE Cash Market
61
+ SEGMENT_BSE_CM = "BSECM" # BSE Cash Market
62
+ SEGMENT_NSE_FO = "NSEFO" # NSE Futures and Options
63
+ SEGMENT_WZREQ = "WZREQ" # Wizzer Basket Segment
26
64
 
65
+ # URIs to various API endpoints
66
+ _routes = {
67
+ # Order related endpoints
68
+ "order.place": "/orders",
69
+ "order.modify": "/orders/{order_id}",
70
+ "order.cancel": "/orders/{order_id}",
71
+ "order.info": "/orders/{order_id}",
72
+
73
+ # Basket order endpoints
74
+ "basket.order.place": "/orders/basket",
75
+ "basket.order.exit": "/orders/basket/exit",
76
+ "basket.order.modify": "/orders/basket/{order_id}",
77
+
78
+ # Portfolio and position management
79
+ "portfolio.positions": "/portfolios/positions",
80
+ "portfolio.positions.exit.all": "/portfolios/positions/exit/all",
81
+ "portfolio.positions.exit.strategy": "/portfolios/positions/exit/strategies/{strategy_id}",
82
+ "portfolio.holdings": "/portfolios/holdings",
83
+
84
+ # Basket management
85
+ "basket.create": "/baskets",
86
+ "basket.list": "/baskets",
87
+ "basket.info": "/baskets/{basket_id}",
88
+ "basket.instruments": "/baskets/{basket_id}/instruments",
89
+ "basket.rebalance": "/baskets/rebalance",
90
+
91
+ # Data hub endpoints
92
+ "datahub.indices": "/datahub/indices",
93
+ "datahub.index.components": "/datahub/index/components",
94
+ "datahub.historical.ohlcv": "/datahub/historical/ohlcv",
95
+ }
96
+
27
97
  def __init__(
28
98
  self,
29
99
  base_url: Optional[str] = None,
@@ -90,7 +160,6 @@ class WizzerClient:
90
160
  Returns:
91
161
  List[Dict[str, Any]]: List of index information.
92
162
  """
93
- endpoint = "/datahub/indices"
94
163
  params = {}
95
164
 
96
165
  if trading_symbol:
@@ -99,7 +168,7 @@ class WizzerClient:
99
168
  params["exchange"] = exchange
100
169
 
101
170
  logger.debug("Fetching indices with params: %s", params)
102
- response = self._make_request("GET", endpoint, params=params)
171
+ response = self._make_request("GET", self._routes["datahub.indices"], params=params)
103
172
  return response
104
173
 
105
174
  def get_index_components(self, trading_symbol: str, exchange: str) -> List[Dict[str, Any]]:
@@ -113,14 +182,13 @@ class WizzerClient:
113
182
  Returns:
114
183
  List[Dict[str, Any]]: List of component stocks in the index.
115
184
  """
116
- endpoint = "/datahub/index/components"
117
185
  params = {
118
186
  "tradingSymbol": trading_symbol,
119
187
  "exchange": exchange
120
188
  }
121
189
 
122
190
  logger.debug("Fetching index components with params: %s", params)
123
- response = self._make_request("GET", endpoint, params=params)
191
+ response = self._make_request("GET", self._routes["datahub.index.components"], params=params)
124
192
  return response
125
193
 
126
194
  def get_historical_ohlcv(
@@ -144,7 +212,6 @@ class WizzerClient:
144
212
  Returns:
145
213
  List[Dict[str, Any]]: Historical data for requested instruments.
146
214
  """
147
- endpoint = "/datahub/historical/ohlcv"
148
215
  data = {
149
216
  "instruments": instruments,
150
217
  "startDate": start_date,
@@ -154,7 +221,7 @@ class WizzerClient:
154
221
  }
155
222
 
156
223
  logger.debug("Fetching historical OHLCV with data: %s", data)
157
- response = self._make_request("POST", endpoint, json=data)
224
+ response = self._make_request("POST", self._routes["datahub.historical.ohlcv"], json=data)
158
225
  return response
159
226
 
160
227
  # ===== ORDER MANAGEMENT METHODS =====
@@ -165,13 +232,13 @@ class WizzerClient:
165
232
  trading_symbol: str,
166
233
  transaction_type: str,
167
234
  quantity: int,
168
- order_type: str = "MARKET",
169
- product: str = "CNC",
235
+ order_type: str = None,
236
+ product: str = None,
170
237
  price: float = 0,
171
238
  trigger_price: float = 0,
172
239
  disclosed_qty: int = 0,
173
- validity: str = "DAY",
174
- variety: str = "REGULAR",
240
+ validity: str = None,
241
+ variety: str = None,
175
242
  stoploss: float = 0,
176
243
  target: float = 0,
177
244
  segment: Optional[str] = None,
@@ -187,13 +254,13 @@ class WizzerClient:
187
254
  trading_symbol (str): Symbol of the instrument.
188
255
  transaction_type (str): "BUY" or "SELL".
189
256
  quantity (int): Number of shares to trade.
190
- order_type (str, optional): Order type (e.g., "MARKET", "LIMIT"). Defaults to "MARKET".
191
- product (str, optional): Product code (e.g., "CNC" for delivery). Defaults to "CNC".
257
+ order_type (str, optional): Order type (e.g., "MARKET", "LIMIT"). Defaults to MARKET.
258
+ product (str, optional): Product code (e.g., "CNC" for delivery). Defaults to CNC.
192
259
  price (float, optional): Price for limit orders. Defaults to 0.
193
260
  trigger_price (float, optional): Trigger price for stop orders. Defaults to 0.
194
261
  disclosed_qty (int, optional): Disclosed quantity. Defaults to 0.
195
- validity (str, optional): Order validity (e.g., "DAY", "IOC"). Defaults to "DAY".
196
- variety (str, optional): Order variety. Defaults to "REGULAR".
262
+ validity (str, optional): Order validity (e.g., "DAY", "IOC"). Defaults to DAY.
263
+ variety (str, optional): Order variety. Defaults to REGULAR.
197
264
  stoploss (float, optional): Stop loss price. Defaults to 0.
198
265
  target (float, optional): Target price. Defaults to 0.
199
266
  segment (Optional[str], optional): Market segment. If None, determined from exchange.
@@ -204,11 +271,27 @@ class WizzerClient:
204
271
  Returns:
205
272
  Dict[str, Any]: Order response containing orderId.
206
273
  """
207
- endpoint = "/orders"
274
+ endpoint = self._routes["order.place"]
275
+
276
+ # Set default values from constants if not provided
277
+ if order_type is None:
278
+ order_type = self.ORDER_TYPE_MARKET
279
+ if product is None:
280
+ product = self.PRODUCT_CNC
281
+ if validity is None:
282
+ validity = self.VALIDITY_DAY
283
+ if variety is None:
284
+ variety = self.VARIETY_REGULAR
208
285
 
209
286
  # Determine segment if not provided
210
287
  if not segment:
211
288
  segment = f"{exchange}CM"
289
+ # If exchange is NSE, use the NSE_CM constant
290
+ if exchange == self.EXCHANGE_NSE:
291
+ segment = self.SEGMENT_NSE_CM
292
+ # If exchange is BSE, use the BSE_CM constant
293
+ elif exchange == self.EXCHANGE_BSE:
294
+ segment = self.SEGMENT_BSE_CM
212
295
 
213
296
  # Get strategy information
214
297
  strategy_info = self._get_strategy(strategy)
@@ -236,7 +319,7 @@ class WizzerClient:
236
319
  data["exchangeToken"] = exchange_token
237
320
 
238
321
  logger.debug("Placing order: %s", data)
239
- return self._make_request("POST", endpoint, json=data)
322
+ return self._make_request("POST", self._routes["order.place"], json=data)
240
323
 
241
324
  def modify_order(
242
325
  self,
@@ -253,7 +336,7 @@ class WizzerClient:
253
336
  Returns:
254
337
  Dict[str, Any]: Order response containing orderId.
255
338
  """
256
- endpoint = f"/orders/{order_id}"
339
+ endpoint = self._routes["order.modify"].format(order_id=order_id)
257
340
 
258
341
  logger.debug("Modifying order %s with params: %s", order_id, params)
259
342
  return self._make_request("PATCH", endpoint, json=params)
@@ -268,7 +351,7 @@ class WizzerClient:
268
351
  Returns:
269
352
  Dict[str, Any]: Response with the cancelled order ID.
270
353
  """
271
- endpoint = f"/orders/{order_id}"
354
+ endpoint = self._routes["order.cancel"].format(order_id=order_id)
272
355
 
273
356
  logger.debug("Cancelling order: %s", order_id)
274
357
  return self._make_request("DELETE", endpoint)
@@ -283,7 +366,7 @@ class WizzerClient:
283
366
  Returns:
284
367
  Dict[str, Any]: Order details.
285
368
  """
286
- endpoint = f"/orders/{order_id}"
369
+ endpoint = self._routes["order.info"].format(order_id=order_id)
287
370
 
288
371
  logger.debug("Fetching order: %s", order_id)
289
372
  return self._make_request("GET", endpoint)
@@ -299,7 +382,7 @@ class WizzerClient:
299
382
  Returns:
300
383
  List[Dict[str, Any]]: List of positions matching the filter criteria.
301
384
  """
302
- endpoint = "/portfolios/positions"
385
+ endpoint = self._routes["portfolio.positions"]
303
386
  params = {}
304
387
 
305
388
  if position_status:
@@ -338,7 +421,7 @@ class WizzerClient:
338
421
  Returns:
339
422
  List[Dict[str, Any]]: List of holdings.
340
423
  """
341
- endpoint = "/portfolios/holdings"
424
+ endpoint = self._routes["portfolio.holdings"]
342
425
  params = {"portfolios": portfolios}
343
426
 
344
427
  logger.debug("Fetching holdings for portfolio: %s", portfolios)
@@ -368,7 +451,7 @@ class WizzerClient:
368
451
  Returns:
369
452
  Dict[str, Any]: Basket information.
370
453
  """
371
- endpoint = "/baskets"
454
+ endpoint = self._routes["basket.create"]
372
455
 
373
456
  # Set defaults
374
457
  if capital is None:
@@ -392,7 +475,7 @@ class WizzerClient:
392
475
  Returns:
393
476
  List[Dict[str, Any]]: List of baskets.
394
477
  """
395
- endpoint = "/baskets"
478
+ endpoint = self._routes["basket.list"]
396
479
 
397
480
  logger.debug("Fetching baskets")
398
481
  return self._make_request("GET", endpoint)
@@ -407,7 +490,7 @@ class WizzerClient:
407
490
  Returns:
408
491
  Dict[str, Any]: Basket information.
409
492
  """
410
- endpoint = f"/baskets/{basket_id}"
493
+ endpoint = self._routes["basket.info"].format(basket_id=basket_id)
411
494
 
412
495
  logger.debug("Fetching basket: %s", basket_id)
413
496
  return self._make_request("GET", endpoint)
@@ -422,7 +505,7 @@ class WizzerClient:
422
505
  Returns:
423
506
  List[Dict[str, Any]]: List of instruments in the basket.
424
507
  """
425
- endpoint = f"/baskets/{basket_id}/instruments"
508
+ endpoint = self._routes["basket.instruments"].format(basket_id=basket_id)
426
509
 
427
510
  logger.debug("Fetching instruments for basket: %s", basket_id)
428
511
  return self._make_request("GET", endpoint)
@@ -433,15 +516,15 @@ class WizzerClient:
433
516
  transaction_type: str,
434
517
  quantity: float,
435
518
  price: float = 0,
436
- order_type: str = "MARKET",
437
- product: str = "CNC",
438
- validity: str = "DAY",
519
+ order_type: str = None,
520
+ product: str = None,
521
+ validity: str = None,
439
522
  exchange_token: Optional[int] = None,
440
523
  trigger_price: float = 0,
441
524
  stoploss: float = 0,
442
525
  target: float = 0,
443
526
  broker: str = "wizzer",
444
- variety: str = "REGULAR",
527
+ variety: str = None,
445
528
  strategy: Optional[Dict[str, str]] = None,
446
529
  disclosed_qty: int = 0,
447
530
  sl_applied_level: Optional[str] = None
@@ -454,15 +537,15 @@ class WizzerClient:
454
537
  transaction_type (str): "BUY" or "SELL".
455
538
  quantity (float): Quantity/units of the basket.
456
539
  price (float, optional): Price for limit orders. Defaults to 0.
457
- order_type (str, optional): Order type. Defaults to "MARKET".
458
- product (str, optional): Product code. Defaults to "CNC".
459
- validity (str, optional): Order validity. Defaults to "DAY".
540
+ order_type (str, optional): Order type. Defaults to MARKET.
541
+ product (str, optional): Product code. Defaults to CNC.
542
+ validity (str, optional): Order validity. Defaults to DAY.
460
543
  exchange_token (Optional[int], optional): Exchange token for the basket.
461
544
  trigger_price (float, optional): Trigger price. Defaults to 0.
462
545
  stoploss (float, optional): Stop loss price. Defaults to 0.
463
546
  target (float, optional): Target price. Defaults to 0.
464
547
  broker (str, optional): Broker code. Defaults to "wizzer".
465
- variety (str, optional): Order variety. Defaults to "REGULAR".
548
+ variety (str, optional): Order variety. Defaults to REGULAR.
466
549
  strategy (Optional[Dict[str, str]], optional): Strategy information. If None, uses default.
467
550
  disclosed_qty (int, optional): Disclosed quantity. Defaults to 0.
468
551
  sl_applied_level (Optional[str], optional): Stop loss applied level (e.g., "basket").
@@ -470,14 +553,24 @@ class WizzerClient:
470
553
  Returns:
471
554
  Dict[str, Any]: Order response containing orderId.
472
555
  """
473
- endpoint = "/orders/basket"
556
+ endpoint = self._routes["order.basket"]
557
+
558
+ # Set default values from constants if not provided
559
+ if order_type is None:
560
+ order_type = self.ORDER_TYPE_MARKET
561
+ if product is None:
562
+ product = self.PRODUCT_CNC
563
+ if validity is None:
564
+ validity = self.VALIDITY_DAY
565
+ if variety is None:
566
+ variety = self.VARIETY_REGULAR
474
567
 
475
568
  # Get strategy information
476
569
  strategy_info = self._get_strategy(strategy)
477
570
 
478
571
  data = {
479
572
  "tradingSymbol": trading_symbol,
480
- "exchange": "WZR",
573
+ "exchange": self.EXCHANGE_WZR,
481
574
  "transactionType": transaction_type,
482
575
  "qty": quantity,
483
576
  "price": price,
@@ -490,7 +583,7 @@ class WizzerClient:
490
583
  "broker": broker,
491
584
  "variety": variety,
492
585
  "strategy": strategy_info,
493
- "segment": "WZREQ",
586
+ "segment": self.SEGMENT_WZREQ,
494
587
  "disclosedQty": disclosed_qty
495
588
  }
496
589
 
@@ -528,7 +621,7 @@ class WizzerClient:
528
621
  Returns:
529
622
  Dict[str, Any]: Order response containing orderId.
530
623
  """
531
- endpoint = "/orders/basket/exit"
624
+ endpoint = self._routes["order.basket_exit"]
532
625
 
533
626
  # Build base data
534
627
  data = {
@@ -546,17 +639,17 @@ class WizzerClient:
546
639
 
547
640
  # Set defaults if not in kwargs
548
641
  defaults = {
549
- "orderType": "MARKET",
550
- "product": "CNC",
551
- "validity": "DAY",
642
+ "orderType": self.ORDER_TYPE_MARKET,
643
+ "product": self.PRODUCT_CNC,
644
+ "validity": self.VALIDITY_DAY,
552
645
  "disclosedQty": 0,
553
646
  "price": 0,
554
- "variety": "REGULAR",
647
+ "variety": self.VARIETY_REGULAR,
555
648
  "stoploss": 0,
556
649
  "broker": "wizzer",
557
650
  "triggerPrice": 0,
558
651
  "target": 0,
559
- "segment": "WZREQ"
652
+ "segment": self.SEGMENT_WZREQ
560
653
  }
561
654
 
562
655
  for key, value in defaults.items():
@@ -581,7 +674,7 @@ class WizzerClient:
581
674
  Returns:
582
675
  Dict[str, Any]: Order response containing orderId.
583
676
  """
584
- endpoint = f"/orders/basket/{order_id}"
677
+ endpoint = self._routes["order.basket_modify"].format(order_id=order_id)
585
678
 
586
679
  logger.debug("Modifying basket order %s with params: %s", order_id, params)
587
680
  return self._make_request("PATCH", endpoint, json=params)
@@ -601,7 +694,7 @@ class WizzerClient:
601
694
  Returns:
602
695
  Dict[str, Any]: Rebalance response.
603
696
  """
604
- endpoint = "/baskets/rebalance"
697
+ endpoint = self._routes["basket.rebalance"]
605
698
 
606
699
  data = {
607
700
  "tradingSymbol": trading_symbol,
@@ -610,6 +703,45 @@ class WizzerClient:
610
703
 
611
704
  logger.debug("Rebalancing basket %s with instruments: %s", trading_symbol, instruments)
612
705
  return self._make_request("POST", endpoint, json=data)
706
+
707
+ def exit_all_positions(self) -> Dict[str, Any]:
708
+ """
709
+ Exit all positions across all strategies.
710
+
711
+ This method sends a request to close all open positions for the user.
712
+
713
+ Returns:
714
+ Dict[str, Any]: Response with summary of success and failure counts.
715
+ """
716
+ endpoint = self._routes["portfolio.positions_exit_all"]
717
+
718
+ logger.debug("Exiting all positions")
719
+ return self._make_request("POST", endpoint)
720
+
721
+ def exit_strategy_positions(self, strategy_id: Optional[str] = None) -> Dict[str, Any]:
722
+ """
723
+ Exit all positions for a specific strategy.
724
+
725
+ Args:
726
+ strategy_id (Optional[str]): ID of the strategy to exit positions for.
727
+ If None, uses the default strategy ID.
728
+
729
+ Returns:
730
+ Dict[str, Any]: Response with summary of success and failure counts.
731
+
732
+ Raises:
733
+ ValueError: If no strategy_id is provided and no default is set.
734
+ """
735
+ # Get strategy ID (either from parameter or default)
736
+ if not strategy_id:
737
+ if not self.strategy_id:
738
+ raise ValueError("Strategy ID must be provided either as a parameter or set in .env (WZ__STRATEGY_ID)")
739
+ strategy_id = self.strategy_id
740
+
741
+ endpoint = self._routes["portfolio.positions_exit_strategies"].format(strategy_id=strategy_id)
742
+
743
+ logger.debug("Exiting all positions for strategy: %s", strategy_id)
744
+ return self._make_request("POST", endpoint)
613
745
 
614
746
  def _make_request(
615
747
  self,