tea-bond 0.3.1__cp310-abi3-macosx_11_0_arm64.whl → 0.3.2__cp310-abi3-macosx_11_0_arm64.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/__init__.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from .bond import Bond
4
-
5
4
  from .pybond import Future, Ib, Sse
6
5
  from .pybond import TfEvaluator as _TfEvaluatorRS
7
6
 
@@ -9,7 +8,9 @@ from .pybond import TfEvaluator as _TfEvaluatorRS
9
8
  class TfEvaluator(_TfEvaluatorRS):
10
9
  def __new__(cls, future, bond, *args, **kwargs):
11
10
  if not isinstance(bond, Bond):
11
+ # 便于直接从Wind下载债券基础数据
12
12
  bond = Bond(bond)
13
13
  return super().__new__(cls, future, bond, *args, **kwargs)
14
14
 
15
+
15
16
  __all__ = ["Bond", "Future", "Ib", "Sse", "TfEvaluator"]
pybond/pd.py ADDED
@@ -0,0 +1,394 @@
1
+ from __future__ import annotations
2
+
3
+ import pandas as pd
4
+ import polars as pl
5
+
6
+ from .pl import Bonds as PlBonds
7
+ from .pl import TfEvaluators as PlTfEvaluators
8
+
9
+
10
+ class TfEvaluators:
11
+ """
12
+ Treasury Futures Evaluators for pandas DataFrames.
13
+
14
+ A pandas-compatible wrapper around the Polars-based TfEvaluators that provides
15
+ various financial calculations for treasury futures and bonds analysis.
16
+
17
+ This class converts pandas inputs to Polars for computation and returns
18
+ pandas Series results for seamless integration with pandas workflows.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ future: str | pd.Series,
24
+ bond: str | pd.Series,
25
+ date: str | pd.Series,
26
+ future_price: pd.Series,
27
+ bond_ytm: pd.Series,
28
+ capital_rate: float | pd.Series,
29
+ reinvest_rate: float | None = None,
30
+ ):
31
+ """
32
+ Initialize TfEvaluators with market data.
33
+
34
+ Args:
35
+ future: Future contract code(s)
36
+ bond: Bond code(s)
37
+ date: Evaluation date(s)
38
+ future_price: Future price(s)
39
+ bond_ytm: Bond yield to maturity
40
+ capital_rate: Capital cost rate for carry calculations
41
+ reinvest_rate: Reinvestment rate for coupon payments (optional)
42
+ """
43
+ self.pl_df = pl.DataFrame(
44
+ {
45
+ "future": future,
46
+ "bond": bond,
47
+ "date": date,
48
+ "future_price": future_price,
49
+ "bond_ytm": bond_ytm,
50
+ "capital_rate": capital_rate,
51
+ }
52
+ )
53
+ self._evaluators = PlTfEvaluators(reinvest_rate=reinvest_rate)
54
+ # self.reinvest_rate = reinvest_rate
55
+
56
+ @property
57
+ def net_basis_spread(self):
58
+ """
59
+ Calculate net basis spread (净基差).
60
+
61
+ Net basis spread = basis spread - carry return
62
+
63
+ Returns:
64
+ pd.Series: Net basis spread values
65
+ """
66
+ return self.pl_df.select(net_basis_spread=self._evaluators.net_basis_spread)[
67
+ "net_basis_spread"
68
+ ].to_pandas()
69
+
70
+ @property
71
+ def accrued_interest(self):
72
+ """
73
+ Calculate accrued interest (应计利息).
74
+
75
+ Returns:
76
+ pd.Series: Accrued interest values
77
+ """
78
+ return self.pl_df.select(accrued_interest=self._evaluators.accrued_interest)[
79
+ "accrued_interest"
80
+ ].to_pandas()
81
+
82
+ @property
83
+ def deliver_accrued_interest(self):
84
+ """
85
+ Calculate delivery accrued interest (国债期货交割应计利息).
86
+
87
+ Returns:
88
+ pd.Series: Delivery accrued interest values
89
+ """
90
+ return self.pl_df.select(
91
+ deliver_accrued_interest=self._evaluators.deliver_accrued_interest
92
+ )["deliver_accrued_interest"].to_pandas()
93
+
94
+ @property
95
+ def cf(self):
96
+ """
97
+ Calculate conversion factor (转换因子).
98
+
99
+ Returns:
100
+ pd.Series: Conversion factor values
101
+ """
102
+ return self.pl_df.select(cf=self._evaluators.cf)["cf"].to_pandas()
103
+
104
+ @property
105
+ def dirty_price(self):
106
+ """
107
+ Calculate bond dirty price (债券全价).
108
+
109
+ Returns:
110
+ pd.Series: Bond dirty price values
111
+ """
112
+ return self.pl_df.select(dirty_price=self._evaluators.dirty_price)[
113
+ "dirty_price"
114
+ ].to_pandas()
115
+
116
+ @property
117
+ def clean_price(self):
118
+ """
119
+ Calculate bond clean price (债券净价).
120
+
121
+ Returns:
122
+ pd.Series: Bond clean price values
123
+ """
124
+ return self.pl_df.select(clean_price=self._evaluators.clean_price)[
125
+ "clean_price"
126
+ ].to_pandas()
127
+
128
+ @property
129
+ def future_dirty_price(self):
130
+ """
131
+ Calculate future dirty price (期货全价/发票价格).
132
+
133
+ Returns:
134
+ pd.Series: Future dirty price values
135
+ """
136
+ return self.pl_df.select(
137
+ future_dirty_price=self._evaluators.future_dirty_price
138
+ )["future_dirty_price"].to_pandas()
139
+
140
+ @property
141
+ def deliver_cost(self):
142
+ """
143
+ Calculate delivery cost (交割成本).
144
+
145
+ Delivery cost = bond dirty price - interim coupon payments
146
+
147
+ Returns:
148
+ pd.Series: Delivery cost values
149
+ """
150
+ return self.pl_df.select(deliver_cost=self._evaluators.deliver_cost)[
151
+ "deliver_cost"
152
+ ].to_pandas()
153
+
154
+ @property
155
+ def basis_spread(self):
156
+ """
157
+ Calculate basis spread (基差).
158
+
159
+ Returns:
160
+ pd.Series: Basis spread values
161
+ """
162
+ return self.pl_df.select(basis_spread=self._evaluators.basis_spread)[
163
+ "basis_spread"
164
+ ].to_pandas()
165
+
166
+ @property
167
+ def f_b_spread(self):
168
+ """
169
+ Calculate futures-bond spread (期现价差).
170
+
171
+ Returns:
172
+ pd.Series: Futures-bond spread values
173
+ """
174
+ return self.pl_df.select(f_b_spread=self._evaluators.f_b_spread)[
175
+ "f_b_spread"
176
+ ].to_pandas()
177
+
178
+ @property
179
+ def carry(self):
180
+ """
181
+ Calculate carry return (持有收益).
182
+
183
+ Carry return = (delivery accrued - trading accrued + interim coupons) +
184
+ capital cost rate * (weighted average interim coupons - bond dirty price * remaining days / 365)
185
+
186
+ Returns:
187
+ pd.Series: Carry return values
188
+ """
189
+ return self.pl_df.select(carry=self._evaluators.carry)["carry"].to_pandas()
190
+
191
+ @property
192
+ def duration(self):
193
+ """
194
+ Calculate modified duration (修正久期).
195
+
196
+ Returns:
197
+ pd.Series: Modified duration values
198
+ """
199
+ return self.pl_df.select(duration=self._evaluators.duration)[
200
+ "duration"
201
+ ].to_pandas()
202
+
203
+ @property
204
+ def irr(self):
205
+ """
206
+ Calculate internal rate of return (内部收益率).
207
+
208
+ Returns:
209
+ pd.Series: Internal rate of return values
210
+ """
211
+ return self.pl_df.select(irr=self._evaluators.irr)["irr"].to_pandas()
212
+
213
+ @property
214
+ def future_ytm(self):
215
+ """
216
+ Calculate futures implied yield to maturity (期货隐含收益率).
217
+
218
+ Returns:
219
+ pd.Series: Futures implied yield to maturity values
220
+ """
221
+ return self.pl_df.select(future_ytm=self._evaluators.future_ytm)[
222
+ "future_ytm"
223
+ ].to_pandas()
224
+
225
+ @property
226
+ def remain_cp_to_deliver(self):
227
+ """
228
+ Calculate remaining coupon payments to delivery (到交割的期间付息).
229
+
230
+ Returns:
231
+ pd.Series: Remaining coupon payments to delivery values
232
+ """
233
+ return self.pl_df.select(
234
+ remain_cp_to_deliver=self._evaluators.remain_cp_to_deliver
235
+ )["remain_cp_to_deliver"].to_pandas()
236
+
237
+ @property
238
+ def remain_cp_to_deliver_wm(self):
239
+ """
240
+ Calculate weighted average remaining coupon payments to delivery (加权平均到交割的期间付息).
241
+
242
+ Returns:
243
+ pd.Series: Weighted average remaining coupon payments to delivery values
244
+ """
245
+ return self.pl_df.select(
246
+ remain_cp_to_deliver_wm=self._evaluators.remain_cp_to_deliver_wm
247
+ )["remain_cp_to_deliver_wm"].to_pandas()
248
+
249
+ @property
250
+ def remain_cp_num(self):
251
+ """
252
+ Calculate remaining number of coupon payments (债券剩余付息次数).
253
+
254
+ Returns:
255
+ pd.Series: Remaining number of coupon payments values
256
+ """
257
+ return self.pl_df.select(remain_cp_num=self._evaluators.remain_cp_num)[
258
+ "remain_cp_num"
259
+ ].to_pandas()
260
+
261
+
262
+ class Bonds:
263
+ """
264
+ Bond calculations for pandas DataFrames.
265
+
266
+ A pandas-compatible wrapper around the Polars-based Bonds class that provides
267
+ bond-specific financial calculations without requiring futures contract information.
268
+ """
269
+
270
+ def __init__(self, bond: str | pd.Series):
271
+ """
272
+ Initialize Bonds with bond identifier.
273
+
274
+ Args:
275
+ bond: Bond code(s)
276
+ """
277
+ self.bond = bond
278
+
279
+ def accrued_interest(self, date: str | pd.Series):
280
+ """
281
+ Calculate accrued interest for the bond (应计利息).
282
+
283
+ Args:
284
+ date: Evaluation date(s)
285
+
286
+ Returns:
287
+ pd.Series: Accrued interest values
288
+ """
289
+ df = pl.DataFrame({"bond": self.bond, "date": date})
290
+ return df.select(accrued_interest=PlBonds("bond").accrued_interest("date"))[
291
+ "accrued_interest"
292
+ ].to_pandas()
293
+
294
+ def clean_price(self, ytm: float | pd.Series, date: str | pd.Series):
295
+ """
296
+ Calculate bond clean price (债券净价).
297
+
298
+ Args:
299
+ ytm: Yield to maturity
300
+ date: Evaluation date(s)
301
+
302
+ Returns:
303
+ pd.Series: Bond clean price values
304
+ """
305
+ df = pl.DataFrame({"bond": self.bond, "ytm": ytm, "date": date})
306
+ return df.select(clean_price=PlBonds("bond").clean_price("ytm", "date"))[
307
+ "clean_price"
308
+ ].to_pandas()
309
+
310
+ def dirty_price(self, ytm: float | pd.Series, date: str | pd.Series):
311
+ """
312
+ Calculate bond dirty price (债券全价).
313
+
314
+ Args:
315
+ ytm: Yield to maturity
316
+ date: Evaluation date(s)
317
+
318
+ Returns:
319
+ pd.Series: Bond dirty price values
320
+ """
321
+ df = pl.DataFrame({"bond": self.bond, "ytm": ytm, "date": date})
322
+ return df.select(dirty_price=PlBonds("bond").dirty_price("ytm", "date"))[
323
+ "dirty_price"
324
+ ].to_pandas()
325
+
326
+ def duration(self, ytm: float | pd.Series, date: str | pd.Series):
327
+ """
328
+ Calculate modified duration (修正久期).
329
+
330
+ Args:
331
+ ytm: Yield to maturity
332
+ date: Evaluation date(s)
333
+
334
+ Returns:
335
+ pd.Series: Modified duration values
336
+ """
337
+ df = pl.DataFrame({"bond": self.bond, "ytm": ytm, "date": date})
338
+ return df.select(duration=PlBonds("bond").duration("ytm", "date"))[
339
+ "duration"
340
+ ].to_pandas()
341
+
342
+ def remain_cp_num(self, date: str | pd.Series):
343
+ """
344
+ Calculate remaining number of coupon payments (债券剩余付息次数).
345
+
346
+ Args:
347
+ date: Evaluation date(s)
348
+
349
+ Returns:
350
+ pd.Series: Remaining number of coupon payments values
351
+ """
352
+ df = pl.DataFrame({"bond": self.bond, "date": date})
353
+ return df.select(remain_cp_num=PlBonds("bond").remain_cp_num("date"))[
354
+ "remain_cp_num"
355
+ ].to_pandas()
356
+
357
+
358
+ def find_workday(date: str | pd.Series, market: str, offset: int = 0):
359
+ """
360
+ Find the workday based on the given date and market calendar.
361
+
362
+ Args:
363
+ date: Input date(s)
364
+ market: Market identifier ("IB" or "SSE")
365
+ offset: Number of workdays to offset (default: 0)
366
+
367
+ Returns:
368
+ pd.Series: Adjusted workday values
369
+ """
370
+ from .pl import find_workday as pl_find_workday
371
+
372
+ df = pl.DataFrame({"date": date}).select(pl.col("date").dt.date())
373
+ return df.select(workday=pl_find_workday("date", market, offset))[
374
+ "workday"
375
+ ].to_pandas()
376
+
377
+
378
+ def is_business_day(date: str | pd.Series, market: str):
379
+ """
380
+ Check if the given date is a business day for the specified market.
381
+
382
+ Args:
383
+ date: Input date(s)
384
+ market: Market identifier ("IB" or "SSE")
385
+
386
+ Returns:
387
+ pd.Series: Boolean values indicating if dates are business days
388
+ """
389
+ from .pl import is_business_day as pl_is_business_day
390
+
391
+ df = pl.DataFrame({"date": date}).select(pl.col("date").dt.date())
392
+ return df.select(is_business=pl_is_business_day("date", market))[
393
+ "is_business"
394
+ ].to_pandas()
pybond/pl.py CHANGED
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from .pybond import Ib, Sse
6
+
5
7
  if TYPE_CHECKING:
6
8
  from polars.type_aliases import IntoExpr
7
9
  import polars as pl
@@ -239,10 +241,35 @@ class TfEvaluators:
239
241
 
240
242
 
241
243
  class Bonds:
244
+ """
245
+ A class for bond-specific calculations using Polars expressions.
246
+
247
+ This class provides methods for calculating various bond metrics
248
+ without requiring futures contract information.
249
+ """
250
+
242
251
  def __init__(self, bond: IntoExpr = "symbol"):
252
+ """
253
+ Initialize Bonds with bond identifier.
254
+
255
+ Args:
256
+ bond: Bond code column expression (default: "symbol")
257
+ """
243
258
  self.bond = bond
244
259
 
245
- def _evaluator(self, date=None, ytm=None) -> TfEvaluators:
260
+ def _evaluator(
261
+ self, date: IntoExpr | None = None, ytm: IntoExpr | None = None
262
+ ) -> TfEvaluators:
263
+ """
264
+ Create a TfEvaluators instance for bond-only calculations.
265
+
266
+ Args:
267
+ date: Evaluation date column expression
268
+ ytm: Yield to maturity column expression
269
+
270
+ Returns:
271
+ TfEvaluators: Configured evaluator instance
272
+ """
246
273
  return TfEvaluators(
247
274
  future=None,
248
275
  bond=self.bond,
@@ -253,19 +280,116 @@ class Bonds:
253
280
  reinvest_rate=None,
254
281
  )
255
282
 
256
- def accrued_interest(self, date="date"):
283
+ def accrued_interest(self, date: IntoExpr = "date"):
284
+ """
285
+ Calculate accrued interest for the bond (应计利息).
286
+
287
+ Args:
288
+ date: Evaluation date column expression
289
+
290
+ Returns:
291
+ Polars expression for accrued interest
292
+ """
257
293
  return self._evaluator(date=date).accrued_interest
258
294
 
259
- def clean_price(self, ytm="ytm", date="date"):
295
+ def clean_price(self, ytm: IntoExpr = "ytm", date: IntoExpr = "date"):
296
+ """
297
+ Calculate bond clean price (债券净价).
298
+
299
+ Args:
300
+ ytm: Yield to maturity column expression
301
+ date: Evaluation date column expression
302
+
303
+ Returns:
304
+ Polars expression for bond clean price
305
+ """
260
306
  return self._evaluator(date=date, ytm=ytm).clean_price
261
307
 
262
- def dirty_price(self, ytm="ytm", date="date"):
308
+ def dirty_price(self, ytm: IntoExpr = "ytm", date: IntoExpr = "date"):
309
+ """
310
+ Calculate bond dirty price (债券全价).
311
+
312
+ Args:
313
+ ytm: Yield to maturity column expression
314
+ date: Evaluation date column expression
315
+
316
+ Returns:
317
+ Polars expression for bond dirty price
318
+ """
263
319
  return self._evaluator(date=date, ytm=ytm).dirty_price
264
320
 
265
- def duration(self, ytm="ytm", date="date"):
321
+ def duration(self, ytm: IntoExpr = "ytm", date: IntoExpr = "date"):
322
+ """
323
+ Calculate modified duration (修正久期).
324
+
325
+ Args:
326
+ ytm: Yield to maturity column expression
327
+ date: Evaluation date column expression
328
+
329
+ Returns:
330
+ Polars expression for modified duration
331
+ """
266
332
  return self._evaluator(date=date, ytm=ytm).duration
267
333
 
268
- def remain_cp_num(self, date="date"):
334
+ def remain_cp_num(self, date: IntoExpr = "date"):
335
+ """
336
+ Calculate remaining number of coupon payments (债券剩余付息次数).
337
+
338
+ Args:
339
+ date: Evaluation date column expression
340
+
341
+ Returns:
342
+ Polars expression for remaining number of coupon payments
343
+ """
269
344
  return self._evaluator(date=date).remain_cp_num
270
345
 
271
346
  # TODO(Teamon): 实现向量化根据净价反推ytm的函数
347
+
348
+
349
+ def find_workday(date: IntoExpr, market: str | Ib | Sse, offset: int = 0):
350
+ """
351
+ Find the workday based on the given date and market calendar.
352
+
353
+ Args:
354
+ date: Input date column expression
355
+ market: Market identifier (IB, SSE, or string)
356
+ offset: Number of workdays to offset (default: 0)
357
+
358
+ Returns:
359
+ Polars expression for the adjusted workday
360
+ """
361
+ if market == Ib:
362
+ market = "IB"
363
+ elif market == Sse:
364
+ market = "SSE"
365
+ date = parse_into_expr(date)
366
+ return register_plugin(
367
+ args=[date],
368
+ kwargs={"market": market, "offset": offset},
369
+ symbol="calendar_find_workday",
370
+ is_elementwise=True,
371
+ )
372
+
373
+
374
+ def is_business_day(date: IntoExpr, market: str | Ib | Sse):
375
+ """
376
+ Check if the given date is a business day for the specified market.
377
+
378
+ Args:
379
+ date: Input date column expression
380
+ market: Market identifier (IB, SSE, or string)
381
+
382
+ Returns:
383
+ Polars expression returning boolean values for business day check
384
+ """
385
+ if market == Ib:
386
+ market = "IB"
387
+ elif market == Sse:
388
+ market = "SSE"
389
+ date = parse_into_expr(date)
390
+ return register_plugin(
391
+ args=[date],
392
+ kwargs={"market": market},
393
+ symbol="calendar_is_business_day",
394
+ is_elementwise=True,
395
+ )
pybond/pybond.abi3.so CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tea-bond
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,4 +1,4 @@
1
- pybond/__init__.py,sha256=7x1hBQtlQRhvgxfe6Z0wtQ5tWpTfUPMnaKyjddRyk9Y,430
1
+ pybond/__init__.py,sha256=k0LZ2gzo0mln4AFUnFMClgDorRWfUGUlt7T_UESYS7A,488
2
2
  pybond/bond.py,sha256=_bpG2MZkFobGS8kkem3z7eNvCR7GqWJHMH4WJCfB5EQ,6568
3
3
  pybond/download.py,sha256=ehl_LfcfjCB5dtVvqD0P9PJzK1UYlqhzpDcBq5BycDI,4222
4
4
  pybond/ffi/__init__.py,sha256=x4FSqdM-SZPir9DL_XSMjoO9c4CWsyE5XfQr4GraGsg,94
@@ -15,11 +15,12 @@ pybond/nb/nb_datetime.py,sha256=9fLD9rnfpHQ-cvX4H6PoFh_O3jgk475ZKrG8q_UxT34,1026
15
15
  pybond/nb/nb_duration.py,sha256=FSX1y2JbHB_SJn-hcVe-7Zf2v0xpQKKZ1MfWBY7d7IQ,1639
16
16
  pybond/nb/nb_evaluators.py,sha256=fCYxcvW717_1E4qY9AV8R5Pv8eTvWqKk3Zj-zMU1kiw,14987
17
17
  pybond/nb/nb_time.py,sha256=LaqXfcNvmYD_lZ2u108MwrxCXoVCBX3cr3oOnPp3c1g,8649
18
- pybond/pl.py,sha256=CakQvQPjvWu8XykDfkBznFmFkIB8m-lVllF-8_BrUsw,7860
18
+ pybond/pd.py,sha256=QmkADhIu0T7w1pbhYZB9dFDaPSO9aZ3kdFxktZf5Kro,11591
19
+ pybond/pl.py,sha256=fk5AGXym02gCkJIZmOpQRPf1qnqYUa1V_PawbeYm7WE,11285
19
20
  pybond/pnl.py,sha256=P3KXc-gz6_TA2QxDQR9NHJIJPCvLOGT_v4qN4gzdcRY,1568
20
21
  pybond/polars_utils.py,sha256=A8D5T0x08oMCndWiQ5DPhLsuWp8s4OPgqvAnK36d8yY,2567
21
- pybond/pybond.abi3.so,sha256=hWQdqKMPT9KZU6fPD8z7dLy7b0q5J6HBJi2O0hrikNU,23186096
22
+ pybond/pybond.abi3.so,sha256=xgxcMNOinn1BMAsOhzwxWI_PfZBSmNFE1lPyfKeIll4,23265984
22
23
  pybond/pybond.pyi,sha256=xME119HJzVNqNCJ9FapVaQg1amxbNHlbd-qfFyiuhL4,11230
23
- tea_bond-0.3.1.dist-info/METADATA,sha256=9zA8uD2lRgPAeN66HKlZYrvs1CHs2ZyM6IeJRhWveC4,258
24
- tea_bond-0.3.1.dist-info/WHEEL,sha256=Mdosfxua6Dx1zYgObRH97e3wyiELqBbLtoRJj4RUSQE,103
25
- tea_bond-0.3.1.dist-info/RECORD,,
24
+ tea_bond-0.3.2.dist-info/METADATA,sha256=gZqfIfy4fjk1QBHtuRogb71fM-6cyltEf1lw-rsNo44,258
25
+ tea_bond-0.3.2.dist-info/WHEEL,sha256=Mdosfxua6Dx1zYgObRH97e3wyiELqBbLtoRJj4RUSQE,103
26
+ tea_bond-0.3.2.dist-info/RECORD,,