lixinger-python 0.1.2__tar.gz → 0.2.0__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.2.0/.claude/settings.local.json +81 -0
- lixinger_python-0.2.0/ADD_NEW_API_QUICK.md +203 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/AGENTS.md +41 -18
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/CHANGELOG.md +48 -1
- lixinger_python-0.2.0/PKG-INFO +329 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/PUBLISHING.md +1 -1
- lixinger_python-0.2.0/README.md +303 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/SETUP.md +117 -7
- lixinger_python-0.2.0/docs/examples/company.md +55 -0
- lixinger_python-0.2.0/docs/getting-started/quickstart.md +368 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/index.md +30 -14
- lixinger_python-0.2.0/examples/company_example.py +41 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/examples/company_profile_example.py +11 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/__init__.py +2 -2
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/base.py +12 -12
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/announcement.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/candlestick.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/company.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/dividend.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/equity_change.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/fs/non_financial.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/fundamental.py +24 -24
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/indices.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/profile.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/announcement.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/asset_combination.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/asset_industry_combination.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/candlestick.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/fund.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/profile.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/shareholdings.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/candlestick.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/constituent_weightings.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/constituents.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/drawdown.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/fundamental.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/index.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/tracking_fund.py +4 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/client.py +15 -13
- lixinger_python-0.2.0/lixinger/utils/api.py +15 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/utils/rate_limiter.py +5 -4
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/utils/retry.py +9 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/pyproject.toml +2 -2
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/api/cn/company/test_announcement.py +13 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/api/cn/company/test_dividend.py +9 -6
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_candlestick.py +8 -6
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_client.py +15 -11
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_cn_index_candlestick.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_cn_index_constituent_weightings.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_company.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_company_profile.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_equity_change.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_announcement.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_asset_combination.py +15 -11
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_asset_industry_combination.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_candlestick.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_profile.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fund_shareholdings.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_fundamental.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_index.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_index_constituents.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_index_drawdown.py +4 -3
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_index_fundamental.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_indices.py +9 -7
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_integration.py +52 -28
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_non_financial_statements.py +12 -9
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/test_tracking_fund.py +11 -8
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/uv.lock +64 -58
- lixinger_python-0.1.2/.claude/settings.local.json +0 -56
- lixinger_python-0.1.2/ADD_NEW_API_QUICK.md +0 -150
- lixinger_python-0.1.2/PKG-INFO +0 -171
- lixinger_python-0.1.2/README.md +0 -145
- lixinger_python-0.1.2/docs/examples/company.md +0 -24
- lixinger_python-0.1.2/docs/getting-started/quickstart.md +0 -256
- lixinger_python-0.1.2/examples/company_example.py +0 -39
- lixinger_python-0.1.2/lixinger/utils/api.py +0 -15
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.env.example +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.github/copilot-instructions.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.github/workflows/README.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.github/workflows/pr-checks.yml +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.gitignore +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.pre-commit-config.yaml +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.python-version +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/.ruff.toml +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/CLAUDE.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/DOCS.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/LICENSE +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/MANIFEST.in +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/codecov.yml +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/client.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/announcement.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/candlestick.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/company.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/dividend.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/equity_change.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/fs/non_financial.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/fundamental.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/indices.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/company/profile.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/announcement.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/asset_combination.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/asset_industry_combination.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/candlestick.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/fund.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/profile.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/fund/shareholdings.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/candlestick.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/constituent_weightings.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/constituents.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/drawdown.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/fundamental.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/index.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/index/tracking_fund.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/api/overview.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/development/code-style.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/development/contributing.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/development/testing.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/examples/fund.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/examples/index.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/getting-started/configuration.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/docs/getting-started/installation.md +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/fund_response.json +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/fs/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/company/namespace.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/fund/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/api/cn/index/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/config.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/exceptions.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/announcement.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/candlestick.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/company.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/dividend.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/equity_change.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/fs/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/fs/non_financial.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/fundamental.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/company/indices.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/announcement.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/asset_combination.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/asset_industry_combination.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/candlestick.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/fund.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/profile.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/fund/shareholdings.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/candlestick.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/constituent_weightings.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/constituents.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/drawdown.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/fundamental.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/index.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/models/cn/index/tracking_fund.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/py.typed +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/utils/__init__.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/utils/dataframe.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/lixinger/utils/dict.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/mkdocs.yml +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/mypy.ini +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/scripts/explore_api.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/scripts/generate_docs.py +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/scripts/publish.sh +0 -0
- {lixinger_python-0.1.2 → lixinger_python-0.2.0}/tests/conftest.py +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(tree -L 3 -I \".venv|venv|__pycache__|*.pyc\")",
|
|
5
|
+
"Bash(find . -type d -not -path ./.venv/* -not -path ./.git/*)",
|
|
6
|
+
"Bash(uv --version)",
|
|
7
|
+
"WebFetch(domain:www.lixinger.com)",
|
|
8
|
+
"Bash(uv add python-dotenv)",
|
|
9
|
+
"Bash(python -c \"from lixinger.config import get_config_from_env; print\\(''Config module loads successfully''\\)\")",
|
|
10
|
+
"Bash(python -c \"from lixinger import LixingerClient; print\\(''LixingerClient imports successfully''\\)\")",
|
|
11
|
+
"Bash(ls -la .env*)",
|
|
12
|
+
"Bash(python -c \"\nimport sys\nsys.path.insert\\(0, ''.''\\)\n\n# Test that api_key parameter doesn''t exist\nfrom lixinger.client import LixingerClient\nimport inspect\n\nsig = inspect.signature\\(LixingerClient.__init__\\)\nparams = list\\(sig.parameters.keys\\(\\)\\)\nprint\\(''LixingerClient.__init__ parameters:'', params\\)\nprint\\(\\)\nprint\\(''✓ api_key parameter removed!'' if ''api_key'' not in params else ''✗ api_key still exists!''\\)\nprint\\(''✓ Only keyword-only parameters remain''\\)\n\")",
|
|
13
|
+
"Bash(uv add --dev pre-commit)",
|
|
14
|
+
"Bash(uv pip install -e .)",
|
|
15
|
+
"Bash(python -c \"import lixinger; print\\(''✓ lixinger imports successfully''\\)\")",
|
|
16
|
+
"Bash(python -c \"from lixinger import LixingerClient; print\\(''✓ LixingerClient imports successfully''\\)\")",
|
|
17
|
+
"Bash(python -c \"import yaml; yaml.safe_load\\(open\\(''codecov.yml''\\)\\)\")",
|
|
18
|
+
"Bash(python3 -c \"\nimport yaml\n\nwith open\\(''codecov.yml'', ''r''\\) as f:\n config = yaml.safe_load\\(f\\)\n\nprint\\(''✅ Codecov Configuration Validation\\\\n''\\)\nprint\\(f''Project Coverage Target: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"project\"\"][\"\"default\"\"][\"\"target\"\"]}''\\)\nprint\\(f''Project Threshold: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"project\"\"][\"\"default\"\"][\"\"threshold\"\"]}''\\)\nprint\\(f''Patch Coverage Target: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"patch\"\"][\"\"default\"\"][\"\"target\"\"]}''\\)\nprint\\(f''Patch Threshold: {config[\"\"coverage\"\"][\"\"status\"\"][\"\"patch\"\"][\"\"default\"\"][\"\"threshold\"\"]}''\\)\nprint\\(f''\\\\nFlags Configured: {list\\(config[\"\"flags\"\"].keys\\(\\)\\)}''\\)\nprint\\(f''Components: {len\\(config[\"\"component_management\"\"][\"\"individual_components\"\"]\\)} defined''\\)\nprint\\(f''GitHub Annotations: {config[\"\"github_checks\"\"][\"\"annotations\"\"]}''\\)\nprint\\(f''\\\\n✅ All fields are valid!''\\)\n\")",
|
|
19
|
+
"Bash(curl --data-binary @codecov.yml https://codecov.io/validate)",
|
|
20
|
+
"WebFetch(domain:docs.codecov.com)",
|
|
21
|
+
"WebSearch",
|
|
22
|
+
"Bash(python3 -c \"import yaml; yaml.safe_load\\(open\\(''''codecov.yml''''\\)\\)\")",
|
|
23
|
+
"Bash(ls -1 codecov.yml docs/CODECOV*.md)",
|
|
24
|
+
"Bash(curl -s --data-binary @codecov.yml https://codecov.io/validate)",
|
|
25
|
+
"Bash(uv run ruff check /Users/JianGuo/Documents/CursorProject/lixinger-python/AGENTS.md)",
|
|
26
|
+
"Bash(ls -la /Users/JianGuo/Documents/CursorProject/lixinger-python/*.md)",
|
|
27
|
+
"Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger/api -L 3)",
|
|
28
|
+
"Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger/models -L 3)",
|
|
29
|
+
"Bash(uv run pytest tests/ -m \"not integration\" -v)",
|
|
30
|
+
"Bash(uv run ruff check lixinger/)",
|
|
31
|
+
"Bash(uv run mypy lixinger/)",
|
|
32
|
+
"Bash(uv run pytest tests/ -m \"not integration\" --tb=short)",
|
|
33
|
+
"Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger -L 4 -I __pycache__)",
|
|
34
|
+
"Bash(uv run ruff check .)",
|
|
35
|
+
"Bash(find /Users/JianGuo/Documents/CursorProject/lixinger-python/examples -name *.py)",
|
|
36
|
+
"Bash(tree /Users/JianGuo/Documents/CursorProject/lixinger-python/lixinger -L 4 -I '__pycache__|*.pyc' --dirsfirst)",
|
|
37
|
+
"Bash(uv run pytest tests/ -m \"not integration\" --tb=short -q)",
|
|
38
|
+
"Bash(uv run pytest tests/ -m \"not integration\" -q)",
|
|
39
|
+
"Bash(uv add:*)",
|
|
40
|
+
"Bash(uv run python:*)",
|
|
41
|
+
"Bash(uv run mkdocs build:*)",
|
|
42
|
+
"Bash(git check-ignore:*)",
|
|
43
|
+
"Bash(uv run pytest:*)",
|
|
44
|
+
"Bash(uv run ruff check:*)",
|
|
45
|
+
"Bash(uv run ruff format:*)",
|
|
46
|
+
"Bash(lsof:*)",
|
|
47
|
+
"Bash(xargs kill:*)",
|
|
48
|
+
"Bash(chmod +x scripts/publish.sh)",
|
|
49
|
+
"Bash(chmod +x scripts/check_before_publish.py)",
|
|
50
|
+
"Bash(uv run twine check dist/*)",
|
|
51
|
+
"Bash(unzip -l dist/lixinger_python-0.1.0-py3-none-any.whl | head -30)",
|
|
52
|
+
"Bash(find lixinger -type f -name \"*.py\" -exec grep -l \"^import pandera as pa\" {} \\\\;)",
|
|
53
|
+
"Bash(find lixinger -type f -name \"*.py\" -exec sed -i '' 's/^import pandera as pa$/import pandera.pandas as pa/' {} \\\\;)",
|
|
54
|
+
"Bash(uv pip list 2>/dev/null | grep -E \"\\(pandas|llama\\)\" || echo \"No packages found\")",
|
|
55
|
+
"Bash(uv pip index versions llama-index 2>/dev/null | head -20 || echo \"Checking package info...\")",
|
|
56
|
+
"Bash(pip index versions llama-index 2>&1 | head -5 || uv pip show llama-index 2>&1 | head -10 || echo \"Checking...\")",
|
|
57
|
+
"WebFetch(domain:pypi.org)",
|
|
58
|
+
"Bash(curl -s \"https://pypi.org/pypi/llama-index/0.14.15/json\" | python3 -c \"import json,sys; data=json.load\\(sys.stdin\\); deps=[d for d in data['info'].get\\('requires_dist',[]\\) if 'pandas' in d.lower\\(\\)]; print\\('\\\\n'.join\\(deps\\) if deps else 'No pandas dependency'\\)\")",
|
|
59
|
+
"Bash(curl -s \"https://pypi.org/pypi/llama-index-readers-file/0.5.6/json\" | python3 -c \"import json,sys; data=json.load\\(sys.stdin\\); deps=[d for d in data['info'].get\\('requires_dist',[]\\) if 'pandas' in d.lower\\(\\)]; print\\('\\\\n'.join\\(deps\\) if deps else 'No pandas dependency'\\)\")",
|
|
60
|
+
"Bash(uv sync)",
|
|
61
|
+
"Bash(wc -l /Users/JianGuo/Documents/CursorProject/lixinger-python/tests/*.py | tail -1)",
|
|
62
|
+
"Bash(uv pip list | grep -E \"pytest|asyncio\")",
|
|
63
|
+
"Bash(uv run mypy lixinger/utils/)",
|
|
64
|
+
"Bash(uv run mypy lixinger/api/base.py)",
|
|
65
|
+
"Bash(uv run mypy lixinger/api/cn/)",
|
|
66
|
+
"Bash(uv run mypy lixinger/client.py)",
|
|
67
|
+
"Bash(python3 -c \"from lixinger import AsyncLixingerClient; print\\('✅ AsyncLixingerClient imported'\\)\")",
|
|
68
|
+
"Bash(python3 -c \"from lixinger import LixingerClient\" 2>&1 || echo \"✅ LixingerClient correctly removed\")",
|
|
69
|
+
"Bash(grep -l \"LixingerClient\\\\|with client\\\\|client\\\\.company\\\\.get\" --include=\"*.md\" -r . 2>/dev/null | grep -v \".venv\" | grep -v \"site/\" | grep -v \".git/\" | head -20)",
|
|
70
|
+
"Bash(grep -n \"from lixinger import\\\\|LixingerClient\\\\|with client\\\\|def main\" docs/getting-started/quickstart.md docs/index.md docs/examples/*.md 2>/dev/null | head -40)",
|
|
71
|
+
"Bash(cat > /tmp/async_docs_update_summary.md << 'EOF'\n# 异步文档更新总结\n\n## ✅ 已完成更新的文件\n\n### 核心文档\n1. **README.md** - 完全重写,展示异步特性\n2. **SETUP.md** - 更新所有示例为 async/await\n3. **CHANGELOG.md** - 添加 v2.0.0 迁移指南\n4. **AGENTS.md** - 强调 async 要求,更新所有示例\n5. **ADD_NEW_API_QUICK.md** - 所有模板改为 async\n\n### 开发指南\n6. **docs/getting-started/quickstart.md** - 完全重写为异步版本\n - 添加醒目的异步警告\n - 所有示例使用 asyncio.run\\(main\\(\\)\\)\n - 新增并发请求示例\n - 新增 Jupyter 用法说明\n\n7. **docs/index.md** - 更新首页示例为异步\n\n### 示例文件\n8. **examples/company_example.py** - 异步版本\n9. **examples/company_profile_example.py** - 异步版本\n\n## ⚠️ 需要检查的文件\n\n以下文件可能包含同步代码示例,需要批量更新:\n\n### API 文档(自动生成部分)\n- `docs/api/company/*.md` \\(9个文件\\)\n- `docs/api/index/*.md` \\(7个文件\\)\n- `docs/api/fund/*.md` \\(7个文件\\)\n\n**处理建议**: 这些文件由 mkdocstrings 自动生成,只要源码的 docstrings 是正确的(已完成),重新生成文档即可。\n\n### 示例文档\n- `docs/examples/company.md`\n- `docs/examples/fund.md`\n- `docs/examples/index.md`\n\n### 其他配置文档\n- `docs/getting-started/configuration.md`\n- `docs/getting-started/installation.md`\n\n## 🔧 修复步骤\n\n### 方法1:重新生成 API 文档(推荐)\n```bash\nuv run python scripts/generate_docs.py\n```\n这会基于最新的 docstrings 重新生成所有 API 文档。\n\n### 方法2:手动修复剩余文件\n逐个检查并更新 docs/examples/*.md 等手写文档文件。\n\n## 📋 检查清单\n\n- [x] README.md\n- [x] SETUP.md\n- [x] CHANGELOG.md\n- [x] AGENTS.md\n- [x] ADD_NEW_API_QUICK.md\n- [x] docs/getting-started/quickstart.md\n- [x] docs/index.md\n- [x] examples/*.py\n- [ ] docs/examples/*.md \\(3个\\)\n- [ ] docs/getting-started/configuration.md\n- [ ] docs/getting-started/installation.md\n- [ ] docs/api/*/*.md \\(重新生成\\)\n\n## 🎯 关键改进点\n\n所有更新的文档都包含:\n1. ✅ 使用 `AsyncLixingerClient` 而不是 `LixingerClient`\n2. ✅ 使用 `async def main\\(\\)` 包装代码\n3. ✅ 使用 `async with` 上下文管理器\n4. ✅ 所有 API 调用前加 `await`\n5. ✅ 使用 `asyncio.run\\(main\\(\\)\\)` 运行\n6. ✅ 说明 Jupyter 可以直接使用 await\n7. ✅ 展示并发请求示例(where appropriate)\nEOF\ncat /tmp/async_docs_update_summary.md)",
|
|
72
|
+
"Bash(ls -lah scripts/*.py | grep -E \"\\(convert|fix\\)\")",
|
|
73
|
+
"Bash(ls -1 scripts/*.py)",
|
|
74
|
+
"Bash(grep -rn \"client\\\\.company\\\\.get_company\\\\|client\\\\.company\\\\.get_profile\" --include=\"*.md\" . 2>/dev/null | grep -v \".venv\\\\|site\\\\|.git\" | head -20)",
|
|
75
|
+
"Bash(cat > /tmp/fix_api_paths.sh << 'EOF'\n#!/bin/bash\n# Fix incorrect API paths in documentation\n\n# Find all markdown files \\(excluding .venv, site, .git\\)\nfind . -name \"*.md\" -type f ! -path \"./.venv/*\" ! -path \"./site/*\" ! -path \"./.git/*\" | while read file; do\n # Check if file contains the incorrect patterns\n if grep -q \"client\\\\.company\\\\.get_company\\\\|client\\\\.company\\\\.get_profile\" \"$file\"; then\n echo \"Fixing: $file\"\n \n # Fix get_company\n sed -i '' 's/client\\\\.company\\\\.get_company/client.company.company.get_company/g' \"$file\"\n \n # Fix get_profile\n sed -i '' 's/client\\\\.company\\\\.get_profile/client.company.profile.get_profile/g' \"$file\"\n fi\ndone\n\necho \"Done!\"\nEOF\n\nchmod +x /tmp/fix_api_paths.sh\n/tmp/fix_api_paths.sh)",
|
|
76
|
+
"Bash(grep -rn \"client\\\\.company\\\\.get_company\\\\|client\\\\.company\\\\.get_profile\" --include=\"*.md\" . 2>/dev/null | grep -v \".venv\\\\|site\\\\|.git\\\\|\\\\.company\\\\.company\\\\|\\\\.company\\\\.profile\" | head -5)",
|
|
77
|
+
"Bash(cat > /tmp/api_path_fix_summary.md << 'EOF'\n# API 路径修复总结\n\n## 🐛 发现的问题\n\n文档中使用了错误的 API 调用路径:\n\n### ❌ 错误示例\n```python\nclient.company.get_company\\(...\\) # 错误!\nclient.company.get_profile\\(...\\) # 错误!\n```\n\n### ✅ 正确示例\n```python\nclient.company.company.get_company\\(...\\) # 正确\nclient.company.profile.get_profile\\(...\\) # 正确\n```\n\n## 📐 客户端结构\n\n```python\nAsyncLixingerClient\n├── company \\(CompanyNamespace\\)\n│ ├── company \\(CompanyAPI\\)\n│ │ └── get_company\\(\\)\n│ ├── profile \\(CompanyProfileAPI\\)\n│ │ └── get_profile\\(\\)\n│ ├── fundamental \\(FundamentalAPI\\)\n│ │ ├── get_non_financial\\(\\)\n│ │ ├── get_bank\\(\\)\n│ │ └── ...\n│ ├── candlestick \\(CandlestickAPI\\)\n│ ├── equity_change \\(EquityChangeAPI\\)\n│ ├── fs\n│ │ └── non_financial \\(NonFinancialStatementAPI\\)\n│ ├── indices \\(IndicesAPI\\)\n│ ├── dividend \\(DividendAPI\\)\n│ └── announcement \\(AnnouncementAPI\\)\n│\n├── cn_index \\(IndexAPI\\)\n├── cn_index_constituents \\(ConstituentsAPI\\)\n└── ...\n```\n\n## ✅ 修复的文件(7个)\n\n1. **README.md** - 5处修复\n - 基础用法示例\n - 并发请求示例\n - Jupyter 用法\n - Import 示例\n\n2. **SETUP.md** - 4处修复\n - 使用示例\n - 配置覆盖\n - Jupyter 示例\n - 测试示例\n\n3. **CHANGELOG.md** - 2处修复\n - v0.x 示例\n - v2.0 迁移示例\n\n4. **docs/getting-started/quickstart.md** - 3处修复\n - 并发查询示例\n - 客户端配置示例\n - Jupyter 用法\n\n5. **docs/index.md** - 1处修复\n - Jupyter 使用示例\n\n6. **docs/examples/company.md** - 3处修复\n - 并发查询示例\n\n7. **PUBLISHING.md** - 1处修复\n - 示例代码\n\n## 🎯 正确的 API 调用模式\n\n### Company APIs\n```python\n# Company information\nawait client.company.company.get_company\\(stock_codes=[\"000001\"]\\)\n\n# Company profile \nawait client.company.profile.get_profile\\(stock_codes=[\"000001\"]\\)\n\n# Fundamental data\nawait client.company.fundamental.get_non_financial\\(\n stock_codes=[\"000001\"],\n metrics=[\"pe\", \"pb\"]\n\\)\n\n# Candlestick data\nawait client.company.candlestick.get_candlestick\\(...\\)\n\n# Equity change\nawait client.company.equity_change.get_equity_change\\(...\\)\n\n# Financial statements\nawait client.company.fs.non_financial.get_non_financial_statements\\(...\\)\n\n# Indices membership\nawait client.company.indices.get_indices\\(...\\)\n\n# Dividend\nawait client.company.dividend.get_dividend\\(...\\)\n\n# Announcements\nawait client.company.announcement.get_announcement\\(...\\)\n```\n\n### Index APIs\n```python\n# 使用扁平访问(旧版兼容)\nawait client.cn_index.get_index\\(...\\)\nawait client.cn_index_constituents.get_constituents\\(...\\)\nawait client.cn_index_candlestick.get_candlestick\\(...\\)\n```\n\n### Fund APIs\n```python\n# 使用扁平访问(旧版兼容)\nawait client.cn_fund.get_fund\\(...\\)\nawait client.cn_fund_profile.get_profile\\(...\\)\nawait client.cn_fund_candlestick.get_candlestick\\(...\\)\n```\n\n## 📊 修复统计\n\n- **文件数量**: 7个文档文件\n- **修复点**: 19处 API 调用路径\n- **覆盖范围**: \n - ✅ README.md\n - ✅ SETUP.md \n - ✅ CHANGELOG.md\n - ✅ 快速入门文档\n - ✅ 首页文档\n - ✅ 示例文档\n - ✅ 发布文档\n\n## ✨ 验证\n\n所有修复后的代码现在都使用正确的 API 路径:\n- `client.company.company.get_company\\(\\)` ✅\n- `client.company.profile.get_profile\\(\\)` ✅\n- `client.company.fundamental.get_non_financial\\(\\)` ✅\n\n用户现在可以直接复制文档中的代码并成功运行!\nEOF\ncat /tmp/api_path_fix_summary.md)",
|
|
78
|
+
"Bash(grep -rn \"client\\\\.[a-z_]*\\\\.\\\\\\(get_\\\\|fetch_\\\\\\)\" --include=\"*.md\" README.md SETUP.md docs/getting-started/quickstart.md 2>/dev/null | grep -v \"company\\\\.company\\\\|company\\\\.profile\\\\|company\\\\.fundamental\\\\|company\\\\.candlestick\\\\|company\\\\.equity_change\\\\|company\\\\.fs\\\\|company\\\\.indices\\\\|company\\\\.dividend\\\\|company\\\\.announcement\\\\|cn_index\\\\|cn_fund\" | head -10)"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# 📚 添加新 API - 快速参考
|
|
2
|
+
|
|
3
|
+
## ⚠️ 重要:SDK 采用完全异步架构
|
|
4
|
+
|
|
5
|
+
所有 API 方法必须使用 `async def` 并用 `await` 调用。
|
|
6
|
+
|
|
7
|
+
## 🎯 三步添加新 API
|
|
8
|
+
|
|
9
|
+
### 1️⃣ 写代码 + Docstrings
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
# lixinger/api/cn/company/your_api.py
|
|
13
|
+
|
|
14
|
+
class YourAPI(BaseAPI):
|
|
15
|
+
"""Your API description."""
|
|
16
|
+
|
|
17
|
+
async def get_data(
|
|
18
|
+
self,
|
|
19
|
+
stock_code: str,
|
|
20
|
+
param: str | None = None,
|
|
21
|
+
) -> pd.DataFrame:
|
|
22
|
+
"""一行描述.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
stock_code: 股票代码
|
|
26
|
+
param: 参数说明
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
返回值说明
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
>>> import asyncio
|
|
33
|
+
>>> async def main():
|
|
34
|
+
... client = AsyncLixingerClient()
|
|
35
|
+
... df = await client.your_api.get_data("000001")
|
|
36
|
+
>>> asyncio.run(main())
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
# 使用 await 调用 _request
|
|
40
|
+
data = await self._request("POST", "/endpoint", json=payload)
|
|
41
|
+
return get_response_df(data, Model)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**关键**:
|
|
45
|
+
- ✅ 使用 `async def` 定义所有方法
|
|
46
|
+
- ✅ 使用 `await self._request(...)`
|
|
47
|
+
- ✅ 使用 **Google-style docstrings** + 完整**类型注解**
|
|
48
|
+
- ✅ Example 中展示 async/await 用法
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### 2️⃣ 生成文档
|
|
53
|
+
|
|
54
|
+
**方式 A: 自动生成(推荐)**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# 1. 编辑 scripts/generate_docs.py,添加配置
|
|
58
|
+
vim scripts/generate_docs.py
|
|
59
|
+
|
|
60
|
+
# 2. 运行生成脚本
|
|
61
|
+
uv run python scripts/generate_docs.py
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**方式 B: 手动创建**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# 创建文档文件
|
|
68
|
+
cat > docs/api/company/your_api.md << 'EOF'
|
|
69
|
+
# API 名称
|
|
70
|
+
|
|
71
|
+
## 类参考
|
|
72
|
+
|
|
73
|
+
::: lixinger.api.cn.company.your_api.YourAPI
|
|
74
|
+
options:
|
|
75
|
+
show_root_heading: true
|
|
76
|
+
show_source: true
|
|
77
|
+
EOF
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### 3️⃣ 预览 & 部署
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 本地预览
|
|
86
|
+
uv run mkdocs serve
|
|
87
|
+
|
|
88
|
+
# 部署到线上
|
|
89
|
+
uv run mkdocs gh-deploy
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## ✅ 检查清单
|
|
95
|
+
|
|
96
|
+
添加新 API 时确保:
|
|
97
|
+
|
|
98
|
+
- [ ] **所有方法使用 `async def`**
|
|
99
|
+
- [ ] **所有 `_request` 调用使用 `await`**
|
|
100
|
+
- [ ] 代码使用 Google-style docstrings
|
|
101
|
+
- [ ] 包含 `Args`, `Returns`, `Example` 部分
|
|
102
|
+
- [ ] **Example 展示 async/await 用法**
|
|
103
|
+
- [ ] 完整的类型注解(`str`, `int | None`, `pd.DataFrame` 等)
|
|
104
|
+
- [ ] 运行了文档生成脚本或手动创建了文档页面
|
|
105
|
+
- [ ] 本地预览文档正常:`uv run mkdocs serve`
|
|
106
|
+
- [ ] **测试函数使用 `async def` 和 `@pytest.mark.asyncio`**
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 🔑 关键点
|
|
111
|
+
|
|
112
|
+
### ✅ DO (推荐做法)
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
async def get_data(self, code: str) -> pd.DataFrame:
|
|
116
|
+
"""获取数据.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
code: 股票代码
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
数据 DataFrame
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
>>> import asyncio
|
|
126
|
+
>>> async def main():
|
|
127
|
+
... df = await get_data("000001")
|
|
128
|
+
>>> asyncio.run(main())
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
data = await self._request("POST", "/endpoint", json={"code": code})
|
|
132
|
+
return get_response_df(data, Model)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### ❌ DON'T (避免)
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
def get_data(self, code): # ❌ 缺少 async、类型注解
|
|
139
|
+
"""Get data.""" # ❌ Docstring 太简单、缺少 Example
|
|
140
|
+
# ❌ 缺少 await
|
|
141
|
+
data = self._request("POST", "/endpoint", json={"code": code})
|
|
142
|
+
return data
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 📖 Docstring 最小模板
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
async def method_name(param: type) -> return_type:
|
|
151
|
+
"""一行简短描述.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
param: 参数说明
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
返回值说明
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
>>> import asyncio
|
|
161
|
+
>>> async def main():
|
|
162
|
+
... result = await method_name("value")
|
|
163
|
+
>>> asyncio.run(main())
|
|
164
|
+
|
|
165
|
+
"""
|
|
166
|
+
data = await self._request("POST", "/endpoint", json={"param": param})
|
|
167
|
+
return data
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 🧪 测试模板
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
import pytest
|
|
176
|
+
|
|
177
|
+
@pytest.mark.asyncio
|
|
178
|
+
async def test_your_api(monkeypatch):
|
|
179
|
+
"""测试你的 API."""
|
|
180
|
+
|
|
181
|
+
# Mock async _request
|
|
182
|
+
async def mock_request(*args, **kwargs):
|
|
183
|
+
return [{"field": "value"}]
|
|
184
|
+
|
|
185
|
+
monkeypatch.setattr(YourAPI, "_request", mock_request)
|
|
186
|
+
monkeypatch.setenv("LIXINGER_API_KEY", "test_key")
|
|
187
|
+
|
|
188
|
+
# 使用 await 调用
|
|
189
|
+
client = AsyncLixingerClient()
|
|
190
|
+
df = await client.your_api.get_data("000001")
|
|
191
|
+
|
|
192
|
+
assert not df.empty
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🔗 完整文档
|
|
198
|
+
|
|
199
|
+
详细指南请查看:[ADD_NEW_API.md](ADD_NEW_API.md)
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
**重点**:只要写好 docstrings,文档会自动生成!✨
|
|
@@ -7,6 +7,14 @@ Instructions for LLM agents implementing the Lixinger API Python SDK.
|
|
|
7
7
|
- **API Docs**: <https://www.lixinger.com/open/api/doc>
|
|
8
8
|
- **Code Examples**: See `lixinger/api/` and `lixinger/models/` for patterns
|
|
9
9
|
|
|
10
|
+
## Architecture
|
|
11
|
+
|
|
12
|
+
**⚠️ IMPORTANT: This SDK is fully async/await based.**
|
|
13
|
+
|
|
14
|
+
- All API methods are `async def` and must be awaited
|
|
15
|
+
- Uses `httpx.AsyncClient` for non-blocking I/O
|
|
16
|
+
- Perfect for AI agents, Jupyter notebooks, and async web frameworks
|
|
17
|
+
|
|
10
18
|
## Critical: API Response Format
|
|
11
19
|
|
|
12
20
|
All responses follow this format:
|
|
@@ -31,10 +39,10 @@ All responses follow this format:
|
|
|
31
39
|
|
|
32
40
|
```python
|
|
33
41
|
# ✅ Correct
|
|
34
|
-
client =
|
|
42
|
+
client = AsyncLixingerClient()
|
|
35
43
|
|
|
36
44
|
# ❌ Wrong - will raise TypeError
|
|
37
|
-
client =
|
|
45
|
+
client = AsyncLixingerClient(api_key="key")
|
|
38
46
|
```
|
|
39
47
|
|
|
40
48
|
**Optional env vars:** `LIXINGER_BASE_URL`, `LIXINGER_TIMEOUT`, `LIXINGER_MAX_RETRIES`, `LIXINGER_PROXY`, `LIXINGER_MAX_REQUESTS_PER_MINUTE`
|
|
@@ -127,6 +135,7 @@ curl -X POST https://open.lixinger.com/api/cn/company \
|
|
|
127
135
|
|
|
128
136
|
2. **API class** (`lixinger/api/cn/{endpoint}/`)
|
|
129
137
|
- Inherit from `BaseAPI`
|
|
138
|
+
- **All methods MUST be async** (`async def`)
|
|
130
139
|
- **Method parameters MUST match API documentation exactly**
|
|
131
140
|
- Add docstring with:
|
|
132
141
|
- Endpoint URL
|
|
@@ -134,10 +143,11 @@ curl -X POST https://open.lixinger.com/api/cn/company \
|
|
|
134
143
|
- Parameter descriptions from docs
|
|
135
144
|
- Use proper type hints (match API docs)
|
|
136
145
|
- **Validate parameters against documented constraints**
|
|
146
|
+
- All API calls use `await self._request(...)`
|
|
137
147
|
- Add `__init__.py` with exports
|
|
138
148
|
|
|
139
149
|
3. **Integrate**
|
|
140
|
-
- Add to `
|
|
150
|
+
- Add to `AsyncLixingerClient` in `client.py`
|
|
141
151
|
- Export in `lixinger/__init__.py`
|
|
142
152
|
|
|
143
153
|
**Compliance Checklist:**
|
|
@@ -152,13 +162,18 @@ curl -X POST https://open.lixinger.com/api/cn/company \
|
|
|
152
162
|
### 3. Add Tests
|
|
153
163
|
|
|
154
164
|
**Unit tests** (`tests/test_*.py`):
|
|
165
|
+
- **All test functions MUST be async** (`async def test_...()`)
|
|
166
|
+
- Add `@pytest.mark.asyncio` decorator to all async tests
|
|
167
|
+
- Use `await` for all API calls
|
|
155
168
|
- Mock with full `{code, message, data}` format
|
|
169
|
+
- Mock `_request` as async: `async def mock_request(...)`
|
|
156
170
|
- Test success, errors, edge cases
|
|
157
171
|
- Use `monkeypatch.setenv("LIXINGER_API_KEY", "test_key")`
|
|
158
172
|
|
|
159
173
|
**Integration tests** (`tests/test_integration.py`):
|
|
160
174
|
- Mark with `@pytest.mark.integration`
|
|
161
|
-
-
|
|
175
|
+
- Use `async with AsyncLixingerClient() as client:`
|
|
176
|
+
- Test with real API using `await`
|
|
162
177
|
|
|
163
178
|
**Coverage:** >80% on new code
|
|
164
179
|
|
|
@@ -175,7 +190,7 @@ uv run pytest --cov=lixinger --cov-report=term-missing -m "not integration"
|
|
|
175
190
|
All public methods **MUST** have complete Google-style docstrings:
|
|
176
191
|
|
|
177
192
|
```python
|
|
178
|
-
def get_data(
|
|
193
|
+
async def get_data(
|
|
179
194
|
self,
|
|
180
195
|
stock_code: str,
|
|
181
196
|
start_date: str | None = None,
|
|
@@ -205,19 +220,23 @@ def get_data(
|
|
|
205
220
|
Example:
|
|
206
221
|
获取平安银行的市盈率和市净率:
|
|
207
222
|
|
|
208
|
-
>>>
|
|
209
|
-
>>>
|
|
210
|
-
...
|
|
211
|
-
...
|
|
212
|
-
...
|
|
213
|
-
...
|
|
214
|
-
|
|
223
|
+
>>> import asyncio
|
|
224
|
+
>>> async def main():
|
|
225
|
+
... client = AsyncLixingerClient()
|
|
226
|
+
... df = await client.company.data.get_data(
|
|
227
|
+
... stock_code="000001",
|
|
228
|
+
... start_date="2023-01-01",
|
|
229
|
+
... metrics=["pe", "pb"]
|
|
230
|
+
... )
|
|
231
|
+
... print(df.head())
|
|
232
|
+
>>> asyncio.run(main())
|
|
215
233
|
|
|
216
234
|
Note:
|
|
217
235
|
API 限制每分钟最多 1000 次请求。
|
|
218
236
|
|
|
219
237
|
"""
|
|
220
|
-
|
|
238
|
+
data = await self._request("POST", "/cn/company/data", json=payload)
|
|
239
|
+
return get_response_df(data, DataModel)
|
|
221
240
|
```
|
|
222
241
|
|
|
223
242
|
**Required Sections**:
|
|
@@ -251,13 +270,17 @@ API_DOCS = {
|
|
|
251
270
|
"endpoint": "POST /cn/company/your-api",
|
|
252
271
|
"module": "lixinger.api.cn.company.your_api.YourAPI",
|
|
253
272
|
"example": """```python
|
|
254
|
-
|
|
273
|
+
import asyncio
|
|
274
|
+
from lixinger import AsyncLixingerClient
|
|
275
|
+
|
|
276
|
+
async def main():
|
|
277
|
+
client = AsyncLixingerClient()
|
|
255
278
|
|
|
256
|
-
|
|
279
|
+
# 使用示例
|
|
280
|
+
df = await client.company.your_api.get_data("000001")
|
|
281
|
+
print(df)
|
|
257
282
|
|
|
258
|
-
|
|
259
|
-
df = client.company.your_api.get_data("000001")
|
|
260
|
-
print(df)
|
|
283
|
+
asyncio.run(main())
|
|
261
284
|
```""",
|
|
262
285
|
"doc_url": "https://www.lixinger.com/open/api/doc?api-key=cn/company/your-api",
|
|
263
286
|
},
|
|
@@ -10,7 +10,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
### Planned
|
|
11
11
|
- Add support for US stock market APIs
|
|
12
12
|
- Add caching mechanism for API responses
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
## [0.2.0] - 2026-02-26
|
|
15
|
+
|
|
16
|
+
### ⚠️ BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
This release completely migrates the SDK to async/await. All existing code must be updated.
|
|
19
|
+
|
|
20
|
+
**Migration Guide:**
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
# v0.1.x (old - blocking)
|
|
24
|
+
from lixinger import LixingerClient
|
|
25
|
+
|
|
26
|
+
with LixingerClient() as client:
|
|
27
|
+
df = client.company.company.get_company(["000001"])
|
|
28
|
+
|
|
29
|
+
# v0.2.0 (new - async)
|
|
30
|
+
import asyncio
|
|
31
|
+
from lixinger import AsyncLixingerClient
|
|
32
|
+
|
|
33
|
+
async def main():
|
|
34
|
+
async with AsyncLixingerClient() as client:
|
|
35
|
+
df = await client.company.company.get_company(["000001"])
|
|
36
|
+
|
|
37
|
+
asyncio.run(main())
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- **BREAKING**: `LixingerClient` renamed to `AsyncLixingerClient`
|
|
42
|
+
- **BREAKING**: All API methods now return coroutines and must be awaited
|
|
43
|
+
- **BREAKING**: Context manager now requires `async with` instead of `with`
|
|
44
|
+
- **BREAKING**: `client.close()` is now async and must be awaited
|
|
45
|
+
- **BREAKING**: All functional APIs (e.g., `get_company()`) are now async
|
|
46
|
+
- Replaced `httpx.Client` with `httpx.AsyncClient`
|
|
47
|
+
- Replaced `time.sleep` with `asyncio.sleep` in rate limiter and retry logic
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
- ✨ Full async/await support throughout the SDK
|
|
51
|
+
- ✨ Concurrent request capability - query multiple stocks in parallel
|
|
52
|
+
- ✨ Native Jupyter Notebook support (top-level await)
|
|
53
|
+
- ✨ Perfect integration with AI Agent frameworks (LangChain, LlamaIndex)
|
|
54
|
+
- ✨ Compatible with async web frameworks (FastAPI, Sanic, Quart)
|
|
55
|
+
|
|
56
|
+
### Benefits
|
|
57
|
+
- Non-blocking I/O for better performance in async contexts
|
|
58
|
+
- Ability to query multiple stocks concurrently using `asyncio.gather()`
|
|
59
|
+
- Seamless integration with modern Python async ecosystem
|
|
60
|
+
- Ideal for AI agents that need to fetch financial data without blocking
|
|
14
61
|
|
|
15
62
|
## [0.1.0] - 2026-02-24
|
|
16
63
|
|