tea-bond 0.3.11__cp38-abi3-macosx_10_13_x86_64.macosx_11_0_arm64.macosx_10_13_universal2.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 +18 -0
- pybond/bond.py +190 -0
- pybond/download.py +147 -0
- pybond/ffi/__init__.py +4 -0
- pybond/ffi/bond.py +68 -0
- pybond/ffi/datetime.py +58 -0
- pybond/ffi/duration.py +19 -0
- pybond/ffi/evaluators.py +186 -0
- pybond/ffi/lib.py +8 -0
- pybond/nb/__init__.py +31 -0
- pybond/nb/ir_utils.py +47 -0
- pybond/nb/nb_bond.py +213 -0
- pybond/nb/nb_date.py +209 -0
- pybond/nb/nb_datetime.py +415 -0
- pybond/nb/nb_duration.py +70 -0
- pybond/nb/nb_evaluators.py +557 -0
- pybond/nb/nb_time.py +279 -0
- pybond/pd.py +458 -0
- pybond/pl.py +480 -0
- pybond/pnl.py +110 -0
- pybond/polars_utils.py +96 -0
- pybond/pybond.abi3.so +0 -0
- pybond/pybond.pyi +391 -0
- tea_bond-0.3.11.dist-info/METADATA +7 -0
- tea_bond-0.3.11.dist-info/RECORD +26 -0
- tea_bond-0.3.11.dist-info/WHEEL +4 -0
pybond/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .bond import Bond
|
|
4
|
+
from .pybond import Future, Ib, Sse, get_version
|
|
5
|
+
from .pybond import TfEvaluator as _TfEvaluatorRS
|
|
6
|
+
|
|
7
|
+
__version__ = get_version()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TfEvaluator(_TfEvaluatorRS):
|
|
11
|
+
def __new__(cls, future, bond, *args, **kwargs):
|
|
12
|
+
if not isinstance(bond, Bond):
|
|
13
|
+
# 便于直接从Wind下载债券基础数据
|
|
14
|
+
bond = Bond(bond)
|
|
15
|
+
return super().__new__(cls, future, bond, *args, **kwargs)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = ["Bond", "Future", "Ib", "Sse", "TfEvaluator", "__version__"]
|
pybond/bond.py
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from datetime import date
|
|
5
|
+
from importlib.util import find_spec
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from .pybond import Bond as _BondRS
|
|
9
|
+
from .pybond import Future, download_bond
|
|
10
|
+
|
|
11
|
+
# from .ffi.utils import set_bond_data_path
|
|
12
|
+
|
|
13
|
+
WIND_AVAILABLE = find_spec("WindPy") is not None
|
|
14
|
+
|
|
15
|
+
if os.environ.get("BONDS_INFO_PATH") is not None:
|
|
16
|
+
bonds_info_environ_flag = True
|
|
17
|
+
bonds_info_path = Path(os.environ.get("BONDS_INFO_PATH"))
|
|
18
|
+
else:
|
|
19
|
+
bonds_info_environ_flag = False
|
|
20
|
+
bonds_info_path = Path(__file__).parent / "data" / "bonds_info"
|
|
21
|
+
os.environ["BONDS_INFO_PATH"] = str(bonds_info_path)
|
|
22
|
+
|
|
23
|
+
if not bonds_info_path.exists():
|
|
24
|
+
bonds_info_path.mkdir(parents=True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Bond(_BondRS):
|
|
28
|
+
def __new__(cls, code: str | int = "", path: str | Path | None = None):
|
|
29
|
+
"""
|
|
30
|
+
Create a new Bond instance.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
code (str | int): The bond code. If no extension is provided, '.IB' will be appended.
|
|
34
|
+
path (str | Path | None): Path to the bond info file. If None, uses default bonds_info_path.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Bond: A new Bond instance, either loaded from existing JSON file or downloaded.
|
|
38
|
+
|
|
39
|
+
Note:
|
|
40
|
+
If a JSON file for the bond code doesn't exist at the specified path,
|
|
41
|
+
the bond info will be downloaded automatically.
|
|
42
|
+
"""
|
|
43
|
+
code = str(code)
|
|
44
|
+
if code == "":
|
|
45
|
+
return super().__new__(cls, "", path)
|
|
46
|
+
if "." not in code:
|
|
47
|
+
code = code + ".IB"
|
|
48
|
+
path = bonds_info_path if path is None else Path(path)
|
|
49
|
+
if (path / (code + ".json")).exists():
|
|
50
|
+
return super().__new__(cls, code, path)
|
|
51
|
+
else:
|
|
52
|
+
cls.download(code, path)
|
|
53
|
+
return super().__new__(cls, code, path)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_json(cls, data: str | dict) -> Bond:
|
|
57
|
+
if isinstance(data, str):
|
|
58
|
+
import json
|
|
59
|
+
|
|
60
|
+
data = json.loads(data)
|
|
61
|
+
bond = Bond()
|
|
62
|
+
for k, v in data.items():
|
|
63
|
+
setattr(bond, k, v)
|
|
64
|
+
return bond
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def download(
|
|
68
|
+
code: str, path: str | None = None, source: str | None = None, save=True
|
|
69
|
+
):
|
|
70
|
+
"""
|
|
71
|
+
Download bond information from a specified source.
|
|
72
|
+
|
|
73
|
+
This method downloads bond information for a given bond code from either Wind or Rust.
|
|
74
|
+
If no source is specified, it defaults to Wind if the WindPy module is available; otherwise,
|
|
75
|
+
it falls back to Rust.
|
|
76
|
+
|
|
77
|
+
If the source is 'rust', the method will download IB bond information from China Money and
|
|
78
|
+
SH bond information from SSE (Shanghai Stock Exchange).
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
code (str): The bond code in the format 'XXXXXX.YY'. The code must include a dot.
|
|
82
|
+
path (str | None): The directory path where the downloaded bond information should be saved.
|
|
83
|
+
If None, the default path is used.
|
|
84
|
+
source (str | None): The source from which to download the bond information. Valid options are
|
|
85
|
+
'wind' or 'rust'. If None, the source is automatically determined.
|
|
86
|
+
save (bool): Whether to save the downloaded bond information to the specified path.
|
|
87
|
+
Defaults to True.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Bond: The downloaded bond object if the source is 'rust' and save is False.
|
|
91
|
+
Otherwise, returns None.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
AssertionError: If the code is not in the correct format or if the source is invalid.
|
|
95
|
+
"""
|
|
96
|
+
if source is None:
|
|
97
|
+
# 优先从wind下载
|
|
98
|
+
source = "wind" if WIND_AVAILABLE else "rust"
|
|
99
|
+
assert "." in code, "code should be in the format of XXXXXX.YY"
|
|
100
|
+
assert source in ("wind", "rust")
|
|
101
|
+
if source == "wind":
|
|
102
|
+
from .download import fetch_symbols, login
|
|
103
|
+
|
|
104
|
+
print(f"Start downloading bond info for {code} from Wind")
|
|
105
|
+
login()
|
|
106
|
+
fetch_symbols([code], save=save, save_folder=path)
|
|
107
|
+
else:
|
|
108
|
+
# let rust side handle the download
|
|
109
|
+
print(f"download {code}")
|
|
110
|
+
bond = download_bond(code)
|
|
111
|
+
if save:
|
|
112
|
+
bond.save(path)
|
|
113
|
+
return bond
|
|
114
|
+
|
|
115
|
+
def accrued_interest(
|
|
116
|
+
self, date: date, cp_dates: tuple[date, date] | None = None
|
|
117
|
+
) -> float:
|
|
118
|
+
"""
|
|
119
|
+
计算应计利息
|
|
120
|
+
|
|
121
|
+
银行间和交易所的计算规则不同,银行间是算头不算尾,而交易所是算头又算尾
|
|
122
|
+
"""
|
|
123
|
+
return self.calc_accrued_interest(date, cp_dates=cp_dates)
|
|
124
|
+
|
|
125
|
+
def dirty_price(
|
|
126
|
+
self,
|
|
127
|
+
ytm: float,
|
|
128
|
+
date: date,
|
|
129
|
+
cp_dates: tuple[date, date] | None = None,
|
|
130
|
+
remain_cp_num: int | None = None,
|
|
131
|
+
) -> float:
|
|
132
|
+
"""通过ytm计算债券全价"""
|
|
133
|
+
return self.calc_dirty_price_with_ytm(
|
|
134
|
+
ytm, date, cp_dates=cp_dates, remain_cp_num=remain_cp_num
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def clean_price(
|
|
138
|
+
self,
|
|
139
|
+
ytm: float,
|
|
140
|
+
date: date,
|
|
141
|
+
cp_dates: tuple[date, date] | None = None,
|
|
142
|
+
remain_cp_num: int | None = None,
|
|
143
|
+
) -> float:
|
|
144
|
+
"""通过ytm计算债券净价"""
|
|
145
|
+
return self.calc_clean_price_with_ytm(
|
|
146
|
+
ytm, date, cp_dates=cp_dates, remain_cp_num=remain_cp_num
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def macaulay_duration(
|
|
150
|
+
self,
|
|
151
|
+
ytm: float,
|
|
152
|
+
date: date,
|
|
153
|
+
cp_dates: tuple[date, date] | None = None,
|
|
154
|
+
remain_cp_num: int | None = None,
|
|
155
|
+
) -> float:
|
|
156
|
+
"""计算麦考利久期"""
|
|
157
|
+
return self.calc_macaulay_duration(
|
|
158
|
+
ytm, date, cp_dates=cp_dates, remain_cp_num=remain_cp_num
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def duration(
|
|
162
|
+
self,
|
|
163
|
+
ytm: float,
|
|
164
|
+
date: date,
|
|
165
|
+
cp_dates: tuple[date, date] | None = None,
|
|
166
|
+
remain_cp_num: int | None = None,
|
|
167
|
+
) -> float:
|
|
168
|
+
"""计算修正久期"""
|
|
169
|
+
return self.calc_duration(
|
|
170
|
+
ytm, date, cp_dates=cp_dates, remain_cp_num=remain_cp_num
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def cf(self, future: str | Future) -> float:
|
|
174
|
+
"""计算转换因子"""
|
|
175
|
+
from .pybond import TfEvaluator
|
|
176
|
+
|
|
177
|
+
return TfEvaluator(future, self).cf
|
|
178
|
+
|
|
179
|
+
def calc_ytm_with_clean_price(
|
|
180
|
+
self,
|
|
181
|
+
clean_price: float,
|
|
182
|
+
date: date,
|
|
183
|
+
cp_dates: tuple[date, date] | None = None,
|
|
184
|
+
remain_cp_num: int | None = None,
|
|
185
|
+
) -> float:
|
|
186
|
+
"""通过净价计算债券ytm"""
|
|
187
|
+
dirty_price = clean_price + self.accrued_interest(date, cp_dates=cp_dates)
|
|
188
|
+
return self.calc_ytm_with_price(
|
|
189
|
+
dirty_price, date, cp_dates=cp_dates, remain_cp_num=remain_cp_num
|
|
190
|
+
)
|
pybond/download.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from datetime import date
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from WindPy import w
|
|
9
|
+
|
|
10
|
+
default_save_folder = Path("bonds_info")
|
|
11
|
+
# if not default_save_folder.exists():
|
|
12
|
+
# default_save_folder.mkdir()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def save_json(path: Path | str, data: dict) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Save data into json file in temp path.
|
|
18
|
+
"""
|
|
19
|
+
path = Path(path)
|
|
20
|
+
with path.open(mode="w+", encoding="UTF-8") as f:
|
|
21
|
+
json.dump(data, f, indent=4, ensure_ascii=False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_interest_type(typ: str):
|
|
25
|
+
if typ == "固定利率":
|
|
26
|
+
return "Fixed"
|
|
27
|
+
elif typ == "浮动利率":
|
|
28
|
+
return "Floating"
|
|
29
|
+
elif typ == "累进利率":
|
|
30
|
+
return "Progressive"
|
|
31
|
+
elif typ == "零息":
|
|
32
|
+
return "Zero"
|
|
33
|
+
else:
|
|
34
|
+
msg = f"Unknown interest type: {typ}"
|
|
35
|
+
raise ValueError(msg)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_payment_type(typ: str):
|
|
39
|
+
if typ == "附息":
|
|
40
|
+
return "Coupon_Bear"
|
|
41
|
+
elif typ == "到期一次还本付息":
|
|
42
|
+
return "One_Time"
|
|
43
|
+
elif typ == "贴现":
|
|
44
|
+
return "Zero_Coupon"
|
|
45
|
+
else:
|
|
46
|
+
msg = f"Unknown payment type: {typ}"
|
|
47
|
+
raise ValueError(msg)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def fetch_symbols(
|
|
51
|
+
symbols: list[str],
|
|
52
|
+
*,
|
|
53
|
+
save: bool = True,
|
|
54
|
+
skip: bool = True,
|
|
55
|
+
save_folder: Path | str | None = None,
|
|
56
|
+
):
|
|
57
|
+
if save_folder is None:
|
|
58
|
+
save_folder = default_save_folder
|
|
59
|
+
if isinstance(save_folder, str):
|
|
60
|
+
save_folder = Path(save_folder)
|
|
61
|
+
if skip:
|
|
62
|
+
symbols = [s for s in symbols if not (save_folder / f"{s}.json").exists()]
|
|
63
|
+
data = w.wss(
|
|
64
|
+
symbols,
|
|
65
|
+
"sec_name,carrydate,maturitydate,interesttype,couponrate,paymenttype,actualbenchmark,coupon,interestfrequency,latestpar",
|
|
66
|
+
f"tradeDate={date.today()}",
|
|
67
|
+
).Data
|
|
68
|
+
returns = []
|
|
69
|
+
for i, symbol in enumerate(symbols):
|
|
70
|
+
m = {"bond_code": symbol}
|
|
71
|
+
m["mkt"] = symbol.split(".")[1].upper()
|
|
72
|
+
m["abbr"] = data[0][i] # 债券简称
|
|
73
|
+
m["par_value"] = float(data[9][i]) # 面值
|
|
74
|
+
m["cp_type"] = get_payment_type(data[7][i]) # 付息频率
|
|
75
|
+
m["interest_type"] = get_interest_type(data[3][i]) # 付息方式
|
|
76
|
+
m["cp_rate_1st"] = float(Decimal(str(data[4][i])) / 100) # 票面利率
|
|
77
|
+
m["base_rate"] = None
|
|
78
|
+
m["rate_spread"] = None
|
|
79
|
+
if m["cp_type"] == "Coupon_Bear":
|
|
80
|
+
m["inst_freq"] = int(data[8][i]) # 年付息次数
|
|
81
|
+
elif m["cp_type"] == "One_Time":
|
|
82
|
+
m["inst_freq"] = 1
|
|
83
|
+
elif m["cp_type"] == "Zero_Coupon":
|
|
84
|
+
m["inst_freq"] = 0
|
|
85
|
+
m["carry_date"] = data[1][i].strftime("%Y-%m-%d") # 起息日
|
|
86
|
+
m["maturity_date"] = data[2][i].strftime("%Y-%m-%d") # 到期日
|
|
87
|
+
m["day_count"] = data[6][i] # 实际基准
|
|
88
|
+
returns.append(m)
|
|
89
|
+
print(m)
|
|
90
|
+
if save:
|
|
91
|
+
if not save_folder.exists():
|
|
92
|
+
save_folder.mkdir(parents=True)
|
|
93
|
+
path = save_folder / f"{symbol}.json"
|
|
94
|
+
save_json(path, m)
|
|
95
|
+
return returns
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
WAIT_LOGIN = False
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def login():
|
|
102
|
+
global WAIT_LOGIN
|
|
103
|
+
if w.isconnected():
|
|
104
|
+
return
|
|
105
|
+
if WAIT_LOGIN:
|
|
106
|
+
import time
|
|
107
|
+
|
|
108
|
+
time.sleep(0.2)
|
|
109
|
+
login()
|
|
110
|
+
WAIT_LOGIN = True
|
|
111
|
+
login_res = w.start(waitTime=8)
|
|
112
|
+
WAIT_LOGIN = False
|
|
113
|
+
if login_res.ErrorCode != 0:
|
|
114
|
+
msg = f"Failed to login to Wind: {login_res.ErrorCode}"
|
|
115
|
+
raise RuntimeError(msg)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_all_symbols():
|
|
119
|
+
sector_ids = (
|
|
120
|
+
# "a101010101000000", # 国债银行间
|
|
121
|
+
# "a101010104000000", # 政策性银行债
|
|
122
|
+
"a101010201000000", # 上交所国债
|
|
123
|
+
)
|
|
124
|
+
res = []
|
|
125
|
+
names = []
|
|
126
|
+
for sector_id in sector_ids:
|
|
127
|
+
all_symbols = w.wset(
|
|
128
|
+
"sectorconstituent", f"sectorid={sector_id};field=wind_code,sec_name"
|
|
129
|
+
).Data
|
|
130
|
+
res.extend(all_symbols[0])
|
|
131
|
+
names.extend(all_symbols[1])
|
|
132
|
+
print("共有", len(res), "只债券")
|
|
133
|
+
return res
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
login()
|
|
138
|
+
# symbols = ["220003.IB", "220021.IB", "220006.IB", "220010.IB"]
|
|
139
|
+
# symbols = ["240006.IB"]
|
|
140
|
+
|
|
141
|
+
# symbols = ["019733.SH"]
|
|
142
|
+
# symbols = ["020647.SH"]
|
|
143
|
+
# symbols = ["019727.SH"]
|
|
144
|
+
symbols = ["2400006.IB"]
|
|
145
|
+
# symbols = get_all_symbols()
|
|
146
|
+
|
|
147
|
+
fetch_symbols(symbols, save=0, skip=True)
|
pybond/ffi/__init__.py
ADDED
pybond/ffi/bond.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
|
|
3
|
+
from .lib import lib
|
|
4
|
+
|
|
5
|
+
create_bond = lib.create_bond
|
|
6
|
+
create_bond.argtypes = (ctypes.c_void_p, ctypes.c_size_t)
|
|
7
|
+
create_bond.restype = ctypes.c_void_p
|
|
8
|
+
|
|
9
|
+
free_bond = lib.free_bond
|
|
10
|
+
free_bond.argtypes = [ctypes.c_void_p]
|
|
11
|
+
free_bond.restype = None
|
|
12
|
+
|
|
13
|
+
bond_coupon_rate = lib.bond_coupon_rate
|
|
14
|
+
bond_coupon_rate.argtypes = [ctypes.c_void_p]
|
|
15
|
+
bond_coupon_rate.restype = ctypes.c_double
|
|
16
|
+
|
|
17
|
+
bond_full_code = lib.bond_full_code
|
|
18
|
+
bond_full_code.argtypes = [ctypes.c_void_p]
|
|
19
|
+
bond_full_code.restype = ctypes.c_char_p
|
|
20
|
+
|
|
21
|
+
bond_calc_ytm = lib.bond_calc_ytm
|
|
22
|
+
bond_calc_ytm.argtypes = [
|
|
23
|
+
ctypes.c_void_p,
|
|
24
|
+
ctypes.c_double,
|
|
25
|
+
ctypes.c_uint32,
|
|
26
|
+
ctypes.c_uint32,
|
|
27
|
+
ctypes.c_uint32,
|
|
28
|
+
]
|
|
29
|
+
bond_calc_ytm.restype = ctypes.c_double
|
|
30
|
+
|
|
31
|
+
bond_duration = lib.bond_duration
|
|
32
|
+
bond_duration.argtypes = [
|
|
33
|
+
ctypes.c_void_p,
|
|
34
|
+
ctypes.c_double,
|
|
35
|
+
ctypes.c_uint32,
|
|
36
|
+
ctypes.c_uint32,
|
|
37
|
+
ctypes.c_uint32,
|
|
38
|
+
]
|
|
39
|
+
bond_duration.restype = ctypes.c_double
|
|
40
|
+
|
|
41
|
+
bond_accrued_interest = lib.bond_accrued_interest
|
|
42
|
+
bond_accrued_interest.argtypes = [
|
|
43
|
+
ctypes.c_void_p,
|
|
44
|
+
ctypes.c_uint32,
|
|
45
|
+
ctypes.c_uint32,
|
|
46
|
+
ctypes.c_uint32,
|
|
47
|
+
]
|
|
48
|
+
bond_accrued_interest.restype = ctypes.c_double
|
|
49
|
+
|
|
50
|
+
bond_dirty_price = lib.bond_dirty_price
|
|
51
|
+
bond_dirty_price.argtypes = [
|
|
52
|
+
ctypes.c_void_p,
|
|
53
|
+
ctypes.c_double,
|
|
54
|
+
ctypes.c_uint32,
|
|
55
|
+
ctypes.c_uint32,
|
|
56
|
+
ctypes.c_uint32,
|
|
57
|
+
]
|
|
58
|
+
bond_dirty_price.restype = ctypes.c_double
|
|
59
|
+
|
|
60
|
+
bond_clean_price = lib.bond_clean_price
|
|
61
|
+
bond_clean_price.argtypes = [
|
|
62
|
+
ctypes.c_void_p,
|
|
63
|
+
ctypes.c_double,
|
|
64
|
+
ctypes.c_uint32,
|
|
65
|
+
ctypes.c_uint32,
|
|
66
|
+
ctypes.c_uint32,
|
|
67
|
+
]
|
|
68
|
+
bond_clean_price.restype = ctypes.c_double
|
pybond/ffi/datetime.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
|
|
3
|
+
from .lib import lib
|
|
4
|
+
|
|
5
|
+
build_datetime_ns = lib.build_datetime_ns
|
|
6
|
+
build_datetime_ns.argtypes = (ctypes.c_int64,)
|
|
7
|
+
build_datetime_ns.restype = ctypes.c_void_p
|
|
8
|
+
|
|
9
|
+
build_datetime_from_utc_ns = lib.build_datetime_from_utc_ns
|
|
10
|
+
build_datetime_from_utc_ns.argtypes = (ctypes.c_int64,)
|
|
11
|
+
build_datetime_from_utc_ns.restype = ctypes.c_void_p
|
|
12
|
+
|
|
13
|
+
local_timestamp_nanos = lib.local_timestamp_nanos
|
|
14
|
+
local_timestamp_nanos.argtypes = (ctypes.c_void_p,)
|
|
15
|
+
local_timestamp_nanos.restype = ctypes.c_int64
|
|
16
|
+
|
|
17
|
+
timestamp_nanos = lib.timestamp_nanos
|
|
18
|
+
timestamp_nanos.argtypes = (ctypes.c_void_p,)
|
|
19
|
+
timestamp_nanos.restype = ctypes.c_int64
|
|
20
|
+
|
|
21
|
+
utc_timestamp_to_local = lib.utc_timestamp_to_local
|
|
22
|
+
utc_timestamp_to_local.argtypes = (ctypes.c_int64,)
|
|
23
|
+
utc_timestamp_to_local.restype = ctypes.c_int64
|
|
24
|
+
|
|
25
|
+
_free_datetime = lib.free_datetime
|
|
26
|
+
_free_datetime.argtypes = (ctypes.c_void_p,)
|
|
27
|
+
|
|
28
|
+
get_datetime_year = lib.get_datetime_year
|
|
29
|
+
get_datetime_year.argtypes = (ctypes.c_void_p,)
|
|
30
|
+
get_datetime_year.restype = ctypes.c_int32
|
|
31
|
+
|
|
32
|
+
get_datetime_month = lib.get_datetime_month
|
|
33
|
+
get_datetime_month.argtypes = (ctypes.c_void_p,)
|
|
34
|
+
get_datetime_month.restype = ctypes.c_int32
|
|
35
|
+
|
|
36
|
+
get_datetime_day = lib.get_datetime_day
|
|
37
|
+
get_datetime_day.argtypes = (ctypes.c_void_p,)
|
|
38
|
+
get_datetime_day.restype = ctypes.c_int32
|
|
39
|
+
|
|
40
|
+
get_datetime_hour = lib.get_datetime_hour
|
|
41
|
+
get_datetime_hour.argtypes = (ctypes.c_void_p,)
|
|
42
|
+
get_datetime_hour.restype = ctypes.c_int32
|
|
43
|
+
|
|
44
|
+
get_datetime_minute = lib.get_datetime_minute
|
|
45
|
+
get_datetime_minute.argtypes = (ctypes.c_void_p,)
|
|
46
|
+
get_datetime_minute.restype = ctypes.c_int32
|
|
47
|
+
|
|
48
|
+
get_datetime_second = lib.get_datetime_second
|
|
49
|
+
get_datetime_second.argtypes = (ctypes.c_void_p,)
|
|
50
|
+
get_datetime_second.restype = ctypes.c_int32
|
|
51
|
+
|
|
52
|
+
get_datetime_nanosecond = lib.get_datetime_nanosecond
|
|
53
|
+
get_datetime_nanosecond.argtypes = (ctypes.c_void_p,)
|
|
54
|
+
get_datetime_nanosecond.restype = ctypes.c_int32
|
|
55
|
+
|
|
56
|
+
datetime_with_time = lib.datetime_with_time
|
|
57
|
+
datetime_with_time.argtypes = (ctypes.c_void_p, (ctypes.c_uint32 * 6))
|
|
58
|
+
datetime_with_time.restype = ctypes.c_void_p
|
pybond/ffi/duration.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
|
|
3
|
+
from .lib import lib
|
|
4
|
+
|
|
5
|
+
parse_duration = lib.parse_duration
|
|
6
|
+
parse_duration.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
|
|
7
|
+
parse_duration.restype = ctypes.c_void_p
|
|
8
|
+
|
|
9
|
+
datetime_sub_datetime = lib.datetime_sub_datetime
|
|
10
|
+
datetime_sub_datetime.argtypes = [ctypes.c_int64, ctypes.c_int64]
|
|
11
|
+
datetime_sub_datetime.restype = ctypes.c_void_p
|
|
12
|
+
|
|
13
|
+
datetime_add_duration = lib.datetime_add_duration
|
|
14
|
+
datetime_add_duration.argtypes = [ctypes.c_int64, ctypes.c_void_p]
|
|
15
|
+
datetime_add_duration.restype = ctypes.c_int64
|
|
16
|
+
|
|
17
|
+
datetime_sub_duration = lib.datetime_sub_duration
|
|
18
|
+
datetime_sub_duration.argtypes = [ctypes.c_int64, ctypes.c_void_p]
|
|
19
|
+
datetime_sub_duration.restype = ctypes.c_int64
|
pybond/ffi/evaluators.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
|
|
3
|
+
from .lib import lib
|
|
4
|
+
|
|
5
|
+
# TfEvaluator creation and destruction
|
|
6
|
+
create_tf_evaluator = lib.create_tf_evaluator
|
|
7
|
+
create_tf_evaluator.argtypes = [
|
|
8
|
+
ctypes.c_void_p, # future_code_ptr
|
|
9
|
+
ctypes.c_size_t, # future_code_len
|
|
10
|
+
ctypes.c_double, # future_price
|
|
11
|
+
ctypes.c_void_p, # bond_code_ptr
|
|
12
|
+
ctypes.c_size_t, # bond_code_len
|
|
13
|
+
ctypes.c_double, # bond_ytm
|
|
14
|
+
ctypes.c_double, # capital_rate
|
|
15
|
+
ctypes.c_uint32, # year
|
|
16
|
+
ctypes.c_uint32, # month
|
|
17
|
+
ctypes.c_uint32, # day
|
|
18
|
+
]
|
|
19
|
+
create_tf_evaluator.restype = ctypes.c_void_p
|
|
20
|
+
|
|
21
|
+
create_tf_evaluator_with_reinvest = lib.create_tf_evaluator_with_reinvest
|
|
22
|
+
create_tf_evaluator_with_reinvest.argtypes = [
|
|
23
|
+
ctypes.c_void_p, # future_code_ptr
|
|
24
|
+
ctypes.c_size_t, # future_code_len
|
|
25
|
+
ctypes.c_double, # future_price
|
|
26
|
+
ctypes.c_void_p, # bond_code_ptr
|
|
27
|
+
ctypes.c_size_t, # bond_code_len
|
|
28
|
+
ctypes.c_double, # bond_ytm
|
|
29
|
+
ctypes.c_double, # capital_rate
|
|
30
|
+
ctypes.c_double, # reinvest_rate
|
|
31
|
+
ctypes.c_uint32, # year
|
|
32
|
+
ctypes.c_uint32, # month
|
|
33
|
+
ctypes.c_uint32, # day
|
|
34
|
+
]
|
|
35
|
+
create_tf_evaluator_with_reinvest.restype = ctypes.c_void_p
|
|
36
|
+
|
|
37
|
+
free_tf_evaluator = lib.free_tf_evaluator
|
|
38
|
+
free_tf_evaluator.argtypes = [ctypes.c_void_p]
|
|
39
|
+
free_tf_evaluator.restype = None
|
|
40
|
+
|
|
41
|
+
# Basic properties
|
|
42
|
+
tf_evaluator_is_deliverable = lib.tf_evaluator_is_deliverable
|
|
43
|
+
tf_evaluator_is_deliverable.argtypes = [ctypes.c_void_p]
|
|
44
|
+
tf_evaluator_is_deliverable.restype = ctypes.c_int
|
|
45
|
+
|
|
46
|
+
tf_evaluator_bond_code = lib.tf_evaluator_bond_code
|
|
47
|
+
tf_evaluator_bond_code.argtypes = [ctypes.c_void_p]
|
|
48
|
+
tf_evaluator_bond_code.restype = ctypes.c_char_p
|
|
49
|
+
|
|
50
|
+
tf_evaluator_future_code = lib.tf_evaluator_future_code
|
|
51
|
+
tf_evaluator_future_code.argtypes = [ctypes.c_void_p]
|
|
52
|
+
tf_evaluator_future_code.restype = ctypes.c_char_p
|
|
53
|
+
|
|
54
|
+
tf_evaluator_bond_ytm = lib.tf_evaluator_bond_ytm
|
|
55
|
+
tf_evaluator_bond_ytm.argtypes = [ctypes.c_void_p]
|
|
56
|
+
tf_evaluator_bond_ytm.restype = ctypes.c_double
|
|
57
|
+
|
|
58
|
+
tf_evaluator_future_price = lib.tf_evaluator_future_price
|
|
59
|
+
tf_evaluator_future_price.argtypes = [ctypes.c_void_p]
|
|
60
|
+
tf_evaluator_future_price.restype = ctypes.c_double
|
|
61
|
+
|
|
62
|
+
tf_evaluator_capital_rate = lib.tf_evaluator_capital_rate
|
|
63
|
+
tf_evaluator_capital_rate.argtypes = [ctypes.c_void_p]
|
|
64
|
+
tf_evaluator_capital_rate.restype = ctypes.c_double
|
|
65
|
+
|
|
66
|
+
tf_evaluator_reinvest_rate = lib.tf_evaluator_reinvest_rate
|
|
67
|
+
tf_evaluator_reinvest_rate.argtypes = [ctypes.c_void_p]
|
|
68
|
+
tf_evaluator_reinvest_rate.restype = ctypes.c_double
|
|
69
|
+
|
|
70
|
+
# Date functions
|
|
71
|
+
tf_evaluator_get_date = lib.tf_evaluator_get_date
|
|
72
|
+
tf_evaluator_get_date.argtypes = [
|
|
73
|
+
ctypes.c_void_p,
|
|
74
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
75
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
76
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
77
|
+
]
|
|
78
|
+
tf_evaluator_get_date.restype = None
|
|
79
|
+
|
|
80
|
+
tf_evaluator_get_deliver_date = lib.tf_evaluator_get_deliver_date
|
|
81
|
+
tf_evaluator_get_deliver_date.argtypes = [
|
|
82
|
+
ctypes.c_void_p,
|
|
83
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
84
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
85
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
86
|
+
]
|
|
87
|
+
tf_evaluator_get_deliver_date.restype = ctypes.c_int
|
|
88
|
+
|
|
89
|
+
# Calculation functions
|
|
90
|
+
tf_evaluator_accrued_interest = lib.tf_evaluator_accrued_interest
|
|
91
|
+
tf_evaluator_accrued_interest.argtypes = [ctypes.c_void_p]
|
|
92
|
+
tf_evaluator_accrued_interest.restype = ctypes.c_double
|
|
93
|
+
|
|
94
|
+
tf_evaluator_deliver_accrued_interest = lib.tf_evaluator_deliver_accrued_interest
|
|
95
|
+
tf_evaluator_deliver_accrued_interest.argtypes = [ctypes.c_void_p]
|
|
96
|
+
tf_evaluator_deliver_accrued_interest.restype = ctypes.c_double
|
|
97
|
+
|
|
98
|
+
tf_evaluator_cf = lib.tf_evaluator_cf
|
|
99
|
+
tf_evaluator_cf.argtypes = [ctypes.c_void_p]
|
|
100
|
+
tf_evaluator_cf.restype = ctypes.c_double
|
|
101
|
+
|
|
102
|
+
tf_evaluator_dirty_price = lib.tf_evaluator_dirty_price
|
|
103
|
+
tf_evaluator_dirty_price.argtypes = [ctypes.c_void_p]
|
|
104
|
+
tf_evaluator_dirty_price.restype = ctypes.c_double
|
|
105
|
+
|
|
106
|
+
tf_evaluator_clean_price = lib.tf_evaluator_clean_price
|
|
107
|
+
tf_evaluator_clean_price.argtypes = [ctypes.c_void_p]
|
|
108
|
+
tf_evaluator_clean_price.restype = ctypes.c_double
|
|
109
|
+
|
|
110
|
+
tf_evaluator_future_dirty_price = lib.tf_evaluator_future_dirty_price
|
|
111
|
+
tf_evaluator_future_dirty_price.argtypes = [ctypes.c_void_p]
|
|
112
|
+
tf_evaluator_future_dirty_price.restype = ctypes.c_double
|
|
113
|
+
|
|
114
|
+
tf_evaluator_deliver_cost = lib.tf_evaluator_deliver_cost
|
|
115
|
+
tf_evaluator_deliver_cost.argtypes = [ctypes.c_void_p]
|
|
116
|
+
tf_evaluator_deliver_cost.restype = ctypes.c_double
|
|
117
|
+
|
|
118
|
+
tf_evaluator_basis_spread = lib.tf_evaluator_basis_spread
|
|
119
|
+
tf_evaluator_basis_spread.argtypes = [ctypes.c_void_p]
|
|
120
|
+
tf_evaluator_basis_spread.restype = ctypes.c_double
|
|
121
|
+
|
|
122
|
+
tf_evaluator_f_b_spread = lib.tf_evaluator_f_b_spread
|
|
123
|
+
tf_evaluator_f_b_spread.argtypes = [ctypes.c_void_p]
|
|
124
|
+
tf_evaluator_f_b_spread.restype = ctypes.c_double
|
|
125
|
+
|
|
126
|
+
tf_evaluator_carry = lib.tf_evaluator_carry
|
|
127
|
+
tf_evaluator_carry.argtypes = [ctypes.c_void_p]
|
|
128
|
+
tf_evaluator_carry.restype = ctypes.c_double
|
|
129
|
+
|
|
130
|
+
tf_evaluator_net_basis_spread = lib.tf_evaluator_net_basis_spread
|
|
131
|
+
tf_evaluator_net_basis_spread.argtypes = [ctypes.c_void_p]
|
|
132
|
+
tf_evaluator_net_basis_spread.restype = ctypes.c_double
|
|
133
|
+
|
|
134
|
+
tf_evaluator_duration = lib.tf_evaluator_duration
|
|
135
|
+
tf_evaluator_duration.argtypes = [ctypes.c_void_p]
|
|
136
|
+
tf_evaluator_duration.restype = ctypes.c_double
|
|
137
|
+
|
|
138
|
+
tf_evaluator_irr = lib.tf_evaluator_irr
|
|
139
|
+
tf_evaluator_irr.argtypes = [ctypes.c_void_p]
|
|
140
|
+
tf_evaluator_irr.restype = ctypes.c_double
|
|
141
|
+
|
|
142
|
+
tf_evaluator_future_ytm = lib.tf_evaluator_future_ytm
|
|
143
|
+
tf_evaluator_future_ytm.argtypes = [ctypes.c_void_p]
|
|
144
|
+
tf_evaluator_future_ytm.restype = ctypes.c_double
|
|
145
|
+
|
|
146
|
+
tf_evaluator_remain_days_to_deliver = lib.tf_evaluator_remain_days_to_deliver
|
|
147
|
+
tf_evaluator_remain_days_to_deliver.argtypes = [ctypes.c_void_p]
|
|
148
|
+
tf_evaluator_remain_days_to_deliver.restype = ctypes.c_int
|
|
149
|
+
|
|
150
|
+
tf_evaluator_remain_cp_num = lib.tf_evaluator_remain_cp_num
|
|
151
|
+
tf_evaluator_remain_cp_num.argtypes = [ctypes.c_void_p]
|
|
152
|
+
tf_evaluator_remain_cp_num.restype = ctypes.c_int
|
|
153
|
+
|
|
154
|
+
tf_evaluator_remain_cp_to_deliver = lib.tf_evaluator_remain_cp_to_deliver
|
|
155
|
+
tf_evaluator_remain_cp_to_deliver.argtypes = [ctypes.c_void_p]
|
|
156
|
+
tf_evaluator_remain_cp_to_deliver.restype = ctypes.c_double
|
|
157
|
+
|
|
158
|
+
tf_evaluator_remain_cp_to_deliver_wm = lib.tf_evaluator_remain_cp_to_deliver_wm
|
|
159
|
+
tf_evaluator_remain_cp_to_deliver_wm.argtypes = [ctypes.c_void_p]
|
|
160
|
+
tf_evaluator_remain_cp_to_deliver_wm.restype = ctypes.c_double
|
|
161
|
+
|
|
162
|
+
tf_evaluator_calc_all = lib.tf_evaluator_calc_all
|
|
163
|
+
tf_evaluator_calc_all.argtypes = [ctypes.c_void_p]
|
|
164
|
+
tf_evaluator_calc_all.restype = ctypes.c_int
|
|
165
|
+
|
|
166
|
+
# Update function
|
|
167
|
+
tf_evaluator_update_info = lib.tf_evaluator_update_info
|
|
168
|
+
tf_evaluator_update_info.argtypes = [
|
|
169
|
+
ctypes.c_void_p, # evaluator
|
|
170
|
+
ctypes.c_void_p, # future_code_ptr
|
|
171
|
+
ctypes.c_size_t, # future_code_len
|
|
172
|
+
ctypes.c_double, # future_price
|
|
173
|
+
ctypes.c_void_p, # bond_code_ptr
|
|
174
|
+
ctypes.c_size_t, # bond_code_len
|
|
175
|
+
ctypes.c_double, # bond_ytm
|
|
176
|
+
ctypes.c_double, # capital_rate
|
|
177
|
+
ctypes.c_uint32, # year
|
|
178
|
+
ctypes.c_uint32, # month
|
|
179
|
+
ctypes.c_uint32, # day
|
|
180
|
+
]
|
|
181
|
+
tf_evaluator_update_info.restype = ctypes.c_int
|
|
182
|
+
|
|
183
|
+
# Utility function
|
|
184
|
+
free_string = lib.free_string
|
|
185
|
+
free_string.argtypes = [ctypes.c_char_p]
|
|
186
|
+
free_string.restype = None
|