wiz-trader 0.39.0__py3-none-any.whl → 0.41.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.39.0"
6
+ __version__ = "0.41.0"
7
7
 
8
8
  __all__ = ["QuotesClient", "WizzerClient"]
wiz_trader/apis/client.py CHANGED
@@ -282,11 +282,19 @@ class WizzerClient:
282
282
  "analytics.marketdata.average_volume": "/analytics/marketdata/averageVolume",
283
283
  "analytics.index.max_drawdown": "/analytics/index/metrics/maxDrawdown",
284
284
  "analytics.instrument.drawdown_duration": "/analytics/instrument/metrics/drawdownDuration",
285
- "analytics.price.rolling_peak": "/analytics/analytics/price/rollingPeak",
286
- "analytics.price.rolling_mean": "/analytics/analytics/price/rollingMean",
287
- "analytics.volatility.realized": "/analytics/analytics/volatility/realized",
285
+ "analytics.price.rolling_peak": "/analytics/price/rollingPeak",
286
+ "analytics.price.rolling_mean": "/analytics/price/rollingMean",
287
+ "analytics.volatility.realized": "/analytics/volatility/realized",
288
288
  "analytics.risk.beta_90d": "/analytics/risk/beta90d",
289
289
  "analytics.risk.beta_custom": "/analytics/risk/beta",
290
+ "analytics.strategy.drawdown_max": "/analytics/strategy/drawdown/max",
291
+ "analytics.product.drawdown_max": "/analytics/product/drawdown/max",
292
+ "analytics.volatility.atr": "/analytics/volatility/atr",
293
+ "analytics.returns.simple": "/analytics/returns/simple",
294
+ "analytics.corporate.actions.events": "/analytics/corporate/actions/events",
295
+ "analytics.corporate.actions.filter": "/analytics/corporate/actions/filter",
296
+ "analytics.corporate.announcements.events": "/analytics/corporate/announcements/events",
297
+ "analytics.corporate.announcements.filter": "/analytics/corporate/announcements/filter",
290
298
 
291
299
  }
292
300
 
@@ -3875,3 +3883,396 @@ class WizzerClient:
3875
3883
 
3876
3884
  logger.debug("Fetching custom period beta for %s vs %s from %s to %s", symbol, benchmark, start_date, end_date)
3877
3885
  return self._make_request("GET", self._routes["analytics.risk.beta_custom"], params=params)
