wiz-trader 0.3.0__tar.gz → 0.4.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.
- {wiz_trader-0.3.0/src/wiz_trader.egg-info → wiz_trader-0.4.0}/PKG-INFO +1 -1
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/pyproject.toml +1 -1
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/setup.py +1 -1
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader/__init__.py +1 -1
- wiz_trader-0.4.0/src/wiz_trader/apis/client.py +614 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0/src/wiz_trader.egg-info}/PKG-INFO +1 -1
- wiz_trader-0.3.0/src/wiz_trader/apis/client.py +0 -169
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/MANIFEST.in +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/README.md +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/setup.cfg +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader/apis/__init__.py +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader/quotes/__init__.py +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader/quotes/client.py +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader.egg-info/SOURCES.txt +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader.egg-info/dependency_links.txt +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader.egg-info/requires.txt +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/src/wiz_trader.egg-info/top_level.txt +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/tests/test_apis.py +0 -0
- {wiz_trader-0.3.0 → wiz_trader-0.4.0}/tests/test_quotes.py +0 -0
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
2
2
|
|
3
3
|
setup(
|
4
4
|
name='wiz_trader',
|
5
|
-
version='0.
|
5
|
+
version='0.4.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',
|
@@ -0,0 +1,614 @@
|
|
1
|
+
import os
|
2
|
+
import json
|
3
|
+
import logging
|
4
|
+
from typing import Dict, List, Optional, Union, Any
|
5
|
+
|
6
|
+
import requests
|
7
|
+
|
8
|
+
# Setup module-level logger with a default handler if none exists.
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
if not logger.handlers:
|
11
|
+
handler = logging.StreamHandler()
|
12
|
+
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
13
|
+
handler.setFormatter(formatter)
|
14
|
+
logger.addHandler(handler)
|
15
|
+
|
16
|
+
class WizzerClient:
|
17
|
+
"""
|
18
|
+
A Python SDK for connecting to the Wizzer's REST API.
|
19
|
+
|
20
|
+
Attributes:
|
21
|
+
base_url (str): Base URL of the Wizzer's API server.
|
22
|
+
token (str): JWT token for authentication.
|
23
|
+
log_level (str): Logging level. Options: "error", "info", "debug".
|
24
|
+
strategy_id (str): Default strategy ID to use if not provided in methods.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
base_url: Optional[str] = None,
|
30
|
+
token: Optional[str] = None,
|
31
|
+
strategy_id: Optional[str] = None,
|
32
|
+
log_level: str = "error" # default only errors
|
33
|
+
):
|
34
|
+
# Configure logger based on log_level.
|
35
|
+
valid_levels = {"error": logging.ERROR, "info": logging.INFO, "debug": logging.DEBUG}
|
36
|
+
if log_level not in valid_levels:
|
37
|
+
raise ValueError(f"log_level must be one of {list(valid_levels.keys())}")
|
38
|
+
logger.setLevel(valid_levels[log_level])
|
39
|
+
|
40
|
+
self.log_level = log_level
|
41
|
+
# System env vars take precedence over .env
|
42
|
+
self.base_url = base_url or os.environ.get("WZ__API_BASE_URL")
|
43
|
+
self.token = token or os.environ.get("WZ__TOKEN")
|
44
|
+
self.strategy_id = strategy_id or os.environ.get("WZ__STRATEGY_ID")
|
45
|
+
|
46
|
+
if not self.token:
|
47
|
+
raise ValueError("JWT token must be provided as an argument or in .env (WZ__TOKEN)")
|
48
|
+
if not self.base_url:
|
49
|
+
raise ValueError("Base URL must be provided as an argument or in .env (WZ__API_BASE_URL)")
|
50
|
+
|
51
|
+
# Prepare the authorization header
|
52
|
+
self.headers = {
|
53
|
+
"Authorization": f"Bearer {self.token}",
|
54
|
+
"Content-Type": "application/json"
|
55
|
+
}
|
56
|
+
|
57
|
+
logger.debug("Initialized WizzerClient with URL: %s", self.base_url)
|
58
|
+
|
59
|
+
def _get_strategy(self, strategy: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
60
|
+
"""
|
61
|
+
Get strategy information, either from the provided parameter or from the default.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
strategy (Optional[Dict[str, str]]): Strategy object with id, identifier, and name.
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
Dict[str, str]: A strategy object with at least the id field.
|
68
|
+
|
69
|
+
Raises:
|
70
|
+
ValueError: If no strategy is provided and no default is set.
|
71
|
+
"""
|
72
|
+
if strategy and "id" in strategy:
|
73
|
+
return strategy
|
74
|
+
|
75
|
+
if not self.strategy_id:
|
76
|
+
raise ValueError("Strategy ID must be provided either as a parameter or set in .env (WZ__STRATEGY_ID)")
|
77
|
+
|
78
|
+
return {"id": self.strategy_id}
|
79
|
+
|
80
|
+
# ===== DATA HUB METHODS =====
|
81
|
+
|
82
|
+
def get_indices(self, trading_symbol: Optional[str] = None, exchange: Optional[str] = None) -> List[Dict[str, Any]]:
|
83
|
+
"""
|
84
|
+
Get list of indices available on the exchange.
|
85
|
+
|
86
|
+
Args:
|
87
|
+
trading_symbol (Optional[str]): Filter by specific index symbol.
|
88
|
+
exchange (Optional[str]): Filter by specific exchange (NSE, BSE).
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
List[Dict[str, Any]]: List of index information.
|
92
|
+
"""
|
93
|
+
endpoint = "/datahub/indices"
|
94
|
+
params = {}
|
95
|
+
|
96
|
+
if trading_symbol:
|
97
|
+
params["tradingSymbol"] = trading_symbol
|
98
|
+
if exchange:
|
99
|
+
params["exchange"] = exchange
|
100
|
+
|
101
|
+
logger.debug("Fetching indices with params: %s", params)
|
102
|
+
response = self._make_request("GET", endpoint, params=params)
|
103
|
+
return response
|
104
|
+
|
105
|
+
def get_index_components(self, trading_symbol: str, exchange: str) -> List[Dict[str, Any]]:
|
106
|
+
"""
|
107
|
+
Get list of components (stocks) for a specific index.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
trading_symbol (str): Index symbol (e.g., "NIFTY 50").
|
111
|
+
exchange (str): Exchange name (NSE, BSE).
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
List[Dict[str, Any]]: List of component stocks in the index.
|
115
|
+
"""
|
116
|
+
endpoint = "/datahub/index/components"
|
117
|
+
params = {
|
118
|
+
"tradingSymbol": trading_symbol,
|
119
|
+
"exchange": exchange
|
120
|
+
}
|
121
|
+
|
122
|
+
logger.debug("Fetching index components with params: %s", params)
|
123
|
+
response = self._make_request("GET", endpoint, params=params)
|
124
|
+
return response
|
125
|
+
|
126
|
+
def get_historical_ohlcv(
|
127
|
+
self,
|
128
|
+
instruments: List[str],
|
129
|
+
start_date: str,
|
130
|
+
end_date: str,
|
131
|
+
ohlcv: List[str],
|
132
|
+
interval: str = "1d"
|
133
|
+
) -> List[Dict[str, Any]]:
|
134
|
+
"""
|
135
|
+
Get historical OHLCV data for specified instruments.
|
136
|
+
|
137
|
+
Args:
|
138
|
+
instruments (List[str]): List of instrument identifiers (e.g., ["NSE:SBIN:3045"]).
|
139
|
+
start_date (str): Start date in YYYY-MM-DD format.
|
140
|
+
end_date (str): End date in YYYY-MM-DD format.
|
141
|
+
ohlcv (List[str]): List of OHLCV fields to retrieve (open, high, low, close, volume).
|
142
|
+
interval (str, optional): Data interval. Options: "1d" (daily, default), "1M" (monthly - last trading day of month).
|
143
|
+
|
144
|
+
Returns:
|
145
|
+
List[Dict[str, Any]]: Historical data for requested instruments.
|
146
|
+
"""
|
147
|
+
endpoint = "/datahub/historical/ohlcv"
|
148
|
+
data = {
|
149
|
+
"instruments": instruments,
|
150
|
+
"startDate": start_date,
|
151
|
+
"endDate": end_date,
|
152
|
+
"ohlcv": ohlcv,
|
153
|
+
"interval": interval
|
154
|
+
}
|
155
|
+
|
156
|
+
logger.debug("Fetching historical OHLCV with data: %s", data)
|
157
|
+
response = self._make_request("POST", endpoint, json=data)
|
158
|
+
return response
|
159
|
+
|
160
|
+
# ===== ORDER MANAGEMENT METHODS =====
|
161
|
+
|
162
|
+
def place_order(
|
163
|
+
self,
|
164
|
+
exchange: str,
|
165
|
+
trading_symbol: str,
|
166
|
+
transaction_type: str,
|
167
|
+
quantity: int,
|
168
|
+
order_type: str = "MARKET",
|
169
|
+
product: str = "CNC",
|
170
|
+
price: float = 0,
|
171
|
+
trigger_price: float = 0,
|
172
|
+
disclosed_qty: int = 0,
|
173
|
+
validity: str = "DAY",
|
174
|
+
variety: str = "REGULAR",
|
175
|
+
stoploss: float = 0,
|
176
|
+
target: float = 0,
|
177
|
+
segment: Optional[str] = None,
|
178
|
+
exchange_token: Optional[int] = None,
|
179
|
+
broker: str = "",
|
180
|
+
strategy: Optional[Dict[str, str]] = None
|
181
|
+
) -> Dict[str, Any]:
|
182
|
+
"""
|
183
|
+
Place a regular order.
|
184
|
+
|
185
|
+
Args:
|
186
|
+
exchange (str): Exchange code (e.g., "NSE", "BSE").
|
187
|
+
trading_symbol (str): Symbol of the instrument.
|
188
|
+
transaction_type (str): "BUY" or "SELL".
|
189
|
+
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".
|
192
|
+
price (float, optional): Price for limit orders. Defaults to 0.
|
193
|
+
trigger_price (float, optional): Trigger price for stop orders. Defaults to 0.
|
194
|
+
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".
|
197
|
+
stoploss (float, optional): Stop loss price. Defaults to 0.
|
198
|
+
target (float, optional): Target price. Defaults to 0.
|
199
|
+
segment (Optional[str], optional): Market segment. If None, determined from exchange.
|
200
|
+
exchange_token (Optional[int], optional): Exchange token for the instrument.
|
201
|
+
broker (str, optional): Broker code.
|
202
|
+
strategy (Optional[Dict[str, str]], optional): Strategy information. If None, uses default.
|
203
|
+
|
204
|
+
Returns:
|
205
|
+
Dict[str, Any]: Order response containing orderId.
|
206
|
+
"""
|
207
|
+
endpoint = "/orders"
|
208
|
+
|
209
|
+
# Determine segment if not provided
|
210
|
+
if not segment:
|
211
|
+
segment = f"{exchange}CM"
|
212
|
+
|
213
|
+
# Get strategy information
|
214
|
+
strategy_info = self._get_strategy(strategy)
|
215
|
+
|
216
|
+
data = {
|
217
|
+
"exchange": exchange,
|
218
|
+
"tradingSymbol": trading_symbol,
|
219
|
+
"transactionType": transaction_type,
|
220
|
+
"qty": quantity,
|
221
|
+
"orderType": order_type,
|
222
|
+
"product": product,
|
223
|
+
"price": price,
|
224
|
+
"triggerPrice": trigger_price,
|
225
|
+
"disclosedQty": disclosed_qty,
|
226
|
+
"validity": validity,
|
227
|
+
"variety": variety,
|
228
|
+
"stoploss": stoploss,
|
229
|
+
"target": target,
|
230
|
+
"segment": segment,
|
231
|
+
"broker": broker,
|
232
|
+
"strategy": strategy_info
|
233
|
+
}
|
234
|
+
|
235
|
+
# Add exchange token if provided
|
236
|
+
if exchange_token:
|
237
|
+
data["exchangeToken"] = exchange_token
|
238
|
+
|
239
|
+
logger.debug("Placing order: %s", data)
|
240
|
+
return self._make_request("POST", endpoint, json=data)
|
241
|
+
|
242
|
+
def modify_order(
|
243
|
+
self,
|
244
|
+
order_id: str,
|
245
|
+
**params
|
246
|
+
) -> Dict[str, Any]:
|
247
|
+
"""
|
248
|
+
Modify an existing order.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
order_id (str): Order ID to modify.
|
252
|
+
**params: Parameters to update in the order.
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
Dict[str, Any]: Order response containing orderId.
|
256
|
+
"""
|
257
|
+
endpoint = f"/orders/{order_id}"
|
258
|
+
|
259
|
+
logger.debug("Modifying order %s with params: %s", order_id, params)
|
260
|
+
return self._make_request("PATCH", endpoint, json=params)
|
261
|
+
|
262
|
+
def cancel_order(self, order_id: str) -> Dict[str, Any]:
|
263
|
+
"""
|
264
|
+
Cancel an existing order.
|
265
|
+
|
266
|
+
Args:
|
267
|
+
order_id (str): Order ID to cancel.
|
268
|
+
|
269
|
+
Returns:
|
270
|
+
Dict[str, Any]: Response with the cancelled order ID.
|
271
|
+
"""
|
272
|
+
endpoint = f"/orders/{order_id}"
|
273
|
+
|
274
|
+
logger.debug("Cancelling order: %s", order_id)
|
275
|
+
return self._make_request("DELETE", endpoint)
|
276
|
+
|
277
|
+
def get_positions(self) -> List[Dict[str, Any]]:
|
278
|
+
"""
|
279
|
+
Get current portfolio positions.
|
280
|
+
|
281
|
+
Returns:
|
282
|
+
List[Dict[str, Any]]: List of positions.
|
283
|
+
"""
|
284
|
+
endpoint = "/portfolios/positions"
|
285
|
+
|
286
|
+
logger.debug("Fetching positions")
|
287
|
+
return self._make_request("GET", endpoint)
|
288
|
+
|
289
|
+
def get_holdings(self, portfolios: Optional[str] = "default") -> List[Dict[str, Any]]:
|
290
|
+
"""
|
291
|
+
Get current holdings.
|
292
|
+
|
293
|
+
Args:
|
294
|
+
portfolios (str, optional): Portfolio name. Defaults to "default".
|
295
|
+
|
296
|
+
Returns:
|
297
|
+
List[Dict[str, Any]]: List of holdings.
|
298
|
+
"""
|
299
|
+
endpoint = "/portfolios/holdings"
|
300
|
+
params = {"portfolios": portfolios}
|
301
|
+
|
302
|
+
logger.debug("Fetching holdings for portfolio: %s", portfolios)
|
303
|
+
return self._make_request("GET", endpoint, params=params)
|
304
|
+
|
305
|
+
# ===== BASKET MANAGEMENT METHODS =====
|
306
|
+
|
307
|
+
def create_basket(
|
308
|
+
self,
|
309
|
+
name: str,
|
310
|
+
instruments: List[Dict[str, Any]],
|
311
|
+
weightage_scheme: str = "equi_weighted",
|
312
|
+
capital: Optional[Dict[str, float]] = None,
|
313
|
+
instrument_types: Optional[List[str]] = None,
|
314
|
+
trading_symbol: Optional[str] = None
|
315
|
+
) -> Dict[str, Any]:
|
316
|
+
"""
|
317
|
+
Create a new basket.
|
318
|
+
|
319
|
+
Args:
|
320
|
+
name (str): Name of the basket.
|
321
|
+
instruments (List[Dict[str, Any]]): List of instruments with weightage and shares.
|
322
|
+
weightage_scheme (str, optional): Weightage scheme. Defaults to "equi_weighted".
|
323
|
+
capital (Optional[Dict[str, float]], optional): Capital allocation. Defaults to {"minValue": 0, "actualValue": 0}.
|
324
|
+
instrument_types (Optional[List[str]], optional): Types of instruments. Defaults to ["EQLC"].
|
325
|
+
|
326
|
+
Returns:
|
327
|
+
Dict[str, Any]: Basket information.
|
328
|
+
"""
|
329
|
+
endpoint = "/baskets"
|
330
|
+
|
331
|
+
# Set defaults
|
332
|
+
if capital is None:
|
333
|
+
capital = {"minValue": 0, "actualValue": 0}
|
334
|
+
|
335
|
+
data = {
|
336
|
+
"name": name,
|
337
|
+
"weightageScheme": weightage_scheme,
|
338
|
+
"instruments": instruments,
|
339
|
+
"capital": capital,
|
340
|
+
"instrumentTypes": instrument_types
|
341
|
+
}
|
342
|
+
|
343
|
+
logger.debug("Creating basket: %s", data)
|
344
|
+
return self._make_request("POST", endpoint, json=data)
|
345
|
+
|
346
|
+
def get_baskets(self) -> List[Dict[str, Any]]:
|
347
|
+
"""
|
348
|
+
Get all baskets.
|
349
|
+
|
350
|
+
Returns:
|
351
|
+
List[Dict[str, Any]]: List of baskets.
|
352
|
+
"""
|
353
|
+
endpoint = "/baskets"
|
354
|
+
|
355
|
+
logger.debug("Fetching baskets")
|
356
|
+
return self._make_request("GET", endpoint)
|
357
|
+
|
358
|
+
def get_basket(self, basket_id: str) -> Dict[str, Any]:
|
359
|
+
"""
|
360
|
+
Get a specific basket by ID.
|
361
|
+
|
362
|
+
Args:
|
363
|
+
basket_id (str): Basket ID.
|
364
|
+
|
365
|
+
Returns:
|
366
|
+
Dict[str, Any]: Basket information.
|
367
|
+
"""
|
368
|
+
endpoint = f"/baskets/{basket_id}"
|
369
|
+
|
370
|
+
logger.debug("Fetching basket: %s", basket_id)
|
371
|
+
return self._make_request("GET", endpoint)
|
372
|
+
|
373
|
+
def get_basket_instruments(self, basket_id: str) -> List[Dict[str, Any]]:
|
374
|
+
"""
|
375
|
+
Get instruments in a basket.
|
376
|
+
|
377
|
+
Args:
|
378
|
+
basket_id (str): Basket ID.
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
List[Dict[str, Any]]: List of instruments in the basket.
|
382
|
+
"""
|
383
|
+
endpoint = f"/baskets/{basket_id}/instruments"
|
384
|
+
|
385
|
+
logger.debug("Fetching instruments for basket: %s", basket_id)
|
386
|
+
return self._make_request("GET", endpoint)
|
387
|
+
|
388
|
+
def place_basket_order(
|
389
|
+
self,
|
390
|
+
trading_symbol: str,
|
391
|
+
transaction_type: str,
|
392
|
+
quantity: float,
|
393
|
+
price: float = 0,
|
394
|
+
order_type: str = "MARKET",
|
395
|
+
product: str = "CNC",
|
396
|
+
validity: str = "DAY",
|
397
|
+
exchange_token: Optional[int] = None,
|
398
|
+
trigger_price: float = 0,
|
399
|
+
stoploss: float = 0,
|
400
|
+
target: float = 0,
|
401
|
+
broker: str = "wizzer",
|
402
|
+
variety: str = "REGULAR",
|
403
|
+
strategy: Optional[Dict[str, str]] = None,
|
404
|
+
disclosed_qty: int = 0,
|
405
|
+
sl_applied_level: Optional[str] = None
|
406
|
+
) -> Dict[str, Any]:
|
407
|
+
"""
|
408
|
+
Place a basket order.
|
409
|
+
|
410
|
+
Args:
|
411
|
+
trading_symbol (str): Basket trading symbol (e.g., "/BASKET_NAME").
|
412
|
+
transaction_type (str): "BUY" or "SELL".
|
413
|
+
quantity (float): Quantity/units of the basket.
|
414
|
+
price (float, optional): Price for limit orders. Defaults to 0.
|
415
|
+
order_type (str, optional): Order type. Defaults to "MARKET".
|
416
|
+
product (str, optional): Product code. Defaults to "CNC".
|
417
|
+
validity (str, optional): Order validity. Defaults to "DAY".
|
418
|
+
exchange_token (Optional[int], optional): Exchange token for the basket.
|
419
|
+
trigger_price (float, optional): Trigger price. Defaults to 0.
|
420
|
+
stoploss (float, optional): Stop loss price. Defaults to 0.
|
421
|
+
target (float, optional): Target price. Defaults to 0.
|
422
|
+
broker (str, optional): Broker code. Defaults to "wizzer".
|
423
|
+
variety (str, optional): Order variety. Defaults to "REGULAR".
|
424
|
+
strategy (Optional[Dict[str, str]], optional): Strategy information. If None, uses default.
|
425
|
+
disclosed_qty (int, optional): Disclosed quantity. Defaults to 0.
|
426
|
+
sl_applied_level (Optional[str], optional): Stop loss applied level (e.g., "basket").
|
427
|
+
|
428
|
+
Returns:
|
429
|
+
Dict[str, Any]: Order response containing orderId.
|
430
|
+
"""
|
431
|
+
endpoint = "/orders/basket"
|
432
|
+
|
433
|
+
# Get strategy information
|
434
|
+
strategy_info = self._get_strategy(strategy)
|
435
|
+
|
436
|
+
data = {
|
437
|
+
"tradingSymbol": trading_symbol,
|
438
|
+
"exchange": "WZR",
|
439
|
+
"transactionType": transaction_type,
|
440
|
+
"qty": quantity,
|
441
|
+
"price": price,
|
442
|
+
"orderType": order_type,
|
443
|
+
"product": product,
|
444
|
+
"validity": validity,
|
445
|
+
"triggerPrice": trigger_price,
|
446
|
+
"stoploss": stoploss,
|
447
|
+
"target": target,
|
448
|
+
"broker": broker,
|
449
|
+
"variety": variety,
|
450
|
+
"strategy": strategy_info,
|
451
|
+
"segment": "WZREQ",
|
452
|
+
"disclosedQty": disclosed_qty
|
453
|
+
}
|
454
|
+
|
455
|
+
# Add exchange token if provided
|
456
|
+
if exchange_token:
|
457
|
+
data["exchangeToken"] = exchange_token
|
458
|
+
|
459
|
+
# Add stop loss level if provided
|
460
|
+
if sl_applied_level:
|
461
|
+
data["slAppliedLevel"] = sl_applied_level
|
462
|
+
|
463
|
+
logger.debug("Placing basket order: %s", data)
|
464
|
+
return self._make_request("POST", endpoint, json=data)
|
465
|
+
|
466
|
+
def place_basket_exit_order(
|
467
|
+
self,
|
468
|
+
trading_symbol: str,
|
469
|
+
exchange: str,
|
470
|
+
transaction_type: str,
|
471
|
+
quantity: float,
|
472
|
+
exchange_token: int,
|
473
|
+
**kwargs
|
474
|
+
) -> Dict[str, Any]:
|
475
|
+
"""
|
476
|
+
Place a basket exit order.
|
477
|
+
|
478
|
+
Args:
|
479
|
+
trading_symbol (str): Basket trading symbol.
|
480
|
+
exchange (str): Exchange code (usually "WZR" for baskets).
|
481
|
+
transaction_type (str): "BUY" or "SELL" (usually "SELL" for exit).
|
482
|
+
quantity (float): Quantity/units of the basket.
|
483
|
+
exchange_token (int): Exchange token for the basket.
|
484
|
+
**kwargs: Additional parameters for the order.
|
485
|
+
|
486
|
+
Returns:
|
487
|
+
Dict[str, Any]: Order response containing orderId.
|
488
|
+
"""
|
489
|
+
endpoint = "/orders/basket/exit"
|
490
|
+
|
491
|
+
# Build base data
|
492
|
+
data = {
|
493
|
+
"tradingSymbol": trading_symbol,
|
494
|
+
"exchange": exchange,
|
495
|
+
"transactionType": transaction_type,
|
496
|
+
"qty": quantity,
|
497
|
+
"exchangeToken": exchange_token,
|
498
|
+
**kwargs
|
499
|
+
}
|
500
|
+
|
501
|
+
# Set strategy if not in kwargs
|
502
|
+
if "strategy" not in kwargs:
|
503
|
+
data["strategy"] = self._get_strategy(None)
|
504
|
+
|
505
|
+
# Set defaults if not in kwargs
|
506
|
+
defaults = {
|
507
|
+
"orderType": "MARKET",
|
508
|
+
"product": "CNC",
|
509
|
+
"validity": "DAY",
|
510
|
+
"disclosedQty": 0,
|
511
|
+
"price": 0,
|
512
|
+
"variety": "REGULAR",
|
513
|
+
"stoploss": 0,
|
514
|
+
"broker": "wizzer",
|
515
|
+
"triggerPrice": 0,
|
516
|
+
"target": 0,
|
517
|
+
"segment": "WZREQ"
|
518
|
+
}
|
519
|
+
|
520
|
+
for key, value in defaults.items():
|
521
|
+
if key not in data:
|
522
|
+
data[key] = value
|
523
|
+
|
524
|
+
logger.debug("Placing basket exit order: %s", data)
|
525
|
+
return self._make_request("POST", endpoint, json=data)
|
526
|
+
|
527
|
+
def modify_basket_order(
|
528
|
+
self,
|
529
|
+
order_id: str,
|
530
|
+
**params
|
531
|
+
) -> Dict[str, Any]:
|
532
|
+
"""
|
533
|
+
Modify an existing basket order.
|
534
|
+
|
535
|
+
Args:
|
536
|
+
order_id (str): Order ID to modify.
|
537
|
+
**params: Parameters to update in the order.
|
538
|
+
|
539
|
+
Returns:
|
540
|
+
Dict[str, Any]: Order response containing orderId.
|
541
|
+
"""
|
542
|
+
endpoint = f"/orders/basket/{order_id}"
|
543
|
+
|
544
|
+
logger.debug("Modifying basket order %s with params: %s", order_id, params)
|
545
|
+
return self._make_request("PATCH", endpoint, json=params)
|
546
|
+
|
547
|
+
def rebalance_basket(
|
548
|
+
self,
|
549
|
+
trading_symbol: str,
|
550
|
+
instruments: List[str]
|
551
|
+
) -> Dict[str, Any]:
|
552
|
+
"""
|
553
|
+
Rebalance a basket with new instruments.
|
554
|
+
|
555
|
+
Args:
|
556
|
+
trading_symbol (str): Basket trading symbol.
|
557
|
+
instruments (List[str]): List of instrument identifiers for the new basket composition.
|
558
|
+
|
559
|
+
Returns:
|
560
|
+
Dict[str, Any]: Rebalance response.
|
561
|
+
"""
|
562
|
+
endpoint = "/baskets/rebalance"
|
563
|
+
|
564
|
+
data = {
|
565
|
+
"tradingSymbol": trading_symbol,
|
566
|
+
"instruments": instruments
|
567
|
+
}
|
568
|
+
|
569
|
+
logger.debug("Rebalancing basket %s with instruments: %s", trading_symbol, instruments)
|
570
|
+
return self._make_request("POST", endpoint, json=data)
|
571
|
+
|
572
|
+
def _make_request(
|
573
|
+
self,
|
574
|
+
method: str,
|
575
|
+
endpoint: str,
|
576
|
+
params: Optional[Dict[str, str]] = None,
|
577
|
+
json: Optional[Dict[str, Any]] = None,
|
578
|
+
headers: Optional[Dict[str, str]] = None
|
579
|
+
) -> Any:
|
580
|
+
"""
|
581
|
+
Make an HTTP request to the API.
|
582
|
+
|
583
|
+
Args:
|
584
|
+
method (str): HTTP method (GET, POST, etc.)
|
585
|
+
endpoint (str): API endpoint path.
|
586
|
+
params (Optional[Dict[str, str]]): Query parameters for GET requests.
|
587
|
+
json (Optional[Dict[str, Any]]): JSON payload for POST requests.
|
588
|
+
headers (Optional[Dict[str, str]]): Custom headers to override the defaults.
|
589
|
+
|
590
|
+
Returns:
|
591
|
+
Any: Parsed JSON response.
|
592
|
+
|
593
|
+
Raises:
|
594
|
+
requests.RequestException: If the request fails.
|
595
|
+
"""
|
596
|
+
url = f"{self.base_url}{endpoint}"
|
597
|
+
request_headers = headers if headers else self.headers
|
598
|
+
|
599
|
+
try:
|
600
|
+
logger.debug("%s request to %s", method, url)
|
601
|
+
response = requests.request(
|
602
|
+
method=method,
|
603
|
+
url=url,
|
604
|
+
headers=request_headers,
|
605
|
+
params=params,
|
606
|
+
json=json
|
607
|
+
)
|
608
|
+
response.raise_for_status()
|
609
|
+
return response.json()
|
610
|
+
except requests.RequestException as e:
|
611
|
+
logger.error("API request failed: %s", e, exc_info=True)
|
612
|
+
if hasattr(e.response, 'text'):
|
613
|
+
logger.error("Response content: %s", e.response.text)
|
614
|
+
raise
|
@@ -1,169 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import json
|
3
|
-
import logging
|
4
|
-
from typing import Dict, List, Optional, Union, Any
|
5
|
-
|
6
|
-
import requests
|
7
|
-
|
8
|
-
# Setup module-level logger with a default handler if none exists.
|
9
|
-
logger = logging.getLogger(__name__)
|
10
|
-
if not logger.handlers:
|
11
|
-
handler = logging.StreamHandler()
|
12
|
-
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
13
|
-
handler.setFormatter(formatter)
|
14
|
-
logger.addHandler(handler)
|
15
|
-
|
16
|
-
class WizzerClient:
|
17
|
-
"""
|
18
|
-
A Python SDK for connecting to the Wizzer's REST API.
|
19
|
-
|
20
|
-
Attributes:
|
21
|
-
base_url (str): Base URL of the Wizzer's API server.
|
22
|
-
token (str): JWT token for authentication.
|
23
|
-
log_level (str): Logging level. Options: "error", "info", "debug".
|
24
|
-
"""
|
25
|
-
|
26
|
-
def __init__(
|
27
|
-
self,
|
28
|
-
base_url: Optional[str] = None,
|
29
|
-
token: Optional[str] = None,
|
30
|
-
log_level: str = "error" # default only errors
|
31
|
-
):
|
32
|
-
# Configure logger based on log_level.
|
33
|
-
valid_levels = {"error": logging.ERROR, "info": logging.INFO, "debug": logging.DEBUG}
|
34
|
-
if log_level not in valid_levels:
|
35
|
-
raise ValueError(f"log_level must be one of {list(valid_levels.keys())}")
|
36
|
-
logger.setLevel(valid_levels[log_level])
|
37
|
-
|
38
|
-
self.log_level = log_level
|
39
|
-
# System env vars take precedence over .env
|
40
|
-
self.base_url = base_url or os.environ.get("WZ__API_BASE_URL")
|
41
|
-
self.token = token or os.environ.get("WZ__TOKEN")
|
42
|
-
if not self.token:
|
43
|
-
raise ValueError("JWT token must be provided as an argument or in .env (WZ__TOKEN)")
|
44
|
-
if not self.base_url:
|
45
|
-
raise ValueError("Base URL must be provided as an argument or in .env (WZ__API_BASE_URL)")
|
46
|
-
|
47
|
-
# Prepare the authorization header
|
48
|
-
self.headers = {
|
49
|
-
"Authorization": f"Bearer {self.token}",
|
50
|
-
"Content-Type": "application/json"
|
51
|
-
}
|
52
|
-
|
53
|
-
logger.debug("Initialized WizzerClient with URL: %s", self.base_url)
|
54
|
-
|
55
|
-
def get_indices(self, trading_symbol: Optional[str] = None, exchange: Optional[str] = None) -> List[Dict[str, Any]]:
|
56
|
-
"""
|
57
|
-
Get list of indices available on the exchange.
|
58
|
-
|
59
|
-
Args:
|
60
|
-
trading_symbol (Optional[str]): Filter by specific index symbol.
|
61
|
-
exchange (Optional[str]): Filter by specific exchange (NSE, BSE).
|
62
|
-
|
63
|
-
Returns:
|
64
|
-
List[Dict[str, Any]]: List of index information.
|
65
|
-
"""
|
66
|
-
endpoint = "/datahub/indices"
|
67
|
-
params = {}
|
68
|
-
|
69
|
-
if trading_symbol:
|
70
|
-
params["tradingSymbol"] = trading_symbol
|
71
|
-
if exchange:
|
72
|
-
params["exchange"] = exchange
|
73
|
-
|
74
|
-
logger.debug("Fetching indices with params: %s", params)
|
75
|
-
response = self._make_request("GET", endpoint, params=params)
|
76
|
-
return response
|
77
|
-
|
78
|
-
def get_index_components(self, trading_symbol: str, exchange: str) -> List[Dict[str, Any]]:
|
79
|
-
"""
|
80
|
-
Get list of components (stocks) for a specific index.
|
81
|
-
|
82
|
-
Args:
|
83
|
-
trading_symbol (str): Index symbol (e.g., "NIFTY 50").
|
84
|
-
exchange (str): Exchange name (NSE, BSE).
|
85
|
-
|
86
|
-
Returns:
|
87
|
-
List[Dict[str, Any]]: List of component stocks in the index.
|
88
|
-
"""
|
89
|
-
endpoint = "/datahub/index/components"
|
90
|
-
params = {
|
91
|
-
"tradingSymbol": trading_symbol,
|
92
|
-
"exchange": exchange
|
93
|
-
}
|
94
|
-
|
95
|
-
logger.debug("Fetching index components with params: %s", params)
|
96
|
-
response = self._make_request("GET", endpoint, params=params)
|
97
|
-
return response
|
98
|
-
|
99
|
-
def get_historical_ohlcv(
|
100
|
-
self,
|
101
|
-
instruments: List[str],
|
102
|
-
start_date: str,
|
103
|
-
end_date: str,
|
104
|
-
ohlcv: List[str]
|
105
|
-
) -> List[Dict[str, Any]]:
|
106
|
-
"""
|
107
|
-
Get historical OHLCV data for specified instruments.
|
108
|
-
|
109
|
-
Args:
|
110
|
-
instruments (List[str]): List of instrument identifiers (e.g., ["NSE:SBIN:3045"]).
|
111
|
-
start_date (str): Start date in YYYY-MM-DD format.
|
112
|
-
end_date (str): End date in YYYY-MM-DD format.
|
113
|
-
ohlcv (List[str]): List of OHLCV fields to retrieve (open, high, low, close, volume).
|
114
|
-
|
115
|
-
Returns:
|
116
|
-
List[Dict[str, Any]]: Historical data for requested instruments.
|
117
|
-
"""
|
118
|
-
endpoint = "/datahub/historical/ohlcv"
|
119
|
-
data = {
|
120
|
-
"instruments": instruments,
|
121
|
-
"startDate": start_date,
|
122
|
-
"endDate": end_date,
|
123
|
-
"ohlcv": ohlcv
|
124
|
-
}
|
125
|
-
|
126
|
-
logger.debug("Fetching historical OHLCV with data: %s", data)
|
127
|
-
response = self._make_request("POST", endpoint, json=data)
|
128
|
-
return response
|
129
|
-
|
130
|
-
def _make_request(
|
131
|
-
self,
|
132
|
-
method: str,
|
133
|
-
endpoint: str,
|
134
|
-
params: Optional[Dict[str, str]] = None,
|
135
|
-
json: Optional[Dict[str, Any]] = None
|
136
|
-
) -> Any:
|
137
|
-
"""
|
138
|
-
Make an HTTP request to the DataHub API.
|
139
|
-
|
140
|
-
Args:
|
141
|
-
method (str): HTTP method (GET, POST, etc.)
|
142
|
-
endpoint (str): API endpoint path.
|
143
|
-
params (Optional[Dict[str, str]]): Query parameters for GET requests.
|
144
|
-
json (Optional[Dict[str, Any]]): JSON payload for POST requests.
|
145
|
-
|
146
|
-
Returns:
|
147
|
-
Any: Parsed JSON response.
|
148
|
-
|
149
|
-
Raises:
|
150
|
-
requests.RequestException: If the request fails.
|
151
|
-
"""
|
152
|
-
url = f"{self.base_url}{endpoint}"
|
153
|
-
|
154
|
-
try:
|
155
|
-
logger.debug("%s request to %s", method, url)
|
156
|
-
response = requests.request(
|
157
|
-
method=method,
|
158
|
-
url=url,
|
159
|
-
headers=self.headers,
|
160
|
-
params=params,
|
161
|
-
json=json
|
162
|
-
)
|
163
|
-
response.raise_for_status()
|
164
|
-
return response.json()
|
165
|
-
except requests.RequestException as e:
|
166
|
-
logger.error("API request failed: %s", e, exc_info=True)
|
167
|
-
if hasattr(e.response, 'text'):
|
168
|
-
logger.error("Response content: %s", e.response.text)
|
169
|
-
raise
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|