wiz-trader 0.27.0__tar.gz → 0.29.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wiz_trader
3
- Version: 0.27.0
3
+ Version: 0.29.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
@@ -186,17 +186,25 @@ client.stop()
186
186
 
187
187
  All callbacks are **plain `def`** functions. Inside them you can call `subscribe(...)`, which under the hood schedules the actual async work—so you never `await` in your callbacks.
188
188
 
189
+ The QuotesClient routes messages based on their `type` field:
190
+ - Messages with `type: 'ticks'` are sent to the `on_tick` callback
191
+ - Messages with `type: 'greeks'` are sent to the `on_stats` callback
192
+ - Messages without a `type` field are sent to `on_tick` for backward compatibility
193
+
189
194
  ```python
190
195
  def on_tick(ws, tick):
191
196
  print("Tick:", tick)
192
197
 
198
+ def on_stats(ws, stats):
199
+ print("Stats:", stats)
200
+ # Stats/Greek messages contain: {'type': 'greeks', 'identifier': '...', 'rho': 0.1, 'theta': 9.8, ...}
201
+
193
202
  def on_connect(ws):
194
203
  print("Connected!")
195
- # fire‑and‑forget subscribe—no await needed
196
- ws.subscribe([
197
- "NSE:SBIN:3045",
198
- "NSE:RELIANCE:2885"
199
- ])
204
+ # Subscribe with different modes
205
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks") # Will receive tick messages
206
+ ws.subscribe(["NSE:NIFTY25JULFUT:53216"], mode="greeks") # Will receive greek messages
207
+ ws.subscribe(["NSE:RELIANCE:2885"], mode="full") # Will receive both types
200
208
 
201
209
  def on_close(ws, code, reason):
202
210
  print(f"Connection closed [{code}]: {reason}")
@@ -206,6 +214,7 @@ def on_error(ws, error):
206
214
  print("Error:", error)
207
215
 
208
216
  client.on_tick = on_tick
217
+ client.on_stats = on_stats
209
218
  client.on_connect = on_connect
210
219
  client.on_close = on_close
211
220
  client.on_error = on_error
@@ -261,9 +270,13 @@ client = QuotesClient(
261
270
  def on_tick(ws, tick):
262
271
  logging.debug("Tick: %s", tick)
263
272
 
273
+ def on_stats(ws, stats):
274
+ logging.debug("Stats: %s", stats)
275
+
264
276
  def on_connect(ws):
265
277
  logging.info("Connected.")
266
- ws.subscribe(["NSE:SBIN:3045", "NSE:RELIANCE:2885"]) # no await
278
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks")
279
+ ws.subscribe(["NSE:NIFTY24JUN20100CE:20100"], mode="greeks")
267
280
 
268
281
  def on_close(ws, code, reason):
269
282
  logging.warning("Closed: %s", reason)
@@ -273,6 +286,7 @@ def on_error(ws, error):
273
286
  logging.error("Error: %s", error)
274
287
 
275
288
  client.on_tick = on_tick
289
+ client.on_stats = on_stats
276
290
  client.on_connect = on_connect
277
291
  client.on_close = on_close
278
292
  client.on_error = on_error
@@ -307,17 +321,58 @@ async def main():
307
321
  asyncio.run(main())
308
322
  ```
309
323
 
310
- #### Ticks structure
311
- ts = tick timestamp
312
- lastTradedTs = timestamp of the last executed trade in this instrument
324
+ #### Message Data Structures
313
325
 
314
- **New Fields Added:**
326
+ ##### Ticks structure
327
+ Messages with `type: 'ticks'` contain market data:
328
+ - `ts`: tick timestamp
329
+ - `lastTradedTs`: timestamp of the last executed trade in this instrument
315
330
  - `oi`: Current open interest (for futures and options)
316
331
  - `oiDayHigh`: Day's highest open interest value
317
332
  - `oiDayLow`: Day's lowest open interest value
318
333
 