3886
+
3887
+ def get_strategy_max_drawdown(
3888
+ self,
3889
+ strategy_id: str,
3890
+ start_date: str,
3891
+ end_date: str,
3892
+ interval: str = "daily"
3893
+ ) -> Dict[str, Any]:
3894
+ """
3895
+ Get maximum drawdown for a strategy over a specified period.
3896
+
3897
+ Args:
3898
+ strategy_id (str): Strategy identifier.
3899
+ start_date (str): Start date (YYYY-MM-DD).
3900
+ end_date (str): End date (YYYY-MM-DD).
3901
+ interval (str): Time interval ('daily', 'weekly', 'monthly').
3902
+
3903
+ Returns:
3904
+ Dict[str, Any]: Strategy max drawdown data.
3905
+
3906
+ Example Response:
3907
+ {
3908
+ "strategyId": "str_01jspb8z36edjsp5pecqq0mpm3",
3909
+ "startDate": "2023-01-01",
3910
+ "endDate": "2024-12-31",
3911
+ "maxDrawdown": -0.15,
3912
+ "peakDate": "2023-06-15",
3913
+ "troughDate": "2023-09-20",
3914
+ "peakNav": 1.25,
3915
+ "troughNav": 1.06
3916
+ }
3917
+ """
3918
+ params = self._normalize_params({
3919
+ "strategyId": strategy_id,
3920
+ "startDate": start_date,
3921
+ "endDate": end_date,
3922
+ "interval": interval
3923
+ })
3924
+
3925
+ logger.debug("Fetching max drawdown for strategy %s from %s to %s", strategy_id, start_date, end_date)
3926
+ return self._make_request("GET", self._routes["analytics.strategy.drawdown_max"], params=params)
3927
+
3928
+ def get_product_max_drawdown(
3929
+ self,
3930
+ product_id: str,
3931
+ start_date: str,
3932
+ end_date: str,
3933
+ interval: str = "daily"
3934
+ ) -> Dict[str, Any]:
3935
+ """
3936
+ Get maximum drawdown for a product over a specified period.
3937
+
3938
+ Args:
3939
+ product_id (str): Product identifier.
3940
+ start_date (str): Start date (YYYY-MM-DD).
3941
+ end_date (str): End date (YYYY-MM-DD).
3942
+ interval (str): Time interval ('daily', 'weekly', 'monthly').
3943
+
3944
+ Returns:
3945
+ Dict[str, Any]: Product max drawdown data.
3946
+
3947
+ Example Response:
3948
+ {
3949
+ "productId": "prd_01jyrg7ffkemq9hz3rkeznh9dr",
3950
+ "startDate": "2023-01-01",
3951
+ "endDate": "2024-12-31",
3952
+ "maxDrawdown": -0.12,
3953
+ "peakDate": "2023-05-10",
3954
+ "troughDate": "2023-08-15",
3955
+ "peakNav": 1.18,
3956
+ "troughNav": 1.04
3957
+ }
3958
+ """
3959
+ params = self._normalize_params({
3960
+ "productId": product_id,
3961
+ "startDate": start_date,
3962
+ "endDate": end_date,
3963
+ "interval": interval
3964
+ })
3965
+
3966
+ logger.debug("Fetching max drawdown for product %s from %s to %s", product_id, start_date, end_date)
3967
+ return self._make_request("GET", self._routes["analytics.product.drawdown_max"], params=params)
3968
+
3969
+ def get_atr(
3970
+ self,
3971
+ symbol: str,
3972
+ start_date: str,
3973
+ end_date: str,
3974
+ window: int,
3975
+ adjusted: bool = True,
3976
+ interval: str = "daily"
3977
+ ) -> Dict[str, Any]:
3978
+ """
3979
+ Calculate Average True Range (ATR) for a security over a specified period.
3980
+
3981
+ Args:
3982
+ symbol (str): Stock symbol.
3983
+ start_date (str): Start date (YYYY-MM-DD).
3984
+ end_date (str): End date (YYYY-MM-DD).
3985
+ window (int): Lookback period (e.g., 14, 20).
3986
+ adjusted (bool): Adjust for splits/dividends.
3987
+ interval (str): Time interval ('daily', 'weekly', 'intraday').
3988
+
3989
+ Returns:
3990
+ Dict[str, Any]: ATR data.
3991
+
3992
+ Example Response:
3993
+ {
3994
+ "symbol": "AXISBANK",
3995
+ "startDate": "2023-01-01",
3996
+ "endDate": "2024-12-31",
3997
+ "window": 14,
3998
+ "adjusted": true,
3999
+ "atr": [
4000
+ {
4001
+ "date": "2023-01-15",
4002
+ "atrValue": 25.67
4003
+ }
4004
+ ]
4005
+ }
4006
+ """
4007
+ params = self._normalize_params({
4008
+ "symbol": symbol,
4009
+ "startDate": start_date,
4010
+ "endDate": end_date,
4011
+ "window": window,
4012
+ "adjusted": adjusted,
4013
+ "interval": interval
4014
+ })
4015
+
4016
+ logger.debug("Fetching ATR for %s with window %d from %s to %s", symbol, window, start_date, end_date)
4017
+ return self._make_request("GET", self._routes["analytics.volatility.atr"], params=params)
4018
+
4019
+ def get_simple_return(
4020
+ self,
4021
+ symbol: str,
4022
+ start_date: str,
4023
+ end_date: str,
4024
+ adjusted: bool = True,
4025
+ interval: str = "daily",
4026
+ benchmark: Optional[str] = None
4027
+ ) -> Dict[str, Any]:
4028
+ """
4029
+ Calculate simple return for a security or sectoral index.
4030
+
4031
+ Args:
4032
+ symbol (str): Stock/index symbol.
4033
+ start_date (str): Start date (YYYY-MM-DD).
4034
+ end_date (str): End date (YYYY-MM-DD).
4035
+ adjusted (bool): Adjust for corporate actions.
4036
+ interval (str): Time interval ('daily', 'weekly', 'monthly').
4037
+ benchmark (str, optional): Benchmark for relative return calculation.
4038
+
4039
+ Returns:
4040
+ Dict[str, Any]: Simple return data.
4041
+
4042
+ Example Response:
4043
+ {
4044
+ "symbol": "NIFTY IT",
4045
+ "startDate": "2023-01-01",
4046
+ "endDate": "2024-12-31",
4047
+ "adjusted": true,
4048
+ "totalReturn": 0.15,
4049
+ "startPrice": 1250.0,
4050
+ "endPrice": 1437.5,
4051
+ "benchmarkReturn": 0.12,
4052
+ "relativeReturn": 0.03,
4053
+ "unit": "decimal"
4054
+ }
4055
+ """
4056
+ params = self._normalize_params({
4057
+ "symbol": symbol,
4058
+ "startDate": start_date,
4059
+ "endDate": end_date,
4060
+ "adjusted": adjusted,
4061
+ "interval": interval,
4062
+ "benchmark": benchmark
4063
+ })
4064
+
4065
+ logger.debug("Fetching simple return for %s from %s to %s", symbol, start_date, end_date)
4066
+ return self._make_request("GET", self._routes["analytics.returns.simple"], params=params)
4067
+
4068
+ def get_corporate_actions_events(self) -> List[str]:
4069
+ """
4070
+ Get all available corporate action event types.
4071
+
4072
+ Returns:
4073
+ List[str]: List of available corporate action event types.
4074
+
4075
+ Example Response:
4076
+ [
4077
+ "AGM",
4078
+ "BONUS",
4079
+ "DIVIDEND",
4080
+ "SPLIT",
4081
+ "RIGHTS",
4082
+ "BUYBACK"
4083
+ ]
4084
+ """
4085
+ logger.debug("Fetching corporate actions events")
4086
+ return self._make_request("GET", self._routes["analytics.corporate.actions.events"])
4087
+
4088
+ def get_corporate_actions_filter(
4089
+ self,
4090
+ symbol: Optional[str] = None,
4091
+ events: Optional[str] = None,
4092
+ actionCategory: Optional[str] = None,
4093
+ fromDate: Optional[str] = None,
4094
+ toDate: Optional[str] = None,
4095
+ exchange: Optional[str] = None,
4096
+ hasDividend: Optional[bool] = None,
4097
+ hasBonus: Optional[bool] = None,
4098
+ hasAgm: Optional[bool] = None,
4099
+ eventTextContains: Optional[str] = None,
4100
+ faceValue: Optional[float] = None,
4101
+ ratioNumerator: Optional[int] = None,
4102
+ ratioDenominator: Optional[int] = None,
4103
+ actionSubcategory: Optional[str] = None,
4104
+ primaryAmount: Optional[float] = None,
4105
+ secondaryAmount: Optional[float] = None,
4106
+ oldFaceValue: Optional[float] = None,
4107
+ newFaceValue: Optional[float] = None,
4108
+ page: int = 1,
4109
+ pageSize: int = 20
4110
+ ) -> Dict[str, Any]:
4111
+ """
4112
+ Filter corporate actions based on various criteria with pagination support.
4113
+
4114
+ Args:
4115
+ symbol (str, optional): Trading symbol to filter by.
4116
+ events (str, optional): Event type to filter by.
4117
+ actionCategory (str, optional): Action category to filter by.
4118
+ fromDate (str, optional): Ex-date from (YYYY-MM-DD).
4119
+ toDate (str, optional): Ex-date to (YYYY-MM-DD).
4120
+ exchange (str, optional): Exchange to filter by.
4121
+ hasDividend (bool, optional): Filter for dividend actions.
4122
+ hasBonus (bool, optional): Filter for bonus actions.
4123
+ hasAgm (bool, optional): Filter for AGM actions.
4124
+ eventTextContains (str, optional): Search text within event description.
4125
+ faceValue (float, optional): Face value to filter by.
4126
+ ratioNumerator (int, optional): Ratio numerator value.
4127
+ ratioDenominator (int, optional): Ratio denominator value.
4128
+ actionSubcategory (str, optional): Action subcategory to filter by.
4129
+ primaryAmount (float, optional): Primary amount to filter by.
4130
+ secondaryAmount (float, optional): Secondary amount to filter by.
4131
+ oldFaceValue (float, optional): Old face value to filter by.
4132
+ newFaceValue (float, optional): New face value to filter by.
4133
+ page (int): Page number for pagination (default: 1).
4134
+ pageSize (int): Records per page (default: 20, max: 100).
4135
+
4136
+ Returns:
4137
+ Dict[str, Any]: Filtered corporate actions data with pagination.
4138
+
4139
+ Example Response:
4140
+ {
4141
+ "data": [
4142
+ {
4143
+ "tradingSymbol": "RELIANCE",
4144
+ "faceValue": 10.0,
4145
+ "eventText": "Reliance Industries Limited has declared dividend",
4146
+ "exDate": "15-03-2024",
4147
+ "recDate": "16-03-2024",
4148
+ "events": "Dividend",
4149
+ "exchange": "NSE",
4150
+ "actionCategory": "DIVIDEND",
4151
+ "actionSubcategory": "INTERIM",
4152
+ "primaryAmount": 8.5,
4153
+ "hasDividend": true,
4154
+ "hasBonus": false,
4155
+ "hasAgm": false
4156
+ }
4157
+ ],
4158
+ "totalCount": 150,
4159
+ "page": 1,
4160
+ "pageSize": 20
4161
+ }
4162
+ """
4163
+ params = self._normalize_params({
4164
+ "symbol": symbol,
4165
+ "events": events,
4166
+ "actionCategory": actionCategory,
4167
+ "fromDate": fromDate,
4168
+ "toDate": toDate,
4169
+ "exchange": exchange,
4170
+ "hasDividend": hasDividend,
4171
+ "hasBonus": hasBonus,
4172
+ "hasAgm": hasAgm,
4173
+ "eventTextContains": eventTextContains,
4174
+ "faceValue": faceValue,
4175
+ "ratioNumerator": ratioNumerator,
4176
+ "ratioDenominator": ratioDenominator,
4177
+ "actionSubcategory": actionSubcategory,
4178
+ "primaryAmount": primaryAmount,
4179
+ "secondaryAmount": secondaryAmount,
4180
+ "oldFaceValue": oldFaceValue,
4181
+ "newFaceValue": newFaceValue,
4182
+ "page": page,
4183
+ "pageSize": pageSize
4184
+ })
4185
+
4186
+ logger.debug("Filtering corporate actions with %d parameters", len([p for p in params.values() if p is not None]))
4187
+ return self._make_request("GET", self._routes["analytics.corporate.actions.filter"], params=params)
4188
+
4189
+ def get_corporate_announcements_events(self) -> List[str]:
4190
+ """
4191
+ Get all available corporate announcement event types.
4192
+
4193
+ Returns:
4194
+ List[str]: List of available corporate announcement event types.
4195
+
4196
+ Example Response:
4197
+ [
4198
+ "AGM / Book Closure",
4199
+ "Results Update",
4200
+ "Board Meeting",
4201
+ "Financial Results",
4202
+ "Dividend Declaration",
4203
+ "Merger/Acquisition"
4204
+ ]
4205
+ """
4206
+ logger.debug("Fetching corporate announcements events")
4207
+ return self._make_request("GET", self._routes["analytics.corporate.announcements.events"])
4208
+
4209
+ def get_corporate_announcements_filter(
4210
+ self,
4211
+ symbol: Optional[str] = None,
4212
+ events: Optional[str] = None,
4213
+ fromDate: Optional[str] = None,
4214
+ toDate: Optional[str] = None,
4215
+ announcementDateFrom: Optional[str] = None,
4216
+ announcementDateTo: Optional[str] = None,
4217
+ exchange: Optional[str] = None,
4218
+ announcementContains: Optional[str] = None,
4219
+ hasXbrl: Optional[bool] = None,
4220
+ page: int = 1,
4221
+ pageSize: int = 20
4222
+ ) -> Dict[str, Any]:
4223
+ """
4224
+ Filter corporate announcements based on various criteria with pagination support.
4225
+
4226
+ Args:
4227
+ symbol (str, optional): Trading symbol to filter by.
4228
+ events (str, optional): Event type to filter by.
4229
+ fromDate (str, optional): Date from (YYYY-MM-DD).
4230
+ toDate (str, optional): Date to (YYYY-MM-DD).
4231
+ announcementfromDate(str, optional): Announcement date from (YYYY-MM-DD).
4232
+ announcementDateTo (str, optional): Announcement date to (YYYY-MM-DD).
4233
+ exchange (str, optional): Exchange to filter by.
4234
+ announcementContains (str, optional): Search text within announcement.
4235
+ hasXbrl (bool, optional): Filter for announcements with XBRL.
4236
+ page (int): Page number for pagination (default: 1).
4237
+ pageSize (int): Records per page (default: 20, max: 100).
4238
+
4239
+ Returns:
4240
+ Dict[str, Any]: Filtered corporate announcements data with pagination.
4241
+
4242
+ Example Response:
4243
+ {
4244
+ "data": [
4245
+ {
4246
+ "tradingSymbol": "FINCABLES",
4247
+ "events": "AGM",
4248
+ "date": "30-04-2010",
4249
+ "companyName": "Finolex Cables Limited",
4250
+ "announcementDate": "30-04-2010",
4251
+ "sortDate": "30-04-2010",
4252
+ "announcement": "Finolex Cables Limited has informed the Exchange...",
4253
+ "exchange": "NSE",
4254
+ "attachmentFile": "-",
4255
+ "industry": "Cables - Power"
4256
+ }
4257
+ ],
4258
+ "totalCount": 250,
4259
+ "page": 1,
4260
+ "pageSize": 20
4261
+ }
4262
+ """
4263
+ params = self._normalize_params({
4264
+ "symbol": symbol,
4265
+ "events": events,
4266
+ "fromDate": fromDate,
4267
+ "toDate": toDate,
4268
+ "announcementDateFrom": announcementDateFrom,
4269
+ "announcementDateTo": announcementDateTo,
4270
+ "exchange": exchange,
4271
+ "announcementContains": announcementContains,
4272
+ "hasXbrl": hasXbrl,
4273
+ "page": page,
4274
+ "pageSize": pageSize
4275
+ })
4276
+
4277
+ logger.debug("Filtering corporate announcements with %d parameters", len([p for p in params.values() if p is not None]))
4278
+ return self._make_request("GET", self._routes["analytics.corporate.announcements.filter"], params=params)
@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  import logging
5
5
  import random
