mopr 0.0.57__tar.gz → 0.0.57.dev1__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 (119) hide show
  1. {mopr-0.0.57 → mopr-0.0.57.dev1}/CHANGELOG.md +1 -26
  2. {mopr-0.0.57/mopr.egg-info → mopr-0.0.57.dev1}/PKG-INFO +1 -1
  3. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/__init__.py +1 -9
  4. {mopr-0.0.57 → mopr-0.0.57.dev1/mopr.egg-info}/PKG-INFO +1 -1
  5. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/SOURCES.txt +0 -19
  6. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/scm_file_list.json +0 -7
  7. mopr-0.0.57.dev1/mopr.egg-info/scm_version.json +8 -0
  8. {mopr-0.0.57 → mopr-0.0.57.dev1}/pyproject.toml +0 -6
  9. {mopr-0.0.57 → mopr-0.0.57.dev1}/python/_version.py +3 -3
  10. {mopr-0.0.57 → mopr-0.0.57.dev1}/runtime/ringbuffer.py +2 -2
  11. mopr-0.0.57/base/dataclass_utils.py +0 -36
  12. mopr-0.0.57/base/types.py +0 -8
  13. mopr-0.0.57/finance/__init__.py +0 -0
  14. mopr-0.0.57/finance/forex/__init__.py +0 -0
  15. mopr-0.0.57/finance/forex/demo.py +0 -109
  16. mopr-0.0.57/finance/forex/engine.py +0 -144
  17. mopr-0.0.57/finance/forex/exchange_rate_api.py +0 -319
  18. mopr-0.0.57/finance/forex/forex.py +0 -374
  19. mopr-0.0.57/finance/forex/ingest.py +0 -129
  20. mopr-0.0.57/finance/forex/models.py +0 -96
  21. mopr-0.0.57/finance/forex/scheduler.py +0 -47
  22. mopr-0.0.57/finance/forex/seed.py +0 -70
  23. mopr-0.0.57/finance/forex/test_engine.py +0 -81
  24. mopr-0.0.57/finance/forex/yahoo.py +0 -202
  25. mopr-0.0.57/finance/forex//344/272/272/346/260/221/345/270/201/346/261/207/347/216/207/344/270/255/351/227/264/344/273/267/357/274/2101994-2025/357/274/211.xlsx +0 -0
  26. mopr-0.0.57/mopr.egg-info/scm_version.json +0 -8
  27. mopr-0.0.57/storage/__init__.py +0 -11
  28. mopr-0.0.57/storage/csv.py +0 -141
  29. mopr-0.0.57/storage/storage.md +0 -198
  30. mopr-0.0.57/storage/storage.py +0 -69
  31. {mopr-0.0.57 → mopr-0.0.57.dev1}/.clang-format +0 -0
  32. {mopr-0.0.57 → mopr-0.0.57.dev1}/.clang-tidy +0 -0
  33. {mopr-0.0.57 → mopr-0.0.57.dev1}/.clangd +0 -0
  34. {mopr-0.0.57 → mopr-0.0.57.dev1}/.editorconfig +0 -0
  35. {mopr-0.0.57 → mopr-0.0.57.dev1}/.gitattributes +0 -0
  36. {mopr-0.0.57 → mopr-0.0.57.dev1}/.githooks/commit-msg +0 -0
  37. {mopr-0.0.57 → mopr-0.0.57.dev1}/.gitignore +0 -0
  38. {mopr-0.0.57 → mopr-0.0.57.dev1}/.vscode/launch.json +0 -0
  39. {mopr-0.0.57 → mopr-0.0.57.dev1}/.vscode/settings.json +0 -0
  40. {mopr-0.0.57 → mopr-0.0.57.dev1}/.vscode/tasks.json +0 -0
  41. {mopr-0.0.57 → mopr-0.0.57.dev1}/AGENTS.md +0 -0
  42. {mopr-0.0.57 → mopr-0.0.57.dev1}/BUILDING.md +0 -0
  43. {mopr-0.0.57 → mopr-0.0.57.dev1}/CONTRIBUTING.md +0 -0
  44. {mopr-0.0.57 → mopr-0.0.57.dev1}/LICENSE +0 -0
  45. {mopr-0.0.57 → mopr-0.0.57.dev1}/MANIFEST.in +0 -0
  46. {mopr-0.0.57 → mopr-0.0.57.dev1}/README.md +0 -0
  47. {mopr-0.0.57 → mopr-0.0.57.dev1}/THIRD_PARTY_NOTICES.md +0 -0
  48. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/environment.py +0 -0
  49. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/filesystem.py +0 -0
  50. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/region.md +0 -0
  51. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/region.py +0 -0
  52. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/strings.md +0 -0
  53. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/strings.py +0 -0
  54. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/time.py +0 -0
  55. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/timestamp.md +0 -0
  56. {mopr-0.0.57 → mopr-0.0.57.dev1}/base/timestamp.py +0 -0
  57. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/__init__.py +0 -0
  58. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/FileStateStore.java +0 -0
  59. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/Generator.java +0 -0
  60. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/HLC.java +0 -0
  61. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/Id.java +0 -0
  62. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/Option.java +0 -0
  63. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/PersistentState.java +0 -0
  64. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/README.md +0 -0
  65. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/StateStore.java +0 -0
  66. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/__init__.py +0 -0
  67. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/generator.py +0 -0
  68. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/hlc.py +0 -0
  69. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/id.py +0 -0
  70. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/option.py +0 -0
  71. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/queue.py +0 -0
  72. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/state_store.py +0 -0
  73. {mopr-0.0.57 → mopr-0.0.57.dev1}/distributed/id/tests.py +0 -0
  74. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/forex/DESIGN.md +0 -0
  75. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/forex/README.md +0 -0
  76. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/forex/seed_rates.csv +0 -0
  77. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/forex/seed_rates_hkd.csv +0 -0
  78. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/rates/DESIGN.md +0 -0
  79. {mopr-0.0.57 → mopr-0.0.57.dev1}/finance/rates/README.md +0 -0
  80. {mopr-0.0.57 → mopr-0.0.57.dev1}/log/__init__.py +0 -0
  81. {mopr-0.0.57 → mopr-0.0.57.dev1}/log/core.py +0 -0
  82. {mopr-0.0.57 → mopr-0.0.57.dev1}/machine/__init__.py +0 -0
  83. {mopr-0.0.57 → mopr-0.0.57.dev1}/machine/id.py +0 -0
  84. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/dependency_links.txt +0 -0
  85. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/entry_points.txt +0 -0
  86. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/requires.txt +0 -0
  87. {mopr-0.0.57 → mopr-0.0.57.dev1}/mopr.egg-info/top_level.txt +0 -0
  88. {mopr-0.0.57 → mopr-0.0.57.dev1}/python/__init__.py +0 -0
  89. {mopr-0.0.57 → mopr-0.0.57.dev1}/runtime/__init__.py +0 -0
  90. {mopr-0.0.57 → mopr-0.0.57.dev1}/runtime/ringbuffer.md +0 -0
  91. {mopr-0.0.57 → mopr-0.0.57.dev1}/runtime/ringbuffer_test.py +0 -0
  92. {mopr-0.0.57 → mopr-0.0.57.dev1}/setup.cfg +0 -0
  93. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/__init__.py +0 -0
  94. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/__main__.py +0 -0
  95. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/__init__.py +0 -0
  96. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/__main__.py +0 -0
  97. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/cli.py +0 -0
  98. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/__init__.py +0 -0
  99. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/cpp.py +0 -0
  100. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/go.py +0 -0
  101. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/java.py +0 -0
  102. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/python.py +0 -0
  103. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/handlers/rust.py +0 -0
  104. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/notices.py +0 -0
  105. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/report.py +0 -0
  106. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_audit/todo.py +0 -0
  107. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/__init__.py +0 -0
  108. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/__main__.py +0 -0
  109. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/cli.py +0 -0
  110. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/core.py +0 -0
  111. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/__init__.py +0 -0
  112. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/base.py +0 -0
  113. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/cpp.py +0 -0
  114. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/go.py +0 -0
  115. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/java.py +0 -0
  116. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/python.py +0 -0
  117. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/handlers/rust.py +0 -0
  118. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/license_header/project_meta.py +0 -0
  119. {mopr-0.0.57 → mopr-0.0.57.dev1}/tools/run_module.py +0 -0
