voly 0.0.9__tar.gz → 0.0.10__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.9
3
+ Version: 0.0.10
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -27,7 +27,6 @@ Requires-Dist: websockets>=10.0
27
27
  Requires-Dist: requests>=2.26.0
28
28
  Requires-Dist: loguru>=0.5.3
29
29
  Provides-Extra: dev
30
- Requires-Dist: pytest>=7.0.0; extra == "dev"
31
30
  Requires-Dist: black>=22.1.0; extra == "dev"
32
31
  Requires-Dist: isort>=5.10.1; extra == "dev"
33
32
  Requires-Dist: mypy>=0.931; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "voly"
7
- version = "0.0.9"
7
+ version = "0.0.10"
8
8
  description = "Options & volatility research package"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -38,7 +38,6 @@ dependencies = [
38
38
 
39
39
  [project.optional-dependencies]
40
40
  dev = [
41
- "pytest>=7.0.0",
42
41
  "black>=22.1.0",
43
42
  "isort>=5.10.1",
44
43
  "mypy>=0.931",
@@ -61,7 +60,7 @@ line_length = 100
61
60
  multi_line_output = 3
62
61
 
63
62
  [tool.mypy]
64
- python_version = "0.0.9"
63
+ python_version = "0.0.10"
65
64
  warn_return_any = true
66
65
  warn_unused_configs = true
67
66
  disallow_untyped_defs = true
@@ -17,7 +17,6 @@ if __name__ == "__main__":
17
17
  ],
18
18
  extras_require={
19
19
  'dev': [
20
- "pytest>=7.0.0",
21
20
  "black>=22.1.0",
22
21
  "isort>=5.10.1",
23
22
  "mypy>=0.931",
@@ -26,4 +25,4 @@ if __name__ == "__main__":
26
25
  "build>=0.8.0",
27
26
  ]
28
27
  }
29
- )
28
+ )
@@ -294,7 +294,7 @@ class VolyClient:
294
294
 
295
295
  @staticmethod
296
296
  def fit_model(market_data: pd.DataFrame,
297
- model_type: str = 'svi',
297
+ model_name: str = 'svi',
298
298
  moneyness_range: Tuple[float, float] = (-2, 2),
299
299
  num_points: int = 500,
300
300
  plot: bool = False) -> Dict[str, Any]:
@@ -303,7 +303,7 @@ class VolyClient:
303
303
 
304
304
  Parameters:
305
305
  - market_data: DataFrame with market data
306
- - model_type: Type of model to fit (default: 'svi')
306
+ - model_name: Name of model to fit (default: 'svi')
307
307
  - moneyness_range: (min, max) range for moneyness grid
308
308
  - num_points: Number of points for moneyness grid
309
309
  - plot: Whether to generate and return plots
@@ -311,12 +311,12 @@ class VolyClient:
311
311
  Returns:
312
312
  - Dictionary with fitting results and optional plots
