onetick-py 1.172.0__py3-none-any.whl → 1.174.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.
- onetick/py/_version.py +1 -1
- onetick/py/aggregations/_base.py +1 -1
- onetick/py/aggregations/order_book.py +13 -7
- onetick/py/aggregations/other.py +2 -2
- onetick/py/compatibility.py +15 -10
- onetick/py/configuration.py +3 -2
- onetick/py/core/_source/source_methods/misc.py +59 -13
- onetick/py/core/_source/symbol.py +5 -2
- onetick/py/core/column_operations/_methods/conversions.py +3 -2
- onetick/py/core/column_operations/_methods/op_types.py +12 -3
- onetick/py/core/column_operations/base.py +2 -2
- onetick/py/core/source.py +9 -1
- onetick/py/functions.py +3 -2
- onetick/py/run.py +3 -1
- onetick/py/types.py +127 -59
- onetick/py/utils/render.py +271 -58
- onetick/py/utils/types.py +2 -0
- onetick/py/utils/tz.py +8 -6
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/METADATA +12 -26
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/RECORD +24 -24
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/WHEEL +0 -0
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/entry_points.txt +0 -0
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/licenses/LICENSE +0 -0
- {onetick_py-1.172.0.dist-info → onetick_py-1.174.0.dist-info}/top_level.txt +0 -0
onetick/py/types.py
CHANGED
|
@@ -2,15 +2,16 @@ import ctypes
|
|
|
2
2
|
import functools
|
|
3
3
|
import inspect
|
|
4
4
|
import warnings
|
|
5
|
+
import decimal as _decimal
|
|
5
6
|
from typing import Optional, Type, Union
|
|
6
|
-
from packaging.version import parse as parse_version
|
|
7
|
-
|
|
8
|
-
import pandas as pd
|
|
9
|
-
import numpy as np
|
|
10
7
|
from datetime import date as _date
|
|
11
8
|
from datetime import datetime as _datetime
|
|
9
|
+
from datetime import timedelta as _timedelta
|
|
12
10
|
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
13
|
from pandas.tseries import offsets
|
|
14
|
+
from packaging.version import parse as parse_version
|
|
14
15
|
|
|
15
16
|
import onetick.py as otp
|
|
16
17
|
from onetick.py.otq import otq, pyomd
|
|
@@ -707,18 +708,23 @@ class _inf(float, metaclass=_nan_base):
|
|
|
707
708
|
inf = _inf()
|
|
708
709
|
|
|
709
710
|
|
|
710
|
-
class
|
|
711
|
-
def __str__(cls):
|
|
712
|
-
return 'decimal'
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
class decimal(float, metaclass=_decimal_str):
|
|
711
|
+
class decimal:
|
|
716
712
|
"""
|
|
717
713
|
Object that represents decimal OneTick value.
|
|
718
714
|
Decimal is 128 bit base 10 floating point number.
|
|
719
715
|
|
|
716
|
+
Parameters
|
|
717
|
+
----------
|
|
718
|
+
value: int, float, str
|
|
719
|
+
The value to initialize decimal from.
|
|
720
|
+
Note that float values may be converted with precision lost.
|
|
721
|
+
|
|
720
722
|
Examples
|
|
721
723
|
--------
|
|
724
|
+
|
|
725
|
+
:py:class:`~onetick.py.types.decimal` objects can be used in tick generators
|
|
726
|
+
and column operations as any other onetick-py type:
|
|
727
|
+
|
|
722
728
|
>>> t = otp.Ticks({'A': [otp.decimal(1), otp.decimal(2)]})
|
|
723
729
|
>>> t['B'] = otp.decimal(1.23456789)
|
|
724
730
|
>>> t['C'] = t['A'] / 0
|
|
@@ -727,43 +733,107 @@ class decimal(float, metaclass=_decimal_str):
|
|
|
727
733
|
Time A B C D
|
|
728
734
|
0 2003-12-01 00:00:00.000 1.0 1.234568 inf NaN
|
|
729
735
|
1 2003-12-01 00:00:00.001 2.0 1.234568 inf NaN
|
|
730
|
-
"""
|
|
731
|
-
def __wrap(self, res):
|
|
732
|
-
# if parent class doesn't support some operation, it returns NotImplemented and so do we
|
|
733
|
-
# In other case we wrap float result with our decimal class
|
|
734
|
-
if isinstance(res, type(NotImplemented)):
|
|
735
|
-
return NotImplemented
|
|
736
|
-
return self.__class__(res)
|
|
737
736
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
def __radd__(self, other):
|
|
742
|
-
return self.__wrap(super().__radd__(other))
|
|
743
|
-
|
|
744
|
-
def __sub__(self, other):
|
|
745
|
-
return self.__wrap(super().__sub__(other))
|
|
737
|
+
Additionally, any arithmetic operation with :py:class:`~onetick.py.types.decimal` object will return
|
|
738
|
+
an :py:class:`~onetick.py.Operation` object:
|
|
746
739
|
|
|
747
|
-
|
|
748
|
-
|
|
740
|
+
>>> t = otp.Tick(A=1)
|
|
741
|
+
>>> t['X'] = otp.decimal(1) / 0
|
|
742
|
+
>>> otp.run(t)
|
|
743
|
+
Time A X
|
|
744
|
+
0 2003-12-01 1 inf
|
|
745
|
+
|
|
746
|
+
Note that converting from float (first row) may result in losing precision.
|
|
747
|
+
:py:class:`~onetick.py.types.decimal` objects are created from strings or integers, so they don't lose precision:
|
|
748
|
+
|
|
749
|
+
>>> t0 = otp.Tick(A=0.1)
|
|
750
|
+
>>> t1 = otp.Tick(A=otp.decimal(0.01))
|
|
751
|
+
>>> t2 = otp.Tick(A=otp.decimal('0.001'))
|
|
752
|
+
>>> t3 = otp.Tick(A=otp.decimal(1) / otp.decimal(10_000))
|
|
753
|
+
>>> t = otp.merge([t0, t1, t2, t3], enforce_order=True)
|
|
754
|
+
>>> t['STR_A'] = t['A'].decimal.str(34)
|
|
755
|
+
>>> otp.run(t)
|
|
756
|
+
Time A STR_A
|
|
757
|
+
0 2003-12-01 0.1000 0.1000000000000000055511151231257827
|
|
758
|
+
1 2003-12-01 0.0100 0.0100000000000000000000000000000000
|
|
759
|
+
2 2003-12-01 0.0010 0.0010000000000000000000000000000000
|
|
760
|
+
3 2003-12-01 0.0001 0.0001000000000000000000000000000000
|
|
749
761
|
|
|
750
|
-
|
|
751
|
-
|
|
762
|
+
Note that :py:class:`otp.Ticks <onetick.py.Ticks>` will convert everything from string under the hood,
|
|
763
|
+
so even the float values will not lose precision:
|
|
752
764
|
|
|
753
|
-
|
|
754
|
-
|
|
765
|
+
>>> t = otp.Ticks({'A': [0.1, otp.decimal(0.01), otp.decimal('0.001'), otp.decimal(1e-4)]})
|
|
766
|
+
>>> t['STR_A'] = t['A'].decimal.str(34)
|
|
767
|
+
>>> otp.run(t)
|
|
768
|
+
Time A STR_A
|
|
769
|
+
0 2003-12-01 00:00:00.000 0.1000 0.1000000000000000000000000000000000
|
|
770
|
+
1 2003-12-01 00:00:00.001 0.0100 0.0100000000000000000000000000000000
|
|
771
|
+
2 2003-12-01 00:00:00.002 0.0010 0.0010000000000000000000000000000000
|
|
772
|
+
3 2003-12-01 00:00:00.003 0.0001 0.0001000000000000000000000000000000
|
|
773
|
+
"""
|
|
774
|
+
def __new__(cls, *args, **kwargs):
|
|
775
|
+
# this method dynamically adds properties and methods
|
|
776
|
+
# from otp.Operation class to this one
|
|
777
|
+
|
|
778
|
+
# otp.decimal class doesn't fit well in onetick-py type system,
|
|
779
|
+
# so this class is a mix of both type and Operation logic
|
|
780
|
+
|
|
781
|
+
# Basically it works like this:
|
|
782
|
+
# otp.decimal is a OneTick type
|
|
783
|
+
# otp.decimal(1) is a decimal type object
|
|
784
|
+
# Doing anything with this object returns an otp.Operation:
|
|
785
|
+
# otp.decimal(1) / 2
|
|
786
|
+
|
|
787
|
+
def proxy_wrap(attr, value):
|
|
788
|
+
if callable(value):
|
|
789
|
+
@functools.wraps(value)
|
|
790
|
+
def f(self, *args, **kwargs):
|
|
791
|
+
op = self.to_operation()
|
|
792
|
+
return getattr(op, attr)(*args, **kwargs)
|
|
793
|
+
return f
|
|
794
|
+
else:
|
|
795
|
+
@functools.wraps(value)
|
|
796
|
+
def f(self):
|
|
797
|
+
op = self.to_operation()
|
|
798
|
+
return getattr(op, attr)
|
|
799
|
+
return property(f)
|
|
800
|
+
|
|
801
|
+
for attr, value in inspect.getmembers(otp.Operation):
|
|
802
|
+
# comparison methods are defined by default for some reason,
|
|
803
|
+
# but we want to get them from otp.Operation
|
|
804
|
+
if not hasattr(cls, attr) or attr in ('__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__'):
|
|
805
|
+
setattr(cls, attr, proxy_wrap(attr, value))
|
|
806
|
+
|
|
807
|
+
return super().__new__(cls)
|
|
808
|
+
|
|
809
|
+
def __init__(self, value):
|
|
810
|
+
supported_types = (str, int, float)
|
|
811
|
+
if not isinstance(value, supported_types):
|
|
812
|
+
raise TypeError("Parameter 'value' must be one of these types: {supported_types}")
|
|
813
|
+
self.__value = value
|
|
814
|
+
|
|
815
|
+
@classmethod
|
|
816
|
+
def _to_onetick_type_string(cls):
|
|
817
|
+
# called by ott.type2str
|
|
818
|
+
return 'decimal'
|
|
755
819
|
|
|
756
|
-
def
|
|
757
|
-
|
|
820
|
+
def _to_onetick_string(self):
|
|
821
|
+
# called by ott.value2str
|
|
822
|
+
value = str(self.__value)
|
|
823
|
+
return f'STRING_TO_DECIMAL({value2str(value)})'
|
|
758
824
|
|
|
759
|
-
def
|
|
760
|
-
return
|
|
825
|
+
def to_operation(self):
|
|
826
|
+
return otp.Operation(op_str=self._to_onetick_string(), dtype=decimal)
|
|
761
827
|
|
|
762
828
|
def __str__(self):
|
|
763
|
-
|
|
829
|
+
# called by otp.CSV, we don't need to convert the value with OneTick functions in this case
|
|
830
|
+
return str(self.__value)
|
|
764
831
|
|
|
765
832
|
def __repr__(self):
|
|
766
|
-
return f"{self.__class__.__name__}({self})"
|
|
833
|
+
return f"{self.__class__.__name__}({value2str(self.__value)})"
|
|
834
|
+
|
|
835
|
+
def __format__(self, __format_spec: str) -> str:
|
|
836
|
+
return _decimal.Decimal(self.__value).__format__(__format_spec)
|
|
767
837
|
|
|
768
838
|
# --------------------------------------------------------------- #
|
|
769
839
|
# AUXILIARY FUNCTIONS
|
|
@@ -809,7 +879,7 @@ def get_source_base_type(value):
|
|
|
809
879
|
value_type = nsectime
|
|
810
880
|
|
|
811
881
|
# check valid value type
|
|
812
|
-
if get_base_type(value_type) not in [int, float, str, bool]:
|
|
882
|
+
if get_base_type(value_type) not in [int, float, str, bool, decimal]:
|
|
813
883
|
raise TypeError(f'Type "{repr(value_type)}" is not supported.')
|
|
814
884
|
|
|
815
885
|
if not is_type_basic(value_type):
|
|
@@ -818,7 +888,7 @@ def get_source_base_type(value):
|
|
|
818
888
|
|
|
819
889
|
|
|
820
890
|
def is_type_supported(dtype):
|
|
821
|
-
return get_base_type(dtype) in [int, float, str, bool] or issubclass(dtype, (datetime, date))
|
|
891
|
+
return get_base_type(dtype) in [int, float, str, bool, decimal] or issubclass(dtype, (datetime, date))
|
|
822
892
|
|
|
823
893
|
|
|
824
894
|
def get_base_type(obj):
|
|
@@ -830,6 +900,8 @@ def get_base_type(obj):
|
|
|
830
900
|
return int
|
|
831
901
|
elif issubclass(obj, float):
|
|
832
902
|
return float
|
|
903
|
+
elif issubclass(obj, decimal):
|
|
904
|
+
return decimal
|
|
833
905
|
|
|
834
906
|
return type(None)
|
|
835
907
|
|
|
@@ -1095,7 +1167,9 @@ class datetime(AbstractTime):
|
|
|
1095
1167
|
def _process_timezones_args(self, tz, tzinfo):
|
|
1096
1168
|
if tz is not None:
|
|
1097
1169
|
if tzinfo is None:
|
|
1098
|
-
|
|
1170
|
+
# parameter tz in pandas.Timestamp is broken https://github.com/pandas-dev/pandas/issues/31929
|
|
1171
|
+
# it is fixed in pandas>=2.0.0, but we need to support older versions
|
|
1172
|
+
tzinfo = get_tzfile_by_name(tz)
|
|
1099
1173
|
tz = None
|
|
1100
1174
|
else:
|
|
1101
1175
|
raise ValueError(
|
|
@@ -1684,6 +1758,8 @@ def type2str(t):
|
|
|
1684
1758
|
return "double"
|
|
1685
1759
|
if t is None:
|
|
1686
1760
|
return ''
|
|
1761
|
+
if t is decimal:
|
|
1762
|
+
return t._to_onetick_type_string()
|
|
1687
1763
|
return str(t)
|
|
1688
1764
|
|
|
1689
1765
|
|
|
@@ -1839,15 +1915,16 @@ def value2str(v):
|
|
|
1839
1915
|
# there is no escape, so replacing double quotes with concatenation with it
|
|
1840
1916
|
return '"' + str(v).replace('"', '''"+'"'+"''') + '"'
|
|
1841
1917
|
|
|
1842
|
-
if isinstance(v,
|
|
1918
|
+
if isinstance(v, decimal):
|
|
1919
|
+
return v._to_onetick_string()
|
|
1920
|
+
|
|
1921
|
+
if isinstance(v, float) and not (isinstance(v, (_inf, _nan))):
|
|
1843
1922
|
# PY-286: support science notation
|
|
1844
1923
|
s = str(v)
|
|
1845
1924
|
if "e" in s:
|
|
1846
|
-
|
|
1925
|
+
return f'atof({value2str(s)})'
|
|
1847
1926
|
if s == "nan":
|
|
1848
1927
|
return str(nan)
|
|
1849
|
-
if isinstance(v, decimal):
|
|
1850
|
-
return f'DECIMAL({s})'
|
|
1851
1928
|
return s
|
|
1852
1929
|
|
|
1853
1930
|
if is_time_type(v):
|
|
@@ -1936,21 +2013,12 @@ def is_time_type(time):
|
|
|
1936
2013
|
|
|
1937
2014
|
def next_day(dt_obj: Union[_date, _datetime, date, datetime, pd.Timestamp]) -> _datetime:
|
|
1938
2015
|
"""
|
|
1939
|
-
Return next day of ``dt_obj`` as datetime.datetime
|
|
1940
|
-
"""
|
|
1941
|
-
updated_dt = dt_obj + Day(1)
|
|
1942
|
-
|
|
1943
|
-
# If we switch ts on timezone transition time, ts changes its timezone offset
|
|
1944
|
-
if hasattr(dt_obj, "tzinfo") and updated_dt.tzinfo != dt_obj.tzinfo:
|
|
1945
|
-
dt_offset = dt_obj.tzinfo.utcoffset(dt_obj)
|
|
1946
|
-
updated_dt_offset = updated_dt.tzinfo.utcoffset(updated_dt)
|
|
1947
|
-
|
|
1948
|
-
tz_diff = dt_offset - updated_dt_offset
|
|
1949
|
-
updated_dt += tz_diff
|
|
2016
|
+
Return the start of the next day of ``dt_obj`` as timezone-naive :py:class:`datetime.datetime`.
|
|
1950
2017
|
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2018
|
+
Next day in this case means simply incrementing day/month/year number,
|
|
2019
|
+
not adding 24 hours (which may return the same date on DST days).
|
|
2020
|
+
"""
|
|
2021
|
+
return _datetime(dt_obj.year, dt_obj.month, dt_obj.day) + _timedelta(days=1)
|
|
1954
2022
|
|
|
1955
2023
|
|
|
1956
2024
|
def default_by_type(dtype):
|
|
@@ -1967,7 +2035,7 @@ def default_by_type(dtype):
|
|
|
1967
2035
|
>>> otp.default_by_type(float)
|
|
1968
2036
|
nan
|
|
1969
2037
|
>>> otp.default_by_type(otp.decimal)
|
|
1970
|
-
decimal(0
|
|
2038
|
+
decimal(0)
|
|
1971
2039
|
>>> otp.default_by_type(int)
|
|
1972
2040
|
0
|
|
1973
2041
|
>>> otp.default_by_type(otp.ulong)
|