data-finder 0.1.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 (37) hide show
  1. data-finder/calc/src/calc/__init__.py +0 -0
  2. data-finder/calc/src/calc/calc_protocol.py +56 -0
  3. data-finder/calc/src/calc/simple_price.py +24 -0
  4. data-finder/calc/tests/test_calc.py +52 -0
  5. data-finder/datafinder/src/datafinder/__init__.py +6 -0
  6. data-finder/datafinder/src/datafinder/attribute.py +26 -0
  7. data-finder/datafinder/src/datafinder/finder.py +7 -0
  8. data-finder/datafinder/src/datafinder/operation.py +162 -0
  9. data-finder/datafinder/src/datafinder/output.py +15 -0
  10. data-finder/datafinder/src/datafinder/runner.py +30 -0
  11. data-finder/datafinder/src/datafinder/typed_attributes.py +43 -0
  12. data-finder/datafinder/src/datafinder/typed_operations.py +80 -0
  13. data-finder/datafinder_duckdb/__init__.py +0 -0
  14. data-finder/datafinder_duckdb/duckdb_engine.py +30 -0
  15. data-finder/datafinder_generator/src/datafinder_generator/generator.py +25 -0
  16. data-finder/datafinder_ibis/src/datafinder_ibis/__init__.py +0 -0
  17. data-finder/datafinder_ibis/src/datafinder_ibis/ibis_engine.py +31 -0
  18. data-finder/datafinder_ibis/tests/test_ibis_engine.py +8 -0
  19. data-finder/datafinder_ibis_duckdb/tests/test_datafinder_ibis_duckdb.py +53 -0
  20. data-finder/example/__init__.py +0 -0
  21. data-finder/example/account_finder.py +37 -0
  22. data-finder/example/contractualposition_finder.py +56 -0
  23. data-finder/example/instrument_finder.py +37 -0
  24. data-finder/example/mappings.py +106 -0
  25. data-finder/example/queries.py +29 -0
  26. data-finder/example/trade_finder.py +47 -0
  27. data-finder/example_duckdb/__init__.py +0 -0
  28. data-finder/example_duckdb/duckdb_example.py +20 -0
  29. data-finder/example_duckdb/duckdb_trade_finder.py +24 -0
  30. data-finder/model/src/model/__init__.py +0 -0
  31. data-finder/model/src/model/m3.py +51 -0
  32. data-finder/model/src/model/mapping.py +19 -0
  33. data-finder/model/src/model/relational.py +41 -0
  34. data_finder-0.1.0.dist-info/METADATA +38 -0
  35. data_finder-0.1.0.dist-info/RECORD +37 -0
  36. data_finder-0.1.0.dist-info/WHEEL +5 -0
  37. data_finder-0.1.0.dist-info/top_level.txt +1 -0
File without changes
@@ -0,0 +1,56 @@
1
+ from abc import ABC
2
+
3
+ from numpy import ndarray
4
+
5
+ from datafinder import Attribute
6
+
7
+
8
+ class DomainAwareCalcProtocol:
9
+ def name(self):
10
+ raise NotImplementedError
11
+
12
+ # TODO should this be an ordered set ? to avoid duplicates
13
+ def inputs_spec(self) -> list[Attribute]:
14
+ raise NotImplementedError
15
+
16
+ def output_spec(self) -> Attribute:
17
+ raise NotImplementedError
18
+
19
+ #TODO should have key and time?
20
+ def calculate(self, inputs:ndarray) -> []:
21
+ raise NotImplementedError
22
+
23
+
24
+ # From https://charlesreid1.github.io/python-patterns-the-registry.html
25
+ class RegistryBase(type):
26
+
27
+ REGISTRY = {}
28
+
29
+ def __new__(cls, name, bases, attrs):
30
+ # instantiate a new type corresponding to the type of class being defined
31
+ # this is currently RegisterBase but in child classes will be the child class
32
+ new_cls = type.__new__(cls, name, bases, attrs)
33
+ cls.REGISTRY[new_cls.__name__] = new_cls
34
+ return new_cls
35
+
36
+ @classmethod
37
+ def get_registry(cls):
38
+ return dict(cls.REGISTRY)
39
+
40
+
41
+ class BaseRegisteredClass(metaclass=RegistryBase):
42
+ pass
43
+
44
+
45
+ class CalcEngineRegistry(metaclass=RegistryBase):
46
+ calcs = {}
47
+
48
+ @staticmethod
49
+ def register(calc:DomainAwareCalcProtocol):
50
+ CalcEngineRegistry.calcs[calc.name()] = calc
51
+
52
+
53
+ class DomainAwareCalc(DomainAwareCalcProtocol, ABC):
54
+
55
+ def __init__(self):
56
+ CalcEngineRegistry.register(self)
@@ -0,0 +1,24 @@
1
+ from calc.calc_protocol import DomainAwareCalc
2
+ from contractualposition_finder import ContractualPositionFinder
3
+ from datafinder import Attribute
4
+ from numpy import array
5
+
6
+
7
+ def npv(quantity: array, price: array):
8
+ return quantity * price
9
+
10
+
11
+ class PositionNPVCalc(DomainAwareCalc):
12
+
13
+ def name(self) -> str:
14
+ return 'npv'
15
+
16
+ def inputs_spec(self) -> list[Attribute]:
17
+ return [ContractualPositionFinder.quantity(),
18
+ ContractualPositionFinder.instrument().price()]
19
+
20
+ def calculate(self, inputs):
21
+ return npv(inputs[0], inputs[1])
22
+
23
+ def output_spec(self) -> Attribute:
24
+ return ContractualPositionFinder.npv()
@@ -0,0 +1,52 @@
1
+ import datetime
2
+
3
+ import duckdb
4
+ import numpy
5
+
6
+ from calc.calc_protocol import CalcEngineRegistry
7
+ from numpy.testing import assert_array_almost_equal
8
+
9
+ from datafinder import QueryRunnerBase
10
+ from mappings import generate_mappings
11
+
12
+
13
+ class TestCalc:
14
+
15
+ def setup(self):
16
+ #Register the Ibis engine
17
+ from datafinder_ibis.ibis_engine import IbisConnect
18
+ assert QueryRunnerBase.get_runner() == IbisConnect
19
+
20
+ generate_mappings()
21
+ con = duckdb.connect('test.db')
22
+ con.execute("DROP TABLE IF EXISTS contractualposition;")
23
+ con.execute(
24
+ "CREATE TABLE contractualposition(DATE DATE, INSTRUMENT VARCHAR, CPTY_ID INT, QUANTITY DOUBLE); COPY contractualposition FROM 'data/contractualpositions.csv'")
25
+ con.sql("SELECT * from contractualposition").show()
26
+
27
+ con.execute("DROP TABLE IF EXISTS price;")
28
+ con.execute(
29
+ "CREATE TABLE price(DATE_TIME DATETIME, SYM VARCHAR, PRICE DOUBLE); COPY price FROM 'data/prices.csv'")
30
+
31
+ def test_price(self):
32
+ self.setup()
33
+ # TODO - we have to import this first for it to register due to Python's dynamic nature
34
+ # need to do imports to force the load = https://stackoverflow.com/questions/73829483/register-classes-in-different-files-to-a-class-factory
35
+ from calc.simple_price import PositionNPVCalc
36
+ calc = PositionNPVCalc()
37
+ assert len(CalcEngineRegistry.calcs) == 1
38
+
39
+ inputs = calc.inputs_spec()
40
+
41
+ #Calc run
42
+ from contractualposition_finder import ContractualPositionFinder
43
+ input_data = ContractualPositionFinder.find_all(datetime.date.today(), datetime.date.today(), "LATEST",
44
+ inputs).to_numpy()
45
+
46
+ output = calc.calculate(input_data)
47
+
48
+ assert_array_almost_equal(output, numpy.array([200000.0, 54999.95]), decimal=2)
49
+
50
+
51
+
52
+
@@ -0,0 +1,6 @@
1
+ from .attribute import *
2
+ from .operation import *
3
+ from .typed_attributes import *
4
+ from .typed_operations import *
5
+ from .output import *
6
+ from .runner import *
@@ -0,0 +1,26 @@
1
+ from typing import Any
2
+
3
+
4
+ class Attribute:
5
+ __name: str
6
+ __column_db_type: str
7
+ __owner: str
8
+ __parent: Any
9
+
10
+ def __init__(self, name: str, column_db_type: str, owner:str, parent=None):
11
+ self.__name = name
12
+ self.__column_db_type = column_db_type
13
+ self.__owner = owner
14
+ self.__parent = parent
15
+
16
+ def column_name(self) -> str:
17
+ return self.__name
18
+
19
+ def column_type(self) -> str:
20
+ return self.__column_db_type
21
+
22
+ def owner(self) -> str:
23
+ return self.__owner
24
+
25
+ def parent(self) -> Any:
26
+ return self.__parent
@@ -0,0 +1,7 @@
1
+ from datafinder import JoinOperation, Attribute
2
+
3
+
4
+ class RelatedFinder:
5
+ def __init__(self, source: Attribute, target: Attribute):
6
+ self.__join = JoinOperation(source, target)
7
+
@@ -0,0 +1,162 @@
1
+ import datetime
2
+
3
+ from datafinder.attribute import Attribute
4
+
5
+
6
+ class TableAlias:
7
+ def __init__(self, table: str, alias: str):
8
+ self.table = table
9
+ self.alias = alias
10
+
11
+
12
+ class ColumnAlias:
13
+ def __init__(self, column_name: str, table_alias: TableAlias):
14
+ self.column_name = column_name
15
+ self.table_alias = table_alias
16
+
17
+
18
+ class Join:
19
+ def __init__(self, source: ColumnAlias, target: ColumnAlias):
20
+ self.source = source
21
+ self.target = target
22
+
23
+
24
+ class QueryEngine:
25
+ _select: list[ColumnAlias]
26
+ _from: set[TableAlias]
27
+ _where: list[str]
28
+ _join: list[Join]
29
+ __table_alias_incr: int
30
+
31
+ def __init__(self):
32
+ self._where = []
33
+ self._select = []
34
+ self._from = set()
35
+ self._join = []
36
+ self.__table_alias_incr = 0
37
+ self.__table_aliases_by_table = {}
38
+
39
+ def select(self, cols: list[Attribute]):
40
+ for col in cols:
41
+ table = col.owner()
42
+ ta = self.__table_alias_for_table(table)
43
+ parent: JoinOperation = col.parent()
44
+ if parent is not None:
45
+ left = parent.left
46
+ sc = ColumnAlias(left.column_name(), self.__table_alias_for_table(left.owner()))
47
+ right = parent.right
48
+ tc = ColumnAlias(right.column_name(), self.__table_alias_for_table(right.owner()))
49
+ self._join.append(Join(sc, tc))
50
+ else:
51
+ self._from.add(ta)
52
+ ca = ColumnAlias(col.column_name(), ta)
53
+ self._select.append(ca)
54
+
55
+ def __table_alias_for_table(self, table: str) -> TableAlias:
56
+ ta = None
57
+ if table in self.__table_aliases_by_table:
58
+ ta = self.__table_aliases_by_table[table]
59
+ else:
60
+ ta = TableAlias(table, "t" + str(self.__table_alias_incr))
61
+ self.__table_alias_incr = self.__table_alias_incr + 1
62
+ self.__table_aliases_by_table[table] = ta
63
+ return ta
64
+
65
+ def append_where_binary_clause(self, op: str):
66
+ self._where.append(op)
67
+
68
+ def append_where_clause(self, attr: Attribute, op: str, value: str):
69
+ ta = self.__table_alias_for_table(attr.owner())
70
+ self._where.append(ta.alias + '.' + attr.column_name() + ' ' + op + ' ' + value)
71
+
72
+ def build_query_string(self) -> str:
73
+ joins = map(lambda j: ' LEFT OUTER JOIN ' + j.target.table_alias.table + ' AS ' + j.target.table_alias.alias +
74
+ ' ON ' + j.source.table_alias.alias + '.' + j.source.column_name + ' = ' +
75
+ j.target.table_alias.alias + '.' + j.target.column_name, self._join)
76
+ return 'SELECT ' + ','.join(map(lambda ca: ca.table_alias.alias + '.' + ca.column_name, self._select)) \
77
+ + ' FROM ' + ','.join(map(lambda ta: ta.table + ' AS ' + ta.alias, self._from)) \
78
+ + ''.join(joins) \
79
+ + self.__build_where()
80
+
81
+ def __build_where(self) -> str:
82
+ if len(self._where) > 0:
83
+ return ' WHERE ' + ''.join(self._where)
84
+ else:
85
+ return ''
86
+
87
+ def where_clauses(self):
88
+ return self._where
89
+
90
+ def start_and(self):
91
+ pass
92
+
93
+ def end_and(self):
94
+ pass
95
+
96
+
97
+ # Interface
98
+ class Operation:
99
+
100
+ def generate_query(self, query: QueryEngine):
101
+ pass
102
+
103
+
104
+ class NoOperation(Operation):
105
+ def __init__(self):
106
+ pass
107
+
108
+
109
+ class SelectOperation(Operation):
110
+ def __init__(self, display: list[Attribute], table: str, filter: Operation):
111
+ self.__display = display
112
+ self.__table = table
113
+ self.__filter = filter
114
+
115
+ def generate_query(self, qe: QueryEngine):
116
+ qe.select(self.__display)
117
+ self.__filter.generate_query(qe)
118
+
119
+
120
+ class AndOperation(Operation):
121
+ __left: Operation
122
+ __right: Operation
123
+
124
+ def __init__(self, lhs: Operation, rhs: Operation):
125
+ self.__left = lhs
126
+ self.__right = rhs
127
+
128
+ def generate_query(self, query: QueryEngine):
129
+ query.start_and()
130
+ self.__left.generate_query(query)
131
+ query.append_where_binary_clause(" and ")
132
+ self.__right.generate_query(query)
133
+ query.end_and()
134
+
135
+
136
+ class BusinessTemporalOperation(Operation):
137
+ # TODO - which date format should we use
138
+ __business_date_from_inclusive: datetime.date
139
+ __business_date_to_inclusive: datetime.date
140
+
141
+
142
+ class BaseOperation(Operation):
143
+
144
+ def and_op(self, rhs: Operation):
145
+ return AndOperation(self, rhs)
146
+
147
+
148
+ class JoinOperation(Operation):
149
+ left: Attribute
150
+ right: Attribute
151
+
152
+ def __init__(self, lhs: Attribute, rhs: Attribute):
153
+ self.left = lhs
154
+ self.right = rhs
155
+
156
+
157
+ def select_sql_to_string(columns: list[Attribute], table: str, op: Operation) -> str:
158
+ qe = QueryEngine()
159
+ select = SelectOperation(columns, table, op)
160
+ select.generate_query(qe)
161
+ return qe.build_query_string()
162
+
@@ -0,0 +1,15 @@
1
+ import numpy as np
2
+ import pandas as pd
3
+
4
+
5
+ class ToNumpy:
6
+
7
+ def to_numpy(self) -> np.array:
8
+ pass
9
+
10
+
11
+ class DataFrame(ToNumpy):
12
+
13
+ def to_pandas(self) -> pd.DataFrame:
14
+ pass
15
+
@@ -0,0 +1,30 @@
1
+ from datafinder import Attribute, Operation, DataFrame
2
+
3
+
4
+ class RegistryBase(type):
5
+ REGISTRY = {}
6
+
7
+ def __new__(cls, name, bases, attrs):
8
+ # instantiate a new type corresponding to the type of class being defined
9
+ # this is currently RegisterBase but in child classes will be the child class
10
+ new_cls = type.__new__(cls, name, bases, attrs)
11
+ cls.REGISTRY[new_cls.__name__] = new_cls
12
+ return new_cls
13
+
14
+ @classmethod
15
+ def get_registry(cls):
16
+ return dict(cls.REGISTRY)
17
+
18
+
19
+ class QueryRunnerBase(metaclass=RegistryBase):
20
+
21
+ @staticmethod
22
+ def select(columns: list[Attribute], table: str, op: Operation) -> DataFrame:
23
+ pass
24
+
25
+ @staticmethod
26
+ def get_runner():
27
+ for k in RegistryBase.REGISTRY.keys():
28
+ if k != 'QueryRunnerBase':
29
+ return RegistryBase.REGISTRY[k]
30
+ raise Exception("No query runner registered")
@@ -0,0 +1,43 @@
1
+ from .operation import *
2
+ from .typed_operations import *
3
+
4
+
5
+ class StringAttribute(Attribute):
6
+
7
+ def __init__(self, name: str, column_db_type: str, owner:str, parent=None):
8
+ super().__init__(name, column_db_type, owner, parent)
9
+
10
+ def eq(self, value: str) -> Operation:
11
+ return StringEqOperation(self, value)
12
+
13
+ def __eq__(self, value: str) -> Operation:
14
+ return StringEqOperation(self, value)
15
+
16
+
17
+ class FloatAttribute(Attribute):
18
+
19
+ def __init__(self, name: str, column_db_type: str, owner:str, parent=None):
20
+ super().__init__(name, column_db_type, owner, parent)
21
+
22
+ def eq(self, value: float) -> Operation:
23
+ return PrimitiveEqOperation(self, value)
24
+
25
+ def __eq__(self, value: float) -> Operation:
26
+ return PrimitiveEqOperation(self, value)
27
+
28
+ def __gt__(self, value: float) -> Operation:
29
+ return PrimitiveGreaterThanOperation(self, value)
30
+
31
+ class IntegerAttribute(Attribute):
32
+
33
+ def __init__(self, name: str, column_db_type: str, owner:str, parent=None):
34
+ super().__init__(name, column_db_type, owner, parent)
35
+
36
+ def eq(self, value: float) -> Operation:
37
+ return PrimitiveEqOperation(self, value)
38
+
39
+ def __eq__(self, value: float) -> Operation:
40
+ return PrimitiveEqOperation(self, value)
41
+
42
+ def __gt__(self, value: float) -> Operation:
43
+ return PrimitiveGreaterThanOperation(self, value)
@@ -0,0 +1,80 @@
1
+ from .operation import BaseOperation, QueryEngine
2
+ from .attribute import Attribute
3
+
4
+
5
+ class EqOperation(BaseOperation):
6
+ __attribute: Attribute
7
+
8
+ def __init__(self, attrib: Attribute):
9
+ self.__attribute = attrib
10
+
11
+ def attribute(self) -> Attribute:
12
+ return self.__attribute
13
+
14
+ def column_type(self) -> str:
15
+ return self.__attribute.column_type()
16
+
17
+ def column_name(self) -> str:
18
+ return self.__attribute.column_name()
19
+
20
+ def prepare_value(self) -> str:
21
+ pass
22
+
23
+
24
+ class PrimitiveEqOperation(EqOperation):
25
+ __value: []
26
+
27
+ def __init__(self, attrib: Attribute, value):
28
+ super().__init__(attrib)
29
+ self.__value = value
30
+
31
+ def generate_query(self, query: QueryEngine):
32
+ query.append_where_clause(self.attribute(), '=', self.prepare_value())
33
+
34
+ def prepare_value(self) -> str:
35
+ return str(self.__value)
36
+
37
+
38
+ class StringEqOperation(EqOperation):
39
+ __value: str
40
+
41
+ def __init__(self, attrib: Attribute, value: str):
42
+ super().__init__(attrib)
43
+ self.__value = value
44
+
45
+ def generate_query(self, query: QueryEngine):
46
+ query.append_where_clause(self.attribute(), 'LIKE', self.prepare_value())
47
+
48
+ def prepare_value(self) -> str:
49
+ #TODO - String escaper
50
+ if self.column_type() == 'kx_symbol':
51
+ return "`" + self.__value
52
+ else:
53
+ return "'" + self.__value + "'"
54
+
55
+
56
+ class GreaterThanOperation(BaseOperation):
57
+ __attribute: Attribute
58
+
59
+ def column_type(self) -> str:
60
+ return self.__attribute.column_type()
61
+
62
+ def __init__(self, attrib: Attribute):
63
+ self.__attribute = attrib
64
+
65
+ def generate_query(self, query: QueryEngine):
66
+ query.append_where_clause(self.__attribute, '>', self.prepare_value())
67
+
68
+ def prepare_value(self) -> str:
69
+ pass
70
+
71
+
72
+ class PrimitiveGreaterThanOperation(GreaterThanOperation):
73
+ __value: []
74
+
75
+ def __init__(self, attrib: Attribute, value):
76
+ super().__init__(attrib)
77
+ self.__value = value
78
+
79
+ def prepare_value(self) -> str:
80
+ return str(self.__value)
File without changes
@@ -0,0 +1,30 @@
1
+ from datafinder import Operation, DataFrame, Attribute, select_sql_to_string
2
+
3
+ import duckdb
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+
8
+ class DuckDbConnect:
9
+
10
+ @staticmethod
11
+ def select(columns: list[Attribute], table: str, op: Operation) -> list:
12
+ conn = duckdb.connect('test.db')
13
+ query = select_sql_to_string(columns, table, op)
14
+ print(query)
15
+ # TODO this is inefficient, could convert straight to desired output - such as numpy, instead of list
16
+ return conn.sql(query).fetchall()
17
+
18
+
19
+ class DuckDbOutput(DataFrame):
20
+ __table: list
21
+
22
+ def __init__(self, t: list):
23
+ self.__table = t
24
+
25
+ def to_numpy(self) -> np.array:
26
+ return np.array(self.__table)
27
+
28
+ def to_pandas(self) -> pd.DataFrame:
29
+ #todo - this needs to be better, to ensure types and column names
30
+ return pd.DataFrame(self.__table)
@@ -0,0 +1,25 @@
1
+ import os
2
+
3
+ from model.m3 import PrimitiveType, Property
4
+ from jinja2 import Environment, PackageLoader
5
+
6
+ from model.mapping import Mapping
7
+
8
+
9
+ def is_primitive(prop: Property) -> bool:
10
+ return isinstance(prop.type, PrimitiveType)
11
+
12
+
13
+ def generate(mapping:Mapping, output_directory):
14
+ environment = Environment(loader=PackageLoader("datafinder_generator"), trim_blocks=True, lstrip_blocks=True)
15
+ template = environment.get_template("finder_template.txt")
16
+
17
+ for rcm in mapping.mappings:
18
+ filename = f"{rcm.clazz.name.lower()}_finder.py"
19
+ filepath = os.path.join(output_directory, filename)
20
+ content = template.render(rcm=rcm,is_primitive=is_primitive)
21
+ with open(filepath, mode="w", encoding="utf-8") as message:
22
+ message.write(content)
23
+ print(f"... wrote {filename}")
24
+
25
+
@@ -0,0 +1,31 @@
1
+ from datafinder import Operation, DataFrame, Attribute, select_sql_to_string
2
+
3
+ import ibis
4
+ import numpy as np
5
+ import pandas as pd
6
+
7
+ from datafinder import QueryRunnerBase
8
+
9
+
10
+ class IbisConnect(QueryRunnerBase):
11
+
12
+ @staticmethod
13
+ def select(columns: list[Attribute], table: str, op: Operation) -> DataFrame:
14
+ conn = ibis.connect('duckdb://test.db')
15
+ query = select_sql_to_string(columns, table, op)
16
+ print(query)
17
+ t = conn.table(table)
18
+ #todo - can also do this with the dataframe API
19
+ return IbisOutput(t.sql(query))
20
+
21
+
22
+ class IbisOutput(DataFrame):
23
+
24
+ def __init__(self, t: ibis.Table):
25
+ self.__table = t
26
+
27
+ def to_numpy(self) -> np.array:
28
+ return self.__table.__array__()
29
+
30
+ def to_pandas(self) -> pd.DataFrame:
31
+ return self.__table.to_pandas()
@@ -0,0 +1,8 @@
1
+ from datafinder import QueryRunnerBase
2
+
3
+ class TestIbisEngine:
4
+
5
+ def test_initialization(self):
6
+ from datafinder_ibis.ibis_engine import IbisConnect
7
+ out = QueryRunnerBase.get_runner()
8
+ assert out == IbisConnect
@@ -0,0 +1,53 @@
1
+ import duckdb
2
+ import datetime
3
+
4
+ import numpy as np
5
+
6
+ from datafinder import QueryRunnerBase
7
+ from example import queries
8
+ from numpy.testing import assert_array_equal
9
+
10
+ from mappings import generate_mappings
11
+
12
+
13
+ class TestDataFinderIbisDuckDb:
14
+
15
+ def setup(self):
16
+ #Register the Ibis engine
17
+ from datafinder_ibis.ibis_engine import IbisConnect
18
+ assert QueryRunnerBase.get_runner() == IbisConnect
19
+ generate_mappings()
20
+ con = duckdb.connect('test.db')
21
+ con.sql("SELECT 42 AS x").show()
22
+ con.execute("DROP TABLE IF EXISTS trade;")
23
+ con.execute(
24
+ "CREATE TABLE trade(id INT, account_id INT, sym VARCHAR, price DOUBLE); COPY trade FROM 'data/trades.csv'")
25
+ con.sql("SELECT * from trade").show()
26
+ con.sql("SELECT * from trade where sym LIKE 'AAPL'").show()
27
+
28
+ con.execute("DROP TABLE IF EXISTS account;")
29
+ con.execute(
30
+ "CREATE TABLE account(id INT, name VARCHAR); COPY account FROM 'data/accounts.csv'")
31
+
32
+ def test_queries(self):
33
+ self.setup()
34
+ # Import after generation, so we get the latest version
35
+ from trade_finder import TradeFinder
36
+ queries.find_trades(TradeFinder)
37
+ from account_finder import AccountFinder
38
+ np_accts = AccountFinder \
39
+ .find_all(datetime.date.today(), datetime.date.today(), "LATEST",
40
+ [AccountFinder.id(), AccountFinder.name()],
41
+ AccountFinder.id().eq(211978)) \
42
+ .to_numpy()
43
+ print(np_accts)
44
+ assert_array_equal(np_accts, np.array([[211978, 'Trading Account 1']],dtype=object))
45
+
46
+
47
+ trades_with_account = TradeFinder.find_all(datetime.date.today(), datetime.date.today(), "LATEST",
48
+ [TradeFinder.account().name(), TradeFinder.symbol(),
49
+ TradeFinder.price()],
50
+ TradeFinder.symbol().eq("AAPL"))
51
+ np_trades = trades_with_account.to_numpy()
52
+ print(np_trades)
53
+ assert_array_equal(np_trades, np.array([['Trading Account 1', 'AAPL', 84.11]], dtype=object))
File without changes
@@ -0,0 +1,37 @@
1
+ from datafinder.typed_attributes import *
2
+ from datafinder import QueryRunnerBase, DataFrame
3
+
4
+
5
+ class AccountFinder:
6
+ __table = 'account'
7
+
8
+ __id = IntegerAttribute('id', 'INT', 'account')
9
+ __name = StringAttribute('name', 'VARCHAR', 'account')
10
+
11
+ @staticmethod
12
+ def id() -> IntegerAttribute:
13
+ return AccountFinder.__id
14
+
15
+ @staticmethod
16
+ def name() -> StringAttribute:
17
+ return AccountFinder.__name
18
+
19
+ @staticmethod
20
+ def find_all(date_from: datetime.date, date_to: datetime.date, as_of: str,
21
+ display_columns: list[Attribute],
22
+ filter_op: Operation = NoOperation()) -> DataFrame:
23
+ return QueryRunnerBase.get_runner().select(display_columns, AccountFinder.__table, filter_op)
24
+
25
+
26
+ class AccountRelatedFinder:
27
+ def __init__(self, source: Attribute, target: Attribute):
28
+ join = JoinOperation(source,target)
29
+ self.__id = IntegerAttribute('id', 'INT', 'account', join)
30
+ self.__name = StringAttribute('name', 'VARCHAR', 'account', join)
31
+
32
+ def id(self) -> IntegerAttribute:
33
+ return self.__id
34
+
35
+ def name(self) -> StringAttribute:
36
+ return self.__name
37
+
@@ -0,0 +1,56 @@
1
+ from datafinder.typed_attributes import *
2
+ from datafinder import QueryRunnerBase, DataFrame
3
+ from instrument_finder import InstrumentRelatedFinder
4
+
5
+
6
+ class ContractualPositionFinder:
7
+ __table = 'contractualposition'
8
+
9
+ __quantity = FloatAttribute('QUANTITY', 'DOUBLE', 'contractualposition')
10
+ __counterparty = IntegerAttribute('CPTY_ID', 'INT', 'contractualposition')
11
+ __npv = FloatAttribute('NPV', 'DOUBLE', 'contractualposition')
12
+ __instrument = InstrumentRelatedFinder(Attribute('INSTRUMENT', 'VARCHAR', 'contractualposition'),Attribute('SYM', 'VARCHAR', 'price'))
13
+
14
+ @staticmethod
15
+ def quantity() -> FloatAttribute:
16
+ return ContractualPositionFinder.__quantity
17
+
18
+ @staticmethod
19
+ def counterparty() -> IntegerAttribute:
20
+ return ContractualPositionFinder.__counterparty
21
+
22
+ @staticmethod
23
+ def instrument() -> InstrumentRelatedFinder:
24
+ return ContractualPositionFinder.__instrument
25
+
26
+ @staticmethod
27
+ def npv() -> FloatAttribute:
28
+ return ContractualPositionFinder.__npv
29
+
30
+ @staticmethod
31
+ def find_all(date_from: datetime.date, date_to: datetime.date, as_of: str,
32
+ display_columns: list[Attribute],
33
+ filter_op: Operation = NoOperation()) -> DataFrame:
34
+ return QueryRunnerBase.get_runner().select(display_columns, ContractualPositionFinder.__table, filter_op)
35
+
36
+
37
+ class ContractualPositionRelatedFinder:
38
+ def __init__(self, source: Attribute, target: Attribute):
39
+ join = JoinOperation(source,target)
40
+ self.__quantity = FloatAttribute('QUANTITY', 'DOUBLE', 'contractualposition', join)
41
+ self.__counterparty = IntegerAttribute('CPTY_ID', 'INT', 'contractualposition', join)
42
+ self.__npv = FloatAttribute('NPV', 'DOUBLE', 'contractualposition', join)
43
+ self.__instrument = InstrumentRelatedFinder(Attribute('INSTRUMENT', 'VARCHAR', 'contractualposition'),Attribute('SYM', 'VARCHAR', 'price'))
44
+
45
+ def quantity(self) -> FloatAttribute:
46
+ return self.__quantity
47
+
48
+ def counterparty(self) -> IntegerAttribute:
49
+ return self.__counterparty
50
+
51
+ def instrument(self) -> InstrumentRelatedFinder:
52
+ return self.__instrument
53
+
54
+ def npv(self) -> FloatAttribute:
55
+ return self.__npv
56
+
@@ -0,0 +1,37 @@
1
+ from datafinder.typed_attributes import *
2
+ from datafinder import QueryRunnerBase, DataFrame
3
+
4
+
5
+ class InstrumentFinder:
6
+ __table = 'price'
7
+
8
+ __symbol = StringAttribute('SYM', 'VARCHAR', 'price')
9
+ __price = FloatAttribute('PRICE', 'DOUBLE', 'price')
10
+
11
+ @staticmethod
12
+ def symbol() -> StringAttribute:
13
+ return InstrumentFinder.__symbol
14
+
15
+ @staticmethod
16
+ def price() -> FloatAttribute:
17
+ return InstrumentFinder.__price
18
+
19
+ @staticmethod
20
+ def find_all(date_from: datetime.date, date_to: datetime.date, as_of: str,
21
+ display_columns: list[Attribute],
22
+ filter_op: Operation = NoOperation()) -> DataFrame:
23
+ return QueryRunnerBase.get_runner().select(display_columns, InstrumentFinder.__table, filter_op)
24
+
25
+
26
+ class InstrumentRelatedFinder:
27
+ def __init__(self, source: Attribute, target: Attribute):
28
+ join = JoinOperation(source,target)
29
+ self.__symbol = StringAttribute('SYM', 'VARCHAR', 'price', join)
30
+ self.__price = FloatAttribute('PRICE', 'DOUBLE', 'price', join)
31
+
32
+ def symbol(self) -> StringAttribute:
33
+ return self.__symbol
34
+
35
+ def price(self) -> FloatAttribute:
36
+ return self.__price
37
+
@@ -0,0 +1,106 @@
1
+ import os
2
+
3
+ from datafinder_generator.generator import generate
4
+ from model.m3 import Class, Property, String, Float, Package, Integer, Date
5
+ from model.mapping import Mapping
6
+ from model.relational import Column, Table, RelationalClassMapping, RelationalPropertyMapping, Join
7
+
8
+
9
+ def create_account_class() -> Class:
10
+ p1 = Property('id', Integer)
11
+ p2 = Property('name', String)
12
+
13
+ account_c = Class('Account', [p1, p2], Package('finance'))
14
+ return account_c
15
+
16
+
17
+ def create_instrument_class() -> Class:
18
+ p1 = Property('symbol', String)
19
+ #TODO this doesn't belong here, but using for simple example
20
+ p2 = Property('price', Float)
21
+
22
+ instrument_c = Class('Instrument', [p1, p2], Package('finance'))
23
+ return instrument_c
24
+
25
+
26
+ def create_trade_class(account:Class) -> Class:
27
+ p1 = Property('symbol', String)
28
+ p2 = Property('price', Float)
29
+ p3 = Property('account', account)
30
+
31
+ trade_c = Class('Trade', [p1, p2, p3], Package('finance'))
32
+ return trade_c
33
+
34
+
35
+ def create_contractual_position_class(instrument:Class) -> Class:
36
+ p1 = Property('date', Date)
37
+ p2 = Property('quantity', Float)
38
+ p3 = Property('counterparty', Integer)
39
+ p4 = Property('instrument', instrument)
40
+ p5 = Property('npv', Float)
41
+ pos_c = Class('ContractualPosition', [p1, p2, p3, p4, p5], Package('finance'))
42
+ return pos_c
43
+
44
+
45
+ def create_mappings_normalized() -> Mapping:
46
+ account_c = create_account_class()
47
+
48
+ ac1 = Column('id', 'INT')
49
+ ac2 = Column('name', 'VARCHAR')
50
+ account_t = Table('account', [ac1, ac2])
51
+
52
+ instrument_c = create_instrument_class()
53
+ ic1 = Column('SYM', 'VARCHAR')
54
+ ic2 = Column('PRICE', 'DOUBLE')
55
+ instrument_t = Table('price', [ic1,ic2])
56
+
57
+ c_position_c = create_contractual_position_class(instrument_c)
58
+ p1 = Column('DATE', 'DATE')
59
+ p2 = Column('INSTRUMENT', 'VARCHAR')
60
+ p3 = Column('CPTY_ID', 'INT')
61
+ p4 = Column('QUANTITY', 'DOUBLE')
62
+ p5 = Column('NPV', 'DOUBLE')
63
+ pos_t = Table('contractualposition', [p1, p2, p3, p4, p5])
64
+
65
+ trade_c = create_trade_class(account_c)
66
+
67
+ c1 = Column('id', 'INT')
68
+ c2 = Column('account_id', 'INT')
69
+ c3 = Column('sym', 'VARCHAR')
70
+ c4 = Column('price', 'DOUBLE')
71
+
72
+ trade_t = Table('trade', [c1, c2, c3, c4])
73
+
74
+ pm1 = RelationalPropertyMapping(trade_c.property('symbol'), c3)
75
+ pm2 = RelationalPropertyMapping(trade_c.property('price'), c4)
76
+ pm3 = RelationalPropertyMapping(trade_c.property('account'),Join(c2,ac1))
77
+ rm_t = RelationalClassMapping(trade_c, [pm1, pm2, pm3])
78
+
79
+ a_pm1 = RelationalPropertyMapping(account_c.property('id'), ac1)
80
+ a_pm2 = RelationalPropertyMapping(account_c.property('name'), ac2)
81
+ rm_a = RelationalClassMapping(account_c, [a_pm1, a_pm2])
82
+
83
+ i_pm1 = RelationalPropertyMapping(instrument_c.property('symbol'), ic1)
84
+ i_pm2 = RelationalPropertyMapping(instrument_c.property('price'), ic2)
85
+ rm_i = RelationalClassMapping(instrument_c, [i_pm1, i_pm2])
86
+
87
+
88
+ cpm1 = RelationalPropertyMapping(c_position_c.property('quantity'), p4)
89
+ cpm2 = RelationalPropertyMapping(c_position_c.property('counterparty'), p3)
90
+ cpm3 = RelationalPropertyMapping(c_position_c.property('instrument'), Join(p2,ic1))
91
+ cpm4 = RelationalPropertyMapping(c_position_c.property('npv'), p5)
92
+ rm_cp = RelationalClassMapping(c_position_c, [cpm1, cpm2, cpm3, cpm4])
93
+
94
+ return Mapping('Test Mapping 1', [rm_t,rm_a,rm_i,rm_cp])
95
+
96
+
97
+ def generate_mappings():
98
+ rcms = create_mappings_normalized()
99
+ import sys
100
+ mn = sys.modules[__name__]
101
+ directory = os.path.dirname(mn.__file__)
102
+ generate(rcms, directory)
103
+
104
+
105
+ if __name__ == '__main__':
106
+ generate_mappings()
@@ -0,0 +1,29 @@
1
+ import datetime
2
+
3
+
4
+ def find_trades(trade_finder):
5
+ print(f'Finding trades')
6
+
7
+ trades = trade_finder.find_all(datetime.date.today(), datetime.date.today(), "LATEST",
8
+ [trade_finder.symbol(), trade_finder.price()],
9
+ trade_finder.symbol().eq("AAPL"))
10
+ np_trades = trades.to_numpy()
11
+ print(np_trades)
12
+ df = trades.to_pandas()
13
+ print(df)
14
+
15
+ trades = trade_finder.find_all(datetime.date.today(), datetime.date.today(), "LATEST",
16
+ [trade_finder.symbol(), trade_finder.price()],
17
+ trade_finder.price() > 200.0,)
18
+ np_trades = trades.to_numpy()
19
+ print(np_trades)
20
+ df = trades.to_pandas()
21
+ print(df)
22
+
23
+ trades = trade_finder.find_all(datetime.date.today(), datetime.date.today(), "LATEST",
24
+ [trade_finder.symbol(), trade_finder.price()],
25
+ (trade_finder.symbol() == "AAPL").and_op(trade_finder.price() == 84.11))
26
+ np_trades = trades.to_numpy()
27
+ print(np_trades)
28
+ df = trades.to_pandas()
29
+ print(df)
@@ -0,0 +1,47 @@
1
+ from datafinder.typed_attributes import *
2
+ from datafinder import QueryRunnerBase, DataFrame
3
+ from account_finder import AccountRelatedFinder
4
+
5
+
6
+ class TradeFinder:
7
+ __table = 'trade'
8
+
9
+ __symbol = StringAttribute('sym', 'VARCHAR', 'trade')
10
+ __price = FloatAttribute('price', 'DOUBLE', 'trade')
11
+ __account = AccountRelatedFinder(Attribute('account_id', 'INT', 'trade'),Attribute('id', 'INT', 'account'))
12
+
13
+ @staticmethod
14
+ def symbol() -> StringAttribute:
15
+ return TradeFinder.__symbol
16
+
17
+ @staticmethod
18
+ def price() -> FloatAttribute:
19
+ return TradeFinder.__price
20
+
21
+ @staticmethod
22
+ def account() -> AccountRelatedFinder:
23
+ return TradeFinder.__account
24
+
25
+ @staticmethod
26
+ def find_all(date_from: datetime.date, date_to: datetime.date, as_of: str,
27
+ display_columns: list[Attribute],
28
+ filter_op: Operation = NoOperation()) -> DataFrame:
29
+ return QueryRunnerBase.get_runner().select(display_columns, TradeFinder.__table, filter_op)
30
+
31
+
32
+ class TradeRelatedFinder:
33
+ def __init__(self, source: Attribute, target: Attribute):
34
+ join = JoinOperation(source,target)
35
+ self.__symbol = StringAttribute('sym', 'VARCHAR', 'trade', join)
36
+ self.__price = FloatAttribute('price', 'DOUBLE', 'trade', join)
37
+ self.__account = AccountRelatedFinder(Attribute('account_id', 'INT', 'trade'),Attribute('id', 'INT', 'account'))
38
+
39
+ def symbol(self) -> StringAttribute:
40
+ return self.__symbol
41
+
42
+ def price(self) -> FloatAttribute:
43
+ return self.__price
44
+
45
+ def account(self) -> AccountRelatedFinder:
46
+ return self.__account
47
+
File without changes
@@ -0,0 +1,20 @@
1
+ import duckdb
2
+
3
+ from duckdb_trade_finder import *
4
+ from example import queries
5
+
6
+ def duckdb_sample():
7
+ con = duckdb.connect('test.db')
8
+ con.sql("SELECT 42 AS x").show()
9
+ con.execute("DROP TABLE IF EXISTS trade;")
10
+ con.execute(
11
+ "CREATE TABLE trade(id INT, account_id INT, sym VARCHAR, price DOUBLE); COPY trade FROM '../data/trades.csv'")
12
+ con.sql("SELECT * from trade").show()
13
+ con.sql("SELECT * from trade where sym LIKE 'AAPL'").show()
14
+
15
+
16
+
17
+
18
+ if __name__ == '__main__':
19
+ duckdb_sample()
20
+ queries.find_trades(TradeFinder)
@@ -0,0 +1,24 @@
1
+ from datafinder.typed_attributes import *
2
+ from datafinder_duckdb.duckdb_engine import *
3
+
4
+
5
+ class TradeFinder:
6
+ __table = 'trade'
7
+ __symbol = StringAttribute('sym', 'char')
8
+ __price = FloatAttribute('price', 'double precision')
9
+
10
+ @staticmethod
11
+ def symbol() -> StringAttribute:
12
+ return TradeFinder.__symbol
13
+
14
+ @staticmethod
15
+ def price() -> FloatAttribute:
16
+ return TradeFinder.__price
17
+
18
+ @staticmethod
19
+ def find_all(date_from: datetime.date, date_to: datetime.date, as_of: str,
20
+ filter_op: Operation,
21
+ display_columns: list[Attribute]) -> DataFrame:
22
+
23
+ out = DuckDbConnect.select(display_columns, TradeFinder.__table, filter_op)
24
+ return DuckDbOutput(out)
File without changes
@@ -0,0 +1,51 @@
1
+ class Package:
2
+ def __init__(self, name: str):
3
+ self.name = name
4
+
5
+
6
+ class PackagableElement:
7
+ def __init__(self, package: Package):
8
+ self.package = package
9
+
10
+
11
+ class Type:
12
+ def __init(self):
13
+ pass
14
+
15
+
16
+ class PrimitiveType(Type):
17
+ def __init__(self, name: str):
18
+ self.name = name
19
+
20
+
21
+ Integer = PrimitiveType("Integer")
22
+ String = PrimitiveType("String")
23
+ Float = PrimitiveType("Float")
24
+ DateTime = PrimitiveType("DateTime")
25
+ Date = PrimitiveType("Date")
26
+
27
+
28
+ class Property:
29
+ def __init__(self, name: str, type: Type):
30
+ self.name = name
31
+ self.type = type
32
+
33
+
34
+ class Class(PackagableElement, Type):
35
+ def __init__(self, name: str, properties: list[Property], package: Package):
36
+ super().__init__(package)
37
+ self.properties = {}
38
+ self.name = name
39
+ for prop in properties:
40
+ self.properties[prop.name] = prop
41
+
42
+ def property(self, name:str) -> Property:
43
+ return self.properties[name]
44
+
45
+
46
+ class Association(PackagableElement):
47
+ def __init__(self, name: str, source: str, target: str, package: Package):
48
+ super().__init__(package)
49
+ self.name = name
50
+ self.source = source
51
+ self.target = target
@@ -0,0 +1,19 @@
1
+ from typing import Any
2
+
3
+ from model.m3 import Class, Property
4
+
5
+
6
+ class PropertyMapping:
7
+ def __init__(self, property: Property, target: Any):
8
+ self.property = property
9
+ self.target = target
10
+
11
+ class ClassMapping:
12
+ def __init__(self, clazz: Class, property_mappings: list[PropertyMapping]):
13
+ self.clazz = clazz
14
+ self.property_mappings = property_mappings
15
+
16
+ class Mapping:
17
+ def __init__(self, name: str, mappings: list[ClassMapping]):
18
+ self.name = name
19
+ self.mappings = mappings
@@ -0,0 +1,41 @@
1
+ from model.m3 import Property, Class
2
+ from model.mapping import ClassMapping, PropertyMapping
3
+
4
+
5
+ class RelationalElement:
6
+ def __init(self):
7
+ pass
8
+
9
+
10
+ class Column(RelationalElement):
11
+ def __init__(self, name: str, type: str):
12
+ self.name = name
13
+ self.type = type
14
+ self.table = None
15
+
16
+
17
+ class Table:
18
+ def __init__(self, name: str, columns: list[Column]):
19
+ self.name = name
20
+ self.columns = columns
21
+ for col in columns:
22
+ col.table = self
23
+
24
+
25
+ class Join(RelationalElement):
26
+ def __init__(self, lhs: Column, rhs: Column):
27
+ self.lhs = lhs
28
+ self.rhs = rhs
29
+
30
+
31
+ class RelationalPropertyMapping(PropertyMapping):
32
+ def __init__(self, property: Property, target: RelationalElement):
33
+ super().__init__(property, target)
34
+
35
+
36
+ class RelationalClassMapping(ClassMapping):
37
+ def __init__(self, clazz: Class, property_mappings: list[RelationalPropertyMapping]):
38
+ super().__init__(clazz, property_mappings)
39
+
40
+
41
+
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: data-finder
3
+ Version: 0.1.0
4
+ Summary: Model driven data finders
5
+ Requires-Python: >=3.11
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: atpublic==4.1.0
8
+ Requires-Dist: bidict==0.23.1
9
+ Requires-Dist: duckdb==1.0.0
10
+ Requires-Dist: greenlet==3.0.3
11
+ Requires-Dist: ibis-framework==9.0.0
12
+ Requires-Dist: iniconfig==2.1.0
13
+ Requires-Dist: jinja2==3.1.6
14
+ Requires-Dist: markdown-it-py==3.0.0
15
+ Requires-Dist: markupsafe==3.0.2
16
+ Requires-Dist: mdurl==0.1.2
17
+ Requires-Dist: numpy==1.26.4
18
+ Requires-Dist: packaging==25.0
19
+ Requires-Dist: pandas==2.2.2
20
+ Requires-Dist: parsy==2.1
21
+ Requires-Dist: pluggy==1.6.0
22
+ Requires-Dist: pyarrow==16.1.0
23
+ Requires-Dist: pyarrow-hotfix==0.7
24
+ Requires-Dist: pygments==2.19.1
25
+ Requires-Dist: pytest==8.3.4
26
+ Requires-Dist: python-dateutil==2.9.0.post0
27
+ Requires-Dist: pytz==2024.1
28
+ Requires-Dist: rich==13.9.4
29
+ Requires-Dist: six==1.16.0
30
+ Requires-Dist: sqlglot==23.12.2
31
+ Requires-Dist: toml==0.10.2
32
+ Requires-Dist: toolz==0.12.1
33
+ Requires-Dist: typing-extensions==4.12.2
34
+ Requires-Dist: tzdata==2024.1
35
+
36
+ # Experimental typed finders
37
+
38
+ This generates helper classes to query data using a logical model and mapping
@@ -0,0 +1,37 @@
1
+ data-finder/calc/src/calc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ data-finder/calc/src/calc/calc_protocol.py,sha256=fEvRa27Jb2HyjrM9jKugUDhtwCUhxzyUWZ4bR4vhccE,1412
3
+ data-finder/calc/src/calc/simple_price.py,sha256=K0xGKbo3F86xsMbLRXAgsTTrrm3JA8t0sBgDD-EHUwI,658
4
+ data-finder/calc/tests/test_calc.py,sha256=lo_dPD-clIJhjlCrU-Wgm_2U0tPoURDZa4lSpIuYNt4,1866
5
+ data-finder/datafinder/src/datafinder/__init__.py,sha256=z8UksHLDOjGDgPHlpufqpLWEI3-qmvHvhleq_aUTbfY,157
6
+ data-finder/datafinder/src/datafinder/attribute.py,sha256=BCN_A3pOwQefOn1UVI7wUY7LiuJCfuW-LisnA8mMKDA,584
7
+ data-finder/datafinder/src/datafinder/finder.py,sha256=jyo44zDnBX3FM8n-M1gX4NXhyuHxJe88kjeajXCX-7U,186
8
+ data-finder/datafinder/src/datafinder/operation.py,sha256=txGzabAvX8lAG7wZ2WLvXcbQWzqyUO_0wUw-BKvEsoA,4812
9
+ data-finder/datafinder/src/datafinder/output.py,sha256=CYbfNoVF8Hnq3G6rFcKfLHhdULXer-UON4CPcmHpTM4,196
10
+ data-finder/datafinder/src/datafinder/runner.py,sha256=9IDbRZ_xIAlnjB02uqXRQDtF48b8d-fJDrnwj8OnyV0,911
11
+ data-finder/datafinder/src/datafinder/typed_attributes.py,sha256=Bnx0PdQA77u6fmsgex9aNpbj3O4y5h90khZ0aD-JoN0,1381
12
+ data-finder/datafinder/src/datafinder/typed_operations.py,sha256=9plTaVJewfrjnn39OJcrCrhfJh8wiGVCki9B7hmycEQ,2068
13
+ data-finder/datafinder_duckdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ data-finder/datafinder_duckdb/duckdb_engine.py,sha256=OAf1ym3AqY4_p7fohHfY1ENh5sIapFx377oyfiger-U,864
15
+ data-finder/datafinder_generator/src/datafinder_generator/generator.py,sha256=yUyif6M0jYmFsSHrn18Yx-RTLeoiDS3q7LIkJQmbvWU,825
16
+ data-finder/datafinder_ibis/src/datafinder_ibis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ data-finder/datafinder_ibis/src/datafinder_ibis/ibis_engine.py,sha256=Zlh2yUtVNhpcRGb4HVu9x01oC-1_xFhwJknPDzsA5Fc,815
18
+ data-finder/datafinder_ibis/tests/test_ibis_engine.py,sha256=Y0zAtk0wTpikzUR8FobBhEaVNFl2lD5kGEiVDGmeNAs,235
19
+ data-finder/datafinder_ibis_duckdb/tests/test_datafinder_ibis_duckdb.py,sha256=eXPR_MZWfwhScsAUKRljaqFQbGZDOfUi3RUQkUi4xZQ,2172
20
+ data-finder/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ data-finder/example/account_finder.py,sha256=bYw3itpmjT5PhqM252pR7Kqze4hdcUxOTOcRXXbIx4Y,1157
22
+ data-finder/example/contractualposition_finder.py,sha256=DzIoDNiJ5K7UBNzM6ajNvJ0GXR5lazcBYuAwVz07ti8,2235
23
+ data-finder/example/instrument_finder.py,sha256=DDPXeOTmXRyW7M6eZIjF-VUDrRhfGs28jNIv1eJkEtk,1194
24
+ data-finder/example/mappings.py,sha256=GtEcVwffcbxUXamKqasRVkEd4ZVtbovS49dfhUMUPkQ,3617
25
+ data-finder/example/queries.py,sha256=THu-RLldl6tEaowWt3pZKCxrhGTf564eQ0W1sZWEhN4,1134
26
+ data-finder/example/trade_finder.py,sha256=WLW62qbRAfxeJK_2OTvOAURz7BvCuadf9CvEQN8iaGk,1627
27
+ data-finder/example_duckdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ data-finder/example_duckdb/duckdb_example.py,sha256=tSV3uwH15XrGhefJldiIdKKX7Y84Bwu5Ny9auof5X-Y,548
29
+ data-finder/example_duckdb/duckdb_trade_finder.py,sha256=on6ug-tkUdIZ8W1NgIo7-RvmCVwDAjm8BN702kTCOfI,736
30
+ data-finder/model/src/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ data-finder/model/src/model/m3.py,sha256=h1dyACsgcXDSc8bKXWXBDkZ8BvvPtLMdCVQ6CdnAWfg,1202
32
+ data-finder/model/src/model/mapping.py,sha256=UPE8ZPZBFPQ_v79WCWsJ6fluHWxCZztAZKzxZCZZYaM,522
33
+ data-finder/model/src/model/relational.py,sha256=mniAfpmVXojWtNjxDj6jGjUU2UIetl1Hc_R-qCxnKCo,983
34
+ data_finder-0.1.0.dist-info/METADATA,sha256=REbMiO6hykkP48tzsZ3_D1I1B6K2qPCI90EcSxPIDcw,1138
35
+ data_finder-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ data_finder-0.1.0.dist-info/top_level.txt,sha256=TkqwsWs5BMfXjQjd97MD6YQY8D5nmv5wZ7OlXupzHFQ,12
37
+ data_finder-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ data-finder