313
313
  """
314
- logger.info(f"Fitting {model_type.upper()} model to market data")
314
+ logger.info(f"Fitting {model_name.upper()} model to market data")
315
315
 
316
316
  # Fit the model
317
317
  fit_results = fit_model(
318
318
  market_data=market_data,
319
- model_type=model_type,
319
+ model_name=model_name,
320
320
  moneyness_range=moneyness_range,
321
321
  num_points=num_points
322
322
  )
@@ -45,8 +45,6 @@ async def unsubscribe_channels(ws, channels):
45
45
  @catch_exception
46
46
  async def process_batch(ws, batch: List[str], batch_num: int, total_batches: int) -> List[Dict[str, Any]]:
47
47
  """Process a batch of instruments and return their data"""
48
- batch_start = time.time()
49
-
50
48
  # Create channel subscriptions
51
49
  ticker_channels = [f"ticker.{instr}.100ms" for instr in batch]
52
50
  book_channels = [f"book.{instr}.100ms" for instr in batch]
@@ -123,10 +121,6 @@ async def process_batch(ws, batch: List[str], batch_num: int, total_batches: int
123
121
 
124
122
  batch_results.append(row)
125
123
 
126
- batch_time = time.time() - batch_start
127
- logger.info(
128
- f"Batch {batch_num}/{total_batches} completed in {batch_time:.2f}s - {len(batch_results)} instruments processed")
129
-
130
124
  return batch_results
131
125
 
132
126
 
@@ -227,7 +221,6 @@ def process_option_chain(df: pd.DataFrame, currency: str, min_dte: float = 2.0)
227
221
  return None
228
222
 
229
223
  # Apply extraction to create new columns
230
- logger.info(f"Extracting option details from instrument names...")
231
224
  df['details'] = df['instrument_name'].apply(lambda x: extract_details(x))
232
225
  df['strike'] = df['details'].apply(lambda x: x['strike'] if x else None)
233
226
  df['option_type'] = df['details'].apply(lambda x: x['option_type'] if x else None)
@@ -240,7 +233,6 @@ def process_option_chain(df: pd.DataFrame, currency: str, min_dte: float = 2.0)
240
233
 
241
234
  # Get reference time from timestamp
242
235
  reference_time = datetime.datetime.fromtimestamp(df['timestamp'].iloc[0] / 1000)
243
- logger.info(f"Reference time: {reference_time}")
244
236
 
245
237
  # Calculate days to expiry (DTE)
246
238
  df['dte'] = (df['expiry_date'] - reference_time).dt.total_seconds() / (24 * 60 * 60)
@@ -261,23 +253,6 @@ def process_option_chain(df: pd.DataFrame, currency: str, min_dte: float = 2.0)
261
253
  df = df.dropna(subset=['mark_iv', 'log_moneyness', 'yte'])
262
254
  logger.info(f"Removed {original_rows - len(df)} rows with missing data")
263
255
 
264
- # Filter options with DTE > min_dte
265
- if min_dte > 0:
266
- original_count = len(df)
267
- df = df[df['dte'] > min_dte]
268
- logger.info(f"Filtered out {original_count - len(df)} options with DTE < {min_dte}")
269
-
270
- # Group by time to expiry and ensure we have enough data points for each expiry
271
- expiry_counts = df.groupby('yte').size()
272
- valid_expiries = expiry_counts[expiry_counts >= 5].index
273
- df = df[df['yte'].isin(valid_expiries)]
274
- logger.info(f"Filtered to {len(df)} options with at least 5 strikes per expiry")
275
-
276
- # Report on the maturities we're working with
277
- maturities = df.groupby(['maturity_name', 'yte']).size().reset_index()
278
- for _, row in maturities.iterrows():
279
- logger.info(f"Maturity: {row['maturity_name']}, YTE: {row['yte']:.4f}, Strikes: {row[0]}")
280
-
281
256
  return df
282
257
 
283
258
 
@@ -66,7 +66,3 @@ def catch_exception(func):
66
66
  return async_wrapper
67
67
  else:
68
68
  return sync_wrapper
69
-
70
-
71
- # Initialize logger with basic info
72
- logger.info("Voly logger initialized")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voly
3
- Version: 0.0.9
3
+ Version: 0.0.10
4
4
  Summary: Options & volatility research package
5
5
  Author-email: Manu de Cara <manu.de.cara@gmail.com>
6
6
  License: MIT
@@ -27,7 +27,6 @@ Requires-Dist: websockets>=10.0
27
27
  Requires-Dist: requests>=2.26.0
28
28
  Requires-Dist: loguru>=0.5.3
29
29
  Provides-Extra: dev
30
- Requires-Dist: pytest>=7.0.0; extra == "dev"
31
30
  Requires-Dist: black>=22.1.0; extra == "dev"
32
31
  Requires-Dist: isort>=5.10.1; extra == "dev"
33
32
  Requires-Dist: mypy>=0.931; extra == "dev"
@@ -8,7 +8,6 @@ requests>=2.26.0
8
8
  loguru>=0.5.3
9
9
 
10
10
  [dev]
11
- pytest>=7.0.0
12
11
  black>=22.1.0
13
12
  isort>=5.10.1
14
13
  mypy>=0.931
@@ -0,0 +1,244 @@
1
+ """
2
+ Comprehensive tests for the VolyClient class with detailed output.
3
+ This file demonstrates expected values and provides informative output.
4
+ """
5
+
6
+ import unittest
7
+ import numpy as np
8
+ from voly import VolyClient
9
+ import sys
10
+
11
+
12
+ class VolyClientTestCase(unittest.TestCase):
13
+ """
14
+ Test cases for the VolyClient class with expected values and detailed output.
15
+ """
16
+
17
+ def setUp(self):
18
+ """Set up test fixtures."""
19
+ self.voly = VolyClient()
20
+ # Add a divider line before each test for clearer output
21
+ print("\n" + "=" * 80)
22
+
23
+ def test_bs_pricing_with_expected_values(self):
24
+ """Test Black-Scholes pricing with expected values."""
25
+ print("\nTEST: Black-Scholes Pricing")
26
+
27
+ # Test parameters
28
+ s = 100.0
29
+ k = 100.0
30
+ r = 0.05
31
+ vol = 0.2
32
+ t = 1.0
33
+
34
+ # Expected values (pre-calculated)
35
+ expected_call = 10.45
36
+ expected_put = 5.57
37
+
38
+ # Calculate actual values
39
+ actual_call = self.voly.bs(s=s, k=k, r=r, vol=vol, t=t, option_type='call')
40
+ actual_put = self.voly.bs(s=s, k=k, r=r, vol=vol, t=t, option_type='put')
41
+
42
+ # Print actual vs expected
43
+ print(f"Parameters: S={s}, K={k}, r={r}, vol={vol}, t={t}")
44
+ print(
45
+ f"Call Price: Actual={actual_call:.4f}, Expected={expected_call:.4f}, Diff={abs(actual_call - expected_call):.6f}")
46
+ print(
47
+ f"Put Price: Actual={actual_put:.4f}, Expected={expected_put:.4f}, Diff={abs(actual_put - expected_put):.6f}")
48
+
49
+ # Check put-call parity
50
+ pcp_diff = actual_call - actual_put - s + k * np.exp(-r * t)
51
+ print(f"Put-Call Parity Check: {pcp_diff:.8f} (should be close to 0)")
52
+
53
+ # Assertions with tolerance
54
+ self.assertAlmostEqual(actual_call, expected_call, delta=0.01,
55
+ msg=f"Call price {actual_call:.4f} doesn't match expected {expected_call:.4f}")
56
+ self.assertAlmostEqual(actual_put, expected_put, delta=0.01,
57
+ msg=f"Put price {actual_put:.4f} doesn't match expected {expected_put:.4f}")
58
+ self.assertAlmostEqual(pcp_diff, 0, delta=1e-10,
59
+ msg="Put-call parity violated")
60
+
61
+ def test_delta_values_across_moneyness(self):
62
+ """Test delta values across different moneyness levels."""
63
+ print("\nTEST: Delta Values Across Moneyness")
64
+
65
+ # Test parameters
66
+ s = 100.0
67
+ r = 0.05
68
+ vol = 0.2
69
+ t = 1.0
70
+
71
+ # Define test cases: strike, expected call delta, expected put delta
72
+ test_cases = [
73
+ (50.0, 0.9999, -0.0001), # Deep ITM call / Deep OTM put
74
+ (75.0, 0.9631, -0.0369), # ITM call / OTM put
75
+ (100.0, 0.6368, -0.3632), # ATM
76
+ (125.0, 0.2219, -0.7781), # OTM call / ITM put
77
+ (150.0, 0.0467, -0.9533) # Deep OTM call / Deep ITM put
78
+ ]
79
+
80
+ print(f"Parameters: S={s}, r={r}, vol={vol}, t={t}")
81
+ print("\nDelta Values:")
82
+ print(f"{'Strike':<10} {'Call Delta':<15} {'Expected':<15} {'Put Delta':<15} {'Expected':<15}")
83
+ print("-" * 70)
84
+
85
+ for strike, exp_call_delta, exp_put_delta in test_cases:
86
+ call_delta = self.voly.delta(s=s, k=strike, r=r, vol=vol, t=t, option_type='call')
87
+ put_delta = self.voly.delta(s=s, k=strike, r=r, vol=vol, t=t, option_type='put')
88
+
89
+ print(f"{strike:<10.1f} {call_delta:<15.4f} {exp_call_delta:<15.4f} "
90
+ f"{put_delta:<15.4f} {exp_put_delta:<15.4f}")
91
+
92
+ # Check deltas are within expected range
93
+ self.assertAlmostEqual(call_delta, exp_call_delta, delta=0.01,
94
+ msg=f"Call delta for K={strike} incorrect")
95
+ self.assertAlmostEqual(put_delta, exp_put_delta, delta=0.01,
96
+ msg=f"Put delta for K={strike} incorrect")
97
+
98
+ # Check put-call delta relationship: call_delta - put_delta = 1
99
+ self.assertAlmostEqual(call_delta - put_delta, 1.0, delta=1e-10,
100
+ msg="Delta relationship violated")
101
+
102
+ def test_all_greeks_values(self):
103
+ """Test all Greeks calculation with expected values."""
104
+ print("\nTEST: All Greeks Values")
105
+
106
+ # Test parameters
107
+ s = 100.0
108
+ k = 100.0
109
+ r = 0.05
110
+ vol = 0.2
111
+ t = 1.0
112
+
113
+ # Calculate all Greeks for a call
114
+ call_greeks = self.voly.greeks(s=s, k=k, r=r, vol=vol, t=t, option_type='call')
115
+
116
+ # Expected values (calculated using standard Black-Scholes formulas)
117
+ expected_greeks = {
118
+ 'price': 10.45,
119
+ 'delta': 0.637,
120
+ 'gamma': 0.019,
121
+ 'vega': 0.375,
122
+ 'theta': -0.018,
123
+ 'rho': 0.52,
124
+ }
125
+
126
+ print(f"Parameters: S={s}, K={k}, r={r}, vol={vol}, t={t}")
127
+ print("\nCall Option Greeks:")
128
+ print(f"{'Greek':<10} {'Actual':<15} {'Expected':<15} {'Diff':<15}")
129
+ print("-" * 55)
130
+
131
+ for greek, expected in expected_greeks.items():
132
+ actual = call_greeks[greek]
133
+ print(f"{greek.capitalize():<10} {actual:<15.6f} {expected:<15.6f} {abs(actual - expected):<15.6f}")
134
+
135
+ # Assert with appropriate tolerance
136
+ self.assertAlmostEqual(actual, expected, delta=max(0.01, expected * 0.05),
137
+ msg=f"{greek.capitalize()} value incorrect")
138
+
139
+ # Additional checks for other Greeks
140
+ print("\nAdditional Greeks:")
141
+ for greek in ['vanna', 'volga', 'charm']:
142
+ if greek in call_greeks:
143
+ print(f"{greek.capitalize():<10} {call_greeks[greek]:<15.6f}")
144
+
145
+ # Check basic relationships
146
+ # Gamma should be positive for both calls and puts
147
+ self.assertGreater(call_greeks['gamma'], 0, "Gamma should be positive")
148
+
149
+ # Vega should be positive for both calls and puts
150
+ self.assertGreater(call_greeks['vega'], 0, "Vega should be positive")
151
+
152
+ # Theta is typically negative for calls and puts (time decay)
153
+ self.assertLess(call_greeks['theta'], 0, "Theta should be negative for calls")
154
+
155
+ def test_implied_volatility_calculation(self):
156
+ """Test implied volatility calculation with known prices."""
157
+ print("\nTEST: Implied Volatility Calculation")
158
+
159
+ # Define test cases
160
+ test_cases = [
161
+ # S, K, r, vol, t
162
+ (100.0, 100.0, 0.05, 0.2, 1.0), # ATM
163
+ (100.0, 90.0, 0.05, 0.25, 0.5), # ITM
164
+ (100.0, 110.0, 0.05, 0.3, 0.25) # OTM
165
+ ]
166
+
167
+ print(f"{'S':<8} {'K':<8} {'r':<8} {'t':<8} {'Input Vol':<12} {'Option Price':<15} "
168
+ f"{'Implied Vol':<15} {'Diff':<10}")
169
+ print("-" * 90)
170
+
171
+ for s, k, r, vol, t in test_cases:
172
+ # Calculate option price with known volatility
173
+ call_price = self.voly.bs(s=s, k=k, r=r, vol=vol, t=t, option_type='call')
174
+
175
+ # Calculate implied volatility from the price
176
+ try:
177
+ implied_vol = self.voly.iv(option_price=call_price, s=s, k=k, r=r, t=t, option_type='call')
178
+ vol_diff = abs(vol - implied_vol)
179
+ print(f"{s:<8.1f} {k:<8.1f} {r:<8.3f} {t:<8.2f} {vol:<12.4f} {call_price:<15.6f} "
180
+ f"{implied_vol:<15.6f} {vol_diff:<10.6f}")
181
+
182
+ # Assert implied vol matches input vol
183
+ self.assertAlmostEqual(vol, implied_vol, delta=0.0001,
184
+ msg=f"Implied volatility {implied_vol:.6f} doesn't match input {vol:.6f}")
185
+ except Exception as e:
186
+ print(f"{s:<8.1f} {k:<8.1f} {r:<8.3f} {t:<8.2f} {vol:<12.4f} {call_price:<15.6f} "
187
+ f"ERROR: {str(e)}")
188
+ self.fail(f"Implied volatility calculation failed: {str(e)}")
189
+
190
+ def test_bs_pricing_extreme_cases(self):
191
+ """Test Black-Scholes pricing under extreme conditions."""
192
+ print("\nTEST: Black-Scholes Pricing - Extreme Cases")
193
+
194
+ # Test zero volatility
195
+ zero_vol_call = self.voly.bs(s=100, k=90, r=0.05, vol=0, t=1, option_type='call')
196
+ zero_vol_put = self.voly.bs(s=100, k=110, r=0.05, vol=0, t=1, option_type='put')
197
+
198
+ print("Zero Volatility:")
199
+ print(f"Call (S=100, K=90): {zero_vol_call:.4f} (should equal intrinsic value 10)")
200
+ print(f"Put (S=100, K=110): {zero_vol_put:.4f} (should equal intrinsic value 10)")
201
+
202
+ self.assertAlmostEqual(zero_vol_call, 10.0, delta=0.01,
203
+ msg="Zero vol ITM call should equal intrinsic value")
204
+ self.assertAlmostEqual(zero_vol_put, 10.0, delta=0.01,
205
+ msg="Zero vol ITM put should equal intrinsic value")
206
+
207
+ # Test zero time to expiry
208
+ zero_time_call = self.voly.bs(s=100, k=90, r=0.05, vol=0.2, t=0, option_type='call')
209
+ zero_time_put = self.voly.bs(s=100, k=110, r=0.05, vol=0.2, t=0, option_type='put')
210
+
211
+ print("\nZero Time to Expiry:")
212
+ print(f"Call (S=100, K=90): {zero_time_call:.4f} (should equal intrinsic value 10)")
213
+ print(f"Put (S=100, K=110): {zero_time_put:.4f} (should equal intrinsic value 10)")
214
+
215
+ self.assertAlmostEqual(zero_time_call, 10.0, delta=0.01,
216
+ msg="Zero time ITM call should equal intrinsic value")
217
+ self.assertAlmostEqual(zero_time_put, 10.0, delta=0.01,
218
+ msg="Zero time ITM put should equal intrinsic value")
219
+
220
+ # Test deep ITM and OTM
221
+ deep_itm_call = self.voly.bs(s=100, k=50, r=0.05, vol=0.2, t=1, option_type='call')
222
+ deep_otm_call = self.voly.bs(s=100, k=200, r=0.05, vol=0.2, t=1, option_type='call')
223
+
224
+ print("\nDeep ITM/OTM:")
225
+ print(f"Deep ITM Call (S=100, K=50): {deep_itm_call:.4f}")
226
+ print(f"Deep OTM Call (S=100, K=200): {deep_otm_call:.4f}")
227
+
228
+ self.assertGreater(deep_itm_call, 50.0,
229
+ msg="Deep ITM call should be greater than intrinsic value")
230
+ self.assertGreater(deep_otm_call, 0.0,
231
+ msg="Deep OTM call should have positive value")
232
+ self.assertLess(deep_otm_call, 5.0,
233
+ msg="Deep OTM call should have small value")
234
+
235
+
236
+ if __name__ == '__main__':
237
+ # More detailed output
238
+ print("\nVOLY CLIENT DETAILED TEST SUITE")
239
+ print("=" * 80)
240
+ print(f"Testing against expected values for options pricing and Greeks")
241
+ print("-" * 80)
242
+
243
+ # Run tests with more verbose output
244
+ unittest.main(verbosity=2)
@@ -1,91 +0,0 @@
1
- """
2
- Tests for the VolyClient class using Python's built-in unittest framework.
3
- """
4
-
5
- import unittest
6
- import numpy as np
7
- from voly import VolyClient
8
-
9
-
10
- class TestVolyClient(unittest.TestCase):
11
- """Test cases for the VolyClient class."""
12
-
13
- def setUp(self):
14
- """Set up test fixtures."""
15
- self.voly = VolyClient()
16
-
17
- def test_voly_initialization(self):
18
- """Test that the voly initializes correctly."""
19
- self.assertIsNotNone(self.voly)
20
-
21
- def test_bs_pricing(self):
22
- """Test Black-Scholes pricing function."""
23
- # Test call price
24
- call_price = self.voly.bs(s=100, k=100, r=0.05, vol=0.2, t=1, option_type='call')
25
- self.assertGreater(call_price, 0)
26
- self.assertLess(call_price, 20) # Sanity check
27
-
28
- # Test put price
29
- put_price = self.voly.bs(s=100, k=100, r=0.05, vol=0.2, t=1, option_type='put')
30
- self.assertGreater(put_price, 0)
31
- self.assertLess(put_price, 20) # Sanity check
32
-
33
- # Test put-call parity (approximately)
34
- parity_diff = abs(call_price - put_price - 100 + 100 * np.exp(-0.05))
35
- self.assertLess(parity_diff, 1e-10)
36
-
37
- def test_greeks_calculation(self):
38
- """Test that all Greeks are calculated correctly."""
39
- # Calculate all Greeks
40
- greeks = self.voly.greeks(s=100, k=100, r=0.05, vol=0.2, t=1, option_type='call')
41
-
42
- # Check that all expected Greeks are present
43
- expected_keys = ['price', 'delta', 'gamma', 'vega', 'theta', 'rho', 'vanna', 'volga', 'charm']
44
- for key in expected_keys:
45
- self.assertIn(key, greeks)
46
- self.assertIsInstance(greeks[key], float)
47
-
48
- # Basic sanity checks
49
- self.assertGreater(greeks['delta'], 0) # Call delta positive
50
- self.assertLess(greeks['delta'], 1) # Call delta less than 1
51
- self.assertGreater(greeks['gamma'], 0) # Gamma always positive
52
- self.assertGreater(greeks['vega'], 0) # Vega always positive
53
- self.assertLess(greeks['theta'], 0) # Call theta typically negative
54
-
55
- def test_delta_values(self):
56
- """Test delta values for different moneyness levels."""
57
- # Deep ITM call should have delta close to 1
58
- itm_delta = self.voly.delta(s=100, k=50, r=0.05, vol=0.2, t=1, option_type='call')
59
- self.assertGreater(itm_delta, 0.95)
60
-
61
- # Deep OTM call should have delta close to 0
62
- otm_delta = self.voly.delta(s=100, k=200, r=0.05, vol=0.2, t=1, option_type='call')
63
- self.assertLess(otm_delta, 0.05)
64
-
65
- # Deep ITM put should have delta close to -1
66
- itm_put_delta = self.voly.delta(s=100, k=200, r=0.05, vol=0.2, t=1, option_type='put')
67
- self.assertLess(itm_put_delta, -0.95)
68
-
69
- # Deep OTM put should have delta close to 0
70
- otm_put_delta = self.voly.delta(s=100, k=50, r=0.05, vol=0.2, t=1, option_type='put')
71
- self.assertGreater(otm_put_delta, -0.05)
72
-
73
- def test_implied_volatility(self):
74
- """Test implied volatility calculation."""
75
- # Calculate option price with known volatility
76
- vol = 0.2
77
- s = 100
78
- k = 100
79
- r = 0.05
80
- t = 1
81
- option_price = self.voly.bs(s=s, k=k, r=r, vol=vol, t=t, option_type='call')
82
-
83
- # Calculate implied volatility from the price
84
- implied_vol = self.voly.iv(option_price=option_price, s=s, k=k, r=r, t=t, option_type='call')
85
-
86
- # Verify that the implied volatility is close to the original volatility
87
- self.assertAlmostEqual(vol, implied_vol, places=4)
88
-
89
-
90
- if __name__ == '__main__':
91
- unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes