trade-database-manager 0.0.3.dev1__tar.gz → 0.0.6.dev0__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 (25) hide show
  1. {trade_database_manager-0.0.3.dev1/trade_database_manager.egg-info → trade_database_manager-0.0.6.dev0}/PKG-INFO +1 -1
  2. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/pyproject.toml +2 -6
  3. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/kdb/__init__.py +1 -1
  4. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/kdb/kdbmanager.py +41 -17
  5. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/sql/__init__.py +2 -1
  6. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/sql/sqlmanager.py +67 -21
  7. trade_database_manager-0.0.6.dev0/trade_database_manager/manager/fields_data_type.py +33 -0
  8. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/manager/metadata_sql.py +53 -21
  9. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0/trade_database_manager.egg-info}/PKG-INFO +1 -1
  10. trade_database_manager-0.0.3.dev1/trade_database_manager/manager/fields_data_type.py +0 -23
  11. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/LICENSE +0 -0
  12. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/README.md +0 -0
  13. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/setup.cfg +0 -0
  14. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/__init__.py +0 -0
  15. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/config.py +0 -0
  16. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/__init__.py +0 -0
  17. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/sql/sqlreader.py +0 -0
  18. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/sql/sqlwriter.py +0 -0
  19. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/core/typedefs.py +0 -0
  20. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/manager/__init__.py +0 -0
  21. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager/manager/typedefs.py +0 -0
  22. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager.egg-info/SOURCES.txt +0 -0
  23. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager.egg-info/dependency_links.txt +0 -0
  24. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager.egg-info/requires.txt +0 -0
  25. {trade_database_manager-0.0.3.dev1 → trade_database_manager-0.0.6.dev0}/trade_database_manager.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trade_database_manager
3
- Version: 0.0.3.dev1
3
+ Version: 0.0.6.dev0
4
4
  Summary: A wrapper of kdb and sql for convenient trade data management.
5
5
  Author-email: "Y.Q. Cui" <qianyun210603@hotmail.com>
6
6
  Classifier: Operating System :: POSIX :: Linux
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "trade_database_manager"
7
- version = "0.0.3.dev1"
7
+ version = "0.0.6.dev0"
8
8
  description = "A wrapper of kdb and sql for convenient trade data management."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -30,7 +30,7 @@ include = ["trade_database_manager*"]
30
30
  const-rgx='[a-z_][a-z0-9_]{2,30}$'
31
31
 
32
32
  [tool.pylint.main]
33
- disable="C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R1702,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,E0401,E1121,C0103,C0209,R0402,R1705,R1710,R1725,R1735,W0102,W0212,W0221,W0223,W0231,W0237,W0612,W0621,W0622,W0703,W1309,E1102,E1136"
33
+ disable="C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R1702,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,E0401,E1121,C0103,C0209,R0402,R0917,R1705,R1710,R1725,R1735,W0102,W0212,W0221,W0223,W0231,W0237,W0612,W0621,W0622,W0703,W1309,E1102,E1136"
34
34
  ignore-paths="\\.ipynb_checkpoints/*"
35
35
 
36
36
  [tool.pylint.format]
@@ -43,7 +43,3 @@ target-version = ['py310']
43
43
  [tool.isort]
44
44
  profile = "black"
45
45
  line_length = 120
46
-
47
- [tool.flake8]
48
- max-line-length = 120
49
- ignore = "E203,E501,W503"
@@ -2,4 +2,4 @@
2
2
  # @Time : 2024/4/15 20:28
3
3
  # @Author : YQ Tsui
4
4
  # @File : __init__.py
5
- # @Purpose :
5
+ # @Purpose :
@@ -17,7 +17,6 @@ class KdbManager:
17
17
  def instance(cls):
18
18
  if cls._instance is None:
19
19
  cls._instance = cls()
20
- cls._instance.__init__()
21
20
  return cls._instance
22
21
 
23
22
  def __init__(self):
@@ -47,7 +46,7 @@ class KdbManager:
47
46
  :type path: str
48
47
  """
49
48
  with pykx.QConnection(self.host, self.port, username=self.username, password=self.password) as conn:
50
- conn(f".path.mkdir \"{path}\"")
49
+ conn(f'.path.mkdir "{path}"')
51
50
 
52
51
  def write(self, table_name: str, data: pd.DataFrame, path: str = "", splayed: bool = False):
53
52
  """
@@ -69,7 +68,9 @@ class KdbManager:
69
68
  with pykx.QConnection(self.host, self.port, username=self.username, password=self.password) as conn:
