tea-bond 0.3.14__tar.gz → 0.3.15__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.

Potentially problematic release.


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

Files changed (101) hide show
  1. {tea_bond-0.3.14 → tea_bond-0.3.15}/Cargo.lock +3 -3
  2. {tea_bond-0.3.14 → tea_bond-0.3.15}/Cargo.toml +1 -1
  3. {tea_bond-0.3.14 → tea_bond-0.3.15}/PKG-INFO +1 -1
  4. {tea_bond-0.3.14/pybond → tea_bond-0.3.15}/pybond/pl.py +21 -1
  5. {tea_bond-0.3.14 → tea_bond-0.3.15/pybond}/pybond/pl.py +21 -1
  6. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/batch_eval.rs +59 -1
  7. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/mod.rs +9 -0
  8. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/tf_evaluator/evaluator.rs +7 -2
  9. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/.gitignore +0 -0
  10. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/Cargo.toml +0 -0
  11. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/__init__.py +0 -0
  12. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/bond.py +0 -0
  13. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/download.py +0 -0
  14. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/__init__.py +0 -0
  15. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/bond.py +0 -0
  16. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/datetime.py +0 -0
  17. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/duration.py +0 -0
  18. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/evaluators.py +0 -0
  19. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/ffi/lib.py +0 -0
  20. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/__init__.py +0 -0
  21. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/ir_utils.py +0 -0
  22. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_bond.py +0 -0
  23. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_date.py +0 -0
  24. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_datetime.py +0 -0
  25. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_duration.py +0 -0
  26. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_evaluators.py +0 -0
  27. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb/nb_time.py +0 -0
  28. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/nb_test.py +0 -0
  29. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pd.py +0 -0
  30. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pnl.py +0 -0
  31. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/polars_utils.py +0 -0
  32. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/__init__.py +0 -0
  33. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/bond.py +0 -0
  34. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/download.py +0 -0
  35. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/__init__.py +0 -0
  36. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/bond.py +0 -0
  37. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/datetime.py +0 -0
  38. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/duration.py +0 -0
  39. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/evaluators.py +0 -0
  40. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/ffi/lib.py +0 -0
  41. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/__init__.py +0 -0
  42. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/ir_utils.py +0 -0
  43. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_bond.py +0 -0
  44. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_date.py +0 -0
  45. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_datetime.py +0 -0
  46. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_duration.py +0 -0
  47. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_evaluators.py +0 -0
  48. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/nb/nb_time.py +0 -0
  49. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/pd.py +0 -0
  50. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/pnl.py +0 -0
  51. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/polars_utils.py +0 -0
  52. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond/pybond.pyi +0 -0
  53. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/pybond.pyi +0 -0
  54. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/bond.rs +0 -0
  55. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/calendar.rs +0 -0
  56. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/bond.rs +0 -0
  57. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/datetime.rs +0 -0
  58. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/duration.rs +0 -0
  59. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/evaluators.rs +0 -0
  60. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/mod.rs +0 -0
  61. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/ffi/utils.rs +0 -0
  62. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/future.rs +0 -0
  63. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/lib.rs +0 -0
  64. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/pnl.rs +0 -0
  65. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/tf_evaluator.rs +0 -0
  66. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/src/utils.rs +0 -0
  67. {tea_bond-0.3.14 → tea_bond-0.3.15}/pybond/test.py +0 -0
  68. {tea_bond-0.3.14 → tea_bond-0.3.15}/pyproject.toml +0 -0
  69. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/Cargo.toml +0 -0
  70. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/batch/evaluator.rs +0 -0
  71. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/batch/mod.rs +0 -0
  72. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/bond_ytm.rs +0 -0
  73. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/cached_bond.rs +0 -0
  74. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/download/china_money.rs +0 -0
  75. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/download/mod.rs +0 -0
  76. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/download/sse.rs +0 -0
  77. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/enums.rs +0 -0
  78. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/impl_convert.rs +0 -0
  79. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/impl_traits.rs +0 -0
  80. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/bond/io.rs +0 -0
  81. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/day_counter.rs +0 -0
  82. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/export.rs +0 -0
  83. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/future/future_price.rs +0 -0
  84. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/future/future_type.rs +0 -0
  85. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/future/impls.rs +0 -0
  86. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/future/mod.rs +0 -0
  87. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/lib.rs +0 -0
  88. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/pnl/fee.rs +0 -0
  89. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/pnl/mod.rs +0 -0
  90. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/pnl/trade_from_signal.rs +0 -0
  91. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/tf_evaluator/impl_traits.rs +0 -0
  92. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/tf_evaluator/mod.rs +0 -0
  93. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/tf_evaluator/update_with_new_info.rs +0 -0
  94. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-bond/src/utils.rs +0 -0
  95. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/Cargo.toml +0 -0
  96. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/calendars/china/ib.rs +0 -0
  97. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/calendars/china/mod.rs +0 -0
  98. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/calendars/china/others.rs +0 -0
  99. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/calendars/china/sse.rs +0 -0
  100. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/calendars/mod.rs +0 -0
  101. {tea_bond-0.3.14 → tea_bond-0.3.15}/tea-calendar/src/lib.rs +0 -0
