wiz-trader 0.17.0__py3-none-any.whl → 0.19.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 +1 -1
- wiz_trader/apis/client.py +176 -10
- {wiz_trader-0.17.0.dist-info → wiz_trader-0.19.0.dist-info}/METADATA +635 -128
- wiz_trader-0.19.0.dist-info/RECORD +9 -0
- {wiz_trader-0.17.0.dist-info → wiz_trader-0.19.0.dist-info}/WHEEL +1 -1
- wiz_trader-0.17.0.dist-info/RECORD +0 -9
- {wiz_trader-0.17.0.dist-info → wiz_trader-0.19.0.dist-info}/top_level.txt +0 -0
wiz_trader/__init__.py
CHANGED
wiz_trader/apis/client.py
CHANGED
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
import json
|
3
3
|
import logging
|
4
4
|
from typing import Dict, List, Optional, Union, Any
|
5
|
+
from datetime import datetime
|
5
6
|
|
6
7
|
import requests
|
7
8
|
|
@@ -86,6 +87,70 @@ class WizzerClient:
|
|
86
87
|
MONEYNESS_ATM = "ATM"
|
87
88
|
MONEYNESS_ITM = "ITM"
|
88
89
|
MONEYNESS_OTM = "OTM"
|
90
|
+
|
91
|
+
# Expiry preference constants
|
92
|
+
EXPIRY_CURRENT_WEEKLY = "current_weekly"
|
93
|
+
EXPIRY_NEAR_MONTH = "near_month"
|
94
|
+
EXPIRY_MID_MONTH = "mid_month"
|
95
|
+
EXPIRY_FAR_MONTH = "far_month"
|
96
|
+
EXPIRY_FIRST_WEEKLY = "first_weekly"
|
97
|
+
EXPIRY_SECOND_WEEKLY = "second_weekly"
|
98
|
+
EXPIRY_THIRD_WEEKLY = "third_weekly"
|
99
|
+
EXPIRY_FOURTH_WEEKLY = "fourth_weekly"
|
100
|
+
EXPIRY_FIFTH_WEEKLY = "fifth_weekly"
|
101
|
+
EXPIRY_FIRST_WEEKLY_MID_MONTH = "first_weekly_mid_month"
|
102
|
+
EXPIRY_SECOND_WEEKLY_MID_MONTH = "second_weekly_mid_month"
|
103
|
+
EXPIRY_THIRD_WEEKLY_MID_MONTH = "third_weekly_mid_month"
|
104
|
+
EXPIRY_FOURTH_WEEKLY_MID_MONTH = "fourth_weekly_mid_month"
|
105
|
+
EXPIRY_FIFTH_WEEKLY_MID_MONTH = "fifth_weekly_mid_month"
|
106
|
+
EXPIRY_FIRST_WEEKLY_FAR_MONTH = "first_weekly_far_month"
|
107
|
+
EXPIRY_SECOND_WEEKLY_FAR_MONTH = "second_weekly_far_month"
|
108
|
+
EXPIRY_THIRD_WEEKLY_FAR_MONTH = "third_weekly_far_month"
|
109
|
+
EXPIRY_FOURTH_WEEKLY_FAR_MONTH = "fourth_weekly_far_month"
|
110
|
+
EXPIRY_FIFTH_WEEKLY_FAR_MONTH = "fifth_weekly_far_month"
|
111
|
+
EXPIRY_FIRST_QUARTER = "first_quarter"
|
112
|
+
EXPIRY_SECOND_QUARTER = "second_quarter"
|
113
|
+
EXPIRY_THIRD_QUARTER = "third_quarter"
|
114
|
+
EXPIRY_FOURTH_QUARTER = "fourth_quarter"
|
115
|
+
EXPIRY_FIRST_HALF = "first_half_yearly"
|
116
|
+
EXPIRY_SECOND_HALF = "second_half_yearly"
|
117
|
+
EXPIRY_FIRST_QUARTER_PLUS_1 = "first_quarter_plus_1"
|
118
|
+
EXPIRY_SECOND_QUARTER_PLUS_1 = "second_quarter_plus_1"
|
119
|
+
EXPIRY_THIRD_QUARTER_PLUS_1 = "third_quarter_plus_1"
|
120
|
+
EXPIRY_FOURTH_QUARTER_PLUS_1 = "fourth_quarter_plus_1"
|
121
|
+
EXPIRY_FIRST_HALF_PLUS_1 = "first_half_yearly_plus_1"
|
122
|
+
EXPIRY_SECOND_HALF_PLUS_1 = "second_half_yearly_plus_1"
|
123
|
+
EXPIRY_FIRST_QUARTER_PLUS_2 = "first_quarter_plus_2"
|
124
|
+
EXPIRY_SECOND_QUARTER_PLUS_2 = "second_quarter_plus_2"
|
125
|
+
EXPIRY_THIRD_QUARTER_PLUS_2 = "third_quarter_plus_2"
|
126
|
+
EXPIRY_FOURTH_QUARTER_PLUS_2 = "fourth_quarter_plus_2"
|
127
|
+
EXPIRY_FIRST_HALF_PLUS_2 = "first_half_yearly_plus_2"
|
128
|
+
EXPIRY_SECOND_HALF_PLUS_2 = "second_half_yearly_plus_2"
|
129
|
+
EXPIRY_FIRST_QUARTER_PLUS_3 = "first_quarter_plus_3"
|
130
|
+
EXPIRY_SECOND_QUARTER_PLUS_3 = "second_quarter_plus_3"
|
131
|
+
EXPIRY_THIRD_QUARTER_PLUS_3 = "third_quarter_plus_3"
|
132
|
+
EXPIRY_FOURTH_QUARTER_PLUS_3 = "fourth_quarter_plus_3"
|
133
|
+
EXPIRY_FIRST_HALF_PLUS_3 = "first_half_yearly_plus_3"
|
134
|
+
EXPIRY_SECOND_HALF_PLUS_3 = "second_half_yearly_plus_3"
|
135
|
+
EXPIRY_FIRST_QUARTER_PLUS_4 = "first_quarter_plus_4"
|
136
|
+
EXPIRY_SECOND_QUARTER_PLUS_4 = "second_quarter_plus_4"
|
137
|
+
EXPIRY_THIRD_QUARTER_PLUS_4 = "third_quarter_plus_4"
|
138
|
+
EXPIRY_FOURTH_QUARTER_PLUS_4 = "fourth_quarter_plus_4"
|
139
|
+
EXPIRY_FIRST_HALF_PLUS_4 = "first_half_yearly_plus_4"
|
140
|
+
EXPIRY_SECOND_HALF_PLUS_4 = "second_half_yearly_plus_4"
|
141
|
+
|
142
|
+
# Historical data interval constants
|
143
|
+
INTERVAL_1_MINUTE = "1m"
|
144
|
+
INTERVAL_2_MINUTES = "2m"
|
145
|
+
INTERVAL_3_MINUTES = "3m"
|
146
|
+
INTERVAL_5_MINUTES = "5m"
|
147
|
+
INTERVAL_10_MINUTES = "10m"
|
148
|
+
INTERVAL_15_MINUTES = "15m"
|
149
|
+
INTERVAL_30_MINUTES = "30m"
|
150
|
+
INTERVAL_45_MINUTES = "45m"
|
151
|
+
INTERVAL_1_HOUR = "1h"
|
152
|
+
INTERVAL_1_DAY = "1d"
|
153
|
+
INTERVAL_1_MONTH = "1M"
|
89
154
|
|
90
155
|
# URIs to various API endpoints
|
91
156
|
_routes = {
|
@@ -121,7 +186,8 @@ class WizzerClient:
|
|
121
186
|
|
122
187
|
# Instrument & asset class endpoints
|
123
188
|
"instrument.metrics": "/instruments/metrics",
|
124
|
-
"instrument.option_chain": "/instruments/options/chain"
|
189
|
+
"instrument.option_chain": "/instruments/options/chain",
|
190
|
+
"instrument.expiry_list": "/instruments/options/chain/expirylist"
|
125
191
|
}
|
126
192
|
|
127
193
|
def __init__(
|
@@ -177,6 +243,33 @@ class WizzerClient:
|
|
177
243
|
|
178
244
|
return {"id": self.strategy_id}
|
179
245
|
|
246
|
+
def _validate_datetime_format(self, datetime_str: str, interval: str) -> bool:
|
247
|
+
"""
|
248
|
+
Validate datetime format based on interval type.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
datetime_str (str): The datetime string to validate
|
252
|
+
interval (str): The interval type
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
bool: True if format is valid for the interval
|
256
|
+
"""
|
257
|
+
# For daily and monthly intervals, only date is required
|
258
|
+
if interval in [self.INTERVAL_1_DAY, self.INTERVAL_1_MONTH]:
|
259
|
+
try:
|
260
|
+
datetime.strptime(datetime_str, "%Y-%m-%d")
|
261
|
+
return True
|
262
|
+
except ValueError:
|
263
|
+
return False
|
264
|
+
|
265
|
+
# For intraday intervals, datetime is required
|
266
|
+
else:
|
267
|
+
try:
|
268
|
+
datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
|
269
|
+
return True
|
270
|
+
except ValueError:
|
271
|
+
return False
|
272
|
+
|
180
273
|
# ===== DATA HUB METHODS =====
|
181
274
|
|
182
275
|
def get_indices(self, trading_symbol: Optional[str] = None, exchange: Optional[str] = None) -> List[Dict[str, Any]]:
|
@@ -220,7 +313,7 @@ class WizzerClient:
|
|
220
313
|
logger.debug("Fetching index components with params: %s", params)
|
221
314
|
response = self._make_request("GET", self._routes["datahub.index.components"], params=params)
|
222
315
|
return response
|
223
|
-
|
316
|
+
|
224
317
|
def get_historical_ohlcv(
|
225
318
|
self,
|
226
319
|
instruments: List[str],
|
@@ -234,14 +327,41 @@ class WizzerClient:
|
|
234
327
|
|
235
328
|
Args:
|
236
329
|
instruments (List[str]): List of instrument identifiers (e.g., ["NSE:SBIN:3045"]).
|
237
|
-
start_date (str): Start date
|
238
|
-
|
330
|
+
start_date (str): Start date. For daily/monthly intervals: YYYY-MM-DD format.
|
331
|
+
For intraday intervals: YYYY-MM-DD HH:MM:SS format.
|
332
|
+
end_date (str): End date. For daily/monthly intervals: YYYY-MM-DD format.
|
333
|
+
For intraday intervals: YYYY-MM-DD HH:MM:SS format.
|
239
334
|
ohlcv (List[str]): List of OHLCV fields to retrieve (open, high, low, close, volume).
|
240
|
-
interval (str, optional): Data interval. Options:
|
335
|
+
interval (str, optional): Data interval. Options:
|
336
|
+
- Intraday: "1m", "2m", "3m", "5m", "10m", "15m", "30m", "45m", "1h"
|
337
|
+
- Daily: "1d" (default)
|
338
|
+
- Monthly: "1M" (last trading day of month)
|
339
|
+
|
340
|
+
Use constants like client.INTERVAL_5_MINUTES, client.INTERVAL_1_DAY, etc.
|
241
341
|
|
242
342
|
Returns:
|
243
343
|
List[Dict[str, Any]]: Historical data for requested instruments.
|
344
|
+
|
345
|
+
Note: For daily/monthly intervals, the 'date' field contains YYYY-MM-DD.
|
346
|
+
For intraday intervals, the 'date' field contains YYYY-MM-DD HH:MM:SS.
|
347
|
+
|
348
|
+
Raises:
|
349
|
+
ValueError: If datetime format doesn't match the interval requirements.
|
244
350
|
"""
|
351
|
+
# Validate datetime formats
|
352
|
+
if not self._validate_datetime_format(start_date, interval):
|
353
|
+
if interval in [self.INTERVAL_1_DAY, self.INTERVAL_1_MONTH]:
|
354
|
+
raise ValueError(f"For interval '{interval}', start_date must be in YYYY-MM-DD format")
|
355
|
+
else:
|
356
|
+
raise ValueError(f"For interval '{interval}', start_date must be in YYYY-MM-DD HH:MM:SS format")
|
357
|
+
|
358
|
+
if not self._validate_datetime_format(end_date, interval):
|
359
|
+
if interval in [self.INTERVAL_1_DAY, self.INTERVAL_1_MONTH]:
|
360
|
+
raise ValueError(f"For interval '{interval}', end_date must be in YYYY-MM-DD format")
|
361
|
+
else:
|
362
|
+
raise ValueError(f"For interval '{interval}', end_date must be in YYYY-MM-DD HH:MM:SS format")
|
363
|
+
|
364
|
+
endpoint = self._routes["datahub.historical.ohlcv"]
|
245
365
|
data = {
|
246
366
|
"instruments": instruments,
|
247
367
|
"startDate": start_date,
|
@@ -251,7 +371,7 @@ class WizzerClient:
|
|
251
371
|
}
|
252
372
|
|
253
373
|
logger.debug("Fetching historical OHLCV with data: %s", data)
|
254
|
-
response = self._make_request("POST",
|
374
|
+
response = self._make_request("POST", endpoint, json=data)
|
255
375
|
return response
|
256
376
|
|
257
377
|
# ===== ORDER MANAGEMENT METHODS =====
|
@@ -1310,9 +1430,10 @@ class WizzerClient:
|
|
1310
1430
|
def get_option_chain(
|
1311
1431
|
self,
|
1312
1432
|
identifier: str,
|
1313
|
-
expiry_date: str,
|
1314
|
-
|
1315
|
-
|
1433
|
+
expiry_date: Optional[str] = None,
|
1434
|
+
expiry_preference: Optional[str] = None,
|
1435
|
+
option_type: List[str] = None,
|
1436
|
+
moneyness: List[str] = None
|
1316
1437
|
) -> Dict[str, Any]:
|
1317
1438
|
"""
|
1318
1439
|
Get option chain for a specified instrument.
|
@@ -1320,6 +1441,8 @@ class WizzerClient:
|
|
1320
1441
|
Args:
|
1321
1442
|
identifier (str): Instrument identifier (e.g., "NSE:SBIN:3045").
|
1322
1443
|
expiry_date (str): Expiry date in YYYY-MM-DD format.
|
1444
|
+
expiry_preference (Optional[str]): Expiry category preference instead of exact date.
|
1445
|
+
Use the EXPIRY_* constants provided by the class.
|
1323
1446
|
option_type (List[str]): List of option types to include.
|
1324
1447
|
Values from [OPTION_TYPE_CE, OPTION_TYPE_PE].
|
1325
1448
|
moneyness (List[str]): List of moneyness types to include.
|
@@ -1338,6 +1461,8 @@ class WizzerClient:
|
|
1338
1461
|
raise ValueError("At least one option type must be specified")
|
1339
1462
|
if not moneyness:
|
1340
1463
|
raise ValueError("At least one moneyness type must be specified")
|
1464
|
+
if not expiry_date and not expiry_preference:
|
1465
|
+
raise ValueError("Either expiry_date or expiry_preference must be provided")
|
1341
1466
|
|
1342
1467
|
# Validate option_type values
|
1343
1468
|
valid_option_types = [self.OPTION_TYPE_CE, self.OPTION_TYPE_PE]
|
@@ -1353,14 +1478,55 @@ class WizzerClient:
|
|
1353
1478
|
|
1354
1479
|
data = {
|
1355
1480
|
"identifier": identifier,
|
1356
|
-
"expiryDate": expiry_date,
|
1357
1481
|
"optionType": option_type,
|
1358
1482
|
"moneyness": moneyness
|
1359
1483
|
}
|
1360
1484
|
|
1485
|
+
# Add either expiryDate or expiryPreference
|
1486
|
+
if expiry_date:
|
1487
|
+
data["expiryDate"] = expiry_date
|
1488
|
+
elif expiry_preference:
|
1489
|
+
data["expiryPreference"] = expiry_preference
|
1490
|
+
|
1361
1491
|
logger.debug("Fetching option chain with data: %s", data)
|
1362
1492
|
return self._make_request("POST", endpoint, json=data)
|
1363
1493
|
|
1494
|
+
def get_option_expiry_list(
|
1495
|
+
self,
|
1496
|
+
identifier: str
|
1497
|
+
) -> Dict[str, Any]:
|
1498
|
+
"""
|
1499
|
+
Get a list of available expiry dates for an instrument's options.
|
1500
|
+
|
1501
|
+
Args:
|
1502
|
+
identifier (str): Instrument identifier in the format "EXCHANGE:SYMBOL:TOKEN"
|
1503
|
+
(e.g., "NSE:SBIN:3045" or "NSE:NIFTY 50:26000").
|
1504
|
+
|
1505
|
+
Returns:
|
1506
|
+
Dict[str, Any]: Response containing the list of available expiry dates with contract types.
|
1507
|
+
|
1508
|
+
Example:
|
1509
|
+
```python
|
1510
|
+
# Get expiry dates for a stock
|
1511
|
+
expiry_list = client.get_option_expiry_list("NSE:SBIN:3045")
|
1512
|
+
|
1513
|
+
# Get expiry dates for an index
|
1514
|
+
nifty_expiry = client.get_option_expiry_list("NSE:NIFTY 50:26000")
|
1515
|
+
|
1516
|
+
# Access the expiry dates
|
1517
|
+
for expiry in expiry_list.get('expiryList', []):
|
1518
|
+
print(f"{expiry['date']} - {expiry['contract']}")
|
1519
|
+
```
|
1520
|
+
"""
|
1521
|
+
endpoint = self._routes["instrument.expiry_list"]
|
1522
|
+
|
1523
|
+
data = {
|
1524
|
+
"identifier": identifier
|
1525
|
+
}
|
1526
|
+
|
1527
|
+
logger.debug("Fetching option expiry list for %s", identifier)
|
1528
|
+
return self._make_request("POST", endpoint, json=data)
|
1529
|
+
|
1364
1530
|
def _make_request(
|
1365
1531
|
self,
|
1366
1532
|
method: str,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: wiz_trader
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.19.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
|
@@ -388,29 +388,388 @@ for component in components[:5]:
|
|
388
388
|
```
|
389
389
|
|
390
390
|
#### Get Historical OHLCV Data
|
391
|
+
## Overview
|
391
392
|
|
392
|
-
|
393
|
+
The `get_historical_ohlcv` method supports multiple time intervals for historical data retrieval, from 1-minute intraday data to monthly aggregated data.
|
394
|
+
|
395
|
+
## Supported Intervals
|
396
|
+
|
397
|
+
### Intraday Intervals
|
398
|
+
- `1m` - 1 minute
|
399
|
+
- `2m` - 2 minutes
|
400
|
+
- `3m` - 3 minutes
|
401
|
+
- `5m` - 5 minutes
|
402
|
+
- `10m` - 10 minutes
|
403
|
+
- `15m` - 15 minutes
|
404
|
+
- `30m` - 30 minutes
|
405
|
+
- `45m` - 45 minutes
|
406
|
+
- `1h` - 1 hour
|
407
|
+
|
408
|
+
### Daily/Monthly Intervals
|
409
|
+
- `1d` - 1 day (default)
|
410
|
+
- `1M` - 1 month (last trading day of month)
|
411
|
+
|
412
|
+
## Date Format Requirements
|
413
|
+
|
414
|
+
### For Daily and Monthly Intervals (`1d`, `1M`)
|
415
|
+
- **Format**: `YYYY-MM-DD`
|
416
|
+
- **Example**: `"2024-01-15"`
|
417
|
+
|
418
|
+
### For Intraday Intervals (all minute/hour intervals)
|
419
|
+
- **Format**: `YYYY-MM-DD HH:MM:SS`
|
420
|
+
- **Example**: `"2024-01-15 09:30:00"`
|
421
|
+
|
422
|
+
## Using Constants
|
423
|
+
|
424
|
+
The client provides constants for all intervals:
|
425
|
+
|
426
|
+
```python
|
427
|
+
from wiz_trader import WizzerClient
|
428
|
+
|
429
|
+
client = WizzerClient(...)
|
430
|
+
|
431
|
+
# Intraday intervals
|
432
|
+
client.INTERVAL_1_MINUTE # "1m"
|
433
|
+
client.INTERVAL_2_MINUTES # "2m"
|
434
|
+
client.INTERVAL_3_MINUTES # "3m"
|
435
|
+
client.INTERVAL_5_MINUTES # "5m"
|
436
|
+
client.INTERVAL_10_MINUTES # "10m"
|
437
|
+
client.INTERVAL_15_MINUTES # "15m"
|
438
|
+
client.INTERVAL_30_MINUTES # "30m"
|
439
|
+
client.INTERVAL_45_MINUTES # "45m"
|
440
|
+
client.INTERVAL_1_HOUR # "1h"
|
441
|
+
|
442
|
+
# Daily/Monthly intervals
|
443
|
+
client.INTERVAL_1_DAY # "1d"
|
444
|
+
client.INTERVAL_1_MONTH # "1M"
|
445
|
+
```
|
446
|
+
|
447
|
+
## Usage Examples
|
448
|
+
|
449
|
+
### 1. Get 5-minute intraday data
|
450
|
+
|
451
|
+
```python
|
452
|
+
# Get 5-minute candles for a trading session
|
453
|
+
intraday_data = client.get_historical_ohlcv(
|
454
|
+
instruments=["NSE:SBIN:3045"],
|
455
|
+
start_date="2024-01-15 09:15:00", # Market open
|
456
|
+
end_date="2024-01-15 15:30:00", # Market close
|
457
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
458
|
+
interval=client.INTERVAL_5_MINUTES
|
459
|
+
)
|
460
|
+
|
461
|
+
# Response will have date field as: "2024-01-15 09:15:00", "2024-01-15 09:20:00", etc.
|
462
|
+
```
|
463
|
+
|
464
|
+
### 2. Get 1-minute data for scalping
|
465
|
+
|
466
|
+
```python
|
467
|
+
# Get 1-minute data for the first hour of trading
|
468
|
+
scalping_data = client.get_historical_ohlcv(
|
469
|
+
instruments=["NSE:RELIANCE:2885", "NSE:TCS:2953"],
|
470
|
+
start_date="2024-01-15 09:15:00",
|
471
|
+
end_date="2024-01-15 10:15:00",
|
472
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
473
|
+
interval=client.INTERVAL_1_MINUTE
|
474
|
+
)
|
475
|
+
```
|
476
|
+
|
477
|
+
### 3. Get hourly data for swing trading
|
478
|
+
|
479
|
+
```python
|
480
|
+
# Get hourly data for a week
|
481
|
+
hourly_data = client.get_historical_ohlcv(
|
482
|
+
instruments=["NSE:NIFTY 50:26000"],
|
483
|
+
start_date="2024-01-15 09:15:00",
|
484
|
+
end_date="2024-01-19 15:30:00",
|
485
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
486
|
+
interval=client.INTERVAL_1_HOUR
|
487
|
+
)
|
488
|
+
```
|
489
|
+
|
490
|
+
### 4. Get daily data (traditional)
|
393
491
|
|
394
492
|
```python
|
395
|
-
# Get daily data
|
493
|
+
# Get daily data for a month
|
396
494
|
daily_data = client.get_historical_ohlcv(
|
397
495
|
instruments=["NSE:SBIN:3045"],
|
398
|
-
start_date="2024-01-01",
|
496
|
+
start_date="2024-01-01", # Note: No time component
|
399
497
|
end_date="2024-01-31",
|
400
498
|
ohlcv=["open", "high", "low", "close", "volume"],
|
401
|
-
interval=
|
499
|
+
interval=client.INTERVAL_1_DAY
|
402
500
|
)
|
403
501
|
|
404
|
-
#
|
502
|
+
# Response will have date field as: "2024-01-01", "2024-01-02", etc.
|
503
|
+
```
|
504
|
+
|
505
|
+
### 5. Get monthly data for long-term analysis
|
506
|
+
|
507
|
+
```python
|
508
|
+
# Get monthly data for a year
|
405
509
|
monthly_data = client.get_historical_ohlcv(
|
406
|
-
instruments=["NSE:SBIN:3045"
|
510
|
+
instruments=["NSE:SBIN:3045"],
|
407
511
|
start_date="2023-01-01",
|
408
|
-
end_date="
|
512
|
+
end_date="2023-12-31",
|
409
513
|
ohlcv=["close", "volume"],
|
410
|
-
interval=
|
514
|
+
interval=client.INTERVAL_1_MONTH
|
515
|
+
)
|
516
|
+
```
|
517
|
+
|
518
|
+
## Response Structure
|
519
|
+
|
520
|
+
The response structure remains the same, but the `date` field format varies:
|
521
|
+
|
522
|
+
### For Daily/Monthly Intervals
|
523
|
+
```json
|
524
|
+
{
|
525
|
+
"instrument": {
|
526
|
+
"exchange": "NSE",
|
527
|
+
"tradingSymbol": "SBIN",
|
528
|
+
"exchangeToken": 3045,
|
529
|
+
"identifier": "NSE:SBIN:3045"
|
530
|
+
},
|
531
|
+
"data": [
|
532
|
+
{
|
533
|
+
"date": "2024-01-15", // YYYY-MM-DD format
|
534
|
+
"open": 750.0,
|
535
|
+
"high": 765.0,
|
536
|
+
"low": 745.0,
|
537
|
+
"close": 760.0,
|
538
|
+
"volume": 1000000
|
539
|
+
}
|
540
|
+
]
|
541
|
+
}
|
542
|
+
```
|
543
|
+
|
544
|
+
### For Intraday Intervals
|
545
|
+
```json
|
546
|
+
{
|
547
|
+
"instrument": {
|
548
|
+
"exchange": "NSE",
|
549
|
+
"tradingSymbol": "SBIN",
|
550
|
+
"exchangeToken": 3045,
|
551
|
+
"identifier": "NSE:SBIN:3045"
|
552
|
+
},
|
553
|
+
"data": [
|
554
|
+
{
|
555
|
+
"date": "2024-01-15 09:15:00", // YYYY-MM-DD HH:MM:SS format
|
556
|
+
"open": 750.0,
|
557
|
+
"high": 752.0,
|
558
|
+
"low": 749.0,
|
559
|
+
"close": 751.0,
|
560
|
+
"volume": 50000
|
561
|
+
}
|
562
|
+
]
|
563
|
+
}
|
564
|
+
```
|
565
|
+
|
566
|
+
## Advanced Usage Examples
|
567
|
+
|
568
|
+
### Multiple Timeframe Analysis
|
569
|
+
|
570
|
+
```python
|
571
|
+
def get_multi_timeframe_data(symbol, date):
|
572
|
+
"""Get data across multiple timeframes for comprehensive analysis"""
|
573
|
+
|
574
|
+
# 1-minute data for entry/exit precision
|
575
|
+
minute_data = client.get_historical_ohlcv(
|
576
|
+
instruments=[symbol],
|
577
|
+
start_date=f"{date} 09:15:00",
|
578
|
+
end_date=f"{date} 15:30:00",
|
579
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
580
|
+
interval=client.INTERVAL_1_MINUTE
|
581
|
+
)
|
582
|
+
|
583
|
+
# 5-minute data for short-term trends
|
584
|
+
five_min_data = client.get_historical_ohlcv(
|
585
|
+
instruments=[symbol],
|
586
|
+
start_date=f"{date} 09:15:00",
|
587
|
+
end_date=f"{date} 15:30:00",
|
588
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
589
|
+
interval=client.INTERVAL_5_MINUTES
|
590
|
+
)
|
591
|
+
|
592
|
+
# 15-minute data for medium-term trends
|
593
|
+
fifteen_min_data = client.get_historical_ohlcv(
|
594
|
+
instruments=[symbol],
|
595
|
+
start_date=f"{date} 09:15:00",
|
596
|
+
end_date=f"{date} 15:30:00",
|
597
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
598
|
+
interval=client.INTERVAL_15_MINUTES
|
599
|
+
)
|
600
|
+
|
601
|
+
# Hourly data for major trend
|
602
|
+
hourly_data = client.get_historical_ohlcv(
|
603
|
+
instruments=[symbol],
|
604
|
+
start_date=f"{date} 09:15:00",
|
605
|
+
end_date=f"{date} 15:30:00",
|
606
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
607
|
+
interval=client.INTERVAL_1_HOUR
|
608
|
+
)
|
609
|
+
|
610
|
+
return {
|
611
|
+
"1m": minute_data,
|
612
|
+
"5m": five_min_data,
|
613
|
+
"15m": fifteen_min_data,
|
614
|
+
"1h": hourly_data
|
615
|
+
}
|
616
|
+
|
617
|
+
# Usage
|
618
|
+
multi_data = get_multi_timeframe_data("NSE:SBIN:3045", "2024-01-15")
|
619
|
+
```
|
620
|
+
|
621
|
+
### Building OHLC Candlestick Data
|
622
|
+
|
623
|
+
```python
|
624
|
+
import pandas as pd
|
625
|
+
import matplotlib.pyplot as plt
|
626
|
+
from mplfinance import plot as mpf
|
627
|
+
|
628
|
+
def plot_candlestick_chart(symbol, start_date, end_date, interval):
|
629
|
+
"""Create a candlestick chart from historical data"""
|
630
|
+
|
631
|
+
# Get historical data
|
632
|
+
data = client.get_historical_ohlcv(
|
633
|
+
instruments=[symbol],
|
634
|
+
start_date=start_date,
|
635
|
+
end_date=end_date,
|
636
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
637
|
+
interval=interval
|
638
|
+
)
|
639
|
+
|
640
|
+
if not data or not data[0].get('data'):
|
641
|
+
print("No data available")
|
642
|
+
return
|
643
|
+
|
644
|
+
# Convert to DataFrame
|
645
|
+
df = pd.DataFrame(data[0]['data'])
|
646
|
+
df['date'] = pd.to_datetime(df['date'])
|
647
|
+
df.set_index('date', inplace=True)
|
648
|
+
|
649
|
+
# Rename columns for mplfinance
|
650
|
+
df.columns = ['Open', 'High', 'Low', 'Close', 'Volume']
|
651
|
+
|
652
|
+
# Create candlestick chart
|
653
|
+
mpf.plot(df, type='candle', volume=True,
|
654
|
+
title=f'{symbol} - {interval} Chart',
|
655
|
+
style='charles')
|
656
|
+
|
657
|
+
# Plot 5-minute chart
|
658
|
+
plot_candlestick_chart(
|
659
|
+
"NSE:SBIN:3045",
|
660
|
+
"2024-01-15 09:15:00",
|
661
|
+
"2024-01-15 15:30:00",
|
662
|
+
client.INTERVAL_5_MINUTES
|
663
|
+
)
|
664
|
+
```
|
665
|
+
|
666
|
+
### Backtesting with Different Intervals
|
667
|
+
|
668
|
+
```python
|
669
|
+
def backtest_strategy_multiple_intervals(symbol, start_date, end_date):
|
670
|
+
"""Backtest a strategy across different intervals"""
|
671
|
+
|
672
|
+
intervals = [
|
673
|
+
(client.INTERVAL_5_MINUTES, "5m"),
|
674
|
+
(client.INTERVAL_15_MINUTES, "15m"),
|
675
|
+
(client.INTERVAL_30_MINUTES, "30m"),
|
676
|
+
(client.INTERVAL_1_HOUR, "1h")
|
677
|
+
]
|
678
|
+
|
679
|
+
results = {}
|
680
|
+
|
681
|
+
for interval_code, interval_name in intervals:
|
682
|
+
# For intraday intervals, use datetime format
|
683
|
+
data = client.get_historical_ohlcv(
|
684
|
+
instruments=[symbol],
|
685
|
+
start_date=f"{start_date} 09:15:00",
|
686
|
+
end_date=f"{end_date} 15:30:00",
|
687
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
688
|
+
interval=interval_code
|
689
|
+
)
|
690
|
+
|
691
|
+
if data and data[0].get('data'):
|
692
|
+
df = pd.DataFrame(data[0]['data'])
|
693
|
+
df['date'] = pd.to_datetime(df['date'])
|
694
|
+
|
695
|
+
# Simple moving average crossover strategy
|
696
|
+
df['sma_10'] = df['close'].rolling(10).mean()
|
697
|
+
df['sma_20'] = df['close'].rolling(20).mean()
|
698
|
+
|
699
|
+
# Generate signals
|
700
|
+
df['signal'] = 0
|
701
|
+
df.loc[df['sma_10'] > df['sma_20'], 'signal'] = 1
|
702
|
+
df.loc[df['sma_10'] < df['sma_20'], 'signal'] = -1
|
703
|
+
|
704
|
+
# Calculate returns
|
705
|
+
df['returns'] = df['close'].pct_change()
|
706
|
+
df['strategy_returns'] = df['signal'].shift(1) * df['returns']
|
707
|
+
|
708
|
+
# Calculate total return
|
709
|
+
total_return = (1 + df['strategy_returns']).prod() - 1
|
710
|
+
|
711
|
+
results[interval_name] = {
|
712
|
+
'total_return': total_return,
|
713
|
+
'num_trades': df['signal'].diff().abs().sum() / 2,
|
714
|
+
'num_candles': len(df)
|
715
|
+
}
|
716
|
+
|
717
|
+
return results
|
718
|
+
|
719
|
+
# Compare strategy performance across intervals
|
720
|
+
performance = backtest_strategy_multiple_intervals(
|
721
|
+
"NSE:SBIN:3045",
|
722
|
+
"2024-01-01",
|
723
|
+
"2024-01-31"
|
411
724
|
)
|
725
|
+
|
726
|
+
for interval, metrics in performance.items():
|
727
|
+
print(f"{interval}: Return={metrics['total_return']:.2%}, "
|
728
|
+
f"Trades={metrics['num_trades']:.0f}, "
|
729
|
+
f"Candles={metrics['num_candles']}")
|
730
|
+
```
|
731
|
+
|
732
|
+
## Error Handling
|
733
|
+
|
734
|
+
```python
|
735
|
+
try:
|
736
|
+
data = client.get_historical_ohlcv(
|
737
|
+
instruments=["NSE:SBIN:3045"],
|
738
|
+
start_date="2024-01-15 09:15:00",
|
739
|
+
end_date="2024-01-15 15:30:00",
|
740
|
+
ohlcv=["open", "high", "low", "close", "volume"],
|
741
|
+
interval=client.INTERVAL_5_MINUTES
|
742
|
+
)
|
743
|
+
except ValueError as e:
|
744
|
+
print(f"Date format error: {e}")
|
745
|
+
except Exception as e:
|
746
|
+
print(f"API error: {e}")
|
412
747
|
```
|
413
748
|
|
749
|
+
## Best Practices
|
750
|
+
|
751
|
+
1. **Use appropriate intervals for your strategy**:
|
752
|
+
- Scalping: 1m, 2m, 3m
|
753
|
+
- Day trading: 5m, 15m, 30m
|
754
|
+
- Swing trading: 1h, 1d
|
755
|
+
- Position trading: 1d, 1M
|
756
|
+
|
757
|
+
2. **Be mindful of data volume**:
|
758
|
+
- 1-minute data generates a lot of candles
|
759
|
+
- Use shorter date ranges for minute-level data
|
760
|
+
|
761
|
+
3. **Validate date formats**:
|
762
|
+
- The client will validate formats automatically
|
763
|
+
- Use datetime strings consistently
|
764
|
+
|
765
|
+
4. **Consider market hours**:
|
766
|
+
- For intraday data, use market hours: 09:15:00 to 15:30:00
|
767
|
+
- Weekend dates won't have intraday data
|
768
|
+
|
769
|
+
5. **Cache data when possible**:
|
770
|
+
- Historical data doesn't change
|
771
|
+
- Store frequently used datasets locally
|
772
|
+
|
414
773
|
### Order Management
|
415
774
|
|
416
775
|
#### Place Regular Order
|
@@ -643,13 +1002,13 @@ Fetch detailed information and metrics for specific instruments:
|
|
643
1002
|
|
644
1003
|
```python
|
645
1004
|
# Get metrics for a single equity instrument
|
646
|
-
equity_info = client.get_instrument_metrics(
|
1005
|
+
equity_info = client.get_instrument_metrics(identifiers=["NSE:SBIN:3045"])
|
647
1006
|
print(f"SBIN Last Price: {equity_info[0]['ltp']}")
|
648
1007
|
print(f"SBIN 52-week High: {equity_info[0]['week52High']}")
|
649
1008
|
|
650
1009
|
# Get metrics for multiple instruments of different types
|
651
1010
|
instruments_info = client.get_instrument_metrics(
|
652
|
-
|
1011
|
+
identifiers=[
|
653
1012
|
"NSE:AARTIIND:7", # Equity
|
654
1013
|
"NSE:NIFTY26DEC11000CE:61009", # Option
|
655
1014
|
"NSE:SBIN25MAYFUT:57515" # Future
|
@@ -831,12 +1190,150 @@ Note that certain fields may have zero or empty values if the data is not availa
|
|
831
1190
|
|
832
1191
|
### Options Chain
|
833
1192
|
|
1193
|
+
#### Get Option Expiry List
|
1194
|
+
|
1195
|
+
Fetch available expiry dates for an instrument's options:
|
1196
|
+
|
1197
|
+
```python
|
1198
|
+
# For a stock
|
1199
|
+
expiry_list = client.get_option_expiry_list("NSE:SBIN:3045")
|
1200
|
+
|
1201
|
+
# For an index
|
1202
|
+
nifty_expiry = client.get_option_expiry_list("NSE:NIFTY 50:26000")
|
1203
|
+
|
1204
|
+
# Print all available expiry dates
|
1205
|
+
for expiry in expiry_list.get('expiryList', []):
|
1206
|
+
print(f"{expiry['date']} - {expiry['contract']} " +
|
1207
|
+
f"(Futures: {'Yes' if expiry['isFuturesExpiry'] else 'No'}, " +
|
1208
|
+
f"Options: {'Yes' if expiry['isOptionsExpiry'] else 'No'})")
|
1209
|
+
```
|
1210
|
+
|
1211
|
+
#### Response Structure
|
1212
|
+
|
1213
|
+
The response is a JSON object with the following structure:
|
1214
|
+
|
1215
|
+
```json
|
1216
|
+
{
|
1217
|
+
"exchange": "NSE", // Exchange of the instrument
|
1218
|
+
"tradingSymbol": "SBIN", // Trading symbol
|
1219
|
+
"exchangeToken": 3045, // Exchange token
|
1220
|
+
"identifier": "NSE:SBIN:3045", // Full identifier
|
1221
|
+
"expiryList": [ // Array of expiry objects
|
1222
|
+
{
|
1223
|
+
"date": "2025-05-29", // Expiry date in YYYY-MM-DD format
|
1224
|
+
"contract": "near_month", // Contract type (see below)
|
1225
|
+
"isFuturesExpiry": true, // Whether futures expire on this date
|
1226
|
+
"isOptionsExpiry": true // Whether options expire on this date
|
1227
|
+
},
|
1228
|
+
// More expiry objects...
|
1229
|
+
]
|
1230
|
+
}
|
1231
|
+
```
|
1232
|
+
|
1233
|
+
For index options like NIFTY, the response will typically include more expiry dates, including weekly options and longer-dated quarterly options.
|
1234
|
+
|
1235
|
+
The `expiryList` array contains objects with the following fields:
|
1236
|
+
- `date`: The expiry date in YYYY-MM-DD format
|
1237
|
+
- `contract`: The type of contract (e.g., "near_month", "current_weekly")
|
1238
|
+
- `isFuturesExpiry`: Boolean indicating if futures expire on this date
|
1239
|
+
- `isOptionsExpiry`: Boolean indicating if options expire on this date
|
1240
|
+
|
1241
|
+
#### Contract Types
|
1242
|
+
|
1243
|
+
Each expiry date is categorized with a `contract` field that indicates the type of expiry. The possible contract types are:
|
1244
|
+
|
1245
|
+
1. **Weekly Expiries (Thursdays/Wednesdays)**
|
1246
|
+
- `current_weekly`: The first non-expiry Thursday of the current week
|
1247
|
+
|
1248
|
+
2. **Monthly Expiries (last Wed/Thu of month)**
|
1249
|
+
- `near_month`: Last Wed/Thu of this month (current month)
|
1250
|
+
- `mid_month`: Last Wed/Thu of next month
|
1251
|
+
- `far_month`: Last Wed/Thu of month after next
|
1252
|
+
|
1253
|
+
3. **Weekly Ordinals Within Current Month**
|
1254
|
+
- `first_weekly`: 1st non-expiry Thursday of current month
|
1255
|
+
- `second_weekly`: 2nd non-expiry Thursday
|
1256
|
+
- `third_weekly`: 3rd non-expiry Thursday
|
1257
|
+
- `fourth_weekly`: 4th non-expiry Thursday
|
1258
|
+
- `fifth_weekly`: 5th non-expiry Thursday (rare)
|
1259
|
+
|
1260
|
+
4. **Weekly Ordinals in Mid-Month Slot (next month's week-trade dates)**
|
1261
|
+
- `first_weekly_mid_month`: 1st Thursday of next month
|
1262
|
+
- `second_weekly_mid_month`: 2nd Thursday of next month
|
1263
|
+
- `third_weekly_mid_month`: 3rd Thursday of next month
|
1264
|
+
- `fourth_weekly_mid_month`: 4th Thursday of next month
|
1265
|
+
- `fifth_weekly_mid_month`: 5th Thursday of next month (rare)
|
1266
|
+
|
1267
|
+
5. **Weekly Ordinals in Far-Month Slot (month-after-next)**
|
1268
|
+
- `first_weekly_far_month`: 1st Thursday of month after next
|
1269
|
+
- `second_weekly_far_month`: 2nd Thursday of month after next
|
1270
|
+
- `third_weekly_far_month`: 3rd Thursday of month after next
|
1271
|
+
- `fourth_weekly_far_month`: 4th Thursday of month after next
|
1272
|
+
- `fifth_weekly_far_month`: 5th Thursday of month after next (rare)
|
1273
|
+
|
1274
|
+
6. **Quarterly Expiries (last-Thu of Mar/Jun/Sep/Dec)**
|
1275
|
+
- `first_quarter`: Last Thu of March (Q1)
|
1276
|
+
- `second_quarter`: Last Thu of June (Q2)
|
1277
|
+
- `third_quarter`: Last Thu of September (Q3)
|
1278
|
+
- `fourth_quarter`: Last Thu of December (Q4)
|
1279
|
+
|
1280
|
+
7. **Half-Yearly Expiries**
|
1281
|
+
- `first_half_yearly`: Last Thu of June (H1)
|
1282
|
+
- `second_half_yearly`: Last Thu of December (H2)
|
1283
|
+
|
1284
|
+
8. **Year-Plus-N Quarterly/Half-Yearly (N = years ahead)**
|
1285
|
+
- For quarterly options in future years:
|
1286
|
+
- `first_quarter_1`: Q1 (March) of next year
|
1287
|
+
- `second_quarter_1`: Q2 (June) of next year
|
1288
|
+
- `third_quarter_1`: Q3 (September) of next year
|
1289
|
+
- `fourth_quarter_1`: Q4 (December) of next year
|
1290
|
+
- For half-yearly options in future years:
|
1291
|
+
- `first_half_yearly_1`: H1 (June) of next year
|
1292
|
+
- `second_half_yearly_1`: H2 (December) of next year
|
1293
|
+
- This pattern continues with `_2`, `_3`, and `_4` suffixes for up to 4 years ahead
|
1294
|
+
|
1295
|
+
9. **Special Case**
|
1296
|
+
- `none`: No matching expiry category
|
1297
|
+
|
1298
|
+
### Get Options Chain
|
1299
|
+
|
1300
|
+
The Options Chain API provides detailed information about available option contracts for a specified instrument, including strike prices, premiums, and option Greeks.
|
1301
|
+
|
1302
|
+
#### Constants
|
1303
|
+
|
1304
|
+
The SDK provides constants for option types, moneyness, and expiry preferences to make your code more readable and type-safe:
|
1305
|
+
|
1306
|
+
**Option Types:**
|
1307
|
+
```python
|
1308
|
+
client.OPTION_TYPE_CE # Call option
|
1309
|
+
client.OPTION_TYPE_PE # Put option
|
1310
|
+
```
|
1311
|
+
|
1312
|
+
**Moneyness Types:**
|
1313
|
+
```python
|
1314
|
+
client.MONEYNESS_ATM # At-the-money
|
1315
|
+
client.MONEYNESS_ITM # In-the-money
|
1316
|
+
client.MONEYNESS_OTM # Out-of-the-money
|
1317
|
+
```
|
1318
|
+
|
1319
|
+
**Expiry Preferences:**
|
1320
|
+
```python
|
1321
|
+
# Common expiry preferences
|
1322
|
+
client.EXPIRY_CURRENT_WEEKLY # Current week's expiry
|
1323
|
+
client.EXPIRY_NEAR_MONTH # Current month's expiry
|
1324
|
+
client.EXPIRY_MID_MONTH # Next month's expiry
|
1325
|
+
client.EXPIRY_FAR_MONTH # Month after next expiry
|
1326
|
+
|
1327
|
+
# Many more constants available for weekly, monthly,
|
1328
|
+
# quarterly, and yearly expiries
|
1329
|
+
```
|
1330
|
+
|
834
1331
|
#### Get Option Chain
|
835
1332
|
|
836
|
-
Fetch the option chain for a specific instrument
|
1333
|
+
Fetch the option chain for a specific instrument using either an exact expiry date or a expiry preference:
|
837
1334
|
|
838
1335
|
```python
|
839
|
-
# Get ATM call options for SBIN
|
1336
|
+
# Get ATM call options for SBIN with specific expiry date
|
840
1337
|
options = client.get_option_chain(
|
841
1338
|
identifier="NSE:SBIN:3045",
|
842
1339
|
expiry_date="2025-05-30",
|
@@ -846,10 +1343,10 @@ options = client.get_option_chain(
|
|
846
1343
|
print(f"Found {len(options['strikes'])} strikes")
|
847
1344
|
print(f"Current underlying price: {options['underlyingPrice']}")
|
848
1345
|
|
849
|
-
# Get both call and put options
|
1346
|
+
# Get both call and put options with expiry preference
|
850
1347
|
all_options = client.get_option_chain(
|
851
|
-
identifier="NSE:
|
852
|
-
|
1348
|
+
identifier="NSE:NIFTY 50:26000",
|
1349
|
+
expiry_preference=client.EXPIRY_CURRENT_WEEKLY, # Current week's expiry
|
853
1350
|
option_type=[client.OPTION_TYPE_CE, client.OPTION_TYPE_PE], # Both calls and puts
|
854
1351
|
moneyness=[client.MONEYNESS_ATM, client.MONEYNESS_ITM, client.MONEYNESS_OTM] # All moneyness types
|
855
1352
|
)
|
@@ -864,7 +1361,7 @@ for strike in all_options['strikes']:
|
|
864
1361
|
print(f"{strike['tradingSymbol']} ({strike['moneyness']}): {strike['ltp']}")
|
865
1362
|
```
|
866
1363
|
|
867
|
-
#### Response Structure
|
1364
|
+
#### Response Structure
|
868
1365
|
|
869
1366
|
The `get_option_chain` method returns a dictionary with the following structure:
|
870
1367
|
|
@@ -938,133 +1435,143 @@ The `get_option_chain` method returns a dictionary with the following structure:
|
|
938
1435
|
}
|
939
1436
|
```
|
940
1437
|
|
941
|
-
|
1438
|
+
#### Advanced Examples
|
942
1439
|
|
943
|
-
|
944
|
-
|
945
|
-
Here's an example of how to use the option chain API for more advanced analysis:
|
1440
|
+
##### Finding a Straddle/Strangle
|
946
1441
|
|
947
1442
|
```python
|
948
|
-
def
|
949
|
-
"""
|
950
|
-
|
1443
|
+
def find_straddle_strangle(identifier, expiry):
|
1444
|
+
"""Find and analyze straddle/strangle opportunities."""
|
1445
|
+
# Get the option chain
|
1446
|
+
option_chain = client.get_option_chain(
|
1447
|
+
identifier=identifier,
|
1448
|
+
expiry_date=expiry,
|
1449
|
+
option_type=[client.OPTION_TYPE_CE, client.OPTION_TYPE_PE],
|
1450
|
+
moneyness=[client.MONEYNESS_ATM]
|
1451
|
+
)
|
1452
|
+
|
1453
|
+
# Find ATM call and put
|
1454
|
+
calls = [s for s in option_chain["strikes"] if s["optionType"] == "CE"]
|
1455
|
+
puts = [s for s in option_chain["strikes"] if s["optionType"] == "PE"]
|
1456
|
+
|
1457
|
+
if not calls or not puts:
|
1458
|
+
print("Couldn't find both call and put options")
|
1459
|
+
return
|
1460
|
+
|
1461
|
+
# For a straddle, we want the same strike price
|
1462
|
+
atm_strike = calls[0]["strikePrice"]
|
1463
|
+
atm_call = calls[0]
|
1464
|
+
atm_put = next((p for p in puts if p["strikePrice"] == atm_strike), None)
|
951
1465
|
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
1466
|
+
if atm_call and atm_put:
|
1467
|
+
call_premium = atm_call["ltp"]
|
1468
|
+
put_premium = atm_put["ltp"]
|
1469
|
+
total_premium = call_premium + put_premium
|
1470
|
+
|
1471
|
+
print(f"ATM Straddle Analysis for {identifier} (Expiry: {expiry})")
|
1472
|
+
print(f"Underlying Price: {option_chain['underlyingPrice']}")
|
1473
|
+
print(f"ATM Strike: {atm_strike}")
|
1474
|
+
print(f"Call Premium: {call_premium}")
|
1475
|
+
print(f"Put Premium: {put_premium}")
|
1476
|
+
print(f"Total Premium: {total_premium}")
|
1477
|
+
print(f"Breakeven Upper: {atm_strike + total_premium}")
|
1478
|
+
print(f"Breakeven Lower: {atm_strike - total_premium}")
|
1479
|
+
|
1480
|
+
# Calculate the percentage move needed for breakeven
|
1481
|
+
pct_move = (total_premium / option_chain['underlyingPrice']) * 100
|
1482
|
+
print(f"Required % Move for Breakeven: {pct_move:.2f}%")
|
957
1483
|
|
958
|
-
#
|
1484
|
+
# For a strangle, we want OTM call and put
|
1485
|
+
otm_options = client.get_option_chain(
|
1486
|
+
identifier=identifier,
|
1487
|
+
expiry_date=expiry,
|
1488
|
+
option_type=[client.OPTION_TYPE_CE, client.OPTION_TYPE_PE],
|
1489
|
+
moneyness=[client.MONEYNESS_OTM]
|
1490
|
+
)
|
1491
|
+
|
1492
|
+
otm_calls = sorted([s for s in otm_options["strikes"] if s["optionType"] == "CE"],
|
1493
|
+
key=lambda x: x["strikePrice"])
|
1494
|
+
otm_puts = sorted([s for s in otm_options["strikes"] if s["optionType"] == "PE"],
|
1495
|
+
key=lambda x: x["strikePrice"], reverse=True)
|
1496
|
+
|
1497
|
+
if otm_calls and otm_puts:
|
1498
|
+
otm_call = otm_calls[0] # First OTM call
|
1499
|
+
otm_put = otm_puts[0] # First OTM put
|
1500
|
+
|
1501
|
+
call_premium = otm_call["ltp"]
|
1502
|
+
put_premium = otm_put["ltp"]
|
1503
|
+
total_premium = call_premium + put_premium
|
1504
|
+
|
1505
|
+
print(f"\nOTM Strangle Analysis for {identifier} (Expiry: {expiry})")
|
1506
|
+
print(f"Call Strike: {otm_call['strikePrice']} (Premium: {call_premium})")
|
1507
|
+
print(f"Put Strike: {otm_put['strikePrice']} (Premium: {put_premium})")
|
1508
|
+
print(f"Total Premium: {total_premium}")
|
1509
|
+
print(f"Breakeven Upper: {otm_call['strikePrice'] + total_premium}")
|
1510
|
+
print(f"Breakeven Lower: {otm_put['strikePrice'] - total_premium}")
|
1511
|
+
|
1512
|
+
return option_chain
|
1513
|
+
|
1514
|
+
# Example usage
|
1515
|
+
find_straddle_strangle("NSE:SBIN:3045", "2025-05-30")
|
1516
|
+
```
|
1517
|
+
|
1518
|
+
##### Option Chain Visualization
|
1519
|
+
|
1520
|
+
```python
|
1521
|
+
def visualize_option_chain(identifier, expiry):
|
1522
|
+
"""Create a visualization of the option chain."""
|
1523
|
+
import matplotlib.pyplot as plt
|
1524
|
+
import numpy as np
|
1525
|
+
|
1526
|
+
# Get the option chain
|
959
1527
|
option_chain = client.get_option_chain(
|
960
|
-
identifier=
|
961
|
-
expiry_date=
|
1528
|
+
identifier=identifier,
|
1529
|
+
expiry_date=expiry,
|
962
1530
|
option_type=[client.OPTION_TYPE_CE, client.OPTION_TYPE_PE],
|
963
1531
|
moneyness=[client.MONEYNESS_ATM, client.MONEYNESS_ITM, client.MONEYNESS_OTM]
|
964
1532
|
)
|
965
1533
|
|
966
|
-
# Extract
|
1534
|
+
# Extract data
|
967
1535
|
underlying_price = option_chain["underlyingPrice"]
|
968
|
-
future_price = option_chain["futurePrice"]
|
969
|
-
resolved_expiry = option_chain["resolvedExpiry"]
|
970
1536
|
|
971
1537
|
# Separate calls and puts
|
972
|
-
calls = [s for s in option_chain["strikes"] if s["optionType"] == "CE"]
|
973
|
-
|
1538
|
+
calls = sorted([s for s in option_chain["strikes"] if s["optionType"] == "CE"],
|
1539
|
+
key=lambda x: x["strikePrice"])
|
1540
|
+
puts = sorted([s for s in option_chain["strikes"] if s["optionType"] == "PE"],
|
1541
|
+
key=lambda x: x["strikePrice"])
|
1542
|
+
|
1543
|
+
# Extract strike prices and premiums
|
1544
|
+
call_strikes = [c["strikePrice"] for c in calls]
|
1545
|
+
call_premiums = [c["ltp"] for c in calls]
|
1546
|
+
|
1547
|
+
put_strikes = [p["strikePrice"] for p in puts]
|
1548
|
+
put_premiums = [p["ltp"] for p in puts]
|
1549
|
+
|
1550
|
+
# Create figure and axis
|
1551
|
+
fig, ax = plt.subplots(figsize=(12, 8))
|
1552
|
+
|
1553
|
+
# Plot calls and puts
|
1554
|
+
ax.plot(call_strikes, call_premiums, 'b-', marker='o', label='Calls')
|
1555
|
+
ax.plot(put_strikes, put_premiums, 'r-', marker='o', label='Puts')
|
1556
|
+
|
1557
|
+
# Add vertical line for current price
|
1558
|
+
ax.axvline(x=underlying_price, color='g', linestyle='--',
|
1559
|
+
label=f'Current Price ({underlying_price})')
|
1560
|
+
|
1561
|
+
# Add labels and title
|
1562
|
+
ax.set_xlabel('Strike Price')
|
1563
|
+
ax.set_ylabel('Premium')
|
1564
|
+
ax.set_title(f'Option Chain for {identifier} (Expiry: {expiry})')
|
1565
|
+
ax.legend()
|
1566
|
+
ax.grid(True)
|
1567
|
+
|
1568
|
+
plt.tight_layout()
|
1569
|
+
plt.show()
|
974
1570
|
|
975
|
-
# Sort by strike price
|
976
|
-
calls.sort(key=lambda x: x["strikePrice"])
|
977
|
-
puts.sort(key=lambda x: x["strikePrice"])
|
978
|
-
|
979
|
-
# Find ATM options
|
980
|
-
atm_call = next((s for s in calls if s["moneyness"] == "ATM"), None)
|
981
|
-
atm_put = next((s for s in puts if s["moneyness"] == "ATM"), None)
|
982
|
-
atm_strike = atm_call["strikePrice"] if atm_call else None
|
983
|
-
|
984
|
-
print(f"Analysis for Option Chain (Expiry: {resolved_expiry})")
|
985
|
-
print(f"Underlying Price: {underlying_price}")
|
986
|
-
print(f"Future Price: {future_price}")
|
987
|
-
print(f"ATM Strike: {atm_strike}")
|
988
|
-
print(f"Days to Expiry: {(datetime.strptime(resolved_expiry, '%Y-%m-%d') - datetime.now()).days}")
|
989
|
-
print("=" * 60)
|
990
|
-
|
991
|
-
# Find interesting spreads
|
992
|
-
if len(calls) >= 2 and len(puts) >= 2:
|
993
|
-
# Vertical spreads
|
994
|
-
bull_call_spread = {
|
995
|
-
'long': atm_call,
|
996
|
-
'short': next((c for c in calls if c["strikePrice"] > atm_strike), None)
|
997
|
-
}
|
998
|
-
|
999
|
-
bear_put_spread = {
|
1000
|
-
'long': atm_put,
|
1001
|
-
'short': next((p for p in puts if p["strikePrice"] < atm_strike), None)
|
1002
|
-
}
|
1003
|
-
|
1004
|
-
# Iron condor
|
1005
|
-
iron_condor = {
|
1006
|
-
'long_put': next((p for p in puts if p["strikePrice"] < atm_strike and p["moneyness"] == "OTM"), None),
|
1007
|
-
'short_put': atm_put,
|
1008
|
-
'short_call': atm_call,
|
1009
|
-
'long_call': next((c for c in calls if c["strikePrice"] > atm_strike and c["moneyness"] == "OTM"), None)
|
1010
|
-
}
|
1011
|
-
|
1012
|
-
# Print potential strategies
|
1013
|
-
print("Potential Strategies:")
|
1014
|
-
|
1015
|
-
# Bull Call Spread
|
1016
|
-
if bull_call_spread['long'] and bull_call_spread['short']:
|
1017
|
-
long_premium = bull_call_spread['long']['ltp']
|
1018
|
-
short_premium = bull_call_spread['short']['ltp']
|
1019
|
-
net_premium = long_premium - short_premium
|
1020
|
-
max_profit = bull_call_spread['short']['strikePrice'] - bull_call_spread['long']['strikePrice'] - net_premium
|
1021
|
-
|
1022
|
-
print(f"Bull Call Spread: Buy {bull_call_spread['long']['tradingSymbol']} @ {long_premium}, "
|
1023
|
-
f"Sell {bull_call_spread['short']['tradingSymbol']} @ {short_premium}")
|
1024
|
-
print(f" Net Premium: {net_premium}")
|
1025
|
-
print(f" Max Profit: {max_profit}")
|
1026
|
-
print(f" Break-even: {bull_call_spread['long']['strikePrice'] + net_premium}")
|
1027
|
-
|
1028
|
-
# Bear Put Spread
|
1029
|
-
if bear_put_spread['long'] and bear_put_spread['short']:
|
1030
|
-
long_premium = bear_put_spread['long']['ltp']
|
1031
|
-
short_premium = bear_put_spread['short']['ltp']
|
1032
|
-
net_premium = long_premium - short_premium
|
1033
|
-
max_profit = bear_put_spread['long']['strikePrice'] - bear_put_spread['short']['strikePrice'] - net_premium
|
1034
|
-
|
1035
|
-
print(f"Bear Put Spread: Buy {bear_put_spread['long']['tradingSymbol']} @ {long_premium}, "
|
1036
|
-
f"Sell {bear_put_spread['short']['tradingSymbol']} @ {short_premium}")
|
1037
|
-
print(f" Net Premium: {net_premium}")
|
1038
|
-
print(f" Max Profit: {max_profit}")
|
1039
|
-
print(f" Break-even: {bear_put_spread['long']['strikePrice'] - net_premium}")
|
1040
|
-
|
1041
|
-
# Iron Condor
|
1042
|
-
if all(iron_condor.values()):
|
1043
|
-
long_put_premium = iron_condor['long_put']['ltp']
|
1044
|
-
short_put_premium = iron_condor['short_put']['ltp']
|
1045
|
-
short_call_premium = iron_condor['short_call']['ltp']
|
1046
|
-
long_call_premium = iron_condor['long_call']['ltp']
|
1047
|
-
|
1048
|
-
net_premium = (short_put_premium + short_call_premium) - (long_put_premium + long_call_premium)
|
1049
|
-
put_wing_width = iron_condor['short_put']['strikePrice'] - iron_condor['long_put']['strikePrice']
|
1050
|
-
call_wing_width = iron_condor['long_call']['strikePrice'] - iron_condor['short_call']['strikePrice']
|
1051
|
-
|
1052
|
-
max_risk = max(put_wing_width, call_wing_width) - net_premium
|
1053
|
-
|
1054
|
-
print(f"Iron Condor Strategy:")
|
1055
|
-
print(f" Long Put: {iron_condor['long_put']['tradingSymbol']} @ {long_put_premium}")
|
1056
|
-
print(f" Short Put: {iron_condor['short_put']['tradingSymbol']} @ {short_put_premium}")
|
1057
|
-
print(f" Short Call: {iron_condor['short_call']['tradingSymbol']} @ {short_call_premium}")
|
1058
|
-
print(f" Long Call: {iron_condor['long_call']['tradingSymbol']} @ {long_call_premium}")
|
1059
|
-
print(f" Net Premium Received: {net_premium}")
|
1060
|
-
print(f" Max Risk: {max_risk}")
|
1061
|
-
print(f" Max Profit: {net_premium}")
|
1062
|
-
|
1063
|
-
# Return the option chain for further analysis
|
1064
1571
|
return option_chain
|
1065
1572
|
|
1066
|
-
# Example usage
|
1067
|
-
|
1573
|
+
# Example usage
|
1574
|
+
visualize_option_chain("NSE:NIFTY 50:26000", "2025-05-30")
|
1068
1575
|
```
|
1069
1576
|
|
1070
1577
|
### Wizzer Client Examples
|
@@ -0,0 +1,9 @@
|
|
1
|
+
wiz_trader/__init__.py,sha256=iU9Crioz_4eIjYFIOJ9aqjfGXBMxvEi_836Ln39sV70,182
|
2
|
+
wiz_trader/apis/__init__.py,sha256=ItWKMOl4omiW0g2f-M7WRW3v-dss_ULd9vYnFyIIT9o,132
|
3
|
+
wiz_trader/apis/client.py,sha256=vFoKHJWfyLTX7Alb6WK2fxJimqLj7E42ri1bl_jUDWY,56281
|
4
|
+
wiz_trader/quotes/__init__.py,sha256=RF9g9CNP6bVWlmCh_ad8krm3-EWOIuVfLp0-H9fAeEM,108
|
5
|
+
wiz_trader/quotes/client.py,sha256=LJeMcQPjJIRxrTIGalWsLYh_XfinDXBP5-4cNS7qCxc,9709
|
6
|
+
wiz_trader-0.19.0.dist-info/METADATA,sha256=GxrCN8ospIwoUahWVp6WC669msDd61KTNaCQvg7_Tqw,82928
|
7
|
+
wiz_trader-0.19.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
8
|
+
wiz_trader-0.19.0.dist-info/top_level.txt,sha256=lnYS_g8LlA6ryKYnvY8xIQ6K2K-xzOsd-99AWgnW6VY,11
|
9
|
+
wiz_trader-0.19.0.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
wiz_trader/__init__.py,sha256=w6C8CU2CdbmX1NpPJZQDKK4dTvlnjy8mkfBX4HHAzo0,182
|
2
|
-
wiz_trader/apis/__init__.py,sha256=ItWKMOl4omiW0g2f-M7WRW3v-dss_ULd9vYnFyIIT9o,132
|
3
|
-
wiz_trader/apis/client.py,sha256=Gil47UnBWVHtUKgITJmYgzYWYNT0Gz4zl8dIzJvn7BQ,49347
|
4
|
-
wiz_trader/quotes/__init__.py,sha256=RF9g9CNP6bVWlmCh_ad8krm3-EWOIuVfLp0-H9fAeEM,108
|
5
|
-
wiz_trader/quotes/client.py,sha256=LJeMcQPjJIRxrTIGalWsLYh_XfinDXBP5-4cNS7qCxc,9709
|
6
|
-
wiz_trader-0.17.0.dist-info/METADATA,sha256=bNm4bYfNSMoOkfgAHdsodIYdPVDFR4PW1lZFER3PI6Y,68861
|
7
|
-
wiz_trader-0.17.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
8
|
-
wiz_trader-0.17.0.dist-info/top_level.txt,sha256=lnYS_g8LlA6ryKYnvY8xIQ6K2K-xzOsd-99AWgnW6VY,11
|
9
|
-
wiz_trader-0.17.0.dist-info/RECORD,,
|
File without changes
|