tea-bond 0.2.8__cp310-abi3-win_amd64.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.

Potentially problematic release.


This version of tea-bond might be problematic. Click here for more details.

pybond/nb/nb_time.py ADDED
@@ -0,0 +1,279 @@
1
+ import datetime
2
+ import operator
3
+
4
+ from llvmlite import ir
5
+ from numba import types
6
+ from numba.core import cgutils
7
+ from numba.extending import (
8
+ NativeValue,
9
+ as_numba_type,
10
+ box,
11
+ lower_builtin,
12
+ make_attribute_wrapper,
13
+ models,
14
+ overload,
15
+ overload_attribute,
16
+ # overload_method,
17
+ register_model,
18
+ type_callable,
19
+ typeof_impl,
20
+ unbox,
21
+ )
22
+
23
+ from .ir_utils import ir_isinstance, long_as_ulong
24
+
25
+
26
+ class Time:
27
+ def __init__(self, hour, minute, second, nanosecond):
28
+ # secs = hour * 3600 + minute * 60 + sec
29
+ # self.val = int(secs * 1e9 + nsecs)
30
+ self.hour = hour
31
+ self.minute = minute
32
+ self.second = second
33
+ self.nanosecond = nanosecond
34
+
35
+
36
+ class TimeType(types.Type):
37
+ def __init__(self):
38
+ super().__init__(name="Time")
39
+
40
+
41
+ time_type = TimeType()
42
+ as_numba_type.register(datetime.time, time_type)
43
+
44
+
45
+ @typeof_impl.register(datetime.time)
46
+ @typeof_impl.register(Time)
47
+ def typeof_datetime_time(val, c):
48
+ return time_type
49
+
50
+
51
+ @type_callable(datetime.time)
52
+ @type_callable(Time)
53
+ def type_time(context):
54
+ def typer(hour, minute, second, nanosecond=0):
55
+ return time_type
56
+
57
+ return typer
58
+
59
+
60
+ @register_model(TimeType)
61
+ class TimeModel(models.StructModel):
62
+ def __init__(self, dmm, fe_type):
63
+ members = [
64
+ ("hour", types.uint32),
65
+ ("minute", types.uint32),
66
+ ("second", types.uint32),
67
+ ("nanosecond", types.uint32),
68
+ ]
69
+ models.StructModel.__init__(self, dmm, fe_type, members)
70
+
71
+
72
+ make_attribute_wrapper(TimeType, "hour", "hour")
73
+ make_attribute_wrapper(TimeType, "minute", "minute")
74
+ make_attribute_wrapper(TimeType, "second", "second")
75
+ make_attribute_wrapper(TimeType, "nanosecond", "nanosecond")
76
+
77
+
78
+ @lower_builtin(Time, types.uint32, types.uint32, types.uint32, types.uint32)
79
+ @lower_builtin(Time, types.int32, types.int32, types.int32, types.int32)
80
+ def time_constructor_u32(context, builder, sig, args):
81
+ time_type = sig.return_type
82
+ time = cgutils.create_struct_proxy(time_type)(context, builder)
83
+ time.hour = args[0]
84
+ time.minute = args[1]
85
+ time.second = args[2]
86
+ time.nanosecond = args[3]
87
+ return time._getvalue()
88
+
89
+
90
+ @lower_builtin(datetime.time, types.uint32, types.uint32, types.uint32, types.uint32)
91
+ @lower_builtin(datetime.time, types.int32, types.int32, types.int32, types.int32)
92
+ def datetime_time_constructor_u32(context, builder, sig, args):
93
+ time_type = sig.return_type
94
+ time = cgutils.create_struct_proxy(time_type)(context, builder)
95
+ time.hour = args[0]
96
+ time.minute = args[1]
97
+ time.second = args[2]
98
+ # the last param of datetime time is microsecond, but we store nanosecond
99
+ time.nanosecond = builder.mul(args[3], ir.Constant(ir.IntType(32), 1000))
100
+ return time._getvalue()
101
+
102
+
103
+ @lower_builtin(Time, types.int64, types.int64, types.int64, types.int64)
104
+ def time_constructor_i64(context, builder, sig, args):
105
+ time_type = sig.return_type
106
+ time = cgutils.create_struct_proxy(time_type)(context, builder)
107
+ time.hour = builder.trunc(args[0], ir.IntType(32))
108
+ time.minute = builder.trunc(args[1], ir.IntType(32))
109
+ time.second = builder.trunc(args[2], ir.IntType(32))
110
+ time.nanosecond = builder.trunc(args[3], ir.IntType(32))
111
+ return time._getvalue()
112
+
113
+
114
+ @lower_builtin(datetime.time, types.int64, types.int64, types.int64, types.int64)
115
+ def datetime_time_constructor_i64(context, builder, sig, args):
116
+ time_type = sig.return_type
117
+ time = cgutils.create_struct_proxy(time_type)(context, builder)
118
+ time.hour = builder.trunc(args[0], ir.IntType(32))
119
+ time.minute = builder.trunc(args[1], ir.IntType(32))
120
+ time.second = builder.trunc(args[2], ir.IntType(32))
121
+ nanosecond = builder.mul(args[3], ir.Constant(ir.IntType(64), 1000))
122
+ time.nanosecond = builder.trunc(nanosecond, ir.IntType(32))
123
+ return time._getvalue()
124
+
125
+
126
+ @lower_builtin(datetime.time, types.int64, types.int64, types.int64)
127
+ @lower_builtin(Time, types.int64, types.int64, types.int64)
128
+ def time_constructor_i64_hms(context, builder, sig, args):
129
+ time_type = sig.return_type
130
+ time = cgutils.create_struct_proxy(time_type)(context, builder)
131
+ time.hour = builder.trunc(args[0], ir.IntType(32))
132
+ time.minute = builder.trunc(args[1], ir.IntType(32))
133
+ time.second = builder.trunc(args[2], ir.IntType(32))
134
+ time.nanosecond = ir.Constant(ir.IntType(32), 0)
135
+ return time._getvalue()
136
+
137
+
138
+ @unbox(TimeType)
139
+ def unbox_time(typ, obj, c):
140
+ time_type_c = c.pyapi.unserialize(c.pyapi.serialize_object(datetime.time))
141
+ is_time = ir_isinstance(c.pyapi, obj, time_type_c)
142
+ c.pyapi.decref(time_type_c)
143
+
144
+ hour_obj = c.pyapi.object_getattr_string(obj, "hour")
145
+ hour = c.builder.trunc(long_as_ulong(c.pyapi, hour_obj), ir.IntType(32))
146
+ minute_obj = c.pyapi.object_getattr_string(obj, "minute")
147
+ minute = c.builder.trunc(long_as_ulong(c.pyapi, minute_obj), ir.IntType(32))
148
+ second_obj = c.pyapi.object_getattr_string(obj, "second")
149
+ second = c.builder.trunc(long_as_ulong(c.pyapi, second_obj), ir.IntType(32))
150
+ c.pyapi.decref(hour_obj)
151
+ c.pyapi.decref(minute_obj)
152
+ c.pyapi.decref(second_obj)
153
+
154
+ nsecs_ptr = cgutils.alloca_once(c.builder, ir.IntType(32))
155
+ with c.builder.if_else(is_time) as (then, otherwise):
156
+ with then:
157
+ msecs_obj = c.pyapi.object_getattr_string(obj, "microsecond")
158
+ msecs = long_as_ulong(c.pyapi, msecs_obj)
159
+ nsecs = c.builder.mul(msecs, ir.Constant(c.pyapi.ulong, 1000))
160
+ nsecs = c.builder.trunc(nsecs, ir.IntType(32))
161
+ c.pyapi.decref(msecs_obj)
162
+ c.builder.store(nsecs, nsecs_ptr)
163
+ with otherwise:
164
+ nsecs_obj = c.pyapi.object_getattr_string(obj, "nanosecond")
165
+ nsecs = long_as_ulong(c.pyapi, nsecs_obj)
166
+ nsecs = c.builder.trunc(nsecs, ir.IntType(32))
167
+ c.pyapi.decref(nsecs_obj)
168
+ c.builder.store(nsecs, nsecs_ptr)
169
+ nanosecond = c.builder.load(nsecs_ptr)
170
+ time = cgutils.create_struct_proxy(typ)(c.context, c.builder)
171
+ time.hour = hour
172
+ time.minute = minute
173
+ time.second = second
174
+ time.nanosecond = nanosecond
175
+ # Check for errors
176
+ is_error = cgutils.is_not_null(c.builder, c.pyapi.err_occurred())
177
+ return NativeValue(time._getvalue(), is_error=is_error)
178
+
179
+
180
+ @box(TimeType)
181
+ def box_time(typ, val, c):
182
+ """
183
+ Box a native time object into a Python datetime.time object.
184
+ """
185
+ time = cgutils.create_struct_proxy(typ)(c.context, c.builder, value=val)
186
+ hour_obj = c.pyapi.long_from_unsigned_int(time.hour)
187
+ minute_obj = c.pyapi.long_from_unsigned_int(time.minute)
188
+ second_obj = c.pyapi.long_from_unsigned_int(time.second)
189
+ microsecond = c.builder.udiv(
190
+ time.nanosecond, ir.Constant(time.nanosecond.type, 1000)
191
+ )
192
+ microsecond_obj = c.pyapi.long_from_unsigned_int(microsecond)
193
+
194
+ class_obj = c.pyapi.unserialize(c.pyapi.serialize_object(datetime.time))
195
+ time_obj = c.pyapi.call_function_objargs(
196
+ class_obj, (hour_obj, minute_obj, second_obj, microsecond_obj)
197
+ )
198
+ c.pyapi.decref(hour_obj)
199
+ c.pyapi.decref(minute_obj)
200
+ c.pyapi.decref(second_obj)
201
+ c.pyapi.decref(microsecond_obj)
202
+ return time_obj
203
+
204
+
205
+ @overload_attribute(TimeType, "nsecs")
206
+ def get_time_impl(dt):
207
+ def getter(dt):
208
+ return int(
209
+ (dt.hour * 3600 + dt.minute * 60 + dt.second) * 1_000_000_000
210
+ + dt.nanosecond
211
+ )
212
+
213
+ return getter
214
+
215
+
216
+ @overload(operator.ge)
217
+ def impl_ge(dt, dt2):
218
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
219
+ return
220
+
221
+ def impl(dt, dt2):
222
+ return dt.nsecs >= dt2.nsecs
223
+
224
+ return impl
225
+
226
+
227
+ @overload(operator.gt)
228
+ def impl_gt(dt, dt2):
229
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
230
+ return
231
+
232
+ def impl(dt, dt2):
233
+ return dt.nsecs > dt2.nsecs
234
+
235
+ return impl
236
+
237
+
238
+ @overload(operator.le)
239
+ def impl_le(dt, dt2):
240
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
241
+ return
242
+
243
+ def impl(dt, dt2):
244
+ return dt.nsecs <= dt2.nsecs
245
+
246
+ return impl
247
+
248
+
249
+ @overload(operator.lt)
250
+ def impl_lt(dt, dt2):
251
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
252
+ return
253
+
254
+ def impl(dt, dt2):
255
+ return dt.nsecs < dt2.nsecs
256
+
257
+ return impl
258
+
259
+
260
+ @overload(operator.eq)
261
+ def impl_eq(dt, dt2):
262
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
263
+ return
264
+
265
+ def impl(dt, dt2):
266
+ return dt.nsecs == dt2.nsecs
267
+
268
+ return impl
269
+
270
+
271
+ @overload(operator.ne)
272
+ def impl_ne(dt, dt2):
273
+ if not isinstance(dt, TimeType) or not isinstance(dt, TimeType):
274
+ return
275
+
276
+ def impl(dt, dt2):
277
+ return dt.nsecs != dt2.nsecs
278
+
279
+ return impl
pybond/pnl.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ import polars as pl
7
+ from polars.type_aliases import IntoExpr
8
+
9
+ from .polars_utils import parse_into_expr, register_plugin
10
+
11
+
12
+ def calc_bond_trade_pnl(
13
+ settle_time: IntoExpr,
14
+ qty: IntoExpr,
15
+ clean_price: IntoExpr,
16
+ clean_close: IntoExpr,
17
+ symbol: str = "",
18
+ bond_info_path: str | None = None,
19
+ multiplier: float = 1,
20
+ c_rate: float = 0,
21
+ borrowing_cost: float = 0,
22
+ capital_rate: float = 0,
23
+ begin_state=None,
24
+ ) -> pl.Expr:
25
+ settle_time = parse_into_expr(settle_time)
26
+ qty = parse_into_expr(qty)
27
+ clean_price = parse_into_expr(clean_price)
28
+ clean_close = parse_into_expr(clean_close)
29
+ if bond_info_path is None:
30
+ from .bond import bonds_info_path as path
31
+
32
+ bond_info_path = str(path)
33
+
34
+ if begin_state is None:
35
+ begin_state = {
36
+ "pos": 0,
37
+ "avg_price": 0,
38
+ "pnl": 0,
39
+ "realized_pnl": 0,
40
+ "pos_price": 0,
41
+ "unrealized_pnl": 0,
42
+ "coupon_paid": 0,
43
+ "amt": 0,
44
+ }
45
+ kwargs = {
46
+ "symbol": symbol,
47
+ "multiplier": multiplier,
48
+ "c_rate": c_rate,
49
+ "borrowing_cost": borrowing_cost,
50
+ "capital_rate": capital_rate,
51
+ "begin_state": begin_state,
52
+ "bond_info_path": bond_info_path,
53
+ }
54
+ return register_plugin(
55
+ args=[settle_time, qty, clean_price, clean_close],
56
+ kwargs=kwargs,
57
+ symbol="calc_bond_trade_pnl",
58
+ is_elementwise=False,
59
+ )
pybond/polars_utils.py ADDED
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING, Any, Sequence
6
+
7
+ import polars as pl
8
+
9
+ if TYPE_CHECKING:
10
+ from polars.type_aliases import IntoExpr, PolarsDataType
11
+
12
+
13
+ def parse_into_expr(
14
+ expr: IntoExpr,
15
+ *,
16
+ str_as_lit: bool = False,
17
+ list_as_lit: bool = True,
18
+ dtype: PolarsDataType | None = None,
19
+ ) -> pl.Expr:
20
+ """
21
+ Parse a single input into an expression.
22
+
23
+ Parameters
24
+ ----------
25
+ expr
26
+ The input to be parsed as an expression.
27
+ str_as_lit
28
+ Interpret string input as a string literal. If set to `False` (default),
29
+ strings are parsed as column names.
30
+ list_as_lit
31
+ Interpret list input as a lit literal, If set to `False`,
32
+ lists are parsed as `Series` literals.
33
+ dtype
34
+ If the input is expected to resolve to a literal with a known dtype, pass
35
+ this to the `lit` constructor.
36
+
37
+ Returns
38
+ -------
39
+ polars.Expr
40
+ """
41
+ if isinstance(expr, pl.Expr):
42
+ pass
43
+ elif isinstance(expr, str) and not str_as_lit:
44
+ expr = pl.col(expr)
45
+ elif isinstance(expr, list) and not list_as_lit:
46
+ expr = pl.lit(pl.Series(expr), dtype=dtype)
47
+ else:
48
+ expr = pl.lit(expr, dtype=dtype)
49
+
50
+ return expr
51
+
52
+
53
+ def register_plugin(
54
+ *,
55
+ symbol: str,
56
+ is_elementwise: bool,
57
+ kwargs: dict[str, Any] | None = None,
58
+ args: list[IntoExpr],
59
+ # lib: str | Path,
60
+ ) -> pl.Expr:
61
+ global lib
62
+ if parse_version(pl.__version__) < parse_version("0.20.16"):
63
+ assert isinstance(args[0], pl.Expr)
64
+ assert isinstance(lib, str)
65
+ return args[0].register_plugin(
66
+ lib=lib,
67
+ symbol=symbol,
68
+ args=args[1:],
69
+ kwargs=kwargs,
70
+ is_elementwise=is_elementwise,
71
+ )
72
+ from polars.plugins import register_plugin_function
73
+
74
+ return register_plugin_function(
75
+ args=args,
76
+ plugin_path=lib,
77
+ function_name=symbol,
78
+ kwargs=kwargs,
79
+ is_elementwise=is_elementwise,
80
+ )
81
+
82
+
83
+ def parse_version(version: Sequence[str | int]) -> tuple[int, ...]:
84
+ # Simple version parser; split into a tuple of ints for comparison.
85
+ # vendored from Polars
86
+ if isinstance(version, str):
87
+ version = version.split(".")
88
+ return tuple(int(re.sub(r"\D", "", str(v))) for v in version)
89
+
90
+
91
+ if parse_version(pl.__version__) < parse_version("0.20.16"):
92
+ from polars.utils.udfs import _get_shared_lib_location
93
+
94
+ lib: str | Path = _get_shared_lib_location(__file__)
95
+ else:
96
+ lib = Path(__file__).parent
pybond/pybond.pyd ADDED
Binary file