@@ -3,30 +3,6 @@ All notable changes to this project will be documented in this file.
3
3
 
4
4
  ## [Unreleased]
5
5
 
6
- ## [0.0.57] - 2026-09-08
7
- ### Changed
8
- - chore[java]:包名统一回 mopr.distributed.id;vscode 移除 chat autoApprove 并加入 Java 诊断过滤
9
- - fix[java]: deploy改为自动发布
10
- - refactor[python]: 泛型 T 收敛到 base.types 单一来源
11
- - base/types.py 定义仓库唯一 TypeVar T; base/__init__.py 导出 T
12
- - runtime/ringbuffer.py 改为 from ..base.types import T, 消除本地自建 T
13
- - 新增 base/dataclass_utils.py (get_field_names 等泛型字段工具)
14
- - 全仓库泛型 T 引用方统一到 base.types.T, 杜绝类型变量漂移
15
- - feat[python]: finance/forex 汇率缓存模块与 storage 存储抽象
16
- - storage: FileStorage/MetaFileStorage + CSV 存储 (供缓存生命周期复用)
17
- - finance/forex: ExchangeRateCache 多源汇率缓存
18
- * frankfurter(ECB): 默认, 1999 起日频历史区间
19
- * exchangerate(er-api): 免费层当日快照逐日累积
20
- * yahoo: GHS 等 ECB 篮子外货币, Yahoo chart 一次回填历史段 +
21
- er-api 当日定盘覆盖 (含离线自检)
22
- - pyproject 注册 mopr.storage / mopr.finance 子包
23
- - feat[base]: 新增 affinity/filesystem/closer 多语言基础模块
24
- - c++: base/affinity (CPU 亲和) 纳入 mopr_core; base/strings.h 入编译
25
- - go: base/filesystem + closer (gocsv 依赖)
26
- - rust: base/filesystem 模块接线 mod.rs; storage 模块注册 lib.rs
27
- - Cargo/CMake/go 依赖与锁文件同步; .githooks 补版权头
28
- - release version 0.0.57
29
-
30
6
  ## [0.0.56] - 2026-09-07
