Tinkoff / invest-python

Tinkoff Invest Python gRPC client
https://tinkoff.github.io/invest-python/
Apache License 2.0
313 stars 90 forks source link

[Bug] Ошибка при выдаче купонов по облигации #242

Open fast-runner opened 1 year ago

fast-runner commented 1 year ago

Что случилось?

Ищу купоны по облигации RU000A105FS4. Вызовы с одинаковыми параметрами могут вернуть... разные результат.

Тинькофф API обычно возвращает ошибочное количество - 2 купона. Может потребоваться несколько запусков для получения хотя бы одного правильного ответа - 5 купонов.

Воспроизведение

if bond.isin == 'RU000A105FS4':
                    logger.info(f'bond {bond}')
                    now = datetime.now()
                    today = datetime(year=now.year, month=now.month, day=now.day)
                    maturity_date = bond.maturity_date
                    coupons = client.instruments.get_bond_coupons(figi=bond.figi, from_=today, to=maturity_date)
                    logger.error(f'amount: {len(coupons.events)} coupons: {coupons}')
                    coupons = client.instruments.get_bond_coupons(figi=bond.figi, from_=today, to=maturity_date)
                    logger.error(f'amount: {len(coupons.events)} coupons: {coupons}')
                    coupons = client.instruments.get_bond_coupons(figi=bond.figi, from_=today, to=maturity_date)
                    logger.error(f'amount: {len(coupons.events)} coupons: {coupons}')

Result: 5 coupons, 2 coupons, 2 coupons.
5 coupons is correct one.

Tinkoff Invest Version

0.2.0-beta58

Python Version

3.11

OS

Windows

Логи

2023-07-26 20:49:29,882 - ERROR - amount: 5 coupons: GetBondCouponsResponse(events=[Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=2, fix_date=datetime.datetime(2023, 11, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 5, 16, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=3, fix_date=datetime.datetime(2024, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2024, 11, 12, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=4, fix_date=datetime.datetime(2024, 11, 11, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2024, 11, 12, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2025, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=5, fix_date=datetime.datetime(2025, 5, 12, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2024, 11, 12, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2025, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2025, 11, 11, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=6, fix_date=datetime.datetime(2025, 11, 10, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2025, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2025, 11, 11, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182)])
2023-07-26 20:49:29,971 - ERROR - amount: 2 coupons: GetBondCouponsResponse(events=[Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=2, fix_date=datetime.datetime(2023, 11, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 5, 16, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=3, fix_date=datetime.datetime(2024, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182)])
2023-07-26 20:49:30,247 - ERROR - amount: 2 coupons: GetBondCouponsResponse(events=[Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=2, fix_date=datetime.datetime(2023, 11, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 5, 16, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182), Coupon(figi='TCS00A105FS4', coupon_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=3, fix_date=datetime.datetime(2024, 5, 13, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=57, nano=340000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 11, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2024, 5, 14, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=182)])
fast-runner commented 1 year ago

Аналогична проблема с облигацией ticker 'RU000A0ZYCZ4'.

API возвращает только один купон. Финальный купон потерян в недрах backend.

[Coupon(figi='BBG00HZ418L3', coupon_date=datetime.datetime(2023, 8, 3, 0, 0, tzinfo=datetime.timezone.utc), coupon_number=23, fix_date=datetime.datetime(2023, 8, 2, 0, 0, tzinfo=datetime.timezone.utc), pay_one_bond=MoneyValue(currency='rub', units=7, nano=980000000), coupon_type=<CouponType.COUPON_TYPE_CONSTANT: 1>, coupon_start_date=datetime.datetime(2023, 5, 4, 0, 0, tzinfo=datetime.timezone.utc), coupon_end_date=datetime.datetime(2023, 8, 3, 0, 0, tzinfo=datetime.timezone.utc), coupon_period=91)]

fast-runner commented 1 year ago

Выявлено место ошибки.

Баг активно проявляется если использовать вызов client.instruments.get_bondcoupons(figi=bond.figi, from=today, to=maturity_date).

Если делать вызов client.instruments.get_bondcoupons(figi=bond.figi) то возвращаются все купоны. Ошибка связана с фильтрацией купонов по from to