morphiq-sdk 0.1.0__tar.gz → 0.2.0__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 (46) hide show
  1. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/PKG-INFO +25 -15
  2. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/README.md +24 -14
  3. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/analytics.py +3 -1
  4. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/analytics_async.py +3 -1
  5. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/batch_price.py +1 -1
  6. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/batch_price_async.py +1 -1
  7. morphiq_sdk-0.2.0/examples/cost_quote.py +77 -0
  8. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/greeks.py +3 -1
  9. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/greeks_async.py +3 -1
  10. morphiq_sdk-0.2.0/examples/greeks_standard.py +76 -0
  11. morphiq_sdk-0.2.0/examples/pricing_models.py +17 -0
  12. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/README.md +3 -0
  13. morphiq_sdk-0.2.0/notebooks/cost/quotation.ipynb +167 -0
  14. morphiq_sdk-0.2.0/notebooks/pricing/models.ipynb +72 -0
  15. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/pricing/options.ipynb +25 -63
  16. morphiq_sdk-0.2.0/notebooks/pricing/standard-five.ipynb +120 -0
  17. morphiq_sdk-0.2.0/notebooks/start-here.ipynb +23 -0
  18. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/pyproject.toml +1 -1
  19. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/__init__.py +34 -0
  20. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/client.py +300 -55
  21. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/models.py +186 -25
  22. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/pandas.py +3 -1
  23. morphiq_sdk-0.1.0/notebooks/start-here.ipynb +0 -23
  24. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/.gitignore +0 -0
  25. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/LICENSE +0 -0
  26. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/README.md +0 -0
  27. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/__init__.py +0 -0
  28. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/chunked_price.py +0 -0
  29. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/chunked_price_async.py +0 -0
  30. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/identify_regime.py +0 -0
  31. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/identify_regime_async.py +0 -0
  32. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/implied_volatility.py +0 -0
  33. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/implied_volatility_async.py +0 -0
  34. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_american.py +0 -0
  35. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_async.py +0 -0
  36. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_european.py +0 -0
  37. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/provider_regime.py +0 -0
  38. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/quickstart.py +0 -0
  39. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/tabular_price.py +0 -0
  40. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/regime/identification.ipynb +0 -0
  41. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/errors.py +0 -0
  42. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/market_data.py +0 -0
  43. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/py.typed +0 -0
  44. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/workflows.py +0 -0
  45. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk_yahoo/__init__.py +0 -0
  46. {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk_yahoo/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: morphiq-sdk
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Option pricing, Greeks, implied volatility and market regime identification through a Python API client
5
5
  Project-URL: Homepage, https://developers.morphiqlabs.com
6
6
  Project-URL: Documentation, https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/README.md
@@ -36,7 +36,7 @@ Description-Content-Type: text/markdown
36
36
 
37
37
  # Option pricing, Greeks, volatility and market regimes in Python
38
38
 
39
- Price European and American options, calculate ten Greeks, solve implied
39
+ Price European and American options, calculate standard-five or all-ten Greeks, solve implied
40
40
  volatility, and identify market regimes from price histories.
41
41
 
42
42
  `morphiq-sdk` is a typed Python client for the MorphIQ API, with synchronous and
@@ -47,6 +47,12 @@ valuation, sensitivity analysis and market-regime identification.
47
47
  See the [documentation index](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/README.md) for all feature guides and the
48
48
  [Python API reference](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/API.md) for methods, models and errors.
49
49
 
50
+ This client targets API **v0.5.0**, including public model discovery,
51
+ standard-five Greeks, cost utilities and spend caps. SDK 0.1.x lacks these
52
+ additions; use the release containing this contract update or install this checkout.
53
+ See [model capabilities and migration](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/MODELS.md): BSM/BS1993 support batches
54
+ up to 100; QdFp and cash-dividend finite difference support single price and IV.
55
+
50
56
  ## Quickstart
51
57
 
52
58
  ### 1. Get API access
@@ -106,26 +112,26 @@ and prints the JSON response. Example response from the recorded synthetic fixtu
106
112
 
107
113
  ```json
108
114
  {
109
- "result": {
110
- "price": 10.450583572185538,
111
- "exercise_style": "european",
112
- "option_type": "call",
113
- "pricing_model": "black_scholes_merton",
114
- "model_version": "black_scholes_merton/1"
115
- },
116
115
  "metadata": {
117
- "request_id": "fixture-european",
116
+ "request_id": "fr-18e4e90129262501-0000000000000003",
118
117
  "api_version": "v1",
119
- "engine_version": "0.39.1",
118
+ "engine_version": "0.43.5",
120
119
  "engines": [
121
120
  {
122
121
  "name": "morphiq-risk",
123
- "version": "0.39.1",
124
- "revision": "b4c29d492ff96dddf9b25f745d2be4d253202164"
122
+ "version": "0.43.5",
123
+ "revision": "43ca7f93b9edda6078b49950de6a8b7d51a3e3ef"
125
124
  }
126
125
  ],
127
126
  "units_charged": 1,
128
127
  "compute_schedule_version": "2026-09-11"
128
+ },
129
+ "result": {
130
+ "price": 10.45058357218557,
131
+ "exercise_style": "european",
132
+ "option_type": "call",
133
+ "pricing_model": "black_scholes_merton",
134
+ "model_version": "black_scholes_merton/1"
129
135
  }
130
136
  }
131
137
  ```
@@ -218,8 +224,10 @@ no real credentials; native API integration is opt-in. See
218
224
  [CONTRIBUTING.md](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/CONTRIBUTING.md) and [RELEASE.md](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/RELEASE.md).
219
225
 
220
226
  Clients make no automatic retries and do not follow redirects. A timeout
221
- may leave work charged. Missing `fcu_charged` means unreported, not zero; internal
222
- `units_charged` is not a commercial CU charge.
227
+ may leave work charged. Only `fcu_quoted` and `fcu_charged` report commercial CUs.
228
+ Missing `fcu_charged` means unreported, not zero. The retired `units_charged` and
229
+ `compute_schedule_version` fields are optional; they remain accepted from older
230
+ deployments and are never used to calculate a charge.
223
231
 
224
232
  The SDK preserves server results, numerical diagnostics, request IDs, engine
225
233
  provenance and commercial Compute Units (CU). Numerical engines, model selection,
@@ -243,3 +251,5 @@ docs/ Usage guides and contract documentation
243
251
 
244
252
  The SDK, examples and notebooks are licensed under the
245
253
  [MIT License](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/LICENSE).
254
+
255
+ See [cost schedules and request quotes](docs/COST.md) for zero-CU discovery, conditional quotes, and the optional `max_cu` execution ceiling.
@@ -1,6 +1,6 @@
1
1
  # Option pricing, Greeks, volatility and market regimes in Python
2
2
 
3
- Price European and American options, calculate ten Greeks, solve implied
3
+ Price European and American options, calculate standard-five or all-ten Greeks, solve implied
4
4
  volatility, and identify market regimes from price histories.
5
5
 
6
6
  `morphiq-sdk` is a typed Python client for the MorphIQ API, with synchronous and
@@ -11,6 +11,12 @@ valuation, sensitivity analysis and market-regime identification.
11
11
  See the [documentation index](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/README.md) for all feature guides and the
12
12
  [Python API reference](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/API.md) for methods, models and errors.
13
13
 
14
+ This client targets API **v0.5.0**, including public model discovery,
15
+ standard-five Greeks, cost utilities and spend caps. SDK 0.1.x lacks these
16
+ additions; use the release containing this contract update or install this checkout.
17
+ See [model capabilities and migration](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/MODELS.md): BSM/BS1993 support batches
18
+ up to 100; QdFp and cash-dividend finite difference support single price and IV.
19
+
14
20
  ## Quickstart
15
21
 
16
22
  ### 1. Get API access
@@ -70,26 +76,26 @@ and prints the JSON response. Example response from the recorded synthetic fixtu
70
76
 
71
77
  ```json
72
78
  {
73
- "result": {
74
- "price": 10.450583572185538,
75
- "exercise_style": "european",
76
- "option_type": "call",
77
- "pricing_model": "black_scholes_merton",
78
- "model_version": "black_scholes_merton/1"
79
- },
80
79
  "metadata": {
81
- "request_id": "fixture-european",
80
+ "request_id": "fr-18e4e90129262501-0000000000000003",
82
81
  "api_version": "v1",
83
- "engine_version": "0.39.1",
82
+ "engine_version": "0.43.5",
84
83
  "engines": [
85
84
  {
86
85
  "name": "morphiq-risk",
87
- "version": "0.39.1",
88
- "revision": "b4c29d492ff96dddf9b25f745d2be4d253202164"
86
+ "version": "0.43.5",
87
+ "revision": "43ca7f93b9edda6078b49950de6a8b7d51a3e3ef"
89
88
  }
90
89
  ],
91
90
  "units_charged": 1,
92
91
  "compute_schedule_version": "2026-09-11"
92
+ },
93
+ "result": {
94
+ "price": 10.45058357218557,
95
+ "exercise_style": "european",
96
+ "option_type": "call",
97
+ "pricing_model": "black_scholes_merton",
98
+ "model_version": "black_scholes_merton/1"
93
99
  }
94
100
  }
95
101
  ```
@@ -182,8 +188,10 @@ no real credentials; native API integration is opt-in. See
182
188
  [CONTRIBUTING.md](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/CONTRIBUTING.md) and [RELEASE.md](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/RELEASE.md).
183
189
 
184
190
  Clients make no automatic retries and do not follow redirects. A timeout
185
- may leave work charged. Missing `fcu_charged` means unreported, not zero; internal
186
- `units_charged` is not a commercial CU charge.
191
+ may leave work charged. Only `fcu_quoted` and `fcu_charged` report commercial CUs.
192
+ Missing `fcu_charged` means unreported, not zero. The retired `units_charged` and
193
+ `compute_schedule_version` fields are optional; they remain accepted from older
194
+ deployments and are never used to calculate a charge.
187
195
 
188
196
  The SDK preserves server results, numerical diagnostics, request IDs, engine
189
197
  provenance and commercial Compute Units (CU). Numerical engines, model selection,
@@ -207,3 +215,5 @@ docs/ Usage guides and contract documentation
207
215
 
208
216
  The SDK, examples and notebooks are licensed under the
209
217
  [MIT License](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/LICENSE).
218
+
219
+ See [cost schedules and request quotes](docs/COST.md) for zero-CU discovery, conditional quotes, and the optional `max_cu` execution ceiling.
@@ -75,7 +75,9 @@ def main() -> None:
75
75
  "veta",
76
76
  "color",
77
77
  ):