@@ -2195,7 +2195,7 @@ dependencies = [
2195
2195
 
2196
2196
  [[package]]
2197
2197
  name = "pybond"
2198
- version = "0.3.14"
2198
+ version = "0.3.15"
2199
2199
  dependencies = [
2200
2200
  "chrono",
2201
2201
  "itertools 0.14.0",
@@ -3117,7 +3117,7 @@ dependencies = [
3117
3117
 
3118
3118
  [[package]]
3119
3119
  name = "tea-bond"
3120
- version = "0.3.14"
3120
+ version = "0.3.15"
3121
3121
  dependencies = [
3122
3122
  "anyhow",
3123
3123
  "chrono",
@@ -3136,7 +3136,7 @@ dependencies = [
3136
3136
 
3137
3137
  [[package]]
3138
3138
  name = "tea-calendar"
3139
- version = "0.3.14"
3139
+ version = "0.3.15"
3140
3140
  dependencies = [
3141
3141
  "chrono",
3142
3142
  ]
@@ -4,7 +4,7 @@ default-members = ["tea-bond", "tea-calendar"]
4
4
  resolver = "2"
5
5
 
6
6
  [workspace.package]
7
- version = "0.3.14"
7
+ version = "0.3.15"
8
8
  edition = "2024"
9
9
 
10
10
  [workspace.dependencies]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tea-bond
3
- Version: 0.3.14
3
+ Version: 0.3.15
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -406,7 +406,27 @@ class Bonds:
406
406
  """
407
407
  return self._evaluator(date=date).remain_cp_num
408
408
 
409
- # TODO(Teamon): 实现向量化根据净价反推ytm的函数
409
+ def calc_ytm_with_price(
410
+ self,
411
+ date: IntoExpr = "date",
412
+ dirty_price: IntoExpr = "dirty_price",
413
+ clean_price: IntoExpr | None = None,
414
+ ):
415
+ bond = parse_into_expr(self.bond)
416
+ date = parse_into_expr(date)
417
+ if clean_price is None:
418
+ dirty_price = parse_into_expr(dirty_price)
419
+ else:
420
+ assert dirty_price == "dirty_price", (
421
+ "should not set dirty_price when clean_price is set"
422
+ )
423
+ clean_price = parse_into_expr(clean_price)
424
+ dirty_price = clean_price + self.accrued_interest(date)
425
+ return register_plugin(
426
+ args=[bond, date, dirty_price],
427
+ symbol="bonds_calc_ytm_with_price",
428
+ is_elementwise=False,
429
+ )
410
430
 
411
431
 
412
432
  class Futures:
@@ -406,7 +406,27 @@ class Bonds:
406
406
  """
407
407
  return self._evaluator(date=date).remain_cp_num
408
408
 
409
- # TODO(Teamon): 实现向量化根据净价反推ytm的函数
409
+ def calc_ytm_with_price(
410
+ self,
411
+ date: IntoExpr = "date",
412
+ dirty_price: IntoExpr = "dirty_price",
413
+ clean_price: IntoExpr | None = None,
414
+ ):
415
+ bond = parse_into_expr(self.bond)
416
+ date = parse_into_expr(date)
417
+ if clean_price is None:
418
+ dirty_price = parse_into_expr(dirty_price)
419
+ else:
420
+ assert dirty_price == "dirty_price", (
421
+ "should not set dirty_price when clean_price is set"
422
+ )
423
+ clean_price = parse_into_expr(clean_price)
424
+ dirty_price = clean_price + self.accrued_interest(date)
425
+ return register_plugin(
426
+ args=[bond, date, dirty_price],
427
+ symbol="bonds_calc_ytm_with_price",
428
+ is_elementwise=False,
429
+ )
410
430
 
411
431
 
412
432
  class Futures:
@@ -183,6 +183,7 @@ where
183
183
  let (future_price, bond_ytm, capital_rate) =
184
184
  auto_cast!(Float64(future_price, bond_ytm, capital_rate));
185
185
  let date = auto_cast!(Date(date));
186
+ let bond = auto_cast!(String(bond));
186
187
  Ok(batch_eval_impl(
187
188
  future.str()?,
188
189
  bond.str()?,
@@ -524,7 +525,7 @@ fn evaluators_last_trading_date(
524
525
  Ok(result.into_date().into_series())
525
526
  }
526
527
 
527
- #[polars_expr(output_type=Date)]
528
+ #[polars_expr(output_type=Float64)]
528
529
  fn bonds_remain_year(
529
530
  inputs: &[Series],
530
531
  kwargs: EvaluatorBatchParams,
@@ -584,6 +585,63 @@ fn bonds_maturity_date(
584
585
  Ok(result.into_date().into_series())
585
586
  }
586
587
 
588
+ #[polars_expr(output_type=Float64)]
589
+ fn bonds_calc_ytm_with_price(
590
+ inputs: &[Series]
591
+ ) -> PolarsResult<Series> {
592
+ let dirty_price_se = auto_cast!(Float64(&inputs[2]));
593
+ let bond_se = auto_cast!(String(&inputs[0]));
594
+ let date_se = auto_cast!(Date(&inputs[1]));
595
+ let len = dirty_price_se.len();
596
+ let bond = bond_se.str()?;
597
+ let date = date_se.date()?;
598
+ let dirty_price = dirty_price_se.f64()?;
599
+ let mut bond_iter = bond.iter();
600
+ let mut date_iter = date.physical().iter();
601
+ let mut bond = CachedBond::new(bond_iter.next().unwrap().unwrap_or(""), None).unwrap();
602
+ let mut dirty_price_iter = dirty_price.iter();
603
+ let mut date_physical = date_iter.next().unwrap().unwrap_or(0);
604
+ let mut date = EPOCH
605
+ .checked_add_days(Days::new(date_physical as u64))
606
+ .unwrap();
607
+ let mut dirty_price = dirty_price_iter.next().unwrap().unwrap_or(f64::NAN);
608
+ let mut result = Vec::with_capacity(len);
609
+ if bond.bond_code().is_empty() {
610
+ result.push(None)
611
+ } else {
612
+ result.push(bond.calc_ytm_with_price(dirty_price, date, None, None).ok().filter(|v| !v.is_nan()))
613
+ }
614
+ for _ in 1..len {
615
+ if let Some(dp) = dirty_price_iter.next() {
616
+ dirty_price = dp.unwrap_or(f64::NAN);
617
+ };
618
+ if let Some(dt) = date_iter.next() {
619
+ let dt = dt.unwrap_or(0);
620
+ if dt != date_physical {
621
+ date_physical = dt;
622
+ date = EPOCH.checked_add_days(Days::new(dt as u64)).unwrap()
623
+ }
624
+ };
625
+ if let Some(b) = bond_iter.next() {
626
+ if let Some(b) = b {
627
+ if b != bond.code() && bond.bond_code != b {
628
+ bond = CachedBond::new(b, None).unwrap();
629
+ }
630
+ } else {
631
+ result.push(None);
632
+ continue;
633
+ }
634
+ };
635
+ if bond.bond_code().is_empty() {
636
+ result.push(None);
637
+ } else {
638
+ result.push(bond.calc_ytm_with_price(dirty_price, date, None, None).ok().filter(|v| !v.is_nan()))
639
+ }
640
+ }
641
+ let result: Float64Chunked = result.into_iter().collect_trusted();
642
+ Ok(result.into_series())
643
+ }
644
+
587
645
  #[derive(Deserialize)]
588
646
  struct FindWorkdayKwargs {
589
647
  market: Market,
@@ -345,6 +345,15 @@ impl Bond {
345
345
  ) -> Result<f64> {
346
346
  match self.interest_type {
347
347
  InterestType::Fixed => {
348
+ if self.is_zero_coupon() {
349
+ let remain_year = self.remain_year(date);
350
+ if remain_year >= 1. {
351
+ return Ok((self.par_value / dirty_price).powf(1.0 / remain_year) - 1.)
352
+ } else {
353
+ return Ok((self.par_value / dirty_price - 1.) * remain_year)
354
+ }
355
+
356
+ }
348
357
  let inst_freq = self.inst_freq as f64;
349
358
  let coupon = self.get_coupon();
350
359
  let (pre_cp_date, next_cp_date) =
@@ -161,7 +161,8 @@ impl TfEvaluator {
161
161
  pub fn with_accrued_interest(mut self) -> Result<Self> {
162
162
  if self.accrued_interest.is_none() {
163
163
  if self.bond.is_zero_coupon() {
164
- self.accrued_interest = Some(0.);
164
+ let days = ACTUAL.count_days(self.bond.carry_date, self.date);
165
+ self.accrued_interest = Some(self.bond.cp_rate * self.bond.par_value * days as f64 / 365.);
165
166
  return Ok(self);
166
167
  }
167
168
  let mut out = self.with_nearest_cp_dates()?;
@@ -181,7 +182,11 @@ impl TfEvaluator {
181
182
  if self.dirty_price.is_none() {
182
183
  if self.bond.is_zero_coupon() {
183
184
  let remain_year = self.bond.remain_year(self.date);
184
- self.dirty_price = Some(100.0 / (1.0+self.bond.ytm()).powf(remain_year));
185
+ if remain_year > 1. {
186
+ self.dirty_price = Some(self.bond.par_value / (1.0+self.bond.ytm()).powf(remain_year));
187
+ } else {
188
+ self.dirty_price = Some(self.bond.par_value / (1.0+self.bond.ytm() * remain_year));
189
+ }
185
190
  return Ok(self);
186
191
  }
187
192
  let mut out = self.with_remain_cp_num()?;
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes