markovian 1.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Markovian Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: markovian
3
+ Version: 1.0.0
4
+ Summary: Python SDK for the Markovian Protocol: verifiable market-regime data, on-chain ZK proofs, and STAMP commitment from a proof-of-intelligence Layer 1.
5
+ Author-email: Markovian Protocol <hello@quantsynth.net>
6
+ License: MIT
7
+ Project-URL: Homepage, https://markovianprotocol.com
8
+ Project-URL: Documentation, https://markovianprotocol.com/build
9
+ Project-URL: Source, https://github.com/MarkovianProtocol/markovian-protocol
10
+ Project-URL: Explorer, https://chain.quantsynth.net
11
+ Keywords: markovian,oracle,zk,zero-knowledge,blockchain,market-regime,defi,prediction-markets,markov,provenance
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Office/Business :: Financial :: Investment
24
+ Classifier: Topic :: Security :: Cryptography
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests>=2.25
30
+ Requires-Dist: numpy>=1.20
31
+ Requires-Dist: py_ecc>=6.0
32
+ Dynamic: license-file
33
+
34
+ # markovian
35
+
36
+ Python SDK for the [Markovian Protocol](https://markovianprotocol.com), a proof-of-intelligence Layer 1 that produces ZK-verified market-regime classifications and commits arbitrary data to an immutable, Bitcoin-anchored chain.
37
+
38
+ ```bash
39
+ pip install markovian
40
+ ```
41
+
42
+ ## Why it exists
43
+
44
+ More than $400M has been lost to oracle manipulation since 2022. Every exploit shared a root cause: an external price feed that could be skewed. The Markovian Protocol removes the feed. Each block is a Markov state transition, `s_N = M^N x s`, where `M` is the protocol-published transition matrix and `s` derives deterministically from the previous block hash. A BN128 zero-knowledge proof attesting to that computation is verified on-chain, so the output (a regime classification of ACCUMULATION, MARKUP, or DISTRIBUTION) is reproducible and cannot be forged by any single party.
45
+
46
+ This package is the client for reading that data, verifying the proofs locally, and committing records to the chain.
47
+
48
+ ## Quickstart
49
+
50
+ The free tier is keyless. No account, no API key.
51
+
52
+ ```python
53
+ from markovian import MarkovianClient
54
+
55
+ client = MarkovianClient()
56
+
57
+ # Latest ZK-proven regime snapshot for liquid tickers
58
+ for r in client.latest():
59
+ print(f"{r.ticker:<6} {r.regime:<14} {r.confidence*100:.1f}%")
60
+
61
+ # QQQ DISTRIBUTION 61.3%
62
+ # SPY DISTRIBUTION 67.8%
63
+ # GLD DISTRIBUTION 99.0%
64
+
65
+ # Regime history for a single ticker
66
+ history = client.regime("QQQ", days=90)
67
+
68
+ # Snapshot across several tickers at once
69
+ snap = client.batch(["SPY", "QQQ", "GLD", "TLT"])
70
+ ```
71
+
72
+ From the command line:
73
+
74
+ ```bash
75
+ markovian latest
76
+ markovian regime QQQ 90
77
+ markovian tip
78
+ ```
79
+
80
+ ## Verify a proof locally
81
+
82
+ Verification requires no trust in the API. The ZK circuit runs client-side against a Merkle root.
83
+
84
+ ```python
85
+ result = client.verify(merkle_root, local=True)
86
+ print(result["verified"], result.get("local_zk_verified"))
87
+ ```
88
+
89
+ ## Commit data to the chain (STAMP)
90
+
91
+ STAMP writes any JSON-serialisable record to the chain permanently. The result is unforgeable, publicly verifiable, and Bitcoin-anchored.
92
+
93
+ ```python
94
+ client.faucet("my-wallet") # 100 free test MKV
95
+ proof = client.stamp({"event": "audit-2026"}, wallet="my-wallet")
96
+ print(proof["verify_url"])
97
+ ```
98
+
99
+ ## Tiers
100
+
101
+ | Tier | Access | Key |
102
+ |------------|-----------------------------------------|-----|
103
+ | Free | Liquid tickers, 90-day window | None |
104
+ | Pro | All tickers, full history, CSV export | Required |
105
+ | Enterprise | All of Pro, plus inline ZK proofs | Required |
106
+
107
+ Keys are issued at [markovianprotocol.com](https://markovianprotocol.com).
108
+
109
+ ## API surface
110
+
111
+ - `latest()` — current regime snapshot for liquid tickers
112
+ - `regime(ticker, days)` — regime history for one ticker
113
+ - `batch(tickers)` — snapshot across multiple tickers
114
+ - `catalog()` — available tickers and coverage
115
+ - `verify(merkle_root, local)` — verify a proof against the registry, optionally re-running the ZK circuit locally
116
+ - `stamp(data, wallet)` — commit a record to the chain
117
+ - `faucet(wallet)` — claim test MKV
118
+ - `watch(tickers, callback)` — poll for regime transitions
119
+ - `tip()`, `block(height)`, `ledger()` — chain primitives
120
+
121
+ ## Requirements
122
+
123
+ Python 3.8+, with `requests`, `numpy`, and `py_ecc` (installed automatically).
124
+
125
+ ## Links
126
+
127
+ - Protocol: https://markovianprotocol.com
128
+ - Build docs: https://markovianprotocol.com/build
129
+ - Explorer: https://chain.quantsynth.net
130
+ - Source: https://github.com/MarkovianProtocol/markovian-protocol
131
+
132
+ MIT licensed.
@@ -0,0 +1,99 @@
1
+ # markovian
2
+
3
+ Python SDK for the [Markovian Protocol](https://markovianprotocol.com), a proof-of-intelligence Layer 1 that produces ZK-verified market-regime classifications and commits arbitrary data to an immutable, Bitcoin-anchored chain.
4
+
5
+ ```bash
6
+ pip install markovian
7
+ ```
8
+
9
+ ## Why it exists
10
+
11
+ More than $400M has been lost to oracle manipulation since 2022. Every exploit shared a root cause: an external price feed that could be skewed. The Markovian Protocol removes the feed. Each block is a Markov state transition, `s_N = M^N x s`, where `M` is the protocol-published transition matrix and `s` derives deterministically from the previous block hash. A BN128 zero-knowledge proof attesting to that computation is verified on-chain, so the output (a regime classification of ACCUMULATION, MARKUP, or DISTRIBUTION) is reproducible and cannot be forged by any single party.
12
+
13
+ This package is the client for reading that data, verifying the proofs locally, and committing records to the chain.
14
+
15
+ ## Quickstart
16
+
17
+ The free tier is keyless. No account, no API key.
18
+
19
+ ```python
20
+ from markovian import MarkovianClient
21
+
22
+ client = MarkovianClient()
23
+
24
+ # Latest ZK-proven regime snapshot for liquid tickers
25
+ for r in client.latest():
26
+ print(f"{r.ticker:<6} {r.regime:<14} {r.confidence*100:.1f}%")
27
+
28
+ # QQQ DISTRIBUTION 61.3%
29
+ # SPY DISTRIBUTION 67.8%
30
+ # GLD DISTRIBUTION 99.0%
31
+
32
+ # Regime history for a single ticker
33
+ history = client.regime("QQQ", days=90)
34
+
35
+ # Snapshot across several tickers at once
36
+ snap = client.batch(["SPY", "QQQ", "GLD", "TLT"])
37
+ ```
38
+
39
+ From the command line:
40
+
41
+ ```bash
42
+ markovian latest
43
+ markovian regime QQQ 90
44
+ markovian tip
45
+ ```
46
+
47
+ ## Verify a proof locally
48
+
49
+ Verification requires no trust in the API. The ZK circuit runs client-side against a Merkle root.
50
+
51
+ ```python
52
+ result = client.verify(merkle_root, local=True)
53
+ print(result["verified"], result.get("local_zk_verified"))
54
+ ```
55
+
56
+ ## Commit data to the chain (STAMP)
57
+
58
+ STAMP writes any JSON-serialisable record to the chain permanently. The result is unforgeable, publicly verifiable, and Bitcoin-anchored.
59
+
60
+ ```python
61
+ client.faucet("my-wallet") # 100 free test MKV
62
+ proof = client.stamp({"event": "audit-2026"}, wallet="my-wallet")
63
+ print(proof["verify_url"])
64
+ ```
65
+
66
+ ## Tiers
67
+
68
+ | Tier | Access | Key |
69
+ |------------|-----------------------------------------|-----|
70
+ | Free | Liquid tickers, 90-day window | None |
71
+ | Pro | All tickers, full history, CSV export | Required |
72
+ | Enterprise | All of Pro, plus inline ZK proofs | Required |
73
+
74
+ Keys are issued at [markovianprotocol.com](https://markovianprotocol.com).
75
+
76
+ ## API surface
77
+
78
+ - `latest()` — current regime snapshot for liquid tickers
79
+ - `regime(ticker, days)` — regime history for one ticker
80
+ - `batch(tickers)` — snapshot across multiple tickers
81
+ - `catalog()` — available tickers and coverage
82
+ - `verify(merkle_root, local)` — verify a proof against the registry, optionally re-running the ZK circuit locally
83
+ - `stamp(data, wallet)` — commit a record to the chain
84
+ - `faucet(wallet)` — claim test MKV
85
+ - `watch(tickers, callback)` — poll for regime transitions
86
+ - `tip()`, `block(height)`, `ledger()` — chain primitives
87
+
88
+ ## Requirements
89
+
90
+ Python 3.8+, with `requests`, `numpy`, and `py_ecc` (installed automatically).
91
+
92
+ ## Links
93
+
94
+ - Protocol: https://markovianprotocol.com
95
+ - Build docs: https://markovianprotocol.com/build
96
+ - Explorer: https://chain.quantsynth.net
97
+ - Source: https://github.com/MarkovianProtocol/markovian-protocol
98
+
99
+ MIT licensed.
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: markovian
3
+ Version: 1.0.0
4
+ Summary: Python SDK for the Markovian Protocol: verifiable market-regime data, on-chain ZK proofs, and STAMP commitment from a proof-of-intelligence Layer 1.
5
+ Author-email: Markovian Protocol <hello@quantsynth.net>
6
+ License: MIT
7
+ Project-URL: Homepage, https://markovianprotocol.com
8
+ Project-URL: Documentation, https://markovianprotocol.com/build
9
+ Project-URL: Source, https://github.com/MarkovianProtocol/markovian-protocol
10
+ Project-URL: Explorer, https://chain.quantsynth.net
11
+ Keywords: markovian,oracle,zk,zero-knowledge,blockchain,market-regime,defi,prediction-markets,markov,provenance
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Topic :: Office/Business :: Financial :: Investment
24
+ Classifier: Topic :: Security :: Cryptography
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests>=2.25
30
+ Requires-Dist: numpy>=1.20
31
+ Requires-Dist: py_ecc>=6.0
32
+ Dynamic: license-file
33
+
34
+ # markovian
35
+
36
+ Python SDK for the [Markovian Protocol](https://markovianprotocol.com), a proof-of-intelligence Layer 1 that produces ZK-verified market-regime classifications and commits arbitrary data to an immutable, Bitcoin-anchored chain.
37
+
38
+ ```bash
39
+ pip install markovian
40
+ ```
41
+
42
+ ## Why it exists
43
+
44
+ More than $400M has been lost to oracle manipulation since 2022. Every exploit shared a root cause: an external price feed that could be skewed. The Markovian Protocol removes the feed. Each block is a Markov state transition, `s_N = M^N x s`, where `M` is the protocol-published transition matrix and `s` derives deterministically from the previous block hash. A BN128 zero-knowledge proof attesting to that computation is verified on-chain, so the output (a regime classification of ACCUMULATION, MARKUP, or DISTRIBUTION) is reproducible and cannot be forged by any single party.
45
+
46
+ This package is the client for reading that data, verifying the proofs locally, and committing records to the chain.
47
+
48
+ ## Quickstart
49
+
50
+ The free tier is keyless. No account, no API key.
51
+
52
+ ```python
53
+ from markovian import MarkovianClient
54
+
55
+ client = MarkovianClient()
56
+
57
+ # Latest ZK-proven regime snapshot for liquid tickers
58
+ for r in client.latest():
59
+ print(f"{r.ticker:<6} {r.regime:<14} {r.confidence*100:.1f}%")
60
+
61
+ # QQQ DISTRIBUTION 61.3%
62
+ # SPY DISTRIBUTION 67.8%
63
+ # GLD DISTRIBUTION 99.0%
64
+
65
+ # Regime history for a single ticker
66
+ history = client.regime("QQQ", days=90)
67
+
68
+ # Snapshot across several tickers at once
69
+ snap = client.batch(["SPY", "QQQ", "GLD", "TLT"])
70
+ ```
71
+
72
+ From the command line:
73
+
74
+ ```bash
75
+ markovian latest
76
+ markovian regime QQQ 90
77
+ markovian tip
78
+ ```
79
+
80
+ ## Verify a proof locally
81
+
82
+ Verification requires no trust in the API. The ZK circuit runs client-side against a Merkle root.
83
+
84
+ ```python
85
+ result = client.verify(merkle_root, local=True)
86
+ print(result["verified"], result.get("local_zk_verified"))
87
+ ```
88
+
89
+ ## Commit data to the chain (STAMP)
90
+
91
+ STAMP writes any JSON-serialisable record to the chain permanently. The result is unforgeable, publicly verifiable, and Bitcoin-anchored.
92
+
93
+ ```python
94
+ client.faucet("my-wallet") # 100 free test MKV
95
+ proof = client.stamp({"event": "audit-2026"}, wallet="my-wallet")
96
+ print(proof["verify_url"])
97
+ ```
98
+
99
+ ## Tiers
100
+
101
+ | Tier | Access | Key |
102
+ |------------|-----------------------------------------|-----|
103
+ | Free | Liquid tickers, 90-day window | None |
104
+ | Pro | All tickers, full history, CSV export | Required |
105
+ | Enterprise | All of Pro, plus inline ZK proofs | Required |
106
+
107
+ Keys are issued at [markovianprotocol.com](https://markovianprotocol.com).
108
+
109
+ ## API surface
110
+
111
+ - `latest()` — current regime snapshot for liquid tickers
112
+ - `regime(ticker, days)` — regime history for one ticker
113
+ - `batch(tickers)` — snapshot across multiple tickers
114
+ - `catalog()` — available tickers and coverage
115
+ - `verify(merkle_root, local)` — verify a proof against the registry, optionally re-running the ZK circuit locally
116
+ - `stamp(data, wallet)` — commit a record to the chain
117
+ - `faucet(wallet)` — claim test MKV
118
+ - `watch(tickers, callback)` — poll for regime transitions
119
+ - `tip()`, `block(height)`, `ledger()` — chain primitives
120
+
121
+ ## Requirements
122
+
123
+ Python 3.8+, with `requests`, `numpy`, and `py_ecc` (installed automatically).
124
+
125
+ ## Links
126
+
127
+ - Protocol: https://markovianprotocol.com
128
+ - Build docs: https://markovianprotocol.com/build
129
+ - Explorer: https://chain.quantsynth.net
130
+ - Source: https://github.com/MarkovianProtocol/markovian-protocol
131
+
132
+ MIT licensed.
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ markovian.py
4
+ pyproject.toml
5
+ markovian.egg-info/PKG-INFO
6
+ markovian.egg-info/SOURCES.txt
7
+ markovian.egg-info/dependency_links.txt
8
+ markovian.egg-info/entry_points.txt
9
+ markovian.egg-info/requires.txt
10
+ markovian.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ markovian = markovian:_cli
@@ -0,0 +1,3 @@
1
+ requests>=2.25
2
+ numpy>=1.20
3
+ py_ecc>=6.0
@@ -0,0 +1 @@
1
+ markovian
@@ -0,0 +1,470 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ markovian.py - Markovian Protocol Python SDK
4
+ Version 1.0 - June 2026
5
+
6
+ Query regime data, verify ZK proofs, and subscribe to state changes
7
+ from the Markovian Protocol Layer 1.
8
+
9
+ Install:
10
+ pip install requests numpy py_ecc
11
+
12
+ Usage:
13
+ from markovian import MarkovianClient
14
+
15
+ client = MarkovianClient() # free tier, no key
16
+ client = MarkovianClient(api_key="your-key") # pro/enterprise
17
+
18
+ # Latest regime snapshot (liquid tickers)
19
+ snap = client.latest()
20
+
21
+ # Regime history for a ticker
22
+ df = client.regime("QQQ", days=90)
23
+
24
+ # Batch query
25
+ data = client.batch(["SPY", "QQQ", "GLD", "TLT"], days=30)
26
+
27
+ # Verify a ZK proof
28
+ result = client.verify(merkle_root)
29
+
30
+ # Chain tip
31
+ tip = client.tip()
32
+
33
+ # Block by height
34
+ block = client.block(18000)
35
+
36
+ # Watch for regime changes (polling)
37
+ client.watch(["QQQ", "SPY"], callback=my_fn, interval=60)
38
+ """
39
+
40
+ import hashlib
41
+ import time
42
+ from typing import Callable, Dict, List, Optional
43
+
44
+ import numpy as np
45
+ import requests
46
+
47
+ # ── Constants ─────────────────────────────────────────────────────────────────
48
+
49
+ __version__ = "1.0.0"
50
+ BASE_URL = "https://api.quantsynth.net"
51
+ CHAIN_URL = "https://api.quantsynth.net"
52
+
53
+ STATES = ["ACCUMULATION", "MARKUP", "DISTRIBUTION"]
54
+ STATE_SHORT = {"ACCUMULATION": "ACCUM", "MARKUP": "MUP", "DISTRIBUTION": "DIST"}
55
+
56
+ GENESIS_M = np.array([
57
+ [0.70, 0.25, 0.05],
58
+ [0.10, 0.75, 0.15],
59
+ [0.20, 0.15, 0.65],
60
+ ], dtype=np.float64)
61
+
62
+
63
+ # ── Regime object ─────────────────────────────────────────────────────────────
64
+
65
+ class Regime:
66
+ """A single regime observation for a ticker on a date."""
67
+
68
+ def __init__(self, data: dict):
69
+ self._d = data
70
+ self.ticker = data.get("ticker", "")
71
+ self.date = data.get("date") or data.get("obs_date", "")
72
+ self.regime = data.get("regime", "")
73
+ self.confidence = float(data.get("confidence", 0))
74
+ raw = data.get("s_output") or data.get("s_input") or []
75
+ if isinstance(raw, str):
76
+ import json
77
+ raw = json.loads(raw)
78
+ self.p_accum = float(raw[0]) if len(raw) > 0 else 0.0
79
+ self.p_markup = float(raw[1]) if len(raw) > 1 else 0.0
80
+ self.p_dist = float(raw[2]) if len(raw) > 2 else 0.0
81
+
82
+ def __repr__(self):
83
+ return f"Regime({self.ticker} {self.date}: {self.regime} {self.confidence*100:.1f}%)"
84
+
85
+ def to_dict(self):
86
+ return {
87
+ "ticker": self.ticker,
88
+ "date": self.date,
89
+ "regime": self.regime,
90
+ "confidence": round(self.confidence, 4),
91
+ "p_accum": round(self.p_accum, 4),
92
+ "p_markup": round(self.p_markup, 4),
93
+ "p_dist": round(self.p_dist, 4),
94
+ }
95
+
96
+ @property
97
+ def is_bullish(self):
98
+ return self.regime == "MARKUP"
99
+
100
+ @property
101
+ def is_bearish(self):
102
+ return self.regime == "DISTRIBUTION"
103
+
104
+ @property
105
+ def is_accumulating(self):
106
+ return self.regime == "ACCUMULATION"
107
+
108
+
109
+ # ── Verify (standalone, no API needed) ───────────────────────────────────────
110
+
111
+ def _verify_local(proof_dict: dict) -> dict:
112
+ """
113
+ Verify a MarkovProof locally using the BN128 ZK circuit.
114
+ No network call. Requires: pip install py_ecc numpy
115
+ Full spec: chain.quantsynth.net/zkspec
116
+ """
117
+ try:
118
+ from py_ecc.bn128 import G1, multiply, add, neg, curve_order
119
+ from py_ecc.fields import bn128_FQ as FQ
120
+
121
+ _H_SEED = int(hashlib.sha256(b"Markovian-H-generator-v1").hexdigest(), 16) % curve_order
122
+ H = multiply(G1, _H_SEED)
123
+ M_DENOM = 20
124
+ TOLERANCE = 50
125
+
126
+ def _sm(k, P): k = int(k) % curve_order; return None if k == 0 else multiply(P, k)
127
+ def _pa(P, Q):
128
+ if P is None: return Q
129
+ if Q is None: return P
130
+ return add(P, Q)
131
+ def _ps(P, Q): return _pa(P, neg(Q))
132
+ def _ec(p): return (FQ(int(p[0])), FQ(int(p[1]))) if p else None
133
+ def _hash(*p): return int(hashlib.sha256(b"||".join(str(x).encode() for x in p)).hexdigest(), 16) % curve_order
134
+
135
+ M = GENESIS_M
136
+ Mt = M.T
137
+ M_INT = [[round(Mt[r, c] * M_DENOM) for c in range(3)] for r in range(3)]
138
+
139
+ n_steps = int(proof_dict["n_steps"])
140
+ steps = proof_dict["steps"]
141
+ m_ver = int(proof_dict["m_version"])
142
+ ctx_b = f"mkv|v{m_ver}|N={n_steps}".encode()
143
+
144
+ if len(steps) != n_steps:
145
+ return {"verified": False, "error": f"step count mismatch: {len(steps)} != {n_steps}"}
146
+
147
+ for i, step in enumerate(steps):
148
+ for j in range(3):
149
+ eps = step["epsilons"][j]
150
+ if abs(eps) > TOLERANCE:
151
+ return {"verified": False, "error": f"step {i} j={j} epsilon={eps} > tolerance"}
152
+ D = _sm(M_DENOM, _ec(step["C_out"][j]))
153
+ for k in range(3):
154
+ D = _ps(D, _sm(M_INT[j][k], _ec(step["C_in"][k])))
155
+ if eps != 0:
156
+ D = _ps(D, _sm(int(eps) % curve_order, H))
157
+ ctx = ctx_b + f"|i={i}|step|j={j}|eps={eps}".encode()
158
+ pf = step["proofs"][j]
159
+ R = _ec(pf["R"])
160
+ s, e = int(pf["s"]), int(pf["e"])
161
+ if _pa(_sm(s, G1), _sm(e, D)) != R:
162
+ return {"verified": False, "error": f"Schnorr failed at step {i} j={j}"}
163
+ e2 = _hash([int(D[0]), int(D[1])], [int(R[0]), int(R[1])], ctx)
164
+ if e2 != e:
165
+ return {"verified": False, "error": f"Fiat-Shamir mismatch step {i} j={j}"}
166
+ if i < n_steps - 1 and step["C_out"] != steps[i + 1]["C_in"]:
167
+ return {"verified": False, "error": f"chain break between step {i} and {i+1}"}
168
+
169
+ return {"verified": True, "n_steps": n_steps, "m_version": m_ver}
170
+ except ImportError:
171
+ return {"verified": None, "error": "py_ecc not installed: pip install py_ecc"}
172
+ except Exception as e:
173
+ return {"verified": False, "error": str(e)}
174
+
175
+
176
+ # ── Client ────────────────────────────────────────────────────────────────────
177
+
178
+ class MarkovianClient:
179
+ """
180
+ Markovian Protocol API client.
181
+
182
+ Free tier (no key): liquid tickers, 90-day window, 90 rows/call.
183
+ Pro/Enterprise: all tickers, full history, CSV export.
184
+
185
+ Get a key: https://alphasynth.net
186
+ ZK spec: https://chain.quantsynth.net/zkspec
187
+ """
188
+
189
+ def __init__(self, api_key: Optional[str] = None, timeout: int = 30):
190
+ self.api_key = api_key
191
+ self.timeout = timeout
192
+ self._session = requests.Session()
193
+ self._session.headers.update({
194
+ "User-Agent": f"markovian-sdk/{__version__}",
195
+ "Accept": "application/json",
196
+ })
197
+ if api_key:
198
+ self._session.headers["X-API-Key"] = api_key
199
+
200
+ def _get(self, path: str, params: dict = None) -> dict:
201
+ r = self._session.get(f"{BASE_URL}{path}", params=params, timeout=self.timeout)
202
+ r.raise_for_status()
203
+ return r.json()
204
+
205
+ # ── Regime data ───────────────────────────────────────────────────────────
206
+
207
+ def latest(self) -> List[Regime]:
208
+ """Latest regime snapshot for major liquid tickers. Keyless / free tier."""
209
+ data = self._get("/data/latest")
210
+ regimes = data.get("regimes", {}) if isinstance(data, dict) else data
211
+ if isinstance(regimes, dict):
212
+ out = []
213
+ for ticker, v in regimes.items():
214
+ rec = dict(v)
215
+ rec.setdefault("ticker", ticker)
216
+ out.append(Regime(rec))
217
+ return out
218
+ return [Regime(v) for v in regimes]
219
+
220
+ def regime(self, ticker: str, days: int = 90, fmt: str = "json") -> List[Regime]:
221
+ """
222
+ Regime history for a single ticker.
223
+
224
+ Args:
225
+ ticker: e.g. "QQQ", "SPY", "GLD"
226
+ days: lookback window in days (free tier max: 90)
227
+ fmt: "json" or "csv" (pro/enterprise only)
228
+ """
229
+ import datetime as _dt
230
+ ticker = ticker.upper()
231
+ params = {"limit": days}
232
+ if days:
233
+ params["from"] = (_dt.date.today() - _dt.timedelta(days=days)).isoformat()
234
+ if fmt == "csv":
235
+ params["format"] = "csv"
236
+ try:
237
+ data = self._get(f"/data/regime/{ticker}", params)
238
+ except requests.HTTPError as e:
239
+ # No observations in the requested window is not an error: return empty.
240
+ if e.response is not None and e.response.status_code == 404:
241
+ return [] if fmt != "csv" else ""
242
+ raise
243
+ if fmt == "csv":
244
+ return data
245
+ rows = data if isinstance(data, list) else data.get("data", [])
246
+ return [Regime({**r, "ticker": r.get("ticker", ticker)}) for r in rows]
247
+
248
+ def batch(self, tickers: List[str], date: str = None) -> Dict[str, Regime]:
249
+ """
250
+ Latest (or as-of `date`) regime snapshot for multiple tickers.
251
+
252
+ Args:
253
+ tickers: list of tickers, e.g. ["SPY", "QQQ", "GLD"]
254
+ date: optional "YYYY-MM-DD"; defaults to the most recent observation
255
+
256
+ Returns a dict keyed by ticker -> Regime.
257
+ """
258
+ params = {"tickers": ",".join(t.upper() for t in tickers)}
259
+ if date:
260
+ params["date"] = date
261
+ data = self._get("/data/batch", params)
262
+ rows = data.get("data", {}) if isinstance(data, dict) else {}
263
+ result = {}
264
+ for ticker, rec in rows.items():
265
+ if isinstance(rec, dict):
266
+ r = dict(rec)
267
+ r.setdefault("ticker", ticker)
268
+ result[ticker] = Regime(r)
269
+ return result
270
+
271
+ def catalog(self) -> dict:
272
+ """List available tickers and coverage dates."""
273
+ return self._get("/data/catalog")
274
+
275
+ # ── Chain ─────────────────────────────────────────────────────────────────
276
+
277
+ def tip(self) -> dict:
278
+ """Latest chain tip: height, hash, difficulty."""
279
+ return self._get("/tip")
280
+
281
+ def block(self, height: int) -> dict:
282
+ """Fetch a specific block by height."""
283
+ return self._get(f"/block/{height}")
284
+
285
+ def ledger(self) -> dict:
286
+ """MKV ledger summary: total blocks, MKV issued, Kovs issued."""
287
+ return self._get("/ledger/summary")
288
+
289
+ # ── Verification ──────────────────────────────────────────────────────────
290
+
291
+ def verify(self, merkle_root: str, local: bool = False) -> dict:
292
+ """
293
+ Verify a Merkle root against the provenance registry.
294
+
295
+ Args:
296
+ merkle_root: 64-char hex string
297
+ local: if True, re-verify ZK proof locally (requires py_ecc)
298
+ """
299
+ result = self._get(f"/verify/{merkle_root}", {"format": "json"})
300
+ if local and result.get("zk_proof"):
301
+ local_check = _verify_local(result["zk_proof"])
302
+ result["local_zk_verified"] = local_check.get("verified")
303
+ return result
304
+
305
+ def verify_archive(self, root_hash: str) -> dict:
306
+ """Verify an archive Merkle root against on-chain commitments."""
307
+ return self._get(f"/archive/verify/{root_hash}")
308
+
309
+ # ── STAMP ─────────────────────────────────────────────────────────────────
310
+
311
+ def stamp(self, data, wallet: str, label: str = None) -> dict:
312
+ """
313
+ STAMP interface — commit any data to the chain permanently.
314
+
315
+ Burns 1 MKV from wallet. Returns merkle_root + ZK commitment.
316
+ The record is unforgeable, publicly verifiable, Bitcoin-anchored.
317
+
318
+ Args:
319
+ data: any JSON-serialisable value (dict, list, string, number)
320
+ wallet: MKV wallet address (get test MKV via client.faucet())
321
+ label: optional human-readable label for this stamp
322
+
323
+ Returns dict with: ok, stamp_id, merkle_root, zk_commitment,
324
+ block_height, stamped_at, mkv_burned, mkv_balance, verify_url
325
+
326
+ Example:
327
+ regime = client.latest()[0]
328
+ proof = client.stamp(regime.to_dict(), wallet='my-wallet')
329
+ print(proof['verify_url'])
330
+ """
331
+ r = self._session.post(
332
+ f"{BASE_URL}/stamp",
333
+ json={"wallet": wallet, "data": data, "label": label},
334
+ timeout=self.timeout,
335
+ )
336
+ r.raise_for_status()
337
+ return r.json()
338
+
339
+ def stamps(self, wallet: str, limit: int = 20) -> list:
340
+ """List all STAMP records for a wallet."""
341
+ return self._get(f"/stamps/{wallet}", {"limit": limit})
342
+
343
+ # ── MKV ──────────────────────────────────────────────────────────────────
344
+
345
+ def faucet(self, wallet: str) -> dict:
346
+ """
347
+ Get 100 free test MKV. One grant per wallet, one per IP per 24h.
348
+ No API key required.
349
+
350
+ Args:
351
+ wallet: any string identifier for your wallet address
352
+
353
+ Returns dict with: ok, wallet, granted, balance, next
354
+
355
+ Example:
356
+ result = client.faucet('my-builder-wallet')
357
+ print(result['balance']) # 100
358
+ """
359
+ r = self._session.post(
360
+ f"{BASE_URL}/mkv/faucet",
361
+ json={"wallet": wallet},
362
+ timeout=self.timeout,
363
+ )
364
+ r.raise_for_status()
365
+ return r.json()
366
+
367
+ def mkv_balance(self, wallet: str) -> int:
368
+ """Current MKV balance for a wallet."""
369
+ return self._get(f"/mkv/balance/{wallet}").get("balance_mkv", 0)
370
+
371
+ # ── Watch ─────────────────────────────────────────────────────────────────
372
+
373
+ def watch(
374
+ self,
375
+ tickers: List[str],
376
+ callback: Callable,
377
+ interval: int = 60,
378
+ max_iters: Optional[int] = None,
379
+ ):
380
+ """
381
+ Poll for regime changes and call callback on state transitions.
382
+
383
+ Args:
384
+ tickers: list of tickers to monitor
385
+ callback: fn(ticker, old_regime, new_regime, observation)
386
+ interval: polling interval in seconds
387
+ max_iters: stop after N polls (None = run forever)
388
+
389
+ Example:
390
+ def on_change(ticker, old, new, obs):
391
+ print(f"{ticker}: {old} -> {new}")
392
+
393
+ client.watch(["QQQ", "SPY"], on_change)
394
+ """
395
+ tickers = [t.upper() for t in tickers]
396
+ last_state = {}
397
+ iters = 0
398
+
399
+ print(f"Watching {tickers} every {interval}s. Ctrl+C to stop.")
400
+
401
+ while True:
402
+ try:
403
+ snapshot = self.batch(tickers)
404
+ for ticker, latest in snapshot.items():
405
+ prev = last_state.get(ticker)
406
+ if prev and prev.regime != latest.regime:
407
+ callback(ticker, prev.regime, latest.regime, latest)
408
+ last_state[ticker] = latest
409
+ except Exception as e:
410
+ print(f" watch error: {e}")
411
+
412
+ iters += 1
413
+ if max_iters and iters >= max_iters:
414
+ break
415
+ time.sleep(interval)
416
+
417
+
418
+ # ── CLI ───────────────────────────────────────────────────────────────────────
419
+
420
+ def _cli():
421
+ import sys, json
422
+
423
+ if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
424
+ print(__doc__)
425
+ return
426
+
427
+ cmd = sys.argv[1]
428
+ client = MarkovianClient()
429
+
430
+ if cmd == "latest":
431
+ regimes = client.latest()
432
+ for r in regimes:
433
+ print(f" {r.ticker:<8} {r.regime:<14} {r.confidence*100:.1f}% {r.date}")
434
+
435
+ elif cmd == "regime" and len(sys.argv) >= 3:
436
+ ticker = sys.argv[2]
437
+ days = int(sys.argv[3]) if len(sys.argv) > 3 else 90
438
+ rows = client.regime(ticker, days=days)
439
+ for r in rows[-20:]:
440
+ bar = "#" * int(r.confidence * 20)
441
+ print(f" {r.date} {r.regime:<14} {r.confidence*100:.1f}% {bar}")
442
+
443
+ elif cmd == "tip":
444
+ tip = client.tip()
445
+ print(json.dumps(tip, indent=2))
446
+
447
+ elif cmd == "verify" and len(sys.argv) >= 3:
448
+ result = client.verify(sys.argv[2])
449
+ print(json.dumps(result, indent=2))
450
+
451
+ elif cmd == "ledger":
452
+ led = client.ledger()
453
+ print(json.dumps(led, indent=2))
454
+
455
+ elif cmd == "batch" and len(sys.argv) >= 3:
456
+ tickers = sys.argv[2].split(",")
457
+ data = client.batch(tickers)
458
+ for ticker, rows in data.items():
459
+ if rows:
460
+ r = rows[-1] if isinstance(rows, list) else rows
461
+ if hasattr(r, 'regime'):
462
+ print(f" {ticker:<8} {r.regime:<14} {r.confidence*100:.1f}% {r.date}")
463
+
464
+ else:
465
+ print(f"Unknown command: {cmd}")
466
+ print("Commands: latest, regime <TICKER> [days], tip, ledger, verify <root>, batch <T1,T2>")
467
+
468
+
469
+ if __name__ == "__main__":
470
+ _cli()
@@ -0,0 +1,45 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "markovian"
7
+ version = "1.0.0"
8
+ description = "Python SDK for the Markovian Protocol: verifiable market-regime data, on-chain ZK proofs, and STAMP commitment from a proof-of-intelligence Layer 1."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Markovian Protocol", email = "hello@quantsynth.net" }]
13
+ keywords = [
14
+ "markovian", "oracle", "zk", "zero-knowledge", "blockchain",
15
+ "market-regime", "defi", "prediction-markets", "markov", "provenance",
16
+ ]
17
+ dependencies = ["requests>=2.25", "numpy>=1.20", "py_ecc>=6.0"]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "Intended Audience :: Financial and Insurance Industry",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.8",
26
+ "Programming Language :: Python :: 3.9",
27
+ "Programming Language :: Python :: 3.10",
28
+ "Programming Language :: Python :: 3.11",
29
+ "Programming Language :: Python :: 3.12",
30
+ "Topic :: Office/Business :: Financial :: Investment",
31
+ "Topic :: Security :: Cryptography",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ ]
34
+
35
+ [project.urls]
36
+ Homepage = "https://markovianprotocol.com"
37
+ Documentation = "https://markovianprotocol.com/build"
38
+ Source = "https://github.com/MarkovianProtocol/markovian-protocol"
39
+ Explorer = "https://chain.quantsynth.net"
40
+
41
+ [project.scripts]
42
+ markovian = "markovian:_cli"
43
+
44
+ [tool.setuptools]
45
+ py-modules = ["markovian"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+