tickstream 0.1.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,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: tickstream
3
+ Version: 0.1.0
4
+ Summary: Realtime CME futures, options & Level 2 market data — one key, one line.
5
+ License: MIT
6
+ Project-URL: Homepage, https://tick-stream.xyz
7
+ Project-URL: Documentation, https://tick-stream.xyz/docs
8
+ Keywords: market-data,futures,options,cme,websocket,trading,ticks,level2
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: websocket-client>=1.7
12
+
13
+ # tickstream (Python)
14
+
15
+ Realtime CME futures, options & Level 2 — one key, one line.
16
+
17
+ ```bash
18
+ pip install tickstream
19
+ ```
20
+
21
+ ## Stream ticks
22
+
23
+ ```python
24
+ from tickstream import Stream
25
+
26
+ for tick in Stream("sk_live_…").subscribe("ES", "NQ"):
27
+ print(tick.symbol, tick.price, tick.size, tick.side)
28
+ ```
29
+
30
+ ## Level 2 & options (Pro plans)
31
+
32
+ ```python
33
+ from tickstream import Stream
34
+
35
+ for book in Stream("sk_live_…").book("ES"):
36
+ print(book.bids[0], book.asks[0], book.imbalance)
37
+
38
+ for o in Stream("sk_live_…").options("ES", "SPX"):
39
+ print(o.symbol, o.strike, o.right, o.iv, o.delta)
40
+ ```
41
+
42
+ ## REST: quotes, backfill, reference data
43
+
44
+ ```python
45
+ from tickstream import Client
46
+
47
+ api = Client("sk_live_…")
48
+ api.quote("ES")
49
+ api.ticks("ES", start="2025-06-01T13:30:00Z", end="2025-06-01T20:00:00Z")
50
+ api.symbols()
51
+ api.options("SPX", expiry="2026-06-19")
52
+ ```
53
+
54
+ Each message supports attribute **and** dict access (`tick.price` or `tick["price"]`).
55
+
56
+ Get your API key at <https://tick-stream.xyz>. Full docs: <https://tick-stream.xyz/docs>.
57
+ Point at a local gateway with `TICKSTREAM_WS_URL` / `TICKSTREAM_API_URL`.
@@ -0,0 +1,45 @@
1
+ # tickstream (Python)
2
+
3
+ Realtime CME futures, options & Level 2 — one key, one line.
4
+
5
+ ```bash
6
+ pip install tickstream
7
+ ```
8
+
9
+ ## Stream ticks
10
+
11
+ ```python
12
+ from tickstream import Stream
13
+
14
+ for tick in Stream("sk_live_…").subscribe("ES", "NQ"):
15
+ print(tick.symbol, tick.price, tick.size, tick.side)
16
+ ```
17
+
18
+ ## Level 2 & options (Pro plans)
19
+
20
+ ```python
21
+ from tickstream import Stream
22
+
23
+ for book in Stream("sk_live_…").book("ES"):
24
+ print(book.bids[0], book.asks[0], book.imbalance)
25
+
26
+ for o in Stream("sk_live_…").options("ES", "SPX"):
27
+ print(o.symbol, o.strike, o.right, o.iv, o.delta)
28
+ ```
29
+
30
+ ## REST: quotes, backfill, reference data
31
+
32
+ ```python
33
+ from tickstream import Client
34
+
35
+ api = Client("sk_live_…")
36
+ api.quote("ES")
37
+ api.ticks("ES", start="2025-06-01T13:30:00Z", end="2025-06-01T20:00:00Z")
38
+ api.symbols()
39
+ api.options("SPX", expiry="2026-06-19")
40
+ ```
41
+
42
+ Each message supports attribute **and** dict access (`tick.price` or `tick["price"]`).
43
+
44
+ Get your API key at <https://tick-stream.xyz>. Full docs: <https://tick-stream.xyz/docs>.
45
+ Point at a local gateway with `TICKSTREAM_WS_URL` / `TICKSTREAM_API_URL`.
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "tickstream"
3
+ version = "0.1.0"
4
+ description = "Realtime CME futures, options & Level 2 market data — one key, one line."
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ license = { text = "MIT" }
8
+ keywords = ["market-data", "futures", "options", "cme", "websocket", "trading", "ticks", "level2"]
9
+ dependencies = ["websocket-client>=1.7"]
10
+
11
+ [project.urls]
12
+ Homepage = "https://tick-stream.xyz"
13
+ Documentation = "https://tick-stream.xyz/docs"
14
+
15
+ [build-system]
16
+ requires = ["setuptools>=61"]
17
+ build-backend = "setuptools.build_meta"
18
+
19
+ [tool.setuptools]
20
+ packages = ["tickstream"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,129 @@
1
+ """tickstream — realtime CME futures, options & Level 2. One key, one line.
2
+
3
+ from tickstream import Stream
4
+ for tick in Stream("sk_live_…").subscribe("ES"):
5
+ print(tick.price, tick.size, tick.ts)
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import urllib.parse
11
+ import urllib.request
12
+
13
+ import websocket # websocket-client
14
+
15
+ __all__ = ["Stream", "Client"]
16
+ __version__ = "0.1.0"
17
+
18
+ DEFAULT_WS = "wss://stream.tick-stream.xyz/v1"
19
+ DEFAULT_API = "https://api.tick-stream.xyz/v1"
20
+ _CH_TYPE = {"ticks": "tick", "book": "book", "options": "option"}
21
+
22
+
23
+ class _Obj(dict):
24
+ """A dict that also allows attribute access: msg.price as well as msg['price']."""
25
+
26
+ def __getattr__(self, name):
27
+ try:
28
+ return self[name]
29
+ except KeyError:
30
+ return None
31
+
32
+
33
+ class Stream:
34
+ """Live market-data stream. Iterate the result of subscribe()/book()/options().
35
+
36
+ Blocking, synchronous iteration — reconnects automatically and replies to pings.
37
+ """
38
+
39
+ def __init__(self, api_key, url=None):
40
+ if not api_key:
41
+ raise ValueError("tickstream: an API key is required")
42
+ self.api_key = api_key
43
+ self.url = url or os.environ.get("TICKSTREAM_WS_URL", DEFAULT_WS)
44
+ self.plan = None
45
+
46
+ def subscribe(self, *symbols):
47
+ """Yield live trade ticks for one or more symbols."""
48
+ return self._run("ticks", symbols)
49
+
50
+ def book(self, *symbols):
51
+ """Yield Level 2 order-book snapshots (Realtime+L2 / Pro plans)."""
52
+ return self._run("book", symbols)
53
+
54
+ def options(self, *symbols):
55
+ """Yield option quotes with greeks for one or more underlyings (Pro plan)."""
56
+ return self._run("options", symbols)
57
+
58
+ def _run(self, channel, symbols):
59
+ want = _CH_TYPE[channel]
60
+ syms = [str(s).upper() for s in symbols]
61
+ sep = "&" if "?" in self.url else "?"
62
+ full = "%s%skey=%s" % (self.url, sep, urllib.parse.quote(self.api_key))
63
+ while True:
64
+ ws = websocket.create_connection(full, enable_multithread=True)
65
+ try:
66
+ ws.send(json.dumps({"op": "subscribe", "channel": channel, "symbols": syms}))
67
+ while True:
68
+ raw = ws.recv()
69
+ if not raw:
70
+ break
71
+ msg = json.loads(raw)
72
+ t = msg.get("type")
73
+ if t == "ping":
74
+ ws.send(json.dumps({"op": "pong"}))
75
+ continue
76
+ if t == "welcome":
77
+ self.plan = msg.get("plan")
78
+ continue
79
+ if t == "error":
80
+ raise RuntimeError(msg.get("error", {}).get("message", "stream error"))
81
+ if t == want:
82
+ yield _Obj(msg)
83
+ finally:
84
+ try:
85
+ ws.close()
86
+ except Exception:
87
+ pass
88
+ # connection dropped — reconnect and resubscribe
89
+
90
+
91
+ class Client:
92
+ """REST client for latest quotes, reference data and historical backfill."""
93
+
94
+ def __init__(self, api_key, base=None):
95
+ if not api_key:
96
+ raise ValueError("tickstream: an API key is required")
97
+ self.api_key = api_key
98
+ self.base = (base or os.environ.get("TICKSTREAM_API_URL", DEFAULT_API)).rstrip("/")
99
+
100
+ def _get(self, path):
101
+ req = urllib.request.Request(
102
+ self.base + path, headers={"Authorization": "Bearer " + self.api_key}
103
+ )
104
+ with urllib.request.urlopen(req) as resp:
105
+ return json.loads(resp.read().decode())
106
+
107
+ def quote(self, symbol):
108
+ return self._get("/quote?symbol=" + urllib.parse.quote(symbol.upper()))
109
+
110
+ def ticks(self, symbol, start=None, end=None, cursor=None, limit=None):
111
+ q = {"symbol": symbol.upper()}
112
+ if start:
113
+ q["start"] = start
114
+ if end:
115
+ q["end"] = end
116
+ if cursor:
117
+ q["cursor"] = cursor
118
+ if limit:
119
+ q["limit"] = limit
120
+ return self._get("/ticks?" + urllib.parse.urlencode(q))
121
+
122
+ def symbols(self):
123
+ return self._get("/symbols")
124
+
125
+ def options(self, underlying, expiry=None):
126
+ q = {"underlying": underlying.upper()}
127
+ if expiry:
128
+ q["expiry"] = expiry
129
+ return self._get("/options?" + urllib.parse.urlencode(q))
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: tickstream
3
+ Version: 0.1.0
4
+ Summary: Realtime CME futures, options & Level 2 market data — one key, one line.
5
+ License: MIT
6
+ Project-URL: Homepage, https://tick-stream.xyz
7
+ Project-URL: Documentation, https://tick-stream.xyz/docs
8
+ Keywords: market-data,futures,options,cme,websocket,trading,ticks,level2
9
+ Requires-Python: >=3.8
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: websocket-client>=1.7
12
+
13
+ # tickstream (Python)
14
+
15
+ Realtime CME futures, options & Level 2 — one key, one line.
16
+
17
+ ```bash
18
+ pip install tickstream
19
+ ```
20
+
21
+ ## Stream ticks
22
+
23
+ ```python
24
+ from tickstream import Stream
25
+
26
+ for tick in Stream("sk_live_…").subscribe("ES", "NQ"):
27
+ print(tick.symbol, tick.price, tick.size, tick.side)
28
+ ```
29
+
30
+ ## Level 2 & options (Pro plans)
31
+
32
+ ```python
33
+ from tickstream import Stream
34
+
35
+ for book in Stream("sk_live_…").book("ES"):
36
+ print(book.bids[0], book.asks[0], book.imbalance)
37
+
38
+ for o in Stream("sk_live_…").options("ES", "SPX"):
39
+ print(o.symbol, o.strike, o.right, o.iv, o.delta)
40
+ ```
41
+
42
+ ## REST: quotes, backfill, reference data
43
+
44
+ ```python
45
+ from tickstream import Client
46
+
47
+ api = Client("sk_live_…")
48
+ api.quote("ES")
49
+ api.ticks("ES", start="2025-06-01T13:30:00Z", end="2025-06-01T20:00:00Z")
50
+ api.symbols()
51
+ api.options("SPX", expiry="2026-06-19")
52
+ ```
53
+
54
+ Each message supports attribute **and** dict access (`tick.price` or `tick["price"]`).
55
+
56
+ Get your API key at <https://tick-stream.xyz>. Full docs: <https://tick-stream.xyz/docs>.
57
+ Point at a local gateway with `TICKSTREAM_WS_URL` / `TICKSTREAM_API_URL`.
@@ -0,0 +1,8 @@
1
+ README.md
2
+ pyproject.toml
3
+ tickstream/__init__.py
4
+ tickstream.egg-info/PKG-INFO
5
+ tickstream.egg-info/SOURCES.txt
6
+ tickstream.egg-info/dependency_links.txt
7
+ tickstream.egg-info/requires.txt
8
+ tickstream.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ websocket-client>=1.7
@@ -0,0 +1 @@
1
+ tickstream