78
- print(f" {name}: {getattr(response.result.greeks, name)}")
78
+ value = getattr(response.result.greeks, name)
79
+ reason = response.result.greeks.unavailable.get(name)
80
+ print(f" {name}: {value if value is not None else reason}")
79
81
  # The combined response reports one charge, not one charge per result component.
80
82
  print(f"Model: {response.result.pricing_model} ({response.result.model_version})")
81
83
  print(f"Request ID: {response.metadata.request_id}")
@@ -82,7 +82,9 @@ async def main() -> None:
82
82
  "veta",
83
83
  "color",
84
84
  ):
85
- print(f" {name}: {getattr(response.result.greeks, name)}")
85
+ value = getattr(response.result.greeks, name)
86
+ reason = response.result.greeks.unavailable.get(name)
87
+ print(f" {name}: {value if value is not None else reason}")
86
88
  # The combined response reports one charge, not one charge per result component.
87
89
  print(f"Model: {response.result.pricing_model} ({response.result.model_version})")
88
90
  print(f"Request ID: {response.metadata.request_id}")
@@ -38,7 +38,7 @@ def main() -> None:
38
38
  ),
39
39
  PriceRequest(
40
40
  exercise_style="american",
41
- pricing_model="auto",
41
+ pricing_model="bjerksund_stensland_1993",
42
42
  option_type="put",
43
43
  spot=100.0,
44
44
  strike=105.0,
@@ -39,7 +39,7 @@ async def main() -> None:
39
39
  ),
40
40
  PriceRequest(
41
41
  exercise_style="american",
42
- pricing_model="auto",
42
+ pricing_model="bjerksund_stensland_1993",
43
43
  option_type="put",
44
44
  spot=100.0,
45
45
  strike=105.0,
@@ -0,0 +1,77 @@
1
+ """Two non-billable utility requests; --execute adds one capped pricing request."""
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ from morphiq_sdk import (
7
+ APIError,
8
+ BatchPriceRequest,
9
+ BatchPriceResponse,
10
+ MorphIQ,
11
+ PriceRequest,
12
+ PriceResponse,
13
+ )
14
+
15
+
16
+ def main() -> None:
17
+ parser = argparse.ArgumentParser(description=__doc__)
18
+ parser.add_argument(
19
+ "--execute", action="store_true", help="Execute once with the quoted CU ceiling"
20
+ )
21
+ parser.add_argument("--batch", action="store_true", help="Quote two synthetic contracts")
22
+ parser.add_argument(
23
+ "--max-cu", type=int, help="Explicit execution ceiling; defaults to the quote"
24
+ )
25
+ args = parser.parse_args()
26
+ if args.max_cu is not None and not 0 <= args.max_cu <= 2**64 - 1:
27
+ parser.error("--max-cu must be an unsigned 64-bit integer")
28
+ if args.max_cu is not None and not args.execute:
29
+ parser.error("--max-cu requires --execute")
30
+ request = PriceRequest(
31
+ exercise_style="european",
32
+ option_type="call",
33
+ spot=100.0,
34
+ strike=100.0,
35
+ time_to_expiry_years=1.0,
36
+ risk_free_rate=0.05,
37
+ dividend_yield=0.0,
38
+ volatility=0.2,
39
+ )
40
+ with MorphIQ() as client:
41
+ schedule = client.cost_schedule(operation="price")
42
+ print(schedule.model_dump_json(indent=2))
43
+ batch = BatchPriceRequest(requests=[request, request])
44
+ quote = client.quote(batch if args.batch else request)
45
+ print(quote.model_dump_json(indent=2))
46
+ print("Prospective execution CU:", quote.quote.total_fcu)
47
+ print("Utility CU charged:", quote.metadata.fcu_charged)
48
+ if args.execute:
49
+ ceiling = quote.quote.total_fcu if args.max_cu is None else args.max_cu
50
+ response: PriceResponse | BatchPriceResponse
51
+ try:
52
+ if args.batch:
53
+ response = client.price_batch(batch, max_cu=ceiling)
54
+ else:
55
+ response = client.price(request, max_cu=ceiling)
56
+ except APIError as error:
57
+ print(f"HTTP {error.status_code}: {error.code}", file=sys.stderr)
58
+ print("Request ID:", error.request_id, file=sys.stderr)
59
+ quoted = error.metadata.fcu_quoted if error.metadata else None
60
+ charged = error.metadata.fcu_charged if error.metadata else None
61
+ print(
62
+ "Execution quote CU:",
63
+ quoted if quoted is not None else "not reported",
64
+ file=sys.stderr,
65
+ )
66
+ print(
67
+ "CU charged:",
68
+ charged if charged is not None else "not reported",
69
+ file=sys.stderr,
70
+ )
71
+ print("No retry or automatic ceiling increase.", file=sys.stderr)
72
+ raise SystemExit(1) from None
73
+ print(response.model_dump_json(indent=2))
74
+
75
+
76
+ if __name__ == "__main__":
77
+ main()
@@ -65,7 +65,9 @@ def main() -> None:
65
65
  "veta",
66
66
  "color",
67
67
  ):