70
69
  conn(f"{{`:{real_path}/ set x}}", data)
71
70
 
72
- def write_partitioned(self, table_name: str, data: pd.DataFrame, path: str = "", partition_func=None, key_column=None):
71
+ def write_partitioned(
72
+ self, table_name: str, data: pd.DataFrame, path: str = "", partition_func=None, key_column=None
73
+ ):
73
74
  """
74
75
  Writes data to the kdb database with partitioning.
75
76
 
@@ -89,16 +90,31 @@ class KdbManager:
89
90
  assert "datetime" in data.columns, "datetime column not found"
90
91
  data.sort_values(by="datetime", inplace=True)
91
92
  if key_column is not None and key_column in data.columns:
92
- with pykx.QConnection(host=self.host, port=self.port, username=self.username, password=self.password) as conn:
93
+ with pykx.QConnection(
94
+ host=self.host, port=self.port, username=self.username, password=self.password
95
+ ) as conn:
93
96
  for bucket, df in data.groupby(partition_func(data["datetime"])):
94
- conn(f'{{`{table_name} set x; .partable.createOrAppend[`:{path};{bucket};`{key_column};`{table_name}]}}', df.reset_index(drop=True))
97
+ conn(
98
+ f"{{`{table_name} set x; .partable.createOrAppend[`:{path};{bucket};`{key_column};`{table_name}]}}",
99
+ df.reset_index(drop=True),
100
+ )
95
101
  else:
96
- with pykx.QConnection(host=self.host, port=self.port, username=self.username, password=self.password) as conn:
102
+ with pykx.QConnection(
103
+ host=self.host, port=self.port, username=self.username, password=self.password
104
+ ) as conn:
97
105
  for bucket, df in data.groupby(by=partition_func(data["datetime"])):
98
- conn(f'{{`{table_name} set x;.Q.dpt[`:{path};{bucket};`{table_name}]}}', df.reset_index(drop=True))
99
-
100
- def read_partitioned(self, table_name: str, path: str = "", fields=None, start_time=None, end_time=None, partition_func=None,
101
- other_conditions=None):
106
+ conn(f"{{`{table_name} set x;.Q.dpt[`:{path};{bucket};`{table_name}]}}", df.reset_index(drop=True))
107
+
108
+ def read_partitioned(
109
+ self,
110
+ table_name: str,
111
+ path: str = "",
112
+ fields=None,
113
+ start_time=None,
114
+ end_time=None,
115
+ partition_func=None,
116
+ other_conditions=None,
117
+ ):
102
118
  """
103
119
  Reads data from the kdb database with partitioning.
104
120
 
@@ -120,31 +136,39 @@ class KdbManager:
120
136
  end_time_str = end_time.strftime(time_format) if end_time is not None else None
121
137
  if isinstance(fields, (bytes, str)):
122
138
  fields = [fields]
123
- select_clause = f"select from {table_name}" if fields is None else f"select {','.join(fields)} from {table_name}"
139
+ select_clause = (
140
+ f"select from {table_name}" if fields is None else f"select {','.join(fields)} from {table_name}"
141
+ )
124
142
  where_cond = ""
125
143
  if start_time_str is not None and end_time_str is not None:
126
144
  where_cond += f"datetime within ({start_time_str};{end_time_str})"
127
145
  if partition_func is not None:
128
- where_cond = f"int in {' '.join(str(x) for x in range(partition_func(start_time), partition_func(end_time) + 1))}" + ',' + where_cond
146
+ where_cond = (
147
+ f"int in {' '.join(str(x) for x in range(partition_func(start_time), partition_func(end_time) + 1))}"
148
+ + ","
149
+ + where_cond
150
+ )
129
151
  elif start_time_str is not None:
130
152
  where_cond += f"datetime>={start_time_str}"
131
153
  if partition_func is not None:
132
- where_cond = f"int>={partition_func(start_time)}" + ',' + where_cond
154
+ where_cond = f"int>={partition_func(start_time)}" + "," + where_cond
133
155
  elif end_time_str is not None:
134
156
  where_cond += f"datetime<={end_time_str}"
135
157
  if partition_func is not None:
136
- where_cond = f"int<= {partition_func(end_time)}" + ',' + where_cond
158
+ where_cond = f"int<= {partition_func(end_time)}" + "," + where_cond
137
159
  if other_conditions is not None:
138
- where_cond = other_conditions if where_cond == "" else where_cond + ',' + other_conditions
160
+ where_cond = other_conditions if where_cond == "" else where_cond + "," + other_conditions
139
161
  if where_cond:
140
162
  where_cond = " where " + where_cond
141
163
  final_query = select_clause + where_cond
142
164
 
143
165
  with pykx.QConnection(host=self.host, port=self.port, username=self.username, password=self.password) as conn:
144
- conn("`currpath__ set .path.pwd[]") # save current path to currpath__ as following command will change the path
166
+ conn(
167
+ "`currpath__ set .path.pwd[]"
168
+ ) # save current path to currpath__ as following command will change the path
145
169
  try:
146
170
  conn(f"\\l {path}") # load the path
147
171
  q_table = conn(final_query)
148
172
  return q_table.pd().set_index("datetime")
149
173
  finally:
150
- conn('system "cd ", currpath__')
174
+ conn('system "cd ", currpath__')
@@ -2,4 +2,5 @@
2
2
  # @Time : 2024/4/15 20:28
3
3
  # @Author : YQ Tsui
4
4
  # @File : __init__.py
5
- # @Purpose :
5
+ # @Purpose :
6
+ from .sqlmanager import SqlManager
@@ -7,10 +7,11 @@
7
7
  import re
8
8
  from collections.abc import Container
9
9
  from functools import partial, reduce
10
- from typing import Any, Literal, Sequence, Type, Union
10
+ from typing import Any, Literal, Sequence, Union
11
11
 
12
12
  import pandas as pd
13
- from sqlalchemy import DOUBLE_PRECISION, Index, Integer, MetaData, String, Table, create_engine, inspect, select, sql, text
13
+ from sqlalchemy import Index, MetaData, Table, Column, create_engine, inspect, select, sql, text
14
+ from sqlalchemy.types import TypeEngine
14
15
  from sqlalchemy.dialects.postgresql import insert
15
16
 
16
17
  from ...config import CONFIG
@@ -41,6 +42,13 @@ class SqlManager:
41
42
 
42
43
  def __init__(self):
43
44
  self.engine = create_engine(CONFIG["sqlconnstr"])
45
+ self._inspector = None
46
+
47
+ @property
48
+ def inspector(self):
49
+ if self._inspector is None:
50
+ self._inspector = inspect(self.engine)
51
+ return self._inspector
44
52
 
45
53
  def _execute(self, sql_executable: Union[str, sql.base.Executable]) -> Any:
46
54
  if isinstance(sql_executable, str):
@@ -94,8 +102,7 @@ class SqlManager:
94
102
  :rtype: int
95
103
  """
96
104
  if_exists: Literal["replace", "append"] = "append"
97
- inspector = inspect(self.engine)
98
- new_table = not inspector.has_table(table_name)
105
+ new_table = not self.inspector.has_table(table_name)
99
106
  method = partial(_insert_on_conflict_update, indexes=df.index.names) if upsert and not new_table else None
100
107
  num_rows = df.to_sql(
101
108
  table_name,
@@ -113,17 +120,44 @@ class SqlManager:
113
120
  self.add_index(table_name, column, unique=False)
114
121
  return num_rows
115
122
 
116
- @staticmethod
117
- def _convert_to_sqlalchemy_type(column_type: Type, **kwargs):
118
- if isinstance(column_type, type):
119
- column_type = column_type.__name__
120
- if column_type == "str":
121
- return String(**kwargs)
122
- if column_type == "int":
123
- return Integer()
124
- if column_type == "float":
125
- return DOUBLE_PRECISION()
126
- raise ValueError(f"Unsupported column type {column_type}")
123
+ def table_exists(self, table_name: str) -> bool:
124
+ """
125
+ Checks if a table exists.
126
+
127
+ :param table_name: The name of the table to check for.
128
+ :type table_name: str
129
+ :return: True if the table exists, False otherwise.
130
+ :rtype: bool
131
+ """
132
+ return self.inspector.has_table(table_name)
133
+
134
+ def create_table(
135
+ self,
136
+ table_name: str,
137
+ table_columns: list[tuple[str, TypeEngine]],
138
+ unique_index_columns: Sequence[str] = (),
139
+ primary_key: Union[str, set[str]] = set(),
140
+ ):
141
+ """
142
+ Creates a table.
143
+
144
+ :param table_name: The name of the table to create.
145
+ :type table_name: str
146
+ :param table_columns: The names of the columns to create with their data types. The keys are the column names and the values are the data types.
147
+ :type table_columns: dict[str, Type]
148
+ :param unique_index_columns: Columns to enforce unique values on. Defaults to an empty sequence.
149
+ :type unique_index_columns: Sequence[str]
150
+ :param primary_key: The primary key(s) of the table. Defaults to an empty set.
151
+ :type primary_key: Union[str,set[str]]
152
+ """
153
+ table_meta = MetaData()
154
+ if isinstance(primary_key, str):
155
+ primary_key = {primary_key}
156
+ columns = [Column(name, col_type, primary_key=name in primary_key) for name, col_type in table_columns]
157
+ table = Table(table_name, table_meta, *columns)
158
+ table.create(self.engine)
159
+ for column in unique_index_columns:
160
+ self.add_index(table_name, column, unique=True)
127
161
 
128
162
  def insert_column(self, table_name: str, column_name: str, column_type: str):
129
163
  """
