mxm-refdata 0.3.0__py3-none-any.whl

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 (64) hide show
  1. mxm/refdata/__init__.py +1 -0
  2. mxm/refdata/api/__init__.py +1 -0
  3. mxm/refdata/api/ref_data_api.py +607 -0
  4. mxm/refdata/cli.py +218 -0
  5. mxm/refdata/data/first_day_of_interest_rule.json +88 -0
  6. mxm/refdata/data/futures_products.csv +6 -0
  7. mxm/refdata/data/last_trading_rule.json +34 -0
  8. mxm/refdata/database/__init__.py +1 -0
  9. mxm/refdata/database/sql_session_manager.py +135 -0
  10. mxm/refdata/mappings/__init__.py +31 -0
  11. mxm/refdata/mappings/futures_contract_vs_orm.py +51 -0
  12. mxm/refdata/mappings/futures_product_vs_orm.py +56 -0
  13. mxm/refdata/mappings/period_cycles_vs_orm.py +102 -0
  14. mxm/refdata/mappings/period_vs_orm.py +40 -0
  15. mxm/refdata/models/__init__.py +31 -0
  16. mxm/refdata/models/contracts/__init__.py +0 -0
  17. mxm/refdata/models/contracts/futures_contract.py +22 -0
  18. mxm/refdata/models/currencies.py +24 -0
  19. mxm/refdata/models/months.py +72 -0
  20. mxm/refdata/models/orm/__init__.py +16 -0
  21. mxm/refdata/models/orm/base.py +3 -0
  22. mxm/refdata/models/orm/futures_contracts.py +53 -0
  23. mxm/refdata/models/orm/futures_products.py +61 -0
  24. mxm/refdata/models/orm/period_cycles.py +84 -0
  25. mxm/refdata/models/orm/periods.py +30 -0
  26. mxm/refdata/models/period_cycles.py +73 -0
  27. mxm/refdata/models/periods.py +64 -0
  28. mxm/refdata/models/products/__init__.py +1 -0
  29. mxm/refdata/models/products/futures_product.py +38 -0
  30. mxm/refdata/models/products/settlement.py +10 -0
  31. mxm/refdata/models/reference_events.py +9 -0
  32. mxm/refdata/models/units.py +28 -0
  33. mxm/refdata/models/weekdays.py +66 -0
  34. mxm/refdata/parsing/__init__.py +1 -0
  35. mxm/refdata/parsing/futures_products_from_csv.py +76 -0
  36. mxm/refdata/py.typed +0 -0
  37. mxm/refdata/scripts/__init__.py +1 -0
  38. mxm/refdata/scripts/db_utils.py +61 -0
  39. mxm/refdata/scripts/manage_static_ref_data.py +73 -0
  40. mxm/refdata/services/__init__.py +0 -0
  41. mxm/refdata/services/bootstrap.py +84 -0
  42. mxm/refdata/services/futures_contract_factory.py +127 -0
  43. mxm/refdata/services/futures_product_factory.py +132 -0
  44. mxm/refdata/services/period_factory.py +315 -0
  45. mxm/refdata/services/ref_data_service.py +322 -0
  46. mxm/refdata/services/smokecheck.py +326 -0
  47. mxm/refdata/trading_calendars/__init__.py +0 -0
  48. mxm/refdata/trading_calendars/first_day_of_interest.py +74 -0
  49. mxm/refdata/trading_calendars/last_trading_day.py +117 -0
  50. mxm/refdata/trading_calendars/nth_business_day.py +52 -0
  51. mxm/refdata/trading_calendars/nth_calendar_day_of_period.py +43 -0
  52. mxm/refdata/trading_calendars/nth_weekday_of_period.py +50 -0
  53. mxm/refdata/trading_calendars/trading_calendar.py +186 -0
  54. mxm/refdata/utils/__init__.py +1 -0
  55. mxm/refdata/utils/cache_manager.py +33 -0
  56. mxm/refdata/utils/config.py +32 -0
  57. mxm/refdata/utils/period_types_codec.py +16 -0
  58. mxm/refdata/utils/regex_patterns.py +18 -0
  59. mxm/refdata/utils/resources.py +29 -0
  60. mxm_refdata-0.3.0.dist-info/METADATA +228 -0
  61. mxm_refdata-0.3.0.dist-info/RECORD +64 -0
  62. mxm_refdata-0.3.0.dist-info/WHEEL +4 -0
  63. mxm_refdata-0.3.0.dist-info/entry_points.txt +3 -0
  64. mxm_refdata-0.3.0.dist-info/licenses/LICENSE +21 -0
