quantmod 0.0.4__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.
- {quantmod-0.0.4 → quantmod-0.0.6}/PKG-INFO +25 -11
- {quantmod-0.0.4 → quantmod-0.0.6}/README.md +8 -8
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/__init__.py +3 -10
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/derivatives/__init__.py +2 -4
- quantmod-0.0.6/quantmod/derivatives/nse.py +274 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/indicators/indicators.py +27 -22
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/markets/yahoo.py +30 -15
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/models/__init__.py +5 -1
- quantmod-0.0.6/quantmod/models/binomial.py +184 -0
- quantmod-0.0.6/quantmod/models/blackscholes.py +393 -0
- quantmod-0.0.6/quantmod/models/montecarlo.py +152 -0
- quantmod-0.0.6/quantmod/models/optioninputs.py +49 -0
- quantmod-0.0.6/quantmod/risk/__init__.py +26 -0
- quantmod-0.0.6/quantmod/risk/var.py +108 -0
- quantmod-0.0.6/quantmod/risk/varbacktest.py +124 -0
- quantmod-0.0.6/quantmod/risk/varinputs.py +62 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/timeseries/performance.py +56 -49
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/timeseries/timeseries.py +12 -15
- quantmod-0.0.6/quantmod/version.py +1 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/PKG-INFO +25 -11
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/SOURCES.txt +4 -3
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/requires.txt +3 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/setup.py +9 -10
- quantmod-0.0.4/quantmod/derivatives/options.py +0 -47
- quantmod-0.0.4/quantmod/models/blackscholes.py +0 -156
- quantmod-0.0.4/quantmod/models/montecarlo.py +0 -115
- quantmod-0.0.4/quantmod/models/optioninputs.py +0 -44
- quantmod-0.0.4/quantmod/risk/__init__.py +0 -27
- quantmod-0.0.4/quantmod/risk/riskinputs.py +0 -36
- quantmod-0.0.4/quantmod/risk/var.py +0 -103
- quantmod-0.0.4/quantmod/risk/varbacktester.py +0 -85
- quantmod-0.0.4/quantmod/version.py +0 -1
- {quantmod-0.0.4 → quantmod-0.0.6}/LICENSE.txt +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/_version.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/datasets/__init__.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/datasets/data/__init__.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/datasets/data/nifty50.csv +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/datasets/data/spx.csv +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/datasets/dataloader.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/indicators/__init__.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/main.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/markets/__init__.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/markets/bb.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/timeseries/__init__.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod/utils.py +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/dependency_links.txt +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/entry_points.txt +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/not-zip-safe +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/quantmod.egg-info/top_level.txt +0 -0
- {quantmod-0.0.4 → quantmod-0.0.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: quantmod
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Quantmod Python Package
|
|
5
5
|
Home-page: https://kannansingaravelu.com/
|
|
6
6
|
Author: Kannan Singaravelu
|
|
@@ -9,7 +9,6 @@ License: Apache License 2.0
|
|
|
9
9
|
Keywords: python,quant,quantmod,quantmod-python
|
|
10
10
|
Platform: any
|
|
11
11
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Operating System :: OS Independent
|
|
15
14
|
Classifier: Intended Audience :: Developers
|
|
@@ -20,13 +19,28 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
20
19
|
Requires-Python: >=3.10
|
|
21
20
|
Description-Content-Type: text/markdown
|
|
22
21
|
License-File: LICENSE.txt
|
|
22
|
+
Requires-Dist: joblib
|
|
23
|
+
Requires-Dist: matplotlib
|
|
23
24
|
Requires-Dist: numpy>=2.0.2
|
|
24
25
|
Requires-Dist: pandas>=2.2.2
|
|
25
26
|
Requires-Dist: pydantic>=2.8.2
|
|
26
27
|
Requires-Dist: scipy>=1.13.1
|
|
28
|
+
Requires-Dist: sqlalchemy>=2.0.38
|
|
27
29
|
Requires-Dist: tabulate>=0.9.0
|
|
28
30
|
Requires-Dist: urllib3==1.26.15
|
|
29
31
|
Requires-Dist: yfinance>=0.2.43
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: keywords
|
|
39
|
+
Dynamic: license
|
|
40
|
+
Dynamic: platform
|
|
41
|
+
Dynamic: requires-dist
|
|
42
|
+
Dynamic: requires-python
|
|
43
|
+
Dynamic: summary
|
|
30
44
|
|
|
31
45
|
|
|
32
46
|
Quantmod Python package is inspired by the quantmod package for R. This new tool is designed to assist quantitative traders and data analysts with the development, testing, and rapid prototyping of trading strategies. quantmod features a straightforward and intuitive interface aimed at simplifying workflows and boosting productivity.
|
|
@@ -42,13 +56,13 @@ pip install quantmod
|
|
|
42
56
|
|
|
43
57
|
## Modules
|
|
44
58
|
|
|
45
|
-
* [markets](https://kannansingaravelu.com/
|
|
46
|
-
* [models](https://kannansingaravelu.com/
|
|
47
|
-
* [risk](https://kannansingaravelu.com/
|
|
48
|
-
* [timeseries](https://kannansingaravelu.com/
|
|
49
|
-
* [indicators](https://kannansingaravelu.com/
|
|
50
|
-
* [derivatives](https://kannansingaravelu.com/
|
|
51
|
-
* [datasets](https://kannansingaravelu.com/
|
|
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/)
|
|
52
66
|
|
|
53
67
|
|
|
54
68
|
## Quickstart
|
|
@@ -84,7 +98,7 @@ Refer to the [examples](https://kannansingaravelu.com/) section for more details
|
|
|
84
98
|
|
|
85
99
|
|
|
86
100
|
## Changelog
|
|
87
|
-
The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/
|
|
101
|
+
The list of changes to quantmod between each release can be found [here](https://kannansingaravelu.com/quantmod/changelog/)
|
|
88
102
|
|
|
89
103
|
|
|
90
104
|
## Community
|
|
@@ -12,13 +12,13 @@ pip install quantmod
|
|
|
12
12
|
|
|
13
13
|
## Modules
|
|
14
14
|
|
|
15
|
-
* [markets](https://kannansingaravelu.com/
|
|
16
|
-
* [models](https://kannansingaravelu.com/
|
|
17
|
-
* [risk](https://kannansingaravelu.com/
|
|
18
|
-
* [timeseries](https://kannansingaravelu.com/
|
|
19
|
-
* [indicators](https://kannansingaravelu.com/
|
|
20
|
-
* [derivatives](https://kannansingaravelu.com/
|
|
21
|
-
* [datasets](https://kannansingaravelu.com/
|
|
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/
|
|
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
|
|
@@ -23,14 +23,7 @@
|
|
|
23
23
|
from . import version
|
|
24
24
|
from .main import hello
|
|
25
25
|
|
|
26
|
-
from quantmod import
|
|
27
|
-
derivatives,
|
|
28
|
-
indicators,
|
|
29
|
-
markets,
|
|
30
|
-
models,
|
|
31
|
-
timeseries,
|
|
32
|
-
datasets
|
|
33
|
-
)
|
|
26
|
+
from quantmod import derivatives, indicators, markets, models, timeseries, datasets
|
|
34
27
|
|
|
35
28
|
|
|
36
29
|
__all__ = [
|
|
@@ -40,8 +33,8 @@ __all__ = [
|
|
|
40
33
|
"markets",
|
|
41
34
|
"models",
|
|
42
35
|
"timeseries",
|
|
43
|
-
"datasets"
|
|
36
|
+
"datasets",
|
|
44
37
|
]
|
|
45
38
|
|
|
46
39
|
__version__ = version.version
|
|
47
|
-
__author__ = "Kannan Singaravelu"
|
|
40
|
+
__author__ = "Kannan Singaravelu"
|
|
@@ -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)]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
def ATR(df: pd.DataFrame, lookback: int = 14) -> pd.Series:
|
|
4
5
|
"""
|
|
5
6
|
Calculate the Average True Range (ATR).
|
|
@@ -20,20 +21,20 @@ def ATR(df: pd.DataFrame, lookback: int = 14) -> pd.Series:
|
|
|
20
21
|
pd.Series
|
|
21
22
|
series containing the ATR values for each period
|
|
22
23
|
"""
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
df = df.copy()
|
|
25
26
|
df.rename(columns=str.lower, inplace=True)
|
|
26
|
-
|
|
27
|
-
df[
|
|
28
|
-
df[
|
|
29
|
-
df[
|
|
30
|
-
|
|
31
|
-
df[
|
|
32
|
-
df[
|
|
33
|
-
|
|
34
|
-
data = df.drop([
|
|
35
|
-
|
|
36
|
-
return data[
|
|
27
|
+
|
|
28
|
+
df["H-L"] = abs(df["high"] - df["low"])
|
|
29
|
+
df["H-PC"] = abs(df["high"] - df["close"].shift(1))
|
|
30
|
+
df["L-PC"] = abs(df["low"] - df["close"].shift(1))
|
|
31
|
+
|
|
32
|
+
df["TR"] = df[["H-L", "H-PC", "L-PC"]].max(axis=1, skipna=False)
|
|
33
|
+
df["ATR"] = df["TR"].rolling(lookback).mean()
|
|
34
|
+
|
|
35
|
+
data = df.drop(["H-L", "H-PC", "L-PC"], axis=1) # drop columns
|
|
36
|
+
|
|
37
|
+
return data["ATR"]
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
def BBands(series: pd.Series, lookback: int, multiplier: float = 2) -> pd.Series:
|
|
@@ -77,7 +78,7 @@ def SMA(series: pd.Series, lookback: int) -> pd.Series:
|
|
|
77
78
|
time series to calculate the Moving Average for
|
|
78
79
|
lookback : int
|
|
79
80
|
lookback period for the Moving Average
|
|
80
|
-
|
|
81
|
+
|
|
81
82
|
Returns
|
|
82
83
|
-------
|
|
83
84
|
pd.Series
|
|
@@ -86,6 +87,7 @@ def SMA(series: pd.Series, lookback: int) -> pd.Series:
|
|
|
86
87
|
|
|
87
88
|
return series.rolling(lookback).mean()
|
|
88
89
|
|
|
90
|
+
|
|
89
91
|
def EMA(series: pd.Series, lookback: int) -> pd.Series:
|
|
90
92
|
"""
|
|
91
93
|
Exponential Moving Average
|
|
@@ -96,7 +98,7 @@ def EMA(series: pd.Series, lookback: int) -> pd.Series:
|
|
|
96
98
|
time series to calculate the Exponential Moving Average for
|
|
97
99
|
lookback : int
|
|
98
100
|
lookback period for the Exponential Moving Average
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
Returns
|
|
101
103
|
-------
|
|
102
104
|
pd.Series
|
|
@@ -120,7 +122,7 @@ def MACD(series: pd.Series, lookback: int, fast: int, slow: int) -> pd.Series:
|
|
|
120
122
|
fast period for the MACD
|
|
121
123
|
slow : int
|
|
122
124
|
slow period for the MACD
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
Returns
|
|
125
127
|
-------
|
|
126
128
|
pd.Series
|
|
@@ -128,13 +130,17 @@ def MACD(series: pd.Series, lookback: int, fast: int, slow: int) -> pd.Series:
|
|
|
128
130
|
"""
|
|
129
131
|
|
|
130
132
|
# Calculate MACD line
|
|
131
|
-
macd =
|
|
132
|
-
|
|
133
|
+
macd = (
|
|
134
|
+
series.ewm(span=fast, adjust=False).mean()
|
|
135
|
+
- series.ewm(span=slow, adjust=False).mean()
|
|
136
|
+
)
|
|
137
|
+
|
|
133
138
|
# Calculate Signal line from the MACD line
|
|
134
139
|
signal = macd.ewm(span=lookback, adjust=False).mean()
|
|
135
|
-
|
|
140
|
+
|
|
136
141
|
return macd, signal
|
|
137
142
|
|
|
143
|
+
|
|
138
144
|
def RSI(series: pd.Series, lookback: int) -> pd.Series:
|
|
139
145
|
"""
|
|
140
146
|
Relative Strength Index
|
|
@@ -145,7 +151,7 @@ def RSI(series: pd.Series, lookback: int) -> pd.Series:
|
|
|
145
151
|
time series to calculate the RSI for
|
|
146
152
|
lookback : int
|
|
147
153
|
lookback period for the RSI
|
|
148
|
-
|
|
154
|
+
|
|
149
155
|
Returns
|
|
150
156
|
-------
|
|
151
157
|
pd.Series
|
|
@@ -158,10 +164,9 @@ def RSI(series: pd.Series, lookback: int) -> pd.Series:
|
|
|
158
164
|
# Separate gains and losses
|
|
159
165
|
gain = (delta.where(delta > 0, 0)).rolling(window=lookback).mean()
|
|
160
166
|
loss = (-delta.where(delta < 0, 0)).rolling(window=lookback).mean()
|
|
161
|
-
|
|
167
|
+
|
|
162
168
|
# Calculate the Relative Strength (RS)
|
|
163
169
|
rs = gain / loss
|
|
164
|
-
|
|
170
|
+
|
|
165
171
|
# Calculate RSI
|
|
166
172
|
return 100 - (100 / (1 + rs))
|
|
167
|
-
|
|
@@ -46,32 +46,38 @@ from typing import Union, List
|
|
|
46
46
|
# # Ensure cache directory exists
|
|
47
47
|
# if not os.path.exists(CACHE_DIR):
|
|
48
48
|
# os.makedirs(CACHE_DIR)
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
# cache_file = _get_cache_file_name(tickers, start_date, end_date, period, interval)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# # Check if cached file exists
|
|
53
53
|
# if os.path.exists(cache_file):
|
|
54
54
|
# # print(f"Loading data from cache: {cache_file}")
|
|
55
55
|
# return joblib.load(cache_file)
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
# try:
|
|
58
58
|
# # Download data from yfinance
|
|
59
59
|
# data = yf.download(tickers, start=start_date, end=end_date, auto_adjust=True, progress=False, period=period, interval=interval)
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
# # Save data to cache
|
|
62
62
|
# joblib.dump(data, cache_file)
|
|
63
63
|
# # print(f"Data cached to: {cache_file}")
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
# except Exception as e:
|
|
66
66
|
# print(f"Error downloading data: {e}")
|
|
67
67
|
# raise
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
# return data
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def getData(
|
|
72
|
+
def getData(
|
|
73
|
+
tickers: Union[str, List[str]],
|
|
74
|
+
start_date: str = None,
|
|
75
|
+
end_date: str = None,
|
|
76
|
+
period: str = "1mo",
|
|
77
|
+
interval: str = "1d",
|
|
78
|
+
) -> pd.DataFrame:
|
|
73
79
|
"""
|
|
74
|
-
Retrieve data
|
|
80
|
+
Retrieve data using yfinance library for specified tickers.
|
|
75
81
|
|
|
76
82
|
Parameters
|
|
77
83
|
----------
|
|
@@ -94,16 +100,25 @@ def getData(tickers: Union[str, List[str]], start_date: str = None, end_date: st
|
|
|
94
100
|
DataFrame with OHLC[A]V (Open, High, Low, Close, Adj Close, Volume).
|
|
95
101
|
"""
|
|
96
102
|
|
|
97
|
-
cols = [
|
|
98
|
-
data = yf.download(
|
|
99
|
-
|
|
103
|
+
cols = ["Open", "High", "Low", "Close", "Volume"]
|
|
104
|
+
data = yf.download(
|
|
105
|
+
tickers,
|
|
106
|
+
start=start_date,
|
|
107
|
+
end=end_date,
|
|
108
|
+
auto_adjust=True,
|
|
109
|
+
progress=False,
|
|
110
|
+
multi_level_index=False,
|
|
111
|
+
period=period,
|
|
112
|
+
interval=interval,
|
|
113
|
+
)
|
|
114
|
+
|
|
100
115
|
return data[cols]
|
|
101
116
|
|
|
102
117
|
|
|
103
|
-
# Retrieve ticker object
|
|
118
|
+
# Retrieve ticker object using yfinance library
|
|
104
119
|
def getTicker(ticker: str) -> yf.Ticker:
|
|
105
120
|
"""
|
|
106
|
-
Retrieve ticker object
|
|
121
|
+
Retrieve ticker object using yfinance library.
|
|
107
122
|
|
|
108
123
|
Parameters
|
|
109
124
|
----------
|
|
@@ -118,7 +133,7 @@ def getTicker(ticker: str) -> yf.Ticker:
|
|
|
118
133
|
|
|
119
134
|
# for all available options, refer yfinance
|
|
120
135
|
# .info
|
|
121
|
-
# .history
|
|
136
|
+
# .history
|
|
122
137
|
# .actions
|
|
123
138
|
# .dividends
|
|
124
139
|
# .splits
|
|
@@ -138,7 +153,7 @@ def getTicker(ticker: str) -> yf.Ticker:
|
|
|
138
153
|
# .earnings_dates
|
|
139
154
|
# .isin
|
|
140
155
|
# .options
|
|
141
|
-
|
|
156
|
+
# . option_chain('YYYY-MM-DD')
|
|
142
157
|
# .news
|
|
143
158
|
|
|
144
159
|
return yf.Ticker(ticker)
|
|
@@ -19,10 +19,14 @@
|
|
|
19
19
|
from .optioninputs import OptionInputs
|
|
20
20
|
from .blackscholes import BlackScholesOptionPricing
|
|
21
21
|
from .montecarlo import MonteCarloOptionPricing
|
|
22
|
+
from .binomial import OptionType, ExerciseStyle, BinomialOptionPricing
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
__all__ = [
|
|
25
26
|
"OptionInputs",
|
|
26
|
-
"
|
|
27
|
+
"OptionType",
|
|
28
|
+
"ExerciseStyle",
|
|
29
|
+
"BlackScholesOptionPricing",
|
|
27
30
|
"MonteCarloOptionPricing",
|
|
31
|
+
"BinomialOptionPricing",
|
|
28
32
|
]
|