@@ -314,8 +348,14 @@ class SqlManager:
314
348
  res = self._execute(stmt)
315
349
  return pd.DataFrame(res.fetchall(), columns=res.keys())
316
350
 
317
- def create_ts_table(self, table_name: str, time_column: str, table_columns: dict[str, str],
318
- time_chunk_interval: str = "1 year", other_chunkers: dict[str, int] = None):
351
+ def create_ts_table(
352
+ self,
353
+ table_name: str,
354
+ time_column: str,
355
+ table_columns: dict[str, str],
356
+ time_chunk_interval: str = "1 year",
357
+ other_chunkers: dict[str, int] = None,
358
+ ):
319
359
  """
320
360
  Creates a table with a timestamp column.
321
361
 
@@ -330,19 +370,25 @@ class SqlManager:
330
370
  :param other_chunkers: Other columns to chunk by. The keys are the column names and the values are the num of hashes. Defaults to None.
331
371
  :type other_chunkers: Dict[str, int], optional
332
372
  """
333
- query_create_sensordata_table = f"""
373
+ query_create_sensordata_table = f"""
334
374
  CREATE TABLE {table_name} (
335
375
  {time_column} TIMESTAMP NOT NULL,
336
376
  {", ".join([f"{column_name} {column_type}{' NOT NULL' if column_name in other_chunkers else ''}" for column_name, column_type in table_columns.items()])}
337
377
  );
338
378
  """
339
- query_create_sensordata_hypertable = f"SELECT create_hypertable('{table_name}', by_range('{time_column}', INTERVAL '{time_chunk_interval}'));"
379
+ query_create_sensordata_hypertable = (
380
+ f"SELECT create_hypertable('{table_name}', by_range('{time_column}', INTERVAL '{time_chunk_interval}'));"
381
+ )
340
382
  if other_chunkers is not None:
341
383
  for column_name, num_hash in other_chunkers.items():
342
- query_create_sensordata_hypertable += f"\nSELECT add_dimension('{table_name}', by_hash('{column_name}', {num_hash}));"
384
+ query_create_sensordata_hypertable += (
385
+ f"\nSELECT add_dimension('{table_name}', by_hash('{column_name}', {num_hash}));"
386
+ )
343
387
 
344
388
  uidx_name = f"uix_{table_name}_{'_'.join(other_chunkers.keys())}_{time_column}"
345
- query_unique_index = f"CREATE UNIQUE INDEX {uidx_name} ON {table_name} ({', '.join(other_chunkers.keys())}, {time_column});"
389
+ query_unique_index = (
390
+ f"CREATE UNIQUE INDEX {uidx_name} ON {table_name} ({', '.join(other_chunkers.keys())}, {time_column});"
391
+ )
346
392
  with self.engine.connect() as conn:
347
393
  conn.execute(text(query_create_sensordata_table))
348
394
  conn.execute(text(query_create_sensordata_hypertable))
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Time : 2024/4/19 16:44
3
+ # @Author : YQ Tsui
4
+ # @File : fields_data_type.py
5
+ # @Purpose :
6
+
7
+ from sqlalchemy import DOUBLE_PRECISION, Integer, String, Date
8
+
9
+ FIELD_DATA_TYPE_SQL = {
10
+ "ticker": String(20),
11
+ "name": String(20),
12
+ "currency": String(6),
13
+ "exchange": String(10),
14
+ "timezone": String(30),
15
+ "tick_size": DOUBLE_PRECISION(),
16
+ "lot_size": DOUBLE_PRECISION(),
17
+ "min_lots": DOUBLE_PRECISION(),
18
+ "market_tplus": Integer(),
19
+ "listed_date": Date(),
20
+ "delisted_date": Date(),
21
+ "country": String(6),
22
+ # STK
23
+ "sector": String(30),
24
+ "industry": String(36),
25
+ "board_type": String(200),
26
+ # LOF & ETF
27
+ "issuer": String(60),
28
+ "current_mgr": String(60),
29
+ "custodian": String(60),
30
+ "issuer_country": String(6),
31
+ "fund_type": String(20),
32
+ "benchmark": String(60),
33
+ }
@@ -11,6 +11,8 @@ import pandas as pd
11
11
 
12
12
  from ..core.sql.sqlmanager import SqlManager
13
13
  from .typedefs import EXCHANGE_LITERALS, INST_TYPE_LITERALS, Opt_T_SeqT, T_DictT
14
+ from .fields_data_type import FIELD_DATA_TYPE_SQL, String
15
+
14
16
 
15
17
  COMMON_METADATA_COLUMNS = [
16
18
  "name",
@@ -26,7 +28,9 @@ COMMON_METADATA_COLUMNS = [
26
28
  "delisted_date",
27
29
  ]
28
30
  TYPE_METADATA_COLUMNS = {
29
- "STK": ["sector", "industry", "country", "state", "board_type", "issue_price"],
31
+ "STK": ["country", "state", "board_type", "issue_price"],
32
+ "ETF": ["issuer", "current_mgr", "custodian", "issuer_country", "fund_type", "benchmark"],
33
+ "LOF": ["issuer", "current_mgr", "custodian", "issuer_country", "fund_type", "benchmark"],
30
34
  }
31
35
 
32
36
 
@@ -46,6 +50,24 @@ class MetadataSql:
46
50
  cls._manager = SqlManager()
47
51
  return cls._instance
48
52
 
53
+ def initialize(self, for_inst_types="all"):
54
+ if for_inst_types == "all":
55
+ for_inst_types = list(TYPE_METADATA_COLUMNS.keys())
56
+ elif isinstance(for_inst_types, str):
57
+ for_inst_types = [for_inst_types]
58
+ columns_base = [("ticker", String(10)), ("exchange", String(10))]
59
+ if not self._manager.table_exists("instruments"):
60
+ columns = columns_base + [(col, FIELD_DATA_TYPE_SQL.get(col, String())) for col in COMMON_METADATA_COLUMNS]
61
+ self._manager.create_table("instruments", columns, {"primary_key": ["ticker", "exchange"]})
62
+ for inst_type in for_inst_types:
63
+ if not self._manager.table_exists(f"instruments_{inst_type.lower()}"):
64
+ columns = columns_base + [
65
+ (col, FIELD_DATA_TYPE_SQL.get(col, String())) for col in TYPE_METADATA_COLUMNS[inst_type]
66
+ ]
67
+ self._manager.create_table(
68
+ f"instruments_{inst_type.lower()}", columns, primary_key={"ticker", "exchange"}
69
+ )
70
+
49
71
  def update_instrument_metadata(self, data: Union[pd.DataFrame, list[dict], dict]):
50
72
  """
51
73
  Updates the instrument metadata in the database.
@@ -70,10 +92,12 @@ class MetadataSql:
70
92
 
71
93
  self._manager.insert("instruments", data_common, upsert=True)
72
94
  if "inst_type" in data.columns:
73
- for inst_type, columns in TYPE_METADATA_COLUMNS.items():
74
- data_type_df = data.loc[data.inst_type == inst_type, data.columns.intersection(columns)]
75
- if not data_type_df.empty:
76
- self._manager.insert(f"instruments_{inst_type.lower()}", data_type_df, upsert=True)
95
+ g = data.groupby("inst_type", group_keys=False)
96
+ for inst_type, data_type_df in g:
97
+ if not data_type_df.empty and inst_type in TYPE_METADATA_COLUMNS:
98
+ columns = data_type_df.columns.intersection(TYPE_METADATA_COLUMNS[inst_type])
99
+ if not columns.empty:
100
+ self._manager.insert(f"instruments_{inst_type.lower()}", data_type_df[columns], upsert=True)
77
101
 
78
102
  def _convert_datetime_columns(self, data: pd.DataFrame):
79
103
  for col in ["listed_date", "delisted_date"]:
@@ -132,8 +156,12 @@ class MetadataSql:
132
156
  res = {}
133
157
  for inst_type, common_df_by_type in common_df.groupby("inst_type"):
134
158
  inst_type = cast(INST_TYPE_LITERALS, inst_type)