6
- from typing import Callable, List, Optional, Any, Iterator
6
+ from typing import Callable, List, Optional, Any, Iterator, Dict
7
7
 
8
8
  import websockets
9
9
  from websockets.exceptions import ConnectionClosed
@@ -17,6 +17,9 @@ if not logger.handlers:
17
17
  handler.setFormatter(formatter)
18
18
  logger.addHandler(handler)
19
19
 
20
+ # Type alias for corporate event callbacks
21
+ CorporateEventCallback = Callable[['QuotesClient', Dict[str, Any]], None]
22
+
20
23
 
21
24
  class QuotesClient:
22
25
  """
@@ -32,6 +35,8 @@ class QuotesClient:
32
35
  on_trade (Callable): Callback for trade events (type='trade').
33
36
  on_position (Callable): Callback for position events (type='position').
34
37
  on_holding (Callable): Callback for holding events (type='holding').
38
+ on_corporate_action (Callable): Callback for corporate action events (type='corporateActions').
39
+ on_corporate_announcement (Callable): Callback for corporate announcement events (type='corporateAnnouncements').
35
40
  on_connect (Callable): Callback when connection is established.
36
41
  on_close (Callable): Callback when connection is closed.
37
42
  on_error (Callable): Callback for errors.
@@ -43,6 +48,8 @@ class QuotesClient:
43
48
  - Event messages with type='trade' are routed to on_trade callback
44
49
  - Event messages with type='position' are routed to on_position callback
45
50
  - Event messages with type='holding' are routed to on_holding callback
51
+ - Event messages with type='corporateActions' are routed to on_corporate_action callback
52
+ - Event messages with type='corporateAnnouncements' are routed to on_corporate_announcement callback
46
53
  - Messages without type field are routed to on_tick for backward compatibility
47
54
  - Messages are silently dropped if the appropriate handler is not registered
48
55
 
@@ -118,6 +125,10 @@ class QuotesClient:
118
125
  self.on_trade: Optional[Callable[[Any, dict], None]] = None
119
126
  self.on_position: Optional[Callable[[Any, dict], None]] = None
120
127
  self.on_holding: Optional[Callable[[Any, dict], None]] = None
128
+
129
+ # Corporate event callbacks
130
+ self.on_corporate_action: Optional[CorporateEventCallback] = None
131
+ self.on_corporate_announcement: Optional[CorporateEventCallback] = None
121
132
 
122
133
  logger.debug("Initialized QuotesClient with URL: %s", self.url)
123
134
 
@@ -291,6 +302,22 @@ class QuotesClient:
291
302
  logger.debug("Error in on_holding handler: %s", e)
292
303
  else:
293
304
  logger.debug("Received holding event but no on_holding handler registered")
305
+ elif message_type == 'corporateActions':
306
+ if self.on_corporate_action:
307
+ try:
308
+ self.on_corporate_action(self, data)
309
+ except Exception as e:
310
+ logger.debug("Error in on_corporate_action handler: %s", e)
311
+ else:
312
+ logger.debug("Received corporate action event but no on_corporate_action handler registered")
313
+ elif message_type == 'corporateAnnouncements':
314
+ if self.on_corporate_announcement:
315
+ try:
316
+ self.on_corporate_announcement(self, data)
317
+ except Exception as e:
318
+ logger.debug("Error in on_corporate_announcement handler: %s", e)
319
+ else:
320
+ logger.debug("Received corporate announcement event but no on_corporate_announcement handler registered")
294
321
  else:
295
322
  # No type field - send to on_tick for backward compatibility
296
323
  if self.on_tick:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wiz_trader
3
- Version: 0.39.0
3
+ Version: 0.41.0
4
4
  Summary: A Python SDK for connecting to the Wizzer.
5
5
  Home-page: https://bitbucket.org/wizzer-tech/quotes_sdk.git
6
6
  Author: Pawan Wagh
@@ -39,6 +39,7 @@ Dynamic: requires-python
39
39
  - [Subscribing to Instruments](#subscribing-to-instruments)
40
40
  - [Unsubscribing from Instruments](#unsubscribing-from-instruments)
41
41
  - [Account Events](#account-events)
42
+ - [Corporate Events](#corporate-events)
42
43
  - [Handling WebSocket Connection](#handling-websocket-connection)
43
44
  - [Complete Examples](#quotes-client-examples)
44
45
  5. [Wizzer Client](#wizzer-client)
@@ -596,6 +597,196 @@ client.on_trade = strategy.on_trade_event
596
597
  client.connect()
597
598
  ```
598
599
 
600
+ ### Corporate Events
601
+
602
+ The QuotesClient supports automatic reception of corporate events from the server. These include corporate actions (dividends, stock splits, bonus issues) and corporate announcements (earnings reports, AGM notices, company announcements).
603
+
604
+ #### Corporate Event Handlers
605
+
606
+ ```python
607
+ from wiz_trader import QuotesClient
608
+
609
+ def handle_corporate_action(client, event):
610
+ """Handle corporate actions like dividends, splits, bonus issues"""
611
+ print(f"Corporate Action: {event['data']['eventText']}")
612
+ print(f"Symbol: {event['data']['tradingSymbol']}")
613
+ print(f"Date: {event['data']['date']}")
614
+
615
+ # Handle specific action categories
616
+ action_category = event['data'].get('actionCategory', '').lower()
617
+ if action_category == 'dividend':
618
+ amount = event['data'].get('primaryAmount')
619
+ print(f"Dividend Amount: ₹{amount} per share")
620
+ elif action_category == 'split':
621
+ print(f"Stock Split - Face Value Change from ₹{event['data'].get('oldFaceValue')} to ₹{event['data'].get('newFaceValue')}")
622
+ elif action_category == 'bonus':
623
+ ratio_num = event['data'].get('ratioNumerator')
624
+ ratio_den = event['data'].get('ratioDenominator')
625
+ if ratio_num and ratio_den:
626
+ print(f"Bonus Shares: {ratio_num}:{ratio_den} ratio")
627
+
628
+ def handle_corporate_announcement(client, event):
629
+ """Handle corporate announcements like earnings, AGM notices"""
630
+ print(f"Announcement: {event['data']['announcement']}")
631
+ print(f"Symbol: {event['data']['tradingSymbol']}")
632
+ print(f"Date: {event['data']['date']}")
633
+ print(f"Event Description: {event['data']['eventsDescriptions']}")
634
+
635
+ # Check for attachment file
636
+ if 'attachmentFile' in event['data']:
637
+ print(f"Attachment: {event['data']['attachmentFile']}")
638
+
639
+ # Register corporate event handlers
640
+ client.on_corporate_action = handle_corporate_action
641
+ client.on_corporate_announcement = handle_corporate_announcement
642
+ ```
643
+
644
+ #### Corporate Event Types
645
+
646
+ **Corporate Actions** (`type: 'events.corporate.actions'`):
647
+ - **Dividends**: Cash dividends, special dividends
648
+ - **Stock Splits**: Forward splits, reverse splits
649
+ - **Bonus Issues**: Bonus share distributions
650
+ - **Rights Issues**: Rights share offerings
651
+ - **Mergers & Acquisitions**: Corporate restructuring events
652
+
653
+ **Corporate Announcements** (`type: 'events.corporate.announcements'`):
654
+ - **Earnings Reports**: Quarterly and annual results
655
+ - **AGM/EGM Notices**: Annual and extraordinary general meetings
656
+ - **Board Meetings**: Board meeting announcements
657
+ - **Company Updates**: Material announcements, regulatory filings
658
+
659
+ #### Example Event Data Structures
660
+
661
+ **Corporate Action Example**:
662
+ ```json
663
+ {
664
+ "data": {
665
+ "actionCategory": "dividend",
666
+ "actionSubcategory": "",
667
+ "assetClass": "sme",
668
+ "date": "19-08-2025",
669
+ "eventText": "DIVIDEND - RS 2 PER SHARE",
670
+ "events": "corporate_actions.other_corporate_action",
671
+ "eventsDescriptions": "OTHER CORPORATE ACTION",
672
+ "faceValue": 10,
673
+ "hasAgm": false,
674
+ "hasBonus": false,
675
+ "hasDividend": true,
676
+ "newFaceValue": null,
677
+ "oldFaceValue": null,
678
+ "percentageValue": null,
679
+ "primaryAmount": 2,
680
+ "ratioDenominator": null,
681
+ "ratioNumerator": null,
682
+ "secondaryAmount": null,
683
+ "series": "SM",
684
+ "tradingSymbol": "SBIN"
685
+ },
686
+ "timestamp": "2025-08-19T19:04:47.789Z",
687
+ "type": "events.corporate.actions"
688
+ }
689
+ ```
690
+
691
+ **Corporate Announcement Example**:
692
+ ```json
693
+ {
694
+ "data": {
695
+ "announcement": "Manglam Infra & Engineering Limited has informed the Exchange about award of Additional Work from BRO under Project Deepak, Himachal Pradesh.",
696
+ "assetClass": "sme",
697
+ "attachmentFile": "https://nsearchives.nseindia.com/corporate/MANGLAMINFRA_19082025133231_addition.pdf",
698
+ "date": "19-08-2025",
699
+ "events": "corporate_announcements.general_updates",
700
+ "eventsDescriptions": "General Updates",
701
+ "hasXbrl": false,
702
+ "industry": "",
703
+ "tradingSymbol": "SBIN"
704
+ },
705
+ "timestamp": "2025-08-19T19:04:30.511Z",
706
+ "type": "events.corporate.announcements"
707
+ }
708
+ ```
709
+
710
+ #### Complete Corporate Events Example
711
+
712
+ ```python
713
+ from wiz_trader import QuotesClient
714
+ from datetime import datetime
715
+
716
+ # Initialize client
717
+ client = QuotesClient(
718
+ base_url="url",
719
+ token="your-jwt-token",
720
+ log_level="info"
721
+ )
722
+
723
+ def handle_corporate_action(client, event):
724
+ """Comprehensive corporate action handler"""
725
+ print(f"\n🏢 CORPORATE ACTION - {datetime.now()}")
726
+ data = event.get('data', {})
727
+
728
+ print(f"Symbol: {data.get('tradingSymbol')}")
729
+ print(f"Action: {data.get('eventText')}")
730
+ print(f"Date: {data.get('date')}")
731
+ print(f"Event: {data.get('eventsDescriptions')}")
732
+ print(f"Asset Class: {data.get('assetClass')}")
733
+ print(f"Face Value: ₹{data.get('faceValue')}")
734
+
735
+ # Action-specific handling based on actionCategory
736
+ action_category = data.get('actionCategory', '').lower()
737
+ if action_category == 'dividend' and data.get('hasDividend'):
738
+ amount = data.get('primaryAmount')
739
+ print(f"💰 Dividend: ₹{amount} per share")
740
+ # Implement dividend tracking logic
741
+ elif action_category == 'bonus' and data.get('hasBonus'):
742
+ ratio_num = data.get('ratioNumerator')
743
+ ratio_den = data.get('ratioDenominator')
744
+ if ratio_num and ratio_den:
745
+ print(f"🎁 Bonus Shares: {ratio_num}:{ratio_den} ratio")
746
+ # Update portfolio for bonus shares
747
+ elif action_category == 'split':
748
+ old_fv = data.get('oldFaceValue')
749
+ new_fv = data.get('newFaceValue')
750
+ if old_fv and new_fv:
751
+ print(f"📊 Stock Split: Face value changed from ₹{old_fv} to ₹{new_fv}")
752
+ # Adjust position quantities
753
+
754
+ def handle_corporate_announcement(client, event):
755
+ """Comprehensive corporate announcement handler"""
756
+ print(f"\n📢 CORPORATE ANNOUNCEMENT - {datetime.now()}")
757
+ data = event.get('data', {})
758
+
759
+ print(f"Symbol: {data.get('tradingSymbol')}")
760
+ print(f"Announcement: {data.get('announcement')}")
761
+ print(f"Date: {data.get('date')}")
762
+ print(f"Event: {data.get('eventsDescriptions')}")
763
+ print(f"Asset Class: {data.get('assetClass')}")
764
+
765
+ # Check for attachment file
766
+ if 'attachmentFile' in data:
767
+ print(f"📎 Attachment: {data['attachmentFile']}")
768
+
769
+ # Event-specific handling based on events field
770
+ events = data.get('events', '').lower()
771
+ if 'earnings' in events or 'results' in events:
772
+ print("📈 Earnings/Results announcement detected")
773
+ # Implement earnings analysis
774
+ elif 'general_updates' in events:
775
+ print("📋 General company update")
776
+ # Handle general announcements
777
+
778
+ # Register handlers
779
+ client.on_corporate_action = handle_corporate_action
780
+ client.on_corporate_announcement = handle_corporate_announcement
781
+
782
+ # Optional: Subscribe to market data as well
783
+ instruments = ["NSE:RELIANCE-EQ", "NSE:TCS-EQ"]
784
+ client.subscribe(instruments, mode="ticks")
785
+
786
+ # Start listening for events
787
+ client.connect()
788
+ ```
789
+
599
790
  ### Complete Examples
600
791
 
601
792
  #### Blocking Example
@@ -2834,10 +3025,129 @@ beta_custom = client.get_beta_custom_period(
2834
3025
  )
2835
3026
  print(f"Custom Period Beta: {beta_custom['beta']}")
2836
3027
  print(f"Alpha: {beta_custom['alpha']}")
3028
+
3029
+ # Strategy Max Drawdown
3030
+ strategy_drawdown = client.get_strategy_max_drawdown(
3031
+ strategy_id="str_01jspb8z36edjsp5pecqq0mpm3",
3032
+ start_date="2023-01-01",
3033
+ end_date="2024-12-31",
3034
+ interval="daily"
3035
+ )
3036
+ print(f"Strategy Max Drawdown: {strategy_drawdown['maxDrawdown']}")
3037
+ print(f"Peak Date: {strategy_drawdown['peakDate']}")
3038
+
3039
+ # Product Max Drawdown
3040
+ product_drawdown = client.get_product_max_drawdown(
3041
+ product_id="prd_01jyrg7ffkemq9hz3rkeznh9dr",
3042
+ start_date="2023-01-01",
3043
+ end_date="2024-12-31",
3044
+ interval="monthly"
3045
+ )
3046
+ print(f"Product Max Drawdown: {product_drawdown['maxDrawdown']}")
3047
+
3048
+ # Average True Range (ATR)
3049
+ atr_data = client.get_atr(
3050
+ symbol="AXISBANK",
3051
+ start_date="2023-01-01",
3052
+ end_date="2024-12-31",
3053
+ window=14,
3054
+ adjusted=True,
3055
+ interval="daily"
3056
+ )
3057
+ print(f"Latest ATR: {atr_data['atr'][-1]['atrValue']}")
3058
+
3059
+ # Simple Return
3060
+ simple_return = client.get_simple_return(
3061
+ symbol="NIFTY_IT",
3062
+ start_date="2023-01-01",
3063
+ end_date="2024-12-31",
3064
+ adjusted=True,
3065
+ interval="daily",
3066
+ benchmark="NIFTY 50"
3067
+ )
3068
+ print(f"Total Return: {simple_return['totalReturn']}")
3069
+ print(f"Relative Return: {simple_return['relativeReturn']}")
3070
+
3071
+ # Corporate Actions and Announcements
3072
+ corporate_events = client.get_corporate_actions_events()
3073
+ print(f"Available Corporate Action Types: {corporate_events}")
3074
+
3075
+ # Filter Corporate Actions
3076
+ corporate_actions = client.get_corporate_actions_filter(
3077
+ symbol="RELIANCE",
3078
+ events="corporate_actions.agm",
3079
+ fromDate="2024-01-01",
3080
+ toDate="2024-12-31",
3081
+ hasDividend=True,
3082
+ page=1,
3083
+ pageSize=20
3084
+ )
3085
+ print(f"Found {corporate_actions['totalCount']} corporate actions")
3086
+
3087
+ # Corporate Announcements
3088
+ announcement_events = client.get_corporate_announcements_events()
3089
+ print(f"Available Announcement Types: {announcement_events}")
3090
+
3091
+ # Filter Corporate Announcements
3092
+ announcements = client.get_corporate_announcements_filter(
3093
+ symbol="TCS",
3094
+ events="corporate_announcements.agm",
3095
+ fromDate="2024-01-01",
3096
+ toDate="2024-12-31",
3097
+ announcementContains="dividend",
3098
+ page=1,
3099
+ pageSize=10
3100
+ )
3101
+ print(f"Found {announcements['totalCount']} announcements")
2837
3102
  ```
2838
3103
 
3104
+ ### Corporate Analytics API Reference
3105
+
3106
+ #### Corporate Actions Filter Parameters
3107
+
3108
+ | Parameter | Type | Required | Description | Example |
3109
+ |-----------|------|----------|-------------|----------|
3110
+ | symbol | string | No | Trading symbol to filter by | RELIANCE, TCS |
3111
+ | events | string | No | Event type to filter by | Dividend, BONUS |
3112
+ | actionCategory | string | No | Action category to filter by | DIVIDEND, BONUS, SPLIT |
3113
+ | fromDate | string (date) | No | Ex-date from (YYYY-MM-DD) | 2024-01-01 |
3114
+ | toDate | string (date) | No | Ex-date to (YYYY-MM-DD) | 2024-12-31 |
3115
+ | exchange | string | No | Exchange to filter by | NSE, BSE |
3116
+ | hasDividend | boolean | No | Filter for dividend actions | true, false |
3117
+ | hasBonus | boolean | No | Filter for bonus actions | true, false |
3118
+ | hasAgm | boolean | No | Filter for AGM actions | true, false |
3119
+ | eventTextContains | string | No | Search text within event description | bonus, dividend |
3120
+ | faceValue | number | No | Face value to filter by | 10, 1 |
3121
+ | ratioNumerator | integer | No | Ratio numerator value | 1, 2 |
3122
+ | ratioDenominator | integer | No | Ratio denominator value | 1, 10 |
3123
+ | actionSubcategory | string | No | Action subcategory to filter by | INTERIM, FINAL |
3124
+ | primaryAmount | number | No | Primary amount to filter by | 5.50, 10.00 |
3125
+ | secondaryAmount | number | No | Secondary amount to filter by | 2.50 |
3126
+ | oldFaceValue | number | No | Old face value to filter by | 10 |
3127
+ | newFaceValue | number | No | New face value to filter by | 1 |
3128
+ | page | integer | No | Page number for pagination (default: 1) | 1, 2 |
3129
+ | pageSize | integer | No | Records per page (default: 20, max: 100) | 20, 50 |
3130
+
3131
+ #### Corporate Announcements Filter Parameters
3132
+
3133
+ | Parameter | Type | Required | Description | Example |
3134
+ |-----------|------|----------|-------------|----------|
3135
+ | symbol | string | No | Trading symbol to filter by | 20MICRONS, FINCABLES |
3136
+ | events | string | No | Event type to filter by | AGM, Results Update |
3137
+ | fromDate | string (date) | No | Date from (YYYY-MM-DD) | 2012-01-01 |
3138
+ | toDate | string (date) | No | Date to (YYYY-MM-DD) | 2012-12-31 |
3139
+ | announcementDateFrom | string (date) | No | Announcement date from (YYYY-MM-DD) | 2010-01-01 |
3140
+ | announcementDateTo | string (date) | No | Announcement date to (YYYY-MM-DD) | 2010-12-31 |
3141
+ | exchange | string | No | Exchange to filter by | NSE, BSE |
3142
+ | announcementContains | string | No | Search text within announcement | dividend, merger |
3143
+ | hasXbrl | boolean | No | Filter for announcements with XBRL | true, false |
3144
+ | page | integer | No | Page number for pagination (default: 1) | 1, 2 |
3145
+ | pageSize | integer | No | Records per page (default: 20, max: 100) | 20, 50 |
3146
+
2839
3147
  **Key Features:**
2840
- - **Comprehensive Coverage**: 39+ analytics endpoints covering fundamentals, valuation, returns, market data, ownership, metrics, macro data, risk analysis, sector classification, leverage analysis, and advanced technical analysis
3148
+ - **Comprehensive Coverage**: 47+ analytics endpoints covering fundamentals, valuation, returns, market data, ownership, metrics, macro data, risk analysis, sector classification, leverage analysis, advanced technical analysis, and corporate actions/announcements
3149
+ - **Corporate Actions**: Track dividends, bonus issues, stock splits, rights issues, AGMs, and buybacks with comprehensive filtering
3150
+ - **Corporate Announcements**: Monitor board meetings, financial results, AGM notifications, and regulatory announcements with XBRL support
2841
3151
  - **Fundamentals Analysis**: 9 methods including ROE, ROA, margins, ratios, book-to-market, market cap-to-sales, and cash-to-market cap
2842
3152
  - **Valuation Metrics**: P/E, P/B, EV/EBITDA, FCF yield with TTM and consolidated/standalone options
2843
3153
  - **Risk-Adjusted Metrics**: Sortino ratio, upside capture ratio, maximum drawdown, and returns volatility for comprehensive risk analysis
@@ -0,0 +1,9 @@
1
+ wiz_trader/__init__.py,sha256=VkSBcjg84kJxtyz6qoHYKTlLKYzqXx_41OundOg79MQ,183
2
+ wiz_trader/apis/__init__.py,sha256=6sUr1nzmplNdld0zryMrQSt0jHT2GhOiFYgKKVHzk8U,133
3
+ wiz_trader/apis/client.py,sha256=zSPATxgLM-E_9SrVcpbSthwp4mPrtdXjkvpVeYAbbUw,142151
4
+ wiz_trader/quotes/__init__.py,sha256=RF9g9CNP6bVWlmCh_ad8krm3-EWOIuVfLp0-H9fAeEM,108
5
+ wiz_trader/quotes/client.py,sha256=3m4gGizytyIUtE9BstESjq3o2JnVaCC1m2RnpnG9KqE,22795
6
+ wiz_trader-0.41.0.dist-info/METADATA,sha256=1mJXsH5bCEqy7HC60-ccQV18nfIHED_RABVIMtAAAtU,183434
7
+ wiz_trader-0.41.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ wiz_trader-0.41.0.dist-info/top_level.txt,sha256=lnYS_g8LlA6ryKYnvY8xIQ6K2K-xzOsd-99AWgnW6VY,11
9
+ wiz_trader-0.41.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- wiz_trader/__init__.py,sha256=tGTxOYvxNnCM60gbqm1vUEVVPSwJenwhzcpX4_UVH6U,183
2
- wiz_trader/apis/__init__.py,sha256=6sUr1nzmplNdld0zryMrQSt0jHT2GhOiFYgKKVHzk8U,133
3
- wiz_trader/apis/client.py,sha256=BMjJbh6dgUsfm7rWpmOhzcGRSkSQmk_4WO8ewLN80Jw,128652
4
- wiz_trader/quotes/__init__.py,sha256=RF9g9CNP6bVWlmCh_ad8krm3-EWOIuVfLp0-H9fAeEM,108
5
- wiz_trader/quotes/client.py,sha256=cAGaysLCljZilYgX8Sf3V88F6dWlcewJf6TMOpSKb7I,20862
6
- wiz_trader-0.39.0.dist-info/METADATA,sha256=TZaHzAX7bsObQveYYiN3lCJDyHoCxKGFStajdGk4g8g,171180
7
- wiz_trader-0.39.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- wiz_trader-0.39.0.dist-info/top_level.txt,sha256=lnYS_g8LlA6ryKYnvY8xIQ6K2K-xzOsd-99AWgnW6VY,11
9
- wiz_trader-0.39.0.dist-info/RECORD,,