trade-database-manager 0.0.1.dev0__tar.gz → 0.0.1.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 (22) hide show
  1. {trade_database_manager-0.0.1.dev0/trade_database_manager.egg-info → trade_database_manager-0.0.1.dev1}/PKG-INFO +7 -2
  2. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/README.md +4 -0
  3. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/pyproject.toml +4 -3
  4. trade_database_manager-0.0.1.dev1/trade_database_manager/core/sql/sqlmanager.py +102 -0
  5. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/tools/init_config.py +6 -6
  6. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1/trade_database_manager.egg-info}/PKG-INFO +7 -2
  7. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/requires.txt +1 -1
  8. trade_database_manager-0.0.1.dev0/trade_database_manager/core/sql/sqlmanager.py +0 -57
  9. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/LICENSE +0 -0
  10. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/setup.cfg +0 -0
  11. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/setup.py +0 -0
  12. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/__init__.py +0 -0
  13. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/config.py +0 -0
  14. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/__init__.py +0 -0
  15. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/kdb/__init__.py +0 -0
  16. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/__init__.py +0 -0
  17. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/sqlreader.py +0 -0
  18. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/sqlwriter.py +0 -0
  19. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/tools/__init__.py +0 -0
  20. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/SOURCES.txt +0 -0
  21. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/dependency_links.txt +0 -0
  22. {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/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.1.dev0
3
+ Version: 0.0.1.dev1
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
@@ -11,9 +11,10 @@ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
14
15
  Description-Content-Type: text/markdown
15
16
  License-File: LICENSE
16
- Requires-Dist: pandas
17
+ Requires-Dist: pandas>=2.0.0
17
18
  Requires-Dist: numpy
18
19
  Requires-Dist: ruamel.yaml
19
20
  Requires-Dist: sqlalchemy
@@ -49,3 +50,7 @@ host all tradedbadmin 0.0.0.0/0 scram-sha-256
49
50
  host all tradedbadmin ::/0 scram-sha-256
50
51
  ```
51
52
 
53
+ ### KDB+ Initialization
54
+
55
+
56
+ ```q
@@ -28,3 +28,7 @@ host all tradedbadmin 0.0.0.0/0 scram-sha-256
28
28
  host all tradedbadmin ::/0 scram-sha-256
29
29
  ```
30
30
 
31
+ ### KDB+ Initialization
32
+
33
+
34
+ ```q
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "trade_database_manager"
7
- version = "0.0.1.dev0"
7
+ version = "0.0.1.dev1"
8
8
  description = "A wrapper of kdb and sql for convenient trade data management."
9
9
  readme = "README.md"
10
10
  authors = [
11
11
  {name = "Y.Q. Cui", email = "qianyun210603@hotmail.com"},
12
12
  ]
13
- dependencies = ["pandas", "numpy", "ruamel.yaml", "sqlalchemy", "psycopg2"]
13
+ dependencies = ["pandas>=2.0.0", "numpy", "ruamel.yaml", "sqlalchemy", "psycopg2"]
14
14
  classifiers = [
15
15
  "Operating System :: POSIX :: Linux",
16
16
  "Operating System :: Microsoft :: Windows",
@@ -20,6 +20,7 @@ classifiers = [
20
20
  "Programming Language :: Python :: 3",
21
21
  "Programming Language :: Python :: 3.9",
22
22
  "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
23
24
  ]
24
25
 
25
26
  [tool.setuptools.packages.find]
@@ -37,7 +38,7 @@ max-line-length = "120"
37
38
 
38
39
  [tool.black]
39
40
  line-length = 120
40
- target-version = ['py39']
41
+ target-version = ['py310']
41
42
 
42
43
  [tool.isort]
43
44
  profile = "black"
@@ -0,0 +1,102 @@
1
+ from functools import partial
2
+ from typing import Literal, Sequence, Union
3
+
4
+ import pandas as pd
5
+ from sqlalchemy import create_engine, inspect
6
+ from sqlalchemy.dialects.postgresql import insert
7
+ import re
8
+ from ...config import CONFIG
9
+
10
+
11
+ def _insert_on_conflict_update(table, conn, keys, data_iter, indexes):
12
+ data = [dict(zip(keys, row)) for row in data_iter]
13
+ stmt = insert(table.table).values(data)
14
+ stmt = stmt.on_conflict_do_update(index_elements=indexes, set_={k: getattr(stmt.excluded, k) for k in keys})
15
+ result = conn.execute(stmt)
16
+ return result.rowcount
17
+
18
+
19
+ def _insert_on_conflict_nothing(table, conn, keys, data_iter):
20
+ data = [dict(zip(keys, row)) for row in data_iter]
21
+ stmt = insert(table.table).values(data).on_conflict_do_nothing(index_elements=keys)
22
+ result = conn.execute(stmt)
23
+ return result.rowcount
24
+
25
+
26
+ class SqlManager:
27
+ def __init__(
28
+ self,
29
+ ):
30
+ self.engine = create_engine(CONFIG["sqlconnstr"])
31
+
32
+ def add_index(self, table_name: str, columns: Union[str, Sequence[str]], unique: bool = True):
33
+ if isinstance(columns, str):
34
+ columns = [columns]
35
+ index_name = f"uix_{table_name}_{'_'.join(columns)}"
36
+ columns_str = ", ".join(columns)
37
+ unique_str = "UNIQUE" if unique else ""
38
+ self.engine.execute(f"CREATE {unique_str} INDEX {index_name} ON {table_name} ({columns_str})")
39
+
40
+ def insert(
41
+ self,
42
+ table_name: str,
43
+ df: pd.DataFrame,
44
+ upsert: bool = True,
45
+ other_unique_index_columns: Sequence[str] = (),
46
+ other_non_unique_index_columns: Sequence[str] = (),
47
+ ):
48
+ if_exists: Literal["replace", "append"] = "append"
49
+ inspector = inspect(self.engine)
50
+ new_table = not inspector.has_table(table_name)
51
+ method = (
52
+ partial(_insert_on_conflict_update, indexes=df.index.names)
53
+ if upsert and not new_table
54
+ else None
55
+ )
56
+ num_rows = df.to_sql(
57
+ table_name,
58
+ self.engine,
59
+ if_exists=if_exists,
60
+ index=True,
61
+ index_label=df.index.names,
62
+ method=method,
63
+ )
64
+ if new_table:
65
+ self.add_index(table_name, df.index.names)
66
+ for column in other_unique_index_columns:
67
+ self.add_index(table_name, column, unique=True)
68
+ for column in other_non_unique_index_columns:
69
+ self.add_index(table_name, column, unique=False)
70
+ return num_rows
71
+
72
+ def rename_column(self, table_name: str, old_column_name: str, new_column_name: str):
73
+ # check if any index is referring to the column
74
+ sql_code = f"SELECT indexname, indexdef FROM pg_indexes WHERE indexdef LIKE '%%(%%{old_column_name}%%)%%' and tablename = '{table_name}'"
75
+ index_refering_column = dict(self.engine.execute(sql_code).fetchall())
76
+
77
+ # drop indexes referring to the column
78
+ for index_name in index_refering_column:
79
+ self.engine.execute(f"DROP INDEX IF EXISTS {index_name}")
80
+
81
+ # rename the column
82
+ self.engine.execute(f"ALTER TABLE {table_name} RENAME COLUMN {old_column_name} TO {new_column_name}")
83
+
84
+ # recreate the indexes
85
+ for index_name, index_def in index_refering_column.items():
86
+ new_index_name = index_name.replace(old_column_name, new_column_name)
87
+ new_index_def = re.sub(rf"\(([^)]*?){old_column_name}([^)]*?)\)", rf"(\1{new_column_name}\2)", index_def)
88
+ new_index_def = new_index_def.replace(index_name, new_index_name)
89
+ self.engine.execute(new_index_def)
90
+
91
+ def read_range_data(
92
+ self, table_name: str, query_fields, start_time: pd.Timestamp, end_time: pd.Timestamp, filter_fields=None
93
+ ):
94
+ sql_code = f"SELECT {', '.join(query_fields)} FROM {table_name} WHERE end_time >= '{start_time}' AND start_time <= '{end_time}'"
95
+ if filter_fields:
96
+ sql_code += (
97
+ f" AND {' AND '.join([f'{field} IN {tuple(filter_values)}' for field, filter_values in filter_fields])}"
98
+ )
99
+
100
+ # cannot use pandas.read_sql here as it discards timezone info
101
+ res = self.engine.execute(sql_code)
102
+ return pd.DataFrame(res.fetchall(), columns=res.keys())
@@ -12,13 +12,13 @@ if __name__ == "__main__":
12
12
  "--host",
13
13
  type=str,
14
14
  default="localhost",
15
- description="host of both kdb and sql, will be overwritten if sqlhost or kdbhost is specified",
15
+ help="host of both kdb and sql, will be overwritten if sqlhost or kdbhost is specified",
16
16
  )
17
- parser.add_argument("--sqlhost", type=str, default="", description="host of sql")
18
- parser.add_argument("--sqldb", type=str, default="trade_data", description="database name of sql")
19
- parser.add_argument("--kdbhost", type=str, default="", description="host of kdb")
20
- parser.add_argument("--sqlport", type=str, default="5432", description="port of sql")
21
- parser.add_argument("--kdbport", type=str, default="5000", description="port of kdb")
17
+ parser.add_argument("--sqlhost", type=str, default="", help="host of sql")
18
+ parser.add_argument("--sqldb", type=str, default="trade_data", help="database name of sql")
19
+ parser.add_argument("--kdbhost", type=str, default="", help="host of kdb")
20
+ parser.add_argument("--sqlport", type=str, default="5432", help="port of sql")
21
+ parser.add_argument("--kdbport", type=str, default="5000", help="port of kdb")
22
22
 
23
23
  args = parser.parse_args()
24
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: trade_database_manager
3
- Version: 0.0.1.dev0
3
+ Version: 0.0.1.dev1
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
@@ -11,9 +11,10 @@ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
14
15
  Description-Content-Type: text/markdown
15
16
  License-File: LICENSE
16
- Requires-Dist: pandas
17
+ Requires-Dist: pandas>=2.0.0
17
18
  Requires-Dist: numpy
18
19
  Requires-Dist: ruamel.yaml
19
20
  Requires-Dist: sqlalchemy
@@ -49,3 +50,7 @@ host all tradedbadmin 0.0.0.0/0 scram-sha-256
49
50
  host all tradedbadmin ::/0 scram-sha-256
50
51
  ```
51
52
 
53
+ ### KDB+ Initialization
54
+
55
+
56
+ ```q
@@ -1,4 +1,4 @@
1
- pandas
1
+ pandas>=2.0.0
2
2
  numpy
3
3
  ruamel.yaml
4
4
  sqlalchemy
@@ -1,57 +0,0 @@
1
- from functools import partial
2
- from typing import Literal, Sequence, Union
3
-
4
- import pandas as pd
5
- from sqlalchemy import create_engine
6
- from sqlalchemy.dialects.postgresql import insert
7
-
8
- from ...config import CONFIG
9
-
10
-
11
- def _insert_on_conflict_update(table, conn, keys, data_iter, indexes):
12
- data = [dict(zip(keys, row)) for row in data_iter]
13
- stmt = insert(table.table).values(data)
14
- stmt = stmt.on_conflict_do_update(index_elements=indexes, set_={k: getattr(stmt.excluded, k) for k in keys})
15
- result = conn.execute(stmt)
16
- return result.rowcount
17
-
18
-
19
- def _insert_on_conflict_nothing(table, conn, keys, data_iter):
20
- data = [dict(zip(keys, row)) for row in data_iter]
21
- stmt = insert(table.table).values(data).on_conflict_do_nothing(index_elements=keys)
22
- result = conn.execute(stmt)
23
- return result.rowcount
24
-
25
-
26
- class SqlManager:
27
- def __init__(
28
- self,
29
- ):
30
- self.engine = create_engine(CONFIG["sqlconnstr"])
31
-
32
- def _add_unique_index(self, table_name: str, columns: Union[str, Sequence[str]]):
33
- if isinstance(columns, str):
34
- columns = [columns]
35
- index_name = f"uix_{table_name}_{'_'.join(columns)}"
36
- columns_str = ", ".join(columns)
37
- self.engine.execute(f"CREATE UNIQUE INDEX {index_name} ON {table_name} ({columns_str})")
38
-
39
- def insert(self, table_name: str, df: pd.DataFrame, upsert: bool = True):
40
- if_exists: Literal["replace", "append"] = "append"
41
- method = (
42
- partial(_insert_on_conflict_update, indexes=df.index.names)
43
- if upsert and self.engine.has_table(table_name)
44
- else None
45
- )
46
- new_table = not self.engine.has_table(table_name)
47
- num_rows = df.to_sql(
48
- table_name,
49
- self.engine,
50
- if_exists=if_exists,
51
- index=True,
52
- index_label=df.index.names,
53
- method=method,
54
- )
55
- if new_table:
56
- self._add_unique_index(table_name, df.index.names)
57
- return num_rows