lixinger-python 0.1.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. lixinger/__init__.py +79 -0
  2. lixinger/api/__init__.py +0 -0
  3. lixinger/api/base.py +94 -0
  4. lixinger/api/cn/__init__.py +0 -0
  5. lixinger/api/cn/company/__init__.py +34 -0
  6. lixinger/api/cn/company/announcement.py +74 -0
  7. lixinger/api/cn/company/candlestick.py +76 -0
  8. lixinger/api/cn/company/company.py +88 -0
  9. lixinger/api/cn/company/dividend.py +67 -0
  10. lixinger/api/cn/company/equity_change.py +84 -0
  11. lixinger/api/cn/company/fs/__init__.py +5 -0
  12. lixinger/api/cn/company/fs/non_financial.py +77 -0
  13. lixinger/api/cn/company/fundamental.py +228 -0
  14. lixinger/api/cn/company/indices.py +52 -0
  15. lixinger/api/cn/company/namespace.py +66 -0
  16. lixinger/api/cn/company/profile.py +66 -0
  17. lixinger/api/cn/fund/__init__.py +35 -0
  18. lixinger/api/cn/fund/announcement.py +89 -0
  19. lixinger/api/cn/fund/asset_combination.py +82 -0
  20. lixinger/api/cn/fund/asset_industry_combination.py +93 -0
  21. lixinger/api/cn/fund/candlestick.py +84 -0
  22. lixinger/api/cn/fund/fund.py +70 -0
  23. lixinger/api/cn/fund/profile.py +60 -0
  24. lixinger/api/cn/fund/shareholdings.py +86 -0
  25. lixinger/api/cn/index/__init__.py +32 -0
  26. lixinger/api/cn/index/candlestick.py +86 -0
  27. lixinger/api/cn/index/constituent_weightings.py +71 -0
  28. lixinger/api/cn/index/constituents.py +71 -0
  29. lixinger/api/cn/index/drawdown.py +87 -0
  30. lixinger/api/cn/index/fundamental.py +87 -0
  31. lixinger/api/cn/index/index.py +69 -0
  32. lixinger/api/cn/index/tracking_fund.py +71 -0
  33. lixinger/client.py +184 -0
  34. lixinger/config.py +72 -0
  35. lixinger/exceptions.py +22 -0
  36. lixinger/models/__init__.py +5 -0
  37. lixinger/models/cn/__init__.py +1 -0
  38. lixinger/models/cn/company/__init__.py +20 -0
  39. lixinger/models/cn/company/announcement.py +20 -0
  40. lixinger/models/cn/company/candlestick.py +24 -0
  41. lixinger/models/cn/company/company.py +33 -0
  42. lixinger/models/cn/company/dividend.py +25 -0
  43. lixinger/models/cn/company/equity_change.py +26 -0
  44. lixinger/models/cn/company/fs/__init__.py +5 -0
  45. lixinger/models/cn/company/fs/non_financial.py +20 -0
  46. lixinger/models/cn/company/fundamental.py +16 -0
  47. lixinger/models/cn/company/indices.py +17 -0
  48. lixinger/models/cn/fund/__init__.py +19 -0
  49. lixinger/models/cn/fund/announcement.py +21 -0
  50. lixinger/models/cn/fund/asset_combination.py +40 -0
  51. lixinger/models/cn/fund/asset_industry_combination.py +24 -0
  52. lixinger/models/cn/fund/candlestick.py +23 -0
  53. lixinger/models/cn/fund/fund.py +23 -0
  54. lixinger/models/cn/fund/profile.py +28 -0
  55. lixinger/models/cn/fund/shareholdings.py +21 -0
  56. lixinger/models/cn/index/__init__.py +19 -0
  57. lixinger/models/cn/index/candlestick.py +22 -0
  58. lixinger/models/cn/index/constituent_weightings.py +16 -0
  59. lixinger/models/cn/index/constituents.py +17 -0
  60. lixinger/models/cn/index/drawdown.py +16 -0
  61. lixinger/models/cn/index/fundamental.py +16 -0
  62. lixinger/models/cn/index/index.py +24 -0
  63. lixinger/models/cn/index/tracking_fund.py +19 -0
  64. lixinger/py.typed +0 -0
  65. lixinger/utils/__init__.py +0 -0
  66. lixinger/utils/api.py +15 -0
  67. lixinger/utils/dataframe.py +57 -0
  68. lixinger/utils/dict.py +22 -0
  69. lixinger/utils/rate_limiter.py +35 -0
  70. lixinger/utils/retry.py +31 -0
  71. lixinger_python-0.1.2.dist-info/METADATA +171 -0
  72. lixinger_python-0.1.2.dist-info/RECORD +74 -0
  73. lixinger_python-0.1.2.dist-info/WHEEL +4 -0
  74. lixinger_python-0.1.2.dist-info/licenses/LICENSE +21 -0
