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,576 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from xfintech.data.source.tushare.stock.techindex import (
|
|
10
|
+
KEY,
|
|
11
|
+
NAME,
|
|
12
|
+
PAGINATE,
|
|
13
|
+
SOURCE,
|
|
14
|
+
TARGET,
|
|
15
|
+
TechIndex,
|
|
16
|
+
)
|
|
17
|
+
from xfintech.data.source.tushare.stock.techindex.constant import (
|
|
18
|
+
_IN_BFQ_COLS,
|
|
19
|
+
_IN_HFQ_COLS,
|
|
20
|
+
_IN_MAIN_COLS,
|
|
21
|
+
_IN_QFQ_COLS,
|
|
22
|
+
_OUT_BA_COLS,
|
|
23
|
+
_OUT_FA_COLS,
|
|
24
|
+
_OUT_MAIN_COLS,
|
|
25
|
+
_OUT_NA_COLS,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ================= Fixtures =================
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def mock_session():
|
|
32
|
+
"""创建模拟的Tushare会话对象"""
|
|
33
|
+
session = MagicMock()
|
|
34
|
+
|
|
35
|
+
# Mock the connection object
|
|
36
|
+
mock_connection = MagicMock()
|
|
37
|
+
mock_connection.stk_factor_pro = MagicMock()
|
|
38
|
+
session.connection = mock_connection
|
|
39
|
+
|
|
40
|
+
return session
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def sample_tushare_data():
|
|
45
|
+
"""创建样本Tushare格式数据"""
|
|
46
|
+
return pd.DataFrame(
|
|
47
|
+
{
|
|
48
|
+
"ts_code": ["000001.SZ", "000002.SZ"],
|
|
49
|
+
"trade_date": ["20240101", "20240101"],
|
|
50
|
+
"open": [10.5, 20.3],
|
|
51
|
+
"high": [11.0, 21.0],
|
|
52
|
+
"low": [10.2, 20.0],
|
|
53
|
+
"close": [10.8, 20.8],
|
|
54
|
+
"change": [0.3, 0.5],
|
|
55
|
+
"pct_chg": [2.86, 2.46],
|
|
56
|
+
"vol": [100000.0, 200000.0],
|
|
57
|
+
"amount": [108000.0, 416000.0],
|
|
58
|
+
"turnover_rate": [1.5, 2.3],
|
|
59
|
+
"turnover_rate_f": [1.2, 2.0],
|
|
60
|
+
"volume_ratio": [1.1, 1.3],
|
|
61
|
+
"pe": [15.5, 18.2],
|
|
62
|
+
"pe_ttm": [16.0, 18.5],
|
|
63
|
+
"pb": [2.5, 3.2],
|
|
64
|
+
"ps": [3.5, 4.2],
|
|
65
|
+
"ps_ttm": [3.8, 4.5],
|
|
66
|
+
"dv_ratio": [2.5, 3.0],
|
|
67
|
+
"dv_ttm": [2.8, 3.2],
|
|
68
|
+
"total_share": [1000000.0, 2000000.0],
|
|
69
|
+
"float_share": [800000.0, 1600000.0],
|
|
70
|
+
"free_share": [700000.0, 1400000.0],
|
|
71
|
+
"total_mv": [10800000.0, 41600000.0],
|
|
72
|
+
"circ_mv": [8640000.0, 33280000.0],
|
|
73
|
+
"adj_factor": [1.0, 1.0],
|
|
74
|
+
"downdays": [0.0, 1.0],
|
|
75
|
+
"updays": [3.0, 2.0],
|
|
76
|
+
"lowdays": [5.0, 10.0],
|
|
77
|
+
"topdays": [15.0, 20.0],
|
|
78
|
+
# BFQ columns
|
|
79
|
+
"macd_bfq": [0.05, 0.08],
|
|
80
|
+
"macd_dif_bfq": [0.03, 0.05],
|
|
81
|
+
"macd_dea_bfq": [0.02, 0.03],
|
|
82
|
+
# HFQ columns
|
|
83
|
+
"macd_hfq": [0.06, 0.09],
|
|
84
|
+
"macd_dif_hfq": [0.04, 0.06],
|
|
85
|
+
"macd_dea_hfq": [0.02, 0.03],
|
|
86
|
+
# QFQ columns
|
|
87
|
+
"macd_qfq": [0.055, 0.085],
|
|
88
|
+
"macd_dif_qfq": [0.035, 0.055],
|
|
89
|
+
"macd_dea_qfq": [0.02, 0.03],
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.fixture
|
|
95
|
+
def sample_transformed_data():
|
|
96
|
+
"""创建样本转换后数据"""
|
|
97
|
+
return pd.DataFrame(
|
|
98
|
+
{
|
|
99
|
+
"code": ["000001.SZ", "000002.SZ"],
|
|
100
|
+
"date": ["2024-01-01", "2024-01-01"],
|
|
101
|
+
"datecode": ["20240101", "20240101"],
|
|
102
|
+
"open": [10.5, 20.3],
|
|
103
|
+
"high": [11.0, 21.0],
|
|
104
|
+
"low": [10.2, 20.0],
|
|
105
|
+
"close": [10.8, 20.8],
|
|
106
|
+
"change": [0.3, 0.5],
|
|
107
|
+
"percent_change": [2.86, 2.46],
|
|
108
|
+
"volume": [100000.0, 200000.0],
|
|
109
|
+
"amount": [108000.0, 416000.0],
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# ================= 初始化测试 =================
|
|
115
|
+
class TestTechIndexInitialization:
|
|
116
|
+
"""测试TechIndex类的初始化"""
|
|
117
|
+
|
|
118
|
+
def test_init_with_minimal_params(self, mock_session):
|
|
119
|
+
"""测试使用最少参数初始化"""
|
|
120
|
+
job = TechIndex(session=mock_session)
|
|
121
|
+
assert job.name == NAME
|
|
122
|
+
assert job.key == KEY
|
|
123
|
+
assert job.source == SOURCE
|
|
124
|
+
assert job.target == TARGET
|
|
125
|
+
|
|
126
|
+
def test_init_with_params_dict(self, mock_session):
|
|
127
|
+
"""测试使用字典参数初始化"""
|
|
128
|
+
params = {"ts_code": "000001.SZ"}
|
|
129
|
+
job = TechIndex(session=mock_session, params=params)
|
|
130
|
+
assert job.params.get("ts_code") == "000001.SZ"
|
|
131
|
+
|
|
132
|
+
def test_init_with_date_params(self, mock_session):
|
|
133
|
+
"""测试使用日期参数初始化"""
|
|
134
|
+
params = {
|
|
135
|
+
"ts_code": "000001.SZ",
|
|
136
|
+
"start_date": "20240101",
|
|
137
|
+
"end_date": "20240131",
|
|
138
|
+
}
|
|
139
|
+
job = TechIndex(session=mock_session, params=params)
|
|
140
|
+
assert job.params.get("start_date") == "20240101"
|
|
141
|
+
assert job.params.get("end_date") == "20240131"
|
|
142
|
+
|
|
143
|
+
def test_init_with_cache_enabled(self, mock_session):
|
|
144
|
+
"""测试启用缓存初始化"""
|
|
145
|
+
job = TechIndex(session=mock_session, cache=True)
|
|
146
|
+
assert job.cache is not None
|
|
147
|
+
|
|
148
|
+
def test_init_with_cache_disabled(self, mock_session):
|
|
149
|
+
"""测试禁用缓存初始化"""
|
|
150
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
151
|
+
assert job.cache is None
|
|
152
|
+
|
|
153
|
+
def test_paginate_settings(self, mock_session):
|
|
154
|
+
"""测试分页设置"""
|
|
155
|
+
job = TechIndex(session=mock_session)
|
|
156
|
+
assert job.paginate.pagesize == PAGINATE.pagesize
|
|
157
|
+
assert job.paginate.pagelimit == PAGINATE.pagelimit
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# ================= 转换测试 =================
|
|
161
|
+
class TestTechIndexTransform:
|
|
162
|
+
"""测试TechIndex的数据转换功能"""
|
|
163
|
+
|
|
164
|
+
def test_transform_main_with_valid_data(self, mock_session, sample_tushare_data):
|
|
165
|
+
"""测试主数据转换"""
|
|
166
|
+
job = TechIndex(session=mock_session)
|
|
167
|
+
result = job._transform_main(sample_tushare_data)
|
|
168
|
+
|
|
169
|
+
assert not result.empty
|
|
170
|
+
assert len(result) == 2
|
|
171
|
+
assert "code" in result.columns
|
|
172
|
+
assert "date" in result.columns
|
|
173
|
+
assert "datecode" in result.columns
|
|
174
|
+
assert result["code"].iloc[0] == "000001.SZ"
|
|
175
|
+
assert result["date"].iloc[0] == "2024-01-01"
|
|
176
|
+
assert result["datecode"].iloc[0] == "20240101"
|
|
177
|
+
|
|
178
|
+
def test_transform_main_with_empty_data(self, mock_session):
|
|
179
|
+
"""测试空数据的主数据转换"""
|
|
180
|
+
job = TechIndex(session=mock_session)
|
|
181
|
+
result = job._transform_main(pd.DataFrame())
|
|
182
|
+
|
|
183
|
+
assert result.empty
|
|
184
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS)
|
|
185
|
+
|
|
186
|
+
def test_transform_na_with_valid_data(self, mock_session, sample_tushare_data):
|
|
187
|
+
"""测试不复权因子转换"""
|
|
188
|
+
job = TechIndex(session=mock_session)
|
|
189
|
+
result = job._transform_na(sample_tushare_data)
|
|
190
|
+
|
|
191
|
+
assert not result.empty
|
|
192
|
+
assert len(result) == 2
|
|
193
|
+
assert "open" in result.columns
|
|
194
|
+
assert "macd" in result.columns
|
|
195
|
+
assert result["macd"].iloc[0] == 0.05
|
|
196
|
+
|
|
197
|
+
def test_transform_na_with_empty_data(self, mock_session):
|
|
198
|
+
"""测试空数据的不复权因子转换"""
|
|
199
|
+
job = TechIndex(session=mock_session)
|
|
200
|
+
result = job._transform_na(pd.DataFrame())
|
|
201
|
+
|
|
202
|
+
assert result.empty
|
|
203
|
+
assert len(result.columns) == len(_OUT_NA_COLS)
|
|
204
|
+
|
|
205
|
+
def test_transform_ba_with_valid_data(self, mock_session, sample_tushare_data):
|
|
206
|
+
"""测试后复权因子转换"""
|
|
207
|
+
job = TechIndex(session=mock_session)
|
|
208
|
+
result = job._transform_ba(sample_tushare_data)
|
|
209
|
+
|
|
210
|
+
assert not result.empty
|
|
211
|
+
assert len(result) == 2
|
|
212
|
+
# BA transform adds ba_ prefix
|
|
213
|
+
assert "ba_macd" in result.columns
|
|
214
|
+
assert result["ba_macd"].iloc[0] == 0.06
|
|
215
|
+
|
|
216
|
+
def test_transform_ba_with_empty_data(self, mock_session):
|
|
217
|
+
"""测试空数据的后复权因子转换"""
|
|
218
|
+
job = TechIndex(session=mock_session)
|
|
219
|
+
result = job._transform_ba(pd.DataFrame())
|
|
220
|
+
|
|
221
|
+
assert result.empty
|
|
222
|
+
assert len(result.columns) == len(_OUT_BA_COLS)
|
|
223
|
+
|
|
224
|
+
def test_transform_fa_with_valid_data(self, mock_session, sample_tushare_data):
|
|
225
|
+
"""测试前复权因子转换"""
|
|
226
|
+
job = TechIndex(session=mock_session)
|
|
227
|
+
result = job._transform_fa(sample_tushare_data)
|
|
228
|
+
|
|
229
|
+
assert not result.empty
|
|
230
|
+
assert len(result) == 2
|
|
231
|
+
# FA transform adds fa_ prefix
|
|
232
|
+
assert "fa_macd" in result.columns
|
|
233
|
+
assert result["fa_macd"].iloc[0] == 0.055
|
|
234
|
+
|
|
235
|
+
def test_transform_fa_with_empty_data(self, mock_session):
|
|
236
|
+
"""测试空数据的前复权因子转换"""
|
|
237
|
+
job = TechIndex(session=mock_session)
|
|
238
|
+
result = job._transform_fa(pd.DataFrame())
|
|
239
|
+
|
|
240
|
+
assert result.empty
|
|
241
|
+
assert len(result.columns) == len(_OUT_FA_COLS)
|
|
242
|
+
|
|
243
|
+
def test_transform_complete_with_valid_data(self, mock_session, sample_tushare_data):
|
|
244
|
+
"""测试完整数据转换"""
|
|
245
|
+
job = TechIndex(session=mock_session)
|
|
246
|
+
result = job.transform(sample_tushare_data)
|
|
247
|
+
|
|
248
|
+
assert not result.empty
|
|
249
|
+
assert len(result) == 2
|
|
250
|
+
# Check all column groups are present
|
|
251
|
+
assert "code" in result.columns # main
|
|
252
|
+
assert "open" in result.columns # na
|
|
253
|
+
assert "ba_macd" in result.columns # ba (with prefix)
|
|
254
|
+
assert "fa_macd" in result.columns # fa (with prefix)
|
|
255
|
+
# Total columns should match target
|
|
256
|
+
assert len(result.columns) == len(TARGET.list_column_names())
|
|
257
|
+
|
|
258
|
+
def test_transform_complete_with_empty_data(self, mock_session):
|
|
259
|
+
"""测试空数据的完整转换"""
|
|
260
|
+
job = TechIndex(session=mock_session)
|
|
261
|
+
result = job.transform(pd.DataFrame())
|
|
262
|
+
|
|
263
|
+
assert result.empty
|
|
264
|
+
assert len(result.columns) == len(TARGET.list_column_names())
|
|
265
|
+
|
|
266
|
+
def test_transform_preserves_column_order(self, mock_session, sample_tushare_data):
|
|
267
|
+
"""测试转换保持列顺序"""
|
|
268
|
+
job = TechIndex(session=mock_session)
|
|
269
|
+
result = job.transform(sample_tushare_data)
|
|
270
|
+
|
|
271
|
+
expected_columns = TARGET.list_column_names()
|
|
272
|
+
assert list(result.columns) == expected_columns
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ================= Run方法测试 =================
|
|
276
|
+
class TestTechIndexRun:
|
|
277
|
+
"""测试TechIndex的run方法"""
|
|
278
|
+
|
|
279
|
+
def test_run_with_ts_code(self, mock_session, sample_tushare_data):
|
|
280
|
+
"""测试使用股票代码运行"""
|
|
281
|
+
job = TechIndex(session=mock_session, params={"ts_code": "000001.SZ"}, cache=False)
|
|
282
|
+
|
|
283
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
284
|
+
result = job.run()
|
|
285
|
+
assert isinstance(result, pd.DataFrame)
|
|
286
|
+
assert not result.empty
|
|
287
|
+
|
|
288
|
+
def test_run_with_trade_date_string(self, mock_session, sample_tushare_data):
|
|
289
|
+
"""测试使用字符串交易日期运行"""
|
|
290
|
+
job = TechIndex(session=mock_session, params={"trade_date": "20240101"}, cache=False)
|
|
291
|
+
|
|
292
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
293
|
+
result = job.run()
|
|
294
|
+
assert isinstance(result, pd.DataFrame)
|
|
295
|
+
|
|
296
|
+
def test_run_with_trade_date_datetime(self, mock_session, sample_tushare_data):
|
|
297
|
+
"""测试使用datetime交易日期运行"""
|
|
298
|
+
job = TechIndex(
|
|
299
|
+
session=mock_session,
|
|
300
|
+
params={"trade_date": datetime(2024, 1, 1)},
|
|
301
|
+
cache=False,
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
305
|
+
result = job.run()
|
|
306
|
+
assert isinstance(result, pd.DataFrame)
|
|
307
|
+
|
|
308
|
+
def test_run_with_trade_date_date(self, mock_session, sample_tushare_data):
|
|
309
|
+
"""测试使用date交易日期运行"""
|
|
310
|
+
job = TechIndex(session=mock_session, params={"trade_date": date(2024, 1, 1)}, cache=False)
|
|
311
|
+
|
|
312
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
313
|
+
result = job.run()
|
|
314
|
+
assert isinstance(result, pd.DataFrame)
|
|
315
|
+
|
|
316
|
+
def test_run_with_date_range_string(self, mock_session, sample_tushare_data):
|
|
317
|
+
"""测试使用字符串日期范围运行"""
|
|
318
|
+
job = TechIndex(
|
|
319
|
+
session=mock_session,
|
|
320
|
+
params={"start_date": "20240101", "end_date": "20240131"},
|
|
321
|
+
cache=False,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
325
|
+
result = job.run()
|
|
326
|
+
assert isinstance(result, pd.DataFrame)
|
|
327
|
+
|
|
328
|
+
def test_run_with_date_range_datetime(self, mock_session, sample_tushare_data):
|
|
329
|
+
"""测试使用datetime日期范围运行"""
|
|
330
|
+
job = TechIndex(
|
|
331
|
+
session=mock_session,
|
|
332
|
+
params={
|
|
333
|
+
"start_date": datetime(2024, 1, 1),
|
|
334
|
+
"end_date": datetime(2024, 1, 31),
|
|
335
|
+
},
|
|
336
|
+
cache=False,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
340
|
+
result = job.run()
|
|
341
|
+
assert isinstance(result, pd.DataFrame)
|
|
342
|
+
|
|
343
|
+
def test_run_with_date_range_date(self, mock_session, sample_tushare_data):
|
|
344
|
+
"""测试使用date日期范围运行"""
|
|
345
|
+
job = TechIndex(
|
|
346
|
+
session=mock_session,
|
|
347
|
+
params={"start_date": date(2024, 1, 1), "end_date": date(2024, 1, 31)},
|
|
348
|
+
cache=False,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
352
|
+
result = job.run()
|
|
353
|
+
assert isinstance(result, pd.DataFrame)
|
|
354
|
+
|
|
355
|
+
def test_run_with_mixed_date_formats(self, mock_session, sample_tushare_data):
|
|
356
|
+
"""测试使用混合日期格式运行"""
|
|
357
|
+
job = TechIndex(
|
|
358
|
+
session=mock_session,
|
|
359
|
+
params={"start_date": date(2024, 1, 1), "end_date": "20240131"},
|
|
360
|
+
cache=False,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
364
|
+
result = job.run()
|
|
365
|
+
assert isinstance(result, pd.DataFrame)
|
|
366
|
+
|
|
367
|
+
def test_run_returns_dataframe(self, mock_session, sample_tushare_data):
|
|
368
|
+
"""测试run方法返回DataFrame类型"""
|
|
369
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
370
|
+
|
|
371
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
372
|
+
result = job.run()
|
|
373
|
+
assert isinstance(result, pd.DataFrame)
|
|
374
|
+
assert len(result) > 0
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ================= 缓存测试 =================
|
|
378
|
+
class TestTechIndexCache:
|
|
379
|
+
"""测试TechIndex的缓存功能"""
|
|
380
|
+
|
|
381
|
+
def test_cache_disabled_by_default(self, mock_session):
|
|
382
|
+
"""测试默认不启用缓存"""
|
|
383
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
384
|
+
assert job.cache is None
|
|
385
|
+
|
|
386
|
+
def test_cache_load_returns_none_when_disabled(self, mock_session):
|
|
387
|
+
"""测试禁用缓存时_load_cache返回None"""
|
|
388
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
389
|
+
result = job._load_cache()
|
|
390
|
+
assert result is None
|
|
391
|
+
|
|
392
|
+
def test_cache_save_does_nothing_when_disabled(self, mock_session, sample_transformed_data):
|
|
393
|
+
"""测试禁用缓存时_save_cache不执行操作"""
|
|
394
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
395
|
+
# Should not raise any errors
|
|
396
|
+
job._save_cache(sample_transformed_data)
|
|
397
|
+
|
|
398
|
+
def test_run_with_cache_enabled(self, mock_session, sample_tushare_data):
|
|
399
|
+
"""测试启用缓存运行"""
|
|
400
|
+
job = TechIndex(
|
|
401
|
+
session=mock_session,
|
|
402
|
+
params={"ts_code": "000001.SZ"},
|
|
403
|
+
cache={"store": "memory"},
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
with (
|
|
407
|
+
patch.object(job, "_fetchall", return_value=sample_tushare_data),
|
|
408
|
+
patch.object(job, "_load_cache", return_value=None),
|
|
409
|
+
patch.object(job, "_save_cache") as mock_save,
|
|
410
|
+
):
|
|
411
|
+
result = job.run()
|
|
412
|
+
assert isinstance(result, pd.DataFrame)
|
|
413
|
+
mock_save.assert_called_once()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
# ================= Slice方法测试 =================
|
|
417
|
+
class TestTechIndexSlice:
|
|
418
|
+
"""测试TechIndex的slice方法"""
|
|
419
|
+
|
|
420
|
+
def test_slice_main_without_data(self, mock_session, sample_tushare_data):
|
|
421
|
+
"""测试slice_main方法不传入数据"""
|
|
422
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
423
|
+
|
|
424
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
425
|
+
result = job.slice_main()
|
|
426
|
+
assert isinstance(result, pd.DataFrame)
|
|
427
|
+
assert not result.empty
|
|
428
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS)
|
|
429
|
+
assert "code" in result.columns
|
|
430
|
+
assert "date" in result.columns
|
|
431
|
+
|
|
432
|
+
def test_slice_main_with_data(self, mock_session, sample_tushare_data):
|
|
433
|
+
"""测试slice_main方法传入数据"""
|
|
434
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
435
|
+
full_data = job.transform(sample_tushare_data)
|
|
436
|
+
result = job.slice_main(data=full_data)
|
|
437
|
+
|
|
438
|
+
assert isinstance(result, pd.DataFrame)
|
|
439
|
+
assert not result.empty
|
|
440
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS)
|
|
441
|
+
|
|
442
|
+
def test_slice_na_without_data(self, mock_session, sample_tushare_data):
|
|
443
|
+
"""测试slice_na方法不传入数据"""
|
|
444
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
445
|
+
|
|
446
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
447
|
+
result = job.slice_na()
|
|
448
|
+
assert isinstance(result, pd.DataFrame)
|
|
449
|
+
assert not result.empty
|
|
450
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_NA_COLS)
|
|
451
|
+
assert "open" in result.columns
|
|
452
|
+
assert "macd" in result.columns
|
|
453
|
+
|
|
454
|
+
def test_slice_na_with_data(self, mock_session, sample_tushare_data):
|
|
455
|
+
"""测试slice_na方法传入数据"""
|
|
456
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
457
|
+
full_data = job.transform(sample_tushare_data)
|
|
458
|
+
result = job.slice_na(data=full_data)
|
|
459
|
+
|
|
460
|
+
assert isinstance(result, pd.DataFrame)
|
|
461
|
+
assert not result.empty
|
|
462
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_NA_COLS)
|
|
463
|
+
|
|
464
|
+
def test_slice_ba_without_data(self, mock_session, sample_tushare_data):
|
|
465
|
+
"""测试slice_ba方法不传入数据"""
|
|
466
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
467
|
+
|
|
468
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
469
|
+
result = job.slice_ba()
|
|
470
|
+
assert isinstance(result, pd.DataFrame)
|
|
471
|
+
assert not result.empty
|
|
472
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_BA_COLS)
|
|
473
|
+
assert "ba_macd" in result.columns
|
|
474
|
+
|
|
475
|
+
def test_slice_ba_with_data(self, mock_session, sample_tushare_data):
|
|
476
|
+
"""测试slice_ba方法传入数据"""
|
|
477
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
478
|
+
full_data = job.transform(sample_tushare_data)
|
|
479
|
+
result = job.slice_ba(data=full_data)
|
|
480
|
+
|
|
481
|
+
assert isinstance(result, pd.DataFrame)
|
|
482
|
+
assert not result.empty
|
|
483
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_BA_COLS)
|
|
484
|
+
|
|
485
|
+
def test_slice_fa_without_data(self, mock_session, sample_tushare_data):
|
|
486
|
+
"""测试slice_fa方法不传入数据"""
|
|
487
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
488
|
+
|
|
489
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
490
|
+
result = job.slice_fa()
|
|
491
|
+
assert isinstance(result, pd.DataFrame)
|
|
492
|
+
assert not result.empty
|
|
493
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_FA_COLS)
|
|
494
|
+
assert "fa_macd" in result.columns
|
|
495
|
+
|
|
496
|
+
def test_slice_fa_with_data(self, mock_session, sample_tushare_data):
|
|
497
|
+
"""测试slice_fa方法传入数据"""
|
|
498
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
499
|
+
full_data = job.transform(sample_tushare_data)
|
|
500
|
+
result = job.slice_fa(data=full_data)
|
|
501
|
+
|
|
502
|
+
assert isinstance(result, pd.DataFrame)
|
|
503
|
+
assert not result.empty
|
|
504
|
+
assert len(result.columns) == len(_OUT_MAIN_COLS + _OUT_FA_COLS)
|
|
505
|
+
|
|
506
|
+
def test_slice_with_empty_data(self, mock_session):
|
|
507
|
+
"""测试slice方法处理空数据"""
|
|
508
|
+
job = TechIndex(session=mock_session, cache=False)
|
|
509
|
+
empty_data = job.transform(pd.DataFrame())
|
|
510
|
+
|
|
511
|
+
result_main = job.slice_main(data=empty_data)
|
|
512
|
+
result_na = job.slice_na(data=empty_data)
|
|
513
|
+
result_ba = job.slice_ba(data=empty_data)
|
|
514
|
+
result_fa = job.slice_fa(data=empty_data)
|
|
515
|
+
|
|
516
|
+
assert result_main.empty
|
|
517
|
+
assert result_na.empty
|
|
518
|
+
assert result_ba.empty
|
|
519
|
+
assert result_fa.empty
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
# ================= 集成测试 =================
|
|
523
|
+
class TestTechIndexIntegration:
|
|
524
|
+
"""TechIndex的集成测试"""
|
|
525
|
+
|
|
526
|
+
def test_complete_workflow(self, mock_session, sample_tushare_data):
|
|
527
|
+
"""测试完整工作流程"""
|
|
528
|
+
job = TechIndex(
|
|
529
|
+
session=mock_session,
|
|
530
|
+
params={"ts_code": "000001.SZ", "start_date": "20240101", "end_date": "20240131"},
|
|
531
|
+
cache=False,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
with patch.object(job, "_fetchall", return_value=sample_tushare_data):
|
|
535
|
+
# 运行并获取完整数据
|
|
536
|
+
full_result = job.run()
|
|
537
|
+
assert isinstance(full_result, pd.DataFrame)
|
|
538
|
+
assert not full_result.empty
|
|
539
|
+
|
|
540
|
+
# 获取各部分数据
|
|
541
|
+
main_result = job.slice_main(data=full_result)
|
|
542
|
+
na_result = job.slice_na(data=full_result)
|
|
543
|
+
ba_result = job.slice_ba(data=full_result)
|
|
544
|
+
fa_result = job.slice_fa(data=full_result)
|
|
545
|
+
|
|
546
|
+
# 验证各部分
|
|
547
|
+
assert not main_result.empty
|
|
548
|
+
assert not na_result.empty
|
|
549
|
+
assert not ba_result.empty
|
|
550
|
+
assert not fa_result.empty
|
|
551
|
+
|
|
552
|
+
# 验证行数一致
|
|
553
|
+
assert len(main_result) == len(na_result) == len(ba_result) == len(fa_result)
|
|
554
|
+
|
|
555
|
+
def test_module_constants(self):
|
|
556
|
+
"""测试模块常量"""
|
|
557
|
+
assert NAME == "techindex"
|
|
558
|
+
assert KEY == "/tushare/techindex"
|
|
559
|
+
assert PAGINATE.pagesize == 10000
|
|
560
|
+
assert PAGINATE.pagelimit == 1000
|
|
561
|
+
|
|
562
|
+
def test_source_table_structure(self):
|
|
563
|
+
"""测试源表结构"""
|
|
564
|
+
assert SOURCE.desc == "A股股票技术面因子数据(Tushare格式)"
|
|
565
|
+
assert SOURCE.meta["provider"] == "tushare"
|
|
566
|
+
assert SOURCE.meta["source"] == "stk_factor_pro"
|
|
567
|
+
total_cols = len(_IN_MAIN_COLS) + len(_IN_BFQ_COLS) + len(_IN_HFQ_COLS) + len(_IN_QFQ_COLS)
|
|
568
|
+
assert len(SOURCE.columns) == total_cols
|
|
569
|
+
|
|
570
|
+
def test_target_table_structure(self):
|
|
571
|
+
"""测试目标表结构"""
|
|
572
|
+
assert TARGET.desc == "A股股票技术面因子数据(xfintech格式)"
|
|
573
|
+
assert TARGET.meta["key"] == KEY
|
|
574
|
+
assert TARGET.meta["name"] == NAME
|
|
575
|
+
total_cols = len(_OUT_MAIN_COLS) + len(_OUT_NA_COLS) + len(_OUT_BA_COLS) + len(_OUT_FA_COLS)
|
|
576
|
+
assert len(TARGET.columns) == total_cols
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.source.tushare.stock.tradedate.constant import (
|
|
4
|
+
KEY,
|
|
5
|
+
NAME,
|
|
6
|
+
PAGINATE,
|
|
7
|
+
SOURCE,
|
|
8
|
+
TARGET,
|
|
9
|
+
)
|
|
10
|
+
from xfintech.data.source.tushare.stock.tradedate.tradedate import TradeDate
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"TradeDate",
|
|
14
|
+
"KEY",
|
|
15
|
+
"NAME",
|
|
16
|
+
"PAGINATE",
|
|
17
|
+
"SOURCE",
|
|
18
|
+
"TARGET",
|
|
19
|
+
]
|
|
@@ -0,0 +1,93 @@
|
|
|
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 = "tushare"
|
|
9
|
+
SOURCE_NAME = "trade_cal"
|
|
10
|
+
URL = "https://tushare.pro/document/2?doc_id=26"
|
|
11
|
+
EXCHANGES = [
|
|
12
|
+
"SSE",
|
|
13
|
+
"SZSE",
|
|
14
|
+
"CFFEX",
|
|
15
|
+
"SHFE",
|
|
16
|
+
"CZCE",
|
|
17
|
+
"DCE",
|
|
18
|
+
"INE",
|
|
19
|
+
]
|
|
20
|
+
ARGS = {
|
|
21
|
+
"exchange": {
|
|
22
|
+
"type": ColumnKind.STRING,
|
|
23
|
+
"required": "N",
|
|
24
|
+
"desc": f"交易所: {EXCHANGES}",
|
|
25
|
+
},
|
|
26
|
+
"start_date": {
|
|
27
|
+
"type": ColumnKind.STRING,
|
|
28
|
+
"required": "N",
|
|
29
|
+
"desc": "开始日期: YYYYMMDD",
|
|
30
|
+
},
|
|
31
|
+
"end_date": {
|
|
32
|
+
"type": ColumnKind.STRING,
|
|
33
|
+
"required": "N",
|
|
34
|
+
"desc": "结束日期: YYYYMMDD",
|
|
35
|
+
},
|
|
36
|
+
"is_open": {
|
|
37
|
+
"type": ColumnKind.STRING,
|
|
38
|
+
"required": "N",
|
|
39
|
+
"desc": "是否交易: 0=休市, 1=交易",
|
|
40
|
+
},
|
|
41
|
+
"year": {
|
|
42
|
+
"type": ColumnKind.STRING,
|
|
43
|
+
"required": "N",
|
|
44
|
+
"desc": "年份(YYYY)",
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Exported constants
|
|
49
|
+
NAME = "tradedate"
|
|
50
|
+
KEY = "/tushare/tradedate"
|
|
51
|
+
PAGINATE = Paginate(
|
|
52
|
+
pagesize=1000,
|
|
53
|
+
pagelimit=1000,
|
|
54
|
+
)
|
|
55
|
+
SOURCE = TableInfo(
|
|
56
|
+
desc="交易日历数据,包括交易所的交易日期和非交易日期",
|
|
57
|
+
meta={
|
|
58
|
+
"provider": PROVIDER,
|
|
59
|
+
"source": SOURCE_NAME,
|
|
60
|
+
"url": URL,
|
|
61
|
+
"args": ARGS,
|
|
62
|
+
"type": "static",
|
|
63
|
+
"scale": "crosssection",
|
|
64
|
+
},
|
|
65
|
+
columns=[
|
|
66
|
+
ColumnInfo(name="exchange", kind=ColumnKind.STRING, desc="交易所"),
|
|
67
|
+
ColumnInfo(name="cal_date", kind=ColumnKind.STRING, desc="日历日期"),
|
|
68
|
+
ColumnInfo(name="is_open", kind=ColumnKind.INTEGER, desc="是否交易"),
|
|
69
|
+
ColumnInfo(name="pretrade_date", kind=ColumnKind.STRING, desc="上一个交易日"),
|
|
70
|
+
],
|
|
71
|
+
)
|
|
72
|
+
TARGET = TableInfo(
|
|
73
|
+
desc="交易日历数据,包括日期信息和交易状态",
|
|
74
|
+
meta={
|
|
75
|
+
"key": KEY,
|
|
76
|
+
"name": NAME,
|
|
77
|
+
"type": "static",
|
|
78
|
+
"scale": "crosssection",
|
|
79
|
+
},
|
|
80
|
+
columns=[
|
|
81
|
+
ColumnInfo(name="datecode", kind=ColumnKind.STRING, desc="日期代码:YYYYMMDD"),
|
|
82
|
+
ColumnInfo(name="date", kind=ColumnKind.STRING, desc="日期:YYYY-MM-DD"),
|
|
83
|
+
ColumnInfo(name="exchange", kind=ColumnKind.STRING, desc="交易所"),
|
|
84
|
+
ColumnInfo(name="previous", kind=ColumnKind.STRING, desc="前一个交易日"),
|
|
85
|
+
ColumnInfo(name="is_open", kind=ColumnKind.BOOLEAN, desc="是否交易日"),
|
|
86
|
+
ColumnInfo(name="year", kind=ColumnKind.INTEGER, desc="年份"),
|
|
87
|
+
ColumnInfo(name="month", kind=ColumnKind.INTEGER, desc="月份"),
|
|
88
|
+
ColumnInfo(name="day", kind=ColumnKind.INTEGER, desc="日"),
|
|
89
|
+
ColumnInfo(name="week", kind=ColumnKind.INTEGER, desc="周数"),
|
|
90
|
+
ColumnInfo(name="weekday", kind=ColumnKind.STRING, desc="星期几"),
|
|
91
|
+
ColumnInfo(name="quarter", kind=ColumnKind.INTEGER, desc="季度"),
|
|
92
|
+
],
|
|
93
|
+
)
|
|
File without changes
|