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.
- {trade_database_manager-0.0.1.dev0/trade_database_manager.egg-info → trade_database_manager-0.0.1.dev1}/PKG-INFO +7 -2
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/README.md +4 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/pyproject.toml +4 -3
- trade_database_manager-0.0.1.dev1/trade_database_manager/core/sql/sqlmanager.py +102 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/tools/init_config.py +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
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/requires.txt +1 -1
- trade_database_manager-0.0.1.dev0/trade_database_manager/core/sql/sqlmanager.py +0 -57
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/LICENSE +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/setup.cfg +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/setup.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/__init__.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/config.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/__init__.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/kdb/__init__.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/__init__.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/sqlreader.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/core/sql/sqlwriter.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager/tools/__init__.py +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/SOURCES.txt +0 -0
- {trade_database_manager-0.0.1.dev0 → trade_database_manager-0.0.1.dev1}/trade_database_manager.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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
|
|
@@ -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.
|
|
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 = ['
|
|
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
|
-
|
|
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="",
|
|
18
|
-
parser.add_argument("--sqldb", type=str, default="trade_data",
|
|
19
|
-
parser.add_argument("--kdbhost", type=str, default="",
|
|
20
|
-
parser.add_argument("--sqlport", type=str, default="5432",
|
|
21
|
-
parser.add_argument("--kdbport", type=str, default="5000",
|
|
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.
|
|
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,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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|