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.
- lixinger/__init__.py +79 -0
- lixinger/api/__init__.py +0 -0
- lixinger/api/base.py +94 -0
- lixinger/api/cn/__init__.py +0 -0
- lixinger/api/cn/company/__init__.py +34 -0
- lixinger/api/cn/company/announcement.py +74 -0
- lixinger/api/cn/company/candlestick.py +76 -0
- lixinger/api/cn/company/company.py +88 -0
- lixinger/api/cn/company/dividend.py +67 -0
- lixinger/api/cn/company/equity_change.py +84 -0
- lixinger/api/cn/company/fs/__init__.py +5 -0
- lixinger/api/cn/company/fs/non_financial.py +77 -0
- lixinger/api/cn/company/fundamental.py +228 -0
- lixinger/api/cn/company/indices.py +52 -0
- lixinger/api/cn/company/namespace.py +66 -0
- lixinger/api/cn/company/profile.py +66 -0
- lixinger/api/cn/fund/__init__.py +35 -0
- lixinger/api/cn/fund/announcement.py +89 -0
- lixinger/api/cn/fund/asset_combination.py +82 -0
- lixinger/api/cn/fund/asset_industry_combination.py +93 -0
- lixinger/api/cn/fund/candlestick.py +84 -0
- lixinger/api/cn/fund/fund.py +70 -0
- lixinger/api/cn/fund/profile.py +60 -0
- lixinger/api/cn/fund/shareholdings.py +86 -0
- lixinger/api/cn/index/__init__.py +32 -0
- lixinger/api/cn/index/candlestick.py +86 -0
- lixinger/api/cn/index/constituent_weightings.py +71 -0
- lixinger/api/cn/index/constituents.py +71 -0
- lixinger/api/cn/index/drawdown.py +87 -0
- lixinger/api/cn/index/fundamental.py +87 -0
- lixinger/api/cn/index/index.py +69 -0
- lixinger/api/cn/index/tracking_fund.py +71 -0
- lixinger/client.py +184 -0
- lixinger/config.py +72 -0
- lixinger/exceptions.py +22 -0
- lixinger/models/__init__.py +5 -0
- lixinger/models/cn/__init__.py +1 -0
- lixinger/models/cn/company/__init__.py +20 -0
- lixinger/models/cn/company/announcement.py +20 -0
- lixinger/models/cn/company/candlestick.py +24 -0
- lixinger/models/cn/company/company.py +33 -0
- lixinger/models/cn/company/dividend.py +25 -0
- lixinger/models/cn/company/equity_change.py +26 -0
- lixinger/models/cn/company/fs/__init__.py +5 -0
- lixinger/models/cn/company/fs/non_financial.py +20 -0
- lixinger/models/cn/company/fundamental.py +16 -0
- lixinger/models/cn/company/indices.py +17 -0
- lixinger/models/cn/fund/__init__.py +19 -0
- lixinger/models/cn/fund/announcement.py +21 -0
- lixinger/models/cn/fund/asset_combination.py +40 -0
- lixinger/models/cn/fund/asset_industry_combination.py +24 -0
- lixinger/models/cn/fund/candlestick.py +23 -0
- lixinger/models/cn/fund/fund.py +23 -0
- lixinger/models/cn/fund/profile.py +28 -0
- lixinger/models/cn/fund/shareholdings.py +21 -0
- lixinger/models/cn/index/__init__.py +19 -0
- lixinger/models/cn/index/candlestick.py +22 -0
- lixinger/models/cn/index/constituent_weightings.py +16 -0
- lixinger/models/cn/index/constituents.py +17 -0
- lixinger/models/cn/index/drawdown.py +16 -0
- lixinger/models/cn/index/fundamental.py +16 -0
- lixinger/models/cn/index/index.py +24 -0
- lixinger/models/cn/index/tracking_fund.py +19 -0
- lixinger/py.typed +0 -0
- lixinger/utils/__init__.py +0 -0
- lixinger/utils/api.py +15 -0
- lixinger/utils/dataframe.py +57 -0
- lixinger/utils/dict.py +22 -0
- lixinger/utils/rate_limiter.py +35 -0
- lixinger/utils/retry.py +31 -0
- lixinger_python-0.1.2.dist-info/METADATA +171 -0
- lixinger_python-0.1.2.dist-info/RECORD +74 -0
- lixinger_python-0.1.2.dist-info/WHEEL +4 -0
- 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
|
+
]
|
lixinger/api/__init__.py
ADDED
|
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,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
|
+
)
|