lib-x17-fintech 2.1.3__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.
- lib_x17_fintech-2.1.3.dist-info/METADATA +633 -0
- lib_x17_fintech-2.1.3.dist-info/RECORD +282 -0
- lib_x17_fintech-2.1.3.dist-info/WHEEL +5 -0
- lib_x17_fintech-2.1.3.dist-info/licenses/LICENSE +1 -0
- lib_x17_fintech-2.1.3.dist-info/top_level.txt +1 -0
- xfintech/__init__.py +0 -0
- xfintech/connect/__init__.py +18 -0
- xfintech/connect/artifact/__init__.py +5 -0
- xfintech/connect/artifact/artifact.py +168 -0
- xfintech/connect/artifact/tests/__init__.py +3 -0
- xfintech/connect/artifact/tests/test_class_artifact_all.py +564 -0
- xfintech/connect/common/__init__.py +12 -0
- xfintech/connect/common/connect.py +49 -0
- xfintech/connect/common/connectref.py +119 -0
- xfintech/connect/common/error.py +62 -0
- xfintech/connect/common/tests/__init__.py +1 -0
- xfintech/connect/common/tests/test_class_connectlike_all.py +544 -0
- xfintech/connect/common/tests/test_class_connectref_all.py +586 -0
- xfintech/connect/common/tests/test_class_errors_all.py +524 -0
- xfintech/connect/instance/__init__.py +7 -0
- xfintech/connect/instance/macos.py +121 -0
- xfintech/connect/instance/s3.py +176 -0
- xfintech/connect/instance/tests/__init__.py +1 -0
- xfintech/connect/instance/tests/test_class_macosconnect_all.py +692 -0
- xfintech/connect/instance/tests/test_class_s3connect_all.py +603 -0
- xfintech/data/__init__.py +20 -0
- xfintech/data/common/__init__.py +15 -0
- xfintech/data/common/cache.py +186 -0
- xfintech/data/common/coolant.py +171 -0
- xfintech/data/common/metric.py +138 -0
- xfintech/data/common/paginate.py +132 -0
- xfintech/data/common/params.py +162 -0
- xfintech/data/common/retry.py +201 -0
- xfintech/data/common/tests/__init__.py +1 -0
- xfintech/data/common/tests/test_class_cache_all.py +681 -0
- xfintech/data/common/tests/test_class_coolant_all.py +534 -0
- xfintech/data/common/tests/test_class_metric_all.py +705 -0
- xfintech/data/common/tests/test_class_paginate_all.py +508 -0
- xfintech/data/common/tests/test_class_params_all.py +891 -0
- xfintech/data/common/tests/test_class_retry_all.py +714 -0
- xfintech/data/job/__init__.py +17 -0
- xfintech/data/job/errors.py +112 -0
- xfintech/data/job/house.py +156 -0
- xfintech/data/job/job.py +247 -0
- xfintech/data/job/joblike.py +47 -0
- xfintech/data/job/tests/__init__.py +1 -0
- xfintech/data/job/tests/test_class_errors_all.py +275 -0
- xfintech/data/job/tests/test_class_house_all.py +801 -0
- xfintech/data/job/tests/test_class_job_all.py +684 -0
- xfintech/data/job/tests/test_class_joblike_all.py +482 -0
- xfintech/data/relay/__init__.py +7 -0
- xfintech/data/relay/client.py +114 -0
- xfintech/data/relay/clientlike.py +45 -0
- xfintech/data/relay/tests/test_class_relayclient_all.py +484 -0
- xfintech/data/relay/tests/test_class_relayclientlike_all.py +500 -0
- xfintech/data/source/__init__.py +7 -0
- xfintech/data/source/baostock/__init__.py +21 -0
- xfintech/data/source/baostock/job/__init__.py +5 -0
- xfintech/data/source/baostock/job/job.py +217 -0
- xfintech/data/source/baostock/job/tests/__init__.py +0 -0
- xfintech/data/source/baostock/job/tests/test_class_baostockjob_all.py +547 -0
- xfintech/data/source/baostock/session/__init__.py +8 -0
- xfintech/data/source/baostock/session/relay.py +223 -0
- xfintech/data/source/baostock/session/session.py +241 -0
- xfintech/data/source/baostock/session/tests/__init__.py +0 -0
- xfintech/data/source/baostock/session/tests/test_class_relay_all.py +694 -0
- xfintech/data/source/baostock/session/tests/test_class_session_all.py +505 -0
- xfintech/data/source/baostock/stock/__init__.py +0 -0
- xfintech/data/source/baostock/stock/hs300stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/hs300stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/hs300stock/hs300stock.py +133 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/hs300stock/tests/test_class_hs300index_all.py +413 -0
- xfintech/data/source/baostock/stock/minuteline/__init__.py +19 -0
- xfintech/data/source/baostock/stock/minuteline/constant.py +89 -0
- xfintech/data/source/baostock/stock/minuteline/minuteline.py +163 -0
- xfintech/data/source/baostock/stock/minuteline/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/minuteline/tests/test_class_minuteline_all.py +582 -0
- xfintech/data/source/baostock/stock/stock/__init__.py +19 -0
- xfintech/data/source/baostock/stock/stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/stock/stock.py +149 -0
- xfintech/data/source/baostock/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/stock/tests/test_class_stock_all.py +508 -0
- xfintech/data/source/baostock/stock/stockinfo/__init__.py +5 -0
- xfintech/data/source/baostock/stock/stockinfo/constant.py +66 -0
- xfintech/data/source/baostock/stock/stockinfo/stockinfo.py +176 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/stockinfo/tests/test_class_stockinfo_all.py +617 -0
- xfintech/data/source/baostock/stock/sz50stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/sz50stock/constant.py +49 -0
- xfintech/data/source/baostock/stock/sz50stock/sz50stock.py +133 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/sz50stock/tests/test_class_sz50stock_all.py +397 -0
- xfintech/data/source/baostock/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/baostock/stock/tradedate/constant.py +72 -0
- xfintech/data/source/baostock/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/baostock/stock/tradedate/tests/test_class_tradedate_all.py +695 -0
- xfintech/data/source/baostock/stock/tradedate/tradedate.py +208 -0
- xfintech/data/source/baostock/stock/zz500stock/__init__.py +3 -0
- xfintech/data/source/baostock/stock/zz500stock/constant.py +55 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/__init__.py +1 -0
- xfintech/data/source/baostock/stock/zz500stock/tests/test_class_zz500stock_all.py +421 -0
- xfintech/data/source/baostock/stock/zz500stock/zz500stock.py +133 -0
- xfintech/data/source/tushare/__init__.py +61 -0
- xfintech/data/source/tushare/job/__init__.py +5 -0
- xfintech/data/source/tushare/job/job.py +257 -0
- xfintech/data/source/tushare/job/tests/test_class_tusharejob_all.py +589 -0
- xfintech/data/source/tushare/session/__init__.py +5 -0
- xfintech/data/source/tushare/session/relay.py +231 -0
- xfintech/data/source/tushare/session/session.py +239 -0
- xfintech/data/source/tushare/session/tests/test_class_relay_all.py +719 -0
- xfintech/data/source/tushare/session/tests/test_class_session_all.py +705 -0
- xfintech/data/source/tushare/stock/__init__.py +55 -0
- xfintech/data/source/tushare/stock/adjfactor/__init__.py +19 -0
- xfintech/data/source/tushare/stock/adjfactor/adjfactor.py +150 -0
- xfintech/data/source/tushare/stock/adjfactor/constant.py +71 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/adjfactor/tests/test_class_adjfactor_all.py +372 -0
- xfintech/data/source/tushare/stock/capflow/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflow/capflow.py +171 -0
- xfintech/data/source/tushare/stock/capflow/constant.py +105 -0
- xfintech/data/source/tushare/stock/capflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflow/tests/test_class_capflow_all.py +589 -0
- xfintech/data/source/tushare/stock/capflowdc/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowdc/capflowdc.py +167 -0
- xfintech/data/source/tushare/stock/capflowdc/constant.py +95 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowdc/tests/test_class_capflowdc_all.py +814 -0
- xfintech/data/source/tushare/stock/capflowths/__init__.py +19 -0
- xfintech/data/source/tushare/stock/capflowths/capflowths.py +173 -0
- xfintech/data/source/tushare/stock/capflowths/constant.py +92 -0
- xfintech/data/source/tushare/stock/capflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/capflowths/tests/test_class_capflowths_all.py +551 -0
- xfintech/data/source/tushare/stock/company/__init__.py +19 -0
- xfintech/data/source/tushare/stock/company/company.py +188 -0
- xfintech/data/source/tushare/stock/company/constant.py +92 -0
- xfintech/data/source/tushare/stock/company/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/company/tests/test_class_company_all.py +829 -0
- xfintech/data/source/tushare/stock/companybusiness/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companybusiness/companybusiness.py +183 -0
- xfintech/data/source/tushare/stock/companybusiness/constant.py +91 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companybusiness/tests/test_class_companybusiness_all.py +633 -0
- xfintech/data/source/tushare/stock/companycashflow/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companycashflow/companycashflow.py +277 -0
- xfintech/data/source/tushare/stock/companycashflow/constant.py +293 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companycashflow/tests/test_class_companycashflow_all.py +619 -0
- xfintech/data/source/tushare/stock/companydebt/__init__.py +19 -0
- xfintech/data/source/tushare/stock/companydebt/companydebt.py +339 -0
- xfintech/data/source/tushare/stock/companydebt/constant.py +403 -0
- xfintech/data/source/tushare/stock/companydebt/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companydebt/tests/test_class_companydebt_all.py +655 -0
- xfintech/data/source/tushare/stock/companyoverview/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyoverview/companyoverview.py +214 -0
- xfintech/data/source/tushare/stock/companyoverview/constant.py +152 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyoverview/tests/test_class_companyoverview_all.py +647 -0
- xfintech/data/source/tushare/stock/companyprofit/__init__.py +21 -0
- xfintech/data/source/tushare/stock/companyprofit/companyprofit.py +272 -0
- xfintech/data/source/tushare/stock/companyprofit/constant.py +259 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/companyprofit/tests/test_class_companyprofit_all.py +635 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/conceptcapflowdc.py +175 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/constant.py +106 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowdc/tests/test_class_conceptcapflowdc_all.py +568 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/conceptcapflowths.py +188 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/conceptcapflowths/tests/test_class_conceptcapflowths_all.py +516 -0
- xfintech/data/source/tushare/stock/dayline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/dayline/constant.py +87 -0
- xfintech/data/source/tushare/stock/dayline/dayline.py +177 -0
- xfintech/data/source/tushare/stock/dayline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/dayline/tests/test_class_dayline_all.py +585 -0
- xfintech/data/source/tushare/stock/industrycapflowths/__init__.py +21 -0
- xfintech/data/source/tushare/stock/industrycapflowths/constant.py +89 -0
- xfintech/data/source/tushare/stock/industrycapflowths/industrycapflowths.py +192 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/industrycapflowths/tests/test_class_industrycapflowths_all.py +683 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/__init__.py +21 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/constant.py +90 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/marketindexcapflowdc.py +173 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/marketindexcapflowdc/tests/test_class_marketindexcapflowdc_all.py +793 -0
- xfintech/data/source/tushare/stock/monthline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/monthline/constant.py +87 -0
- xfintech/data/source/tushare/stock/monthline/monthline.py +180 -0
- xfintech/data/source/tushare/stock/monthline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/monthline/tests/test_class_monthline_all.py +574 -0
- xfintech/data/source/tushare/stock/stock/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stock/constant.py +105 -0
- xfintech/data/source/tushare/stock/stock/stock.py +193 -0
- xfintech/data/source/tushare/stock/stock/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stock/tests/test_class_stock_all.py +788 -0
- xfintech/data/source/tushare/stock/stockdividend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockdividend/constant.py +111 -0
- xfintech/data/source/tushare/stock/stockdividend/stockdividend.py +180 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockdividend/tests/test_class_stockdividend_all.py +725 -0
- xfintech/data/source/tushare/stock/stockinfo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockinfo/constant.py +104 -0
- xfintech/data/source/tushare/stock/stockinfo/stockinfo.py +208 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockinfo/tests/test_class_stockinfo_all.py +881 -0
- xfintech/data/source/tushare/stock/stockipo/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockipo/constant.py +90 -0
- xfintech/data/source/tushare/stock/stockipo/stockipo.py +234 -0
- xfintech/data/source/tushare/stock/stockipo/tests/__init__.py +1 -0
- xfintech/data/source/tushare/stock/stockipo/tests/test_class_stockipo_all.py +750 -0
- xfintech/data/source/tushare/stock/stockpledge/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockpledge/constant.py +72 -0
- xfintech/data/source/tushare/stock/stockpledge/stockpledge.py +158 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledge/tests/test_class_stockpledge_all.py +664 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/constant.py +85 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/stockpledgedetail.py +171 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockpledgedetail/tests/test_class_stockpledgedetail_all.py +112 -0
- xfintech/data/source/tushare/stock/stockst/__init__.py +19 -0
- xfintech/data/source/tushare/stock/stockst/constant.py +80 -0
- xfintech/data/source/tushare/stock/stockst/stockst.py +189 -0
- xfintech/data/source/tushare/stock/stockst/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stockst/tests/test_class_stockst_all.py +693 -0
- xfintech/data/source/tushare/stock/stocksuspend/__init__.py +21 -0
- xfintech/data/source/tushare/stock/stocksuspend/constant.py +75 -0
- xfintech/data/source/tushare/stock/stocksuspend/stocksuspend.py +151 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/stocksuspend/tests/test_class_stocksuspend_all.py +626 -0
- xfintech/data/source/tushare/stock/techindex/__init__.py +19 -0
- xfintech/data/source/tushare/stock/techindex/constant.py +600 -0
- xfintech/data/source/tushare/stock/techindex/techindex.py +314 -0
- xfintech/data/source/tushare/stock/techindex/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/techindex/tests/test_class_techindex_all.py +576 -0
- xfintech/data/source/tushare/stock/tradedate/__init__.py +19 -0
- xfintech/data/source/tushare/stock/tradedate/constant.py +93 -0
- xfintech/data/source/tushare/stock/tradedate/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/tradedate/tests/test_class_tradedate_all.py +947 -0
- xfintech/data/source/tushare/stock/tradedate/tradedate.py +234 -0
- xfintech/data/source/tushare/stock/weekline/__init__.py +19 -0
- xfintech/data/source/tushare/stock/weekline/constant.py +87 -0
- xfintech/data/source/tushare/stock/weekline/tests/__init__.py +0 -0
- xfintech/data/source/tushare/stock/weekline/tests/test_class_weekline_all.py +575 -0
- xfintech/data/source/tushare/stock/weekline/weekline.py +182 -0
- xfintech/fabric/__init__.py +18 -0
- xfintech/fabric/column/__init__.py +7 -0
- xfintech/fabric/column/info.py +202 -0
- xfintech/fabric/column/kind.py +102 -0
- xfintech/fabric/column/tests/__init__.py +0 -0
- xfintech/fabric/column/tests/test_class_info_all.py +207 -0
- xfintech/fabric/column/tests/test_class_kind_all.py +80 -0
- xfintech/fabric/table/__init__.py +5 -0
- xfintech/fabric/table/info.py +263 -0
- xfintech/fabric/table/tests/__init__.py +0 -0
- xfintech/fabric/table/tests/test_class_info_all.py +547 -0
- xfintech/serde/__init__.py +35 -0
- xfintech/serde/common/__init__.py +9 -0
- xfintech/serde/common/dataformat.py +78 -0
- xfintech/serde/common/deserialiserlike.py +38 -0
- xfintech/serde/common/error.py +182 -0
- xfintech/serde/common/serialiserlike.py +38 -0
- xfintech/serde/common/tests/__init__.py +1 -0
- xfintech/serde/common/tests/test_class_dataformat_all.py +694 -0
- xfintech/serde/common/tests/test_class_deserialiserlike_all.py +500 -0
- xfintech/serde/common/tests/test_class_errors_all.py +518 -0
- xfintech/serde/common/tests/test_class_serialiserlike_all.py +401 -0
- xfintech/serde/deserialiser/__init__.py +7 -0
- xfintech/serde/deserialiser/pandas.py +113 -0
- xfintech/serde/deserialiser/python.py +68 -0
- xfintech/serde/deserialiser/tests/__init__.py +1 -0
- xfintech/serde/deserialiser/tests/test_class_pandasdeserialiser_all.py +503 -0
- xfintech/serde/deserialiser/tests/test_class_pythondeserialiser_all.py +570 -0
- xfintech/serde/serialiser/__init__.py +7 -0
- xfintech/serde/serialiser/pandas.py +116 -0
- xfintech/serde/serialiser/python.py +71 -0
- xfintech/serde/serialiser/tests/__init__.py +1 -0
- xfintech/serde/serialiser/tests/test_class_pandasserialiser_all.py +474 -0
- xfintech/serde/serialiser/tests/test_class_pythonserialiser_all.py +508 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from xfintech.data.common.cache import Cache
|
|
7
|
+
from xfintech.data.common.coolant import Coolant
|
|
8
|
+
from xfintech.data.common.retry import Retry
|
|
9
|
+
from xfintech.data.source.baostock.session.session import Session
|
|
10
|
+
from xfintech.data.source.baostock.stock.hs300stock.constant import (
|
|
11
|
+
KEY,
|
|
12
|
+
NAME,
|
|
13
|
+
SOURCE,
|
|
14
|
+
TARGET,
|
|
15
|
+
)
|
|
16
|
+
from xfintech.data.source.baostock.stock.hs300stock.hs300stock import HS300Stock
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def mock_session():
|
|
21
|
+
"""Create a mock Baostock session"""
|
|
22
|
+
session = MagicMock(spec=Session)
|
|
23
|
+
session._credential = None
|
|
24
|
+
session.id = "test1234"
|
|
25
|
+
session.mode = "direct"
|
|
26
|
+
session.relay_url = None
|
|
27
|
+
session.relay_secret = None
|
|
28
|
+
session.connected = True
|
|
29
|
+
|
|
30
|
+
# Mock the connection object
|
|
31
|
+
mock_connection = MagicMock()
|
|
32
|
+
mock_connection.query_hs300_stocks = MagicMock()
|
|
33
|
+
session.connection = mock_connection
|
|
34
|
+
|
|
35
|
+
return session
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def sample_source_data():
|
|
40
|
+
"""Create sample source data in Baostock format"""
|
|
41
|
+
return pd.DataFrame(
|
|
42
|
+
{
|
|
43
|
+
"updateDate": ["2018-11-26"] * 15,
|
|
44
|
+
"code": [
|
|
45
|
+
"sh.600000",
|
|
46
|
+
"sh.600008",
|
|
47
|
+
"sh.600009",
|
|
48
|
+
"sh.600010",
|
|
49
|
+
"sh.600015",
|
|
50
|
+
"sh.600016",
|
|
51
|
+
"sh.600018",
|
|
52
|
+
"sh.600019",
|
|
53
|
+
"sh.600028",
|
|
54
|
+
"sh.600029",
|
|
55
|
+
"sz.000001",
|
|
56
|
+
"sz.000002",
|
|
57
|
+
"sz.000063",
|
|
58
|
+
"sz.000069",
|
|
59
|
+
"sz.000100",
|
|
60
|
+
],
|
|
61
|
+
"code_name": [
|
|
62
|
+
"浦发银行",
|
|
63
|
+
"首创股份",
|
|
64
|
+
"上海机场",
|
|
65
|
+
"包钢股份",
|
|
66
|
+
"华夏银行",
|
|
67
|
+
"民生银行",
|
|
68
|
+
"上港集团",
|
|
69
|
+
"宝钢股份",
|
|
70
|
+
"中国石化",
|
|
71
|
+
"南方航空",
|
|
72
|
+
"平安银行",
|
|
73
|
+
"万科A",
|
|
74
|
+
"中兴通讯",
|
|
75
|
+
"华侨城A",
|
|
76
|
+
"TCL科技",
|
|
77
|
+
],
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ============================================================================
|
|
83
|
+
# Initialization Tests
|
|
84
|
+
# ============================================================================
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_hs300stock_initialization_basic(mock_session):
|
|
88
|
+
"""Test basic initialization"""
|
|
89
|
+
job = HS300Stock(session=mock_session)
|
|
90
|
+
|
|
91
|
+
assert job.name == NAME
|
|
92
|
+
assert job.key == KEY
|
|
93
|
+
assert job.source == SOURCE
|
|
94
|
+
assert job.target == TARGET
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_hs300stock_initialization_with_params(mock_session):
|
|
98
|
+
"""Test initialization with params (not used for this API)"""
|
|
99
|
+
params = {}
|
|
100
|
+
job = HS300Stock(session=mock_session, params=params)
|
|
101
|
+
|
|
102
|
+
# No specific params needed for HS300 API
|
|
103
|
+
assert job.params is not None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_hs300stock_initialization_with_all_components(mock_session):
|
|
107
|
+
"""Test initialization with all components"""
|
|
108
|
+
coolant = Coolant(interval=0.2)
|
|
109
|
+
retry = Retry(retry=3)
|
|
110
|
+
cache = Cache(path="/tmp/test_cache")
|
|
111
|
+
|
|
112
|
+
job = HS300Stock(
|
|
113
|
+
session=mock_session,
|
|
114
|
+
coolant=coolant,
|
|
115
|
+
retry=retry,
|
|
116
|
+
cache=cache,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
assert job.coolant.interval == 0.2
|
|
120
|
+
assert job.retry.retry == 3
|
|
121
|
+
assert job.cache is not None
|
|
122
|
+
assert isinstance(job.cache, Cache)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_hs300stock_name_and_key():
|
|
126
|
+
"""Test name and key constants"""
|
|
127
|
+
assert NAME == "hs300stock"
|
|
128
|
+
assert KEY == "/baostock/hs300stock"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_hs300stock_source_schema():
|
|
132
|
+
"""Test source schema has all required columns"""
|
|
133
|
+
assert SOURCE is not None
|
|
134
|
+
assert "沪深300成分股" in SOURCE.desc
|
|
135
|
+
|
|
136
|
+
column_names = SOURCE.columns
|
|
137
|
+
assert "updatedate" in column_names
|
|
138
|
+
assert "code" in column_names
|
|
139
|
+
assert "code_name" in column_names
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_hs300stock_target_schema():
|
|
143
|
+
"""Test target schema has all required columns"""
|
|
144
|
+
assert TARGET is not None
|
|
145
|
+
assert "沪深300成分股" in TARGET.desc
|
|
146
|
+
|
|
147
|
+
column_names = TARGET.columns
|
|
148
|
+
assert "update_date" in column_names
|
|
149
|
+
assert "code" in column_names
|
|
150
|
+
assert "name" in column_names
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
# ============================================================================
|
|
154
|
+
# Transform Tests
|
|
155
|
+
# ============================================================================
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_hs300stock_transform_basic(mock_session, sample_source_data):
|
|
159
|
+
"""Test basic data transformation"""
|
|
160
|
+
job = HS300Stock(session=mock_session)
|
|
161
|
+
result = job.transform(sample_source_data)
|
|
162
|
+
|
|
163
|
+
assert len(result) == 15
|
|
164
|
+
assert "update_date" in result.columns
|
|
165
|
+
assert "code" in result.columns
|
|
166
|
+
assert "name" in result.columns
|
|
167
|
+
assert result.iloc[0]["code"] == "sh.600000"
|
|
168
|
+
assert result.iloc[0]["name"] == "浦发银行"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_hs300stock_transform_field_types(mock_session, sample_source_data):
|
|
172
|
+
"""Test field type conversions"""
|
|
173
|
+
job = HS300Stock(session=mock_session)
|
|
174
|
+
result = job.transform(sample_source_data)
|
|
175
|
+
|
|
176
|
+
# Check string fields
|
|
177
|
+
assert isinstance(result.iloc[0]["update_date"], str)
|
|
178
|
+
assert isinstance(result.iloc[0]["code"], str)
|
|
179
|
+
assert isinstance(result.iloc[0]["name"], str)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def test_hs300stock_transform_field_mapping(mock_session, sample_source_data):
|
|
183
|
+
"""Test field name mappings"""
|
|
184
|
+
job = HS300Stock(session=mock_session)
|
|
185
|
+
result = job.transform(sample_source_data)
|
|
186
|
+
|
|
187
|
+
# Verify field mappings
|
|
188
|
+
row = result[result["code"] == "sh.600000"].iloc[0]
|
|
189
|
+
assert row["update_date"] == "2018-11-26" # from updateDate
|
|
190
|
+
assert row["name"] == "浦发银行" # from code_name
|
|
191
|
+
assert row["code"] == "sh.600000"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def test_hs300stock_transform_empty_data(mock_session):
|
|
195
|
+
"""Test transform with empty data"""
|
|
196
|
+
job = HS300Stock(session=mock_session)
|
|
197
|
+
|
|
198
|
+
# Test with None
|
|
199
|
+
result = job.transform(None)
|
|
200
|
+
assert result.empty
|
|
201
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
202
|
+
|
|
203
|
+
# Test with empty DataFrame
|
|
204
|
+
empty_df = pd.DataFrame()
|
|
205
|
+
result = job.transform(empty_df)
|
|
206
|
+
assert result.empty
|
|
207
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def test_hs300stock_transform_duplicate_removal(mock_session):
|
|
211
|
+
"""Test that duplicates are removed"""
|
|
212
|
+
data = pd.DataFrame(
|
|
213
|
+
{
|
|
214
|
+
"updateDate": ["2018-11-26", "2018-11-26", "2018-11-26"],
|
|
215
|
+
"code": ["sh.600000", "sh.600000", "sh.600008"],
|
|
216
|
+
"code_name": ["浦发银行", "浦发银行", "首创股份"],
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
job = HS300Stock(session=mock_session)
|
|
220
|
+
result = job.transform(data)
|
|
221
|
+
|
|
222
|
+
assert len(result) == 2 # Duplicates removed
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def test_hs300stock_transform_sorting(mock_session):
|
|
226
|
+
"""Test that results are sorted by code"""
|
|
227
|
+
data = pd.DataFrame(
|
|
228
|
+
{
|
|
229
|
+
"updateDate": ["2018-11-26", "2018-11-26", "2018-11-26"],
|
|
230
|
+
"code": ["sz.000001", "sh.600000", "sh.600008"],
|
|
231
|
+
"code_name": ["平安银行", "浦发银行", "首创股份"],
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
job = HS300Stock(session=mock_session)
|
|
235
|
+
result = job.transform(data)
|
|
236
|
+
|
|
237
|
+
# Should be sorted by code
|
|
238
|
+
assert result.iloc[0]["code"] == "sh.600000"
|
|
239
|
+
assert result.iloc[1]["code"] == "sh.600008"
|
|
240
|
+
assert result.iloc[2]["code"] == "sz.000001"
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def test_hs300stock_transform_mixed_exchanges(mock_session, sample_source_data):
|
|
244
|
+
"""Test transform with stocks from both Shanghai and Shenzhen exchanges"""
|
|
245
|
+
job = HS300Stock(session=mock_session)
|
|
246
|
+
result = job.transform(sample_source_data)
|
|
247
|
+
|
|
248
|
+
# Should have both sh. and sz. codes
|
|
249
|
+
sh_codes = [code for code in result["code"] if code.startswith("sh.")]
|
|
250
|
+
sz_codes = [code for code in result["code"] if code.startswith("sz.")]
|
|
251
|
+
|
|
252
|
+
assert len(sh_codes) > 0
|
|
253
|
+
assert len(sz_codes) > 0
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# ============================================================================
|
|
257
|
+
# Run Tests
|
|
258
|
+
# ============================================================================
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_hs300stock_run_basic(mock_session, sample_source_data):
|
|
262
|
+
"""Test basic run functionality"""
|
|
263
|
+
job = HS300Stock(session=mock_session)
|
|
264
|
+
|
|
265
|
+
# Mock _fetchall to return sample data
|
|
266
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
267
|
+
with patch.object(job, "_load_cache", return_value=None):
|
|
268
|
+
with patch.object(job, "_save_cache"):
|
|
269
|
+
result = job._run()
|
|
270
|
+
|
|
271
|
+
assert isinstance(result, pd.DataFrame)
|
|
272
|
+
assert len(result) == 15
|
|
273
|
+
assert "update_date" in result.columns
|
|
274
|
+
assert "code" in result.columns
|
|
275
|
+
assert "name" in result.columns
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def test_hs300stock_run_calls_api(mock_session, sample_source_data):
|
|
279
|
+
"""Test that run calls the correct API"""
|
|
280
|
+
job = HS300Stock(session=mock_session)
|
|
281
|
+
|
|
282
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
283
|
+
with patch.object(job, "_load_cache", return_value=None):
|
|
284
|
+
with patch.object(job, "_save_cache"):
|
|
285
|
+
job._run()
|
|
286
|
+
|
|
287
|
+
# Verify API was called
|
|
288
|
+
mock_fetchall.assert_called_once()
|
|
289
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
290
|
+
assert call_kwargs["api"] == job.connection.query_hs300_stocks
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def test_hs300stock_run_with_cache(mock_session, sample_source_data):
|
|
294
|
+
"""Test that cache is used when available"""
|
|
295
|
+
job = HS300Stock(session=mock_session)
|
|
296
|
+
cached_data = sample_source_data.copy()
|
|
297
|
+
|
|
298
|
+
with patch.object(job, "_load_cache", return_value=cached_data):
|
|
299
|
+
with patch.object(job, "_fetchall") as mock_fetchall:
|
|
300
|
+
result = job._run()
|
|
301
|
+
|
|
302
|
+
# Should return cached data without calling API
|
|
303
|
+
mock_fetchall.assert_not_called()
|
|
304
|
+
assert len(result) == 15
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ============================================================================
|
|
308
|
+
# List Methods Tests
|
|
309
|
+
# ============================================================================
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_hs300stock_list_codes(mock_session, sample_source_data):
|
|
313
|
+
"""Test list_codes method"""
|
|
314
|
+
job = HS300Stock(session=mock_session)
|
|
315
|
+
|
|
316
|
+
with patch.object(job, "run", return_value=job.transform(sample_source_data)):
|
|
317
|
+
codes = job.list_codes()
|
|
318
|
+
|
|
319
|
+
assert isinstance(codes, list)
|
|
320
|
+
assert len(codes) == 15
|
|
321
|
+
assert "sh.600000" in codes
|
|
322
|
+
assert "sz.000100" in codes
|
|
323
|
+
# Should be sorted
|
|
324
|
+
assert codes == sorted(codes)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_hs300stock_list_names(mock_session, sample_source_data):
|
|
328
|
+
"""Test list_names method"""
|
|
329
|
+
job = HS300Stock(session=mock_session)
|
|
330
|
+
|
|
331
|
+
with patch.object(job, "run", return_value=job.transform(sample_source_data)):
|
|
332
|
+
names = job.list_names()
|
|
333
|
+
|
|
334
|
+
assert isinstance(names, list)
|
|
335
|
+
assert len(names) == 15
|
|
336
|
+
assert "浦发银行" in names
|
|
337
|
+
assert "TCL科技" in names
|
|
338
|
+
# Should be sorted
|
|
339
|
+
assert names == sorted(names)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_hs300stock_list_codes_with_duplicates(mock_session):
|
|
343
|
+
"""Test list_codes with duplicate codes"""
|
|
344
|
+
data = pd.DataFrame(
|
|
345
|
+
{
|
|
346
|
+
"updateDate": ["2018-11-26", "2018-11-26", "2018-11-26"],
|
|
347
|
+
"code": ["sh.600000", "sh.600000", "sh.600008"],
|
|
348
|
+
"code_name": ["浦发银行", "浦发银行", "首创股份"],
|
|
349
|
+
}
|
|
350
|
+
)
|
|
351
|
+
job = HS300Stock(session=mock_session)
|
|
352
|
+
transformed = job.transform(data)
|
|
353
|
+
|
|
354
|
+
with patch.object(job, "run", return_value=transformed):
|
|
355
|
+
codes = job.list_codes()
|
|
356
|
+
|
|
357
|
+
# Should return unique codes only
|
|
358
|
+
assert len(codes) == 2
|
|
359
|
+
assert "sh.600000" in codes
|
|
360
|
+
assert "sh.600008" in codes
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# ============================================================================
|
|
364
|
+
# Integration Tests
|
|
365
|
+
# ============================================================================
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def test_hs300stock_full_workflow(mock_session, sample_source_data):
|
|
369
|
+
"""Test full workflow from initialization to result"""
|
|
370
|
+
job = HS300Stock(session=mock_session)
|
|
371
|
+
|
|
372
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
373
|
+
with patch.object(job, "_load_cache", return_value=None):
|
|
374
|
+
with patch.object(job, "_save_cache"):
|
|
375
|
+
result = job._run()
|
|
376
|
+
|
|
377
|
+
# Verify result structure
|
|
378
|
+
assert isinstance(result, pd.DataFrame)
|
|
379
|
+
assert len(result) == 15
|
|
380
|
+
assert "update_date" in result.columns
|
|
381
|
+
assert "code" in result.columns
|
|
382
|
+
assert "name" in result.columns
|
|
383
|
+
|
|
384
|
+
# Verify data types
|
|
385
|
+
assert isinstance(result.iloc[0]["update_date"], str)
|
|
386
|
+
assert isinstance(result.iloc[0]["code"], str)
|
|
387
|
+
assert isinstance(result.iloc[0]["name"], str)
|
|
388
|
+
|
|
389
|
+
# Verify sorting
|
|
390
|
+
codes = result["code"].tolist()
|
|
391
|
+
assert codes == sorted(codes)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def test_hs300stock_same_update_date(mock_session, sample_source_data):
|
|
395
|
+
"""Test that all stocks have the same update date"""
|
|
396
|
+
job = HS300Stock(session=mock_session)
|
|
397
|
+
result = job.transform(sample_source_data)
|
|
398
|
+
|
|
399
|
+
# All stocks should have the same update date
|
|
400
|
+
unique_dates = result["update_date"].unique()
|
|
401
|
+
assert len(unique_dates) == 1
|
|
402
|
+
assert unique_dates[0] == "2018-11-26"
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def test_hs300stock_code_format(mock_session, sample_source_data):
|
|
406
|
+
"""Test that codes follow sh./sz.XXXXXX format"""
|
|
407
|
+
job = HS300Stock(session=mock_session)
|
|
408
|
+
result = job.transform(sample_source_data)
|
|
409
|
+
|
|
410
|
+
# All codes should start with "sh." or "sz."
|
|
411
|
+
for code in result["code"]:
|
|
412
|
+
assert code.startswith("sh.") or code.startswith("sz.")
|
|
413
|
+
assert len(code) == 9 # sh./sz. + 6 digits
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.source.baostock.stock.minuteline.constant import (
|
|
4
|
+
KEY,
|
|
5
|
+
NAME,
|
|
6
|
+
PAGINATE,
|
|
7
|
+
SOURCE,
|
|
8
|
+
TARGET,
|
|
9
|
+
)
|
|
10
|
+
from xfintech.data.source.baostock.stock.minuteline.minuteline import Minuteline
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Minuteline",
|
|
14
|
+
"KEY",
|
|
15
|
+
"NAME",
|
|
16
|
+
"PAGINATE",
|
|
17
|
+
"SOURCE",
|
|
18
|
+
"TARGET",
|
|
19
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.common.paginate import Paginate
|
|
4
|
+
from xfintech.fabric.column.info import ColumnInfo
|
|
5
|
+
from xfintech.fabric.column.kind import ColumnKind
|
|
6
|
+
from xfintech.fabric.table.info import TableInfo
|
|
7
|
+
|
|
8
|
+
PROVIDER = "baostock"
|
|
9
|
+
SOURCE_NAME = "query_history_k_data_plus"
|
|
10
|
+
URL = "http://www.baostock.com/mainContent?file=stockKData.md"
|
|
11
|
+
ARGS = {
|
|
12
|
+
"code": {
|
|
13
|
+
"type": ColumnKind.STRING,
|
|
14
|
+
"required": "N",
|
|
15
|
+
"desc": "股票代码(支持多个股票同时提取,逗号分隔)",
|
|
16
|
+
},
|
|
17
|
+
"start": {
|
|
18
|
+
"type": ColumnKind.STRING,
|
|
19
|
+
"required": "N",
|
|
20
|
+
"desc": "开始日期(YYYYMMDD)",
|
|
21
|
+
},
|
|
22
|
+
"end": {
|
|
23
|
+
"type": ColumnKind.STRING,
|
|
24
|
+
"required": "N",
|
|
25
|
+
"desc": "结束日期(YYYYMMDD)",
|
|
26
|
+
},
|
|
27
|
+
"frequency": {
|
|
28
|
+
"type": ColumnKind.STRING,
|
|
29
|
+
"required": "N",
|
|
30
|
+
"desc": "5=5分钟、15=15分钟、30=30分钟、60=60分钟",
|
|
31
|
+
},
|
|
32
|
+
"adjustflag": {
|
|
33
|
+
"type": ColumnKind.STRING,
|
|
34
|
+
"required": "N",
|
|
35
|
+
"desc": "复权类型,默认不复权:3;1:后复权;2:前复权",
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Exported constants
|
|
40
|
+
NAME = "minuteline"
|
|
41
|
+
KEY = "/baostock/minuteline"
|
|
42
|
+
PAGINATE = Paginate(
|
|
43
|
+
pagesize=10000,
|
|
44
|
+
pagelimit=100,
|
|
45
|
+
)
|
|
46
|
+
SOURCE = TableInfo(
|
|
47
|
+
desc="A股分钟线行情数据(BaoStock格式)",
|
|
48
|
+
meta={
|
|
49
|
+
"provider": PROVIDER,
|
|
50
|
+
"source": SOURCE_NAME,
|
|
51
|
+
"url": URL,
|
|
52
|
+
"args": ARGS,
|
|
53
|
+
"type": "partitioned",
|
|
54
|
+
"scale": "individual",
|
|
55
|
+
},
|
|
56
|
+
columns=[
|
|
57
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="证券代码 格式:sh.600000。sh:上海,sz:深圳"),
|
|
58
|
+
ColumnInfo(name="date", kind=ColumnKind.STRING, desc="交易日期: 格式:YYYY-MM-DD"),
|
|
59
|
+
ColumnInfo(name="time", kind=ColumnKind.STRING, desc="交易时间, 格式:YYYYMMDDHHMMSSsss"),
|
|
60
|
+
ColumnInfo(name="open", kind=ColumnKind.FLOAT, desc="开盘价"),
|
|
61
|
+
ColumnInfo(name="high", kind=ColumnKind.FLOAT, desc="最高价"),
|
|
62
|
+
ColumnInfo(name="low", kind=ColumnKind.FLOAT, desc="最低价"),
|
|
63
|
+
ColumnInfo(name="close", kind=ColumnKind.FLOAT, desc="收盘价"),
|
|
64
|
+
ColumnInfo(name="volume", kind=ColumnKind.FLOAT, desc="成交量(股)"),
|
|
65
|
+
ColumnInfo(name="amount", kind=ColumnKind.FLOAT, desc="成交额(元)"),
|
|
66
|
+
ColumnInfo(name="adjustflag", kind=ColumnKind.STRING, desc="复权状态: 不复权、前复权、后复权"),
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
TARGET = TableInfo(
|
|
70
|
+
desc="A股分钟线行情数据(xfintech格式)",
|
|
71
|
+
meta={
|
|
72
|
+
"key": KEY,
|
|
73
|
+
"name": NAME,
|
|
74
|
+
"type": "partitioned",
|
|
75
|
+
"scale": "individual",
|
|
76
|
+
},
|
|
77
|
+
columns=[
|
|
78
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="股票代码"),
|
|
79
|
+
ColumnInfo(name="date", kind=ColumnKind.STRING, desc="交易日期: 格式:YYYY-MM-DD"),
|
|
80
|
+
ColumnInfo(name="time", kind=ColumnKind.STRING, desc="交易时间代码: 格式:YYYYMMDDHHMMSSsss"),
|
|
81
|
+
ColumnInfo(name="open", kind=ColumnKind.FLOAT, desc="开盘价"),
|
|
82
|
+
ColumnInfo(name="high", kind=ColumnKind.FLOAT, desc="最高价"),
|
|
83
|
+
ColumnInfo(name="low", kind=ColumnKind.FLOAT, desc="最低价"),
|
|
84
|
+
ColumnInfo(name="close", kind=ColumnKind.FLOAT, desc="收盘价"),
|
|
85
|
+
ColumnInfo(name="volume", kind=ColumnKind.FLOAT, desc="成交量(股)"),
|
|
86
|
+
ColumnInfo(name="amount", kind=ColumnKind.FLOAT, desc="成交额(元)"),
|
|
87
|
+
ColumnInfo(name="adjustflag", kind=ColumnKind.STRING, desc="复权状态: 不复权、前复权、后复权"),
|
|
88
|
+
],
|
|
89
|
+
)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from xfintech.data.common.cache import Cache
|
|
8
|
+
from xfintech.data.common.coolant import Coolant
|
|
9
|
+
from xfintech.data.common.params import Params
|
|
10
|
+
from xfintech.data.common.retry import Retry
|
|
11
|
+
from xfintech.data.job import JobHouse
|
|
12
|
+
from xfintech.data.source.baostock.job import BaostockJob
|
|
13
|
+
from xfintech.data.source.baostock.session.session import Session
|
|
14
|
+
from xfintech.data.source.baostock.stock.minuteline.constant import (
|
|
15
|
+
KEY,
|
|
16
|
+
NAME,
|
|
17
|
+
PAGINATE,
|
|
18
|
+
SOURCE,
|
|
19
|
+
TARGET,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@JobHouse.register(KEY, alias=KEY)
|
|
24
|
+
class Minuteline(BaostockJob):
|
|
25
|
+
"""
|
|
26
|
+
描述:
|
|
27
|
+
- 获取A股分钟线行情数据
|
|
28
|
+
- 支持5分钟、15分钟、30分钟、60分钟K线数据
|
|
29
|
+
- 支持前复权、后复权、不复权三种复权方式
|
|
30
|
+
- API文档: http://www.baostock.com/mainContent?file=stockKData.md
|
|
31
|
+
- SCALE: Individual
|
|
32
|
+
- TYPE: Partitioned
|
|
33
|
+
- PAGINATE: 10000 rows / 100 pages
|
|
34
|
+
|
|
35
|
+
属性:
|
|
36
|
+
- name: str, 作业名称 'minuteline'。
|
|
37
|
+
- key: str, 作业键 '/baostock/minuteline'。
|
|
38
|
+
- session: Session, Baostock会话对象。
|
|
39
|
+
- source: TableInfo, 源表信息(Baostock原始格式)。
|
|
40
|
+
- target: TableInfo, 目标表信息(转换后格式)。
|
|
41
|
+
- params: Params, 查询参数。
|
|
42
|
+
- code: str, 必需, 股票代码(如 "sh.600000" 或 "sz.000001")
|
|
43
|
+
- start_date: str, 可选, 开始日期(YYYY-MM-DD 或 YYYYMMDD)
|
|
44
|
+
- end_date: str, 可选, 结束日期(YYYY-MM-DD 或 YYYYMMDD)
|
|
45
|
+
- frequency: str, 可选, 频率(5/15/30/60),默认5
|
|
46
|
+
- adjustflag: str, 可选, 复权类型(1=后复权/2=前复权/3=不复权),默认3
|
|
47
|
+
- coolant: Coolant, 请求冷却控制。
|
|
48
|
+
- paginate: Paginate, 分页控制(pagesize=10000, pagelimit=100)。
|
|
49
|
+
- retry: Retry, 重试策略。
|
|
50
|
+
- cache: Cache, 缓存管理。
|
|
51
|
+
|
|
52
|
+
方法:
|
|
53
|
+
- run(): 执行作业,返回分钟线行情DataFrame。
|
|
54
|
+
- _run(): 内部执行逻辑,处理日期参数并调用API。
|
|
55
|
+
- transform(data): 转换数据格式,将源格式转为目标格式。
|
|
56
|
+
|
|
57
|
+
例子:
|
|
58
|
+
```python
|
|
59
|
+
from xfintech.data.source.baostock.session import Session
|
|
60
|
+
from xfintech.data.source.baostock.stock.minuteline import Minuteline
|
|
61
|
+
|
|
62
|
+
session = Session()
|
|
63
|
+
job = Minuteline(
|
|
64
|
+
session=session,
|
|
65
|
+
params={
|
|
66
|
+
"code": "sh.600000",
|
|
67
|
+
"start_date": "20260101",
|
|
68
|
+
"end_date": "20260131",
|
|
69
|
+
"frequency": "5",
|
|
70
|
+
"adjustflag": "3"
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
df = job.run()
|
|
74
|
+
```
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
session: Session,
|
|
80
|
+
params: Optional[Params | Dict[str, Any]] = None,
|
|
81
|
+
coolant: Optional[Coolant | Dict[str, Any]] = None,
|
|
82
|
+
retry: Optional[Retry | Dict[str, Any]] = None,
|
|
83
|
+
cache: Optional[Cache | Dict[str, str] | bool] = None,
|
|
84
|
+
) -> None:
|
|
85
|
+
super().__init__(
|
|
86
|
+
name=NAME,
|
|
87
|
+
key=KEY,
|
|
88
|
+
session=session,
|
|
89
|
+
source=SOURCE,
|
|
90
|
+
target=TARGET,
|
|
91
|
+
params=params,
|
|
92
|
+
coolant=coolant,
|
|
93
|
+
paginate=PAGINATE,
|
|
94
|
+
retry=retry,
|
|
95
|
+
cache=cache,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def _run(self) -> pd.DataFrame:
|
|
99
|
+
cached = self._load_cache()
|
|
100
|
+
if cached is not None:
|
|
101
|
+
return cached
|
|
102
|
+
|
|
103
|
+
payload = self.params.to_dict()
|
|
104
|
+
payload = self._parse_string_params(
|
|
105
|
+
payload,
|
|
106
|
+
keys=[
|
|
107
|
+
"code",
|
|
108
|
+
"frequency",
|
|
109
|
+
"adjustflag",
|
|
110
|
+
],
|
|
111
|
+
)
|
|
112
|
+
payload = self._parse_date_params(
|
|
113
|
+
payload,
|
|
114
|
+
keys=["start_date", "end_date"],
|
|
115
|
+
)
|
|
116
|
+
fields = self.source.list_column_names()
|
|
117
|
+
payload["fields"] = ",".join(fields)
|
|
118
|
+
|
|
119
|
+
# Fetch data from API
|
|
120
|
+
data = self._fetchall(
|
|
121
|
+
api=self.connection.query_history_k_data_plus,
|
|
122
|
+
**payload,
|
|
123
|
+
)
|
|
124
|
+
result = self.transform(data)
|
|
125
|
+
self._save_cache(result)
|
|
126
|
+
return result
|
|
127
|
+
|
|
128
|
+
# Transform logic
|
|
129
|
+
def transform(
|
|
130
|
+
self,
|
|
131
|
+
data: pd.DataFrame,
|
|
132
|
+
) -> pd.DataFrame:
|
|
133
|
+
cols = self.target.list_column_names()
|
|
134
|
+
if data is None or data.empty:
|
|
135
|
+
return pd.DataFrame(columns=cols)
|
|
136
|
+
|
|
137
|
+
out = data.copy()
|
|
138
|
+
out["code"] = out["code"].astype(str)
|
|
139
|
+
out["date"] = out["date"].astype(str)
|
|
140
|
+
out["time"] = out["time"].astype(str)
|
|
141
|
+
out["adjustflag"] = out["adjustflag"].astype(str)
|
|
142
|
+
|
|
143
|
+
# Convert numeric fields
|
|
144
|
+
numeric_fields = [
|
|
145
|
+
"open",
|
|
146
|
+
"high",
|
|
147
|
+
"low",
|
|
148
|
+
"close",
|
|
149
|
+
"volume",
|
|
150
|
+
"amount",
|
|
151
|
+
]
|
|
152
|
+
for field in numeric_fields:
|
|
153
|
+
out[field] = pd.to_numeric(
|
|
154
|
+
out[field],
|
|
155
|
+
errors="coerce",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Finalize output
|
|
159
|
+
out = out[cols].drop_duplicates()
|
|
160
|
+
out = out.sort_values(by=["code", "date", "time"])
|
|
161
|
+
out = out.reset_index(drop=True)
|
|
162
|
+
self.markpoint("transform[OK]")
|
|
163
|
+
return out
|
|
File without changes
|