lixinger/__init__.py ADDED
@@ -0,0 +1,79 @@
1
+ from lixinger.api.cn.company import get_company, get_equity_change, get_profile
2
+ from lixinger.api.cn.company.candlestick import get_candlestick
3
+ from lixinger.api.cn.company.fs.non_financial import get_non_financial_statements
4
+ from lixinger.api.cn.company.fundamental import (
5
+ get_bank,
6
+ get_fundamental,
7
+ get_insurance,
8
+ get_non_financial,
9
+ get_other_financial,
10
+ get_security,
11
+ )
12
+ from lixinger.api.cn.company.indices import get_indices
13
+ from lixinger.api.cn.fund import (
14
+ get_fund,
15
+ get_fund_announcement,
16
+ get_fund_asset_combination,
17
+ get_fund_asset_industry_combination,
18
+ get_fund_candlestick,
19
+ get_fund_profile,
20
+ get_fund_shareholdings,
21
+ )
22
+ from lixinger.api.cn.index import (
23
+ get_candlestick as get_index_candlestick,
24
+ )
25
+ from lixinger.api.cn.index import (
26
+ get_constituent_weightings,
27
+ get_constituents,
28
+ get_index,
29
+ get_index_fundamental,
30
+ get_tracking_fund,
31
+ )
32
+ from lixinger.api.cn.index import (
33
+ get_drawdown as get_index_drawdown,
34
+ )
35
+ from lixinger.client import LixingerClient
36
+ from lixinger.config import set_token
37
+ from lixinger.exceptions import (
38
+ APIError,
39
+ AuthenticationError,
40
+ LixingerError,
41
+ RateLimitError,
42
+ ValidationError,
43
+ )
44
+
45
+ __all__ = [
46
+ "LixingerClient",
47
+ "LixingerError",
48
+ "APIError",
49
+ "RateLimitError",
50
+ "AuthenticationError",
51
+ "ValidationError",
52
+ "set_token",
53
+ "get_company",
54
+ "get_fund",
55
+ "get_fund_announcement",
56
+ "get_fund_profile",
57
+ "get_fund_candlestick",
58
+ "get_fund_shareholdings",
59
+ "get_fund_asset_combination",
60
+ "get_fund_asset_industry_combination",
61
+ "get_profile",
62
+ "get_equity_change",
63
+ "get_indices",
64
+ "get_index",
65
+ "get_constituents",
66
+ "get_constituent_weightings",
67
+ "get_index_fundamental",
68
+ "get_tracking_fund",
69
+ "get_candlestick",
70
+ "get_index_candlestick",
71
+ "get_index_drawdown",
72
+ "get_non_financial_statements",
73
+ "get_fundamental",
74
+ "get_non_financial",
75
+ "get_bank",
76
+ "get_insurance",
77
+ "get_security",
78
+ "get_other_financial",
79
+ ]
File without changes
lixinger/api/base.py ADDED
@@ -0,0 +1,94 @@
1
+ from typing import Any
2
+
3
+ import httpx
4
+
5
+ from lixinger.config import Config, settings
6
+ from lixinger.exceptions import APIError, AuthenticationError, RateLimitError
7
+ from lixinger.utils.rate_limiter import RateLimiter
8
+ from lixinger.utils.retry import retry_on_failure
9
+
10
+ # Global rate limiter for functional API
11
+ _global_rate_limiter = RateLimiter(max_requests=1000)
12
+
13
+ # Lazy initialization of global http client to avoid issues with proxy/config changes
14
+ _global_http_client: httpx.Client | None = None
15
+
16
+
17
+ def get_global_client() -> httpx.Client:
18
+ """Get or create the global HTTP client."""
19
+ global _global_http_client # noqa: PLW0603
20
+ if _global_http_client is None:
21
+ _global_http_client = httpx.Client(
22
+ headers={
23
+ "Content-Type": "application/json",
24
+ "Accept-Encoding": "gzip, deflate, br",
25
+ },
26
+ timeout=30.0,
27
+ follow_redirects=True,
28
+ )
29
+ return _global_http_client
30
+
31
+
32
+ class BaseAPI:
33
+ """Base class for API endpoint groups."""
34
+
35
+ def __init__(
36
+ self,
37
+ client: httpx.Client | None = None,
38
+ config: Config | None = None,
39
+ rate_limiter: RateLimiter | None = None,
40
+ ):
41
+ self._client = client
42
+ self._config = config or settings
43
+ self._rate_limiter = rate_limiter or _global_rate_limiter
44
+
45
+ @retry_on_failure(max_retries=3, backoff=1.0)
46
+ def _request(
47
+ self,
48
+ method: str,
49
+ endpoint: str,
50
+ *,
51
+ params: dict[str, Any] | None = None,
52
+ json: dict[str, Any] | None = None,
53
+ ) -> Any:
54
+ """Make an API request with retry logic and rate limiting."""
55
+ self._rate_limiter.acquire()
56
+
57
+ # Use the provided client or the global one
58
+ client = self._client or get_global_client()
59
+ api_key = self._config.api_key
60
+
61
+ # Ensure token is in the payload
62
+ if method.upper() == "POST" and json is not None and "token" not in json:
63
+ json["token"] = api_key
64
+ elif params is not None and "token" not in params:
65
+ params["token"] = api_key
66
+
67
+ response = client.request(
68
+ method,
69
+ f"{self._config.base_url.rstrip('/')}/{endpoint.lstrip('/')}",
70
+ params=params,
71
+ json=json,
72
+ )
73
+
74
+ if response.status_code == 429:
75
+ raise RateLimitError("Rate limit exceeded", status_code=429)
76
+ if response.status_code == 401:
77
+ raise AuthenticationError("Authentication failed", status_code=401)
78
+ if response.status_code != 200:
79
+ raise APIError(
80
+ f"API request failed with status {response.status_code}: {response.text}",
81
+ status_code=response.status_code,
82
+ )
83
+
84
+ data = response.json()
85
+ if not isinstance(data, dict):
86
+ raise APIError(f"API returned unexpected response type: {type(data)}")
87
+
88
+ code = data.get("code")
89
+ message = data.get("message")
90
+
91
+ if code != 1 or message != "success":
92
+ raise APIError(f"API returned error code {code}: {message}")
93
+
94
+ return data.get("data")
File without changes
@@ -0,0 +1,34 @@
1
+ """Company-related APIs."""
2
+
3
+ from lixinger.api.cn.company.announcement import AnnouncementAPI, get_announcement
4
+ from lixinger.api.cn.company.candlestick import CandlestickAPI, get_candlestick
5
+ from lixinger.api.cn.company.company import CompanyAPI, get_company
6
+ from lixinger.api.cn.company.dividend import DividendAPI, get_dividend
7
+ from lixinger.api.cn.company.equity_change import EquityChangeAPI, get_equity_change
8
+ from lixinger.api.cn.company.fundamental import FundamentalAPI
9
+ from lixinger.api.cn.company.indices import IndicesAPI, get_indices
10
+ from lixinger.api.cn.company.profile import CompanyProfileAPI, get_profile
11
+
12
+ __all__ = [
13
+ # Company info
14
+ "CompanyAPI",
15
+ "get_company",
16
+ "CompanyProfileAPI",
17
+ "get_profile",
18
+ # Equity
19
+ "EquityChangeAPI",
20
+ "get_equity_change",
21
+ # Fundamental
22
+ "FundamentalAPI",
23
+ # Market data
24
+ "CandlestickAPI",
25
+ "get_candlestick",
26
+ # Corporate actions
27
+ "AnnouncementAPI",
28
+ "get_announcement",
29
+ "DividendAPI",
30
+ "get_dividend",
31
+ # Relations
32
+ "IndicesAPI",
33
+ "get_indices",
34
+ ]
@@ -0,0 +1,74 @@
1
+ from typing import Any
2
+
3
+ import pandas as pd
4
+
5
+ from lixinger.api.base import BaseAPI
6
+ from lixinger.models.cn.company import Announcement
7
+ from lixinger.utils.api import api
8
+ from lixinger.utils.dataframe import get_response_df
9
+
10
+
11
+ class AnnouncementAPI(BaseAPI):
12
+ """Company announcement APIs."""
13
+
14
+ def get_announcement(
15
+ self,
16
+ stock_code: str,
17
+ start_date: str | None = None,
18
+ end_date: str | None = None,
19
+ limit: int | None = None,
20
+ ) -> pd.DataFrame:
21
+ """获取公告数据.
22
+
23
+ API Endpoint: /cn/company/announcement
24
+ API Method: POST
25
+
26
+ Args:
27
+ stock_code: 股票代码
28
+ start_date: 开始日期 (YYYY-MM-DD)
29
+ end_date: 结束日期 (YYYY-MM-DD)
30
+ limit: 限制返回数量
31
+
32
+ """
33
+ payload: dict[str, Any] = {
34
+ "stockCode": stock_code,
35
+ }
36
+ if start_date is not None:
37
+ payload["startDate"] = start_date
38
+ if end_date is not None:
39
+ payload["endDate"] = end_date
40
+ if limit is not None:
41
+ payload["limit"] = limit
42
+
43
+ data = self._request("POST", "/cn/company/announcement", json=payload)
44
+ for item in data:
45
+ item["stockCode"] = stock_code
46
+ return get_response_df(data, Announcement)
47
+
48
+
49
+ # Functional API instance
50
+ _api_instance = AnnouncementAPI()
51
+
52
+
53
+ @api
54
+ def get_announcement(
55
+ stock_code: str,
56
+ start_date: str | None = None,
57
+ end_date: str | None = None,
58
+ limit: int | None = None,
59
+ ) -> pd.DataFrame:
60
+ """获取公告数据.
61
+
62
+ Args:
63
+ stock_code: 股票代码
64
+ start_date: 开始日期 (YYYY-MM-DD)
65
+ end_date: 结束日期 (YYYY-MM-DD)
66
+ limit: 限制返回数量
67
+
68
+ """
69
+ return _api_instance.get_announcement(
70
+ stock_code=stock_code,
71
+ start_date=start_date,
72
+ end_date=end_date,
73
+ limit=limit,
74
+ )
@@ -0,0 +1,76 @@
1
+ from typing import Any, Literal
2
+
3
+ import pandas as pd
4
+
5
+ from lixinger.api.base import BaseAPI
6
+ from lixinger.models.cn.company import Candlestick
7
+ from lixinger.utils.api import api
8
+ from lixinger.utils.dataframe import get_response_df
9
+
10
+
11
+ class CandlestickAPI(BaseAPI):
12
+ """Candlestick (K-line) data APIs."""
13
+
14
+ def get_candlestick(
15
+ self,
16
+ type: Literal["1d", "1w", "1m", "5m", "15m", "30m", "60m"],
17
+ stock_code: str,
18
+ start_date: str,
19
+ end_date: str,
20
+ adjust_type: Literal["none", "qxw", "hxw"] | None = None,
21
+ ) -> pd.DataFrame:
22
+ """获取K线数据.
23
+
24
+ API Endpoint: /cn/company/candlestick
25
+ API Method: POST
26
+
27
+ Args:
28
+ type: K线类型 (1d, 1w, 1m, 5m, 15m, 30m, 60m)
29
+ stock_code: 股票代码
30
+ start_date: 开始日期 (YYYY-MM-DD)
31
+ end_date: 结束日期 (YYYY-MM-DD)
32
+ adjust_type: 复权类型 (none, qxw, hxw)
33
+
34
+ """
35
+ payload: dict[str, Any] = {
36
+ "type": type,
37
+ "stockCode": stock_code,
38
+ "startDate": start_date,
39
+ "endDate": end_date,
40
+ }
41
+ if adjust_type is not None:
42
+ payload["adjustType"] = adjust_type
43
+
44
+ data = self._request("POST", "/cn/company/candlestick", json=payload)
45
+ return get_response_df(data, Candlestick)
46
+
47
+
48
+ # Functional API instance
49
+ _api_instance = CandlestickAPI()
50
+
51
+
52
+ @api
53
+ def get_candlestick(
54
+ type: Literal["1d", "1w", "1m", "5m", "15m", "30m", "60m"],
55
+ stock_code: str,
56
+ start_date: str,
57
+ end_date: str,
58
+ adjust_type: Literal["none", "qxw", "hxw"] | None = None,
59
+ ) -> pd.DataFrame:
60
+ """获取K线数据.
61
+
62
+ Args:
63
+ type: K线类型 (1d, 1w, 1m, 5m, 15m, 30m, 60m)
64
+ stock_code: 股票代码
65
+ start_date: 开始日期 (YYYY-MM-DD)
66
+ end_date: 结束日期 (YYYY-MM-DD)
67
+ adjust_type: 复权类型 (none, qxw, hxw)
68
+
69
+ """
70
+ return _api_instance.get_candlestick(
71
+ type=type,
72
+ stock_code=stock_code,
73
+ start_date=start_date,
74
+ end_date=end_date,
75
+ adjust_type=adjust_type,
76
+ )
@@ -0,0 +1,88 @@
1
+ """Company information API."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ import pandas as pd
6
+
7
+ from lixinger.api.base import BaseAPI
8
+ from lixinger.models.cn.company import Company
9
+ from lixinger.utils.api import api
10
+ from lixinger.utils.dataframe import get_response_df
11
+
12
+
13
+ class CompanyAPI(BaseAPI):
14
+ """Company information API."""
15
+
16
+ def get_company(
17
+ self,
18
+ fs_type: Literal["non_financial", "bank", "insurance", "security", "other_financial"] | None = None,
19
+ mutual_markets: Literal["ha"] | None = None,
20
+ stock_codes: list[str] | None = None,
21
+ include_delisted: bool | None = None,
22
+ ) -> pd.DataFrame:
23
+ """获取股票详细信息.
24
+
25
+ API Endpoint: /cn/company
26
+ API Method: POST
27
+ Documentation: https://www.lixinger.com/open/api/doc?api-key=cn/company
28
+
29
+ Args:
30
+ fs_type: 财务报表类型 (non_financial, bank, insurance, security, other_financial)
31
+ mutual_markets: 互联互通市场 (ha)
32
+ stock_codes: 股票代码列表
33
+ include_delisted: 是否包含已退市股票
34
+
35
+ Returns:
36
+ 包含股票详细信息的 DataFrame
37
+
38
+ Example:
39
+ >>> client = LixingerClient()
40
+ >>> df = client.company.company.get_company(
41
+ ... stock_codes=["000001", "600036"]
42
+ ... )
43
+ >>> print(df.head())
44
+
45
+ """
46
+ payload: dict[str, Any] = {}
47
+ if fs_type is not None:
48
+ payload["fsType"] = fs_type
49
+ if mutual_markets is not None:
50
+ payload["mutualMarkets"] = mutual_markets
51
+ if stock_codes is not None:
52
+ payload["stockCodes"] = stock_codes
53
+ if include_delisted is not None:
54
+ payload["includeDelisted"] = include_delisted
55
+
56
+ data = self._request("POST", "/cn/company", json=payload)
57
+ return get_response_df(data, Company)
58
+
59
+
60
+ # Functional API instance
61
+ _api_instance = CompanyAPI()
62
+
63
+
64
+ @api
65
+ def get_company(
66
+ fs_type: Literal["non_financial", "bank", "insurance", "security", "other_financial"] | None = None,
67
+ mutual_markets: Literal["ha"] | None = None,
68
+ stock_codes: list[str] | None = None,
69
+ include_delisted: bool | None = None,
70
+ ) -> pd.DataFrame:
71
+ """获取股票详细信息.
72
+
73
+ Args:
74
+ fs_type: 财务报表类型 (non_financial, bank, insurance, security, other_financial)
75
+ mutual_markets: 互联互通市场 (ha)
76
+ stock_codes: 股票代码列表
77
+ include_delisted: 是否包含已退市股票
78
+
79
+ Returns:
80
+ 包含股票详细信息的 DataFrame
81
+
82
+ """
83
+ return _api_instance.get_company(
84
+ fs_type=fs_type,
85
+ mutual_markets=mutual_markets,
86
+ stock_codes=stock_codes,
87
+ include_delisted=include_delisted,
88
+ )
@@ -0,0 +1,67 @@
1
+ from typing import Any
2
+
3
+ import pandas as pd
4
+
5
+ from lixinger.api.base import BaseAPI
6
+ from lixinger.models.cn.company import Dividend
7
+ from lixinger.utils.api import api
8
+ from lixinger.utils.dataframe import get_response_df
9
+
10
+
11
+ class DividendAPI(BaseAPI):
12
+ """Company dividend APIs."""
13
+
14
+ def get_dividend(
15
+ self,
16
+ stock_code: str,
17
+ start_date: str | None = None,
18
+ end_date: str | None = None,
19
+ ) -> pd.DataFrame:
20
+ """获取分红数据.
21
+
22
+ API Endpoint: /cn/company/dividend
23
+ API Method: POST
24
+
25
+ Args:
26
+ stock_code: 股票代码
27
+ start_date: 开始日期 (YYYY-MM-DD)
28
+ end_date: 结束日期 (YYYY-MM-DD)
29
+
30
+ """
31
+ payload: dict[str, Any] = {
32
+ "stockCode": stock_code,
33
+ }
34
+ if start_date is not None:
35
+ payload["startDate"] = start_date
36
+ if end_date is not None:
37
+ payload["endDate"] = end_date
38
+
39
+ data = self._request("POST", "/cn/company/dividend", json=payload)
40
+ for item in data:
41
+ item["stockCode"] = stock_code
42
+ return get_response_df(data, Dividend)
43
+
44
+
45
+ # Functional API instance
46
+ _api_instance = DividendAPI()
47
+
48
+
49
+ @api
50
+ def get_dividend(
51
+ stock_code: str,
52
+ start_date: str | None = None,
53
+ end_date: str | None = None,
54
+ ) -> pd.DataFrame:
55
+ """获取分红数据.
56
+
57
+ Args:
58
+ stock_code: 股票代码
59
+ start_date: 开始日期 (YYYY-MM-DD)
60
+ end_date: 结束日期 (YYYY-MM-DD)
61
+
62
+ """
63
+ return _api_instance.get_dividend(
64
+ stock_code=stock_code,
65
+ start_date=start_date,
66
+ end_date=end_date,
67
+ )
@@ -0,0 +1,84 @@
1
+ """Company equity change API."""
2
+
3
+ from typing import Any
4
+
5
+ import pandas as pd
6
+
7
+ from lixinger.api.base import BaseAPI
8
+ from lixinger.models.cn.company import EquityChangeData
9
+ from lixinger.utils.api import api
10
+ from lixinger.utils.dataframe import get_response_df
11
+
12
+
13
+ class EquityChangeAPI(BaseAPI):
14
+ """Company equity change API."""
15
+
16
+ def get_equity_change(
17
+ self,
18
+ stock_code: str,
19
+ start_date: str,
20
+ end_date: str | None = None,
21
+ ) -> pd.DataFrame:
22
+ """获取股本变动.
23
+
24
+ API Endpoint: /cn/company/equity-change
25
+ API Method: POST
26
+ Documentation: https://www.lixinger.com/open/api/doc?api-key=cn/company/equity-change
27
+
28
+ Args:
29
+ stock_code: 股票代码
30
+ start_date: 开始日期 (YYYY-MM-DD)
31
+ end_date: 结束日期 (YYYY-MM-DD), 可选
32
+
33
+ Returns:
34
+ 包含股本变动信息的 DataFrame
35
+
36
+ Example:
37
+ >>> client = LixingerClient()
38
+ >>> df = client.company.equity_change.get_equity_change(
39
+ ... stock_code="000001",
40
+ ... start_date="2023-01-01",
41
+ ... end_date="2023-12-31"
42
+ ... )
43
+ >>> print(df.head())
44
+
45
+ """
46
+ payload: dict[str, Any] = {
47
+ "stockCode": stock_code,
48
+ "startDate": start_date,
49
+ }
50
+ if end_date is not None:
51
+ payload["endDate"] = end_date
52
+
53
+ data = self._request("POST", "/cn/company/equity-change", json=payload)
54
+ for item in data:
55
+ item["stockCode"] = stock_code
56
+ return get_response_df(data, EquityChangeData)
57
+
58
+
59
+ # Functional API instance
60
+ _api_instance = EquityChangeAPI()
61
+
62
+
63
+ @api
64
+ def get_equity_change(
65
+ stock_code: str,
66
+ start_date: str,
67
+ end_date: str | None = None,
68
+ ) -> pd.DataFrame:
69
+ """获取股本变动.
70
+
71
+ Args:
72
+ stock_code: 股票代码
73
+ start_date: 开始日期 (YYYY-MM-DD)
74
+ end_date: 结束日期 (YYYY-MM-DD), 可选
75
+
76
+ Returns:
77
+ 包含股本变动信息的 DataFrame
78
+
79
+ """
80
+ return _api_instance.get_equity_change(
81
+ stock_code=stock_code,
82
+ start_date=start_date,
83
+ end_date=end_date,
84
+ )
@@ -0,0 +1,5 @@
1
+ """Financial statement APIs."""
2
+
3
+ from lixinger.api.cn.company.fs.non_financial import get_non_financial_statements
4
+
5
+ __all__ = ["get_non_financial_statements"]
@@ -0,0 +1,77 @@
1
+ """Non-financial statement APIs."""
2
+
3
+ from typing import Any
4
+
5
+ import pandas as pd
6
+
7
+ from lixinger.api.base import BaseAPI
8
+ from lixinger.models.cn.company.fs.non_financial import NonFinancialStatementSchema
9
+ from lixinger.utils.api import api
10
+ from lixinger.utils.dataframe import get_response_df
11
+ from lixinger.utils.dict import flatten_dict
12
+
13
+
14
+ class NonFinancialStatementAPI(BaseAPI):
15
+ """Non-financial statement APIs."""
16
+
17
+ def get_non_financial_statements(
18
+ self,
19
+ stock_codes: list[str],
20
+ metrics: list[str],
21
+ date: str | None = None,
22
+ start_date: str | None = None,
23
+ end_date: str | None = None,
24
+ ) -> pd.DataFrame:
25
+ """获取非金融企业财务报表数据.
26
+
27
+ API Endpoint: /cn/company/fs/non_financial
28
+ API Method: POST
29
+ API Doc: https://www.lixinger.com/open/api/doc?api-key=cn/company/fs/non_financial
30
+
31
+ Args:
32
+ stock_codes: 股票代码列表
33
+ metrics: 指标列表
34
+ date: 日期 (YYYY-MM-DD), 与 startDate 二选一
35
+ start_date: 开始日期 (YYYY-MM-DD), 与 date 二选一
36
+ end_date: 结束日期 (YYYY-MM-DD), 可选
37
+
38
+ """
39
+ payload: dict[str, Any] = {
40
+ "stockCodes": stock_codes,
41
+ "metricsList": metrics,
42
+ }
43
+ if date is not None:
44
+ payload["date"] = date
45
+ if start_date is not None:
46
+ payload["startDate"] = start_date
47
+ if end_date is not None:
48
+ payload["endDate"] = end_date
49
+
50
+ data = self._request("POST", "/cn/company/fs/non_financial", json=payload)
51
+
52
+ # Flatten nested metrics
53
+ flattened_data = [flatten_dict(item) for item in data]
54
+
55
+ return get_response_df(flattened_data, NonFinancialStatementSchema)
56
+
57
+
58
+ # Functional API instance
59
+ _api_instance = NonFinancialStatementAPI()
60
+
61
+
62
+ @api
63
+ def get_non_financial_statements(
64
+ stock_codes: list[str],
65
+ metrics: list[str],
66
+ date: str | None = None,
67
+ start_date: str | None = None,
68
+ end_date: str | None = None,
69
+ ) -> pd.DataFrame:
70
+ """获取非金融企业财务报表数据."""
71
+ return _api_instance.get_non_financial_statements(
72
+ stock_codes=stock_codes,
73
+ metrics=metrics,
74
+ date=date,
75
+ start_date=start_date,
76
+ end_date=end_date,
77
+ )