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.
Files changed (496) hide show
  1. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.claude/settings.local.json +2 -1
  2. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/CHANGELOG.md +17 -1
  3. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/PKG-INFO +1 -1
  4. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/base.py +48 -3
  5. lixinger_python-0.3.13/lixinger/exceptions.py +38 -0
  6. lixinger_python-0.3.13/lixinger/utils/retry.py +52 -0
  7. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/pyproject.toml +1 -1
  8. lixinger_python-0.3.13/tests/test_retry.py +257 -0
  9. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/uv.lock +1 -1
  10. lixinger_python-0.3.12/lixinger/exceptions.py +0 -22
  11. lixinger_python-0.3.12/lixinger/utils/retry.py +0 -31
  12. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.env.example +0 -0
  13. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/copilot-instructions.md +0 -0
  14. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/workflows/README.md +0 -0
  15. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.github/workflows/pr-checks.yml +0 -0
  16. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.gitignore +0 -0
  17. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.pre-commit-config.yaml +0 -0
  18. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.python-version +0 -0
  19. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/.ruff.toml +0 -0
  20. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/ADD_NEW_API_QUICK.md +0 -0
  21. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/ARCHITECTURE.md +0 -0
  22. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/CLAUDE.md +0 -0
  23. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/DOCUMENTATION.md +0 -0
  24. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/LICENSE +0 -0
  25. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/MANIFEST.in +0 -0
  26. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/PUBLISHING.md +0 -0
  27. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/README.md +0 -0
  28. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/codecov.yml +0 -0
  29. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/client.md +0 -0
  30. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/announcement.md +0 -0
  31. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/candlestick.md +0 -0
  32. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/company.md +0 -0
  33. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/dividend.md +0 -0
  34. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/equity_change.md +0 -0
  35. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fs/non_financial.md +0 -0
  36. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/bank.md +0 -0
  37. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/insurance.md +0 -0
  38. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/non_financial.md +0 -0
  39. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/other_financial.md +0 -0
  40. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/fundamental/security.md +0 -0
  41. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/indices.md +0 -0
  42. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/company/profile.md +0 -0
  43. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/announcement.md +0 -0
  44. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/asset_combination.md +0 -0
  45. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/asset_industry_combination.md +0 -0
  46. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/candlestick.md +0 -0
  47. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/fund.md +0 -0
  48. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/profile.md +0 -0
  49. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/fund/shareholdings.md +0 -0
  50. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/candlestick.md +0 -0
  51. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/constituent_weightings.md +0 -0
  52. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/constituents.md +0 -0
  53. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/drawdown.md +0 -0
  54. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/fundamental.md +0 -0
  55. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/index.md +0 -0
  56. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/index/tracking_fund.md +0 -0
  57. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/api/overview.md +0 -0
  58. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/code-style.md +0 -0
  59. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/contributing.md +0 -0
  60. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/development/testing.md +0 -0
  61. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/company.md +0 -0
  62. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/fund.md +0 -0
  63. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/examples/index.md +0 -0
  64. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/configuration.md +0 -0
  65. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/installation.md +0 -0
  66. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/getting-started/quickstart.md +0 -0
  67. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/docs/index.md +0 -0
  68. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/company_example.py +0 -0
  69. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/company_profile_example.py +0 -0
  70. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/dividend_example.py +0 -0
  71. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/fs_example.py +0 -0
  72. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_constituent_weightings_example.py +0 -0
  73. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_constituents_example.py +0 -0
  74. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_info_example.py +0 -0
  75. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/examples/index_margin_trading_example.py +0 -0
  76. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/__init__.py +0 -0
  77. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/__init__.py +0 -0
  78. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/__init__.py +0 -0
  79. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/__init__.py +0 -0
  80. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/announcement.py +0 -0
  81. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/candlestick.py +0 -0
  82. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/company.py +0 -0
  83. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/dividend.py +0 -0
  84. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/equity_change.py +0 -0
  85. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/__init__.py +0 -0
  86. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/bank.py +0 -0
  87. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/insurance.py +0 -0
  88. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/non_financial.py +0 -0
  89. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/other_financial.py +0 -0
  90. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fs/security.py +0 -0
  91. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/__init__.py +0 -0
  92. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/bank.py +0 -0
  93. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/insurance.py +0 -0
  94. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/non_financial.py +0 -0
  95. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/other_financial.py +0 -0
  96. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/fundamental/security.py +0 -0
  97. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/indices.py +0 -0
  98. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/namespace.py +0 -0
  99. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/company/profile.py +0 -0
  100. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/__init__.py +0 -0
  101. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/announcement.py +0 -0
  102. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/asset_combination.py +0 -0
  103. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/asset_industry_combination.py +0 -0
  104. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/candlestick.py +0 -0
  105. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/fund.py +0 -0
  106. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/profile.py +0 -0
  107. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/fund/shareholdings.py +0 -0
  108. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/__init__.py +0 -0
  109. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/candlestick.py +0 -0
  110. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/constituent_weightings.py +0 -0
  111. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/constituents.py +0 -0
  112. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/drawdown.py +0 -0
  113. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/__init__.py +0 -0
  114. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/bank.py +0 -0
  115. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/hybrid.py +0 -0
  116. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/non_financial.py +0 -0
  117. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fs/security.py +0 -0
  118. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/fundamental.py +0 -0
  119. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/index.py +0 -0
  120. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/margin_trading.py +0 -0
  121. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/namespace.py +0 -0
  122. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/index/tracking_fund.py +0 -0
  123. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/__init__.py +0 -0
  124. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/__init__.py +0 -0
  125. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/cni.py +0 -0
  126. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/sw.py +0 -0
  127. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/margin_trading/sw_2021.py +0 -0
  128. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/cn/industry/namespace.py +0 -0
  129. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/__init__.py +0 -0
  130. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/namespace.py +0 -0
  131. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/national_debt.py +0 -0
  132. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/rmb_deposits.py +0 -0
  133. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/rmb_loans.py +0 -0
  134. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/api/macro/social_financing.py +0 -0
  135. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/client.py +0 -0
  136. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/config.py +0 -0
  137. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/__init__.py +0 -0
  138. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/__init__.py +0 -0
  139. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/__init__.py +0 -0
  140. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/announcement.py +0 -0
  141. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/candlestick.py +0 -0
  142. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/company.py +0 -0
  143. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/dividend.py +0 -0
  144. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/equity_change.py +0 -0
  145. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/__init__.py +0 -0
  146. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/bank.py +0 -0
  147. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/insurance.py +0 -0
  148. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/non_financial.py +0 -0
  149. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/other_financial.py +0 -0
  150. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fs/security.py +0 -0
  151. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/__init__.py +0 -0
  152. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/bank.py +0 -0
  153. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/insurance.py +0 -0
  154. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/non_financial.py +0 -0
  155. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/other_financial.py +0 -0
  156. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/fundamental/security.py +0 -0
  157. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/company/indices.py +0 -0
  158. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/__init__.py +0 -0
  159. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/announcement.py +0 -0
  160. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/asset_combination.py +0 -0
  161. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/asset_industry_combination.py +0 -0
  162. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/candlestick.py +0 -0
  163. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/fund.py +0 -0
  164. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/profile.py +0 -0
  165. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/fund/shareholdings.py +0 -0
  166. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/__init__.py +0 -0
  167. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/candlestick.py +0 -0
  168. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/constituent_weightings.py +0 -0
  169. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/constituents.py +0 -0
  170. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/drawdown.py +0 -0
  171. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/__init__.py +0 -0
  172. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/bank.py +0 -0
  173. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/hybrid.py +0 -0
  174. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/non_financial.py +0 -0
  175. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fs/security.py +0 -0
  176. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/fundamental.py +0 -0
  177. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/index.py +0 -0
  178. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/margin_trading.py +0 -0
  179. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/index/tracking_fund.py +0 -0
  180. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/__init__.py +0 -0
  181. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/__init__.py +0 -0
  182. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/cni.py +0 -0
  183. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/sw.py +0 -0
  184. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/cn/industry/margin_trading/sw_2021.py +0 -0
  185. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/__init__.py +0 -0
  186. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/national_debt.py +0 -0
  187. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/rmb_deposits.py +0 -0
  188. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/rmb_loans.py +0 -0
  189. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/models/macro/social_financing.py +0 -0
  190. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/py.typed +0 -0
  191. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/__init__.py +0 -0
  192. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/api.py +0 -0
  193. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/dataframe.py +0 -0
  194. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/dict.py +0 -0
  195. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger/utils/rate_limiter.py +0 -0
  196. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/REMINDER.md +0 -0
  197. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/allotment.md +0 -0
  198. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/announcement.md +0 -0
  199. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/block-deal.md +0 -0
  200. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/candlestick.md +0 -0
  201. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/customers.md +0 -0
  202. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/dividend.md +0 -0
  203. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/equity-change.md +0 -0
  204. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/bank.md +0 -0
  205. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/insurance.md +0 -0
  206. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/non_financial.md +0 -0
  207. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/other_financial.md +0 -0
  208. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fs/security.md +0 -0
  209. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fund-collection-shareholders.md +0 -0
  210. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fund-shareholders.md +0 -0
  211. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/bank.md +0 -0
  212. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/insurance.md +0 -0
  213. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/non_financial.md +0 -0
  214. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/other_financial.md +0 -0
  215. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/fundamental/security.md +0 -0
  216. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/capita.md +0 -0
  217. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/df.md +0 -0
  218. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/elr.md +0 -0
  219. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/esc.md +0 -0
  220. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mm_ha.md +0 -0
  221. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mssc.md +0 -0
  222. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/mtasl.md +0 -0
  223. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/npd.md +0 -0
  224. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/ple.md +0 -0
  225. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/shnc.md +0 -0
  226. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/t_a.md +0 -0
  227. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/tr.md +0 -0
  228. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/hot/tr_dri.md +0 -0
  229. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/indices.md +0 -0
  230. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/industries.md +0 -0
  231. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/inquiry.md +0 -0
  232. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/major-shareholders-shares-change.md +0 -0
  233. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/majority-shareholders.md +0 -0
  234. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/margin-trading-and-securities-lending.md +0 -0
  235. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/measures.md +0 -0
  236. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/mutual-market.md +0 -0
  237. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/nolimit-shareholders.md +0 -0
  238. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/operating-data.md +0 -0
  239. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/operation-revenue-constitution.md +0 -0
  240. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/pledge.md +0 -0
  241. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/profile.md +0 -0
  242. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/senior-executive-shares-change.md +0 -0
  243. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/shareholders-num.md +0 -0
  244. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/suppliers.md +0 -0
  245. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/trading-abnormal.md +0 -0
  246. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company/volatility.md +0 -0
  247. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/company.md +0 -0
  248. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/announcement.md +0 -0
  249. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/asset-combination.md +0 -0
  250. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/asset-industry-combination.md +0 -0
  251. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/candlestick.md +0 -0
  252. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/dividend.md +0 -0
  253. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/drawdown.md +0 -0
  254. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/fees.md +0 -0
  255. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/f_as.md +0 -0
  256. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/f_nlacan.md +0 -0
  257. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fet_s.md +0 -0
  258. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/ff.md +0 -0
  259. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fp.md +0 -0
  260. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fpr.md +0 -0
  261. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/hot/fss.md +0 -0
  262. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/manager.md +0 -0
  263. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/net-value-of-dividend-reinvestment.md +0 -0
  264. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/net-value.md +0 -0
  265. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/profile.md +0 -0
  266. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shareholders-structure.md +0 -0
  267. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shareholdings.md +0 -0
  268. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/shares.md +0 -0
  269. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/split.md +0 -0
  270. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/total-net-value.md +0 -0
  271. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/turnover-rate.md +0 -0
  272. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund/volatility.md +0 -0
  273. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/asset-scale.md +0 -0
  274. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/fund-list.md +0 -0
  275. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/fund-manager-list.md +0 -0
  276. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/hot/fc_as.md +0 -0
  277. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/hot/fc_asr.md +0 -0
  278. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company/shareholdings.md +0 -0
  279. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-company.md +0 -0
  280. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/hot/fmi.md +0 -0
  281. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/hot/fmp.md +0 -0
  282. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/management-funds.md +0 -0
  283. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/profit-ratio.md +0 -0
  284. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager/shareholdings.md +0 -0
  285. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund-manager.md +0 -0
  286. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/fund.md +0 -0
  287. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/candlestick.md +0 -0
  288. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/constituent-weightings.md +0 -0
  289. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/constituents.md +0 -0
  290. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/drawdown.md +0 -0
  291. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/bank.md +0 -0
  292. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/hybrid.md +0 -0
  293. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/non_financial.md +0 -0
  294. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fs/security.md +0 -0
  295. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/fundamental.md +0 -0
  296. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/cp.md +0 -0
  297. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/ic.md +0 -0
  298. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/ifet_sni.md +0 -0
  299. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/mm_ha.md +0 -0
  300. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/mtasl.md +0 -0
  301. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/tr.md +0 -0
  302. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/hot/tr_cp.md +0 -0
  303. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/margin-trading-and-securities-lending.md +0 -0
  304. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/mutual-market.md +0 -0
  305. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/tracking-fund.md +0 -0
  306. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index/volatility.md +0 -0
  307. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/index.md +0 -0
  308. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/cni.md +0 -0
  309. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/sw.md +0 -0
  310. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/constituents/sw_2021.md +0 -0
  311. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/bank.md +0 -0
  312. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/hybrid.md +0 -0
  313. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/insurance.md +0 -0
  314. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/non_financial.md +0 -0
  315. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/cni/security.md +0 -0
  316. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/bank.md +0 -0
  317. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/hybrid.md +0 -0
  318. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/insurance.md +0 -0
  319. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/non_financial.md +0 -0
  320. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw/security.md +0 -0
  321. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/bank.md +0 -0
  322. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/hybrid.md +0 -0
  323. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/insurance.md +0 -0
  324. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/non_financial.md +0 -0
  325. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fs/sw_2021/security.md +0 -0
  326. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/cni.md +0 -0
  327. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/sw.md +0 -0
  328. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/fundamental/sw_2021.md +0 -0
  329. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/cni.md +0 -0
  330. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/sw.md +0 -0
  331. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mm_ha/sw_2021.md +0 -0
  332. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/cni.md +0 -0
  333. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/sw.md +0 -0
  334. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/hot/mtasl/sw_2021.md +0 -0
  335. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/cni.md +0 -0
  336. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/sw.md +0 -0
  337. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/margin-trading-and-securities-lending/sw_2021.md +0 -0
  338. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/cni.md +0 -0
  339. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/sw.md +0 -0
  340. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry/mutual-market/sw_2021.md +0 -0
  341. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/cn/industry.md +0 -0
  342. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/allotment.md +0 -0
  343. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/announcement.md +0 -0
  344. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/candlestick.md +0 -0
  345. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/dividend.md +0 -0
  346. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/employee.md +0 -0
  347. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/equity-change.md +0 -0
  348. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/bank.md +0 -0
  349. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/insurance.md +0 -0
  350. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/non_financial.md +0 -0
  351. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/other_financial.md +0 -0
  352. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/reit.md +0 -0
  353. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fs/security.md +0 -0
  354. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fund-collection-shareholders.md +0 -0
  355. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fund-shareholders.md +0 -0
  356. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/bank.md +0 -0
  357. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/insurance.md +0 -0
  358. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/non_financial.md +0 -0
  359. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/other_financial.md +0 -0
  360. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/reit.md +0 -0
  361. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/fundamental/security.md +0 -0
  362. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/capita.md +0 -0
  363. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/director_equity_change.md +0 -0
  364. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/mm_ah.md +0 -0
  365. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/npd.md +0 -0
  366. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/rep.md +0 -0
  367. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/ss.md +0 -0
  368. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/ss_ha.md +0 -0
  369. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/tr.md +0 -0
  370. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/hot/tr_dri.md +0 -0
  371. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/indices.md +0 -0
  372. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/industries.md +0 -0
  373. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/latest-shareholders.md +0 -0
  374. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/mutual-market.md +0 -0
  375. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/operation-revenue-constitution.md +0 -0
  376. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/profile.md +0 -0
  377. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/repurchase.md +0 -0
  378. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/shareholders-equity-change.md +0 -0
  379. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/short-selling.md +0 -0
  380. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/split.md +0 -0
  381. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company/volatility.md +0 -0
  382. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/company.md +0 -0
  383. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/candlestick.md +0 -0
  384. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/constituents.md +0 -0
  385. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/drawdown.md +0 -0
  386. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fs/hybrid.md +0 -0
  387. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fs/non_financial.md +0 -0
  388. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/fundamental.md +0 -0
  389. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/cp.md +0 -0
  390. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/ic.md +0 -0
  391. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/ifet_sni.md +0 -0
  392. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/hot/mm_ah.md +0 -0
  393. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/mutual-market.md +0 -0
  394. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/tracking-fund.md +0 -0
  395. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index/volatility.md +0 -0
  396. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/index.md +0 -0
  397. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/constituents/hsi.md +0 -0
  398. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fs/hsi/hybrid.md +0 -0
  399. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fs/hsi/non_financial.md +0 -0
  400. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/fundamental/hsi.md +0 -0
  401. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/hot/mm_ah/hsi.md +0 -0
  402. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry/mutual-market/hsi.md +0 -0
  403. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/hk/industry.md +0 -0
  404. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/bop.md +0 -0
  405. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/central-bank-balance-sheet.md +0 -0
  406. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/credit-securities-account.md +0 -0
  407. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/crude-oil.md +0 -0
  408. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/currency-exchange-rate.md +0 -0
  409. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/domestic-debt-securities.md +0 -0
  410. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/domestic-trade.md +0 -0
  411. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/energy.md +0 -0
  412. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/foreign-assets.md +0 -0
  413. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/foreign-trade.md +0 -0
  414. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/gdp.md +0 -0
  415. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/gold-price.md +0 -0
  416. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/industrialization.md +0 -0
  417. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/interest-rates.md +0 -0
  418. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/investment-in-fixed-assets.md +0 -0
  419. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/investor.md +0 -0
  420. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/leverage-ratio.md +0 -0
  421. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/money-supply.md +0 -0
  422. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/national-debt.md +0 -0
  423. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/national-finance.md +0 -0
  424. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/natural-gas.md +0 -0
  425. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/non-ferrous-metals.md +0 -0
  426. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/official-reserve-assets.md +0 -0
  427. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/platinum-price.md +0 -0
  428. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/population.md +0 -0
  429. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/price-index.md +0 -0
  430. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/real-estate.md +0 -0
  431. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/required-reserves.md +0 -0
  432. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmb-deposits.md +0 -0
  433. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmb-loans.md +0 -0
  434. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/rmbidx.md +0 -0
  435. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/silver-price.md +0 -0
  436. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/social-financing.md +0 -0
  437. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/stamp-duty.md +0 -0
  438. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/traffic-transportation.md +0 -0
  439. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/unemployment-rate.md +0 -0
  440. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/usdx.md +0 -0
  441. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/macro/vix-fear-index.md +0 -0
  442. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/candlestick.md +0 -0
  443. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/constituents.md +0 -0
  444. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/drawdown.md +0 -0
  445. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/fs/non_financial.md +0 -0
  446. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/fundamental.md +0 -0
  447. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/hot/cp.md +0 -0
  448. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/hot/ifet_sni.md +0 -0
  449. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/tracking-fund.md +0 -0
  450. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index/volatility.md +0 -0
  451. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/lixinger-api/us/index.md +0 -0
  452. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/mkdocs.yml +0 -0
  453. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/mypy.ini +0 -0
  454. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/explore_api.py +0 -0
  455. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/generate_docs.py +0 -0
  456. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/scripts/publish.sh +0 -0
  457. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/company/test_announcement.py +0 -0
  458. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/company/test_dividend.py +0 -0
  459. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/api/cn/index/test_constituents.py +0 -0
  460. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/conftest.py +0 -0
  461. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_bank_statements.py +0 -0
  462. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_candlestick.py +0 -0
  463. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_client.py +0 -0
  464. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_cn_index_candlestick.py +0 -0
  465. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_cn_index_constituent_weightings.py +0 -0
  466. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_company.py +0 -0
  467. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_company_profile.py +0 -0
  468. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_equity_change.py +0 -0
  469. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund.py +0 -0
  470. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_announcement.py +0 -0
  471. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_asset_combination.py +0 -0
  472. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_asset_industry_combination.py +0 -0
  473. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_candlestick.py +0 -0
  474. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_profile.py +0 -0
  475. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fund_shareholdings.py +0 -0
  476. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental.py +0 -0
  477. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental_bank.py +0 -0
  478. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_fundamental_security.py +0 -0
  479. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index.py +0 -0
  480. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_constituents.py +0 -0
  481. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_drawdown.py +0 -0
  482. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_bank.py +0 -0
  483. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_hybrid.py +0 -0
  484. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_non_financial.py +0 -0
  485. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fs_security.py +0 -0
  486. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_fundamental.py +0 -0
  487. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_index_margin_trading.py +0 -0
  488. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_indices.py +0 -0
  489. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_industry_margin_trading.py +0 -0
  490. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_insurance_statements.py +0 -0
  491. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_integration.py +0 -0
  492. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_macro.py +0 -0
  493. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_non_financial_statements.py +0 -0
  494. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_other_financial_statements.py +0 -0
  495. {lixinger_python-0.3.12 → lixinger_python-0.3.13}/tests/test_security_statements.py +0 -0
  496. {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.12...HEAD
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lixinger-python
3
- Version: 0.3.12
3
+ Version: 0.3.13
4
4
  Summary: Python SDK for Lixinger Financial Data API
5
5
  Project-URL: Homepage, https://www.lixinger.com
6
6
  Project-URL: Documentation, https://www.lixinger.com/open/api/doc
@@ -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(max_retries=3, backoff=1.0)
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
- raise RateLimitError("Rate limit exceeded", status_code=429)
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lixinger-python"
3
- version = "0.3.12"
3
+ version = "0.3.13"
4
4
  description = "Python SDK for Lixinger Financial Data API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -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
@@ -629,7 +629,7 @@ wheels = [
629
629
 
630
630
  [[package]]
631
631
  name = "lixinger-python"
632
- version = "0.3.12"
632
+ version = "0.3.13"
633
633
  source = { editable = "." }
634
634
  dependencies = [
635
635
  { name = "httpx", extra = ["socks"] },
@@ -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