135
- if all_fields_common:
136
- res[inst_type] = common_df_by_type.set_index(["ticker", "exchange"]) if len(common_df_by_type.columns) > 2 else common_df_by_type
159
+ if all_fields_common or inst_type not in TYPE_METADATA_COLUMNS:
160
+ res[inst_type] = (
161
+ common_df_by_type.set_index(["ticker", "exchange"])
162
+ if len(common_df_by_type.columns) > 2
163
+ else common_df_by_type
164
+ )
137
165
  continue
138
166
  query_fields_type = (
139
167
  ["ticker", "exchange"] + [f for f in query_fields if f in TYPE_METADATA_COLUMNS[inst_type]]
@@ -187,7 +215,7 @@ class MetadataSql:
187
215
  query_fields_cross = "*"
188
216
  else:
189
217
  query_fields_common = ["ticker", "exchange"] + [f for f in query_fields if f in COMMON_METADATA_COLUMNS]
190
- query_fields_type = [f for f in query_fields if f in TYPE_METADATA_COLUMNS[inst_type]]
218
+ query_fields_type = [f for f in query_fields if f in TYPE_METADATA_COLUMNS.get(inst_type, [])]
191
219
  query_fields_cross = {
192
220
  "instruments": query_fields_common,
193
221
  f"instruments_{inst_type.lower()}": query_fields_type,
@@ -196,19 +224,23 @@ class MetadataSql:
196
224
  filter_fields_common = {
197
225
  k: v for k, v in filter_fields.items() if k in ["ticker", "exchange"] + COMMON_METADATA_COLUMNS
198
226
  }
199
- filter_fields_type = {
200
- k: v for k, v in filter_fields.items() if k in ["ticker", "exchange"] + TYPE_METADATA_COLUMNS[inst_type]
201
- }
202
- filter_fields_cross = {
203
- "instruments": filter_fields_common,
204
- f"instruments_{inst_type.lower()}": filter_fields_type,
205
- }
206
- df = self._manager.read_data_across_tables(
207
- ["instruments", f"instruments_{inst_type.lower()}"],
208
- joined_columns=["ticker", "exchange"],
209
- query_fields=query_fields_cross,
210
- filter_fields=filter_fields_cross,
211
- )
227
+ filter_fields_type = {k: v for k, v in filter_fields.items() if k in TYPE_METADATA_COLUMNS.get(inst_type, [])}
228
+
229
+ if (query_fields != "*" or inst_type not in TYPE_METADATA_COLUMNS) and not bool(query_fields_type) and not bool(filter_fields_type):
230
+ df = self._manager.read_data(
231
+ "instruments", query_fields=query_fields_common, filter_fields=filter_fields_common
232
+ )
233
+ else:
234
+ filter_fields_cross = {
235
+ "instruments": filter_fields_common,
236
+ f"instruments_{inst_type.lower()}": filter_fields_type,
237
+ }
238
+ df = self._manager.read_data_across_tables(
239
+ ["instruments", f"instruments_{inst_type.lower()}"],
240
+ joined_columns=["ticker", "exchange"],
241
+ query_fields=query_fields_cross,
242
+ filter_fields=filter_fields_cross,
243
+ )
212
244
 
213
245
  if isinstance(df.columns, pd.Index):
214
246
  df = df.loc[:, ~df.columns.duplicated()]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trade_database_manager
3
- Version: 0.0.3.dev1
3
+ Version: 0.0.6.dev0
4
4
  Summary: A wrapper of kdb and sql for convenient trade data management.
5
5
  Author-email: "Y.Q. Cui" <qianyun210603@hotmail.com>
6
6
  Classifier: Operating System :: POSIX :: Linux
@@ -1,23 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- # @Time : 2024/4/19 16:44
3
- # @Author : YQ Tsui
4
- # @File : fields_data_type.py
5
- # @Purpose :
6
-
7
- FIELD_DATA_TYPE_SQL = {
8
- "ticker": "VARCHAR(20)",
9
- "name": "VARCHAR(20)",
10
- "currency": "VARCHAR(6)",
11
- "exchange": "VARCHAR(10)",
12
- "timezone": "VARCHAR(30)",
13
- "tick_size": "REAL",
14
- "lot_size": "REAL",
15
- "min_lots": "REAL",
16
- "market_tplus": "INTEGER",
17
- "listed_date": "DATE",
18
- "delisted_date": "DATE",
19
- "sector": "VARCHAR(30)",
20
- "industry": "VARCHAR(36)",
21
- "country": "VARCHAR(36)",
22
- "board_type": "VARCHAR(20)",
23
- }