marina-rules 2026.10.26.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. marina_rules-2026.10.26.1/LICENSE +21 -0
  2. marina_rules-2026.10.26.1/LICENSE-DATA +121 -0
  3. marina_rules-2026.10.26.1/PKG-INFO +203 -0
  4. marina_rules-2026.10.26.1/README.md +184 -0
  5. marina_rules-2026.10.26.1/pyproject.toml +39 -0
  6. marina_rules-2026.10.26.1/setup.cfg +4 -0
  7. marina_rules-2026.10.26.1/src/marina_rules/__init__.py +400 -0
  8. marina_rules-2026.10.26.1/src/marina_rules/check.py +119 -0
  9. marina_rules-2026.10.26.1/src/marina_rules/contract.py +225 -0
  10. marina_rules-2026.10.26.1/src/marina_rules/data/cn/equity_fees.yaml +139 -0
  11. marina_rules-2026.10.26.1/src/marina_rules/data/cn/equity_price_limits.yaml +1128 -0
  12. marina_rules-2026.10.26.1/src/marina_rules/data/cn/known_gaps.yaml +93 -0
  13. marina_rules-2026.10.26.1/src/marina_rules/data/cn/scopes.yaml +92 -0
  14. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sh.600519.csv +1888 -0
  15. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sh.603375.csv +658 -0
  16. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sh.688981.csv +1510 -0
  17. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sz.000001.csv +1889 -0
  18. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sz.300001.csv +1889 -0
  19. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sz.300059.csv +1889 -0
  20. marina_rules-2026.10.26.1/src/marina_rules/data/samples/cn_bars/sz.301499.csv +784 -0
  21. marina_rules-2026.10.26.1/src/marina_rules/diff.py +298 -0
  22. marina_rules-2026.10.26.1/src/marina_rules/sourcehash.py +49 -0
  23. marina_rules-2026.10.26.1/src/marina_rules.egg-info/PKG-INFO +203 -0
  24. marina_rules-2026.10.26.1/src/marina_rules.egg-info/SOURCES.txt +31 -0
  25. marina_rules-2026.10.26.1/src/marina_rules.egg-info/dependency_links.txt +1 -0
  26. marina_rules-2026.10.26.1/src/marina_rules.egg-info/entry_points.txt +2 -0
  27. marina_rules-2026.10.26.1/src/marina_rules.egg-info/requires.txt +1 -0
  28. marina_rules-2026.10.26.1/src/marina_rules.egg-info/top_level.txt +1 -0
  29. marina_rules-2026.10.26.1/tests/test_check.py +28 -0
  30. marina_rules-2026.10.26.1/tests/test_conformance.py +20 -0
  31. marina_rules-2026.10.26.1/tests/test_contract.py +67 -0
  32. marina_rules-2026.10.26.1/tests/test_diff.py +91 -0
  33. marina_rules-2026.10.26.1/tests/test_rules.py +234 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Li and Marina contributors
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,121 @@
1
+ Creative Commons Legal Code
2
+
3
+ CC0 1.0 Universal
4
+
5
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12
+ HEREUNDER.
13
+
14
+ Statement of Purpose
15
+
16
+ The laws of most jurisdictions throughout the world automatically confer
17
+ exclusive Copyright and Related Rights (defined below) upon the creator
18
+ and subsequent owner(s) (each and all, an "owner") of an original work of
19
+ authorship and/or a database (each, a "Work").
20
+
21
+ Certain owners wish to permanently relinquish those rights to a Work for
22
+ the purpose of contributing to a commons of creative, cultural and
23
+ scientific works ("Commons") that the public can reliably and without fear
24
+ of later claims of infringement build upon, modify, incorporate in other
25
+ works, reuse and redistribute as freely as possible in any form whatsoever
26
+ and for any purposes, including without limitation commercial purposes.
27
+ These owners may contribute to the Commons to promote the ideal of a free
28
+ culture and the further production of creative, cultural and scientific
29
+ works, or to gain reputation or greater distribution for their Work in
30
+ part through the use and efforts of others.
31
+
32
+ For these and/or other purposes and motivations, and without any
33
+ expectation of additional consideration or compensation, the person
34
+ associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35
+ is an owner of Copyright and Related Rights in the Work, voluntarily
36
+ elects to apply CC0 to the Work and publicly distribute the Work under its
37
+ terms, with knowledge of his or her Copyright and Related Rights in the
38
+ Work and the meaning and intended legal effect of CC0 on those rights.
39
+
40
+ 1. Copyright and Related Rights. A Work made available under CC0 may be
41
+ protected by copyright and related or neighboring rights ("Copyright and
42
+ Related Rights"). Copyright and Related Rights include, but are not
43
+ limited to, the following:
44
+
45
+ i. the right to reproduce, adapt, distribute, perform, display,
46
+ communicate, and translate a Work;
47
+ ii. moral rights retained by the original author(s) and/or performer(s);
48
+ iii. publicity and privacy rights pertaining to a person's image or
49
+ likeness depicted in a Work;
50
+ iv. rights protecting against unfair competition in regards to a Work,
51
+ subject to the limitations in paragraph 4(a), below;
52
+ v. rights protecting the extraction, dissemination, use and reuse of data
53
+ in a Work;
54
+ vi. database rights (such as those arising under Directive 96/9/EC of the
55
+ European Parliament and of the Council of 11 March 1996 on the legal
56
+ protection of databases, and under any national implementation
57
+ thereof, including any amended or successor version of such
58
+ directive); and
59
+ vii. other similar, equivalent or corresponding rights throughout the
60
+ world based on applicable law or treaty, and any national
61
+ implementations thereof.
62
+
63
+ 2. Waiver. To the greatest extent permitted by, but not in contravention
64
+ of, applicable law, Affirmer hereby overtly, fully, permanently,
65
+ irrevocably and unconditionally waives, abandons, and surrenders all of
66
+ Affirmer's Copyright and Related Rights and associated claims and causes
67
+ of action, whether now known or unknown (including existing as well as
68
+ future claims and causes of action), in the Work (i) in all territories
69
+ worldwide, (ii) for the maximum duration provided by applicable law or
70
+ treaty (including future time extensions), (iii) in any current or future
71
+ medium and for any number of copies, and (iv) for any purpose whatsoever,
72
+ including without limitation commercial, advertising or promotional
73
+ purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74
+ member of the public at large and to the detriment of Affirmer's heirs and
75
+ successors, fully intending that such Waiver shall not be subject to
76
+ revocation, rescission, cancellation, termination, or any other legal or
77
+ equitable action to disrupt the quiet enjoyment of the Work by the public
78
+ as contemplated by Affirmer's express Statement of Purpose.
79
+
80
+ 3. Public License Fallback. Should any part of the Waiver for any reason
81
+ be judged legally invalid or ineffective under applicable law, then the
82
+ Waiver shall be preserved to the maximum extent permitted taking into
83
+ account Affirmer's express Statement of Purpose. In addition, to the
84
+ extent the Waiver is so judged Affirmer hereby grants to each affected
85
+ person a royalty-free, non transferable, non sublicensable, non exclusive,
86
+ irrevocable and unconditional license to exercise Affirmer's Copyright and
87
+ Related Rights in the Work (i) in all territories worldwide, (ii) for the
88
+ maximum duration provided by applicable law or treaty (including future
89
+ time extensions), (iii) in any current or future medium and for any number
90
+ of copies, and (iv) for any purpose whatsoever, including without
91
+ limitation commercial, advertising or promotional purposes (the
92
+ "License"). The License shall be deemed effective as of the date CC0 was
93
+ applied by Affirmer to the Work. Should any part of the License for any
94
+ reason be judged legally invalid or ineffective under applicable law, such
95
+ partial invalidity or ineffectiveness shall not invalidate the remainder
96
+ of the License, and in such case Affirmer hereby affirms that he or she
97
+ will not (i) exercise any of his or her remaining Copyright and Related
98
+ Rights in the Work or (ii) assert any associated claims and causes of
99
+ action with respect to the Work, in either case contrary to Affirmer's
100
+ express Statement of Purpose.
101
+
102
+ 4. Limitations and Disclaimers.
103
+
104
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
105
+ surrendered, licensed or otherwise affected by this document.
106
+ b. Affirmer offers the Work as-is and makes no representations or
107
+ warranties of any kind concerning the Work, express, implied,
108
+ statutory or otherwise, including without limitation warranties of
109
+ title, merchantability, fitness for a particular purpose, non
110
+ infringement, or the absence of latent or other defects, accuracy, or
111
+ the present or absence of errors, whether or not discoverable, all to
112
+ the greatest extent permissible under applicable law.
113
+ c. Affirmer disclaims responsibility for clearing rights of other persons
114
+ that may apply to the Work or any use thereof, including without
115
+ limitation any person's Copyright and Related Rights in the Work.
116
+ Further, Affirmer disclaims responsibility for obtaining any necessary
117
+ consents, permissions or other rights required for any use of the
118
+ Work.
119
+ d. Affirmer understands and acknowledges that Creative Commons is not a
120
+ party to this document and has no duty or obligation with respect to
121
+ this CC0 or use of the Work.
@@ -0,0 +1,203 @@
1
+ Metadata-Version: 2.4
2
+ Name: marina-rules
3
+ Version: 2026.10.26.1
4
+ Summary: Point-in-time trading rules for financial markets, as data. Price limits, fees, sessions, settlement — every record cites its source.
5
+ Author: David Li
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/david-lihangyuan/marina-rules
8
+ Project-URL: Issues, https://github.com/david-lihangyuan/marina-rules/issues
9
+ Keywords: trading,backtest,exchange-rules,price-limit,fees,point-in-time,a-share
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Office/Business :: Financial :: Investment
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ License-File: LICENSE-DATA
17
+ Requires-Dist: pyyaml>=6
18
+ Dynamic: license-file
19
+
20
+ # Marina
21
+
22
+ **Point-in-time trading rules for financial markets, as data.**
23
+
24
+ Price limits, fees, trading sessions, settlement — every record says *when* it took effect and *where* it was published. Any backtest or trading agent can cite the exact rules it ran under, and anyone can re-run it against byte-identical rules.
25
+
26
+ > **中文** · 市场交易规则的「tzdata」:涨跌幅、费用、tick 等制度**从哪天起、依据什么**,每条记录带官方原文、引文和内容哈希。给回测引擎和 AI Agent 用——查不到就报错,不猜;`coverage()` 预检整段窗口,`resolve()` 返回统一判定,`diff_rules` 告诉你升级后哪些回测要重跑。目前覆盖 A 股(含科创/创业/北交所、ST、IPO 窗口、ETF、可转债、B 股)。CC0 数据 + MIT 代码。
27
+
28
+ ```
29
+ rules: marina-rules@2026.10.26.1 sha256:1bf8eb29d05157c6
30
+ ```
31
+
32
+ Marina is to trading rules what the [tz database](https://www.iana.org/time-zones) is to time zones and what [exchange_calendars](https://github.com/gerrymanoim/exchange_calendars) is to trading days: small, boring, free, and meant to be the one copy everyone imports instead of the thousand copies everyone gets slightly wrong.
33
+
34
+ > **Status: 2026.10.26.** China A-share price limits and transaction fees — **39 records, 36 `verified` + 3 `single-source`** against primary sources (exchange / ministry / depository notices, with quotes and page hashes). Other markets are not yet covered; see [Coverage](#coverage). Treat the exchange's own notice as authoritative, always.
35
+
36
+ ## Why
37
+
38
+ Every backtest engine, every trading agent, and every research notebook re-implements the same handful of rules — "ChiNext limit is 20%" — and a large share of them get the *date* wrong (it was 10% before 2020-08-24), the *scope* wrong (ST stocks differ), or the *rounding* wrong (exchanges round half-up; `round()` doesn't). These errors used to live in one firm's code. Now that AI agents write backtests by the thousand, the same error gets copied by the thousand.
39
+
40
+ A shared, cited, versioned table fixes this the way tzdata fixed time zones: not by being authoritative, but by being **the same for everyone** and **traceable to the source**.
41
+
42
+ ## Use
43
+
44
+ ```bash
45
+ pip install marina-rules # self-contained: the dataset ships inside the wheel, byte-pinned
46
+ # or take the JSON / SQLite dump attached to each GitHub Release — same bytes, same digest
47
+ ```
48
+
49
+ ```python
50
+ import marina_rules as mr
51
+
52
+ mr.rule_at("stamp_duty_sell", "2023-08-28") # 0.0005
53
+ mr.rule_at("price_limit_pct", "2020-08-23", "chinext", "XSHE") # 0.10
54
+ mr.limit_price(10.00, "chinext", "2021-03-01", "XSHE") # (12.0, 8.0) — exchange rounding
55
+ mr.limit_price(10.00, "ipo_first5_main", "2024-01-30", "XSHG+XSHE") # None — unlimited band
56
+ mr.rule_at("stamp_duty_sell", "2005-01-01") # KeyError — no defaults, ever
57
+ mr.citation() # 'rules: marina-rules@2026.10.25 sha256:…'
58
+
59
+ # Pre-flight a whole backtest window before running it:
60
+ mr.coverage("XSHE", "chinext", ["price_limit_pct"], "2019-01-01", "2026-10-19")
61
+ # {'status': 'complete', 'gaps': [], 'conflicts': [], 'verification': 'verified',
62
+ # 'required_context': ['board', 'st_status', 'listing_day_index'], 'excludes': [...], ...}
63
+ ```
64
+
65
+ ```bash
66
+ python -m marina_rules.diff 2026.10.24b 2026.10.25b # what changed, and what must be re-run
67
+ ```
68
+
69
+ `coverage()` answers "can Marina serve every (rule, day) in this window for this scope?" — `complete`,
70
+ `partial` (with the exact gaps), or `unsupported`. It also tells you what you must already know about
71
+ each security to select the scope legitimately (`required_context`): Marina holds rules, never a
72
+ security's ST or listing history, and it will not guess them for you. Gaps carry `known_gap_ids`
73
+ when they match [`data/cn/known_gaps.yaml`](data/cn/known_gaps.yaml) — the list of things Marina
74
+ knows it does not cover (delisting period, T+1, sessions, REITs, pre-2022 convertibles…), each with
75
+ a reason and a status, so "unsupported" can be cited rather than worked around. Agents: read [SKILL.md](SKILL.md).
76
+
77
+ The data is plain YAML under [`data/`](data/). You do not need the Python package; read the files from any language.
78
+
79
+ ### Cite it
80
+
81
+ Put `mr.citation()` in every result you publish. A result without it cannot be reproduced; a result with it can be re-run by anyone against the same bytes.
82
+
83
+ ### Check a fill log
84
+
85
+ ```bash
86
+ python -m marina_rules.check fills.csv [--strict]
87
+ ```
88
+
89
+ Verifies each fill against the rules in force that day — no buys above limit-up, no sells below limit-down, fees not below the statutory floor — and emits a `rules-verified@<version>:<hash>` stamp when clean. The stamp is recomputable by anyone holding the same log and the same rules version.
90
+
91
+ **What the stamp does not mean:** it says nothing about look-ahead bias, survivorship, capacity, or statistical validity. It says only that the fills respected the rules in this dataset. Name it accordingly.
92
+
93
+ ### Audit the rules against your own data
94
+
95
+ ```bash
96
+ python scripts/audit_bars.py /path/to/daily_bars --complete-from-listing
97
+ ```
98
+
99
+ A price-limit rule is falsifiable: after it takes effect, no close may move beyond the band. This script counts violations per rule. A violation means the rule is wrong or your data is — either way you want to know. Marina was first audited this way against 9.1 million A-share bars; see [`docs/audit-2026-10-24.md`](docs/audit-2026-10-24.md). The primary-source audit that followed, and the four corrections it produced, is in [`docs/sources-audit-2026-10-24.md`](docs/sources-audit-2026-10-24.md).
100
+
101
+ ## Coverage
102
+
103
+ | Market | Price limits | Fees | Sessions / auctions | Tick & lot | Settlement | Margin |
104
+ |---|---|---|---|---|---|---|
105
+ | China A-share (XSHG, XSHE, BJSE) | ✅ 27 records (incl. B-share, ETF, convertible) | ✅ 6 records | — | — | — | n/a |
106
+ | China futures | — | — | — | — | — | — |
107
+ | US equities | — | — | — | — | — | — |
108
+ | Crypto | — | — | — | — | — | — |
109
+
110
+ Blank cells are blank on purpose. A missing rule raises `KeyError`; it does not return a guess.
111
+
112
+ ## Scope — what Marina will and will not contain
113
+
114
+ **In:** rules that change *whether an order fills, at what price, or at what cost* — price limits, trading hours and auction windows, tick size and lot size, price cages, T+N settlement, statutory fees, exchange margin requirements, trading calendars (delegated to `exchange_calendars`).
115
+
116
+ **Out, permanently:** constituent lists, ST/suspension lists, borrow lists, prices, adjustment factors, anything with an owner or a license. Those are *data*; Marina holds *rules*. This boundary is what lets tzdata survive forty years without a legal department, and we are not going to be cleverer than tzdata.
117
+
118
+ ## What the dates mean (and don't)
119
+
120
+ Each record carries three dates: `effective_from` (when the rule applied), `published_at` (when the
121
+ official notice was issued — `null` when only later restatements were located), and `recorded_at`
122
+ (when the record entered Marina). **`recorded_at` is not "known at".** Marina was assembled after the
123
+ fact; it does not by itself let you prove that a backtest avoided look-ahead on rule changes. If that
124
+ matters, use `published_at` and treat records with `published_at: null` as unknown-at-the-time.
125
+
126
+ ## One answer shape for every language: the decision contract
127
+
128
+ ```bash
129
+ python -m marina_rules.contract price_limit_pct 2024-06-14 st_main XSHE --context board=main_board,st_status=true
130
+ python -m marina_rules.contract --schema # JSON Schema of the answer
131
+ python -m marina_rules.contract --capabilities # what can be asked, generated from the data
132
+ ```
133
+
134
+ `resolve()` returns one dict with `status ∈ {DETERMINED, MISSING_CONTEXT, OUT_OF_COVERAGE, CONFLICT,
135
+ UNVERIFIED}`, a `result` only when determined, the record and evidence refs, `known_gap_ids`, the
136
+ ruleset version and hash, and a `reason` in plain words. It is the boundary other languages, agents
137
+ and logs should see; the Python exceptions remain for in-process use.
138
+
139
+ ## Upgrading: `diff_rules`
140
+
141
+ `python -m marina_rules.diff <from> <to>` lists every change between two tags (or directories) and
142
+ classifies each as **correction** (Marina was wrong about the past — results computed under the old
143
+ version on that interval were computed with a wrong rule), **regime_change** (the rule itself changed
144
+ on that date), **coverage_added**, **rename**, **bookkeeping** or **metadata**. It ends with the
145
+ `(market, scope, rule, interval)` list you must re-run. Both versions are pinned by dataset digest.
146
+ Marina does not estimate P&L impact; that is what the interval is for.
147
+
148
+ ## Conformance vectors — check *your* implementation, in any language
149
+
150
+ [`conformance/vectors/`](conformance/) holds 88 input→expected pairs whose expected values come from
151
+ the exchange texts and hand arithmetic quoted in each vector's `oracle`, not from running Marina.
152
+ Regime switches one day before and on the day, ST vs non-ST, IPO windows, both exchanges' one-tick
153
+ floors, funds, convertibles, fees, missing and contradictory context, known gaps. Marina itself is
154
+ tested against them in CI; the first run caught a real defect. `python conformance/run.py`.
155
+
156
+ ## Errata
157
+
158
+ When data contradicts a rule, so far the data has been wrong every time. [`errata/`](errata/) records each case with a sample and a one-command detector you can run on your own bars (stale pct-change columns; sub-¥1 adjusted-price rounding). Contributions welcome under the same bar: sample + detector + the fact that settles it.
159
+
160
+ ## Verification levels
161
+
162
+ Every record carries one of three levels, in the data, in the open:
163
+
164
+ | level | meaning |
165
+ |---|---|
166
+ | `verified` | ≥1 official primary source (exchange / ministry / depository) states this value and date, **or** ≥2 independent authoritative sources agree. Every source carries a verbatim `quote` and a content hash of the fetched document (text for HTML, bytes for PDF) — mandatory, enforced at load time |
167
+ | `single-source` | one source, or an official source where date or value had to be inferred |
168
+ | `unverified` | secondary sources only, or sources disagree (the disagreement is recorded in `notes`) |
169
+
170
+ `strict=True` in the API refuses `unverified` records. Release tags include all levels; the level is part of the record, not hidden in a changelog.
171
+
172
+ ## Contributing
173
+
174
+ Yes, please — this only works as a commons. One record per pull request, with the primary source linked. CI checks the schema, the sources, interval overlap, and runs the audit on the sample bars. See [CONTRIBUTING.md](CONTRIBUTING.md) and [GOVERNANCE.md](GOVERNANCE.md).
175
+
176
+ ## Acknowledgements
177
+
178
+ The China A-share timeline was cross-checked against the rule histories compiled in the MIT-licensed PyPI package [`data-backtest-live-common`](https://pypi.org/project/data-backtest-live-common/) (2026-09), whose anonymous author did careful work on the fee and limit chronology, and against [`exchange_calendars`](https://github.com/gerrymanoim/exchange_calendars) for calendar conventions. If you are the dbl_common maintainer: please open an issue; this table should have your name on it.
179
+
180
+ ## License
181
+
182
+ Data (`data/`, `docs/`): [CC0 1.0](LICENSE-DATA) — no rights reserved.
183
+ Code (`src/`, `scripts/`, `tests/`): [MIT](LICENSE).
184
+
185
+ ## About the name
186
+
187
+ Marina is named after my wife, Marina. This project was created and first published on **24 October 2026**, her birthday.
188
+
189
+ A rules table is an unglamorous thing: a few hundred lines of dates, percentages, and citations. But every one of those lines is a mistake that someone, somewhere, no longer has to make. If this table spares even a few people a wrong number, a bad trade, or a wasted week, then it has done what I hoped it would do on the day it was named — bring a little more good into the world, in her name.
190
+
191
+ It is free for everyone, forever. No rights reserved.
192
+
193
+ — David Li, 24 October 2026
194
+
195
+ ---
196
+
197
+ **关于名字**
198
+
199
+ Marina 以我爱人 Marina 的名字命名。这个项目建立并首次发布于 **2026 年 10 月 24 日**,她的生日。
200
+
201
+ 一张规则表是件很不起眼的东西:几百行日期、百分比和出处。但其中每一行,都是某个人在某个地方不必再犯的一个错误。如果这张表能让哪怕几个人少写错一个数字、少做错一笔交易、少白费一周时间,它就做到了命名那天我希望它做到的事——以她的名字,给这个世界多带来一点好。
202
+
203
+ 它对所有人免费,永远如此。不保留任何权利。
@@ -0,0 +1,184 @@
1
+ # Marina
2
+
3
+ **Point-in-time trading rules for financial markets, as data.**
4
+
5
+ Price limits, fees, trading sessions, settlement — every record says *when* it took effect and *where* it was published. Any backtest or trading agent can cite the exact rules it ran under, and anyone can re-run it against byte-identical rules.
6
+
7
+ > **中文** · 市场交易规则的「tzdata」:涨跌幅、费用、tick 等制度**从哪天起、依据什么**,每条记录带官方原文、引文和内容哈希。给回测引擎和 AI Agent 用——查不到就报错,不猜;`coverage()` 预检整段窗口,`resolve()` 返回统一判定,`diff_rules` 告诉你升级后哪些回测要重跑。目前覆盖 A 股(含科创/创业/北交所、ST、IPO 窗口、ETF、可转债、B 股)。CC0 数据 + MIT 代码。
8
+
9
+ ```
10
+ rules: marina-rules@2026.10.26.1 sha256:1bf8eb29d05157c6
11
+ ```
12
+
13
+ Marina is to trading rules what the [tz database](https://www.iana.org/time-zones) is to time zones and what [exchange_calendars](https://github.com/gerrymanoim/exchange_calendars) is to trading days: small, boring, free, and meant to be the one copy everyone imports instead of the thousand copies everyone gets slightly wrong.
14
+
15
+ > **Status: 2026.10.26.** China A-share price limits and transaction fees — **39 records, 36 `verified` + 3 `single-source`** against primary sources (exchange / ministry / depository notices, with quotes and page hashes). Other markets are not yet covered; see [Coverage](#coverage). Treat the exchange's own notice as authoritative, always.
16
+
17
+ ## Why
18
+
19
+ Every backtest engine, every trading agent, and every research notebook re-implements the same handful of rules — "ChiNext limit is 20%" — and a large share of them get the *date* wrong (it was 10% before 2020-08-24), the *scope* wrong (ST stocks differ), or the *rounding* wrong (exchanges round half-up; `round()` doesn't). These errors used to live in one firm's code. Now that AI agents write backtests by the thousand, the same error gets copied by the thousand.
20
+
21
+ A shared, cited, versioned table fixes this the way tzdata fixed time zones: not by being authoritative, but by being **the same for everyone** and **traceable to the source**.
22
+
23
+ ## Use
24
+
25
+ ```bash
26
+ pip install marina-rules # self-contained: the dataset ships inside the wheel, byte-pinned
27
+ # or take the JSON / SQLite dump attached to each GitHub Release — same bytes, same digest
28
+ ```
29
+
30
+ ```python
31
+ import marina_rules as mr
32
+
33
+ mr.rule_at("stamp_duty_sell", "2023-08-28") # 0.0005
34
+ mr.rule_at("price_limit_pct", "2020-08-23", "chinext", "XSHE") # 0.10
35
+ mr.limit_price(10.00, "chinext", "2021-03-01", "XSHE") # (12.0, 8.0) — exchange rounding
36
+ mr.limit_price(10.00, "ipo_first5_main", "2024-01-30", "XSHG+XSHE") # None — unlimited band
37
+ mr.rule_at("stamp_duty_sell", "2005-01-01") # KeyError — no defaults, ever
38
+ mr.citation() # 'rules: marina-rules@2026.10.25 sha256:…'
39
+
40
+ # Pre-flight a whole backtest window before running it:
41
+ mr.coverage("XSHE", "chinext", ["price_limit_pct"], "2019-01-01", "2026-10-19")
42
+ # {'status': 'complete', 'gaps': [], 'conflicts': [], 'verification': 'verified',
43
+ # 'required_context': ['board', 'st_status', 'listing_day_index'], 'excludes': [...], ...}
44
+ ```
45
+
46
+ ```bash
47
+ python -m marina_rules.diff 2026.10.24b 2026.10.25b # what changed, and what must be re-run
48
+ ```
49
+
50
+ `coverage()` answers "can Marina serve every (rule, day) in this window for this scope?" — `complete`,
51
+ `partial` (with the exact gaps), or `unsupported`. It also tells you what you must already know about
52
+ each security to select the scope legitimately (`required_context`): Marina holds rules, never a
53
+ security's ST or listing history, and it will not guess them for you. Gaps carry `known_gap_ids`
54
+ when they match [`data/cn/known_gaps.yaml`](data/cn/known_gaps.yaml) — the list of things Marina
55
+ knows it does not cover (delisting period, T+1, sessions, REITs, pre-2022 convertibles…), each with
56
+ a reason and a status, so "unsupported" can be cited rather than worked around. Agents: read [SKILL.md](SKILL.md).
57
+
58
+ The data is plain YAML under [`data/`](data/). You do not need the Python package; read the files from any language.
59
+
60
+ ### Cite it
61
+
62
+ Put `mr.citation()` in every result you publish. A result without it cannot be reproduced; a result with it can be re-run by anyone against the same bytes.
63
+
64
+ ### Check a fill log
65
+
66
+ ```bash
67
+ python -m marina_rules.check fills.csv [--strict]
68
+ ```
69
+
70
+ Verifies each fill against the rules in force that day — no buys above limit-up, no sells below limit-down, fees not below the statutory floor — and emits a `rules-verified@<version>:<hash>` stamp when clean. The stamp is recomputable by anyone holding the same log and the same rules version.
71
+
72
+ **What the stamp does not mean:** it says nothing about look-ahead bias, survivorship, capacity, or statistical validity. It says only that the fills respected the rules in this dataset. Name it accordingly.
73
+
74
+ ### Audit the rules against your own data
75
+
76
+ ```bash
77
+ python scripts/audit_bars.py /path/to/daily_bars --complete-from-listing
78
+ ```
79
+
80
+ A price-limit rule is falsifiable: after it takes effect, no close may move beyond the band. This script counts violations per rule. A violation means the rule is wrong or your data is — either way you want to know. Marina was first audited this way against 9.1 million A-share bars; see [`docs/audit-2026-10-24.md`](docs/audit-2026-10-24.md). The primary-source audit that followed, and the four corrections it produced, is in [`docs/sources-audit-2026-10-24.md`](docs/sources-audit-2026-10-24.md).
81
+
82
+ ## Coverage
83
+
84
+ | Market | Price limits | Fees | Sessions / auctions | Tick & lot | Settlement | Margin |
85
+ |---|---|---|---|---|---|---|
86
+ | China A-share (XSHG, XSHE, BJSE) | ✅ 27 records (incl. B-share, ETF, convertible) | ✅ 6 records | — | — | — | n/a |
87
+ | China futures | — | — | — | — | — | — |
88
+ | US equities | — | — | — | — | — | — |
89
+ | Crypto | — | — | — | — | — | — |
90
+
91
+ Blank cells are blank on purpose. A missing rule raises `KeyError`; it does not return a guess.
92
+
93
+ ## Scope — what Marina will and will not contain
94
+
95
+ **In:** rules that change *whether an order fills, at what price, or at what cost* — price limits, trading hours and auction windows, tick size and lot size, price cages, T+N settlement, statutory fees, exchange margin requirements, trading calendars (delegated to `exchange_calendars`).
96
+
97
+ **Out, permanently:** constituent lists, ST/suspension lists, borrow lists, prices, adjustment factors, anything with an owner or a license. Those are *data*; Marina holds *rules*. This boundary is what lets tzdata survive forty years without a legal department, and we are not going to be cleverer than tzdata.
98
+
99
+ ## What the dates mean (and don't)
100
+
101
+ Each record carries three dates: `effective_from` (when the rule applied), `published_at` (when the
102
+ official notice was issued — `null` when only later restatements were located), and `recorded_at`
103
+ (when the record entered Marina). **`recorded_at` is not "known at".** Marina was assembled after the
104
+ fact; it does not by itself let you prove that a backtest avoided look-ahead on rule changes. If that
105
+ matters, use `published_at` and treat records with `published_at: null` as unknown-at-the-time.
106
+
107
+ ## One answer shape for every language: the decision contract
108
+
109
+ ```bash
110
+ python -m marina_rules.contract price_limit_pct 2024-06-14 st_main XSHE --context board=main_board,st_status=true
111
+ python -m marina_rules.contract --schema # JSON Schema of the answer
112
+ python -m marina_rules.contract --capabilities # what can be asked, generated from the data
113
+ ```
114
+
115
+ `resolve()` returns one dict with `status ∈ {DETERMINED, MISSING_CONTEXT, OUT_OF_COVERAGE, CONFLICT,
116
+ UNVERIFIED}`, a `result` only when determined, the record and evidence refs, `known_gap_ids`, the
117
+ ruleset version and hash, and a `reason` in plain words. It is the boundary other languages, agents
118
+ and logs should see; the Python exceptions remain for in-process use.
119
+
120
+ ## Upgrading: `diff_rules`
121
+
122
+ `python -m marina_rules.diff <from> <to>` lists every change between two tags (or directories) and
123
+ classifies each as **correction** (Marina was wrong about the past — results computed under the old
124
+ version on that interval were computed with a wrong rule), **regime_change** (the rule itself changed
125
+ on that date), **coverage_added**, **rename**, **bookkeeping** or **metadata**. It ends with the
126
+ `(market, scope, rule, interval)` list you must re-run. Both versions are pinned by dataset digest.
127
+ Marina does not estimate P&L impact; that is what the interval is for.
128
+
129
+ ## Conformance vectors — check *your* implementation, in any language
130
+
131
+ [`conformance/vectors/`](conformance/) holds 88 input→expected pairs whose expected values come from
132
+ the exchange texts and hand arithmetic quoted in each vector's `oracle`, not from running Marina.
133
+ Regime switches one day before and on the day, ST vs non-ST, IPO windows, both exchanges' one-tick
134
+ floors, funds, convertibles, fees, missing and contradictory context, known gaps. Marina itself is
135
+ tested against them in CI; the first run caught a real defect. `python conformance/run.py`.
136
+
137
+ ## Errata
138
+
139
+ When data contradicts a rule, so far the data has been wrong every time. [`errata/`](errata/) records each case with a sample and a one-command detector you can run on your own bars (stale pct-change columns; sub-¥1 adjusted-price rounding). Contributions welcome under the same bar: sample + detector + the fact that settles it.
140
+
141
+ ## Verification levels
142
+
143
+ Every record carries one of three levels, in the data, in the open:
144
+
145
+ | level | meaning |
146
+ |---|---|
147
+ | `verified` | ≥1 official primary source (exchange / ministry / depository) states this value and date, **or** ≥2 independent authoritative sources agree. Every source carries a verbatim `quote` and a content hash of the fetched document (text for HTML, bytes for PDF) — mandatory, enforced at load time |
148
+ | `single-source` | one source, or an official source where date or value had to be inferred |
149
+ | `unverified` | secondary sources only, or sources disagree (the disagreement is recorded in `notes`) |
150
+
151
+ `strict=True` in the API refuses `unverified` records. Release tags include all levels; the level is part of the record, not hidden in a changelog.
152
+
153
+ ## Contributing
154
+
155
+ Yes, please — this only works as a commons. One record per pull request, with the primary source linked. CI checks the schema, the sources, interval overlap, and runs the audit on the sample bars. See [CONTRIBUTING.md](CONTRIBUTING.md) and [GOVERNANCE.md](GOVERNANCE.md).
156
+
157
+ ## Acknowledgements
158
+
159
+ The China A-share timeline was cross-checked against the rule histories compiled in the MIT-licensed PyPI package [`data-backtest-live-common`](https://pypi.org/project/data-backtest-live-common/) (2026-09), whose anonymous author did careful work on the fee and limit chronology, and against [`exchange_calendars`](https://github.com/gerrymanoim/exchange_calendars) for calendar conventions. If you are the dbl_common maintainer: please open an issue; this table should have your name on it.
160
+
161
+ ## License
162
+
163
+ Data (`data/`, `docs/`): [CC0 1.0](LICENSE-DATA) — no rights reserved.
164
+ Code (`src/`, `scripts/`, `tests/`): [MIT](LICENSE).
165
+
166
+ ## About the name
167
+
168
+ Marina is named after my wife, Marina. This project was created and first published on **24 October 2026**, her birthday.
169
+
170
+ A rules table is an unglamorous thing: a few hundred lines of dates, percentages, and citations. But every one of those lines is a mistake that someone, somewhere, no longer has to make. If this table spares even a few people a wrong number, a bad trade, or a wasted week, then it has done what I hoped it would do on the day it was named — bring a little more good into the world, in her name.
171
+
172
+ It is free for everyone, forever. No rights reserved.
173
+
174
+ — David Li, 24 October 2026
175
+
176
+ ---
177
+
178
+ **关于名字**
179
+
180
+ Marina 以我爱人 Marina 的名字命名。这个项目建立并首次发布于 **2026 年 10 月 24 日**,她的生日。
181
+
182
+ 一张规则表是件很不起眼的东西:几百行日期、百分比和出处。但其中每一行,都是某个人在某个地方不必再犯的一个错误。如果这张表能让哪怕几个人少写错一个数字、少做错一笔交易、少白费一周时间,它就做到了命名那天我希望它做到的事——以她的名字,给这个世界多带来一点好。
183
+
184
+ 它对所有人免费,永远如此。不保留任何权利。
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "marina-rules"
7
+ version = "2026.10.26.1"
8
+ description = "Point-in-time trading rules for financial markets, as data. Price limits, fees, sessions, settlement — every record cites its source."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = {text = "MIT"}
12
+ authors = [{name = "David Li"}]
13
+ keywords = ["trading", "backtest", "exchange-rules", "price-limit", "fees", "point-in-time", "a-share"]
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Office/Business :: Financial :: Investment",
18
+ ]
19
+ dependencies = ["pyyaml>=6"]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/david-lihangyuan/marina-rules"
23
+ Issues = "https://github.com/david-lihangyuan/marina-rules/issues"
24
+
25
+ [project.scripts]
26
+ marina-check = "marina_rules.check:main"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
30
+
31
+ [tool.pytest.ini_options]
32
+ pythonpath = ["src"]
33
+ testpaths = ["tests"]
34
+
35
+ # The dataset is vendored into the wheel so `pip install marina-rules` is self-contained.
36
+ # scripts/vendor_data.py copies data/ → src/marina_rules/data/ right before build; the copy is
37
+ # git-ignored and must byte-match data/ (checked in CI and by `marina_rules.dataset_digest()`).
38
+ [tool.setuptools.package-data]
39
+ marina_rules = ["data/**/*.yaml", "data/samples/**/*.csv"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+