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,568 @@
|
|
|
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.tushare.session.session import Session
|
|
10
|
+
from xfintech.data.source.tushare.stock.conceptcapflowdc.conceptcapflowdc import (
|
|
11
|
+
ConceptCapflowDC,
|
|
12
|
+
)
|
|
13
|
+
from xfintech.data.source.tushare.stock.conceptcapflowdc.constant import (
|
|
14
|
+
KEY,
|
|
15
|
+
NAME,
|
|
16
|
+
SOURCE,
|
|
17
|
+
TARGET,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# ============================================================================
|
|
21
|
+
# Fixtures
|
|
22
|
+
# ============================================================================
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def mock_session():
|
|
27
|
+
"""Create a mock Tushare session"""
|
|
28
|
+
session = MagicMock(spec=Session)
|
|
29
|
+
session._credential = "test_token"
|
|
30
|
+
session.id = "test1234"
|
|
31
|
+
session.mode = "direct"
|
|
32
|
+
session.relay_url = None
|
|
33
|
+
session.relay_secret = None
|
|
34
|
+
session.connected = True
|
|
35
|
+
|
|
36
|
+
# Mock the connection object
|
|
37
|
+
mock_connection = MagicMock()
|
|
38
|
+
mock_connection.moneyflow_ind_dc = MagicMock()
|
|
39
|
+
session.connection = mock_connection
|
|
40
|
+
|
|
41
|
+
return session
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.fixture
|
|
45
|
+
def sample_source_data():
|
|
46
|
+
"""Create sample source data in Tushare format"""
|
|
47
|
+
return pd.DataFrame(
|
|
48
|
+
{
|
|
49
|
+
"trade_date": ["20241201", "20241201", "20241129"],
|
|
50
|
+
"content_type": ["概念", "行业", "概念"],
|
|
51
|
+
"ts_code": ["BK0420", "BK0425", "BK0430"],
|
|
52
|
+
"name": ["互联网服务", "证券", "软件开发"],
|
|
53
|
+
"pct_change": [6.28, 8.23, 8.28],
|
|
54
|
+
"close": [16883.55, 135249.80, 721.35],
|
|
55
|
+
"net_amount": [3056382208.00, 2875528704.00, 2733378816.00],
|
|
56
|
+
"net_amount_rate": [3.93, 4.64, 3.18],
|
|
57
|
+
"buy_elg_amount": [2500000000.00, 2300000000.00, 2200000000.00],
|
|
58
|
+
"buy_elg_amount_rate": [3.21, 3.72, 2.56],
|
|
59
|
+
"buy_lg_amount": [556382208.00, 575528704.00, 533378816.00],
|
|
60
|
+
"buy_lg_amount_rate": [0.72, 0.92, 0.62],
|
|
61
|
+
"buy_md_amount": [-100000000.00, -50000000.00, -80000000.00],
|
|
62
|
+
"buy_md_amount_rate": [-0.13, -0.08, -0.09],
|
|
63
|
+
"buy_sm_amount": [-150000000.00, -120000000.00, -140000000.00],
|
|
64
|
+
"buy_sm_amount_rate": [-0.19, -0.19, -0.16],
|
|
65
|
+
"buy_sm_amount_stock": ["腾讯控股", "中信证券", "用友网络"],
|
|
66
|
+
"rank": [1, 2, 3],
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# ============================================================================
|
|
72
|
+
# Initialization Tests
|
|
73
|
+
# ============================================================================
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class TestInitialization:
|
|
77
|
+
"""Test initialization and configuration"""
|
|
78
|
+
|
|
79
|
+
def test_init_basic(self, mock_session):
|
|
80
|
+
"""Test basic initialization"""
|
|
81
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
82
|
+
|
|
83
|
+
assert job.name == NAME
|
|
84
|
+
assert job.key == KEY
|
|
85
|
+
assert job.source == SOURCE
|
|
86
|
+
assert job.target == TARGET
|
|
87
|
+
assert job.paginate.pagesize == 5000
|
|
88
|
+
|
|
89
|
+
def test_init_with_params(self, mock_session):
|
|
90
|
+
"""Test initialization with params"""
|
|
91
|
+
params = {"ts_code": "BK0420", "start_date": "20240101"}
|
|
92
|
+
job = ConceptCapflowDC(session=mock_session, params=params)
|
|
93
|
+
|
|
94
|
+
assert job.params.ts_code == "BK0420"
|
|
95
|
+
assert job.params.start_date == "20240101"
|
|
96
|
+
|
|
97
|
+
def test_init_with_all_components(self, mock_session):
|
|
98
|
+
"""Test initialization with all components"""
|
|
99
|
+
params = {"ts_code": "BK0420"}
|
|
100
|
+
coolant = Coolant(interval=0.2)
|
|
101
|
+
retry = Retry(retry=3)
|
|
102
|
+
cache = Cache(path="/tmp/test_cache")
|
|
103
|
+
|
|
104
|
+
job = ConceptCapflowDC(
|
|
105
|
+
session=mock_session,
|
|
106
|
+
params=params,
|
|
107
|
+
coolant=coolant,
|
|
108
|
+
retry=retry,
|
|
109
|
+
cache=cache,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
assert job.params.ts_code == "BK0420"
|
|
113
|
+
assert job.coolant.interval == 0.2
|
|
114
|
+
assert job.retry.retry == 3
|
|
115
|
+
assert job.cache is not None
|
|
116
|
+
assert isinstance(job.cache, Cache)
|
|
117
|
+
|
|
118
|
+
def test_name_constant(self):
|
|
119
|
+
"""Test name constant"""
|
|
120
|
+
assert NAME == "conceptcapflowdc"
|
|
121
|
+
|
|
122
|
+
def test_key_constant(self):
|
|
123
|
+
"""Test key constant"""
|
|
124
|
+
assert KEY == "/tushare/conceptcapflowdc"
|
|
125
|
+
|
|
126
|
+
def test_source_schema(self):
|
|
127
|
+
"""Test source schema has all required columns"""
|
|
128
|
+
assert SOURCE is not None
|
|
129
|
+
assert SOURCE.desc == "东财概念及行业板块资金流向数据(Tushare格式)"
|
|
130
|
+
|
|
131
|
+
column_names = SOURCE.columns
|
|
132
|
+
assert "ts_code" in column_names
|
|
133
|
+
assert "trade_date" in column_names
|
|
134
|
+
assert "content_type" in column_names
|
|
135
|
+
assert "name" in column_names
|
|
136
|
+
assert "pct_change" in column_names
|
|
137
|
+
assert "close" in column_names
|
|
138
|
+
assert "net_amount" in column_names
|
|
139
|
+
assert "net_amount_rate" in column_names
|
|
140
|
+
|
|
141
|
+
def test_target_schema(self):
|
|
142
|
+
"""Test target schema has all required columns"""
|
|
143
|
+
assert TARGET is not None
|
|
144
|
+
assert TARGET.desc == "东财概念及行业板块资金流向数据(xfintech标准格式)"
|
|
145
|
+
|
|
146
|
+
column_names = TARGET.columns
|
|
147
|
+
assert "code" in column_names
|
|
148
|
+
assert "date" in column_names
|
|
149
|
+
assert "datecode" in column_names
|
|
150
|
+
assert "content_type" in column_names
|
|
151
|
+
assert "name" in column_names
|
|
152
|
+
assert "percent_change" in column_names
|
|
153
|
+
assert "close" in column_names
|
|
154
|
+
assert "net_amount" in column_names
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ============================================================================
|
|
158
|
+
# Transform Tests
|
|
159
|
+
# ============================================================================
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class TestTransform:
|
|
163
|
+
"""Test data transformation"""
|
|
164
|
+
|
|
165
|
+
def test_transform_basic(self, mock_session, sample_source_data):
|
|
166
|
+
"""Test basic data transformation"""
|
|
167
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
168
|
+
result = job.transform(sample_source_data)
|
|
169
|
+
|
|
170
|
+
assert len(result) == 3
|
|
171
|
+
assert "code" in result.columns
|
|
172
|
+
assert "date" in result.columns
|
|
173
|
+
assert "datecode" in result.columns
|
|
174
|
+
# Data is sorted by code, so first row is BK0420
|
|
175
|
+
assert result.iloc[0]["code"] == "BK0420"
|
|
176
|
+
assert result.iloc[2]["datecode"] == "20241129"
|
|
177
|
+
|
|
178
|
+
def test_transform_date_conversion(self, mock_session, sample_source_data):
|
|
179
|
+
"""Test date field conversions"""
|
|
180
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
181
|
+
result = job.transform(sample_source_data)
|
|
182
|
+
|
|
183
|
+
# Check date format (YYYY-MM-DD) - sorted by code
|
|
184
|
+
assert result.iloc[0]["date"] == "2024-12-01" # BK0420
|
|
185
|
+
assert result.iloc[1]["date"] == "2024-12-01" # BK0425
|
|
186
|
+
assert result.iloc[2]["date"] == "2024-11-29" # BK0430
|
|
187
|
+
|
|
188
|
+
# Check datecode format (YYYYMMDD)
|
|
189
|
+
assert result.iloc[1]["datecode"] == "20241201"
|
|
190
|
+
|
|
191
|
+
def test_transform_field_mappings(self, mock_session, sample_source_data):
|
|
192
|
+
"""Test field mapping transformations"""
|
|
193
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
194
|
+
result = job.transform(sample_source_data)
|
|
195
|
+
|
|
196
|
+
# Use first row (BK0420) after sorting
|
|
197
|
+
row = result.iloc[0]
|
|
198
|
+
assert row["code"] == "BK0420"
|
|
199
|
+
assert row["name"] == "互联网服务"
|
|
200
|
+
assert row["content_type"] == "概念"
|
|
201
|
+
assert row["percent_change"] == 6.28
|
|
202
|
+
assert row["close"] == 16883.55
|
|
203
|
+
|
|
204
|
+
def test_transform_numeric_fields(self, mock_session, sample_source_data):
|
|
205
|
+
"""Test numeric field transformations"""
|
|
206
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
207
|
+
result = job.transform(sample_source_data)
|
|
208
|
+
|
|
209
|
+
row = result.iloc[0]
|
|
210
|
+
# Check all numeric fields are properly converted
|
|
211
|
+
assert pd.notna(row["close"])
|
|
212
|
+
assert pd.notna(row["percent_change"])
|
|
213
|
+
assert pd.notna(row["net_amount"])
|
|
214
|
+
assert pd.notna(row["net_amount_rate"])
|
|
215
|
+
assert pd.notna(row["buy_elg_amount"])
|
|
216
|
+
assert pd.notna(row["rank"])
|
|
217
|
+
|
|
218
|
+
def test_transform_empty_data(self, mock_session):
|
|
219
|
+
"""Test transform with empty data"""
|
|
220
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
221
|
+
|
|
222
|
+
# Test with None
|
|
223
|
+
result = job.transform(None)
|
|
224
|
+
assert result.empty
|
|
225
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
226
|
+
|
|
227
|
+
# Test with empty DataFrame
|
|
228
|
+
empty_df = pd.DataFrame()
|
|
229
|
+
result = job.transform(empty_df)
|
|
230
|
+
assert result.empty
|
|
231
|
+
assert len(result.columns) == len(TARGET.columns)
|
|
232
|
+
|
|
233
|
+
def test_transform_none_data(self, mock_session):
|
|
234
|
+
"""Test transform with None data"""
|
|
235
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
236
|
+
result = job.transform(None)
|
|
237
|
+
|
|
238
|
+
assert result.empty
|
|
239
|
+
assert list(result.columns) == TARGET.list_column_names()
|
|
240
|
+
|
|
241
|
+
def test_transform_invalid_data(self, mock_session):
|
|
242
|
+
"""Test transform with invalid numeric values"""
|
|
243
|
+
data = pd.DataFrame(
|
|
244
|
+
{
|
|
245
|
+
"trade_date": ["20241201", "invalid_date"],
|
|
246
|
+
"content_type": ["概念", "行业"],
|
|
247
|
+
"ts_code": ["BK0420", "BK0425"],
|
|
248
|
+
"name": ["互联网服务", "证券"],
|
|
249
|
+
"pct_change": [6.28, "invalid"],
|
|
250
|
+
"close": [16883.55, 135249.80],
|
|
251
|
+
"net_amount": [3056382208.00, 2875528704.00],
|
|
252
|
+
"net_amount_rate": [3.93, 4.64],
|
|
253
|
+
"buy_elg_amount": [2500000000.00, 2300000000.00],
|
|
254
|
+
"buy_elg_amount_rate": [3.21, 3.72],
|
|
255
|
+
"buy_lg_amount": [556382208.00, 575528704.00],
|
|
256
|
+
"buy_lg_amount_rate": [0.72, 0.92],
|
|
257
|
+
"buy_md_amount": [-100000000.00, -50000000.00],
|
|
258
|
+
"buy_md_amount_rate": [-0.13, -0.08],
|
|
259
|
+
"buy_sm_amount": [-150000000.00, -120000000.00],
|
|
260
|
+
"buy_sm_amount_rate": [-0.19, -0.19],
|
|
261
|
+
"buy_sm_amount_stock": ["腾讯控股", "中信证券"],
|
|
262
|
+
"rank": [1, 2],
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
266
|
+
result = job.transform(data)
|
|
267
|
+
|
|
268
|
+
# Should handle invalid data gracefully
|
|
269
|
+
assert len(result) == 2
|
|
270
|
+
assert pd.isna(result.iloc[1]["date"]) # Invalid date
|
|
271
|
+
assert pd.isna(result.iloc[1]["percent_change"]) # Invalid numeric
|
|
272
|
+
|
|
273
|
+
def test_transform_duplicates_removed(self, mock_session):
|
|
274
|
+
"""Test that duplicates are removed"""
|
|
275
|
+
data = pd.DataFrame(
|
|
276
|
+
{
|
|
277
|
+
"trade_date": ["20241201", "20241201", "20241201"],
|
|
278
|
+
"content_type": ["概念", "概念", "行业"],
|
|
279
|
+
"ts_code": ["BK0420", "BK0420", "BK0425"],
|
|
280
|
+
"name": ["互联网服务", "互联网服务", "证券"],
|
|
281
|
+
"pct_change": [6.28, 6.28, 8.23],
|
|
282
|
+
"close": [16883.55, 16883.55, 135249.80],
|
|
283
|
+
"net_amount": [3056382208.00, 3056382208.00, 2875528704.00],
|
|
284
|
+
"net_amount_rate": [3.93, 3.93, 4.64],
|
|
285
|
+
"buy_elg_amount": [2500000000.00, 2500000000.00, 2300000000.00],
|
|
286
|
+
"buy_elg_amount_rate": [3.21, 3.21, 3.72],
|
|
287
|
+
"buy_lg_amount": [556382208.00, 556382208.00, 575528704.00],
|
|
288
|
+
"buy_lg_amount_rate": [0.72, 0.72, 0.92],
|
|
289
|
+
"buy_md_amount": [-100000000.00, -100000000.00, -50000000.00],
|
|
290
|
+
"buy_md_amount_rate": [-0.13, -0.13, -0.08],
|
|
291
|
+
"buy_sm_amount": [-150000000.00, -150000000.00, -120000000.00],
|
|
292
|
+
"buy_sm_amount_rate": [-0.19, -0.19, -0.19],
|
|
293
|
+
"buy_sm_amount_stock": ["腾讯控股", "腾讯控股", "中信证券"],
|
|
294
|
+
"rank": [1, 1, 2],
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
298
|
+
result = job.transform(data)
|
|
299
|
+
|
|
300
|
+
# Duplicates should be removed
|
|
301
|
+
assert len(result) == 2
|
|
302
|
+
|
|
303
|
+
def test_transform_sorting(self, mock_session):
|
|
304
|
+
"""Test that result is sorted by code and date"""
|
|
305
|
+
data = pd.DataFrame(
|
|
306
|
+
{
|
|
307
|
+
"trade_date": ["20241115", "20241201", "20241129"],
|
|
308
|
+
"content_type": ["概念", "行业", "概念"],
|
|
309
|
+
"ts_code": ["BK0430", "BK0420", "BK0425"],
|
|
310
|
+
"name": ["软件开发", "互联网服务", "证券"],
|
|
311
|
+
"pct_change": [8.28, 6.28, 8.23],
|
|
312
|
+
"close": [721.35, 16883.55, 135249.80],
|
|
313
|
+
"net_amount": [2733378816.00, 3056382208.00, 2875528704.00],
|
|
314
|
+
"net_amount_rate": [3.18, 3.93, 4.64],
|
|
315
|
+
"buy_elg_amount": [2200000000.00, 2500000000.00, 2300000000.00],
|
|
316
|
+
"buy_elg_amount_rate": [2.56, 3.21, 3.72],
|
|
317
|
+
"buy_lg_amount": [533378816.00, 556382208.00, 575528704.00],
|
|
318
|
+
"buy_lg_amount_rate": [0.62, 0.72, 0.92],
|
|
319
|
+
"buy_md_amount": [-80000000.00, -100000000.00, -50000000.00],
|
|
320
|
+
"buy_md_amount_rate": [-0.09, -0.13, -0.08],
|
|
321
|
+
"buy_sm_amount": [-140000000.00, -150000000.00, -120000000.00],
|
|
322
|
+
"buy_sm_amount_rate": [-0.16, -0.19, -0.19],
|
|
323
|
+
"buy_sm_amount_stock": ["用友网络", "腾讯控股", "中信证券"],
|
|
324
|
+
"rank": [3, 1, 2],
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
328
|
+
result = job.transform(data)
|
|
329
|
+
|
|
330
|
+
# Should be sorted by code, then date
|
|
331
|
+
expected_order = ["BK0420", "BK0425", "BK0430"]
|
|
332
|
+
actual_order = result["code"].tolist()
|
|
333
|
+
assert actual_order == expected_order
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
# ============================================================================
|
|
337
|
+
# Run Tests
|
|
338
|
+
# ============================================================================
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
class TestRun:
|
|
342
|
+
"""Test execution logic"""
|
|
343
|
+
|
|
344
|
+
def test_run_basic(self, mock_session, sample_source_data):
|
|
345
|
+
"""Test basic run method"""
|
|
346
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
347
|
+
|
|
348
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
349
|
+
result = job.run()
|
|
350
|
+
|
|
351
|
+
assert isinstance(result, pd.DataFrame)
|
|
352
|
+
assert len(result) == 3
|
|
353
|
+
assert "code" in result.columns
|
|
354
|
+
assert "date" in result.columns
|
|
355
|
+
|
|
356
|
+
def test_run_with_params(self, mock_session, sample_source_data):
|
|
357
|
+
"""Test run with ts_code parameter"""
|
|
358
|
+
filtered_data = sample_source_data[sample_source_data["ts_code"] == "BK0420"]
|
|
359
|
+
|
|
360
|
+
job = ConceptCapflowDC(session=mock_session, params={"ts_code": "BK0420"})
|
|
361
|
+
|
|
362
|
+
with patch.object(job, "_fetchall", return_value=filtered_data):
|
|
363
|
+
result = job.run()
|
|
364
|
+
|
|
365
|
+
assert len(result) == 1
|
|
366
|
+
assert result["code"].iloc[0] == "BK0420"
|
|
367
|
+
|
|
368
|
+
def test_run_with_date_string(self, mock_session, sample_source_data):
|
|
369
|
+
"""Test run with trade_date as string"""
|
|
370
|
+
job = ConceptCapflowDC(session=mock_session, params={"trade_date": "20241201"})
|
|
371
|
+
|
|
372
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
373
|
+
job.run()
|
|
374
|
+
|
|
375
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
376
|
+
assert call_kwargs["trade_date"] == "20241201"
|
|
377
|
+
|
|
378
|
+
def test_run_with_date_datetime(self, mock_session, sample_source_data):
|
|
379
|
+
"""Test run with trade_date as datetime object"""
|
|
380
|
+
trade_date = "20241201"
|
|
381
|
+
job = ConceptCapflowDC(session=mock_session, params={"trade_date": trade_date})
|
|
382
|
+
|
|
383
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
384
|
+
job.run()
|
|
385
|
+
|
|
386
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
387
|
+
assert call_kwargs["trade_date"] == "20241201"
|
|
388
|
+
|
|
389
|
+
def test_run_with_date_date(self, mock_session, sample_source_data):
|
|
390
|
+
"""Test run with trade_date as date object (not datetime)"""
|
|
391
|
+
trade_date = "20241201"
|
|
392
|
+
job = ConceptCapflowDC(session=mock_session, params={"trade_date": trade_date})
|
|
393
|
+
|
|
394
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
395
|
+
job.run()
|
|
396
|
+
|
|
397
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
398
|
+
assert call_kwargs["trade_date"] == "20241201"
|
|
399
|
+
|
|
400
|
+
def test_run_with_date_range_string(self, mock_session, sample_source_data):
|
|
401
|
+
"""Test run with start_date and end_date as strings"""
|
|
402
|
+
job = ConceptCapflowDC(
|
|
403
|
+
session=mock_session,
|
|
404
|
+
params={"start_date": "20241101", "end_date": "20241231"},
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
408
|
+
job.run()
|
|
409
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
410
|
+
assert call_kwargs["start_date"] == "20241101"
|
|
411
|
+
assert call_kwargs["end_date"] == "20241231"
|
|
412
|
+
|
|
413
|
+
def test_run_calls_transform(self, mock_session, sample_source_data):
|
|
414
|
+
"""Test that run calls transform"""
|
|
415
|
+
job = ConceptCapflowDC(session=mock_session)
|
|
416
|
+
|
|
417
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
418
|
+
with patch.object(job, "transform", wraps=job.transform) as mock_transform:
|
|
419
|
+
job.run()
|
|
420
|
+
|
|
421
|
+
mock_transform.assert_called_once()
|
|
422
|
+
|
|
423
|
+
def test_run_with_content_type(self, mock_session, sample_source_data):
|
|
424
|
+
"""Test run with content_type parameter"""
|
|
425
|
+
job = ConceptCapflowDC(session=mock_session, params={"trade_date": "20241201", "content_type": "概念"})
|
|
426
|
+
|
|
427
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
428
|
+
job.run()
|
|
429
|
+
call_kwargs = mock_fetchall.call_args[1]
|
|
430
|
+
assert call_kwargs["trade_date"] == "20241201"
|
|
431
|
+
assert call_kwargs["content_type"] == "概念"
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
# ============================================================================
|
|
435
|
+
# Cache Tests
|
|
436
|
+
# ============================================================================
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
class TestCache:
|
|
440
|
+
"""Test caching behavior"""
|
|
441
|
+
|
|
442
|
+
def test_cache_persistence(self, mock_session, sample_source_data):
|
|
443
|
+
"""Test that cache persists across runs"""
|
|
444
|
+
job = ConceptCapflowDC(session=mock_session, cache=True)
|
|
445
|
+
|
|
446
|
+
with patch.object(job, "_load_cache", return_value=None) as mock_load:
|
|
447
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
448
|
+
# First run - fetches data and caches it
|
|
449
|
+
result1 = job.run()
|
|
450
|
+
assert mock_fetchall.call_count == 1
|
|
451
|
+
assert mock_load.call_count == 1
|
|
452
|
+
|
|
453
|
+
# Second run - _load_cache still returns None, so _fetchall called again
|
|
454
|
+
result2 = job.run()
|
|
455
|
+
assert mock_fetchall.call_count == 2
|
|
456
|
+
assert mock_load.call_count == 2
|
|
457
|
+
|
|
458
|
+
pd.testing.assert_frame_equal(result1, result2)
|
|
459
|
+
|
|
460
|
+
def test_run_without_cache(self, mock_session, sample_source_data):
|
|
461
|
+
"""Test that conceptcapflowdc works correctly without cache"""
|
|
462
|
+
job = ConceptCapflowDC(session=mock_session, cache=False)
|
|
463
|
+
|
|
464
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
|
|
465
|
+
job.run()
|
|
466
|
+
job.run()
|
|
467
|
+
|
|
468
|
+
# Should fetch twice (no caching)
|
|
469
|
+
assert mock_fetchall.call_count == 2
|
|
470
|
+
|
|
471
|
+
def test_params_identifier_uniqueness(self, mock_session):
|
|
472
|
+
"""Test that different params create different cache keys"""
|
|
473
|
+
job1 = ConceptCapflowDC(session=mock_session, params={"trade_date": "20241201"}, cache=True)
|
|
474
|
+
job2 = ConceptCapflowDC(session=mock_session, params={"trade_date": "20241129"}, cache=True)
|
|
475
|
+
|
|
476
|
+
assert job1.params.identifier != job2.params.identifier
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
# ============================================================================
|
|
480
|
+
# Integration Tests
|
|
481
|
+
# ============================================================================
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
class TestIntegration:
|
|
485
|
+
"""Test end-to-end workflows"""
|
|
486
|
+
|
|
487
|
+
def test_full_workflow(self, mock_session, sample_source_data):
|
|
488
|
+
"""Test complete workflow from initialization to data retrieval"""
|
|
489
|
+
job = ConceptCapflowDC(
|
|
490
|
+
session=mock_session,
|
|
491
|
+
params={
|
|
492
|
+
"ts_code": "BK0420",
|
|
493
|
+
"start_date": "20241101",
|
|
494
|
+
"end_date": "20241231",
|
|
495
|
+
},
|
|
496
|
+
cache=False,
|
|
497
|
+
)
|
|
498
|
+
|
|
499
|
+
with patch.object(job, "_fetchall", return_value=sample_source_data):
|
|
500
|
+
result = job.run()
|
|
501
|
+
assert len(result) > 0
|
|
502
|
+
|
|
503
|
+
def test_large_dataset_handling(self, mock_session):
|
|
504
|
+
"""Test handling of large datasets"""
|
|
505
|
+
# Create large dataset
|
|
506
|
+
large_data = pd.DataFrame(
|
|
507
|
+
{
|
|
508
|
+
"trade_date": ["20241201"] * 1000,
|
|
509
|
+
"content_type": ["概念"] * 1000,
|
|
510
|
+
"ts_code": [f"BK{str(i).zfill(4)}" for i in range(1000)],
|
|
511
|
+
"name": ["板块名称"] * 1000,
|
|
512
|
+
"pct_change": [5.00] * 1000,
|
|
513
|
+
"close": [10000.00] * 1000,
|
|
514
|
+
"net_amount": [1000000000.00] * 1000,
|
|
515
|
+
"net_amount_rate": [2.50] * 1000,
|
|
516
|
+
"buy_elg_amount": [800000000.00] * 1000,
|
|
517
|
+
"buy_elg_amount_rate": [2.00] * 1000,
|
|
518
|
+
"buy_lg_amount": [200000000.00] * 1000,
|
|
519
|
+
"buy_lg_amount_rate": [0.50] * 1000,
|
|
520
|
+
"buy_md_amount": [-50000000.00] * 1000,
|
|
521
|
+
"buy_md_amount_rate": [-0.12] * 1000,
|
|
522
|
+
"buy_sm_amount": [-100000000.00] * 1000,
|
|
523
|
+
"buy_sm_amount_rate": [-0.25] * 1000,
|
|
524
|
+
"buy_sm_amount_stock": ["领涨股"] * 1000,
|
|
525
|
+
"rank": list(range(1, 1001)),
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
job = ConceptCapflowDC(session=mock_session, cache=False)
|
|
530
|
+
|
|
531
|
+
with patch.object(job, "_fetchall", return_value=large_data):
|
|
532
|
+
result = job.run()
|
|
533
|
+
|
|
534
|
+
assert len(result) == 1000
|
|
535
|
+
|
|
536
|
+
def test_missing_fields_handling(self, mock_session):
|
|
537
|
+
"""Test handling of data with some missing fields"""
|
|
538
|
+
data = pd.DataFrame(
|
|
539
|
+
{
|
|
540
|
+
"trade_date": ["20241201"],
|
|
541
|
+
"content_type": ["概念"],
|
|
542
|
+
"ts_code": ["BK0420"],
|
|
543
|
+
"name": ["互联网服务"],
|
|
544
|
+
"pct_change": [None], # Missing data
|
|
545
|
+
"close": [16883.55],
|
|
546
|
+
"net_amount": [3056382208.00],
|
|
547
|
+
"net_amount_rate": [None], # Missing data
|
|
548
|
+
"buy_elg_amount": [2500000000.00],
|
|
549
|
+
"buy_elg_amount_rate": [3.21],
|
|
550
|
+
"buy_lg_amount": [556382208.00],
|
|
551
|
+
"buy_lg_amount_rate": [0.72],
|
|
552
|
+
"buy_md_amount": [-100000000.00],
|
|
553
|
+
"buy_md_amount_rate": [-0.13],
|
|
554
|
+
"buy_sm_amount": [-150000000.00],
|
|
555
|
+
"buy_sm_amount_rate": [-0.19],
|
|
556
|
+
"buy_sm_amount_stock": ["腾讯控股"],
|
|
557
|
+
"rank": [1],
|
|
558
|
+
}
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
job = ConceptCapflowDC(session=mock_session, cache=False)
|
|
562
|
+
|
|
563
|
+
with patch.object(job, "_fetchall", return_value=data):
|
|
564
|
+
result = job.run()
|
|
565
|
+
|
|
566
|
+
assert len(result) == 1
|
|
567
|
+
assert pd.isna(result.iloc[0]["percent_change"])
|
|
568
|
+
assert pd.isna(result.iloc[0]["net_amount_rate"])
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.source.tushare.stock.conceptcapflowths.conceptcapflowths import (
|
|
4
|
+
ConceptCapflowTHS,
|
|
5
|
+
)
|
|
6
|
+
from xfintech.data.source.tushare.stock.conceptcapflowths.constant import (
|
|
7
|
+
KEY,
|
|
8
|
+
NAME,
|
|
9
|
+
PAGINATE,
|
|
10
|
+
SOURCE,
|
|
11
|
+
TARGET,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"ConceptCapflowTHS",
|
|
16
|
+
"KEY",
|
|
17
|
+
"NAME",
|
|
18
|
+
"PAGINATE",
|
|
19
|
+
"SOURCE",
|
|
20
|
+
"TARGET",
|
|
21
|
+
]
|