334
+ ```json
335
+ {
336
+ "type": "ticks",
337
+ "identifier": "NSE:RELIANCE:2885",
338
+ "tradingSymbol": "RELIANCE",
339
+ "exchange": "NSE",
340
+ "segment": "NSECM",
341
+ "exchangeToken": 2885,
342
+ "bids": [{"volume": 1722, "price": 1295.5, "orders": 43}, ...],
343
+ "offers": [{"volume": 0, "price": 0, "orders": 0}, ...],
344
+ "ltp": 1295.5,
345
+ "lastTradedQty": 10,
346
+ "buyQty": 1722,
347
+ "sellQty": 0,
348
+ "volume": 10429964,
349
+ "avgPrice": 1291.46,
350
+ "netChange": 0,
351
+ "ohlc": {"open": 1270, "high": 1300.9, "low": 1267, "close": 1295.5},
352
+ "oi": 1234567,
353
+ "oiDayHigh": 1250000,
354
+ "oiDayLow": 1200000,
355
+ "lastTradedTs": "2025-04-21T10:29:33Z",
356
+ "ts": "2025-04-21T10:29:46Z"
357
+ }
319
358
  ```
320
- {'identifier': 'NSE:RELIANCE:2885', 'tradingSymbol': 'RELIANCE', 'exchange': 'NSE', 'segment': 'NSECM', 'exchangeToken': 2885, 'bids': [{'volume': 1722, 'price': 1295.5, 'orders': 43}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'offers': [{'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'ltp': 1295.5, 'lastTradedQty': 10, 'buyQty': 1722, 'sellQty': 0, 'volume': 10429964, 'avgPrice': 1291.46, 'netChange': 0, 'ohlc': {'open': 1270, 'high': 1300.9, 'low': 1267, 'close': 1295.5}, 'oi': 1234567, 'oiDayHigh': 1250000, 'oiDayLow': 1200000, 'lastTradedTs': '2025-04-21T10:29:33Z', 'ts': '2025-04-21T10:29:46Z'}
359
+
360
+ ##### Greeks structure
361
+ Messages with `type: 'greeks'` contain options Greeks data:
362
+
363
+ ```json
364
+ {
365
+ "type": "greeks",
366
+ "identifier": "NSE:NIFTY25JULFUT:53216",
367
+ "tradingSymbol": "NIFTY25JULFUT",
368
+ "iv": 0.0,
369
+ "delta": 0.0,
370
+ "gamma": 0.0,
371
+ "theta": 0.0,
372
+ "vega": 0.0,
373
+ "rho": 0.0,
374
+ "ts": "2025-04-21T10:29:46Z"
375
+ }
321
376
  ```
322
377
 
323
378
  ## Wizzer Client
@@ -159,17 +159,25 @@ client.stop()
159
159
 
160
160
  All callbacks are **plain `def`** functions. Inside them you can call `subscribe(...)`, which under the hood schedules the actual async work—so you never `await` in your callbacks.
161
161
 
162
+ The QuotesClient routes messages based on their `type` field:
163
+ - Messages with `type: 'ticks'` are sent to the `on_tick` callback
164
+ - Messages with `type: 'greeks'` are sent to the `on_stats` callback
165
+ - Messages without a `type` field are sent to `on_tick` for backward compatibility
166
+
162
167
  ```python
163
168
  def on_tick(ws, tick):
164
169
  print("Tick:", tick)
165
170
 
171
+ def on_stats(ws, stats):
172
+ print("Stats:", stats)
173
+ # Stats/Greek messages contain: {'type': 'greeks', 'identifier': '...', 'rho': 0.1, 'theta': 9.8, ...}
174
+
166
175
  def on_connect(ws):
167
176
  print("Connected!")
168
- # fire‑and‑forget subscribe—no await needed
169
- ws.subscribe([
170
- "NSE:SBIN:3045",
171
- "NSE:RELIANCE:2885"
172
- ])
177
+ # Subscribe with different modes
178
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks") # Will receive tick messages
179
+ ws.subscribe(["NSE:NIFTY25JULFUT:53216"], mode="greeks") # Will receive greek messages
180
+ ws.subscribe(["NSE:RELIANCE:2885"], mode="full") # Will receive both types
173
181
 
174
182
  def on_close(ws, code, reason):
175
183
  print(f"Connection closed [{code}]: {reason}")
@@ -179,6 +187,7 @@ def on_error(ws, error):
179
187
  print("Error:", error)
180
188
 
181
189
  client.on_tick = on_tick
190
+ client.on_stats = on_stats
182
191
  client.on_connect = on_connect
183
192
  client.on_close = on_close
184
193
  client.on_error = on_error
@@ -234,9 +243,13 @@ client = QuotesClient(
234
243
  def on_tick(ws, tick):
235
244
  logging.debug("Tick: %s", tick)
236
245
 
246
+ def on_stats(ws, stats):
247
+ logging.debug("Stats: %s", stats)
248
+
237
249
  def on_connect(ws):
238
250
  logging.info("Connected.")
239
- ws.subscribe(["NSE:SBIN:3045", "NSE:RELIANCE:2885"]) # no await
251
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks")
252
+ ws.subscribe(["NSE:NIFTY24JUN20100CE:20100"], mode="greeks")
240
253
 
241
254
  def on_close(ws, code, reason):
242
255
  logging.warning("Closed: %s", reason)
@@ -246,6 +259,7 @@ def on_error(ws, error):
246
259
  logging.error("Error: %s", error)
247
260
 
248
261
  client.on_tick = on_tick
262
+ client.on_stats = on_stats
249
263
  client.on_connect = on_connect
250
264
  client.on_close = on_close
251
265
  client.on_error = on_error
@@ -280,17 +294,58 @@ async def main():
280
294
  asyncio.run(main())
281
295
  ```
282
296
 
283
- #### Ticks structure
284
- ts = tick timestamp
285
- lastTradedTs = timestamp of the last executed trade in this instrument
297
+ #### Message Data Structures
286
298
 
287
- **New Fields Added:**
299
+ ##### Ticks structure
300
+ Messages with `type: 'ticks'` contain market data:
301
+ - `ts`: tick timestamp
302
+ - `lastTradedTs`: timestamp of the last executed trade in this instrument
288
303
  - `oi`: Current open interest (for futures and options)
289
304
  - `oiDayHigh`: Day's highest open interest value
290
305
  - `oiDayLow`: Day's lowest open interest value
291
306
 
307
+ ```json
308
+ {
309
+ "type": "ticks",
310
+ "identifier": "NSE:RELIANCE:2885",
311
+ "tradingSymbol": "RELIANCE",
312
+ "exchange": "NSE",
313
+ "segment": "NSECM",
314
+ "exchangeToken": 2885,
315
+ "bids": [{"volume": 1722, "price": 1295.5, "orders": 43}, ...],
316
+ "offers": [{"volume": 0, "price": 0, "orders": 0}, ...],
317
+ "ltp": 1295.5,
318
+ "lastTradedQty": 10,
319
+ "buyQty": 1722,
320
+ "sellQty": 0,
321
+ "volume": 10429964,
322
+ "avgPrice": 1291.46,
323
+ "netChange": 0,
324
+ "ohlc": {"open": 1270, "high": 1300.9, "low": 1267, "close": 1295.5},
325
+ "oi": 1234567,
326
+ "oiDayHigh": 1250000,
327
+ "oiDayLow": 1200000,
328
+ "lastTradedTs": "2025-04-21T10:29:33Z",
329
+ "ts": "2025-04-21T10:29:46Z"
330
+ }
292
331
  ```
293
- {'identifier': 'NSE:RELIANCE:2885', 'tradingSymbol': 'RELIANCE', 'exchange': 'NSE', 'segment': 'NSECM', 'exchangeToken': 2885, 'bids': [{'volume': 1722, 'price': 1295.5, 'orders': 43}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'offers': [{'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'ltp': 1295.5, 'lastTradedQty': 10, 'buyQty': 1722, 'sellQty': 0, 'volume': 10429964, 'avgPrice': 1291.46, 'netChange': 0, 'ohlc': {'open': 1270, 'high': 1300.9, 'low': 1267, 'close': 1295.5}, 'oi': 1234567, 'oiDayHigh': 1250000, 'oiDayLow': 1200000, 'lastTradedTs': '2025-04-21T10:29:33Z', 'ts': '2025-04-21T10:29:46Z'}
332
+
333
+ ##### Greeks structure
334
+ Messages with `type: 'greeks'` contain options Greeks data:
335
+
336
+ ```json
337
+ {
338
+ "type": "greeks",
339
+ "identifier": "NSE:NIFTY25JULFUT:53216",
340
+ "tradingSymbol": "NIFTY25JULFUT",
341
+ "iv": 0.0,
342
+ "delta": 0.0,
343
+ "gamma": 0.0,
344
+ "theta": 0.0,
345
+ "vega": 0.0,
346
+ "rho": 0.0,
347
+ "ts": "2025-04-21T10:29:46Z"
348
+ }
294
349
  ```
295
350
 
296
351
  ## Wizzer Client
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wiz_trader"
7
- version = "0.27.0"
7
+ version = "0.29.0"
8
8
  description = "A Python SDK for connecting to the Wizzer."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='wiz_trader',
5
- version='0.27.0',
5
+ version='0.29.0',
6
6
  description='A Python SDK for connecting to the Wizzer.',
7
7
  long_description=open('README.md').read() if open('README.md') else "",
8
8
  long_description_content_type='text/markdown',
@@ -3,6 +3,6 @@
3
3
  from .quotes import QuotesClient
4
4
  from .apis import WizzerClient
5
5
 
6
- __version__ = "0.27.0"
6
+ __version__ = "0.29.0"
7
7
 
8
8
  __all__ = ["QuotesClient", "WizzerClient"]
@@ -7,6 +7,7 @@ from typing import Callable, List, Optional, Any, Iterator
7
7
 
8
8
  import websockets
9
9
  from websockets.exceptions import ConnectionClosed
10
+ from websockets.protocol import State
10
11
 
11
12
  # Setup module‐level logger with a default handler if none exists.
12
13
  logger = logging.getLogger(__name__)
@@ -25,10 +26,26 @@ class QuotesClient:
25
26
  base_url (str): WebSocket URL of the quotes server.
26
27
  token (str): JWT token for authentication.
27
28
  log_level (str): Logging level. Options: "error", "info", "debug".
29
+ on_tick (Callable): Callback for tick messages (type='ticks' or no type field).
30
+ on_stats (Callable): Callback for stats/greeks messages (type='greeks').
31
+ on_connect (Callable): Callback when connection is established.
32
+ on_close (Callable): Callback when connection is closed.
33
+ on_error (Callable): Callback for errors.
34
+
35
+ Message Routing:
36
+ - Messages with type='ticks' are routed to on_tick callback
37
+ - Messages with type='greeks' are routed to on_stats callback
38
+ - Messages without type field are routed to on_tick for backward compatibility
39
+ - Messages are silently dropped if the appropriate handler is not registered
28
40
  """
29
41
 
30
42
  ACTION_SUBSCRIBE = "subscribe"
31
43
  ACTION_UNSUBSCRIBE = "unsubscribe"
44
+
45
+ # Subscription modes
46
+ MODE_GREEKS = "greeks"
47
+ MODE_TICKS = "ticks"
48
+ MODE_FULL = "full"
32
49
 
33
50
  def __init__(
34
51
  self,
@@ -57,6 +74,7 @@ class QuotesClient:
57
74
  self.url = f"{self.base_url}?token={self.token}"
58
75
  self.ws: Optional[websockets.WebSocketClientProtocol] = None
59
76
  self.subscribed_instruments: set = set()
77
+ self.subscription_modes: dict = {} # instrument -> mode mapping
60
78
  self._running = False
61
79
  self._background_task = None
62
80
  self._loop: Optional[asyncio.AbstractEventLoop] = None
@@ -67,6 +85,7 @@ class QuotesClient:
67
85
 
68
86
  # Callbacks are plain synchronous functions
69
87
  self.on_tick: Optional[Callable[[Any, dict], None]] = None
88
+ self.on_stats: Optional[Callable[[Any, dict], None]] = None
70
89
  self.on_connect: Optional[Callable[[Any], None]] = None
71
90
  self.on_close: Optional[Callable[[Any, Optional[int], Optional[str]], None]] = None
72
91
  self.on_error: Optional[Callable[[Any, Exception], None]] = None
@@ -94,13 +113,26 @@ class QuotesClient:
94
113
  except Exception as e:
95
114
  logger.error("Error in on_connect callback: %s", e, exc_info=True)
96
115
 
97
- # re-subscribe on reconnect
116
+ # re-subscribe on reconnect with modes
98
117
  if self.subscribed_instruments:
99
- for batch in self._chunk_list(list(self.subscribed_instruments), self.batch_size):
100
- msg = {"action": self.ACTION_SUBSCRIBE, "instruments": batch}
101
- await self.ws.send(json.dumps(msg))
102
- logger.info("Re-subscribed to %d instruments", len(batch))
103
- await asyncio.sleep(0.1)
118
+ # Group instruments by mode for efficient re-subscription
119
+ mode_groups = {}
120
+ for instrument in self.subscribed_instruments:
121
+ mode = self.subscription_modes.get(instrument, self.MODE_TICKS)
122
+ if mode not in mode_groups:
123
+ mode_groups[mode] = []
124
+ mode_groups[mode].append(instrument)
125
+
126
+ for mode, instruments in mode_groups.items():
127
+ for batch in self._chunk_list(instruments, self.batch_size):
128
+ msg = {
129
+ "action": self.ACTION_SUBSCRIBE,
130
+ "instruments": batch,
131
+ "mode": mode
132
+ }
133
+ await self.ws.send(json.dumps(msg))
134
+ logger.info("Re-subscribed to %d instruments with mode '%s'", len(batch), mode)
135
+ await asyncio.sleep(0.1)
104
136
 
105
137
  backoff = self._backoff_base
106
138
  await self._handle_messages()
@@ -142,9 +174,26 @@ class QuotesClient:
142
174
  if not chunk:
143
175
  continue
144
176
  try:
145
- tick = json.loads(chunk)
146
- if self.on_tick:
147
- self.on_tick(self, tick)
177
+ data = json.loads(chunk)
178
+ # Route based on message type
179
+ message_type = data.get('type')
180
+
181
+ if message_type == 'greeks':
182
+ if self.on_stats:
183
+ self.on_stats(self, data)
184
+ else:
185
+ logger.debug("Received greeks message but no on_stats handler registered")
186
+ elif message_type == 'ticks':
187
+ if self.on_tick:
188
+ self.on_tick(self, data)
189
+ else:
190
+ logger.debug("Received ticks message but no on_tick handler registered")
191
+ else:
192
+ # No type field - send to on_tick for backward compatibility
193
+ if self.on_tick:
194
+ self.on_tick(self, data)
195
+ else:
196
+ logger.debug("Received message without type field and no on_tick handler registered")
148
197
  except json.JSONDecodeError as e:
149
198
  logger.error("JSON parse error: %s", e)
150
199
  else:
@@ -161,26 +210,40 @@ class QuotesClient:
161
210
 
162
211
  # -- Async core methods (for internal use) --
163
212
 
164
- async def _subscribe_async(self, instruments: List[str]) -> None:
165
- if self.ws and self.ws.open:
213
+ async def _subscribe_async(self, instruments: List[str], mode: str = MODE_TICKS) -> None:
214
+ if self.ws and self.ws.state == State.OPEN:
166
215
  new = set(instruments) - self.subscribed_instruments
167
216
  if new:
168
217
  self.subscribed_instruments |= new
218
+ # Track mode for each instrument
219
+ for instrument in new:
220
+ self.subscription_modes[instrument] = mode
221
+
169
222
  for batch in self._chunk_list(list(new), self.batch_size):
170
- logger.info("Subscribing to %d instruments", len(batch))
171
- await self.ws.send(json.dumps({
223
+ logger.info("Subscribing to %d instruments with mode '%s'", len(batch), mode)
224
+ message = {
172
225
  "action": self.ACTION_SUBSCRIBE,
173
- "instruments": batch
174
- }))
226
+ "instruments": batch,
227
+ "mode": mode
228
+ }
229
+ print(f"Subscribing to {batch} with mode {mode}")
230
+ await self.ws.send(json.dumps(message))
175
231
  await asyncio.sleep(0.1)
176
232
  else:
177
233
  self.subscribed_instruments |= set(instruments)
234
+ # Track mode for each instrument
235
+ for instrument in instruments:
236
+ self.subscription_modes[instrument] = mode
178
237
 
179
238
  async def _unsubscribe_async(self, instruments: List[str]) -> None:
180
- if self.ws and self.ws.open:
239
+ if self.ws and self.ws.state == State.OPEN:
181
240
  to_remove = set(instruments) & self.subscribed_instruments
182
241
  if to_remove:
183
242
  self.subscribed_instruments -= to_remove
243
+ # Remove mode tracking for unsubscribed instruments
244
+ for instrument in to_remove:
245
+ self.subscription_modes.pop(instrument, None)
246
+
184
247
  for batch in self._chunk_list(list(to_remove), self.batch_size):
185
248
  logger.info("Unsubscribing from %d instruments", len(batch))
186
249
  await self.ws.send(json.dumps({
@@ -190,20 +253,26 @@ class QuotesClient:
190
253
  await asyncio.sleep(0.1)
191
254
  else:
192
255
  self.subscribed_instruments -= set(instruments)
256
+ # Remove mode tracking for unsubscribed instruments
257
+ for instrument in instruments:
258
+ self.subscription_modes.pop(instrument, None)
193
259
 
194
260
  # -- Public wrappers for plain callback users --
195
261
 
196
- def subscribe(self, instruments: List[str]) -> None:
262
+ def subscribe(self, instruments: List[str], mode: str = MODE_TICKS) -> None:
197
263
  """
198
264
  Schedule subscribe onto the client’s event loop.
199
265
  """
200
266
  if self._loop:
201
267
  asyncio.run_coroutine_threadsafe(
202
- self._subscribe_async(instruments),
268
+ self._subscribe_async(instruments, mode),
203
269
  self._loop
204
270
  )
205
271
  else:
206
272
  self.subscribed_instruments |= set(instruments)
273
+ # Track mode for each instrument
274
+ for instrument in instruments:
275
+ self.subscription_modes[instrument] = mode
207
276
 
208
277
  def unsubscribe(self, instruments: List[str]) -> None:
209
278
  """
@@ -216,6 +285,9 @@ class QuotesClient:
216
285
  )
217
286
  else:
218
287
  self.subscribed_instruments -= set(instruments)
288
+ # Remove mode tracking for unsubscribed instruments
289
+ for instrument in instruments:
290
+ self.subscription_modes.pop(instrument, None)
219
291
 
220
292
  def unsubscribe_all(self) -> None:
221
293
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wiz_trader
3
- Version: 0.27.0
3
+ Version: 0.29.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
@@ -186,17 +186,25 @@ client.stop()
186
186
 
187
187
  All callbacks are **plain `def`** functions. Inside them you can call `subscribe(...)`, which under the hood schedules the actual async work—so you never `await` in your callbacks.
188
188
 
189
+ The QuotesClient routes messages based on their `type` field:
190
+ - Messages with `type: 'ticks'` are sent to the `on_tick` callback
191
+ - Messages with `type: 'greeks'` are sent to the `on_stats` callback
192
+ - Messages without a `type` field are sent to `on_tick` for backward compatibility
193
+
189
194
  ```python
190
195
  def on_tick(ws, tick):
191
196
  print("Tick:", tick)
192
197
 
198
+ def on_stats(ws, stats):
199
+ print("Stats:", stats)
200
+ # Stats/Greek messages contain: {'type': 'greeks', 'identifier': '...', 'rho': 0.1, 'theta': 9.8, ...}
201
+
193
202
  def on_connect(ws):
194
203
  print("Connected!")
195
- # fire‑and‑forget subscribe—no await needed
196
- ws.subscribe([
197
- "NSE:SBIN:3045",
198
- "NSE:RELIANCE:2885"
199
- ])
204
+ # Subscribe with different modes
205
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks") # Will receive tick messages
206
+ ws.subscribe(["NSE:NIFTY25JULFUT:53216"], mode="greeks") # Will receive greek messages
207
+ ws.subscribe(["NSE:RELIANCE:2885"], mode="full") # Will receive both types
200
208
 
201
209
  def on_close(ws, code, reason):
202
210
  print(f"Connection closed [{code}]: {reason}")
@@ -206,6 +214,7 @@ def on_error(ws, error):
206
214
  print("Error:", error)
207
215
 
208
216
  client.on_tick = on_tick
217
+ client.on_stats = on_stats
209
218
  client.on_connect = on_connect
210
219
  client.on_close = on_close
211
220
  client.on_error = on_error
@@ -261,9 +270,13 @@ client = QuotesClient(
261
270
  def on_tick(ws, tick):
262
271
  logging.debug("Tick: %s", tick)
263
272
 
273
+ def on_stats(ws, stats):
274
+ logging.debug("Stats: %s", stats)
275
+
264
276
  def on_connect(ws):
265
277
  logging.info("Connected.")
266
- ws.subscribe(["NSE:SBIN:3045", "NSE:RELIANCE:2885"]) # no await
278
+ ws.subscribe(["NSE:SBIN:3045"], mode="ticks")
279
+ ws.subscribe(["NSE:NIFTY24JUN20100CE:20100"], mode="greeks")
267
280
 
268
281
  def on_close(ws, code, reason):
269
282
  logging.warning("Closed: %s", reason)
@@ -273,6 +286,7 @@ def on_error(ws, error):
273
286
  logging.error("Error: %s", error)
274
287
 
275
288
  client.on_tick = on_tick
289
+ client.on_stats = on_stats
276
290
  client.on_connect = on_connect
277
291
  client.on_close = on_close
278
292
  client.on_error = on_error
@@ -307,17 +321,58 @@ async def main():
307
321
  asyncio.run(main())
308
322
  ```
309
323
 
310
- #### Ticks structure
311
- ts = tick timestamp
312
- lastTradedTs = timestamp of the last executed trade in this instrument
324
+ #### Message Data Structures
313
325
 
314
- **New Fields Added:**
326
+ ##### Ticks structure
327
+ Messages with `type: 'ticks'` contain market data:
328
+ - `ts`: tick timestamp
329
+ - `lastTradedTs`: timestamp of the last executed trade in this instrument
315
330
  - `oi`: Current open interest (for futures and options)
316
331
  - `oiDayHigh`: Day's highest open interest value
317
332
  - `oiDayLow`: Day's lowest open interest value
318
333
 
334
+ ```json
335
+ {
336
+ "type": "ticks",
337
+ "identifier": "NSE:RELIANCE:2885",
338
+ "tradingSymbol": "RELIANCE",
339
+ "exchange": "NSE",
340
+ "segment": "NSECM",
341
+ "exchangeToken": 2885,
342
+ "bids": [{"volume": 1722, "price": 1295.5, "orders": 43}, ...],
343
+ "offers": [{"volume": 0, "price": 0, "orders": 0}, ...],
344
+ "ltp": 1295.5,
345
+ "lastTradedQty": 10,
346
+ "buyQty": 1722,
347
+ "sellQty": 0,
348
+ "volume": 10429964,
349
+ "avgPrice": 1291.46,
350
+ "netChange": 0,
351
+ "ohlc": {"open": 1270, "high": 1300.9, "low": 1267, "close": 1295.5},
352
+ "oi": 1234567,
353
+ "oiDayHigh": 1250000,
354
+ "oiDayLow": 1200000,
355
+ "lastTradedTs": "2025-04-21T10:29:33Z",
356
+ "ts": "2025-04-21T10:29:46Z"
357
+ }
319
358
  ```
320
- {'identifier': 'NSE:RELIANCE:2885', 'tradingSymbol': 'RELIANCE', 'exchange': 'NSE', 'segment': 'NSECM', 'exchangeToken': 2885, 'bids': [{'volume': 1722, 'price': 1295.5, 'orders': 43}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'offers': [{'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}, {'volume': 0, 'price': 0, 'orders': 0}], 'ltp': 1295.5, 'lastTradedQty': 10, 'buyQty': 1722, 'sellQty': 0, 'volume': 10429964, 'avgPrice': 1291.46, 'netChange': 0, 'ohlc': {'open': 1270, 'high': 1300.9, 'low': 1267, 'close': 1295.5}, 'oi': 1234567, 'oiDayHigh': 1250000, 'oiDayLow': 1200000, 'lastTradedTs': '2025-04-21T10:29:33Z', 'ts': '2025-04-21T10:29:46Z'}
359
+
360
+ ##### Greeks structure
361
+ Messages with `type: 'greeks'` contain options Greeks data:
362
+
363
+ ```json
364
+ {
365
+ "type": "greeks",
366
+ "identifier": "NSE:NIFTY25JULFUT:53216",
367
+ "tradingSymbol": "NIFTY25JULFUT",
368
+ "iv": 0.0,
369
+ "delta": 0.0,
370
+ "gamma": 0.0,
371
+ "theta": 0.0,
372
+ "vega": 0.0,
373
+ "rho": 0.0,
374
+ "ts": "2025-04-21T10:29:46Z"
375
+ }
321
376
  ```
322
377
 
323
378
  ## Wizzer Client
File without changes
File without changes