lixinger-python 0.3.12__tar.gz → 0.3.13__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.
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.claude/settings.local.json +2 -1
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/CHANGELOG.md +17 -1
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/PKG-INFO +1 -1
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/base.py +48 -3
- lixinger_python-0.3.13/lixinger/exceptions.py +38 -0
- lixinger_python-0.3.13/lixinger/utils/retry.py +52 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/pyproject.toml +1 -1
- lixinger_python-0.3.13/tests/test_retry.py +257 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/uv.lock +1 -1
- lixinger_python-0.3.12/lixinger/exceptions.py +0 -22
- lixinger_python-0.3.12/lixinger/utils/retry.py +0 -31
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.env.example +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/copilot-instructions.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/workflows/README.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/workflows/pr-checks.yml +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.gitignore +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.pre-commit-config.yaml +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.python-version +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.ruff.toml +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/ADD_NEW_API_QUICK.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/ARCHITECTURE.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/CLAUDE.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/DOCUMENTATION.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/LICENSE +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/MANIFEST.in +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/PUBLISHING.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/README.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/codecov.yml +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/client.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/announcement.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/company.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/dividend.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/equity_change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/other_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/indices.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/profile.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/announcement.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/asset_combination.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/asset_industry_combination.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/profile.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/shareholdings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/constituent_weightings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/constituents.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/drawdown.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/fundamental.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/tracking_fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/overview.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/code-style.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/contributing.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/testing.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/company.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/configuration.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/installation.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/quickstart.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/company_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/company_profile_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/dividend_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/fs_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_constituent_weightings_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_constituents_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_info_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_margin_trading_example.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/company.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/dividend.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/equity_change.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/insurance.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/other_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/insurance.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/other_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/indices.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/namespace.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/profile.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/asset_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/asset_industry_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/fund.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/profile.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/shareholdings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/constituent_weightings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/constituents.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/drawdown.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/hybrid.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fundamental.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/index.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/margin_trading.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/namespace.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/tracking_fund.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/cni.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/sw.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/sw_2021.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/namespace.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/namespace.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/national_debt.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/rmb_deposits.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/rmb_loans.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/social_financing.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/client.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/config.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/company.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/dividend.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/equity_change.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/insurance.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/other_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/insurance.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/other_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/indices.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/asset_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/asset_industry_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/fund.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/profile.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/shareholdings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/constituent_weightings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/constituents.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/drawdown.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/hybrid.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fundamental.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/index.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/margin_trading.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/tracking_fund.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/cni.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/sw.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/sw_2021.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/national_debt.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/rmb_deposits.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/rmb_loans.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/social_financing.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/py.typed +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/__init__.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/api.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/dataframe.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/dict.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/rate_limiter.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/REMINDER.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/allotment.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/announcement.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/block-deal.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/customers.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/dividend.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/equity-change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/other_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fund-collection-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fund-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/other_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/capita.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/df.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/elr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/esc.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mm_ha.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mssc.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mtasl.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/npd.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/ple.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/shnc.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/t_a.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/tr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/tr_dri.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/indices.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/industries.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/inquiry.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/major-shareholders-shares-change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/majority-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/margin-trading-and-securities-lending.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/measures.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/mutual-market.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/nolimit-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/operating-data.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/operation-revenue-constitution.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/pledge.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/profile.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/senior-executive-shares-change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/shareholders-num.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/suppliers.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/trading-abnormal.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/announcement.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/asset-combination.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/asset-industry-combination.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/dividend.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/drawdown.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/fees.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/f_as.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/f_nlacan.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fet_s.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/ff.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fpr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fss.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/manager.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/net-value-of-dividend-reinvestment.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/net-value.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/profile.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shareholders-structure.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shareholdings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shares.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/split.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/total-net-value.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/turnover-rate.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/asset-scale.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/fund-list.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/fund-manager-list.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/hot/fc_as.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/hot/fc_asr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/shareholdings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/hot/fmi.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/hot/fmp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/management-funds.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/profit-ratio.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/shareholdings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/constituent-weightings.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/constituents.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/drawdown.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fundamental.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/cp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/ic.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/ifet_sni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/mm_ha.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/mtasl.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/tr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/tr_cp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/margin-trading-and-securities-lending.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/mutual-market.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/tracking-fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/cni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/sw.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/sw_2021.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/allotment.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/announcement.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/dividend.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/employee.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/equity-change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/other_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/reit.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fund-collection-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fund-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/bank.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/insurance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/other_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/reit.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/security.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/capita.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/director_equity_change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/mm_ah.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/npd.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/rep.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/ss.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/ss_ha.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/tr.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/tr_dri.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/indices.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/industries.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/latest-shareholders.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/mutual-market.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/operation-revenue-constitution.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/profile.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/repurchase.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/shareholders-equity-change.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/short-selling.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/split.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/constituents.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/drawdown.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fs/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fundamental.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/cp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/ic.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/ifet_sni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/mm_ah.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/mutual-market.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/tracking-fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/constituents/hsi.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fs/hsi/hybrid.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fs/hsi/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fundamental/hsi.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/hot/mm_ah/hsi.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/mutual-market/hsi.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/bop.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/central-bank-balance-sheet.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/credit-securities-account.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/crude-oil.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/currency-exchange-rate.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/domestic-debt-securities.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/domestic-trade.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/energy.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/foreign-assets.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/foreign-trade.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/gdp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/gold-price.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/industrialization.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/interest-rates.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/investment-in-fixed-assets.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/investor.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/leverage-ratio.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/money-supply.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/national-debt.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/national-finance.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/natural-gas.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/non-ferrous-metals.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/official-reserve-assets.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/platinum-price.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/population.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/price-index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/real-estate.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/required-reserves.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmb-deposits.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmb-loans.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmbidx.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/silver-price.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/social-financing.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/stamp-duty.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/traffic-transportation.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/unemployment-rate.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/usdx.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/vix-fear-index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/candlestick.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/constituents.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/drawdown.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/fs/non_financial.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/fundamental.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/hot/cp.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/hot/ifet_sni.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/tracking-fund.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/volatility.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index.md +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/mkdocs.yml +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/mypy.ini +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/explore_api.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/generate_docs.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/publish.sh +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/company/test_announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/company/test_dividend.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/index/test_constituents.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/conftest.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_bank_statements.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_client.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_cn_index_candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_cn_index_constituent_weightings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_company.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_company_profile.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_equity_change.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_announcement.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_asset_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_asset_industry_combination.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_candlestick.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_profile.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_shareholdings.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental_bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental_security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_constituents.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_drawdown.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_bank.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_hybrid.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_non_financial.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_security.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fundamental.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_margin_trading.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_indices.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_industry_margin_trading.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_insurance_statements.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_integration.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_macro.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_non_financial_statements.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_other_financial_statements.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_security_statements.py +0 -0
- {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_tracking_fund.py +0 -0
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"Bash(git push *)",
|
|
13
13
|
"Bash(uv sync *)",
|
|
14
14
|
"Bash(curl -s https://pypi.org/pypi/lixinger-python/0.3.11/json)",
|
|
15
|
-
"Bash(python3 -c \"import sys, json; d = json.load\\(sys.stdin\\); print\\('Name:', d['info']['name']\\); print\\('Version:', d['info']['version']\\); print\\('Files:', len\\(d['urls']\\)\\)\")"
|
|
15
|
+
"Bash(python3 -c \"import sys, json; d = json.load\\(sys.stdin\\); print\\('Name:', d['info']['name']\\); print\\('Version:', d['info']['version']\\); print\\('Files:', len\\(d['urls']\\)\\)\")",
|
|
16
|
+
"Bash(curl -s https://pypi.org/pypi/lixinger-python/0.3.12/json)"
|
|
16
17
|
]
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -11,6 +11,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
- Add support for US stock market APIs
|
|
12
12
|
- Add caching mechanism for API responses
|
|
13
13
|
|
|
14
|
+
## [0.3.13] - 2026-07-05
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `AuthenticationError` (401) 和 `ValidationError` 不再被重试. 之前会被
|
|
18
|
+
重试装饰器兜底 catch 3 次, 白白浪费 ~7 秒和 3 次配额.
|
|
19
|
+
- 429 响应现在会尊重服务端返回的 `Retry-After` header, 按 header 指定的
|
|
20
|
+
时长退避(同时支持 RFC 7231 的 `delta-seconds` 和 `HTTP-date` 两种格式);
|
|
21
|
+
header 缺失时保持原有的 1/2/4 秒指数退避.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- `async_retry_on_failure` 新增 `no_retry_on: tuple[type[Exception], ...]`
|
|
25
|
+
参数, 命中即立即抛出.
|
|
26
|
+
- `RateLimitError` 新增 `retry_after: float | None` 属性; 若异常对象带
|
|
27
|
+
`retry_after` 属性, 重试装饰器会用它替代默认的指数退避.
|
|
28
|
+
|
|
14
29
|
## [0.3.12] - 2026-07-05
|
|
15
30
|
|
|
16
31
|
### Added
|
|
@@ -186,7 +201,8 @@ asyncio.run(main())
|
|
|
186
201
|
- Pre-commit hooks
|
|
187
202
|
- MkDocs for documentation
|
|
188
203
|
|
|
189
|
-
[unreleased]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.
|
|
204
|
+
[unreleased]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.13...HEAD
|
|
205
|
+
[0.3.13]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.12...v0.3.13
|
|
190
206
|
[0.3.12]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.11...v0.3.12
|
|
191
207
|
[0.3.11]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.10...v0.3.11
|
|
192
208
|
[0.3.10]: https://github.com/TedaLIEz/lixinger-python/compare/v0.3.9...v0.3.10
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
from email.utils import parsedate_to_datetime
|
|
1
3
|
from typing import Any
|
|
2
4
|
|
|
3
5
|
import httpx
|
|
4
6
|
|
|
5
7
|
from lixinger.config import Config, settings
|
|
6
|
-
from lixinger.exceptions import APIError, AuthenticationError, RateLimitError
|
|
8
|
+
from lixinger.exceptions import APIError, AuthenticationError, RateLimitError, ValidationError
|
|
7
9
|
from lixinger.utils.rate_limiter import AsyncRateLimiter
|
|
8
10
|
from lixinger.utils.retry import async_retry_on_failure
|
|
9
11
|
|
|
@@ -29,6 +31,39 @@ def get_global_client() -> httpx.AsyncClient:
|
|
|
29
31
|
return _global_http_client
|
|
30
32
|
|
|
31
33
|
|
|
34
|
+
def _parse_retry_after(header_value: str | None) -> float | None:
|
|
35
|
+
"""Parse a Retry-After header into seconds.
|
|
36
|
+
|
|
37
|
+
Accepts both formats defined by RFC 7231 §7.1.3:
|
|
38
|
+
- ``delta-seconds`` (e.g. ``"120"``)
|
|
39
|
+
- ``HTTP-date`` (e.g. ``"Wed, 21 Oct 2015 07:28:00 GMT"``)
|
|
40
|
+
|
|
41
|
+
Returns ``None`` when the header is missing or unparseable so the caller
|
|
42
|
+
can fall back to its default backoff.
|
|
43
|
+
"""
|
|
44
|
+
if not header_value:
|
|
45
|
+
return None
|
|
46
|
+
value = header_value.strip()
|
|
47
|
+
# Try delta-seconds first — the common case.
|
|
48
|
+
try:
|
|
49
|
+
seconds = float(value)
|
|
50
|
+
except ValueError:
|
|
51
|
+
pass
|
|
52
|
+
else:
|
|
53
|
+
return seconds if seconds >= 0 else None
|
|
54
|
+
# Fall back to HTTP-date.
|
|
55
|
+
try:
|
|
56
|
+
target = parsedate_to_datetime(value)
|
|
57
|
+
except (TypeError, ValueError):
|
|
58
|
+
return None
|
|
59
|
+
if target.tzinfo is None:
|
|
60
|
+
# RFC 7231 HTTP-date is always GMT, so treat naive dates as UTC.
|
|
61
|
+
target = target.replace(tzinfo=UTC)
|
|
62
|
+
now = datetime.now(tz=target.tzinfo)
|
|
63
|
+
delta = (target - now).total_seconds()
|
|
64
|
+
return max(delta, 0.0)
|
|
65
|
+
|
|
66
|
+
|
|
32
67
|
class BaseAPI:
|
|
33
68
|
"""Base class for API endpoint groups."""
|
|
34
69
|
|
|
@@ -42,7 +77,12 @@ class BaseAPI:
|
|
|
42
77
|
self._config = config or settings
|
|
43
78
|
self._rate_limiter = rate_limiter or _global_rate_limiter
|
|
44
79
|
|
|
45
|
-
@async_retry_on_failure(
|
|
80
|
+
@async_retry_on_failure(
|
|
81
|
+
max_retries=3,
|
|
82
|
+
backoff=1.0,
|
|
83
|
+
# Do NOT retry non-transient errors even though they inherit from Exception.
|
|
84
|
+
no_retry_on=(AuthenticationError, ValidationError),
|
|
85
|
+
)
|
|
46
86
|
async def _request(
|
|
47
87
|
self,
|
|
48
88
|
method: str,
|
|
@@ -72,7 +112,12 @@ class BaseAPI:
|
|
|
72
112
|
)
|
|
73
113
|
|
|
74
114
|
if response.status_code == 429:
|
|
75
|
-
|
|
115
|
+
retry_after = _parse_retry_after(response.headers.get("Retry-After"))
|
|
116
|
+
raise RateLimitError(
|
|
117
|
+
"Rate limit exceeded",
|
|
118
|
+
status_code=429,
|
|
119
|
+
retry_after=retry_after,
|
|
120
|
+
)
|
|
76
121
|
if response.status_code == 401:
|
|
77
122
|
raise AuthenticationError("Authentication failed", status_code=401)
|
|
78
123
|
if response.status_code != 200:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class LixingerError(Exception):
|
|
2
|
+
"""Base exception for Lixinger SDK."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class APIError(LixingerError):
|
|
6
|
+
"""API request failed."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, message: str, status_code: int | None = None) -> None:
|
|
9
|
+
super().__init__(message)
|
|
10
|
+
self.status_code = status_code
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RateLimitError(APIError):
|
|
14
|
+
"""Rate limit exceeded (429).
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
retry_after: Optional server-provided delay in seconds parsed from the
|
|
18
|
+
``Retry-After`` response header. ``None`` when the header was absent
|
|
19
|
+
or malformed. The retry decorator honors this value in place of the
|
|
20
|
+
default exponential backoff.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
message: str,
|
|
26
|
+
status_code: int | None = None,
|
|
27
|
+
retry_after: float | None = None,
|
|
28
|
+
) -> None:
|
|
29
|
+
super().__init__(message, status_code=status_code)
|
|
30
|
+
self.retry_after = retry_after
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AuthenticationError(APIError):
|
|
34
|
+
"""Authentication failed (401)."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ValidationError(LixingerError):
|
|
38
|
+
"""Request validation failed."""
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Awaitable, Callable
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
T = TypeVar("T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def async_retry_on_failure(
|
|
10
|
+
max_retries: int = 3,
|
|
11
|
+
backoff: float = 1.0,
|
|
12
|
+
retry_on: tuple[type[Exception], ...] = (Exception,),
|
|
13
|
+
no_retry_on: tuple[type[Exception], ...] = (),
|
|
14
|
+
) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]:
|
|
15
|
+
"""Retry failed async requests with exponential backoff.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
max_retries: Maximum number of retry attempts (total attempts = max_retries + 1).
|
|
19
|
+
backoff: Base delay in seconds for exponential backoff (2**attempt * backoff).
|
|
20
|
+
retry_on: Exception types that trigger a retry.
|
|
21
|
+
no_retry_on: Exception types that must NOT be retried even if matched by
|
|
22
|
+
``retry_on``. Use this to short-circuit on non-transient errors such
|
|
23
|
+
as authentication failures.
|
|
24
|
+
|
|
25
|
+
If the caught exception exposes a ``retry_after`` attribute (float seconds,
|
|
26
|
+
typically parsed from a ``Retry-After`` HTTP header on 429 responses), that
|
|
27
|
+
value is used for the next sleep instead of the exponential backoff.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
|
|
31
|
+
@wraps(func)
|
|
32
|
+
async def wrapper(*args: Any, **kwargs: Any) -> T:
|
|
33
|
+
for attempt in range(max_retries + 1):
|
|
34
|
+
try:
|
|
35
|
+
return await func(*args, **kwargs)
|
|
36
|
+
except retry_on as e:
|
|
37
|
+
# Bail out immediately on non-transient errors.
|
|
38
|
+
if no_retry_on and isinstance(e, no_retry_on):
|
|
39
|
+
raise
|
|
40
|
+
if attempt == max_retries:
|
|
41
|
+
raise
|
|
42
|
+
# Prefer server-provided retry hint when available.
|
|
43
|
+
hint = getattr(e, "retry_after", None)
|
|
44
|
+
wait_time = (
|
|
45
|
+
float(hint) if isinstance(hint, int | float) and hint > 0 else backoff * (2**attempt)
|
|
46
|
+
)
|
|
47
|
+
await asyncio.sleep(wait_time)
|
|
48
|
+
raise RuntimeError("Unreachable")
|
|
49
|
+
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
return decorator
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""Unit tests for retry / rate-limit handling in BaseAPI._request."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from unittest.mock import AsyncMock, Mock, patch
|
|
7
|
+
|
|
8
|
+
import httpx
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from lixinger.api.base import BaseAPI, _parse_retry_after
|
|
12
|
+
from lixinger.exceptions import (
|
|
13
|
+
APIError,
|
|
14
|
+
AuthenticationError,
|
|
15
|
+
RateLimitError,
|
|
16
|
+
ValidationError,
|
|
17
|
+
)
|
|
18
|
+
from lixinger.utils.rate_limiter import AsyncRateLimiter
|
|
19
|
+
from lixinger.utils.retry import async_retry_on_failure
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _mock_response(status_code: int, headers: dict[str, str] | None = None, json_body: dict | None = None) -> Mock:
|
|
23
|
+
resp = Mock()
|
|
24
|
+
resp.status_code = status_code
|
|
25
|
+
resp.text = "mock error body"
|
|
26
|
+
resp.headers = headers or {}
|
|
27
|
+
resp.json.return_value = json_body or {}
|
|
28
|
+
return resp
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _make_api(monkeypatch: pytest.MonkeyPatch) -> BaseAPI:
|
|
32
|
+
monkeypatch.setenv("LIXINGER_API_KEY", "test_key")
|
|
33
|
+
# Fresh rate limiter so tests do not interact.
|
|
34
|
+
return BaseAPI(rate_limiter=AsyncRateLimiter(max_requests=1000))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# --- _parse_retry_after ---------------------------------------------------
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_parse_retry_after_delta_seconds():
|
|
41
|
+
assert _parse_retry_after("120") == 120.0
|
|
42
|
+
assert _parse_retry_after(" 5 ") == 5.0
|
|
43
|
+
assert _parse_retry_after("0") == 0.0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_parse_retry_after_negative_is_ignored():
|
|
47
|
+
assert _parse_retry_after("-1") is None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_parse_retry_after_missing_or_bad():
|
|
51
|
+
assert _parse_retry_after(None) is None
|
|
52
|
+
assert _parse_retry_after("") is None
|
|
53
|
+
assert _parse_retry_after("not a date") is None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_parse_retry_after_http_date_in_future():
|
|
57
|
+
# Any date in the far future should map to a positive delta.
|
|
58
|
+
delta = _parse_retry_after("Wed, 21 Oct 2099 07:28:00 GMT")
|
|
59
|
+
assert delta is not None
|
|
60
|
+
assert delta > 0
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_parse_retry_after_http_date_in_past_clamped_to_zero():
|
|
64
|
+
delta = _parse_retry_after("Wed, 21 Oct 2000 07:28:00 GMT")
|
|
65
|
+
assert delta == 0.0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# --- 401 must not be retried (problem 1) ----------------------------------
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.asyncio
|
|
72
|
+
async def test_401_authentication_error_is_not_retried(monkeypatch):
|
|
73
|
+
api = _make_api(monkeypatch)
|
|
74
|
+
|
|
75
|
+
calls = 0
|
|
76
|
+
|
|
77
|
+
async def fake_request(*args, **kwargs):
|
|
78
|
+
nonlocal calls
|
|
79
|
+
calls += 1
|
|
80
|
+
return _mock_response(401)
|
|
81
|
+
|
|
82
|
+
with patch("lixinger.api.base.get_global_client") as mock_client_factory:
|
|
83
|
+
client = Mock(spec=httpx.AsyncClient)
|
|
84
|
+
client.request = AsyncMock(side_effect=fake_request)
|
|
85
|
+
mock_client_factory.return_value = client
|
|
86
|
+
|
|
87
|
+
with pytest.raises(AuthenticationError):
|
|
88
|
+
await api._request("POST", "/whatever", json={"a": 1})
|
|
89
|
+
|
|
90
|
+
assert calls == 1, "401 must fail fast without retries"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@pytest.mark.asyncio
|
|
94
|
+
async def test_validation_error_is_not_retried():
|
|
95
|
+
# The retry decorator should honor no_retry_on for arbitrary exceptions too.
|
|
96
|
+
calls = 0
|
|
97
|
+
|
|
98
|
+
@async_retry_on_failure(max_retries=3, backoff=0, no_retry_on=(ValidationError,))
|
|
99
|
+
async def fn():
|
|
100
|
+
nonlocal calls
|
|
101
|
+
calls += 1
|
|
102
|
+
raise ValidationError("bad payload")
|
|
103
|
+
|
|
104
|
+
with pytest.raises(ValidationError):
|
|
105
|
+
await fn()
|
|
106
|
+
assert calls == 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# --- 429 with Retry-After (problem 3) -------------------------------------
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@pytest.mark.asyncio
|
|
113
|
+
async def test_429_with_retry_after_honors_header(monkeypatch):
|
|
114
|
+
api = _make_api(monkeypatch)
|
|
115
|
+
|
|
116
|
+
# First two responses 429 with Retry-After=2, third succeeds.
|
|
117
|
+
responses = [
|
|
118
|
+
_mock_response(429, headers={"Retry-After": "2"}),
|
|
119
|
+
_mock_response(429, headers={"Retry-After": "3"}),
|
|
120
|
+
_mock_response(200, json_body={"code": 1, "message": "success", "data": [1, 2, 3]}),
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
with (
|
|
124
|
+
patch("lixinger.api.base.get_global_client") as mock_client_factory,
|
|
125
|
+
patch("lixinger.utils.retry.asyncio.sleep", new=AsyncMock()) as mock_sleep,
|
|
126
|
+
):
|
|
127
|
+
client = Mock(spec=httpx.AsyncClient)
|
|
128
|
+
client.request = AsyncMock(side_effect=responses)
|
|
129
|
+
mock_client_factory.return_value = client
|
|
130
|
+
|
|
131
|
+
data = await api._request("POST", "/whatever", json={"a": 1})
|
|
132
|
+
|
|
133
|
+
assert data == [1, 2, 3]
|
|
134
|
+
assert client.request.await_count == 3
|
|
135
|
+
# Two sleeps between three attempts, both using the header value not backoff.
|
|
136
|
+
sleeps = [call.args[0] for call in mock_sleep.await_args_list]
|
|
137
|
+
assert sleeps == [2.0, 3.0]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@pytest.mark.asyncio
|
|
141
|
+
async def test_429_without_retry_after_falls_back_to_exponential_backoff(monkeypatch):
|
|
142
|
+
api = _make_api(monkeypatch)
|
|
143
|
+
|
|
144
|
+
# 429 without Retry-After header — decorator should use 1*2^attempt.
|
|
145
|
+
responses = [
|
|
146
|
+
_mock_response(429),
|
|
147
|
+
_mock_response(429),
|
|
148
|
+
_mock_response(200, json_body={"code": 1, "message": "success", "data": []}),
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
with (
|
|
152
|
+
patch("lixinger.api.base.get_global_client") as mock_client_factory,
|
|
153
|
+
patch("lixinger.utils.retry.asyncio.sleep", new=AsyncMock()) as mock_sleep,
|
|
154
|
+
):
|
|
155
|
+
client = Mock(spec=httpx.AsyncClient)
|
|
156
|
+
client.request = AsyncMock(side_effect=responses)
|
|
157
|
+
mock_client_factory.return_value = client
|
|
158
|
+
|
|
159
|
+
await api._request("POST", "/whatever", json={"a": 1})
|
|
160
|
+
|
|
161
|
+
sleeps = [call.args[0] for call in mock_sleep.await_args_list]
|
|
162
|
+
# base=1.0, attempts 0 and 1 -> 1.0, 2.0
|
|
163
|
+
assert sleeps == [1.0, 2.0]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@pytest.mark.asyncio
|
|
167
|
+
async def test_429_exhausts_retries_and_raises(monkeypatch):
|
|
168
|
+
api = _make_api(monkeypatch)
|
|
169
|
+
|
|
170
|
+
responses = [_mock_response(429, headers={"Retry-After": "1"})] * 4 # 1 initial + 3 retries
|
|
171
|
+
|
|
172
|
+
with (
|
|
173
|
+
patch("lixinger.api.base.get_global_client") as mock_client_factory,
|
|
174
|
+
patch("lixinger.utils.retry.asyncio.sleep", new=AsyncMock()),
|
|
175
|
+
):
|
|
176
|
+
client = Mock(spec=httpx.AsyncClient)
|
|
177
|
+
client.request = AsyncMock(side_effect=responses)
|
|
178
|
+
mock_client_factory.return_value = client
|
|
179
|
+
|
|
180
|
+
with pytest.raises(RateLimitError) as exc:
|
|
181
|
+
await api._request("POST", "/whatever", json={"a": 1})
|
|
182
|
+
|
|
183
|
+
assert exc.value.status_code == 429
|
|
184
|
+
assert exc.value.retry_after == 1.0
|
|
185
|
+
assert client.request.await_count == 4 # max_retries=3 -> 4 total attempts
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# --- Guard: transient APIError (e.g. 5xx) still retried -------------------
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@pytest.mark.asyncio
|
|
192
|
+
async def test_generic_api_error_still_retries(monkeypatch):
|
|
193
|
+
api = _make_api(monkeypatch)
|
|
194
|
+
|
|
195
|
+
responses = [
|
|
196
|
+
_mock_response(500),
|
|
197
|
+
_mock_response(200, json_body={"code": 1, "message": "success", "data": [42]}),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
with (
|
|
201
|
+
patch("lixinger.api.base.get_global_client") as mock_client_factory,
|
|
202
|
+
patch("lixinger.utils.retry.asyncio.sleep", new=AsyncMock()),
|
|
203
|
+
):
|
|
204
|
+
client = Mock(spec=httpx.AsyncClient)
|
|
205
|
+
client.request = AsyncMock(side_effect=responses)
|
|
206
|
+
mock_client_factory.return_value = client
|
|
207
|
+
|
|
208
|
+
data = await api._request("POST", "/whatever", json={"a": 1})
|
|
209
|
+
|
|
210
|
+
assert data == [42]
|
|
211
|
+
assert client.request.await_count == 2
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# --- Retry decorator: retry_after wins over backoff -----------------------
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@pytest.mark.asyncio
|
|
218
|
+
async def test_retry_decorator_uses_retry_after_attribute():
|
|
219
|
+
calls = 0
|
|
220
|
+
|
|
221
|
+
class TransientError(Exception):
|
|
222
|
+
def __init__(self):
|
|
223
|
+
super().__init__("boom")
|
|
224
|
+
self.retry_after = 7.5
|
|
225
|
+
|
|
226
|
+
@async_retry_on_failure(max_retries=2, backoff=1.0)
|
|
227
|
+
async def fn():
|
|
228
|
+
nonlocal calls
|
|
229
|
+
calls += 1
|
|
230
|
+
if calls < 3:
|
|
231
|
+
raise TransientError()
|
|
232
|
+
return "ok"
|
|
233
|
+
|
|
234
|
+
with patch("lixinger.utils.retry.asyncio.sleep", new=AsyncMock()) as mock_sleep:
|
|
235
|
+
assert await fn() == "ok"
|
|
236
|
+
|
|
237
|
+
sleeps = [call.args[0] for call in mock_sleep.await_args_list]
|
|
238
|
+
assert sleeps == [7.5, 7.5]
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# Sanity: real asyncio.sleep still works when the decorator retries once.
|
|
242
|
+
@pytest.mark.asyncio
|
|
243
|
+
async def test_retry_decorator_actually_sleeps():
|
|
244
|
+
calls = 0
|
|
245
|
+
|
|
246
|
+
@async_retry_on_failure(max_retries=1, backoff=0.01)
|
|
247
|
+
async def fn():
|
|
248
|
+
nonlocal calls
|
|
249
|
+
calls += 1
|
|
250
|
+
if calls == 1:
|
|
251
|
+
raise RuntimeError("transient")
|
|
252
|
+
return "done"
|
|
253
|
+
|
|
254
|
+
start = asyncio.get_event_loop().time()
|
|
255
|
+
assert await fn() == "done"
|
|
256
|
+
elapsed = asyncio.get_event_loop().time() - start
|
|
257
|
+
assert elapsed >= 0.005 # slept at least most of 10ms
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
class LixingerError(Exception):
|
|
2
|
-
"""Base exception for Lixinger SDK."""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class APIError(LixingerError):
|
|
6
|
-
"""API request failed."""
|
|
7
|
-
|
|
8
|
-
def __init__(self, message: str, status_code: int | None = None) -> None:
|
|
9
|
-
super().__init__(message)
|
|
10
|
-
self.status_code = status_code
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class RateLimitError(APIError):
|
|
14
|
-
"""Rate limit exceeded (429)."""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class AuthenticationError(APIError):
|
|
18
|
-
"""Authentication failed (401)."""
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ValidationError(LixingerError):
|
|
22
|
-
"""Request validation failed."""
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from collections.abc import Awaitable, Callable
|
|
3
|
-
from functools import wraps
|
|
4
|
-
from typing import Any, TypeVar
|
|
5
|
-
|
|
6
|
-
T = TypeVar("T")
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def async_retry_on_failure(
|
|
10
|
-
max_retries: int = 3,
|
|
11
|
-
backoff: float = 1.0,
|
|
12
|
-
retry_on: tuple[type[Exception], ...] = (Exception,),
|
|
13
|
-
) -> Callable[[Callable[..., Awaitable[T]]], Callable[..., Awaitable[T]]]:
|
|
14
|
-
"""Retry failed async requests with exponential backoff."""
|
|
15
|
-
|
|
16
|
-
def decorator(func: Callable[..., Awaitable[T]]) -> Callable[..., Awaitable[T]]:
|
|
17
|
-
@wraps(func)
|
|
18
|
-
async def wrapper(*args: Any, **kwargs: Any) -> T:
|
|
19
|
-
for attempt in range(max_retries + 1):
|
|
20
|
-
try:
|
|
21
|
-
return await func(*args, **kwargs)
|
|
22
|
-
except retry_on as e:
|
|
23
|
-
if attempt == max_retries:
|
|
24
|
-
raise
|
|
25
|
-
wait_time = backoff * (2**attempt)
|
|
26
|
-
await asyncio.sleep(wait_time)
|
|
27
|
-
raise RuntimeError("Unreachable")
|
|
28
|
-
|
|
29
|
-
return wrapper
|
|
30
|
-
|
|
31
|
-
return decorator
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/non_financial.md
RENAMED
|
File without changes
|
{lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/other_financial.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/asset_industry_combination.md
RENAMED
|
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
|