31
7
  ### Changed
32
8
  - docs[java]:新增 Maven Central 发布指南文档
@@ -816,8 +792,7 @@ All notable changes to this project will be documented in this file.
816
792
  - release version 0.0.1
817
793
 
818
794
 
819
- [Unreleased]: https://gitee.com/quant1x/mopr/compare/v0.0.57...HEAD
820
- [0.0.57]: https://gitee.com/quant1x/mopr/compare/v0.0.56...v0.0.57
795
+ [Unreleased]: https://gitee.com/quant1x/mopr/compare/v0.0.56...HEAD
821
796
  [0.0.56]: https://gitee.com/quant1x/mopr/compare/v0.0.55...v0.0.56
822
797
  [0.0.55]: https://gitee.com/quant1x/mopr/compare/v0.0.54...v0.0.55
823
798
  [0.0.54]: https://gitee.com/quant1x/mopr/compare/v0.0.53...v0.0.54
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mopr
3
- Version: 0.0.57
3
+ Version: 0.0.57.dev1
4
4
  Summary: Monorepo Optimal Portable Runtime
5
5
  Author: Quant1X
6
6
  Author-email: Quant1X <wangfengxy@sina.cn>
@@ -11,10 +11,8 @@ base 各单文件模块的公共函数与类型经 __init__ 直接导出, 业务
11
11
  """
12
12
 
13
13
  # 模块对象保留为包属性; environment 依赖 filesystem, 故 filesystem 在前
14
- from . import dataclass_utils, environment, filesystem, region, strings, time, timestamp
14
+ from . import environment, filesystem, region, strings, time, timestamp
15
15
 
16
- # dataclass_utils
17
- from .dataclass_utils import get_field_names
18
16
  # filesystem
19
17
  from .filesystem import expand_user, homedir, mkdirs, touch, update_file_mtime
20
18
  # environment
@@ -27,12 +25,8 @@ from .timestamp import Timestamp
27
25
  from .strings import str_to_bool, to_snake_case
28
26
  # region
29
27
  from .region import Region, is_offshore, is_onshore
30
- # types
31
- from .types import T
32
28
 
33
29
  __all__ = [
34
- # dataclass_utils
35
- "get_field_names",
36
30
  # filesystem
37
31
  "expand_user", "homedir", "mkdirs", "touch", "update_file_mtime",
38
32
  # environment
@@ -45,6 +39,4 @@ __all__ = [
45
39
  "str_to_bool", "to_snake_case",
46
40
  # region
47
41
  "Region", "is_offshore", "is_onshore",
48
- # types
49
- "T",
50
42
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mopr
3
- Version: 0.0.57
3
+ Version: 0.0.57.dev1
4
4
  Summary: Monorepo Optimal Portable Runtime
5
5
  Author: Quant1X
6
6
  Author-email: Quant1X <wangfengxy@sina.cn>
@@ -18,7 +18,6 @@ pyproject.toml
18
18
  .vscode/settings.json
19
19
  .vscode/tasks.json
20
20
  base/__init__.py
21
- base/dataclass_utils.py
22
21
  base/environment.py
23
22
  base/filesystem.py
24
23
  base/region.md
@@ -28,7 +27,6 @@ base/strings.py
28
27
  base/time.py
29
28
  base/timestamp.md
30
29
  base/timestamp.py
31
- base/types.py
32
30
  distributed/__init__.py
33
31
  distributed/id/FileStateStore.java
34
32
  distributed/id/Generator.java
@@ -46,23 +44,10 @@ distributed/id/option.py
46
44
  distributed/id/queue.py
47
45
  distributed/id/state_store.py
48
46
  distributed/id/tests.py
49
- finance/__init__.py
50
47
  finance/forex/DESIGN.md
51
48
  finance/forex/README.md
52
- finance/forex/__init__.py
53
- finance/forex/demo.py
54
- finance/forex/engine.py
55
- finance/forex/exchange_rate_api.py
56
- finance/forex/forex.py
57
- finance/forex/ingest.py
58
- finance/forex/models.py
59
- finance/forex/scheduler.py
60
- finance/forex/seed.py
61
49
  finance/forex/seed_rates.csv
62
50
  finance/forex/seed_rates_hkd.csv
63
- finance/forex/test_engine.py
64
- finance/forex/yahoo.py
65
- finance/forex/人民币汇率中间价(1994-2025).xlsx
66
51
  finance/rates/DESIGN.md
67
52
  finance/rates/README.md
68
53
  log/__init__.py
@@ -83,10 +68,6 @@ runtime/__init__.py
83
68
  runtime/ringbuffer.md
84
69
  runtime/ringbuffer.py
85
70
  runtime/ringbuffer_test.py
86
- storage/__init__.py
87
- storage/csv.py
88
- storage/storage.md
89
- storage/storage.py
90
71
  tools/__init__.py
91
72
  tools/__main__.py
92
73
  tools/run_module.py
@@ -28,9 +28,6 @@
28
28
  "base\\cache\\mmap_windows.go",
29
29
  "base\\cache\\pool.go",
30
30
  "base\\cache\\pool_test.go",
31
- "base\\closer.go",
32
- "base\\filesystem.go",
33
- "base\\filesystem_test.go",
34
31
  "base\\mem\\page.go",
35
32
  "base\\mem\\page_test.go",
36
33
  "base\\mem\\size.go",
@@ -79,7 +76,6 @@
79
76
  "finance\\forex\\README.md",
80
77
  "finance\\forex\\seed_rates.csv",
81
78
  "finance\\forex\\seed_rates_hkd.csv",
82
- "finance\\forex\\\u4eba\u6c11\u5e01\u6c47\u7387\u4e2d\u95f4\u4ef7\uff081994-2025\uff09.xlsx",
83
79
  "finance\\rates\\DESIGN.md",
84
80
  "finance\\rates\\README.md",
85
81
  "go.mod",
@@ -87,9 +83,6 @@
87
83
  "runtime\\ringbuffer.go",
88
84
  "runtime\\ringbuffer.md",
89
85
  "runtime\\ringbuffer_test.go",
90
- "storage\\csv.go",
91
- "storage\\storage.go",
92
- "storage\\storage.md",
93
86
  "tests\\runtime\\LMAXDisruptorBenchmark.java"
94
87
  ]
95
88
  }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.0.56",
3
+ "distance": 1,
4
+ "node": "g29062dc6192fd8e2321dbc78944708597086927e",
5
+ "dirty": false,
6
+ "branch": "0.0.x",
7
+ "node_date": "2026-09-08"
8
+ }
@@ -55,9 +55,6 @@ packages = [
55
55
  "mopr.distributed.id",
56
56
  "mopr.machine",
57
57
  "mopr.log",
58
- "mopr.storage",
59
- "mopr.finance",
60
- "mopr.finance.forex",
61
58
  # 内置工具集合 (仓库根 tools/, 每个工具一个子包): mopr 的功能模块仅
62
59
  # base/distributed/runtime/machine, 其余辅助工具一律归集于此, 保持一级命名空间干净
63
60
  "mopr.tools",
@@ -75,9 +72,6 @@ mopr = "python"
75
72
  "mopr.distributed.id" = "distributed/id"
76
73
  "mopr.machine" = "machine"
77
74
  "mopr.log" = "log"
78
- "mopr.storage" = "storage"
79
- "mopr.finance" = "finance"
80
- "mopr.finance.forex" = "finance/forex"
81
75
  "mopr.tools" = "tools"
82
76
  "mopr.tools.license_header" = "tools/license_header"
83
77
  "mopr.tools.license_header.handlers" = "tools/license_header/handlers"
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.0.57'
22
- __version_tuple__ = version_tuple = (0, 0, 57)
21
+ __version__ = version = '0.0.57.dev1'
22
+ __version_tuple__ = version_tuple = (0, 0, 57, 'dev1')
23
23
 
24
- __commit_id__ = commit_id = 'gf2bab630a'
24
+ __commit_id__ = commit_id = 'g29062dc61'
@@ -16,9 +16,9 @@
16
16
  from __future__ import annotations
17
17
 
18
18
  import threading
19
- from typing import Generic
19
+ from typing import Generic, TypeVar
20
20
 
21
- from ..base.types import T
21
+ T = TypeVar("T")
22
22
 
23
23
 
24
24
  class QueueError(RuntimeError):
@@ -1,36 +0,0 @@
1
- # -*- coding: UTF-8 -*-
2
- # Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
3
- # SPDX-License-Identifier: AGPL-3.0-only
4
-
5
- """Dataclass 反射/序列化工具 (Python spec, 对齐 quant1x.base.dataclass_utils).
6
-
7
- 提供 dataclass 字段元信息提取 (get_field_names), 支撑 mopr.storage/csv 的
8
- dataclass <-> CSV 双向转换.
9
- """
10
- from dataclasses import fields, is_dataclass
11
- from typing import List, Type
12
-
13
- from .types import T
14
- from . import strings
15
-
16
-
17
- def get_field_names(cls: Type[T]) -> List[str]:
18
- """获取 dataclass 类的所有字段名称列表 (snake_case 化).
19
-
20
- Args:
21
- cls (Type[T]): 需要获取字段名的 dataclass 类
22
-
23
- Returns:
24
- List[str]: 包含所有字段名称的列表
25
-
26
- Raises:
27
- TypeError: 如果传入的类不是 dataclass 类型
28
- """
29
- if not is_dataclass(cls):
30
- raise TypeError(
31
- f"Expected a dataclass type, but got {cls!r}. "
32
- "Please decorate your class with @dataclass."
33
- )
34
- cls_fields = fields(cls)
35
- field_names = [strings.to_snake_case(f.name) for f in cls_fields]
36
- return field_names
mopr-0.0.57/base/types.py DELETED
@@ -1,8 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
3
- # SPDX-License-Identifier: AGPL-3.0-only
4
-
5
- from typing import TypeVar
6
-
7
- T = TypeVar('T')
8
- """定义全局泛型变量"""
File without changes
File without changes
@@ -1,109 +0,0 @@
1
- # -*- coding: UTF-8 -*-
2
- # Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
3
- # SPDX-License-Identifier: AGPL-3.0-only
4
-
5
- """
6
- 端到端演示
7
- ==========
8
- 流程:初始化锚点 -> 灌入演示汇率 -> 派生全量币种对 -> 折算 -> 自洽性校验
9
- 含双轨基准与记账时刻语义演示:
10
- - CN 锚(CFETS):人行 9:15 发布,09:13 记账 -> 以前一交易日(9/3)定盘
11
- - HK 锚(HKMA):金管局 19:00 发布,18:00 记账 -> 以前一交易日(9/3)定盘
12
- - 约定汇率(contractual)优先于官方定盘
13
- """
14
- import os, sys, datetime as dt
15
- from zoneinfo import ZoneInfo
16
- sys.path.insert(0, os.path.dirname(__file__))
17
- try:
18
- sys.stdout.reconfigure(encoding="utf-8") # type: ignore[attr-defined] # Windows GBK 控制台
19
- except Exception:
20
- pass
21
-
22
- # 必须在 import models 之前设置(models 在 import 时读取 DB_PATH)
23
- os.environ["FX_DB_PATH"] = "/tmp/fx_module/demo.db"
24
- if os.path.exists("/tmp/fx_module/demo.db"):
25
- os.remove("/tmp/fx_module/demo.db")
26
-
27
- from models import make_engine, session_factory, Base, FxRate, FxPair
28
- import seed, ingest, engine
29
- from ingest import demo_csv_path, demo_csv_hkd_path
30
-
31
- engine_db = make_engine()
32
- Base.metadata.create_all(engine_db)
33
- SessionLocal = session_factory()
34
-
35
- seed.init_anchors()
36
- seed.write_seed_csv()
37
- seed.write_seed_hkd_csv()
38
-
39
- SH = ZoneInfo("Asia/Shanghai")
40
- HK = ZoneInfo("Asia/Hong_Kong")
41
-
42
- with SessionLocal() as s:
43
- # 1) 灌入演示数据(生产环境替换为 ingest_official / HKMA 接入)
44
- d = ingest.ingest_from_csv(s, demo_csv_path(), anchor_ccy="CNY")
45
- print(f"[1] CN锚 灌入 CNY 在 {d} 的汇率 (CFETS口径)")
46
- d2 = ingest.ingest_from_csv(s, demo_csv_hkd_path(), anchor_ccy="HKD", source="HKMA-csv")
47
- print(f"[1] HK锚 灌入 HKD 在 {d2} 的汇率 (HKMA口径)")
48
-
49
- # 2) 派生全量币种对(三角套算)
50
- n = engine.build_and_persist_pairs(s, "CNY", d)
51
- n2 = engine.build_and_persist_pairs(s, "HKD", d2)
52
- print(f"[2] 派生全量币种对 CNY×{n} / HKD×{n2} 对")
53
-
54
- # 3) 折算:100 美元 -> 欧元 (CN锚, 9/4 定盘)
55
- r1 = engine.convert(s, 100, "USD", "EUR", d, "CNY")
56
- print(f"[3] CN锚 100 USD -> EUR = {r1:.4f} (校验: 7.0980/8.16 = {7.0980/8.16:.6f})")
57
-
58
- # 4) 记账时刻语义:人行 9:15 发布当日,09:13 记账 -> 用前一交易日(9/3)定盘
59
- as_of_0913 = dt.datetime(2026, 9, 4, 9, 13, tzinfo=SH)
60
- rd_0913 = engine.resolve_rate_date(s, "CNY", as_of_0913)
61
- r2 = engine.convert(s, 100, "USD", "CNY", d, "CNY", as_of=as_of_0913)
62
- print(f"[4] CN锚 09/04 09:13 记账: 定盘日={rd_0913} "
63
- f"100 USD -> CNY = {r2:.4f} (9/3中间价7.1100, 非当日7.0980)")
64
-
65
- # 5) 同一天 09:30(已过 9:15 发布)-> 取当日 9/4 定盘
66
- as_of_0930 = dt.datetime(2026, 9, 4, 9, 30, tzinfo=SH)
67
- rd_0930 = engine.resolve_rate_date(s, "CNY", as_of_0930)
68
- r3 = engine.convert(s, 100, "USD", "CNY", d, "CNY", as_of=as_of_0930)
69
- print(f"[5] CN锚 09/04 09:30 记账: 定盘日={rd_0930} "
70
- f"100 USD -> CNY = {r3:.4f} (当日7.0980)")
71
-
72
- # 6) HK锚 金管局口径:19:00 发布,18:00 记账 -> 用前一交易日(9/3)
73
- as_of_hk = dt.datetime(2026, 9, 4, 18, 0, tzinfo=HK)
74
- rd_hk = engine.resolve_rate_date(s, "HKD", as_of_hk)
75
- r4 = engine.convert(s, 100, "USD", "CNY", d2, "HKD", as_of=as_of_hk)
76
- print(f"[6] HK锚 09/04 18:00 记账: 定盘日={rd_hk} "
77
- f"100 USD -> CNY = {r4:.4f} (按HKMA 9/3: 7.7950/1.0960)")
78
-
79
- # 7) HK锚 19:30(已过 19:00 发布)-> 取当日 9/4
80
- as_of_hk2 = dt.datetime(2026, 9, 4, 19, 30, tzinfo=HK)
81
- rd_hk2 = engine.resolve_rate_date(s, "HKD", as_of_hk2)
82
- r5 = engine.convert(s, 100, "USD", "CNY", d2, "HKD", as_of=as_of_hk2)
83
- print(f"[7] HK锚 09/04 19:30 记账: 定盘日={rd_hk2} "
84
- f"100 USD -> CNY = {r5:.4f} (按HKMA当日: 7.7900/1.0970)")
85
-
86
- # 8) 交易双方约定汇率优先:单据约定 1 USD = 7.20 CNY,不再走官方定盘
87
- r6 = engine.convert(s, 100, "USD", "CNY", d, "CNY", contractual_rate=7.20)
88
- print(f"[8] 约定汇率 1 USD = 7.20 CNY: 100 USD -> CNY = {r6:.4f} (绕过官方定盘)")
89
-
90
- # 9) 反向折算自洽: 100 EUR -> USD 再 -> EUR 应还原
91
- usd = engine.convert(s, 100, "EUR", "USD", d, "CNY")
92
- eur_back = engine.convert(s, usd, "USD", "EUR", d, "CNY")
93
- print(f"[9] 100 EUR -> USD = {usd:.4f}, 再折回 EUR = {eur_back:.4f} (应≈100)")
94
-
95
- # 10) 自洽性校验:遍历所有币种对,a->b->a 误差应 < 1e-6
96
- pairs = s.query(FxPair).filter_by(anchor_ccy="CNY", rate_date=d).all()
97
- ccies = sorted({str(p.base) for p in pairs})
98
- max_err = 0
99
- for a in ccies:
100
- for b in ccies:
101
- if a == b:
102
- continue
103
- ab = engine.get_rate_on(s, a, b, d, "CNY")
104
- ba = engine.get_rate_on(s, b, a, d, "CNY")
105
- err = abs(ab * ba - 1.0)
106
- max_err = max(max_err, err)
107
- print(f"[10] 三角套算自洽性校验: 最大 |rate(a,b)*rate(b,a)-1| = {max_err:.2e} (应 < 1e-6)")
108
-
109
- print("\n演示完成")
@@ -1,144 +0,0 @@
1
- # -*- coding: UTF-8 -*-
2
- # Copyright (C) 2026 Quant1X <wangfengxy@sina.cn>
3
- # SPDX-License-Identifier: AGPL-3.0-only
4
-
5
- """
6
- 折算引擎
7
- ========
8
- 核心:全量币种对 = 锚点中间价 + 三角套算。
9
- 规则(以 CNY 为中心枢纽):
10
- - 官方直接提供: foreign/CNY (1 foreign = ? CNY)
11
- - 任意两外币: A/B = (A/CNY) / (B/CNY) # 三角套算
12
- - 锚币本身: CNY/CNY = 1
13
-
14
- "各自锚点"体现:每个主体以自身本币为锚,且绑定官方基准源(provider)。
15
- 例如 CN 主体锚 CNY 按人行(CFETS 9:15),HK 主体锚 HKD 按金管局(19:00)。
16
- 锚币 = 中心枢纽,套算逻辑完全一致 —— 这就是"各自锚点"的工程实现。
17
-
18
- 记账时点语义(重要):
19
- 官方定盘在每日固定时刻发布(人行 9:15 / 金管局 19:00),若记账时刻早于发布,
20
- 当日定盘尚不可用,须取"最近一个已发布的定盘日"(通常为前一交易日);
21
- 若交易双方在单据中约定了汇率(contractual_rate),约定价优先,不再查官方表。
22
- """
23
- import datetime as dt
24
- from zoneinfo import ZoneInfo
25
- from typing import Dict, Optional
26
- from sqlalchemy.orm import Session
27
- from models import Anchor, FxRate, FxPair
28
-
29
-
30
- def resolve_rate_date(session: Session, anchor_ccy: str, as_of: dt.datetime) -> dt.date:
31
- """记账时刻 as_of 下,该锚点"最近一个已发布定盘"的日期。
32
-
33
- 规则:
34
- - 查锚点注册的 provider 发布时点 publish_time / publish_tz;
35
- - as_of 换算到发布时区后,若已过发布时点则当日定盘可用,否则以前一日起算;
36
- - 从候选日起向前找最近一个真实存在数据(fx_rate)的日期
37
- (跳过周末/节假日等无发布日,天然兼容交易日历)。
38
- """
39
- anchor = session.query(Anchor).filter_by(anchor_currency=anchor_ccy).first()
40
- if anchor is None:
41
- raise ValueError(f"锚点 {anchor_ccy} 未注册(anchor 表),请先 seed.init_anchors()")
42
- tz = ZoneInfo(anchor.publish_tz or "UTC")
43
- publish = dt.time.fromisoformat(anchor.publish_time or "09:15")
44
- local = as_of.astimezone(tz)
45
- pub_dt = dt.datetime.combine(local.date(), publish, tzinfo=tz)
46
- if local >= pub_dt:
47
- candidate = local.date()
48
- else:
49
- candidate = local.date() - dt.timedelta(days=1)
50
- # 向前找最近有数据的定盘日(幂等:数据已入库即视为已发布)
51
- rows = (session.query(FxRate.rate_date)
52
- .filter(FxRate.anchor_ccy == anchor_ccy, FxRate.rate_date <= candidate)
53
- .order_by(FxRate.rate_date.desc())
54
- .all())
55
- if not rows:
56
- raise ValueError(f"锚点 {anchor_ccy} 在 {as_of} 前无任何已发布定盘数据")
57
- return rows[0][0]
58
-
59
-
60
- def build_cross_rates(rates_against_anchor: Dict[str, float], anchor_ccy: str) -> Dict[str, Dict[str, float]]:
61
- """
62
- 由 {外币: 1外币=?锚币} 派生全量币种对矩阵 {base: {quote: rate}}。
63
- rate = base_amount 折算成 quote 时的系数 (1 base = rate quote)。
64
- 注:rates_against_anchor 均应为正向口径 "1 foreign = ? anchor"。
65
- """
66
- all_ccy = list(rates_against_anchor.keys()) + [anchor_ccy]
67
- matrix: Dict[str, Dict[str, float]] = {c: {} for c in all_ccy}
68
- # 锚币自身
69
- matrix[anchor_ccy][anchor_ccy] = 1.0
70
- for c in rates_against_anchor:
71
- matrix[c][anchor_ccy] = rates_against_anchor[c] # c -> anchor
72
- matrix[anchor_ccy][c] = 1.0 / rates_against_anchor[c] # anchor -> c
73
- # 三角套算:c1/c2 = (c1/anchor) / (c2/anchor)
74
- for c1 in all_ccy:
75
- for c2 in all_ccy:
76
- if c1 in matrix and c2 in matrix and matrix[c1].get(anchor_ccy) and matrix[c2].get(anchor_ccy):
77
- matrix[c1][c2] = matrix[c1][anchor_ccy] / matrix[c2][anchor_ccy]
78
- return matrix
79
-
80
-
81
- def build_and_persist_pairs(session: Session, anchor_ccy: str, date) -> int:
82
- """把某日全量币种对套算结果写入 fx_pair 表,返回对数。"""
83
- rows = session.query(FxRate).filter_by(anchor_ccy=anchor_ccy, rate_date=date).all()
84
- if not rows:
85
- raise ValueError(f"无 {anchor_ccy} 锚点在 {date} 的汇率数据,请先 ingest")
86
- rates = {r.foreign_ccy: r.rate for r in rows}
87
- matrix = build_cross_rates(rates, anchor_ccy)
88
- count = 0
89
- for base, quotes in matrix.items():
90
- for quote, rate in quotes.items():
91
- existing = session.query(FxPair).filter_by(
92
- anchor_ccy=anchor_ccy, base=base, quote=quote, rate_date=date).first()
93
- if existing:
94
- existing.rate = rate
95
- else:
96
- session.add(FxPair(anchor_ccy=anchor_ccy, base=base, quote=quote,
97
- rate_date=date, rate=rate, source="cross"))
98
- count += 1
99
- session.commit()
100
- return count
101
-
102
-
103
- def get_rate_on(session: Session, base: str, quote: str, date, anchor_ccy: str,
104
- as_of: Optional[dt.datetime] = None) -> float:
105
- """查询某定盘日币种对汇率(优先 fx_pair,缺失则实时套算)。
106
-
107
- 传 as_of 时按记账时刻解析"最近已发布定盘日",忽略 date。
108
- """
109
- if as_of is not None:
110
- date = resolve_rate_date(session, anchor_ccy, as_of)
111
- p = session.query(FxPair).filter_by(anchor_ccy=anchor_ccy, base=base, quote=quote,
112
- rate_date=date).first()
113
- if p:
114
- return p.rate
115
- # 实时套算兜底(锚币自身对锚币恒为 1,fx_rate 不落锚币自身行)
116
- def _rate_vs_anchor(ccy: str) -> float:
117
- if ccy == anchor_ccy:
118
- return 1.0
119
- r = session.query(FxRate).filter_by(anchor_ccy=anchor_ccy, foreign_ccy=ccy, rate_date=date).first()
120
- if not r:
121
- raise ValueError(f"缺少 {ccy} 在 {date} 对 {anchor_ccy} 的汇率")
122
- return r.rate
123
- return _rate_vs_anchor(base) / _rate_vs_anchor(quote)
124
-
125
-
126
- def convert(session: Session, amount: float, from_ccy: str, to_ccy: str, date,
127
- anchor_ccy: str, contractual_rate: Optional[float] = None,
128
- as_of: Optional[dt.datetime] = None) -> float:
129
- """折算:amount (from_ccy) -> to_ccy。
130
-
131
- 汇率来源优先级(从高到低):
132
- 1. 交易双方约定汇率 contractual_rate(单据/合同载明,直接使用);
133
- 2. 官方已发布定盘:as_of 记账时刻最近已发布日(as_of 缺省用 date 当日)。
134
- 注意:约定汇率口径应与 from_ccy/to_ccy 对应(1 from_ccy = ? to_ccy)。
135
- """
136
- if date is None and as_of is None and contractual_rate is None:
137
- raise ValueError("date / as_of / contractual_rate 三者至少提供一个")
138
- if from_ccy == to_ccy:
139
- return amount
140
- if contractual_rate is not None:
141
- return amount * contractual_rate
142
- if as_of is not None:
143
- date = resolve_rate_date(session, anchor_ccy, as_of)
144
- return amount * get_rate_on(session, from_ccy, to_ccy, date, anchor_ccy)