outcometick 1.4.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 Ligengxin
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,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: outcometick
3
+ Version: 1.4.0
4
+ Summary: Strategy SDK for outcometick prediction-market backtests
5
+ License: MIT
6
+ Project-URL: Documentation, https://outcometick.com/docs/sdk
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ <!--
13
+ GENERATED — do not edit this repository directly.
14
+
15
+ Every file here is built from the outcometick monorepo by
16
+ scripts/publish-sdk-repos.mjs and overwritten wholesale on each publish.
17
+ An edit made here survives until the next publish and then disappears.
18
+
19
+ Generated from monorepo revision 27a03218dafadd78d846fb906e64e2981aa6f51c.
20
+ -->
21
+
22
+ # outcometick
23
+
24
+ The Python strategy SDK for [outcometick.com](https://outcometick.com).
25
+
26
+ ```python
27
+ from outcometick import Strategy, Order
28
+
29
+
30
+ class MeanReversion(Strategy):
31
+ def on_market_open(self, ctx, market):
32
+ self.entered = False
33
+
34
+ def on_tick(self, ctx, tick):
35
+ z = ctx.zscore(tick.value, window=180)
36
+ if self.entered or abs(z) < ctx.p.entry_z:
37
+ return None
38
+ side = "DOWN" if z > 0 else "UP"
39
+ book = ctx.book()
40
+ self.entered = True
41
+ return Order(side=side, size=ctx.p.size, limit=book.best(side))
42
+ ```
43
+
44
+ This package is the SDK surface: the `Strategy` base class and the `Order`
45
+ value object, so your editor and type checker know the API and your own tests
46
+ can import it.
47
+
48
+ **The `ot` command line is distributed via npm**, not here:
49
+
50
+ ```
51
+ npm i -g outcometick
52
+ ```
53
+
54
+ It runs Python strategies by spawning your local `python3`. Shipping one CLI
55
+ rather than two is deliberate — `ot check` must be the same validator the queue
56
+ runs, and a second implementation in another language would be the first thing
57
+ to drift.
58
+
59
+ Full reference: https://outcometick.com/docs/sdk
@@ -0,0 +1,48 @@
1
+ <!--
2
+ GENERATED — do not edit this repository directly.
3
+
4
+ Every file here is built from the outcometick monorepo by
5
+ scripts/publish-sdk-repos.mjs and overwritten wholesale on each publish.
6
+ An edit made here survives until the next publish and then disappears.
7
+
8
+ Generated from monorepo revision 27a03218dafadd78d846fb906e64e2981aa6f51c.
9
+ -->
10
+
11
+ # outcometick
12
+
13
+ The Python strategy SDK for [outcometick.com](https://outcometick.com).
14
+
15
+ ```python
16
+ from outcometick import Strategy, Order
17
+
18
+
19
+ class MeanReversion(Strategy):
20
+ def on_market_open(self, ctx, market):
21
+ self.entered = False
22
+
23
+ def on_tick(self, ctx, tick):
24
+ z = ctx.zscore(tick.value, window=180)
25
+ if self.entered or abs(z) < ctx.p.entry_z:
26
+ return None
27
+ side = "DOWN" if z > 0 else "UP"
28
+ book = ctx.book()
29
+ self.entered = True
30
+ return Order(side=side, size=ctx.p.size, limit=book.best(side))
31
+ ```
32
+
33
+ This package is the SDK surface: the `Strategy` base class and the `Order`
34
+ value object, so your editor and type checker know the API and your own tests
35
+ can import it.
36
+
37
+ **The `ot` command line is distributed via npm**, not here:
38
+
39
+ ```
40
+ npm i -g outcometick
41
+ ```
42
+
43
+ It runs Python strategies by spawning your local `python3`. Shipping one CLI
44
+ rather than two is deliberate — `ot check` must be the same validator the queue
45
+ runs, and a second implementation in another language would be the first thing
46
+ to drift.
47
+
48
+ Full reference: https://outcometick.com/docs/sdk
@@ -0,0 +1,67 @@
1
+ """The SDK surface a submitted Python strategy imports.
2
+
3
+ Deliberately tiny. `Strategy` is a base class that exists so `entry` can be
4
+ checked against something, and `Order` is a value object. Everything a strategy
5
+ can actually DO arrives through `ctx`, which the runner constructs — there is no
6
+ way to reach the outside from here, because there is nothing here to reach it
7
+ with.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ # Named explicitly so `from __future__ import annotations` does not leak
13
+ # `annotations` into the package's public surface — this module is also
14
+ # published to PyPI, where dir(outcometick) is what a user reads as the API.
15
+ __all__ = ("Strategy", "Order", "SIDES")
16
+
17
+ SIDES = ("UP", "DOWN")
18
+
19
+
20
+ class Strategy:
21
+ """Base class for a submitted strategy.
22
+
23
+ The hooks are not defined here on purpose. A default no-op `on_tick` would
24
+ turn "you declared a hook you did not implement" — a rejection the submitter
25
+ can fix in seconds — into a run that quietly never trades and bills for an
26
+ empty equity curve.
27
+ """
28
+
29
+ #: Params from the manifest, injected by the runner before the first hook.
30
+ p: dict = {}
31
+
32
+
33
+ class Order:
34
+ """An order a hook returns. Never sent — returned, and matched by the runner
35
+ against the depth that was actually resting at that millisecond.
36
+
37
+ `limit` is a bound in whichever direction protects you: a ceiling when
38
+ opening, a floor when reducing.
39
+ """
40
+
41
+ __slots__ = ("side", "size", "limit", "hold_s", "reduce_only", "tif", "tag")
42
+
43
+ def __init__(self, side, size, limit=None, hold_s=None, reduce_only=False,
44
+ tif="ioc", tag=None):
45
+ if side not in SIDES:
46
+ raise ValueError(f'side must be "UP" or "DOWN", got {side!r}')
47
+ if not (isinstance(size, (int, float)) and size > 0):
48
+ raise ValueError(f"size must be a positive number, got {size!r}")
49
+ if limit is not None and not (0 <= float(limit) <= 1):
50
+ # A binary outcome token trades between 0 and 1. A limit outside
51
+ # that is not a price, and silently clamping it would fill an order
52
+ # the strategy never asked for.
53
+ raise ValueError(f"limit must be between 0 and 1, got {limit!r}")
54
+ if tif != "ioc":
55
+ # Not modelled, so not accepted. See "Not supported yet" in the docs.
56
+ raise ValueError(f'tif must be "ioc"; {tif!r} is not supported yet')
57
+ self.side = side
58
+ self.size = float(size)
59
+ self.limit = None if limit is None else float(limit)
60
+ self.hold_s = None if hold_s is None else int(hold_s)
61
+ self.reduce_only = bool(reduce_only)
62
+ self.tif = tif
63
+ self.tag = tag
64
+
65
+ def __repr__(self) -> str:
66
+ return (f"Order(side={self.side!r}, size={self.size}, limit={self.limit}, "
67
+ f"reduce_only={self.reduce_only})")
File without changes
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.4
2
+ Name: outcometick
3
+ Version: 1.4.0
4
+ Summary: Strategy SDK for outcometick prediction-market backtests
5
+ License: MIT
6
+ Project-URL: Documentation, https://outcometick.com/docs/sdk
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ <!--
13
+ GENERATED — do not edit this repository directly.
14
+
15
+ Every file here is built from the outcometick monorepo by
16
+ scripts/publish-sdk-repos.mjs and overwritten wholesale on each publish.
17
+ An edit made here survives until the next publish and then disappears.
18
+
19
+ Generated from monorepo revision 27a03218dafadd78d846fb906e64e2981aa6f51c.
20
+ -->
21
+
22
+ # outcometick
23
+
24
+ The Python strategy SDK for [outcometick.com](https://outcometick.com).
25
+
26
+ ```python
27
+ from outcometick import Strategy, Order
28
+
29
+
30
+ class MeanReversion(Strategy):
31
+ def on_market_open(self, ctx, market):
32
+ self.entered = False
33
+
34
+ def on_tick(self, ctx, tick):
35
+ z = ctx.zscore(tick.value, window=180)
36
+ if self.entered or abs(z) < ctx.p.entry_z:
37
+ return None
38
+ side = "DOWN" if z > 0 else "UP"
39
+ book = ctx.book()
40
+ self.entered = True
41
+ return Order(side=side, size=ctx.p.size, limit=book.best(side))
42
+ ```
43
+
44
+ This package is the SDK surface: the `Strategy` base class and the `Order`
45
+ value object, so your editor and type checker know the API and your own tests
46
+ can import it.
47
+
48
+ **The `ot` command line is distributed via npm**, not here:
49
+
50
+ ```
51
+ npm i -g outcometick
52
+ ```
53
+
54
+ It runs Python strategies by spawning your local `python3`. Shipping one CLI
55
+ rather than two is deliberate — `ot check` must be the same validator the queue
56
+ runs, and a second implementation in another language would be the first thing
57
+ to drift.
58
+
59
+ Full reference: https://outcometick.com/docs/sdk
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ outcometick/__init__.py
5
+ outcometick/py.typed
6
+ outcometick.egg-info/PKG-INFO
7
+ outcometick.egg-info/SOURCES.txt
8
+ outcometick.egg-info/dependency_links.txt
9
+ outcometick.egg-info/top_level.txt
10
+ tests/test_sdk.py
@@ -0,0 +1 @@
1
+ outcometick
@@ -0,0 +1,20 @@
1
+ [project]
2
+ name = "outcometick"
3
+ version = "1.4.0"
4
+ description = "Strategy SDK for outcometick prediction-market backtests"
5
+ readme = "README.md"
6
+ requires-python = ">=3.14"
7
+ license = { text = "MIT" }
8
+
9
+ [project.urls]
10
+ Documentation = "https://outcometick.com/docs/sdk"
11
+
12
+ [build-system]
13
+ requires = ["setuptools>=68"]
14
+ build-backend = "setuptools.build_meta"
15
+
16
+ [tool.setuptools]
17
+ packages = ["outcometick"]
18
+
19
+ [tool.setuptools.package-data]
20
+ outcometick = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,71 @@
1
+ """Tests for the public Python SDK surface.
2
+
3
+ Small on purpose: this package is the SDK surface and nothing else, so these
4
+ assert the contract a strategy author actually depends on — the names that
5
+ exist, and the argument validation that rejects a bad order at construction
6
+ rather than silently at fill time.
7
+
8
+ They ship with the published package so the mirror is a repository someone can
9
+ clone and verify rather than take on faith.
10
+ """
11
+
12
+ import unittest
13
+
14
+ from outcometick import Order, Strategy, SIDES
15
+
16
+
17
+ class TestSurface(unittest.TestCase):
18
+ def test_exports_are_exactly_what_is_documented(self):
19
+ import outcometick
20
+
21
+ self.assertEqual(sorted(outcometick.__all__), ["Order", "SIDES", "Strategy"])
22
+ # __all__ also keeps `annotations` from `from __future__ import` out of
23
+ # the public namespace, which is what a reader sees in dir().
24
+ self.assertNotIn("annotations", outcometick.__all__)
25
+
26
+ def test_sides_are_the_two_outcome_tokens(self):
27
+ self.assertEqual(tuple(SIDES), ("UP", "DOWN"))
28
+
29
+
30
+ class TestOrder(unittest.TestCase):
31
+ def test_accepts_a_well_formed_order(self):
32
+ o = Order(side="UP", size=100, limit=0.55)
33
+ self.assertEqual(o.side, "UP")
34
+ self.assertEqual(o.size, 100.0)
35
+ self.assertEqual(o.limit, 0.55)
36
+ self.assertFalse(o.reduce_only)
37
+
38
+ def test_limit_is_optional(self):
39
+ self.assertIsNone(Order(side="DOWN", size=1).limit)
40
+
41
+ def test_rejects_a_side_that_is_not_an_outcome_token(self):
42
+ with self.assertRaises(ValueError):
43
+ Order(side="SIDEWAYS", size=100)
44
+
45
+ def test_rejects_a_non_positive_size(self):
46
+ for bad in (0, -1):
47
+ with self.assertRaises(ValueError):
48
+ Order(side="UP", size=bad)
49
+
50
+ def test_rejects_a_limit_outside_zero_to_one(self):
51
+ # A binary outcome token trades between 0 and 1. Anything else is not a
52
+ # price, and clamping it would fill an order nobody asked for.
53
+ for bad in (-0.01, 1.5, 100):
54
+ with self.assertRaises(ValueError):
55
+ Order(side="UP", size=1, limit=bad)
56
+
57
+
58
+ class TestStrategy(unittest.TestCase):
59
+ def test_params_default_to_empty_so_a_hook_can_read_them(self):
60
+ self.assertEqual(Strategy().p, {})
61
+
62
+ def test_hooks_are_not_defined_by_the_base_class(self):
63
+ # Deliberate: a default no-op on_tick would turn "you declared a hook you
64
+ # did not implement" — fixable in seconds — into a run that quietly never
65
+ # trades and bills for an empty equity curve.
66
+ for hook in ("on_tick", "on_market_open", "on_book", "on_trade", "on_settle"):
67
+ self.assertFalse(hasattr(Strategy, hook), hook)
68
+
69
+
70
+ if __name__ == "__main__":
71
+ unittest.main()