68
- print(f" {name}: {getattr(response.result.greeks, name)}")
68
+ value = getattr(response.result.greeks, name)
69
+ reason = response.result.greeks.unavailable.get(name)
70
+ print(f" {name}: {value if value is not None else reason}")
69
71
  print(f"Model: {response.result.pricing_model} ({response.result.model_version})")
70
72
  print(f"Request ID: {response.metadata.request_id}")
71
73
  for engine in response.metadata.engines:
@@ -66,7 +66,9 @@ async def main() -> None:
66
66
  "veta",
67
67
  "color",
68
68
  ):
69
- print(f" {name}: {getattr(response.result.greeks, name)}")
69
+ value = getattr(response.result.greeks, name)
70
+ reason = response.result.greeks.unavailable.get(name)
71
+ print(f" {name}: {value if value is not None else reason}")
70
72
  print(f"Model: {response.result.pricing_model} ({response.result.model_version})")
71
73
  print(f"Request ID: {response.metadata.request_id}")
72
74
  for engine in response.metadata.engines:
@@ -0,0 +1,76 @@
1
+ """Calculate the price and standard five Greeks for a synthetic European call.
2
+
3
+ Run: python examples/greeks_standard.py
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ from getpass import getpass
9
+
10
+ from morphiq_sdk import APIError, GreeksStandardRequest, MorphIQ, ResponseError, TransportError
11
+
12
+
13
+ def main() -> None:
14
+ # Set MORPHIQ_API_KEY in your environment, or enter it at the masked prompt.
15
+ api_key = os.environ.get("MORPHIQ_API_KEY") or getpass("MorphIQ API key: ")
16
+
17
+ # Synthetic European call; spot and strike use the same currency.
18
+ request = GreeksStandardRequest(
19
+ exercise_style="european",
20
+ option_type="call",
21
+ spot=100.0,
22
+ strike=100.0,
23
+ time_to_expiry_years=1.0, # Years from valuation.
24
+ risk_free_rate=0.05, # 5% annual rate.
25
+ dividend_yield=0.0,
26
+ volatility=0.20, # 20% annual volatility.
27
+ pricing_model="auto",
28
+ )
29
+
30
+ try:
31
+ # The client uses https://api.morphiqlabs.com and closes its connection pool here.
32
+ with MorphIQ(api_key=api_key) as client:
33
+ response = client.greeks_standard(request)
34
+ except APIError as error:
35
+ print(f"HTTP {error.status_code}: {error.code}: {error.message}", file=sys.stderr)
36
+ print(f"Request ID: {error.request_id}", file=sys.stderr)
37
+ charged = error.metadata.fcu_charged if error.metadata else None
38
+ print(
39
+ f"Commercial CU charged: {charged if charged is not None else 'not reported'}",
40
+ file=sys.stderr,
41
+ )
42
+ raise SystemExit(1) from None
43
+ except TransportError:
44
+ # Includes timeouts. The service may already have committed a charge.
45
+ raise SystemExit(
46
+ "No complete response; execution and charges may be unknown. No retry."
47
+ ) from None
48
+ except ResponseError as error:
49
+ raise SystemExit(
50
+ f"Invalid response; request_id={error.request_id}. Charges may be unknown. No retry."
51
+ ) from None
52
+
53
+ print(f"Price: {response.result.price}")
54
+ # API time derivatives are per year; vega/rho use unit volatility/rate.
55
+ print("Greeks (raw API units):")
56
+ for name in (
57
+ "delta",
58
+ "gamma",
59
+ "theta",
60
+ "vega",
61
+ "rho",
62
+ ):
63
+ value = getattr(response.result.greeks, name)
64
+ reason = response.result.greeks.unavailable.get(name)
65
+ print(f" {name}: {value if value is not None else reason}")
66
+ print(f"Model: {response.result.pricing_model} ({response.result.model_version})")
67
+ print(f"Request ID: {response.metadata.request_id}")
68
+ for engine in response.metadata.engines:
69
+ print(f"Engine: {engine.name} {engine.version} ({engine.revision})")
70
+ # Missing commercial CU is unreported, not zero.
71
+ charged = response.metadata.fcu_charged
72
+ print("Commercial CU charged:", charged if charged is not None else "not reported")
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
@@ -0,0 +1,17 @@
1
+ """Inspect public model capabilities with one unauthenticated request."""
2
+
3
+ from morphiq_sdk import MorphIQ
4
+
5
+
6
+ def main() -> None:
7
+ with MorphIQ() as client:
8
+ response = client.pricing_models()
9
+ for model in response.models:
10
+ print(model.id, model.operations, model.batch_operations)
11
+ print("Constraints:", [item.model_dump(exclude_unset=True) for item in model.constraints])
12
+ print("Engines:", [item.model_dump() for item in response.engines])
13
+ print("Greek units:", response.greek_units)
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()
@@ -5,6 +5,9 @@ Use the controls to load data and run an analysis; no code edits are required.
5
5
 
6
6
  | Workspace | Tools | API requests per action |
7
7
  | --- | --- | --- |
8
+ | [Available models](pricing/models.ipynb) | Public capabilities, aliases, constraints and engine versions; no key needed | Load: 1 free request |
9
+ | [Standard-five Greeks](pricing/standard-five.ipynb) | BSM/BS1993 inputs, price, five primary sensitivities, unavailable reasons and CU | Calculate: 1 |
10
+ | [Cost quotation](cost/quotation.ipynb) | Account rates, synthetic option inputs, conditional quote, editable execution ceiling | Schedule: 1 utility; quote: 1 utility; execute: 1 capped pricing request |
8
11
  | [Option pricing](pricing/options.ipynb) | European/American and model selectors, call/put, option inputs, automatic Yahoo daily/intraday underlying prices, comparison table, all-ten-Greeks panel, implied-volatility solver, combined premium analysis, session CU ledger, spot-sensitivity chart | Price: 1; price + Greeks: 1; solve IV: 1; analyze premium: 1; sensitivity: 1 batch of 9 items |
9
12
  | [Regime analysis](regime/identification.ipynb) | Ticker and interval selection, one-click Yahoo history download and analysis, price-history chart, latest regime assessment, score charts, provenance | Analyze: 1 |
10
13
 
@@ -0,0 +1,167 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "33ea9e49",
6
+ "metadata": {},
7
+ "source": [
8
+ "# Explore request costs\n",
9
+ "\n",
10
+ "MorphIQ SDK for Python\n\n",
11
+ "Inspect your account’s rates, quote an option, then choose whether to execute.\n",
12
+ "All inputs start with synthetic values. Running setup or changing a control sends\n",
13
+ "no requests. Schedule and Quote each send one zero-CU utility request. Execute sends\n",
14
+ "one pricing request with your chosen CU ceiling. A quote does not lock a price or\n",
15
+ "guarantee funding, capacity or numerical success.\n",
16
+ "\n",
17
+ "Set `MORPHIQ_API_KEY` before opening Jupyter, or enter it in the masked setup prompt.\n",
18
+ "Use a deployment supporting cost utilities. Quote again after changing inputs.\n",
19
+ "\n",
20
+ "Read the output: `total_fcu` is the prospective execution charge; the utility\n",
21
+ "charge printed below it is zero. Rate lines group work by billing class. A minimum\n",
22
+ "is a floor on the whole request, not an extra fee. The Max CU control limits the\n",
23
+ "next execution only. A 409 `cost_limit_exceeded` refusal commits no charge; review\n",
24
+ "your budget before raising the control and clicking Execute again.\n",
25
+ "\n",
26
+ "For complete response examples, batch requests and error handling, see the\n",
27
+ "[cost guide](https://github.com/MorphIQ-Labs/morphiq-sdk-python/blob/main/docs/COST.md).\n"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": null,
33
+ "id": "8d057cc6",
34
+ "metadata": {},
35
+ "outputs": [],
36
+ "source": [
37
+ "import getpass\n",
38
+ "import os\n",
39
+ "\n",
40
+ "import ipywidgets as widgets\n",
41
+ "from IPython.display import display\n",
42
+ "\n",
43
+ "from morphiq_sdk import APIError, MorphIQ, PriceRequest, TransportError\n",
44
+ "\n",
45
+ "api_key = os.environ.get(\"MORPHIQ_API_KEY\") or getpass.getpass(\"MorphIQ API key: \")\n",
46
+ "spot = widgets.FloatText(value=100.0, description=\"Spot\")\n",
47
+ "strike = widgets.FloatText(value=100.0, description=\"Strike\")\n",
48
+ "volatility = widgets.FloatText(value=0.2, description=\"Volatility\")\n",
49
+ "years = widgets.FloatText(value=1.0, description=\"Years\")\n",
50
+ "rate = widgets.FloatText(value=0.05, description=\"Rate\")\n",
51
+ "yield_rate = widgets.FloatText(value=0.0, description=\"Yield\")\n",
52
+ "style = widgets.Dropdown(options=[\"european\", \"american\"], description=\"Exercise\")\n",
53
+ "option_type = widgets.Dropdown(options=[\"call\", \"put\"], description=\"Option\")\n",
54
+ "maximum = widgets.IntText(value=1, description=\"Max CU\")\n",
55
+ "schedule_button = widgets.Button(description=\"Rates · 1 utility\")\n",
56
+ "quote_button = widgets.Button(description=\"Quote · 1 utility\")\n",
57
+ "execute_button = widgets.Button(description=\"Execute · 1 request\", disabled=True)\n",
58
+ "output = widgets.Output()\n",
59
+ "last_quote = None\n",
60
+ "quoted_request = None\n",
61
+ "last_result = None\n",
62
+ "\n",
63
+ "\n",
64
+ "def current_request():\n",
65
+ " return PriceRequest(\n",
66
+ " exercise_style=style.value,\n",
67
+ " option_type=option_type.value,\n",
68
+ " spot=spot.value,\n",
69
+ " strike=strike.value,\n",
70
+ " volatility=volatility.value,\n",
71
+ " time_to_expiry_years=years.value,\n",
72
+ " risk_free_rate=rate.value,\n",
73
+ " dividend_yield=yield_rate.value,\n",
74
+ " )\n",
75
+ "\n",
76
+ "\n",
77
+ "def show_error(error):\n",
78
+ " if isinstance(error, APIError):\n",
79
+ " charged = error.metadata.fcu_charged if error.metadata else \"unknown\"\n",
80
+ " print(f\"API refused: {error.status_code}; {error.code}; charged CU: {charged}\")\n",
81
+ " else:\n",
82
+ " print(str(error))\n",
83
+ "\n",
84
+ "\n",
85
+ "def show_schedule(_):\n",
86
+ " with output:\n",
87
+ " output.clear_output()\n",
88
+ " try:\n",
89
+ " with MorphIQ(api_key=api_key) as client:\n",
90
+ " response = client.cost_schedule(operation=\"price\")\n",
91
+ " print(response.schedule.model_dump_json(indent=2))\n",
92
+ " print(\"Utility CU charged:\", response.metadata.fcu_charged)\n",
93
+ " except (APIError, TransportError, ValueError) as error:\n",
94
+ " show_error(error)\n",
95
+ "\n",
96
+ "\n",
97
+ "def quote_request(_):\n",
98
+ " global last_quote, quoted_request\n",
99
+ " execute_button.disabled = True\n",
100
+ " last_quote = quoted_request = None\n",
101
+ " with output:\n",
102
+ " output.clear_output()\n",
103
+ " try:\n",
104
+ " request = current_request()\n",
105
+ " with MorphIQ(api_key=api_key) as client:\n",
106
+ " response = client.quote(request)\n",
107
+ " last_quote, quoted_request = response, request.to_wire()\n",
108
+ " maximum.value = response.quote.total_fcu\n",
109
+ " execute_button.disabled = False\n",
110
+ " print(response.quote.model_dump_json(indent=2))\n",
111
+ " print(\"Selected models:\", response.resolved_models)\n",
112
+ " print(\"Utility CU charged:\", response.metadata.fcu_charged)\n",
113
+ " print(\"Execution is a separate action. Adjust Max CU if desired.\")\n",
114
+ " except (APIError, TransportError, ValueError) as error:\n",
115
+ " show_error(error)\n",
116
+ "\n",
117
+ "\n",
118
+ "def execute_request(_):\n",
119
+ " global last_result\n",
120
+ " with output:\n",
121
+ " output.clear_output()\n",
122
+ " try:\n",
123
+ " request = current_request()\n",
124
+ " if last_quote is None or request.to_wire() != quoted_request:\n",
125
+ " print(\"Inputs changed. Quote this request before executing.\")\n",
126
+ " return\n",
127
+ " with MorphIQ(api_key=api_key) as client:\n",
128
+ " last_result = client.price(request, max_cu=maximum.value)\n",
129
+ " print(\"Price:\", last_result.result.price)\n",
130
+ " print(\"CU charged:\", last_result.metadata.fcu_charged)\n",
131
+ " except (APIError, TransportError, ValueError) as error:\n",
132
+ " show_error(error)\n",
133
+ "\n",
134
+ "\n",
135
+ "def input_changed(_):\n",
136
+ " execute_button.disabled = True\n",
137
+ "\n",
138
+ "\n",
139
+ "for control in [spot, strike, volatility, years, rate, yield_rate, style, option_type]:\n",
140
+ " control.observe(input_changed, names=\"value\")\n",
141
+ "schedule_button.on_click(show_schedule)\n",
142
+ "quote_button.on_click(quote_request)\n",
143
+ "execute_button.on_click(execute_request)\n",
144
+ "display(\n",
145
+ " widgets.VBox(\n",
146
+ " [\n",
147
+ " widgets.HBox([spot, strike, volatility]),\n",
148
+ " widgets.HBox([years, rate, yield_rate]),\n",
149
+ " widgets.HBox([style, option_type, maximum]),\n",
150
+ " widgets.HBox([schedule_button, quote_button, execute_button]),\n",
151
+ " output,\n",
152
+ " ]\n",
153
+ " )\n",
154
+ ")"
155
+ ]
156
+ }
157
+ ],
158
+ "metadata": {
159
+ "kernelspec": {
160
+ "display_name": "Python 3",
161
+ "language": "python",
162
+ "name": "python3"
163
+ }
164
+ },
165
+ "nbformat": 4,
166
+ "nbformat_minor": 5
167
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "intro",
6
+ "metadata": {},
7
+ "source": "# MorphIQ SDK for Python — Available models\n\nInspect model capabilities, aliases, constraints, engine versions and Greek units.\nRun setup once, then click **Load models — 1 request, 0 CU**. No key is needed.\nSetup makes no requests. This catalog describes compiled capabilities; use the\ncost quotation workspace for account-specific rates and admission.\n\n[Back to start](../start-here.ipynb)\n"
8
+ },
9
+ {
10
+ "cell_type": "code",
11
+ "execution_count": null,
12
+ "id": "workspace",
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "import ipywidgets as widgets\n",
17
+ "from IPython.display import display\n",
18
+ "\n",
19
+ "from morphiq_sdk import MorphIQ, MorphIQError\n",
20
+ "\n",
21
+ "load_button = widgets.Button(\n",
22
+ " description=\"Load models — 1 request, 0 CU\", layout=widgets.Layout(width=\"300px\")\n",
23
+ ")\n",
24
+ "output = widgets.Output()\n",
25
+ "last_catalog = None\n",
26
+ "\n",
27
+ "\n",
28
+ "def load_models(_):\n",
29
+ " global last_catalog\n",
30
+ " last_catalog = None\n",
31
+ " load_button.disabled = True\n",
32
+ " with output:\n",
33
+ " output.clear_output(wait=True)\n",
34
+ " try:\n",
35
+ " with MorphIQ() as client:\n",
36
+ " last_catalog = client.pricing_models()\n",
37
+ " for model in last_catalog.models:\n",
38
+ " print(model.name, \"—\", model.id, \"—\", model.model_version)\n",
39
+ " print(\"Single:\", \", \".join(model.operations))\n",
40
+ " print(\"Batch:\", \", \".join(model.batch_operations) or \"none\")\n",
41
+ " print(\"Aliases:\", model.aliases)\n",
42
+ " print(\"Guidance:\", model.guidance)\n",
43
+ " for constraint in model.constraints:\n",
44
+ " print(\"Constraint:\", constraint.model_dump(exclude_unset=True))\n",
45
+ " print()\n",
46
+ " print(\"Greek units:\", last_catalog.greek_units)\n",
47
+ " print(\"Engines:\", [engine.model_dump() for engine in last_catalog.engines])\n",
48
+ " except MorphIQError as error:\n",
49
+ " print(error)\n",
50
+ " finally:\n",
51
+ " load_button.disabled = False\n",
52
+ "\n",
53
+ "\n",
54
+ "load_button.on_click(load_models)\n",
55
+ "display(load_button, output)"
56
+ ]
57
+ }
58
+ ],
59
+ "metadata": {
60
+ "kernelspec": {
61
+ "display_name": "Python 3",
62
+ "language": "python",
63
+ "name": "python3"
64
+ },
65
+ "language_info": {
66
+ "name": "python",
67
+ "version": "3.11"
68
+ }
69
+ },
70
+ "nbformat": 4,
71
+ "nbformat_minor": 5
72
+ }