quantmod 0.0.5__tar.gz → 0.0.6__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.
Files changed (44) hide show
  1. {quantmod-0.0.5 → quantmod-0.0.6}/PKG-INFO +9 -10
  2. {quantmod-0.0.5 → quantmod-0.0.6}/README.md +8 -8
  3. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/derivatives/__init__.py +2 -2
  4. quantmod-0.0.6/quantmod/derivatives/nse.py +274 -0
  5. quantmod-0.0.6/quantmod/models/montecarlo.py +152 -0
  6. quantmod-0.0.6/quantmod/version.py +1 -0
  7. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/PKG-INFO +9 -10
  8. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/SOURCES.txt +1 -1
  9. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/requires.txt +0 -1
  10. quantmod-0.0.5/quantmod/derivatives/optionchain.py +0 -249
  11. quantmod-0.0.5/quantmod/models/montecarlo.py +0 -145
  12. quantmod-0.0.5/quantmod/version.py +0 -1
  13. {quantmod-0.0.5 → quantmod-0.0.6}/LICENSE.txt +0 -0
  14. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/__init__.py +0 -0
  15. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/_version.py +0 -0
  16. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/datasets/__init__.py +0 -0
  17. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/datasets/data/__init__.py +0 -0
  18. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/datasets/data/nifty50.csv +0 -0
  19. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/datasets/data/spx.csv +0 -0
  20. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/datasets/dataloader.py +0 -0
  21. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/indicators/__init__.py +0 -0
  22. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/indicators/indicators.py +0 -0
  23. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/main.py +0 -0
  24. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/markets/__init__.py +0 -0
  25. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/markets/bb.py +0 -0
  26. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/markets/yahoo.py +0 -0
  27. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/models/__init__.py +0 -0
  28. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/models/binomial.py +0 -0
  29. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/models/blackscholes.py +0 -0
  30. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/models/optioninputs.py +0 -0
  31. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/risk/__init__.py +0 -0
  32. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/risk/var.py +0 -0
  33. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/risk/varbacktest.py +0 -0
  34. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/risk/varinputs.py +0 -0
  35. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/timeseries/__init__.py +0 -0
  36. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/timeseries/performance.py +0 -0
  37. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/timeseries/timeseries.py +0 -0
  38. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod/utils.py +0 -0
  39. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/dependency_links.txt +0 -0
  40. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/entry_points.txt +0 -0
  41. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/not-zip-safe +0 -0
  42. {quantmod-0.0.5 → quantmod-0.0.6}/quantmod.egg-info/top_level.txt +0 -0
  43. {quantmod-0.0.5 → quantmod-0.0.6}/setup.cfg +0 -0
  44. {quantmod-0.0.5 → quantmod-0.0.6}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quantmod
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Quantmod Python Package
5
5
  Home-page: https://kannansingaravelu.com/
6
6
  Author: Kannan Singaravelu
@@ -20,7 +20,6 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: joblib
23
- Requires-Dist: jugaad-data
24
23
  Requires-Dist: matplotlib
25
24
  Requires-Dist: numpy>=2.0.2
26
25
  Requires-Dist: pandas>=2.2.2
@@ -57,13 +56,13 @@ pip install quantmod
57
56
 
58
57
  ## Modules
59
58
 
