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,617 @@
|
|
|
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.stockinfo.constant import (
|
|
11
|
+
KEY,
|
|
12
|
+
NAME,
|
|
13
|
+
SOURCE,
|
|
14
|
+
TARGET,
|
|
15
|
+
)
|
|
16
|
+
from xfintech.data.source.baostock.stock.stockinfo.stockinfo import StockInfo
|
|
17
|
+
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# Fixtures
|
|
20
|
+
# ============================================================================
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def mock_session():
|
|
25
|
+
"""Create a mock Baostock session"""
|
|
26
|
+
session = MagicMock(spec=Session)
|
|
27
|
+
session._credential = None
|
|
28
|
+
session.id = "test1234"
|
|
29
|
+
session.mode = "direct"
|
|
30
|
+
session.relay_url = None
|
|
31
|
+
session.relay_secret = None
|
|
32
|
+
session.connected = True
|
|
33
|
+
|
|
34
|
+
# Mock the connection object
|
|
35
|
+
mock_connection = MagicMock()
|
|
36
|
+
mock_connection.query_stock_basic = MagicMock()
|
|
37
|
+
session.connection = mock_connection
|
|
38
|
+
|
|
39
|
+
return session
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def sample_source_data():
|
|
44
|
+
"""Create sample source data in Baostock format"""
|
|
45
|
+
return pd.DataFrame(
|
|
46
|
+
{
|
|
47
|
+
"code": ["sh.600000", "sz.000001", "sh.000001"],
|
|
48
|
+
"code_name": ["浦发银行", "平安银行", "上证指数"],
|
|
49
|
+
"ipoDate": ["1999-11-10", "1991-04-03", ""],
|
|
50
|
+
"outDate": ["", "", ""],
|
|
51
|
+
"type": ["1", "1", "2"],
|
|
52
|
+
"status": ["1", "1", "1"],
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# ============================================================================
|
|
58
|
+
# Initialization Tests
|
|
59
|
+
# ============================================================================
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_stockinfo_initialization_basic(mock_session):
|
|
63
|
+
"""Test basic initialization"""
|
|
64
|
+
job = StockInfo(session=mock_session)
|
|
65
|
+
|
|
66
|
+
assert job.name == NAME
|
|
67
|
+
assert job.key == KEY
|
|
68
|
+
assert job.source == SOURCE
|
|
69
|
+
assert job.target == TARGET
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_stockinfo_initialization_with_params(mock_session):
|
|
73
|
+
"""Test initialization with params"""
|
|
74
|
+
params = {"code": "sh.600000"}
|
|
75
|
+
job = StockInfo(session=mock_session, params=params)
|
|
76
|
+
|
|
77
|
+
assert job.params.code == "sh.600000"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_stockinfo_initialization_with_all_components(mock_session):
|
|
81
|
+
"""Test initialization with all components"""
|
|
82
|
+
params = {"code": "sh.600000"}
|
|
83
|
+
coolant = Coolant(interval=0.2)
|
|
84
|
+
retry = Retry(retry=3)
|
|
85
|
+
cache = Cache(path="/tmp/test_cache")
|
|
86
|
+
|
|
87
|
+
job = StockInfo(
|
|
88
|
+
session=mock_session,
|
|
89
|
+
params=params,
|
|
90
|
+
coolant=coolant,
|
|
91
|
+
retry=retry,
|
|
92
|
+
cache=cache,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert job.params.code == "sh.600000"
|
|
96
|
+
assert job.coolant.interval == 0.2
|
|
97
|
+
assert job.retry.retry == 3
|
|
98
|
+
assert job.cache is not None
|
|
99
|
+
assert isinstance(job.cache, Cache)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_stockinfo_name_and_key():
|
|
103
|
+
"""Test name and key constants"""
|
|
104
|
+
assert NAME == "stockinfo"
|
|
105
|
+
assert KEY == "/baostock/stockinfo"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_stockinfo_source_schema():
|
|
109
|
+
"""Test source schema has all required columns"""
|
|
110
|
+
assert SOURCE is not None
|
|
111
|
+
assert "证券基本资料" in SOURCE.desc
|
|
112
|
+
|
|
113
|
+
column_names = SOURCE.columns
|
|
114
|
+
assert "code" in column_names
|
|
115
|
+
assert "code_name" in column_names
|
|
116
|
+
assert "ipodate" in column_names
|
|
117
|
+
assert "outdate" in column_names
|
|
118
|
+
assert "type" in column_names
|
|
119
|
+
assert "status" in column_names
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_stockinfo_target_schema():
|
|
123
|
+
"""Test target schema has all required columns"""
|
|
124
|
+
assert TARGET is not None
|
|
125
|
+
assert "证券基本资料" in TARGET.desc
|
|
126
|
+
|
|
127
|
+
column_names = TARGET.columns
|
|
128
|
+
assert "code" in column_names
|
|
129
|
+
assert "name" in column_names
|
|
130
|
+
assert "ipo_date" in column_names
|
|
131
|
+
assert "delist_date" in column_names
|
|
132
|
+
assert "security_type" in column_names
|
|
133
|
+
assert "list_status" in column_names
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ============================================================================
|
|
137
|
+
# Transform Tests
|
|
138
|
+
# ============================================================================
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_stockinfo_transform_basic(mock_session, sample_source_data):
|
|
142
|
+
"""Test basic data transformation"""
|
|
143
|
+
job = StockInfo(session=mock_session)
|
|
144
|
+
result = job.transform(sample_source_data)
|
|
145
|
+
|
|
146
|
+
assert len(result) == 3
|
|
147
|
+
assert "code" in result.columns
|
|
148
|
+
assert "name" in result.columns
|
|
149
|
+
assert "ipo_date" in result.columns
|
|
150
|
+
assert result.iloc[0]["code"] == "sh.000001"
|
|
151
|
+
assert result.iloc[0]["name"] == "上证指数"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_stockinfo_transform_field_types(mock_session, sample_source_data):
|
|
155
|
+
"""Test field type conversions"""
|
|
156
|
+
job = StockInfo(session=mock_session)
|
|
157
|
+
result = job.transform(sample_source_data)
|
|
158
|
+
|
|
159
|
+
# Check string fields
|
|
160
|
+
assert isinstance(result.iloc[0]["code"], str)
|
|
161
|
+
assert isinstance(result.iloc[0]["name"], str)
|
|
162
|
+
assert isinstance(result.iloc[0]["ipo_date"], str)
|
|
163
|
+
assert isinstance(result.iloc[0]["delist_date"], str)
|
|
164
|
+
|
|
165
|
+
# Check integer fields
|
|
166
|
+
assert pd.api.types.is_integer_dtype(result["security_type"])
|
|
167
|
+
assert pd.api.types.is_integer_dtype(result["list_status"])
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def test_stockinfo_transform_field_mapping(mock_session, sample_source_data):
|
|
171
|
+
"""Test field name mappings"""
|
|
172
|
+
job = StockInfo(session=mock_session)
|
|
173
|
+
result = job.transform(sample_source_data)
|
|
174
|
+
|
|
175
|
+
# Verify field mappings
|
|
176
|
+
row = result[result["code"] == "sh.600000"].iloc[0]
|
|
177
|
+
assert row["name"] == "浦发银行" # from code_name
|
|
178
|
+
assert row["ipo_date"] == "1999-11-10" # from ipoDate
|
|
179
|
+
assert row["delist_date"] == "" # from outDate
|
|
180
|
+
assert row["security_type"] == 1 # from type
|
|
181
|
+
assert row["list_status"] == 1 # from status
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def test_stockinfo_transform_security_types(mock_session):
|
|
185
|
+
"""Test security type conversions"""
|
|
186
|
+
data = pd.DataFrame(
|
|
187
|
+
{
|
|
188
|
+
"code": ["sh.600000", "sh.000001", "sh.000300", "sh.113001", "sh.510050"],
|
|
189
|
+
"code_name": ["股票", "指数", "其它", "可转债", "ETF"],
|
|
190
|
+
"ipoDate": ["2000-01-01"] * 5,
|
|
191
|
+
"outDate": [""] * 5,
|
|
192
|
+
"type": ["1", "2", "3", "4", "5"],
|
|
193
|
+
"status": ["1"] * 5,
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
job = StockInfo(session=mock_session)
|
|
197
|
+
result = job.transform(data)
|
|
198
|
+
|
|
199
|
+
# Result is sorted by code, so check by code instead of position
|
|
200
|
+
assert result[result["code"] == "sh.600000"].iloc[0]["security_type"] == 1 # 股票
|
|
201
|
+
assert result[result["code"] == "sh.000001"].iloc[0]["security_type"] == 2 # 指数
|
|
202
|
+
assert result[result["code"] == "sh.000300"].iloc[0]["security_type"] == 3 # 其它
|
|
203
|
+
assert result[result["code"] == "sh.113001"].iloc[0]["security_type"] == 4 # 可转债
|
|
204
|
+
assert result[result["code"] == "sh.510050"].iloc[0]["security_type"] == 5 # ETF
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def test_stockinfo_transform_list_status(mock_session):
|
|
208
|
+
"""Test list status conversions"""
|
|
209
|
+
data = pd.DataFrame(
|
|
210
|
+
{
|
|
211
|
+
"code": ["sh.600000", "sh.600001"],
|
|
212
|
+
"code_name": ["上市股票", "退市股票"],
|
|
213
|
+
"ipoDate": ["2000-01-01", "2000-01-01"],
|
|
214
|
+
"outDate": ["", "2020-01-01"],
|
|
215
|
+
"type": ["1", "1"],
|
|
216
|
+
"status": ["1", "0"],
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
job = StockInfo(session=mock_session)
|
|
220
|
+
result = job.transform(data)
|
|
221
|
+
|
|
222
|
+
assert result.iloc[0]["list_status"] == 1 # 上市
|
|
223
|
+
assert result.iloc[1]["list_status"] == 0 # 退市
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_stockinfo_transform_empty_data(mock_session):
|
|
227
|
+
"""Test transform with empty data"""
|
|
228
|
+
job = StockInfo(session=mock_session)
|
|
229
|
+
|
|
230
|
+
# Test with None
|
|
231
|
+
result = job.transform(None)
|
|
232
|
+
assert result.empty
|
|
233
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
234
|
+
|
|
235
|
+
# Test with empty DataFrame
|
|
236
|
+
empty_df = pd.DataFrame()
|
|
237
|
+
result = job.transform(empty_df)
|
|
238
|
+
assert result.empty
|
|
239
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def test_stockinfo_transform_duplicate_removal(mock_session):
|
|
243
|
+
"""Test that duplicates are removed"""
|
|
244
|
+
data = pd.DataFrame(
|
|
245
|
+
{
|
|
246
|
+
"code": ["sh.600000", "sh.600000", "sz.000001"],
|
|
247
|
+
"code_name": ["浦发银行", "浦发银行", "平安银行"],
|
|
248
|
+
"ipoDate": ["1999-11-10", "1999-11-10", "1991-04-03"],
|
|
249
|
+
"outDate": ["", "", ""],
|
|
250
|
+
"type": ["1", "1", "1"],
|
|
251
|
+
"status": ["1", "1", "1"],
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
job = StockInfo(session=mock_session)
|
|
255
|
+
result = job.transform(data)
|
|
256
|
+
|
|
257
|
+
# Duplicates should be removed
|
|
258
|
+
assert len(result) == 2
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_stockinfo_transform_sorting(mock_session):
|
|
262
|
+
"""Test that result is sorted by code"""
|
|
263
|
+
data = pd.DataFrame(
|
|
264
|
+
{
|
|
265
|
+
"code": ["sz.000002", "sh.600000", "sz.000001"],
|
|
266
|
+
"code_name": ["万科A", "浦发银行", "平安银行"],
|
|
267
|
+
"ipoDate": ["1991-01-29", "1999-11-10", "1991-04-03"],
|
|
268
|
+
"outDate": ["", "", ""],
|
|
269
|
+
"type": ["1", "1", "1"],
|
|
270
|
+
"status": ["1", "1", "1"],
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
job = StockInfo(session=mock_session)
|
|
274
|
+
result = job.transform(data)
|
|
275
|
+
|
|
276
|
+
# Should be sorted by code
|
|
277
|
+
assert result.iloc[0]["code"] == "sh.600000"
|
|
278
|
+
assert result.iloc[1]["code"] == "sz.000001"
|
|
279
|
+
assert result.iloc[2]["code"] == "sz.000002"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_stockinfo_transform_invalid_types(mock_session):
|
|
283
|
+
"""Test transform with invalid type/status values"""
|
|
284
|
+
data = pd.DataFrame(
|
|
285
|
+
{
|
|
286
|
+
"code": ["sh.600000", "sh.600001"],
|
|
287
|
+
"code_name": ["正常", "异常"],
|
|
288
|
+
"ipoDate": ["2000-01-01", "2000-01-01"],
|
|
289
|
+
"outDate": ["", ""],
|
|
290
|
+
"type": ["1", "invalid"],
|
|
291
|
+
"status": ["1", "invalid"],
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
job = StockInfo(session=mock_session)
|
|
295
|
+
result = job.transform(data)
|
|
296
|
+
|
|
297
|
+
# Invalid values should be converted to 0
|
|
298
|
+
assert result.iloc[0]["security_type"] == 1
|
|
299
|
+
assert result.iloc[1]["security_type"] == 0 # invalid -> 0
|
|
300
|
+
assert result.iloc[0]["list_status"] == 1
|
|
301
|
+
assert result.iloc[1]["list_status"] == 0 # invalid -> 0
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# ============================================================================
|
|
305
|
+
# Run Tests
|
|
306
|
+
# ============================================================================
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_stockinfo_run_basic(mock_session, sample_source_data):
|
|
310
|
+
"""Test basic run method"""
|
|
311
|
+
job = StockInfo(session=mock_session)
|
|
312
|
+
|
|
313
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
314
|
+
result = job.run()
|
|
315
|
+
|
|
316
|
+
assert isinstance(result, pd.DataFrame)
|
|
317
|
+
assert len(result) == 3
|
|
318
|
+
assert "code" in result.columns
|
|
319
|
+
assert "name" in result.columns
|
|
320
|
+
assert "security_type" in result.columns
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def test_stockinfo_run_with_code_param(mock_session, sample_source_data):
|
|
324
|
+
"""Test run with code parameter"""
|
|
325
|
+
filtered_data = sample_source_data[sample_source_data["code"] == "sh.600000"]
|
|
326
|
+
|
|
327
|
+
job = StockInfo(session=mock_session, params={"code": "sh.600000"})
|
|
328
|
+
|
|
329
|
+
with patch.object(job, "_fetchall", return_value=filtered_data) as mock_fetchall:
|
|
330
|
+
result = job.run()
|
|
331
|
+
|
|
332
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
333
|
+
assert call_kwargs["code"] == "sh.600000"
|
|
334
|
+
assert len(result) == 1
|
|
335
|
+
assert result["code"].iloc[0] == "sh.600000"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def test_stockinfo_run_with_code_name_param(mock_session, sample_source_data):
|
|
339
|
+
"""Test run with code_name parameter"""
|
|
340
|
+
filtered_data = sample_source_data[sample_source_data["code_name"].str.contains("浦发")]
|
|
341
|
+
|
|
342
|
+
job = StockInfo(session=mock_session, params={"code_name": "浦发"})
|
|
343
|
+
|
|
344
|
+
with patch.object(job, "_fetchall", return_value=filtered_data) as mock_fetchall:
|
|
345
|
+
job.run()
|
|
346
|
+
|
|
347
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
348
|
+
assert call_kwargs["code_name"] == "浦发"
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def test_stockinfo_run_calls_query_stock_basic(mock_session, sample_source_data):
|
|
352
|
+
"""Test that run calls query_stock_basic API"""
|
|
353
|
+
job = StockInfo(session=mock_session)
|
|
354
|
+
|
|
355
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
356
|
+
job.run()
|
|
357
|
+
|
|
358
|
+
# Verify that _fetchall was called with the correct API
|
|
359
|
+
assert mock_fetchall.call_count == 1
|
|
360
|
+
call_args = mock_fetchall.call_args
|
|
361
|
+
assert call_args[1]["api"] == job.connection.query_stock_basic
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def test_stockinfo_run_calls_transform(mock_session, sample_source_data):
|
|
365
|
+
"""Test that run calls transform"""
|
|
366
|
+
job = StockInfo(session=mock_session)
|
|
367
|
+
|
|
368
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
369
|
+
with patch.object(job, "transform", wraps=job.transform) as mock_transform:
|
|
370
|
+
job.run()
|
|
371
|
+
|
|
372
|
+
mock_transform.assert_called_once()
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# ============================================================================
|
|
376
|
+
# Cache Tests
|
|
377
|
+
# ============================================================================
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def test_stockinfo_cache_persistence(mock_session, sample_source_data):
|
|
381
|
+
"""Test that cache persists across runs"""
|
|
382
|
+
job = StockInfo(session=mock_session, cache=True)
|
|
383
|
+
|
|
384
|
+
with patch.object(job, "_load_cache", return_value=None) as mock_load:
|
|
385
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
386
|
+
# First run - fetches data and caches it
|
|
387
|
+
result1 = job.run()
|
|
388
|
+
assert mock_fetchall.call_count == 1
|
|
389
|
+
assert mock_load.call_count == 1
|
|
390
|
+
|
|
391
|
+
# Second run - _load_cache still returns None, so _fetchall called again
|
|
392
|
+
result2 = job.run()
|
|
393
|
+
assert mock_fetchall.call_count == 2
|
|
394
|
+
assert mock_load.call_count == 2
|
|
395
|
+
|
|
396
|
+
pd.testing.assert_frame_equal(result1, result2)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def test_stockinfo_params_identifier_uniqueness(mock_session):
|
|
400
|
+
"""Test that different params create different cache keys"""
|
|
401
|
+
job1 = StockInfo(session=mock_session, params={"code": "sh.600000"}, cache=True)
|
|
402
|
+
job2 = StockInfo(session=mock_session, params={"code": "sz.000001"}, cache=True)
|
|
403
|
+
|
|
404
|
+
assert job1.params.identifier != job2.params.identifier
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def test_stockinfo_without_cache(mock_session, sample_source_data):
|
|
408
|
+
"""Test that stockinfo works correctly without cache"""
|
|
409
|
+
job = StockInfo(session=mock_session, cache=False)
|
|
410
|
+
|
|
411
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
412
|
+
job.run()
|
|
413
|
+
job.run()
|
|
414
|
+
|
|
415
|
+
# Should fetch twice (no caching)
|
|
416
|
+
assert mock_fetchall.call_count == 2
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
# ============================================================================
|
|
420
|
+
# Integration Tests
|
|
421
|
+
# ============================================================================
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def test_stockinfo_full_workflow(mock_session, sample_source_data):
|
|
425
|
+
"""Test complete workflow from initialization to data retrieval"""
|
|
426
|
+
job = StockInfo(session=mock_session, params={"code": "sh.600000"})
|
|
427
|
+
|
|
428
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
429
|
+
result = job.run()
|
|
430
|
+
|
|
431
|
+
assert not result.empty
|
|
432
|
+
assert len(result) == 3
|
|
433
|
+
assert list(result.columns) == [
|
|
434
|
+
"code",
|
|
435
|
+
"name",
|
|
436
|
+
"ipo_date",
|
|
437
|
+
"delist_date",
|
|
438
|
+
"security_type",
|
|
439
|
+
"list_status",
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def test_stockinfo_with_large_dataset(mock_session):
|
|
444
|
+
"""Test handling of large dataset"""
|
|
445
|
+
# Create a large dataset
|
|
446
|
+
large_data = pd.DataFrame(
|
|
447
|
+
{
|
|
448
|
+
"code": [f"sh.{600000 + i}" for i in range(1000)],
|
|
449
|
+
"code_name": [f"股票{i}" for i in range(1000)],
|
|
450
|
+
"ipoDate": ["2000-01-01"] * 1000,
|
|
451
|
+
"outDate": [""] * 1000,
|
|
452
|
+
"type": ["1"] * 1000,
|
|
453
|
+
"status": ["1"] * 1000,
|
|
454
|
+
}
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
job = StockInfo(session=mock_session)
|
|
458
|
+
|
|
459
|
+
with patch.object(job, "_fetchall", return_value=large_data):
|
|
460
|
+
result = job.run()
|
|
461
|
+
|
|
462
|
+
assert len(result) == 1000
|
|
463
|
+
assert result.iloc[0]["code"] == "sh.600000"
|
|
464
|
+
assert result.iloc[-1]["code"] == "sh.600999"
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def test_stockinfo_with_mixed_security_types(mock_session):
|
|
468
|
+
"""Test handling different security types"""
|
|
469
|
+
data = pd.DataFrame(
|
|
470
|
+
{
|
|
471
|
+
"code": ["sh.600000", "sh.000001", "sh.113001"],
|
|
472
|
+
"code_name": ["浦发银行", "上证指数", "某转债"],
|
|
473
|
+
"ipoDate": ["1999-11-10", "", "2020-01-01"],
|
|
474
|
+
"outDate": ["", "", ""],
|
|
475
|
+
"type": ["1", "2", "4"],
|
|
476
|
+
"status": ["1", "1", "1"],
|
|
477
|
+
}
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
job = StockInfo(session=mock_session)
|
|
481
|
+
|
|
482
|
+
with patch.object(job, "_fetchall", return_value=data):
|
|
483
|
+
result = job.run()
|
|
484
|
+
|
|
485
|
+
assert len(result) == 3
|
|
486
|
+
# Check that all security types are preserved
|
|
487
|
+
assert 1 in result["security_type"].values # 股票
|
|
488
|
+
assert 2 in result["security_type"].values # 指数
|
|
489
|
+
assert 4 in result["security_type"].values # 可转债
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def test_stockinfo_with_empty_result_from_api(mock_session):
|
|
493
|
+
"""Test handling of empty result from API"""
|
|
494
|
+
empty_data = pd.DataFrame(columns=["code", "code_name", "ipoDate", "outDate", "type", "status"])
|
|
495
|
+
|
|
496
|
+
job = StockInfo(session=mock_session)
|
|
497
|
+
|
|
498
|
+
with patch.object(job, "_fetchall", return_value=empty_data):
|
|
499
|
+
result = job.run()
|
|
500
|
+
|
|
501
|
+
assert result.empty
|
|
502
|
+
assert list(result.columns) == [
|
|
503
|
+
"code",
|
|
504
|
+
"name",
|
|
505
|
+
"ipo_date",
|
|
506
|
+
"delist_date",
|
|
507
|
+
"security_type",
|
|
508
|
+
"list_status",
|
|
509
|
+
]
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
def test_stockinfo_with_delisted_stocks(mock_session):
|
|
513
|
+
"""Test handling of delisted stocks"""
|
|
514
|
+
data = pd.DataFrame(
|
|
515
|
+
{
|
|
516
|
+
"code": ["sh.600000", "sh.600001"],
|
|
517
|
+
"code_name": ["浦发银行", "退市股票"],
|
|
518
|
+
"ipoDate": ["1999-11-10", "2000-01-01"],
|
|
519
|
+
"outDate": ["", "2020-12-31"],
|
|
520
|
+
"type": ["1", "1"],
|
|
521
|
+
"status": ["1", "0"],
|
|
522
|
+
}
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
job = StockInfo(session=mock_session)
|
|
526
|
+
|
|
527
|
+
with patch.object(job, "_fetchall", return_value=data):
|
|
528
|
+
result = job.run()
|
|
529
|
+
|
|
530
|
+
assert len(result) == 2
|
|
531
|
+
# Check that delisted stock is marked correctly
|
|
532
|
+
delisted = result[result["code"] == "sh.600001"].iloc[0]
|
|
533
|
+
assert delisted["list_status"] == 0
|
|
534
|
+
assert delisted["delist_date"] == "2020-12-31"
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# ============================================================================
|
|
538
|
+
# List Methods Tests
|
|
539
|
+
# ============================================================================
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def test_stockinfo_list_codes(mock_session, sample_source_data):
|
|
543
|
+
"""Test list_codes method"""
|
|
544
|
+
job = StockInfo(session=mock_session)
|
|
545
|
+
|
|
546
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
547
|
+
codes = job.list_codes()
|
|
548
|
+
|
|
549
|
+
assert isinstance(codes, list)
|
|
550
|
+
assert len(codes) == 3
|
|
551
|
+
assert "sh.600000" in codes
|
|
552
|
+
assert "sz.000001" in codes
|
|
553
|
+
assert "sh.000001" in codes
|
|
554
|
+
# Should be sorted
|
|
555
|
+
assert codes == sorted(codes)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def test_stockinfo_list_names(mock_session, sample_source_data):
|
|
559
|
+
"""Test list_names method"""
|
|
560
|
+
job = StockInfo(session=mock_session)
|
|
561
|
+
|
|
562
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
563
|
+
names = job.list_names()
|
|
564
|
+
|
|
565
|
+
assert isinstance(names, list)
|
|
566
|
+
assert len(names) == 3
|
|
567
|
+
assert "浦发银行" in names
|
|
568
|
+
assert "平安银行" in names
|
|
569
|
+
assert "上证指数" in names
|
|
570
|
+
# Should be sorted
|
|
571
|
+
assert names == sorted(names)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def test_stockinfo_list_codes_with_duplicates(mock_session):
|
|
575
|
+
"""Test list_codes removes duplicates"""
|
|
576
|
+
data = pd.DataFrame(
|
|
577
|
+
{
|
|
578
|
+
"code": ["sh.600000", "sh.600000", "sz.000001"],
|
|
579
|
+
"code_name": ["浦发银行", "浦发银行", "平安银行"],
|
|
580
|
+
"ipoDate": ["1999-11-10", "1999-11-10", "1991-04-03"],
|
|
581
|
+
"outDate": ["", "", ""],
|
|
582
|
+
"type": ["1", "1", "1"],
|
|
583
|
+
"status": ["1", "1", "1"],
|
|
584
|
+
}
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
job = StockInfo(session=mock_session)
|
|
588
|
+
|
|
589
|
+
with patch.object(job, "_fetchall", return_value=data):
|
|
590
|
+
codes = job.list_codes()
|
|
591
|
+
|
|
592
|
+
# Should return unique codes only
|
|
593
|
+
assert len(codes) == 2
|
|
594
|
+
assert codes.count("sh.600000") == 1
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
def test_stockinfo_list_names_with_duplicates(mock_session):
|
|
598
|
+
"""Test list_names removes duplicates"""
|
|
599
|
+
data = pd.DataFrame(
|
|
600
|
+
{
|
|
601
|
+
"code": ["sh.600000", "sz.000001", "sz.000002"],
|
|
602
|
+
"code_name": ["浦发银行", "平安银行", "平安银行"],
|
|
603
|
+
"ipoDate": ["1999-11-10", "1991-04-03", "1991-01-29"],
|
|
604
|
+
"outDate": ["", "", ""],
|
|
605
|
+
"type": ["1", "1", "1"],
|
|
606
|
+
"status": ["1", "1", "1"],
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
job = StockInfo(session=mock_session)
|
|
611
|
+
|
|
612
|
+
with patch.object(job, "_fetchall", return_value=data):
|
|
613
|
+
names = job.list_names()
|
|
614
|
+
|
|
615
|
+
# Should return unique names only
|
|
616
|
+
assert len(names) == 2
|
|
617
|
+
assert names.count("平安银行") == 1
|
|
@@ -0,0 +1,49 @@
|
|
|
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_sz50_stocks"
|
|
10
|
+
URL = "http://www.baostock.com/mainContent?file=sz50Stock.md"
|
|
11
|
+
ARGS = {}
|
|
12
|
+
|
|
13
|
+
# Exported constants
|
|
14
|
+
NAME = "sz50stock"
|
|
15
|
+
KEY = "/baostock/sz50stock"
|
|
16
|
+
PAGINATE = Paginate(
|
|
17
|
+
pagesize=10000,
|
|
18
|
+
pagelimit=100,
|
|
19
|
+
)
|
|
20
|
+
SOURCE = TableInfo(
|
|
21
|
+
desc="上证50成分股(BaoStock格式)",
|
|
22
|
+
meta={
|
|
23
|
+
"provider": PROVIDER,
|
|
24
|
+
"source": SOURCE_NAME,
|
|
25
|
+
"url": URL,
|
|
26
|
+
"args": ARGS,
|
|
27
|
+
"type": "static",
|
|
28
|
+
"scale": "crosssection",
|
|
29
|
+
},
|
|
30
|
+
columns=[
|
|
31
|
+
ColumnInfo(name="updateDate", kind=ColumnKind.STRING, desc="更新日期"),
|
|
32
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="证券代码"),
|
|
33
|
+
ColumnInfo(name="code_name", kind=ColumnKind.STRING, desc="证券名称"),
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
TARGET = TableInfo(
|
|
37
|
+
desc="上证50成分股(xfintech格式)",
|
|
38
|
+
meta={
|
|
39
|
+
"key": KEY,
|
|
40
|
+
"name": NAME,
|
|
41
|
+
"type": "static",
|
|
42
|
+
"scale": "crosssection",
|
|
43
|
+
},
|
|
44
|
+
columns=[
|
|
45
|
+
ColumnInfo(name="update_date", kind=ColumnKind.STRING, desc="更新日期"),
|
|
46
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="证券代码"),
|
|
47
|
+
ColumnInfo(name="name", kind=ColumnKind.STRING, desc="证券名称"),
|
|
48
|
+
],
|
|
49
|
+
)
|