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.
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/PKG-INFO +25 -15
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/README.md +24 -14
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/analytics.py +3 -1
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/analytics_async.py +3 -1
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/batch_price.py +1 -1
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/batch_price_async.py +1 -1
- morphiq_sdk-0.2.0/examples/cost_quote.py +77 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/greeks.py +3 -1
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/greeks_async.py +3 -1
- morphiq_sdk-0.2.0/examples/greeks_standard.py +76 -0
- morphiq_sdk-0.2.0/examples/pricing_models.py +17 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/README.md +3 -0
- morphiq_sdk-0.2.0/notebooks/cost/quotation.ipynb +167 -0
- morphiq_sdk-0.2.0/notebooks/pricing/models.ipynb +72 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/pricing/options.ipynb +25 -63
- morphiq_sdk-0.2.0/notebooks/pricing/standard-five.ipynb +120 -0
- morphiq_sdk-0.2.0/notebooks/start-here.ipynb +23 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/pyproject.toml +1 -1
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/__init__.py +34 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/client.py +300 -55
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/models.py +186 -25
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/pandas.py +3 -1
- morphiq_sdk-0.1.0/notebooks/start-here.ipynb +0 -23
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/.gitignore +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/LICENSE +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/README.md +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/__init__.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/chunked_price.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/chunked_price_async.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/identify_regime.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/identify_regime_async.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/implied_volatility.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/implied_volatility_async.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_american.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_async.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/price_european.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/provider_regime.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/quickstart.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/examples/tabular_price.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/notebooks/regime/identification.ipynb +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/errors.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/market_data.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/py.typed +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk/workflows.py +0 -0
- {morphiq_sdk-0.1.0 → morphiq_sdk-0.2.0}/src/morphiq_sdk_yahoo/__init__.py +0 -0
- {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.
|
|
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": "
|
|
116
|
+
"request_id": "fr-18e4e90129262501-0000000000000003",
|
|
118
117
|
"api_version": "v1",
|
|
119
|
-
"engine_version": "0.
|
|
118
|
+
"engine_version": "0.43.5",
|
|
120
119
|
"engines": [
|
|
121
120
|
{
|
|
122
121
|
"name": "morphiq-risk",
|
|
123
|
-
"version": "0.
|
|
124
|
-
"revision": "
|
|
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.
|
|
222
|
-
`
|
|
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": "
|
|
80
|
+
"request_id": "fr-18e4e90129262501-0000000000000003",
|
|
82
81
|
"api_version": "v1",
|
|
83
|
-
"engine_version": "0.
|
|
82
|
+
"engine_version": "0.43.5",
|
|
84
83
|
"engines": [
|
|
85
84
|
{
|
|
86
85
|
"name": "morphiq-risk",
|
|
87
|
-
"version": "0.
|
|
88
|
-
"revision": "
|
|
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.
|
|
186
|
-
`
|
|
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
|
-
|
|
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
|
-
|
|
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}")
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
+
}
|