60
- * [markets](https://kannansingaravelu.com/docs/site/markets/)
61
- * [models](https://kannansingaravelu.com/docs/site/models/)
62
- * [risk](https://kannansingaravelu.com/docs/site/risk/)
63
- * [timeseries](https://kannansingaravelu.com/docs/site/timeseries/)
64
- * [indicators](https://kannansingaravelu.com/docs/site/indicators/)
65
- * [derivatives](https://kannansingaravelu.com/docs/site/derivatives/)
66
- * [datasets](https://kannansingaravelu.com/docs/site/datasets/)
59
+ * [markets](https://kannansingaravelu.com/quantmod/markets/)
60
+ * [models](https://kannansingaravelu.com/quantmod/models/)
61
+ * [risk](https://kannansingaravelu.com/quantmod/risk/)
62
+ * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
63
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
64
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
65
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
67
66
 
68
67
 
69
68
  ## Quickstart
@@ -99,7 +98,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
99
98
 
100
99
 
101
100
  ## Changelog
102
- The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/docs/site/changelog/)
101
+ The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
103
102
 
104
103
 
105
104
  ## Community
@@ -12,13 +12,13 @@ pip install quantmod
12
12
 
13
13
  ## Modules
14
14
 
15
- * [markets](https://kannansingaravelu.com/docs/site/markets/)
16
- * [models](https://kannansingaravelu.com/docs/site/models/)
17
- * [risk](https://kannansingaravelu.com/docs/site/risk/)
18
- * [timeseries](https://kannansingaravelu.com/docs/site/timeseries/)
19
- * [indicators](https://kannansingaravelu.com/docs/site/indicators/)
20
- * [derivatives](https://kannansingaravelu.com/docs/site/derivatives/)
21
- * [datasets](https://kannansingaravelu.com/docs/site/datasets/)
15
+ * [markets](https://kannansingaravelu.com/quantmod/markets/)
16
+ * [models](https://kannansingaravelu.com/quantmod/models/)
17
+ * [risk](https://kannansingaravelu.com/quantmod/risk/)
18
+ * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
19
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
20
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
21
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
22
22
 
23
23
 
24
24
  ## Quickstart
@@ -54,7 +54,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
54
54
 
55
55
 
56
56
  ## Changelog
57
- The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/docs/site/changelog/)
57
+ The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
58
58
 
59
59
 
60
60
  ## Community
@@ -16,6 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- from .optionchain import OptionChain
19
+ from .nse import OptionData
20
20
 
21
- __all__ = ["OptionChain"]
21
+ __all__ = ["OptionData"]
@@ -0,0 +1,274 @@
1
+ # improvised from nsepython
2
+ # to be used only for tutorial purposes
3
+ # for production use, please reach out to NSE India
4
+ import os, sys
5
+ import requests
6
+ import numpy as np
7
+ import pandas as pd
8
+ import json
9
+ import random
10
+ import datetime, time
11
+ import logging
12
+ import re
13
+ import urllib.parse
14
+
15
+ # Constants
16
+ indices = ["NIFTY", "FINNIFTY", "BANKNIFTY"]
17
+
18
+ mode = "local"
19
+
20
+ if mode == "vpn":
21
+
22
+ def nsefetch(payload):
23
+ if ("%26" in payload) or ("%20" in payload):
24
+ encoded_url = payload
25
+ else:
26
+ encoded_url = urllib.parse.quote(payload, safe=":/?&=")
27
+ payload_var = 'curl -b cookies.txt "' + encoded_url + '"' + curl_headers + ""
28
+ try:
29
+ output = os.popen(payload_var).read()
30
+ output = json.loads(output)
31
+ except ValueError: # includes simplejson.decoder.JSONDecodeError:
32
+ payload2 = "https://www.nseindia.com"
33
+ output2 = os.popen(
34
+ 'curl -c cookies.txt "' + payload2 + '"' + curl_headers + ""
35
+ ).read()
36
+
37
+ output = os.popen(payload_var).read()
38
+ output = json.loads(output)
39
+ return output
40
+
41
+
42
+ if mode == "local":
43
+
44
+ def nsefetch(payload):
45
+ try:
46
+ output = requests.get(payload, headers=headers).json()
47
+ # print(output)
48
+ except ValueError:
49
+ s = requests.Session()
50
+ output = s.get("http://nseindia.com", headers=headers)
51
+ output = s.get(payload, headers=headers).json()
52
+ return output
53
+
54
+
55
+ headers = {
56
+ "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
57
+ "accept-language": "en-US,en;q=0.9,en-IN;q=0.8,en-GB;q=0.7",
58
+ "cache-control": "max-age=0",
59
+ "priority": "u=0, i",
60
+ "sec-ch-ua": '"Microsoft Edge";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
61
+ "sec-ch-ua-mobile": "?0",
62
+ "sec-ch-ua-platform": '"Windows"',
63
+ "sec-fetch-dest": "document",
64
+ "sec-fetch-mode": "navigate",
65
+ "sec-fetch-site": "none",
66
+ "sec-fetch-user": "?1",
67
+ "upgrade-insecure-requests": "1",
68
+ "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
69
+ }
70
+ # Curl headers
71
+ curl_headers = """ -H "authority: beta.nseindia.com" -H "cache-control: max-age=0" -H "dnt: 1" -H "upgrade-insecure-requests: 1" -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36" -H "sec-fetch-user: ?1" -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" -H "sec-fetch-site: none" -H "sec-fetch-mode: navigate" -H "accept-encoding: gzip, deflate, br" -H "accept-language: en-US,en;q=0.9,hi;q=0.8" --compressed"""
72
+
73
+
74
+ class OptionData:
75
+ """
76
+ A class to fetch and analyze option chain data from NSE.
77
+
78
+ Parameters
79
+ ----------
80
+ symbol : str
81
+ Trading symbol of the stock/index (e.g., 'NIFTY', 'RELIANCE')
82
+ expiry_dt : str
83
+ Expiry date in format '%d-%b-%Y' (e.g., '27-Mar-2025')
84
+ Note: Month should be first 3 letters capitalized (Jan, Feb, Mar, etc.)
85
+
86
+ Attributes
87
+ ----------
88
+ get_put_call_ratio : float
89
+ Put-Call ratio based on open interest
90
+ get_maximum_pain_strike : float
91
+ Maximum pain strike price
92
+ get_call_option_data : pandas.DataFrame
93
+ Call option chain data
94
+ get_put_option_data : pandas.DataFrame
95
+ Put option chain data
96
+
97
+ Methods
98
+ -------
99
+ get_option_quote : float
100
+ Get option quote for specific strike price, option type and transaction intent
101
+ get_synthetic_future_price : float
102
+ Calculate synthetic futures price using put-call parity
103
+ """
104
+
105
+ def __init__(self, symbol, expiry_dt):
106
+ """
107
+ Initialize the OptionData class.
108
+
109
+ Parameters
110
+ ----------
111
+ symbol : str
112
+ Trading symbol of the stock/index (e.g., 'NIFTY', 'RELIANCE')
113
+ expiry_dt : str
114
+ Expiry date in format '%d-%b-%Y' (e.g., '27-Mar-2025')
115
+ Note: Month should be first 3 letters capitalized (Jan, Feb, Mar, etc.)
116
+ """
117
+ self.expiry_dt = expiry_dt
118
+ self.symbol = symbol.replace(
119
+ "&", "%26"
120
+ ) # URL Parse for Stocks Like M&M Finance
121
+ self.payload = self._nse_optionchain_scrapper()
122
+
123
+ self.get_put_call_ratio = self._get_option_pcr()
124
+ self.get_maximum_pain_strike = self._get_maximum_pain_strike()
125
+ self.get_call_option_data = self._get_call_option_data()
126
+ self.get_put_option_data = self._get_put_option_data()
127
+
128
+ def _nse_optionchain_scrapper(self):
129
+ """
130
+ Fetch option chain data from NSE website.
131
+
132
+ Returns
133
+ -------
134
+ dict
135
+ Raw option chain data from NSE API
136
+ """
137
+ if any(x in self.symbol for x in indices):
138
+ payload = nsefetch(
139
+ "https://www.nseindia.com/api/option-chain-indices?symbol="
140
+ + self.symbol
141
+ )
142
+ else:
143
+ payload = nsefetch(
144
+ "https://www.nseindia.com/api/option-chain-equities?symbol="
145
+ + self.symbol
146
+ )
147
+ return payload
148
+
149
+ def get_option_quote(self, strikePrice, optionType, intent=""):
150
+ """
151
+ Get option quote for specific strike price and option type.
152
+
153
+ Parameters
154
+ ----------
155
+ strikePrice : float
156
+ Strike price of the option
157
+ optionType : str
158
+ Type of option, either 'CE' (Call) or 'PE' (Put)
159
+ intent : str, optional
160
+ Quote type:
161
+ - '' (default) for last traded price
162
+ - 'sell' for bid price
163
+ - 'buy' for ask price
164
+
165
+ Returns
166
+ -------
167
+ float
168
+ Option price based on the specified intent
169
+ """
170
+ for x in range(len(self.payload["records"]["data"])):
171
+ if (self.payload["records"]["data"][x]["strikePrice"] == strikePrice) & (
172
+ self.payload["records"]["data"][x]["expiryDate"] == self.expiry_dt
173
+ ):
174
+ if intent == "":
175
+ return self.payload["records"]["data"][x][optionType]["lastPrice"]
176
+ if intent == "sell":
177
+ return self.payload["records"]["data"][x][optionType]["bidprice"]
178
+ if intent == "buy":
179
+ return self.payload["records"]["data"][x][optionType]["askPrice"]
180
+
181
+ def _get_option_pcr(self):
182
+ """
183
+ Calculate Put-Call Ratio based on open interest.
184
+
185
+ Returns
186
+ -------
187
+ float
188
+ Put-Call ratio rounded to 2 decimal places
189
+ """
190
+ ce_oi = 0
191
+ pe_oi = 0
192
+ for i in self.payload["records"]["data"]:
193
+ if i["expiryDate"] == self.expiry_dt:
194
+ try:
195
+ ce_oi += i["CE"]["openInterest"]
196
+ pe_oi += i["PE"]["openInterest"]
197
+ except KeyError:
198
+ pass
199
+ return round(pe_oi / ce_oi, 2)
200
+
201
+ def get_synthetic_future_price(self, strike):
202
+ """
203
+ Calculate synthetic futures price using put-call parity.
204
+
205
+ Parameters
206
+ ----------
207
+ strike : float
208
+ Strike price to use for calculation
209
+
210
+ Returns
211
+ -------
212
+ float
213
+ Synthetic futures price
214
+ """
215
+ synthetic_futures = (
216
+ strike
217
+ + self.get_option_quote(strike, "CE", "buy")
218
+ - self.get_option_quote(strike, "PE", "sell")
219
+ )
220
+ return synthetic_futures
221
+
222
+ def _get_call_option_data(self):
223
+ """
224
+ Get call options data for current expiry.
225
+
226
+ Returns
227
+ -------
228
+ pandas.DataFrame
229
+ DataFrame containing call options data sorted by strike price
230
+ """
231
+ ce_values = [
232
+ data["CE"]
233
+ for data in self.payload["records"]["data"]
234
+ if "CE" in data and data["expiryDate"] == self.expiry_dt
235
+ ]
236
+ return pd.DataFrame(ce_values).sort_values(["strikePrice"])
237
+
238
+ def _get_put_option_data(self):
239
+ """
240
+ Get put options data for current expiry.
241
+
242
+ Returns
243
+ -------
244
+ pandas.DataFrame
245
+ DataFrame containing put options data sorted by strike price
246
+ """
247
+ pe_values = [
248
+ data["PE"]
249
+ for data in self.payload["records"]["data"]
250
+ if "PE" in data and data["expiryDate"] == self.expiry_dt
251
+ ]
252
+ return pd.DataFrame(pe_values).sort_values(["strikePrice"])
253
+
254
+ def _get_maximum_pain_strike(self):
255
+ """
256
+ Calculate maximum pain strike price.
257
+
258
+ Returns
259
+ -------
260
+ float
261
+ Strike price where maximum pain occurs
262
+ """
263
+ calls = self._get_call_option_data()
264
+ strikes = calls["strikePrice"]
265
+ ce_oi = calls["openInterest"]
266
+ pe_oi = self._get_put_option_data()["openInterest"]
267
+
268
+ total_pain = [
269
+ sum(ce_oi * np.maximum(0, expiry_price - strikes))
270
+ + sum(pe_oi * np.maximum(0, strikes - expiry_price))
271
+ for expiry_price in strikes
272
+ ]
273
+
274
+ return strikes[np.argmin(total_pain)]
@@ -0,0 +1,152 @@
1
+ import numpy as np
2
+ from enum import Enum
3
+ from pydantic import Field
4
+ from typing import Optional
5
+ from .optioninputs import OptionInputs
6
+
7
+
8
+ class OptionType(str, Enum):
9
+ CALL = "call"
10
+ PUT = "put"
11
+
12
+
13
+ class ExerciseStyle(str, Enum):
14
+ EUROPEAN = "european"
15
+ ASIAN = "asian"
16
+ BARRIER = "barrier"
17
+
18
+
19
+ class BarrierType(str, Enum):
20
+ UP_AND_OUT = "up_and_out"
21
+
22
+
23
+ class MonteCarloOptionPricing:
24
+ """
25
+ Monte Carlo Pricing for options.
26
+
27
+ Parameters
28
+ ----------
29
+ inputs : OptionInputs
30
+ The inputs for the option pricing model.
31
+ nsims : int, optional
32
+ Number of simulations (default is Field(..., gt=0)).
33
+ timestep : int, optional
34
+ Time step (default is Field(..., gt=0)).
35
+ option_type : OptionType
36
+ Type of option (Call or Put).
37
+ exercise_style : ExerciseStyle
38
+ Style of exercise (American, European, or Barrier).
39
+ barrier_level : float, optional
40
+ Barrier level for barrier options (default is None).
41
+ barrier_rebate : int, optional
42
+ Barrier rebate for barrier options (default is None).
43
+ barrier_type : BarrierType, optional
44
+ Type of barrier option (default is None).
45
+
46
+ Attributes
47
+ ----------
48
+ option_price : float
49
+ The calculated option price.
50
+
51
+ Raises
52
+ ------
53
+ ValueError
54
+ If an unsupported exercise style or barrier type is provided.
55
+ """
56
+
57
+ def __init__(
58
+ self,
59
+ inputs: OptionInputs,
60
+ nsims: int = Field(..., gt=0, description="Number of simulations"),
61
+ timestep: int = Field(..., gt=0, description="Time step"),
62
+ option_type: OptionType = Field(..., description="Call or Put"),
63
+ exercise_style: ExerciseStyle = Field(
64
+ ..., description="American or European or Barrier"
65
+ ),
66
+ barrier_level: Optional[float] = Field(
67
+ None, gt=0, description="Barrier level (for barrier options)"
68
+ ),
69
+ barrier_rebate: Optional[int] = Field(
70
+ None, gt=0, description="Barrier rebate (for barrier options)"
71
+ ),
72
+ barrier_type: Optional[BarrierType] = Field(
73
+ None, description="Type of barrier option"
74
+ ),
75
+ ) -> None:
76
+ self.inputs = inputs
77
+ self.spot = self.inputs.spot
78
+ self.strike = self.inputs.strike
79
+ self.rate = self.inputs.rate
80
+ self.ttm = self.inputs.ttm
81
+ self.sigma = self.inputs.volatility
82
+
83
+ self.nsims = nsims
84
+ self.timestep = timestep
85
+ self.option_type = option_type
86
+ self.exercise_style = exercise_style
87
+ self.barrier_level = barrier_level
88
+ self.barrier_rebate = barrier_rebate
89
+ self.barrier_type = barrier_type
90
+
91
+ # Calculate the price immediately upon initialization
92
+ self._calculate_price()
93
+
94
+ def _calculate_price(self):
95
+ """
96
+ Calculate the option price using Monte Carlo simulation.
97
+
98
+ Returns
99
+ -------
100
+ float
101
+ The calculated option price.
102
+ """
103
+ dt = self.ttm / self.timestep
104
+ discount_factor = np.exp(-self.rate * self.ttm)
105
+
106
+ # Simulate price paths
107
+ price_paths = np.zeros((self.nsims, self.timestep + 1))
108
+ price_paths[:, 0] = self.spot
109
+
110
+ for t in range(1, self.timestep + 1):
111
+ z = np.random.standard_normal(self.nsims)
112
+ price_paths[:, t] = price_paths[:, t - 1] * np.exp(
113
+ (self.rate - 0.5 * self.sigma**2) * dt + self.sigma * np.sqrt(dt) * z
114
+ )
115
+
116
+ # Calculate payoff based on option style
117
+ if self.exercise_style == ExerciseStyle.EUROPEAN:
118
+ if self.option_type == OptionType.CALL:
119
+ payoff = np.maximum(price_paths[:, -1] - self.strike, 0)
120
+ else:
121
+ payoff = np.maximum(self.strike - price_paths[:, -1], 0)
122
+ elif self.exercise_style == ExerciseStyle.ASIAN:
123
+ avg_price = np.mean(price_paths, axis=1)
124
+ if self.option_type == OptionType.CALL:
125
+ payoff = np.maximum(avg_price - self.strike, 0)
126
+ else:
127
+ payoff = np.maximum(self.strike - avg_price, 0)
128
+ elif self.exercise_style == ExerciseStyle.BARRIER:
129
+ if self.barrier_type == BarrierType.UP_AND_OUT:
130
+ barrier_shift = self.barrier_level * np.exp(
131
+ 0.5826 * self.sigma * np.sqrt(self.ttm / self.timestep)
132
+ )
133
+ if self.option_type == OptionType.CALL:
134
+ payoff = np.where(
135
+ np.max(price_paths, axis=1) < barrier_shift,
136
+ np.maximum(price_paths[:, -1] - self.strike, 0),
137
+ self.barrier_rebate,
138
+ )
139
+ else:
140
+ payoff = np.where(
141
+ np.max(price_paths, axis=1) < barrier_shift,
142
+ np.maximum(self.strike - price_paths[:, -1], 0),
143
+ self.barrier_rebate,
144
+ )
145
+ else:
146
+ raise ValueError("Currently only supports Up-and-out barrier options.")
147
+ else:
148
+ raise ValueError(f"Unsupported exercise style: {self.exercise_style}")
149
+
150
+ # Calculate option price
151
+ self.option_price = discount_factor * np.mean(payoff)
152
+ return self.option_price
@@ -0,0 +1 @@
1
+ version = "0.0.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: quantmod
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: Quantmod Python Package
5
5
  Home-page: https://kannansingaravelu.com/
6
6
  Author: Kannan Singaravelu
@@ -20,7 +20,6 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE.txt
22
22
  Requires-Dist: joblib
23
- Requires-Dist: jugaad-data
24
23
  Requires-Dist: matplotlib
25
24
  Requires-Dist: numpy>=2.0.2
26
25
  Requires-Dist: pandas>=2.2.2
@@ -57,13 +56,13 @@ pip install quantmod
57
56
 
58
57
  ## Modules
59
58
 
60
- * [markets](https://kannansingaravelu.com/docs/site/markets/)
61
- * [models](https://kannansingaravelu.com/docs/site/models/)
62
- * [risk](https://kannansingaravelu.com/docs/site/risk/)
63
- * [timeseries](https://kannansingaravelu.com/docs/site/timeseries/)
64
- * [indicators](https://kannansingaravelu.com/docs/site/indicators/)
65
- * [derivatives](https://kannansingaravelu.com/docs/site/derivatives/)
66
- * [datasets](https://kannansingaravelu.com/docs/site/datasets/)
59
+ * [markets](https://kannansingaravelu.com/quantmod/markets/)
60
+ * [models](https://kannansingaravelu.com/quantmod/models/)
61
+ * [risk](https://kannansingaravelu.com/quantmod/risk/)
62
+ * [timeseries](https://kannansingaravelu.com/quantmod/timeseries/)
63
+ * [indicators](https://kannansingaravelu.com/quantmod/indicators/)
64
+ * [derivatives](https://kannansingaravelu.com/quantmod/derivatives/)
65
+ * [datasets](https://kannansingaravelu.com/quantmod/datasets/)
67
66
 
68
67
 
69
68
  ## Quickstart
@@ -99,7 +98,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
99
98
 
100
99
 
101
100
  ## Changelog
102
- The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/docs/site/changelog/)
101
+ The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
103
102
 
104
103
 
105
104
  ## Community
@@ -20,7 +20,7 @@ quantmod/datasets/data/__init__.py
20
20
  quantmod/datasets/data/nifty50.csv
21
21
  quantmod/datasets/data/spx.csv
22
22
  quantmod/derivatives/__init__.py
23
- quantmod/derivatives/optionchain.py
23
+ quantmod/derivatives/nse.py
24
24
  quantmod/indicators/__init__.py
25
25
  quantmod/indicators/indicators.py
26
26
  quantmod/markets/__init__.py
@@ -1,5 +1,4 @@
1
1
  joblib
2
- jugaad-data
3
2
  matplotlib
4
3
  numpy>=2.0.2
5
4
  pandas>=2.2.2
@@ -1,249 +0,0 @@
1
- import pandas as pd
2
- import numpy as np
3
- from jugaad_data.nse import NSELive
4
-
5
-
6
- # construct a class object
7
- class OptionChain:
8
- """
9
- A class to fetch and process option chain data from NSE (National Stock Exchange).
10
-
11
- Parameters
12
- ----------
13
- symbol : str
14
- Trading symbol (e.g., 'NIFTY', 'BANKNIFTY' or any equity symbol)
15
- expiry_date : str
16
- Expiry date of the options
17
-
18
- Attributes
19
- ----------
20
- option_chain : pandas.DataFrame
21
- Processed option chain data
22
- call_option_data : pandas.DataFrame
23
- Call options data
24
- put_option_date : pandas.DataFrame
25
- Put options data
26
- option_pain : float
27
- Maximum pain strike price
28
-
29
- Methods
30
- -------
31
- get_option_price()
32
- Get the option price (last traded, bid, or ask) for a specific option
33
- get_synthetic_futures()
34
- Calculate synthetic futures price for a given strike price
35
- """
36
-
37
- def __init__(self, symbol, expiry_date):
38
- self.n = NSELive()
39
- self.symbol = symbol
40
- self.expiry_date = expiry_date
41
-
42
- # flatten json and convert to dataframe
43
- if symbol in ["NIFTY", "BANKNIFTY"]:
44
- df = self.n.index_option_chain(self.symbol)["records"]["data"]
45
- else:
46
- df = self.n.equities_option_chain(self.symbol)["records"]["data"]
47
- df = pd.json_normalize(df).fillna(0)
48
- self.df = df[df["expiryDate"] == self.expiry_date]
49
-
50
- # the __dict__ attributes
51
- self.option_chain = self._get_option_chain()
52
- self.call_option_data = self._get_call_option_data()
53
- self.put_option_data = self._get_put_option_data()
54
- self.option_pain = self._get_option_pain()
55
-
56
- # get option chain
57
- def _get_option_chain(self):
58
- """
59
- Retrieve the complete option chain data.
60
-
61
- Returns
62
- -------
63
- pandas.DataFrame
64
- Complete option chain data for the specified symbol and expiry
65
- """
66
- return self.df
67
-
68
- # Create function to get call and put options
69
- def _get_call_option_data(self):
70
- """
71
- Extract and process call option data from the option chain.
72
-
73
- Returns
74
- -------
75
- pandas.DataFrame
76
- Processed call options data with simplified column names
77
- """
78
- calls = self.option_chain.iloc[:, 21:].reset_index(drop=True)
79
- cols = [i[3:] for i in calls.columns]
80
- calls.columns = cols
81
- return calls
82
-
83
- def _get_put_option_data(self):
84
- """
85
- Extract and process put option data from the option chain.
86
-
87
- Returns
88
- -------
89
- pandas.DataFrame
90
- Processed put options data with simplified column names
91
- """
92
- puts = self.option_chain.iloc[:, 2:21].reset_index(drop=True)
93
- cols = [i[3:] for i in puts.columns]
94
- puts.columns = cols
95
- return puts
96
-
97
- # get option price
98
- def get_option_price(self, strike, opt_type, txn_type=None):
99
- """
100
- Get the option price (last traded, bid, or ask) for a specific option.
101
-
102
- Parameters
103
- ----------
104
- strike : float
105
- Strike price of the option
106
- opt_type : str
107
- Option type ('CE' for Call or 'PE' for Put)
108
- txn_type : str, optional
109
- Transaction type ('buy', 'sell', or None for last price)
110
-
111
- Returns
112
- -------
113
- float
114
- Option price based on the specified parameters
115
- """
116
- price_type = (
117
- "bidprice"
118
- if txn_type == "sell"
119
- else "askPrice"
120
- if txn_type == "buy"
121
- else "lastPrice"
122
- )
123
- col = f"{opt_type}.{price_type}"
124
-
125
- return float(self.df[self.df["strikePrice"] == strike][col].iloc[0])
126
-
127
- # get synthetic futures
128
- def get_synthetic_futures(self, strike):
129
- """
130
- Calculate synthetic futures price for a given strike price.
131
- Synthetic Futures price = Strike + Call - Put
132
-
133
- Parameters
134
- ----------
135
- strike : float
136
- Strike price for which to calculate synthetic futures
137
-
138
- Returns
139
- -------
140
- float
141
- Synthetic futures price
142
- """
143
- return (
144
- strike
145
- + self.get_option_price(strike, opt_type="CE", txn_type="buy")
146
- - self.get_option_price(strike, opt_type="PE", txn_type="sell")
147
- )
148
-
149
- def _get_option_pain(self) -> float:
150
- """
151
- Calculate the maximum pain point - the strike price where the total value of all
152
- options (calls and puts) would cause the most financial pain to option writers.
153
-
154
- Returns
155
- -------
156
- float
157
- Strike price at which maximum pain occurs
158
- """
159
- # Get data from class attributes
160
- strikes = np.array(self.option_chain["strikePrice"], dtype=float)
161
- call_oi = np.array(self.call_option_data["openInterest"], dtype=float)
162
- put_oi = np.array(self.put_option_data["openInterest"], dtype=float)
163
-
164
- # Calculate pain for each possible expiry price
165
- total_pain = []
166
-
167
- for expiry_price in strikes:
168
- # Calculate call options pain
169
- call_pain = sum(
170
- call_oi[i] * max(0, expiry_price - strike)
171
- for i, strike in enumerate(strikes)
172
- )
173
-
174
- # Calculate put options pain
175
- put_pain = sum(
176
- put_oi[i] * max(0, strike - expiry_price)
177
- for i, strike in enumerate(strikes)
178
- )
179
-
180
- # Total pain at this price level
181
- total_pain.append(call_pain + put_pain)
182
-
183
- # Find strike with minimum pain
184
- max_pain_index = np.argmin(total_pain)
185
- return float(strikes[max_pain_index])
186
-
187
- def plot_pain_analysis(self):
188
- """
189
- Plot pain analysis for the option chain.
190
- """
191
- # Get data from class attributes
192
- strikes = np.array(self.option_chain["strikePrice"], dtype=float)
193
- call_oi = np.array(self.call_option_data["openInterest"], dtype=float)
194
- put_oi = np.array(self.put_option_data["openInterest"], dtype=float)
195
-
196
- results = []
197
-
198
- for expiry_price in strikes:
199
- # Calculate call options pain
200
- call_pain = sum(
201
- call_oi[i] * max(0, expiry_price - strike)
202
- for i, strike in enumerate(strikes)
203
- )
204
-
205
- # Calculate put options pain
206
- put_pain = sum(
207
- put_oi[i] * max(0, strike - expiry_price)
208
- for i, strike in enumerate(strikes)
209
- )
210
-
211
- results.append(
212
- {
213
- "Strike": expiry_price,
214
- "Call Pain": call_pain,
215
- "Put Pain": put_pain,
216
- "Total Pain": call_pain + put_pain,
217
- }
218
- )
219
-
220
- analysis = pd.DataFrame(results)
221
- # Plot the pain distribution (requires matplotlib)
222
- try:
223
- import matplotlib.pyplot as plt
224
-
225
- plt.figure(figsize=(10, 6))
226
- plt.plot(
227
- analysis["Strike"], analysis["Total Pain"], "b-", label="Total Pain"
228
- )
229
- plt.plot(
230
- analysis["Strike"], analysis["Call Pain"], "g--", label="Call Pain"
231
- )
232
- plt.plot(analysis["Strike"], analysis["Put Pain"], "r--", label="Put Pain")
233
- plt.axvline(
234
- x=self._get_option_pain(),
235
- color="k",
236
- linestyle=":",
237
- label=f"Max Pain Strike: {self._get_option_pain():}",
238
- )
239
-
240
- plt.title("Option Pain Analysis")
241
- plt.xlabel("Strike Price")
242
- plt.ylabel("Pain Value")
243
- plt.legend()
244
- plt.grid(True)
245
-
246
- except ImportError:
247
- print("\nMatplotlib not installed. Skipping plot generation.")
248
-
249
- plt.show()
@@ -1,145 +0,0 @@
1
- import numpy as np
2
- from pydantic import BaseModel, Field
3
- from typing import Optional, Tuple
4
- from .optioninputs import OptionInputs
5
-
6
-
7
- # Monte Carlo Option Pricing Engine
8
- class MonteCarloOptionPricing:
9
- """
10
- Class for Monte Carlo Option Pricing
11
-
12
- Parameters
13
- ----------
14
- inputs : OptionInputs
15
- Object containing the following option parameters:
16
- spot : float
17
- Current price of the underlying asset
18
- strike : float
19
- Strike price of the option
20
- rate : float
21
- Risk-free interest rate (as a decimal)
22
- ttm : float
23
- Time to maturity in years
24
- volatility : float
25
- Implied volatility of the underlying asset (as a decimal)
26
- - callprice : float, optional
27
- Market price of call option (used for implied volatility calculation)
28
- - putprice : float, optional
29
- Market price of put option (used for implied volatility calculation)
30
- nsims : int, optional
31
- Number of simulations (default is Field(..., gt=0)).
32
- timestep : int, optional
33
- Time step (default is Field(252, gt=0)).
34
- option_type : OptionType
35
- Type of option (Call or Put).
36
- option_style : OptionStyle
37
- Style of option (American, European, or Barrier).
38
- barrier_level : float, optional
39
- Barrier level for barrier options (default is None).
40
- barrier_rebate : int, optional
41
- Barrier rebate for barrier options (default is None).
42
- barrier_type : BarrierType
43
- Type of barrier option.
44
-
45
- Returns
46
- -------
47
- attributes: float
48
- call_vanilla, put_vanilla
49
-
50
- call_asian, put_asian
51
-
52
- upandoutcall
53
- """
54
-
55
- def __init__(
56
- self,
57
- inputs: OptionInputs,
58
- initialspot: float = Field(..., gt=0, description="Initial stock price"),
59
- nsims: int = Field(..., gt=0, description="Number of simulations"),
60
- timestep: int = 252,
61
- barrier: Optional[float] = None,
62
- rebate: Optional[int] = None,
63
- ) -> None:
64
- self.inputs = inputs
65
- self.initialspot = initialspot
66
- self.nsims = nsims
67
- self.timestep = timestep
68
- self.barrier = barrier
69
- self.rebate = rebate
70
-
71
- self.call_vanilla, self.put_vanilla = self._vanillaoption()
72
- self.call_asian, self.put_asian = self._asianoption()
73
- self.upandoutcall = self._upandoutcall()
74
-
75
- @property
76
- def _discount_factor(self) -> float:
77
- return np.exp(-self.inputs.rate * self.inputs.ttm)
78
-
79
- @property
80
- def _pseudorandomnumber(self) -> np.ndarray:
81
- return np.random.standard_normal(self.nsims)
82
-
83
- @property
84
- def _simulatepath(self) -> np.ndarray:
85
- """Simulate price path"""
86
- np.random.seed(2024)
87
-
88
- dt = self.inputs.ttm / self.timestep
89
-
90
- S = np.zeros((self.timestep, self.nsims))
91
- S[0] = self.initialspot
92
-
93
- for i in range(0, self.timestep - 1):
94
- w = self._pseudorandomnumber
95
- S[i + 1] = S[i] * (
96
- 1 + self.inputs.rate * dt + self.inputs.volatility * np.sqrt(dt) * w
97
- )
98
-
99
- return S
100
-
101
- def _vanillaoption(self) -> Tuple[float, float]:
102
- """Calculate vanilla option payoff"""
103
- S = self._simulatepath
104
-
105
- vanilla_call = self._discount_factor * np.mean(
106
- np.maximum(0, S[-1] - self.inputs.strike)
107
- )
108
- vanilla_put = self._discount_factor * np.mean(
109
- np.maximum(0, self.inputs.strike - S[-1])
110
- )
111
-
112
- return [vanilla_call, vanilla_put]
113
-
114
- def _asianoption(self) -> Tuple[float]:
115
- """Calculate asian option payoff"""
116
- S = self._simulatepath
117
-
118
- A = S.mean(axis=0)
119
-
120
- asian_call = self._discount_factor * np.mean(
121
- np.maximum(0, A - self.inputs.strike)
122
- )
123
- asian_put = self._discount_factor * np.mean(
124
- np.maximum(0, self.inputs.strike - A)
125
- )
126
-
127
- return [asian_call, asian_put]
128
-
129
- def _upandoutcall(self) -> float:
130
- """Calculate up-and-out barrier call option payoff"""
131
- S = self._simulatepath
132
-
133
- # Barrier shift
134
- barriershift = self.barrier * np.exp(
135
- 0.5826 * self.inputs.volatility * np.sqrt(self.inputs.ttm / self.timestep)
136
- )
137
-
138
- value = 0
139
- for i in range(self.nsims):
140
- if S[:, i].max() < barriershift:
141
- value += np.maximum(0, S[-1, i] - self.inputs.strike)
142
- else:
143
- value += self.rebate
144
-
145
- return self._discount_factor * value / self.nsims
@@ -1 +0,0 @@
1
- version = "0.0.5"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes