stochvolmodels 1.0.27__tar.gz → 1.0.28__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 (48) hide show
  1. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/PKG-INFO +1 -1
  2. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/pyproject.toml +1 -1
  3. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/__init__.py +2 -0
  4. stochvolmodels-1.0.28/stochvolmodels/data/fetch_option_chain.py +174 -0
  5. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/examples/run_lognormal_sv_pricer.py +12 -12
  6. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/logsv_pricer.py +2 -2
  7. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/LICENSE.txt +0 -0
  8. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/README.md +0 -0
  9. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/data/__init__.py +0 -0
  10. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/data/option_chain.py +0 -0
  11. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/data/test_option_chain.py +0 -0
  12. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/examples/quick_run_lognormal_sv_pricer.py +0 -0
  13. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/examples/run_heston.py +0 -0
  14. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/examples/run_heston_sv_pricer.py +0 -0
  15. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/examples/run_pricing_options_on_qvar.py +0 -0
  16. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/__init__.py +0 -0
  17. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/analytic/__init__.py +0 -0
  18. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/analytic/bachelier.py +0 -0
  19. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/analytic/bsm.py +0 -0
  20. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/analytic/tdist.py +0 -0
  21. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/double_exp_pricer.py +0 -0
  22. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/factor_hjm_pricer.py +0 -0
  23. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_affine_expansion.py +0 -0
  24. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_core.py +0 -0
  25. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_evaluate.py +0 -0
  26. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_factor_basis.py +0 -0
  27. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_logsv_ivols.py +0 -0
  28. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_logsv_params.py +0 -0
  29. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/factor_hjm/rate_logsv_pricer.py +0 -0
  30. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/gmm_pricer.py +0 -0
  31. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/hawkes_jd_pricer.py +0 -0
  32. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/heston_pricer.py +0 -0
  33. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/logsv/__init__.py +0 -0
  34. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/logsv/affine_expansion.py +0 -0
  35. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/logsv/logsv_params.py +0 -0
  36. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/logsv/vol_moments_ode.py +0 -0
  37. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/model_pricer.py +0 -0
  38. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/pricers/tdist_pricer.py +0 -0
  39. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/tests/__init__.py +0 -0
  40. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/tests/bsm_mgf_pricer.py +0 -0
  41. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/tests/qv_pricer.py +0 -0
  42. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/__init__.py +0 -0
  43. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/config.py +0 -0
  44. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/funcs.py +0 -0
  45. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/mc_payoffs.py +0 -0
  46. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/mgf_pricer.py +0 -0
  47. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/plots.py +0 -0
  48. {stochvolmodels-1.0.27 → stochvolmodels-1.0.28}/stochvolmodels/utils/var_swap_pricer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: stochvolmodels
3
- Version: 1.0.27
3
+ Version: 1.0.28
4
4
  Summary: Implementation of stochastic volatility models for option pricing
5
5
  License: LICENSE.txt
6
6
  Keywords: volatility,options,Black-Scholes,Heston,Monte-Carlo
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "stochvolmodels"
3
- version = "1.0.27"
3
+ version = "1.0.28"
4
4
  description = "Implementation of stochastic volatility models for option pricing"
5
5
  license = "LICENSE.txt"
6
6
  authors = ["Artur Sepp <artursepp@gmail.com>"]
@@ -159,3 +159,5 @@ from stochvolmodels.utils.plots import (
159
159
 
160
160
 
161
161
  from stochvolmodels.pricers.logsv.vol_moments_ode import compute_analytic_qvar
162
+
163
+ from stochvolmodels.data.option_chain import generate_vol_chain_np
@@ -0,0 +1,174 @@
1
+ """
2
+ this module is using option-chain-analytics package
3
+ to fetch OptionChain data with options data
4
+ see https://pypi.org/project/option-chain-analytics
5
+ """
6
+
7
+ import pandas as pd
8
+ import numpy as np
9
+ import matplotlib.pyplot as plt
10
+ from qis import TimePeriod
11
+ from typing import Dict, Tuple, Optional, Literal
12
+ from numba.typed import List
13
+ from enum import Enum
14
+ import qis as qis
15
+ # chain
16
+ from option_chain_analytics import OptionsDataDFs, create_chain_from_from_options_dfs
17
+ from option_chain_analytics.option_chain import SliceColumn, SlicesChain
18
+ # analytics
19
+ from stochvolmodels.data.option_chain import OptionChain
20
+
21
+
22
+ def generate_vol_chain_np(chain: SlicesChain,
23
+ value_time: pd.Timestamp,
24
+ days_map: Dict[str, int] = {'1w': 7, '1m': 21},
25
+ delta_bounds: Tuple[Optional[float], Optional[float]] = (-0.1, 0.1),
26
+ is_filtered: bool = True
27
+ ) -> OptionChain:
28
+ """
29
+ given SlicesChain generate OptionChain for calibration inputs
30
+ """
31
+
32
+ ttms, future_prices, discfactors = List(), List(), List()
33
+ optiontypes_ttms, strikes_ttms = List(), List()
34
+ bid_ivs, ask_ivs = List(), List()
35
+ bid_prices, ask_prices = List(), List()
36
+ slice_ids = []
37
+ for label, day in days_map.items():
38
+ next_date = value_time + pd.DateOffset(days=day) # if overlapping next date will be last avilable maturity
39
+ slice_date = chain.get_next_slice_after_date(mat_date=next_date)
40
+ slice_t = chain.expiry_slices[slice_date]
41
+ df = slice_t.get_joint_slice(delta_bounds=delta_bounds, is_filtered=is_filtered)
42
+ if not df.empty:
43
+ slice_ids.append(f"{label}: {slice_t.expiry_id}")
44
+ ttms.append(slice_t.get_ttm())
45
+ future_prices.append(slice_t.get_future_price())
46
+ discfactors.append(1.0)
47
+ strikes_ttms.append(df.index.to_numpy())
48
+ optiontypes_ttms.append(df[SliceColumn.OPTION_TYPE].to_numpy(dtype=str))
49
+ bid_ivs.append(df[SliceColumn.BID_IV].to_numpy())
50
+ ask_ivs.append(df[SliceColumn.ASK_IV].to_numpy())
51
+ bid_prices.append(df[SliceColumn.BID_PRICE].to_numpy())
52
+ ask_prices.append(df[SliceColumn.ASK_PRICE].to_numpy())
53
+
54
+ out = OptionChain(ttms=np.array(ttms),
55
+ forwards=np.array(future_prices),
56
+ discfactors=np.array(discfactors),
57
+ ids=np.array(slice_ids),
58
+ strikes_ttms=strikes_ttms,
59
+ optiontypes_ttms=optiontypes_ttms,
60
+ bid_ivs=bid_ivs,
61
+ ask_ivs=ask_ivs,
62
+ bid_prices=bid_prices,
63
+ ask_prices=ask_prices)
64
+ return out
65
+
66
+
67
+ def load_option_chain(options_data_dfs: OptionsDataDFs,
68
+ value_time: pd.Timestamp = pd.Timestamp('2023-02-06 08:00:00+00:00'),
69
+ days_map: Dict[str, int] = {'1w': 7, '1m': 21},
70
+ delta_bounds: Tuple[Optional[float], Optional[float]] = (-0.1, 0.1),
71
+ is_filtered: bool = True
72
+ ) -> Optional[OptionChain]:
73
+ chain = create_chain_from_from_options_dfs(options_data_dfs=options_data_dfs, value_time=value_time)
74
+ if chain is not None:
75
+ option_chain = generate_vol_chain_np(chain=chain,
76
+ value_time=value_time,
77
+ days_map=days_map,
78
+ delta_bounds=delta_bounds,
79
+ is_filtered=is_filtered)
80
+ else:
81
+ option_chain = None
82
+
83
+ return option_chain
84
+
85
+
86
+ def sample_option_chain_at_times(options_data_dfs: OptionsDataDFs,
87
+ time_period: TimePeriod,
88
+ freq: str = 'W-FRI',
89
+ days_map: Dict[str, int] = {'1w': 7, '1m': 21},
90
+ delta_bounds: Tuple[Optional[float], Optional[float]] = (-0.1, 0.1),
91
+ hour_offset: int = 8
92
+ ) -> Dict[pd.Timestamp, OptionChain]:
93
+ value_times = qis.generate_dates_schedule(time_period=time_period,
94
+ freq=freq,
95
+ hour_offset=hour_offset)
96
+ option_chains = {}
97
+ for value_time in value_times:
98
+ option_chains[value_time] = load_option_chain(options_data_dfs=options_data_dfs,
99
+ value_time=value_time,
100
+ days_map=days_map,
101
+ delta_bounds=delta_bounds,
102
+ is_filtered=True)
103
+ return option_chains
104
+
105
+
106
+ def load_price_data(options_data_dfs: OptionsDataDFs,
107
+ time_period: TimePeriod = None,
108
+ data: Literal['spot', 'perp', 'funding_rate'] = 'spot',
109
+ freq: Optional[str] = 'D' # to do
110
+ ) -> pd.Series:
111
+ #options_data_dfs = OptionsDataDFs(**ts_data_loader_wrapper(ticker=ticker, freq='D', hour_offset=8))
112
+ spot_price = options_data_dfs.get_spot_data()[data]
113
+ if freq is not None:
114
+ spot_price = spot_price.resample(freq).last()
115
+ if time_period is not None:
116
+ spot_price = time_period.locate(spot_price)
117
+ return spot_price
118
+
119
+
120
+ class UnitTests(Enum):
121
+ PRINT_CHAIN_DATA = 1
122
+ GENERATE_VOL_CHAIN_NP = 2
123
+ SAMPLE_CHAIN_AT_TIMES = 3
124
+
125
+
126
+ def run_unit_test(unit_test: UnitTests):
127
+
128
+ ticker = 'BTC' # BTC, ETH
129
+ value_time = pd.Timestamp('2021-10-21 08:00:00+00:00')
130
+ value_time = pd.Timestamp('2023-10-06 08:00:00+00:00')
131
+
132
+ from option_chain_analytics.ts_loaders import ts_data_loader_wrapper
133
+ options_data_dfs = OptionsDataDFs(**ts_data_loader_wrapper(ticker=ticker))
134
+ options_data_dfs.get_start_end_date().print()
135
+ chain = create_chain_from_from_options_dfs(options_data_dfs=options_data_dfs, value_time=value_time)
136
+
137
+ if unit_test == UnitTests.PRINT_CHAIN_DATA:
138
+ for expiry, eslice in chain.expiry_slices.items():
139
+ eslice.print()
140
+
141
+ elif unit_test == UnitTests.GENERATE_VOL_CHAIN_NP:
142
+ option_chain = generate_vol_chain_np(chain=chain,
143
+ value_time=value_time,
144
+ days_map={'1w': 7},
145
+ delta_bounds=(-0.1, 0.1),
146
+ is_filtered=True)
147
+ option_chain.print()
148
+ skews = option_chain.get_chain_skews(delta=0.35)
149
+ print(skews)
150
+
151
+ elif unit_test == UnitTests.SAMPLE_CHAIN_AT_TIMES:
152
+ time_period = qis.TimePeriod('01Jan2023', '31Jan2023', tz='UTC')
153
+ option_chains = sample_option_chain_at_times(options_data_dfs=options_data_dfs,
154
+ time_period=time_period,
155
+ freq='W-FRI',
156
+ hour_offset=9
157
+ )
158
+ for key, chain in option_chains.items():
159
+ print(f"{key}")
160
+ print(chain)
161
+
162
+ plt.show()
163
+
164
+
165
+ if __name__ == '__main__':
166
+
167
+ unit_test = UnitTests.SAMPLE_CHAIN_AT_TIMES
168
+
169
+ is_run_all_tests = False
170
+ if is_run_all_tests:
171
+ for unit_test in UnitTests:
172
+ run_unit_test(unit_test=unit_test)
173
+ else:
174
+ run_unit_test(unit_test=unit_test)
@@ -97,17 +97,6 @@ def run_unit_test(unit_test: UnitTests):
97
97
  logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
98
98
  params=btc_calibrated_params)
99
99
 
100
- elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS:
101
- btc_option_chain = sv.get_btc_test_chain_data()
102
- params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
103
- btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
104
- params0=params0,
105
- model_calibration_type=LogsvModelCalibrationType.PARAMS4,
106
- constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
107
- print(btc_calibrated_params)
108
- logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
109
- params=btc_calibrated_params)
110
-
111
100
  elif unit_test == UnitTests.MC_WITH_FIXED_RANDOMS:
112
101
  btc_option_chain = sv.get_btc_test_chain_data()
113
102
  W0s, W1s, dts = sv.get_randoms_for_chain_valuation(ttms=btc_option_chain.ttms,
@@ -138,6 +127,17 @@ def run_unit_test(unit_test: UnitTests):
138
127
  option_prices_ttm, option_std_ttm = sv.logsv_mc_chain_pricer_fixed_randoms(**args)
139
128
  print(option_prices_ttm)
140
129
 
130
+ elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS:
131
+ btc_option_chain = sv.get_btc_test_chain_data()
132
+ params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
133
+ btc_calibrated_params = logsv_pricer.calibrate_model_params_to_chain(option_chain=btc_option_chain,
134
+ params0=params0,
135
+ model_calibration_type=LogsvModelCalibrationType.PARAMS4,
136
+ constraints_type=sv.ConstraintsType.INVERSE_MARTINGALE)
137
+ print(btc_calibrated_params)
138
+ logsv_pricer.plot_model_ivols_vs_bid_ask(option_chain=btc_option_chain,
139
+ params=btc_calibrated_params)
140
+
141
141
  elif unit_test == UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC:
142
142
  btc_option_chain = sv.get_btc_test_chain_data()
143
143
  params0 = LogSvParams(sigma0=0.8, theta=1.0, kappa1=2.21, kappa2=2.18, beta=0.15, volvol=2.0)
@@ -157,7 +157,7 @@ def run_unit_test(unit_test: UnitTests):
157
157
 
158
158
  if __name__ == '__main__':
159
159
 
160
- unit_test = UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS_WITH_MC
160
+ unit_test = UnitTests.CALIBRATE_MODEL_TO_BTC_OPTIONS
161
161
 
162
162
  is_run_all_tests = False
163
163
  if is_run_all_tests:
@@ -204,8 +204,8 @@ class LogSVPricer(ModelPricer):
204
204
  volvol=params.volvol,
205
205
  vol_backbone_etas=params.get_vol_backbone_etas(ttms=option_chain.ttms))
206
206
  model_vols = option_chain.compute_model_ivols_from_chain_data(model_prices=option_prices_ttm)
207
- print(f"option_prices_ttm\n{option_prices_ttm}")
208
- print(f"model_vols\n{model_vols}")
207
+ # print(f"option_prices_ttm\n{option_prices_ttm}")
208
+ # print(f"model_vols\n{model_vols}")
209
209
 
210
210
  else:
211
211
  raise NotImplementedError(f"{calibration_engine}")