mxm/refdata/cli.py ADDED
@@ -0,0 +1,218 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime as dt
4
+ from datetime import date
5
+ from typing import Annotated
6
+
7
+ import typer
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+
11
+ from mxm.refdata.api.ref_data_api import RefDataAPI
12
+ from mxm.refdata.services.bootstrap import build_refdata, rebuild_refdata
13
+ from mxm.refdata.services.smokecheck import run_smokechecks
14
+
15
+ app = typer.Typer(
16
+ add_completion=False,
17
+ help="Inspect and materialize MXM reference data.",
18
+ )
19
+
20
+ console = Console()
21
+
22
+
23
+ def parse_cli_date(value: str) -> date:
24
+ """Parse a CLI date in YYYY-MM-DD format."""
25
+ try:
26
+ return dt.datetime.strptime(value, "%Y-%m-%d").date()
27
+ except ValueError as err:
28
+ raise typer.BadParameter("Expected date in YYYY-MM-DD format.") from err
29
+
30
+
31
+ @app.command("build")
32
+ def build() -> None:
33
+ """Build the local reference-data database."""
34
+ build_refdata()
35
+ console.print("[green]Reference data database built.[/]")
36
+
37
+
38
+ @app.command("rebuild")
39
+ def rebuild() -> None:
40
+ """Destructively rebuild the local reference-data database."""
41
+ rebuild_refdata()
42
+ console.print("[green]Reference data database rebuilt.[/]")
43
+
44
+
45
+ @app.command("products")
46
+ def products() -> None:
47
+ """List available futures products."""
48
+ api = RefDataAPI()
49
+ rows = api.get_all_products()
50
+
51
+ table = Table(title="MXM Futures Products")
52
+ table.add_column("Product ID")
53
+ table.add_column("Venue")
54
+ table.add_column("Currency")
55
+ table.add_column("Unit")
56
+ table.add_column("Period Types")
57
+
58
+ for product in rows:
59
+ table.add_row(
60
+ product.product_id,
61
+ product.venue,
62
+ product.currency.value,
63
+ product.unit.value,
64
+ ", ".join(period_type.value for period_type in product.period_types),
65
+ )
66
+
67
+ console.print(table)
68
+
69
+
70
+ @app.command("product")
71
+ def product(
72
+ product_id: Annotated[str, typer.Argument(help="Canonical product ID")],
73
+ ) -> None:
74
+ """Show one futures product."""
75
+ api = RefDataAPI()
76
+ product_obj = api.get_product_by_id(product_id)
77
+
78
+ table = Table(title=f"Product: {product_obj.product_id}")
79
+ table.add_column("Field")
80
+ table.add_column("Value")
81
+
82
+ table.add_row("product_id", product_obj.product_id)
83
+ table.add_row("venue", product_obj.venue)
84
+ table.add_row("description", product_obj.description)
85
+ table.add_row("currency", product_obj.currency.value)
86
+ table.add_row("unit", product_obj.unit.value)
87
+ table.add_row("contract_size", str(product_obj.contract_size))
88
+ table.add_row("listing_rule", product_obj.listing_rule)
89
+ table.add_row(
90
+ "period_types",
91
+ ", ".join(period_type.value for period_type in product_obj.period_types),
92
+ )
93
+ table.add_row("settlement", product_obj.settlement.value)
94
+ table.add_row("last_trading_rule", product_obj.last_trading_rule)
95
+ table.add_row("expiry_rule", product_obj.expiry_rule)
96
+ table.add_row("trading_calendar", product_obj.trading_calendar)
97
+ table.add_row("tick_size", str(product_obj.tick_size))
98
+ table.add_row("tick_value", str(product_obj.tick_value))
99
+ table.add_row("valid_period_rule", product_obj.valid_period_rule)
100
+
101
+ console.print(table)
102
+
103
+
104
+ @app.command("contracts")
105
+ def contracts(
106
+ product_id: Annotated[str, typer.Argument(help="Canonical product ID")],
107
+ ) -> None:
108
+ """List contracts for a futures product."""
109
+ api = RefDataAPI()
110
+ rows = api.get_contracts_for_product(product_id)
111
+
112
+ table = Table(title=f"Contracts: {product_id}")
113
+ table.add_column("Contract ID")
114
+ table.add_column("Period")
115
+ table.add_column("First Interest")
116
+ table.add_column("Last Trading")
117
+
118
+ for contract in rows:
119
+ table.add_row(
120
+ contract.contract_id,
121
+ contract.period_id,
122
+ contract.first_day_of_interest.isoformat(),
123
+ contract.last_trading_day.isoformat(),
124
+ )
125
+
126
+ console.print(table)
127
+
128
+
129
+ @app.command("active")
130
+ def active(
131
+ as_of_date: Annotated[str, typer.Argument(help="Date in YYYY-MM-DD format")],
132
+ product_id: Annotated[str | None, typer.Option(help="Optional product ID")] = None,
133
+ ) -> None:
134
+ """List active contracts as of a date."""
135
+ parsed_date = parse_cli_date(as_of_date)
136
+ api = RefDataAPI()
137
+ rows = api.get_active_contracts(parsed_date, product_id=product_id)
138
+
139
+ table = Table(title=f"Active contracts: {parsed_date.isoformat()}")
140
+ table.add_column("Product ID")
141
+ table.add_column("Contract ID")
142
+ table.add_column("Period")
143
+ table.add_column("Last Trading")
144
+
145
+ for contract in rows:
146
+ table.add_row(
147
+ contract.product_id,
148
+ contract.contract_id,
149
+ contract.period_id,
150
+ contract.last_trading_day.isoformat(),
151
+ )
152
+
153
+ console.print(table)
154
+
155
+
156
+ @app.command("coverage")
157
+ def coverage() -> None:
158
+ """Show contract coverage by product."""
159
+ api = RefDataAPI()
160
+ products = api.get_all_products()
161
+
162
+ table = Table(title="MXM Refdata Coverage")
163
+ table.add_column("Product ID")
164
+ table.add_column("Venue")
165
+ table.add_column("First Contract")
166
+ table.add_column("Last Contract")
167
+ table.add_column("Count", justify="right")
168
+
169
+ for product_obj in products:
170
+ product_contracts = api.get_contracts_for_product(product_obj.product_id)
171
+
172
+ if not product_contracts:
173
+ table.add_row(product_obj.product_id, product_obj.venue, "-", "-", "0")
174
+ continue
175
+
176
+ table.add_row(
177
+ product_obj.product_id,
178
+ product_obj.venue,
179
+ product_contracts[0].contract_id,
180
+ product_contracts[-1].contract_id,
181
+ str(len(product_contracts)),
182
+ )
183
+
184
+ console.print(table)
185
+
186
+
187
+ @app.command("smokecheck")
188
+ def smokecheck() -> None:
189
+ """Run operational smoke checks against the local refdata database."""
190
+ report = run_smokechecks()
191
+
192
+ console.print("[bold]MXM Refdata Smokecheck[/bold]")
193
+ console.print(
194
+ "Counts: "
195
+ f"products={report.counts.products}, "
196
+ f"periods={report.counts.periods}, "
197
+ f"contracts={report.counts.contracts}, "
198
+ f"cycles={report.counts.cycles}, "
199
+ f"memberships={report.counts.memberships}"
200
+ )
201
+
202
+ table = Table(title="Smokecheck Results")
203
+ table.add_column("Status")
204
+ table.add_column("Check")
205
+ table.add_column("Message")
206
+
207
+ for result in report.results:
208
+ status = "[green]PASS[/]" if result.status == "pass" else "[red]FAIL[/]"
209
+ table.add_row(status, result.name, result.message)
210
+
211
+ console.print(table)
212
+
213
+ if not report.passed:
214
+ raise typer.Exit(code=1)
215
+
216
+
217
+ if __name__ == "__main__":
218
+ app()
@@ -0,0 +1,88 @@
1
+ {
2
+ "comex_gold_futures": {
3
+ "shift_rule": {
4
+ "shift_period_type": "MONTH",
5
+ "n_shift": {
6
+ "Jan": 72,
7
+ "Feb": 24,
8
+ "Mar": 24,
9
+ "Apr": 24,
10
+ "May": 24,
11
+ "Jun": 72,
12
+ "Jul": 24,
13
+ "Aug": 24,
14
+ "Sep": 24,
15
+ "Oct": 24,
16
+ "Nov": 24,
17
+ "Dec": 72
18
+ }
19
+ },
20
+ "reference_rule": "next_b_day_after_period"
21
+ },
22
+ "cbot_corn_futures": {
23
+ "shift_rule": {
24
+ "shift_period_type": "MONTH",
25
+ "n_shift": {
26
+ "Mar": 36,
27
+ "May": 36,
28
+ "Sep": 36,
29
+ "Jul": 48,
30
+ "Dec": 48
31
+ }
32
+ },
33
+ "reference_rule": "next_b_day_after_last_trading_day_of_december"
34
+ },
35
+ "cme_gbp_futures": {
36
+ "shift_rule": {
37
+ "shift_period_type": "MONTH",
38
+ "n_shift": {
39
+ "Mar": 60,
40
+ "Jun": 60,
41
+ "Sep": 60,
42
+ "Dec": 60,
43
+ "Jan": 3,
44
+ "Feb": 3,
45
+ "Apr": 3,
46
+ "May": 3,
47
+ "Jul": 3,
48
+ "Aug": 3,
49
+ "Oct": 3,
50
+ "Nov": 3
51
+ }
52
+ },
53
+ "reference_rule": "next_b_day_after_period"
54
+ },
55
+ "cme_emini_snp500_futures": {
56
+ "shift_rule": {
57
+ "shift_period_type": "MONTH",
58
+ "n_shift": {
59
+ "Mar": 63,
60
+ "Jun": 63,
61
+ "Sep": 63,
62
+ "Dec": 63
63
+ }
64
+ },
65
+ "reference_rule": "next_b_day_after_period"
66
+ },
67
+ "nymex_natural_gas_futures": {
68
+ "shift_rule": {
69
+ "shift_period_type": "MONTH",
70
+ "n_shift": {
71
+ "Jan": 144,
72
+ "Feb": 144,
73
+ "Mar": 144,
74
+ "Apr": 144,
75
+ "May": 144,
76
+ "Jun": 144,
77
+ "Jul": 144,
78
+ "Aug": 144,
79
+ "Sep": 144,
80
+ "Oct": 144,
81
+ "Nov": 144,
82
+ "Dec": 144
83
+ }
84
+ },
85
+ "reference_rule": "next_b_day_after_last_trading_day_of_december"
86
+ }
87
+ }
88
+
@@ -0,0 +1,6 @@
1
+ product_id,venue,description,currency,unit,contract_size,valid_period_rule,listing_rule,period_types,settlement,last_trading_rule,expiry_rule,trading_calendar,trading_hours,tick_size,tick_value,initial_margin,maintenance_margin
2
+ comex_gold_futures,COMEX,Gold Futures,USD,TROY_OUNCE,100,FGHJKMNQUVXZ,Monthly contracts listed for 24 consecutive months and any Jun and Dec in the nearest 72 months.,MONTH,PHYSICAL,Trading terminates at 12:30 p.m. CT on the third last business day of the contract month.,last business day of the contract month.,CMES,Sunday - Friday 6:00 p.m. - 5:00 p.m. (5:00 p.m. - 4:00 p.m. /CT) with a 60-minute break each day beginning at 5:00 p.m. (4:00 p.m. CT),0.1,10,,
3
+ cbot_corn_futures,CBOT,Corn Futures,USD,BUSHEL,5000,HKNUZ,"9 monthly contracts of Mar, May, Sep and 8 monthly contracts of Jul and Dec listed annually after the termination of trading in the December contract of the current year.",MONTH,PHYSICAL,Trading terminates on the business day prior to the 15th day of the contract month.,Second business day following the last trading day of the delivery month.,CMES,Sunday - Friday: 7:00 p.m. - 7:45 a.m. CT and Monday - Friday: 8:30 a.m. - 1:20 p.m. CT,0.0025,12.5,,
4
+ cme_gbp_futures,CME,British Pound Futures,USD,GBP,62500,FGHJKMNQUVXZ,"Quarterly contracts (Mar, Jun, Sep, Dec) listed for 20 consecutive quarters and serial contracts listed for 3 months",MONTH,PHYSICAL,"Trading terminates at 9:16 a.m. CT, 2 business days prior to the third Wednesday of the contract month.",2 business days prior to the third Wednesday of the contract month.,CMES,Sunday - Friday 6:00 p.m. - 5:00 p.m. (5:00 p.m. - 4:00 p.m. CT) with a 60-minute break each day beginning at 5:00 p.m. (4:00 p.m. CT).,0.0001,6.25,,
5
+ cme_emini_snp500_futures,CME,S&P 500 E-mini Futures,USD,INDEX_POINT,50,HMUZ,"Quarterly contracts (Mar, Jun, Sep, Dec) listed for 21 consecutive quarters",MONTH,FINANCIAL,Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.,3rd Friday of the contract month.,CMES,Sunday 6:00 p.m. - Friday 5:00 p.m. ET (5:00 p.m. - 4:00 p.m. CT) with a daily maintenance period from 5:00 p.m. - 6:00 p.m. ET (4:00 p.m. - 5:00 p.m. CT),0.25,12.5,,
6
+ nymex_natural_gas_futures,NYMEX,HH Natural Gas Futures,USD,MMBTU,10000,FGHJKMNQUVXZ,Monthly contracts listed for the current year and the next 12 calendar years. List monthly contracts for a new calendar year following the termination of trading in the December contract of the current year.,MONTH,PHYSICAL,Trading terminates on the 3rd last business day of the month prior to the contract month. ,3rd last business day of the contract month.,CMES,Sunday - Friday 6:00 p.m. - 5:00 p.m. (5:00 p.m. - 4:00 p.m. CT) with a 60-minute break each day beginning at 5:00 p.m. (4:00 p.m. CT).,0.001,10,,
@@ -0,0 +1,34 @@
1
+ {
2
+ "comex_gold_futures": {
3
+ "period_offset": 0,
4
+ "reference_event": "business_day_of_period",
5
+ "n_reference": -3,
6
+ "business_day_offset": 0
7
+ },
8
+ "cbot_corn_futures": {
9
+ "period_offset": 0,
10
+ "reference_event": "calendar_day_of_period",
11
+ "n_reference": 15,
12
+ "business_day_offset": -1
13
+ },
14
+ "cme_gbp_futures": {
15
+ "period_offset": 0,
16
+ "reference_event": "weekday_of_period",
17
+ "n_reference": 3,
18
+ "weekday": "Wednesday",
19
+ "business_day_offset": -2
20
+ },
21
+ "cme_emini_snp500_futures": {
22
+ "period_offset": 0,
23
+ "reference_event": "weekday_of_period",
24
+ "n_reference": 3,
25
+ "weekday": "Friday",
26
+ "business_day_offset": 0
27
+ },
28
+ "nymex_natural_gas_futures": {
29
+ "period_offset": -1,
30
+ "reference_event": "business_day_of_period",
31
+ "n_reference": -3,
32
+ "business_day_offset": 0
33
+ }
34
+ }
@@ -0,0 +1 @@
1
+ """Database integration for refData."""
@@ -0,0 +1,135 @@
1
+ """SQL Session Manager for handling database interactions."""
2
+
3
+ import logging
4
+ from collections.abc import Callable, Iterator
5
+ from contextlib import contextmanager
6
+ from pathlib import Path
7
+
8
+ from sqlalchemy import Engine, create_engine
9
+ from sqlalchemy.orm import Session, sessionmaker
10
+ from sqlalchemy.sql import text
11
+
12
+ from mxm.refdata.models.orm import Base
13
+ from mxm.refdata.utils.config import load_config
14
+
15
+ logging.basicConfig(level=logging.INFO)
16
+
17
+
18
+ class SQLSessionManager:
19
+ """Manages database sessions while ensuring consistent engine initialization."""
20
+
21
+ def __init__(
22
+ self,
23
+ db_url: str | None = None,
24
+ engine: Engine | None = None,
25
+ session_factory: Callable[[], Session] | None = None,
26
+ ):
27
+ """
28
+ Initialize the SQLSessionManager with a specific engine and session factory.
29
+
30
+ Args:
31
+ engine (Optional[Engine]): The database engine to use.
32
+ session_factory (Optional[Callable[[], Session]]): A callable that provides new sessions.
33
+ """
34
+ config = load_config()
35
+ sql_db_url = db_url or config.SQL_DB_URL
36
+
37
+ # Ensure sqlite parent directory exists before engine creation
38
+ _ensure_sqlite_parent_dir(sql_db_url)
39
+
40
+ self.engine: Engine = engine or create_engine(sql_db_url, echo=False)
41
+ self.session_factory: Callable[[], Session] = session_factory or sessionmaker(
42
+ autocommit=False, autoflush=False, bind=self.engine
43
+ )
44
+
45
+ def get_engine(self) -> Engine:
46
+ """Return the configured database engine."""
47
+ return self.engine
48
+
49
+ def get_session_factory(self) -> Callable[[], Session]:
50
+ """Return the configured session factory."""
51
+ return self.session_factory
52
+
53
+ def get_db_session(self) -> Session:
54
+ """
55
+ Provide a new database session.
56
+
57
+ Returns:
58
+ Session: A new SQLAlchemy session.
59
+ """
60
+ return self.session_factory()
61
+
62
+ @contextmanager
63
+ def db_session_scope(self) -> Iterator[Session]:
64
+ """
65
+ Provide a transactional scope for database operations.
66
+
67
+ Yields:
68
+ Session: A transactional database session.
69
+ """
70
+ db_session = self.get_db_session()
71
+ try:
72
+ yield db_session
73
+ db_session.commit()
74
+ except Exception:
75
+ db_session.rollback()
76
+ raise
77
+ finally:
78
+ db_session.close()
79
+
80
+ def init_db(self) -> bool:
81
+ """Initialize the database schema."""
82
+ try:
83
+ logging.info("Initializing database schema...")
84
+ Base.metadata.create_all(bind=self.engine)
85
+ logging.info(f"Tables created: {Base.metadata.tables.keys()}")
86
+ return True
87
+ except Exception as e:
88
+ logging.error(f"Failed to initialize database schema: {e}")
89
+ return False
90
+
91
+ def drop_db(self) -> bool:
92
+ """Drop all tables in the database."""
93
+ try:
94
+ logging.info("Dropping all tables in the database...")
95
+ Base.metadata.drop_all(bind=self.engine)
96
+ logging.info("All tables dropped successfully.")
97
+ return True
98
+ except Exception as e:
99
+ logging.error(f"Failed to drop database: {e}")
100
+ return False
101
+
102
+ def check_db_connection(self) -> bool:
103
+ """Check if the database connection is active."""
104
+ try:
105
+ with self.engine.connect() as connection:
106
+ connection.execute(text("SELECT 1"))
107
+ logging.info("Database connection is active.")
108
+ return True
109
+ except Exception as e:
110
+ logging.error(f"Database connection check failed: {e}")
111
+ return False
112
+
113
+
114
+ def _ensure_sqlite_parent_dir(sql_db_url: str) -> None:
115
+ """
116
+ Ensure the parent directory exists for sqlite database URLs.
117
+
118
+ Only applies to absolute-path sqlite URLs of the form:
119
+ sqlite:////absolute/path/to/db.sqlite
120
+
121
+ Relative sqlite paths are intentionally not supported as defaults.
122
+ """
123
+ prefix = "sqlite:///"
124
+ if not sql_db_url.startswith(prefix):
125
+ return
126
+
127
+ raw_path = sql_db_url[len(prefix) :]
128
+ if not raw_path:
129
+ return
130
+
131
+ db_path = Path(raw_path)
132
+
133
+ # Only create directories for absolute paths
134
+ if db_path.is_absolute():
135
+ db_path.parent.mkdir(parents=True, exist_ok=True)
@@ -0,0 +1,31 @@
1
+ from mxm.refdata.mappings.futures_contract_vs_orm import (
2
+ futures_contract_from_orm,
3
+ futures_contract_to_orm,
4
+ )
5
+ from mxm.refdata.mappings.futures_product_vs_orm import (
6
+ futures_product_from_orm,
7
+ futures_product_to_orm,
8
+ )
9
+ from mxm.refdata.mappings.period_cycles_vs_orm import (
10
+ period_cycle_from_orm,
11
+ period_cycle_membership_from_orm,
12
+ period_cycle_membership_to_orm,
13
+ period_cycle_to_orm,
14
+ )
15
+ from mxm.refdata.mappings.period_vs_orm import (
16
+ period_from_orm,
17
+ period_to_orm,
18
+ )
19
+
20
+ __all__ = [
21
+ "futures_contract_from_orm",
22
+ "futures_contract_to_orm",
23
+ "futures_product_from_orm",
24
+ "futures_product_to_orm",
25
+ "period_cycle_from_orm",
26
+ "period_cycle_membership_from_orm",
27
+ "period_cycle_membership_to_orm",
28
+ "period_cycle_to_orm",
29
+ "period_from_orm",
30
+ "period_to_orm",
31
+ ]
@@ -0,0 +1,51 @@
1
+ """Mapping FuturesContract instances to and from FuturesContractORM instances."""
2
+
3
+ from mxm.refdata.models.contracts.futures_contract import FuturesContract
4
+ from mxm.refdata.models.orm import FuturesContractORM
5
+ from mxm.refdata.models.products.futures_product import Currency, ProductUnit
6
+
7
+
8
+ def futures_contract_to_orm(contract: FuturesContract) -> FuturesContractORM:
9
+ """
10
+ Map a FuturesContract object to a FuturesContractORM instance.
11
+
12
+ Args:
13
+ contract (FuturesContract): The internal FuturesContract object.
14
+
15
+ Returns:
16
+ FuturesContractORM: The corresponding ORM instance.
17
+ """
18
+ return FuturesContractORM(
19
+ contract_id=contract.contract_id,
20
+ product_id=contract.product_id,
21
+ period_id=contract.period_id,
22
+ contract_size=contract.contract_size,
23
+ unit=contract.unit,
24
+ currency=contract.currency,
25
+ trading_calendar=contract.trading_calendar,
26
+ first_day_of_interest=contract.first_day_of_interest,
27
+ last_trading_day=contract.last_trading_day,
28
+ )
29
+
30
+
31
+ def futures_contract_from_orm(orm: FuturesContractORM) -> FuturesContract:
32
+ """
33
+ Map a FuturesContractORM object to a FuturesContract object.
34
+
35
+ Args:
36
+ orm (FuturesContractORM): The ORM object to map from.
37
+
38
+ Returns:
39
+ FuturesContract: The mapped internal representation.
40
+ """
41
+ return FuturesContract(
42
+ product_id=orm.product_id, # Directly use product_id
43
+ period_id=orm.period_id, # Directly use period_id
44
+ contract_id=orm.contract_id,
45
+ contract_size=orm.contract_size,
46
+ currency=Currency[orm.currency.name], # Map string to Currency Enum
47
+ unit=ProductUnit[orm.unit.name], # Map string to ProductUnit Enum
48
+ trading_calendar=orm.trading_calendar,
49
+ first_day_of_interest=orm.first_day_of_interest,
50
+ last_trading_day=orm.last_trading_day,
51
+ )
@@ -0,0 +1,56 @@
1
+ """Mapping FututesProduct instances to and from FuturesProductORM instances."""
2
+
3
+ from mxm.refdata.models.orm import FuturesProductORM
4
+ from mxm.refdata.models.products.futures_product import FuturesProduct
5
+ from mxm.refdata.utils.period_types_codec import (
6
+ decode_period_types,
7
+ encode_period_types,
8
+ )
9
+
10
+
11
+ def futures_product_to_orm(product: FuturesProduct) -> FuturesProductORM:
12
+ """Map a FuturesProduct to a FuturesProductORM instance."""
13
+ return FuturesProductORM(
14
+ product_id=product.product_id,
15
+ venue=product.venue,
16
+ description=product.description,
17
+ currency=product.currency,
18
+ unit=product.unit,
19
+ contract_size=product.contract_size,
20
+ valid_period_rule=product.valid_period_rule,
21
+ listing_rule=product.listing_rule,
22
+ period_types=encode_period_types(product.period_types),
23
+ settlement=product.settlement,
24
+ last_trading_rule=product.last_trading_rule,
25
+ expiry_rule=product.expiry_rule,
26
+ trading_calendar=product.trading_calendar,
27
+ trading_hours=product.trading_hours,
28
+ tick_size=product.tick_size,
29
+ tick_value=product.tick_value,
30
+ initial_margin=product.initial_margin,
31
+ maintenance_margin=product.maintenance_margin,
32
+ )
33
+
34
+
35
+ def futures_product_from_orm(orm_instance: FuturesProductORM) -> FuturesProduct:
36
+ """Map a FuturesProductORM instance to a FuturesProduct."""
37
+ return FuturesProduct(
38
+ product_id=orm_instance.product_id,
39
+ venue=orm_instance.venue,
40
+ description=orm_instance.description,
41
+ currency=orm_instance.currency,
42
+ unit=orm_instance.unit,
43
+ contract_size=orm_instance.contract_size,
44
+ valid_period_rule=orm_instance.valid_period_rule,
45
+ listing_rule=orm_instance.listing_rule,
46
+ period_types=decode_period_types(orm_instance.period_types),
47
+ settlement=orm_instance.settlement,
48
+ last_trading_rule=orm_instance.last_trading_rule,
49
+ expiry_rule=orm_instance.expiry_rule,
50
+ trading_calendar=orm_instance.trading_calendar,
51
+ trading_hours=orm_instance.trading_hours,
52
+ tick_size=orm_instance.tick_size,
53
+ tick_value=orm_instance.tick_value,
54
+ initial_margin=orm_instance.initial_margin,
55
+ maintenance_margin=orm_instance.maintenance_margin,
56
+ )