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,947 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from xfintech.data.common.cache import Cache
|
|
8
|
+
from xfintech.data.common.coolant import Coolant
|
|
9
|
+
from xfintech.data.common.paginate import Paginate
|
|
10
|
+
from xfintech.data.common.params import Params
|
|
11
|
+
from xfintech.data.common.retry import Retry
|
|
12
|
+
from xfintech.data.source.tushare.session.session import Session
|
|
13
|
+
from xfintech.data.source.tushare.stock.tradedate.constant import (
|
|
14
|
+
EXCHANGES,
|
|
15
|
+
KEY,
|
|
16
|
+
NAME,
|
|
17
|
+
PAGINATE,
|
|
18
|
+
SOURCE,
|
|
19
|
+
TARGET,
|
|
20
|
+
)
|
|
21
|
+
from xfintech.data.source.tushare.stock.tradedate.tradedate import TradeDate
|
|
22
|
+
|
|
23
|
+
# ============================================================================
|
|
24
|
+
# Fixtures
|
|
25
|
+
# ============================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture
|
|
29
|
+
def mock_session():
|
|
30
|
+
"""Create a mock Tushare session"""
|
|
31
|
+
session = MagicMock(spec=Session)
|
|
32
|
+
session._credential = "test_token"
|
|
33
|
+
session.id = "test1234"
|
|
34
|
+
session.mode = "direct"
|
|
35
|
+
session.relay_url = None
|
|
36
|
+
session.relay_secret = None
|
|
37
|
+
session.connected = True
|
|
38
|
+
|
|
39
|
+
# Mock the connection object
|
|
40
|
+
mock_connection = MagicMock()
|
|
41
|
+
mock_connection.trade_cal = MagicMock()
|
|
42
|
+
session.connection = mock_connection
|
|
43
|
+
|
|
44
|
+
return session
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.fixture
|
|
48
|
+
def sample_source_data():
|
|
49
|
+
"""Create sample source data in Tushare format"""
|
|
50
|
+
return pd.DataFrame(
|
|
51
|
+
{
|
|
52
|
+
"exchange": ["SSE", "SSE", "SSE", "SSE"],
|
|
53
|
+
"cal_date": ["20230101", "20230102", "20230103", "20230104"],
|
|
54
|
+
"is_open": [0, 1, 1, 0],
|
|
55
|
+
"pretrade_date": ["20221230", "20221230", "20230102", "20230103"],
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.fixture
|
|
61
|
+
def expected_transformed_data():
|
|
62
|
+
"""Create expected transformed data"""
|
|
63
|
+
return pd.DataFrame(
|
|
64
|
+
{
|
|
65
|
+
"datecode": ["20230101", "20230102", "20230103", "20230104"],
|
|
66
|
+
"date": ["2023-01-01", "2023-01-02", "2023-01-03", "2023-01-04"],
|
|
67
|
+
"exchange": ["ALL", "ALL", "ALL", "ALL"],
|
|
68
|
+
"previous": ["2022-12-30", "2022-12-30", "2023-01-02", "2023-01-03"],
|
|
69
|
+
"is_open": [False, True, True, False],
|
|
70
|
+
"year": [2023, 2023, 2023, 2023],
|
|
71
|
+
"month": [1, 1, 1, 1],
|
|
72
|
+
"day": [1, 2, 3, 4],
|
|
73
|
+
"week": [52, 1, 1, 1],
|
|
74
|
+
"weekday": ["Sun", "Mon", "Tue", "Wed"],
|
|
75
|
+
"quarter": [1, 1, 1, 1],
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# ============================================================================
|
|
81
|
+
# Initialization Tests
|
|
82
|
+
# ============================================================================
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_tradedate_init_basic(mock_session):
|
|
86
|
+
"""Test TradeDate initialization with minimal parameters"""
|
|
87
|
+
td = TradeDate(session=mock_session)
|
|
88
|
+
|
|
89
|
+
assert td.name == NAME
|
|
90
|
+
assert td.key == KEY
|
|
91
|
+
assert td.source == SOURCE
|
|
92
|
+
assert td.target == TARGET
|
|
93
|
+
assert isinstance(td.params, Params)
|
|
94
|
+
assert isinstance(td.coolant, Coolant)
|
|
95
|
+
assert isinstance(td.paginate, Paginate)
|
|
96
|
+
assert isinstance(td.retry, Retry)
|
|
97
|
+
assert td.paginate.pagesize == PAGINATE.pagesize
|
|
98
|
+
assert td.paginate.pagelimit == PAGINATE.pagelimit
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_tradedate_init_with_params_dict(mock_session):
|
|
102
|
+
"""Test TradeDate initialization with params as dict"""
|
|
103
|
+
params = {"start_date": "20230101", "end_date": "20231231"}
|
|
104
|
+
td = TradeDate(session=mock_session, params=params)
|
|
105
|
+
|
|
106
|
+
assert td.params.start_date == "20230101"
|
|
107
|
+
assert td.params.end_date == "20231231"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_tradedate_init_with_params_object(mock_session):
|
|
111
|
+
"""Test TradeDate initialization with Params object"""
|
|
112
|
+
params = Params(year="2023")
|
|
113
|
+
td = TradeDate(session=mock_session, params=params)
|
|
114
|
+
|
|
115
|
+
assert td.params.year == "2023"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_tradedate_init_with_year_param(mock_session):
|
|
119
|
+
"""Test TradeDate initialization with year parameter"""
|
|
120
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
121
|
+
|
|
122
|
+
assert td.params.year == "2023"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def test_tradedate_init_with_exchange_param(mock_session):
|
|
126
|
+
"""Test TradeDate initialization with exchange parameter"""
|
|
127
|
+
td = TradeDate(session=mock_session, params={"exchange": "SSE"})
|
|
128
|
+
|
|
129
|
+
assert td.params.exchange == "SSE"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_tradedate_init_with_is_open_param(mock_session):
|
|
133
|
+
"""Test TradeDate initialization with is_open parameter"""
|
|
134
|
+
td = TradeDate(session=mock_session, params={"is_open": "1"})
|
|
135
|
+
|
|
136
|
+
assert td.params.is_open == "1"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_tradedate_init_with_cache_bool_true(mock_session):
|
|
140
|
+
"""Test TradeDate initialization with cache=True"""
|
|
141
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
142
|
+
|
|
143
|
+
assert td.cache is not None
|
|
144
|
+
assert isinstance(td.cache, Cache)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_tradedate_init_with_cache_bool_false(mock_session):
|
|
148
|
+
"""Test TradeDate initialization with cache=False"""
|
|
149
|
+
td = TradeDate(session=mock_session, cache=False)
|
|
150
|
+
|
|
151
|
+
assert td.cache is None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_tradedate_init_with_cache_dict(mock_session):
|
|
155
|
+
"""Test TradeDate initialization with cache as dict"""
|
|
156
|
+
cache_config = {"directory": "/tmp/cache"}
|
|
157
|
+
td = TradeDate(session=mock_session, cache=cache_config)
|
|
158
|
+
|
|
159
|
+
assert td.cache is not None
|
|
160
|
+
assert isinstance(td.cache, Cache)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_tradedate_init_with_all_params(mock_session):
|
|
164
|
+
"""Test TradeDate initialization with all parameters"""
|
|
165
|
+
td = TradeDate(
|
|
166
|
+
session=mock_session,
|
|
167
|
+
params={"year": "2023", "exchange": "SSE"},
|
|
168
|
+
coolant={"interval": 1.0},
|
|
169
|
+
retry={"max_retries": 3},
|
|
170
|
+
cache=True,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert td.name == NAME
|
|
174
|
+
assert td.params.year == "2023"
|
|
175
|
+
assert td.params.exchange == "SSE"
|
|
176
|
+
assert td.cache is not None
|
|
177
|
+
assert td.paginate.pagesize == PAGINATE.pagesize
|
|
178
|
+
assert td.paginate.pagelimit == PAGINATE.pagelimit
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_tradedate_constants():
|
|
182
|
+
"""Test that constants are properly defined"""
|
|
183
|
+
assert NAME == "tradedate"
|
|
184
|
+
assert KEY == "/tushare/tradedate"
|
|
185
|
+
assert SOURCE is not None
|
|
186
|
+
assert TARGET is not None
|
|
187
|
+
assert PAGINATE.pagesize == 1000
|
|
188
|
+
assert PAGINATE.pagelimit == 1000
|
|
189
|
+
assert len(EXCHANGES) == 7
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# ============================================================================
|
|
193
|
+
# Transform Method Tests
|
|
194
|
+
# ============================================================================
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_tradedate_transform_basic(mock_session, sample_source_data):
|
|
198
|
+
"""Test basic data transformation"""
|
|
199
|
+
td = TradeDate(session=mock_session)
|
|
200
|
+
result = td.transform(sample_source_data)
|
|
201
|
+
|
|
202
|
+
assert not result.empty
|
|
203
|
+
assert len(result) == 4
|
|
204
|
+
assert "datecode" in result.columns
|
|
205
|
+
assert "date" in result.columns
|
|
206
|
+
assert "is_open" in result.columns
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def test_tradedate_transform_datecode_mapping(mock_session, sample_source_data):
|
|
210
|
+
"""Test that cal_date is mapped to datecode"""
|
|
211
|
+
td = TradeDate(session=mock_session)
|
|
212
|
+
result = td.transform(sample_source_data)
|
|
213
|
+
|
|
214
|
+
assert result["datecode"].tolist() == ["20230101", "20230102", "20230103", "20230104"]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_tradedate_transform_date_format(mock_session, sample_source_data):
|
|
218
|
+
"""Test that dates are converted from YYYYMMDD to YYYY-MM-DD"""
|
|
219
|
+
td = TradeDate(session=mock_session)
|
|
220
|
+
result = td.transform(sample_source_data)
|
|
221
|
+
|
|
222
|
+
assert "2023-01-01" in result["date"].values
|
|
223
|
+
assert "2023-01-02" in result["date"].values
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_tradedate_transform_is_open_boolean(mock_session, sample_source_data):
|
|
227
|
+
"""Test that is_open is converted to boolean"""
|
|
228
|
+
td = TradeDate(session=mock_session)
|
|
229
|
+
result = td.transform(sample_source_data)
|
|
230
|
+
|
|
231
|
+
assert result["is_open"].dtype == bool
|
|
232
|
+
assert not result.loc[result["datecode"] == "20230101", "is_open"].iloc[0]
|
|
233
|
+
assert result.loc[result["datecode"] == "20230102", "is_open"].iloc[0]
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_tradedate_transform_previous_date(mock_session, sample_source_data):
|
|
237
|
+
"""Test that previous trade date is formatted correctly"""
|
|
238
|
+
td = TradeDate(session=mock_session)
|
|
239
|
+
result = td.transform(sample_source_data)
|
|
240
|
+
|
|
241
|
+
assert "2022-12-30" in result["previous"].values
|
|
242
|
+
assert "2023-01-02" in result["previous"].values
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_tradedate_transform_exchange_field(mock_session, sample_source_data):
|
|
246
|
+
"""Test that exchange is set to ALL"""
|
|
247
|
+
td = TradeDate(session=mock_session)
|
|
248
|
+
result = td.transform(sample_source_data)
|
|
249
|
+
|
|
250
|
+
assert all(result["exchange"] == "ALL")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_tradedate_transform_date_components(mock_session, sample_source_data):
|
|
254
|
+
"""Test that date components are extracted correctly"""
|
|
255
|
+
td = TradeDate(session=mock_session)
|
|
256
|
+
result = td.transform(sample_source_data)
|
|
257
|
+
|
|
258
|
+
first_row = result.iloc[0]
|
|
259
|
+
assert first_row["year"] == 2023
|
|
260
|
+
assert first_row["month"] == 1
|
|
261
|
+
assert first_row["day"] == 1
|
|
262
|
+
assert first_row["quarter"] == 1
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_tradedate_transform_weekday(mock_session, sample_source_data):
|
|
266
|
+
"""Test that weekday is extracted correctly"""
|
|
267
|
+
td = TradeDate(session=mock_session)
|
|
268
|
+
result = td.transform(sample_source_data)
|
|
269
|
+
|
|
270
|
+
# 2023-01-01 is Sunday, 2023-01-02 is Monday
|
|
271
|
+
assert result.loc[result["datecode"] == "20230101", "weekday"].iloc[0] == "Sun"
|
|
272
|
+
assert result.loc[result["datecode"] == "20230102", "weekday"].iloc[0] == "Mon"
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def test_tradedate_transform_empty_dataframe(mock_session):
|
|
276
|
+
"""Test transform with empty DataFrame"""
|
|
277
|
+
td = TradeDate(session=mock_session)
|
|
278
|
+
empty_df = pd.DataFrame()
|
|
279
|
+
result = td.transform(empty_df)
|
|
280
|
+
|
|
281
|
+
assert result.empty
|
|
282
|
+
assert list(result.columns) == TARGET.list_column_names()
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def test_tradedate_transform_none_input(mock_session):
|
|
286
|
+
"""Test transform with None input"""
|
|
287
|
+
td = TradeDate(session=mock_session)
|
|
288
|
+
result = td.transform(None)
|
|
289
|
+
|
|
290
|
+
assert result.empty
|
|
291
|
+
assert list(result.columns) == TARGET.list_column_names()
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_tradedate_transform_handles_invalid_dates(mock_session):
|
|
295
|
+
"""Test transform handles invalid date formats"""
|
|
296
|
+
td = TradeDate(session=mock_session)
|
|
297
|
+
data = pd.DataFrame(
|
|
298
|
+
{
|
|
299
|
+
"exchange": ["SSE"],
|
|
300
|
+
"cal_date": ["invalid"],
|
|
301
|
+
"is_open": [1],
|
|
302
|
+
"pretrade_date": ["20230101"],
|
|
303
|
+
}
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
result = td.transform(data)
|
|
307
|
+
assert pd.isna(result["date"].iloc[0]) or result["date"].iloc[0] == "NaT"
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def test_tradedate_transform_removes_duplicates(mock_session):
|
|
311
|
+
"""Test that transform removes duplicate rows"""
|
|
312
|
+
td = TradeDate(session=mock_session)
|
|
313
|
+
data = pd.DataFrame(
|
|
314
|
+
{
|
|
315
|
+
"exchange": ["SSE", "SSE"], # Duplicate
|
|
316
|
+
"cal_date": ["20230101", "20230101"],
|
|
317
|
+
"is_open": [1, 1],
|
|
318
|
+
"pretrade_date": ["20221230", "20221230"],
|
|
319
|
+
}
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
result = td.transform(data)
|
|
323
|
+
assert len(result) == 1
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_tradedate_transform_sorts_by_datecode(mock_session, sample_source_data):
|
|
327
|
+
"""Test that result is sorted by datecode"""
|
|
328
|
+
td = TradeDate(session=mock_session)
|
|
329
|
+
shuffled = sample_source_data.sample(frac=1).reset_index(drop=True)
|
|
330
|
+
result = td.transform(shuffled)
|
|
331
|
+
|
|
332
|
+
assert result["datecode"].tolist() == sorted(result["datecode"].tolist())
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def test_tradedate_transform_resets_index(mock_session, sample_source_data):
|
|
336
|
+
"""Test that result has reset index"""
|
|
337
|
+
td = TradeDate(session=mock_session)
|
|
338
|
+
result = td.transform(sample_source_data)
|
|
339
|
+
|
|
340
|
+
assert result.index.tolist() == list(range(len(result)))
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_tradedate_transform_only_target_columns(mock_session, sample_source_data):
|
|
344
|
+
"""Test that only target columns are in result"""
|
|
345
|
+
td = TradeDate(session=mock_session)
|
|
346
|
+
result = td.transform(sample_source_data)
|
|
347
|
+
|
|
348
|
+
expected_cols = set(TARGET.list_column_names())
|
|
349
|
+
actual_cols = set(result.columns)
|
|
350
|
+
assert actual_cols == expected_cols
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# ============================================================================
|
|
354
|
+
# _run Method Tests
|
|
355
|
+
# ============================================================================
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def test_tradedate_run_with_cache_hit(mock_session):
|
|
359
|
+
"""Test _run returns cached data when available"""
|
|
360
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
361
|
+
|
|
362
|
+
cached_df = pd.DataFrame({"datecode": ["20230101"]})
|
|
363
|
+
td.cache.set(td.params.identifier, cached_df)
|
|
364
|
+
|
|
365
|
+
result = td._run()
|
|
366
|
+
|
|
367
|
+
assert result.equals(cached_df)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def test_tradedate_run_basic_date_range(mock_session, sample_source_data):
|
|
371
|
+
"""Test _run with start_date and end_date"""
|
|
372
|
+
td = TradeDate(session=mock_session, params={"start_date": "20230101", "end_date": "20231231"})
|
|
373
|
+
|
|
374
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
375
|
+
result = td._run()
|
|
376
|
+
|
|
377
|
+
assert not result.empty
|
|
378
|
+
# _fetchall should be called twice (open and close days)
|
|
379
|
+
assert td._fetchall.call_count == 2
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def test_tradedate_run_with_year_param(mock_session, sample_source_data):
|
|
383
|
+
"""Test _run converts year to start/end date"""
|
|
384
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
385
|
+
|
|
386
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
387
|
+
td._run()
|
|
388
|
+
|
|
389
|
+
# Check that year was converted
|
|
390
|
+
assert td._fetchall.call_count == 2
|
|
391
|
+
first_call_kwargs = td._fetchall.call_args_list[0][1]
|
|
392
|
+
assert first_call_kwargs["start_date"] == "20230101"
|
|
393
|
+
assert first_call_kwargs["end_date"] == "20231231"
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def test_tradedate_run_with_year_param_int(mock_session, sample_source_data):
|
|
397
|
+
"""Test _run handles year as integer"""
|
|
398
|
+
td = TradeDate(session=mock_session, params={"year": 2023})
|
|
399
|
+
|
|
400
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
401
|
+
td._run()
|
|
402
|
+
|
|
403
|
+
# Check that year was converted
|
|
404
|
+
assert td._fetchall.call_count == 2
|
|
405
|
+
first_call_kwargs = td._fetchall.call_args_list[0][1]
|
|
406
|
+
assert first_call_kwargs["start_date"] == "20230101"
|
|
407
|
+
assert first_call_kwargs["end_date"] == "20231231"
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def test_tradedate_run_with_is_open_param(mock_session, sample_source_data):
|
|
411
|
+
"""Test _run with is_open parameter (only fetch one type)"""
|
|
412
|
+
td = TradeDate(session=mock_session, params={"is_open": "1", "start_date": "20230101", "end_date": "20231231"})
|
|
413
|
+
|
|
414
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
415
|
+
td._run()
|
|
416
|
+
|
|
417
|
+
# Should only call _fetchall once when is_open is specified
|
|
418
|
+
assert td._fetchall.call_count == 1
|
|
419
|
+
call_kwargs = td._fetchall.call_args[1]
|
|
420
|
+
assert call_kwargs["is_open"] == "1"
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def test_tradedate_run_without_is_open_fetches_both(mock_session, sample_source_data):
|
|
424
|
+
"""Test _run without is_open parameter fetches both trading and non-trading days"""
|
|
425
|
+
td = TradeDate(session=mock_session, params={"start_date": "20230101", "end_date": "20231231"})
|
|
426
|
+
|
|
427
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
428
|
+
td._run()
|
|
429
|
+
|
|
430
|
+
# Should call _fetchall twice (once for open, once for close)
|
|
431
|
+
assert td._fetchall.call_count == 2
|
|
432
|
+
|
|
433
|
+
# Verify both calls
|
|
434
|
+
calls = td._fetchall.call_args_list
|
|
435
|
+
assert calls[0][1]["is_open"] == "1"
|
|
436
|
+
assert calls[1][1]["is_open"] == "0"
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def test_tradedate_run_adds_fields_param(mock_session, sample_source_data):
|
|
440
|
+
"""Test _run adds fields parameter if not provided"""
|
|
441
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
442
|
+
|
|
443
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
444
|
+
td._run()
|
|
445
|
+
|
|
446
|
+
call_kwargs = td._fetchall.call_args_list[0][1]
|
|
447
|
+
assert "fields" in call_kwargs
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_tradedate_run_sets_cache(mock_session, sample_source_data):
|
|
451
|
+
"""Test _run saves result to cache"""
|
|
452
|
+
td = TradeDate(session=mock_session, cache=True, params={"year": "2023"})
|
|
453
|
+
|
|
454
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
455
|
+
td._run()
|
|
456
|
+
|
|
457
|
+
cached = td.cache.get(td.params.identifier)
|
|
458
|
+
assert cached is not None
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def test_tradedate_run_calls_transform(mock_session, sample_source_data):
|
|
462
|
+
"""Test _run calls transform method"""
|
|
463
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
464
|
+
|
|
465
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
466
|
+
with patch.object(td, "transform", return_value=sample_source_data) as mock_transform:
|
|
467
|
+
td._run()
|
|
468
|
+
|
|
469
|
+
mock_transform.assert_called_once()
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def test_tradedate_run_concatenates_open_close(mock_session):
|
|
473
|
+
"""Test _run concatenates open and close day data"""
|
|
474
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
475
|
+
|
|
476
|
+
open_data = pd.DataFrame(
|
|
477
|
+
{
|
|
478
|
+
"exchange": ["SSE"],
|
|
479
|
+
"cal_date": ["20230102"],
|
|
480
|
+
"is_open": [1],
|
|
481
|
+
"pretrade_date": ["20221230"],
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
close_data = pd.DataFrame(
|
|
486
|
+
{
|
|
487
|
+
"exchange": ["SSE"],
|
|
488
|
+
"cal_date": ["20230101"],
|
|
489
|
+
"is_open": [0],
|
|
490
|
+
"pretrade_date": ["20221230"],
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
with patch.object(td, "_fetchall", side_effect=[open_data, close_data]):
|
|
495
|
+
with patch.object(td, "transform", side_effect=lambda x: x) as mock_transform:
|
|
496
|
+
td._run()
|
|
497
|
+
|
|
498
|
+
# Check that concat happened
|
|
499
|
+
called_df = mock_transform.call_args[0][0]
|
|
500
|
+
assert len(called_df) == 2
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
# ============================================================================
|
|
504
|
+
# list_dates Method Tests
|
|
505
|
+
# ============================================================================
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def test_tradedate_list_dates_basic(mock_session, sample_source_data):
|
|
509
|
+
"""Test list_dates returns list of all dates"""
|
|
510
|
+
td = TradeDate(session=mock_session, cache=True, params={"year": "2023"})
|
|
511
|
+
|
|
512
|
+
transformed = td.transform(sample_source_data)
|
|
513
|
+
td.cache.set(td.params.identifier, transformed)
|
|
514
|
+
|
|
515
|
+
dates = td.list_dates()
|
|
516
|
+
|
|
517
|
+
assert isinstance(dates, list)
|
|
518
|
+
assert len(dates) == 4
|
|
519
|
+
assert "2023-01-01" in dates
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def test_tradedate_list_dates_unique(mock_session):
|
|
523
|
+
"""Test list_dates returns unique dates"""
|
|
524
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
525
|
+
|
|
526
|
+
df = pd.DataFrame(
|
|
527
|
+
{
|
|
528
|
+
"date": ["2023-01-01", "2023-01-01", "2023-01-02"],
|
|
529
|
+
"is_open": [True, True, True],
|
|
530
|
+
"datecode": ["20230101", "20230101", "20230102"],
|
|
531
|
+
}
|
|
532
|
+
)
|
|
533
|
+
td.cache.set(td.params.identifier, df)
|
|
534
|
+
|
|
535
|
+
dates = td.list_dates()
|
|
536
|
+
|
|
537
|
+
assert len(dates) == 2
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def test_tradedate_list_dates_sorted(mock_session, sample_source_data):
|
|
541
|
+
"""Test list_dates returns sorted list"""
|
|
542
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
543
|
+
|
|
544
|
+
transformed = td.transform(sample_source_data)
|
|
545
|
+
td.cache.set(td.params.identifier, transformed)
|
|
546
|
+
|
|
547
|
+
dates = td.list_dates()
|
|
548
|
+
|
|
549
|
+
assert dates == sorted(dates)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def test_tradedate_list_dates_calls_run_when_not_cached(mock_session, sample_source_data):
|
|
553
|
+
"""Test list_dates calls run() when data not in cache"""
|
|
554
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
555
|
+
|
|
556
|
+
with patch.object(td, "run", return_value=td.transform(sample_source_data)):
|
|
557
|
+
dates = td.list_dates()
|
|
558
|
+
|
|
559
|
+
td.run.assert_called_once()
|
|
560
|
+
assert len(dates) == 4
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
# ============================================================================
|
|
564
|
+
# list_datecodes Method Tests
|
|
565
|
+
# ============================================================================
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def test_tradedate_list_datecodes_basic(mock_session, sample_source_data):
|
|
569
|
+
"""Test list_datecodes returns list of all datecodes"""
|
|
570
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
571
|
+
|
|
572
|
+
transformed = td.transform(sample_source_data)
|
|
573
|
+
td.cache.set(td.params.identifier, transformed)
|
|
574
|
+
|
|
575
|
+
datecodes = td.list_datecodes()
|
|
576
|
+
|
|
577
|
+
assert isinstance(datecodes, list)
|
|
578
|
+
assert len(datecodes) == 4
|
|
579
|
+
assert "20230101" in datecodes
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def test_tradedate_list_datecodes_sorted(mock_session, sample_source_data):
|
|
583
|
+
"""Test list_datecodes returns sorted list"""
|
|
584
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
585
|
+
|
|
586
|
+
transformed = td.transform(sample_source_data)
|
|
587
|
+
td.cache.set(td.params.identifier, transformed)
|
|
588
|
+
|
|
589
|
+
datecodes = td.list_datecodes()
|
|
590
|
+
|
|
591
|
+
assert datecodes == sorted(datecodes)
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def test_tradedate_list_datecodes_calls_run_when_not_cached(mock_session, sample_source_data):
|
|
595
|
+
"""Test list_datecodes calls run() when data not in cache"""
|
|
596
|
+
td = TradeDate(session=mock_session)
|
|
597
|
+
|
|
598
|
+
with patch.object(td, "run", return_value=td.transform(sample_source_data)):
|
|
599
|
+
datecodes = td.list_datecodes()
|
|
600
|
+
|
|
601
|
+
td.run.assert_called_once()
|
|
602
|
+
assert len(datecodes) == 4
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
# ============================================================================
|
|
606
|
+
# list_open_dates Method Tests
|
|
607
|
+
# ============================================================================
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def test_tradedate_list_open_dates_basic(mock_session, sample_source_data):
|
|
611
|
+
"""Test list_open_dates returns only trading days"""
|
|
612
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
613
|
+
|
|
614
|
+
transformed = td.transform(sample_source_data)
|
|
615
|
+
td.cache.set(td.params.identifier, transformed)
|
|
616
|
+
|
|
617
|
+
open_dates = td.list_open_dates()
|
|
618
|
+
|
|
619
|
+
assert isinstance(open_dates, list)
|
|
620
|
+
assert len(open_dates) == 2 # Only 2 trading days
|
|
621
|
+
assert "2023-01-02" in open_dates
|
|
622
|
+
assert "2023-01-03" in open_dates
|
|
623
|
+
assert "2023-01-01" not in open_dates # Non-trading day
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def test_tradedate_list_open_dates_sorted(mock_session, sample_source_data):
|
|
627
|
+
"""Test list_open_dates returns sorted list"""
|
|
628
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
629
|
+
|
|
630
|
+
transformed = td.transform(sample_source_data)
|
|
631
|
+
td.cache.set(td.params.identifier, transformed)
|
|
632
|
+
|
|
633
|
+
open_dates = td.list_open_dates()
|
|
634
|
+
|
|
635
|
+
assert open_dates == sorted(open_dates)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def test_tradedate_list_open_dates_calls_run_when_not_cached(mock_session, sample_source_data):
|
|
639
|
+
"""Test list_open_dates calls run() when data not in cache"""
|
|
640
|
+
td = TradeDate(session=mock_session)
|
|
641
|
+
|
|
642
|
+
with patch.object(td, "run", return_value=td.transform(sample_source_data)):
|
|
643
|
+
open_dates = td.list_open_dates()
|
|
644
|
+
|
|
645
|
+
td.run.assert_called_once()
|
|
646
|
+
assert len(open_dates) == 2
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
# ============================================================================
|
|
650
|
+
# list_open_datecodes Method Tests
|
|
651
|
+
# ============================================================================
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def test_tradedate_list_open_datecodes_basic(mock_session, sample_source_data):
|
|
655
|
+
"""Test list_open_datecodes returns only trading day codes"""
|
|
656
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
657
|
+
|
|
658
|
+
transformed = td.transform(sample_source_data)
|
|
659
|
+
td.cache.set(td.params.identifier, transformed)
|
|
660
|
+
|
|
661
|
+
open_datecodes = td.list_open_datecodes()
|
|
662
|
+
|
|
663
|
+
assert isinstance(open_datecodes, list)
|
|
664
|
+
assert len(open_datecodes) == 2 # Only 2 trading days
|
|
665
|
+
assert "20230102" in open_datecodes
|
|
666
|
+
assert "20230103" in open_datecodes
|
|
667
|
+
assert "20230101" not in open_datecodes # Non-trading day
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def test_tradedate_list_open_datecodes_sorted(mock_session, sample_source_data):
|
|
671
|
+
"""Test list_open_datecodes returns sorted list"""
|
|
672
|
+
td = TradeDate(session=mock_session, cache=True)
|
|
673
|
+
|
|
674
|
+
transformed = td.transform(sample_source_data)
|
|
675
|
+
td.cache.set(td.params.identifier, transformed)
|
|
676
|
+
|
|
677
|
+
open_datecodes = td.list_open_datecodes()
|
|
678
|
+
|
|
679
|
+
assert open_datecodes == sorted(open_datecodes)
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def test_tradedate_list_open_datecodes_calls_run_when_not_cached(mock_session, sample_source_data):
|
|
683
|
+
"""Test list_open_datecodes calls run() when data not in cache"""
|
|
684
|
+
td = TradeDate(session=mock_session)
|
|
685
|
+
|
|
686
|
+
with patch.object(td, "run", return_value=td.transform(sample_source_data)):
|
|
687
|
+
open_datecodes = td.list_open_datecodes()
|
|
688
|
+
|
|
689
|
+
td.run.assert_called_once()
|
|
690
|
+
assert len(open_datecodes) == 2
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
# ============================================================================
|
|
694
|
+
# check Method Tests
|
|
695
|
+
# ============================================================================
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def test_tradedate_check_with_string_date_hyphen(mock_session, sample_source_data):
|
|
699
|
+
"""Test check method with string date (YYYY-MM-DD format)"""
|
|
700
|
+
with patch.object(
|
|
701
|
+
TradeDate,
|
|
702
|
+
"run",
|
|
703
|
+
return_value=pd.DataFrame(
|
|
704
|
+
{
|
|
705
|
+
"datecode": ["20230102"],
|
|
706
|
+
"is_open": [True],
|
|
707
|
+
}
|
|
708
|
+
),
|
|
709
|
+
):
|
|
710
|
+
result = TradeDate.check(mock_session, "2023-01-02")
|
|
711
|
+
|
|
712
|
+
assert result is True
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def test_tradedate_check_with_string_date_no_hyphen(mock_session, sample_source_data):
|
|
716
|
+
"""Test check method with string date (YYYYMMDD format)"""
|
|
717
|
+
with patch.object(
|
|
718
|
+
TradeDate,
|
|
719
|
+
"run",
|
|
720
|
+
return_value=pd.DataFrame(
|
|
721
|
+
{
|
|
722
|
+
"datecode": ["20230102"],
|
|
723
|
+
"is_open": [True],
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
):
|
|
727
|
+
result = TradeDate.check(mock_session, "20230102")
|
|
728
|
+
|
|
729
|
+
assert result is True
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
def test_tradedate_check_with_datetime(mock_session):
|
|
733
|
+
"""Test check method with datetime object"""
|
|
734
|
+
test_date = datetime(2023, 1, 2)
|
|
735
|
+
|
|
736
|
+
with patch.object(
|
|
737
|
+
TradeDate,
|
|
738
|
+
"run",
|
|
739
|
+
return_value=pd.DataFrame(
|
|
740
|
+
{
|
|
741
|
+
"datecode": ["20230102"],
|
|
742
|
+
"is_open": [True],
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
):
|
|
746
|
+
result = TradeDate.check(mock_session, test_date)
|
|
747
|
+
|
|
748
|
+
assert result is True
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def test_tradedate_check_with_date(mock_session):
|
|
752
|
+
"""Test check method with date object"""
|
|
753
|
+
test_date = date(2023, 1, 2)
|
|
754
|
+
|
|
755
|
+
with patch.object(
|
|
756
|
+
TradeDate,
|
|
757
|
+
"run",
|
|
758
|
+
return_value=pd.DataFrame(
|
|
759
|
+
{
|
|
760
|
+
"datecode": ["20230102"],
|
|
761
|
+
"is_open": [True],
|
|
762
|
+
}
|
|
763
|
+
),
|
|
764
|
+
):
|
|
765
|
+
result = TradeDate.check(mock_session, test_date)
|
|
766
|
+
|
|
767
|
+
assert result is True
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def test_tradedate_check_non_trading_day(mock_session):
|
|
771
|
+
"""Test check method returns False for non-trading day"""
|
|
772
|
+
with patch.object(
|
|
773
|
+
TradeDate,
|
|
774
|
+
"run",
|
|
775
|
+
return_value=pd.DataFrame(
|
|
776
|
+
{
|
|
777
|
+
"datecode": [],
|
|
778
|
+
"is_open": [],
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
):
|
|
782
|
+
result = TradeDate.check(mock_session, "2023-01-01")
|
|
783
|
+
|
|
784
|
+
assert result is False
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
def test_tradedate_check_with_none_uses_current_date(mock_session):
|
|
788
|
+
"""Test check method uses current date when None is passed"""
|
|
789
|
+
today = datetime.now().date()
|
|
790
|
+
today_code = today.strftime("%Y%m%d")
|
|
791
|
+
|
|
792
|
+
with patch.object(
|
|
793
|
+
TradeDate,
|
|
794
|
+
"run",
|
|
795
|
+
return_value=pd.DataFrame(
|
|
796
|
+
{
|
|
797
|
+
"datecode": [today_code],
|
|
798
|
+
"is_open": [True],
|
|
799
|
+
}
|
|
800
|
+
),
|
|
801
|
+
):
|
|
802
|
+
result = TradeDate.check(mock_session, None)
|
|
803
|
+
|
|
804
|
+
assert isinstance(result, bool)
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
# ============================================================================
|
|
808
|
+
# Integration Tests
|
|
809
|
+
# ============================================================================
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def test_tradedate_full_workflow(mock_session, sample_source_data):
|
|
813
|
+
"""Test complete workflow from initialization to data retrieval"""
|
|
814
|
+
td = TradeDate(
|
|
815
|
+
session=mock_session,
|
|
816
|
+
params={"year": "2023"},
|
|
817
|
+
cache=True,
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
821
|
+
result = td.run()
|
|
822
|
+
|
|
823
|
+
assert not result.empty
|
|
824
|
+
assert "datecode" in result.columns
|
|
825
|
+
|
|
826
|
+
dates = td.list_dates()
|
|
827
|
+
open_dates = td.list_open_dates()
|
|
828
|
+
datecodes = td.list_datecodes()
|
|
829
|
+
open_datecodes = td.list_open_datecodes()
|
|
830
|
+
|
|
831
|
+
assert len(dates) == 4
|
|
832
|
+
assert len(open_dates) == 2
|
|
833
|
+
assert len(datecodes) == 4
|
|
834
|
+
assert len(open_datecodes) == 2
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def test_tradedate_cache_persistence(mock_session, sample_source_data):
|
|
838
|
+
"""Test that cache persists across method calls"""
|
|
839
|
+
td = TradeDate(session=mock_session, cache=True, params={"year": "2023"})
|
|
840
|
+
|
|
841
|
+
with patch.object(td, "_load_cache", return_value=None) as mock_load:
|
|
842
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data) as mock_fetch:
|
|
843
|
+
# First run - fetches data and caches it
|
|
844
|
+
result1 = td.run()
|
|
845
|
+
assert mock_fetch.call_count == 2 # Called twice (open/close)
|
|
846
|
+
assert mock_load.call_count == 1
|
|
847
|
+
|
|
848
|
+
# Second run - _load_cache still returns None, so _fetchall called again
|
|
849
|
+
result2 = td.run()
|
|
850
|
+
assert mock_fetch.call_count == 4 # Called twice more
|
|
851
|
+
assert mock_load.call_count == 2
|
|
852
|
+
|
|
853
|
+
pd.testing.assert_frame_equal(result1, result2)
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def test_tradedate_params_identifier_uniqueness(mock_session):
|
|
857
|
+
"""Test that different params produce different cache keys"""
|
|
858
|
+
td1 = TradeDate(session=mock_session, params={"year": "2022"}, cache=True)
|
|
859
|
+
td2 = TradeDate(session=mock_session, params={"year": "2023"}, cache=True)
|
|
860
|
+
|
|
861
|
+
assert td1.params.identifier != td2.params.identifier
|
|
862
|
+
|
|
863
|
+
|
|
864
|
+
def test_tradedate_different_exchanges(mock_session, sample_source_data):
|
|
865
|
+
"""Test with different exchange parameters"""
|
|
866
|
+
for exchange in EXCHANGES:
|
|
867
|
+
td = TradeDate(session=mock_session, params={"exchange": exchange, "year": "2023"})
|
|
868
|
+
|
|
869
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
870
|
+
td._run()
|
|
871
|
+
|
|
872
|
+
call_kwargs = td._fetchall.call_args_list[0][1]
|
|
873
|
+
assert call_kwargs["exchange"] == exchange
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
# ============================================================================
|
|
877
|
+
# Edge Case Tests
|
|
878
|
+
# ============================================================================
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def test_tradedate_empty_result_handling(mock_session):
|
|
882
|
+
"""Test handling of empty API results"""
|
|
883
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
884
|
+
|
|
885
|
+
empty_df = pd.DataFrame()
|
|
886
|
+
with patch.object(td, "_fetchall", return_value=empty_df):
|
|
887
|
+
result = td._run()
|
|
888
|
+
|
|
889
|
+
assert result.empty
|
|
890
|
+
assert list(result.columns) == TARGET.list_column_names()
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
def test_tradedate_large_dataset_handling(mock_session):
|
|
894
|
+
"""Test handling of large datasets"""
|
|
895
|
+
td = TradeDate(session=mock_session, params={"year": "2023"})
|
|
896
|
+
|
|
897
|
+
# Create large dataset (365 days)
|
|
898
|
+
large_data = pd.DataFrame(
|
|
899
|
+
{
|
|
900
|
+
"exchange": ["SSE"] * 365,
|
|
901
|
+
"cal_date": [f"2023{i:04d}" for i in range(101, 466)], # Simplified
|
|
902
|
+
"is_open": [1] * 365,
|
|
903
|
+
"pretrade_date": [f"2023{i:04d}" for i in range(100, 465)],
|
|
904
|
+
}
|
|
905
|
+
)
|
|
906
|
+
|
|
907
|
+
result = td.transform(large_data)
|
|
908
|
+
|
|
909
|
+
assert len(result) <= 365 # Some may be filtered
|
|
910
|
+
assert not result.empty
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
def test_tradedate_without_cache(mock_session, sample_source_data):
|
|
914
|
+
"""Test TradeDate works correctly without cache"""
|
|
915
|
+
td = TradeDate(session=mock_session, cache=False, params={"year": "2023"})
|
|
916
|
+
|
|
917
|
+
assert td.cache is None
|
|
918
|
+
|
|
919
|
+
with patch.object(td, "_fetchall", return_value=sample_source_data):
|
|
920
|
+
result = td.run()
|
|
921
|
+
|
|
922
|
+
assert not result.empty
|
|
923
|
+
|
|
924
|
+
dates = td.list_dates()
|
|
925
|
+
open_dates = td.list_open_dates()
|
|
926
|
+
|
|
927
|
+
assert len(dates) > 0
|
|
928
|
+
assert len(open_dates) > 0
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def test_tradedate_mixed_trading_non_trading_days(mock_session):
|
|
932
|
+
"""Test handling of mixed trading and non-trading days"""
|
|
933
|
+
td = TradeDate(session=mock_session)
|
|
934
|
+
|
|
935
|
+
data = pd.DataFrame(
|
|
936
|
+
{
|
|
937
|
+
"exchange": ["SSE"] * 7,
|
|
938
|
+
"cal_date": [f"2023010{i}" for i in range(1, 8)],
|
|
939
|
+
"is_open": [0, 1, 1, 1, 1, 1, 0], # Weekend, weekdays, weekend
|
|
940
|
+
"pretrade_date": ["20221230"] + [f"2023010{i}" for i in range(1, 7)],
|
|
941
|
+
}
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
result = td.transform(data)
|
|
945
|
+
|
|
946
|
+
assert len(result) == 7
|
|
947
|
+
assert result["is_open"].sum() == 5 # 5 trading days
|