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,505 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test suite for Session class
|
|
3
|
+
Tests cover initialization, connection modes, state management, and session lifecycle
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from unittest.mock import Mock, patch
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from xfintech.data.source.baostock.session.session import Session
|
|
13
|
+
|
|
14
|
+
# ============================================================================
|
|
15
|
+
# Session Initialization Tests - Direct Mode
|
|
16
|
+
# ============================================================================
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
20
|
+
def test_session_init_direct_mode_basic(mock_bs):
|
|
21
|
+
"""Test Session initialization in direct mode"""
|
|
22
|
+
mock_bs.login.return_value = Mock()
|
|
23
|
+
|
|
24
|
+
session = Session(mode="direct")
|
|
25
|
+
|
|
26
|
+
assert session.mode == "direct"
|
|
27
|
+
assert session._credential is None
|
|
28
|
+
assert session.relay_url is None
|
|
29
|
+
assert session.relay_secret is None
|
|
30
|
+
assert len(session.id) == 8
|
|
31
|
+
assert session.connected is True
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
35
|
+
def test_session_init_direct_mode_default(mock_bs):
|
|
36
|
+
"""Test Session defaults to direct mode"""
|
|
37
|
+
mock_bs.login.return_value = Mock()
|
|
38
|
+
|
|
39
|
+
session = Session()
|
|
40
|
+
|
|
41
|
+
assert session.mode == "direct"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
45
|
+
def test_session_init_direct_mode_uppercase(mock_bs):
|
|
46
|
+
"""Test Session handles uppercase mode"""
|
|
47
|
+
mock_bs.login.return_value = Mock()
|
|
48
|
+
|
|
49
|
+
session = Session(mode="DIRECT")
|
|
50
|
+
|
|
51
|
+
assert session.mode == "direct"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
55
|
+
def test_session_init_direct_calls_login(mock_bs):
|
|
56
|
+
"""Test Session calls bs.login in direct mode"""
|
|
57
|
+
mock_bs.login.return_value = Mock()
|
|
58
|
+
|
|
59
|
+
Session(mode="direct")
|
|
60
|
+
|
|
61
|
+
mock_bs.login.assert_called_once()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
65
|
+
def test_session_init_direct_creates_connection(mock_bs):
|
|
66
|
+
"""Test Session creates baostock connection"""
|
|
67
|
+
mock_bs.login.return_value = Mock()
|
|
68
|
+
|
|
69
|
+
session = Session(mode="direct")
|
|
70
|
+
|
|
71
|
+
assert session.connection is mock_bs
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ============================================================================
|
|
75
|
+
# Session Resolve Methods Tests
|
|
76
|
+
# ============================================================================
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_session_resolve_mode_invalid():
|
|
80
|
+
"""Test _resolve_mode raises error with invalid mode"""
|
|
81
|
+
with pytest.raises(ValueError, match="Unsupported mode"):
|
|
82
|
+
Session(mode="invalid")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
86
|
+
def test_session_resolve_mode_empty_returns_direct(mock_bs):
|
|
87
|
+
"""Test _resolve_mode returns 'direct' for empty string"""
|
|
88
|
+
mock_bs.login.return_value = Mock()
|
|
89
|
+
|
|
90
|
+
session = Session(mode="")
|
|
91
|
+
|
|
92
|
+
assert session.mode == "direct"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ============================================================================
|
|
96
|
+
# Session ID Tests
|
|
97
|
+
# ============================================================================
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
101
|
+
def test_session_id_length(mock_bs):
|
|
102
|
+
"""Test Session ID is 8 characters"""
|
|
103
|
+
mock_bs.login.return_value = Mock()
|
|
104
|
+
|
|
105
|
+
session = Session()
|
|
106
|
+
|
|
107
|
+
assert len(session.id) == 8
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
111
|
+
def test_session_id_unique(mock_bs):
|
|
112
|
+
"""Test different Sessions have different IDs"""
|
|
113
|
+
mock_bs.login.return_value = Mock()
|
|
114
|
+
|
|
115
|
+
session1 = Session()
|
|
116
|
+
session2 = Session()
|
|
117
|
+
|
|
118
|
+
assert session1.id != session2.id
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ============================================================================
|
|
122
|
+
# Session Properties Tests
|
|
123
|
+
# ============================================================================
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
127
|
+
def test_session_connected_property_true(mock_bs):
|
|
128
|
+
"""Test connected property returns True when connection exists"""
|
|
129
|
+
mock_bs.login.return_value = Mock()
|
|
130
|
+
|
|
131
|
+
session = Session()
|
|
132
|
+
|
|
133
|
+
assert session.connected is True
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
137
|
+
def test_session_connected_property_false(mock_bs):
|
|
138
|
+
"""Test connected property returns False when no connection"""
|
|
139
|
+
mock_bs.login.return_value = Mock()
|
|
140
|
+
|
|
141
|
+
session = Session()
|
|
142
|
+
session.connection = None
|
|
143
|
+
|
|
144
|
+
assert session.connected is False
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
148
|
+
@patch("xfintech.data.source.baostock.session.session.pd.Timestamp")
|
|
149
|
+
def test_session_duration_property_no_start(mock_timestamp, mock_bs):
|
|
150
|
+
"""Test duration returns 0.0 when not started"""
|
|
151
|
+
mock_bs.login.return_value = Mock()
|
|
152
|
+
|
|
153
|
+
session = Session()
|
|
154
|
+
session.start_at = None
|
|
155
|
+
|
|
156
|
+
assert session.duration == 0.0
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
160
|
+
def test_session_duration_property_ongoing(mock_bs):
|
|
161
|
+
"""Test duration calculates correctly for ongoing session"""
|
|
162
|
+
mock_bs.login.return_value = Mock()
|
|
163
|
+
|
|
164
|
+
# Create session
|
|
165
|
+
session = Session()
|
|
166
|
+
|
|
167
|
+
# Set times directly
|
|
168
|
+
session.start_at = pd.Timestamp("2024-01-15 10:00:00")
|
|
169
|
+
session.finish_at = None
|
|
170
|
+
|
|
171
|
+
# Mock datetime.now to return a specific time
|
|
172
|
+
with patch(
|
|
173
|
+
"xfintech.data.source.baostock.session.session.datetime",
|
|
174
|
+
) as mock_datetime:
|
|
175
|
+
mock_now = datetime(2024, 1, 15, 10, 0, 10)
|
|
176
|
+
mock_datetime.now.return_value = mock_now
|
|
177
|
+
|
|
178
|
+
duration = session.duration
|
|
179
|
+
|
|
180
|
+
assert duration == 10.0
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
184
|
+
def test_session_duration_property_finished(mock_bs):
|
|
185
|
+
"""Test duration calculates correctly for finished session"""
|
|
186
|
+
mock_bs.login.return_value = Mock()
|
|
187
|
+
|
|
188
|
+
session = Session()
|
|
189
|
+
session.start_at = pd.Timestamp("2024-01-15 10:00:00")
|
|
190
|
+
session.finish_at = pd.Timestamp("2024-01-15 10:00:30")
|
|
191
|
+
|
|
192
|
+
assert session.duration == 30.0
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# ============================================================================
|
|
196
|
+
# Session String Representation Tests
|
|
197
|
+
# ============================================================================
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
201
|
+
def test_session_str(mock_bs):
|
|
202
|
+
"""Test __str__ returns session ID"""
|
|
203
|
+
mock_bs.login.return_value = Mock()
|
|
204
|
+
|
|
205
|
+
session = Session()
|
|
206
|
+
|
|
207
|
+
assert str(session) == session.id
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
211
|
+
def test_session_repr(mock_bs):
|
|
212
|
+
"""Test __repr__ includes class name, connected status, and mode"""
|
|
213
|
+
mock_bs.login.return_value = Mock()
|
|
214
|
+
|
|
215
|
+
session = Session(mode="direct")
|
|
216
|
+
|
|
217
|
+
result = repr(session)
|
|
218
|
+
assert "Session" in result
|
|
219
|
+
assert "connected=True" in result
|
|
220
|
+
assert "mode=direct" in result
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@patch("xfintech.data.source.baostock.session.session.BaostockRelayClient")
|
|
224
|
+
def test_session_repr_relay_mode(mock_relay_client_class):
|
|
225
|
+
"""Test __repr__ shows relay mode"""
|
|
226
|
+
mock_client = Mock()
|
|
227
|
+
mock_client.check_health.return_value = True
|
|
228
|
+
mock_relay_client_class.return_value = mock_client
|
|
229
|
+
|
|
230
|
+
session = Session(
|
|
231
|
+
mode="relay",
|
|
232
|
+
relay_url="https://relay.example.com",
|
|
233
|
+
relay_secret="relay-secret",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
result = repr(session)
|
|
237
|
+
assert "mode=relay" in result
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ============================================================================
|
|
241
|
+
# Session Start/End Methods Tests
|
|
242
|
+
# ============================================================================
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
246
|
+
@patch("xfintech.data.source.baostock.session.session.pd.Timestamp")
|
|
247
|
+
def test_session_start_method(mock_timestamp, mock_bs):
|
|
248
|
+
"""Test start method sets start_at"""
|
|
249
|
+
mock_bs.login.return_value = Mock()
|
|
250
|
+
mock_now = pd.Timestamp("2024-01-15 10:00:00")
|
|
251
|
+
mock_timestamp.now.return_value = mock_now
|
|
252
|
+
|
|
253
|
+
session = Session()
|
|
254
|
+
session.start_at = None
|
|
255
|
+
session.start()
|
|
256
|
+
|
|
257
|
+
assert session.start_at == mock_now
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
261
|
+
@patch("xfintech.data.source.baostock.session.session.pd.Timestamp")
|
|
262
|
+
def test_session_end_method(mock_timestamp, mock_bs):
|
|
263
|
+
"""Test end method sets finish_at"""
|
|
264
|
+
mock_bs.login.return_value = Mock()
|
|
265
|
+
mock_now = pd.Timestamp("2024-01-15 10:00:30")
|
|
266
|
+
mock_timestamp.now.return_value = mock_now
|
|
267
|
+
|
|
268
|
+
session = Session()
|
|
269
|
+
session.end()
|
|
270
|
+
|
|
271
|
+
assert session.finish_at == mock_now
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
275
|
+
def test_session_get_start_iso_none(mock_bs):
|
|
276
|
+
"""Test get_start_iso returns None when start_at is None"""
|
|
277
|
+
mock_bs.login.return_value = Mock()
|
|
278
|
+
|
|
279
|
+
session = Session()
|
|
280
|
+
session.start_at = None
|
|
281
|
+
|
|
282
|
+
assert session.get_start_iso() is None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
286
|
+
def test_session_get_start_iso_with_value(mock_bs):
|
|
287
|
+
"""Test get_start_iso returns ISO format"""
|
|
288
|
+
mock_bs.login.return_value = Mock()
|
|
289
|
+
|
|
290
|
+
session = Session()
|
|
291
|
+
session.start_at = pd.Timestamp("2024-01-15 10:00:00")
|
|
292
|
+
|
|
293
|
+
result = session.get_start_iso()
|
|
294
|
+
assert "2024-01-15" in result
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
298
|
+
def test_session_get_finish_iso_none(mock_bs):
|
|
299
|
+
"""Test get_finish_iso returns None when finish_at is None"""
|
|
300
|
+
mock_bs.login.return_value = Mock()
|
|
301
|
+
|
|
302
|
+
session = Session()
|
|
303
|
+
session.finish_at = None
|
|
304
|
+
|
|
305
|
+
assert session.get_finish_iso() is None
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
309
|
+
def test_session_get_finish_iso_with_value(mock_bs):
|
|
310
|
+
"""Test get_finish_iso returns ISO format"""
|
|
311
|
+
mock_bs.login.return_value = Mock()
|
|
312
|
+
|
|
313
|
+
session = Session()
|
|
314
|
+
session.finish_at = pd.Timestamp("2024-01-15 10:00:30")
|
|
315
|
+
session = Session()
|
|
316
|
+
session.connection = None
|
|
317
|
+
|
|
318
|
+
result = session.connect()
|
|
319
|
+
|
|
320
|
+
assert result is not None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
324
|
+
def test_session_connect_already_connected(mock_bs):
|
|
325
|
+
"""Test connect returns existing connection if already connected"""
|
|
326
|
+
mock_bs.login.return_value = Mock()
|
|
327
|
+
|
|
328
|
+
session = Session()
|
|
329
|
+
first_connection = session.connection
|
|
330
|
+
|
|
331
|
+
# Reset call count
|
|
332
|
+
mock_bs.login.reset_mock()
|
|
333
|
+
|
|
334
|
+
# Try to connect again
|
|
335
|
+
result = session.connect()
|
|
336
|
+
|
|
337
|
+
assert result is first_connection
|
|
338
|
+
# Should not call login again
|
|
339
|
+
mock_bs.login.assert_not_called()
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
343
|
+
@patch("xfintech.data.source.baostock.session.session.pd.Timestamp")
|
|
344
|
+
def test_session_disconnect_clears_connection(mock_timestamp, mock_bs):
|
|
345
|
+
"""Test disconnect clears connection"""
|
|
346
|
+
mock_bs.login.return_value = Mock()
|
|
347
|
+
mock_bs.logout.return_value = Mock()
|
|
348
|
+
|
|
349
|
+
session = Session()
|
|
350
|
+
assert session.connected is True
|
|
351
|
+
|
|
352
|
+
session.disconnect()
|
|
353
|
+
|
|
354
|
+
assert session.connection is None
|
|
355
|
+
assert session.connected is False
|
|
356
|
+
mock_bs.logout.assert_called_once()
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
360
|
+
@patch("xfintech.data.source.baostock.session.session.pd.Timestamp")
|
|
361
|
+
def test_session_disconnect_sets_finish_time(mock_timestamp, mock_bs):
|
|
362
|
+
"""Test disconnect sets finish_at"""
|
|
363
|
+
mock_bs.login.return_value = Mock()
|
|
364
|
+
mock_bs.logout.return_value = Mock()
|
|
365
|
+
mock_now = pd.Timestamp("2024-01-15 10:00:30")
|
|
366
|
+
mock_timestamp.now.return_value = mock_now
|
|
367
|
+
|
|
368
|
+
session = Session()
|
|
369
|
+
session.disconnect()
|
|
370
|
+
|
|
371
|
+
assert session.finish_at == mock_now
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# ============================================================================
|
|
375
|
+
# Session Describe Method Tests
|
|
376
|
+
# ============================================================================
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
380
|
+
def test_session_describe_basic(mock_bs):
|
|
381
|
+
"""Test describe returns basic session info"""
|
|
382
|
+
mock_bs.login.return_value = Mock()
|
|
383
|
+
|
|
384
|
+
session = Session(mode="direct")
|
|
385
|
+
result = session.describe()
|
|
386
|
+
|
|
387
|
+
assert "id" in result
|
|
388
|
+
assert result["id"] == session.id
|
|
389
|
+
assert "mode" in result
|
|
390
|
+
assert result["mode"] == "direct"
|
|
391
|
+
assert "connected" in result
|
|
392
|
+
assert result["connected"] is True
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@patch("xfintech.data.source.baostock.session.session.BaostockRelayClient")
|
|
396
|
+
def test_session_describe_relay_mode(mock_relay_client_class):
|
|
397
|
+
"""Test describe includes relay info in relay mode"""
|
|
398
|
+
mock_client = Mock()
|
|
399
|
+
mock_client.check_health.return_value = True
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
403
|
+
def test_session_describe_with_timestamps(mock_bs):
|
|
404
|
+
"""Test describe includes timestamps when available"""
|
|
405
|
+
mock_bs.login.return_value = Mock()
|
|
406
|
+
|
|
407
|
+
session = Session()
|
|
408
|
+
session.start_at = pd.Timestamp("2024-01-15 10:00:00")
|
|
409
|
+
session.finish_at = pd.Timestamp("2024-01-15 10:00:30")
|
|
410
|
+
|
|
411
|
+
result = session.describe()
|
|
412
|
+
|
|
413
|
+
assert "start_at" in result
|
|
414
|
+
assert "finish_at" in result
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
418
|
+
def test_session_describe_without_finish(mock_bs):
|
|
419
|
+
"""Test describe handles ongoing session"""
|
|
420
|
+
mock_bs.login.return_value = Mock()
|
|
421
|
+
|
|
422
|
+
session = Session()
|
|
423
|
+
session.finish_at = None
|
|
424
|
+
|
|
425
|
+
result = session.describe()
|
|
426
|
+
|
|
427
|
+
assert "start_at" in result
|
|
428
|
+
assert "finish_at" not in result
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
# ============================================================================
|
|
432
|
+
# Session To Dict Method Tests
|
|
433
|
+
# ============================================================================
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
437
|
+
def test_session_to_dict_structure(mock_bs):
|
|
438
|
+
"""Test to_dict returns expected structure"""
|
|
439
|
+
mock_bs.login.return_value = Mock()
|
|
440
|
+
|
|
441
|
+
session = Session(mode="direct")
|
|
442
|
+
result = session.to_dict()
|
|
443
|
+
|
|
444
|
+
assert "id" in result
|
|
445
|
+
assert "connected" in result
|
|
446
|
+
assert "mode" in result
|
|
447
|
+
assert "relay" in result
|
|
448
|
+
assert "start_at" in result
|
|
449
|
+
assert "finish_at" in result
|
|
450
|
+
assert "duration" in result
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
454
|
+
def test_session_to_dict_includes_duration(mock_bs):
|
|
455
|
+
"""Test to_dict includes duration"""
|
|
456
|
+
mock_bs.login.return_value = Mock()
|
|
457
|
+
|
|
458
|
+
session = Session()
|
|
459
|
+
session.start_at = pd.Timestamp("2024-01-15 10:00:00")
|
|
460
|
+
session.finish_at = pd.Timestamp("2024-01-15 10:00:30")
|
|
461
|
+
|
|
462
|
+
result = session.to_dict()
|
|
463
|
+
|
|
464
|
+
assert result["duration"] == 30.0
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
# ============================================================================
|
|
468
|
+
# Integration Tests
|
|
469
|
+
# ============================================================================
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
473
|
+
def test_session_full_lifecycle_direct(mock_bs):
|
|
474
|
+
"""Test complete session lifecycle in direct mode"""
|
|
475
|
+
mock_bs.login.return_value = Mock()
|
|
476
|
+
mock_bs.logout.return_value = Mock()
|
|
477
|
+
|
|
478
|
+
# Create session
|
|
479
|
+
session = Session(mode="direct")
|
|
480
|
+
assert session.connected is True
|
|
481
|
+
|
|
482
|
+
# Use connection
|
|
483
|
+
assert session.connection is mock_bs
|
|
484
|
+
|
|
485
|
+
# Disconnect
|
|
486
|
+
session.disconnect()
|
|
487
|
+
assert session.connected is False
|
|
488
|
+
assert session.finish_at is not None
|
|
489
|
+
mock_bs.logout.assert_called_once()
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
@patch("xfintech.data.source.baostock.session.session.bs")
|
|
493
|
+
def test_session_reconnect(mock_bs):
|
|
494
|
+
"""Test reconnecting after disconnect"""
|
|
495
|
+
mock_bs.login.return_value = Mock()
|
|
496
|
+
mock_bs.logout.return_value = Mock()
|
|
497
|
+
|
|
498
|
+
session = Session()
|
|
499
|
+
session.disconnect()
|
|
500
|
+
|
|
501
|
+
# Reconnect
|
|
502
|
+
session.connect()
|
|
503
|
+
|
|
504
|
+
assert session.connected is True
|
|
505
|
+
assert session.connection is not None
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from xfintech.data.common.paginate import Paginate
|
|
4
|
+
from xfintech.fabric.column.info import ColumnInfo
|
|
5
|
+
from xfintech.fabric.column.kind import ColumnKind
|
|
6
|
+
from xfintech.fabric.table.info import TableInfo
|
|
7
|
+
|
|
8
|
+
PROVIDER = "baostock"
|
|
9
|
+
SOURCE_NAME = "query_hs300_stocks"
|
|
10
|
+
URL = "http://www.baostock.com/mainContent?file=hs300Stock.md"
|
|
11
|
+
ARGS = {}
|
|
12
|
+
|
|
13
|
+
# Exported constants
|
|
14
|
+
NAME = "hs300stock"
|
|
15
|
+
KEY = "/baostock/hs300stock"
|
|
16
|
+
PAGINATE = Paginate(
|
|
17
|
+
pagesize=10000,
|
|
18
|
+
pagelimit=100,
|
|
19
|
+
)
|
|
20
|
+
SOURCE = TableInfo(
|
|
21
|
+
desc="沪深300成分股(BaoStock格式)",
|
|
22
|
+
meta={
|
|
23
|
+
"provider": PROVIDER,
|
|
24
|
+
"source": SOURCE_NAME,
|
|
25
|
+
"url": URL,
|
|
26
|
+
"args": ARGS,
|
|
27
|
+
"type": "static",
|
|
28
|
+
"scale": "crosssection",
|
|
29
|
+
},
|
|
30
|
+
columns=[
|
|
31
|
+
ColumnInfo(name="updateDate", kind=ColumnKind.STRING, desc="更新日期"),
|
|
32
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="证券代码"),
|
|
33
|
+
ColumnInfo(name="code_name", kind=ColumnKind.STRING, desc="证券名称"),
|
|
34
|
+
],
|
|
35
|
+
)
|
|
36
|
+
TARGET = TableInfo(
|
|
37
|
+
desc="沪深300成分股(xfintech格式)",
|
|
38
|
+
meta={
|
|
39
|
+
"key": KEY,
|
|
40
|
+
"name": NAME,
|
|
41
|
+
"type": "static",
|
|
42
|
+
"scale": "crosssection",
|
|
43
|
+
},
|
|
44
|
+
columns=[
|
|
45
|
+
ColumnInfo(name="update_date", kind=ColumnKind.STRING, desc="更新日期"),
|
|
46
|
+
ColumnInfo(name="code", kind=ColumnKind.STRING, desc="证券代码"),
|
|
47
|
+
ColumnInfo(name="name", kind=ColumnKind.STRING, desc="证券名称"),
|
|
48
|
+
],
|
|
49
|
+
)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from xfintech.data.common.cache import Cache
|
|
8
|
+
from xfintech.data.common.coolant import Coolant
|
|
9
|
+
from xfintech.data.common.params import Params
|
|
10
|
+
from xfintech.data.common.retry import Retry
|
|
11
|
+
from xfintech.data.job import JobHouse
|
|
12
|
+
from xfintech.data.source.baostock.job import BaostockJob
|
|
13
|
+
from xfintech.data.source.baostock.session.session import Session
|
|
14
|
+
from xfintech.data.source.baostock.stock.hs300stock.constant import (
|
|
15
|
+
KEY,
|
|
16
|
+
NAME,
|
|
17
|
+
PAGINATE,
|
|
18
|
+
SOURCE,
|
|
19
|
+
TARGET,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@JobHouse.register(KEY, alias=KEY)
|
|
24
|
+
class HS300Stock(BaostockJob):
|
|
25
|
+
"""
|
|
26
|
+
描述:
|
|
27
|
+
- 获取沪深300成分股
|
|
28
|
+
- 返回沪深300指数的300只成份股信息
|
|
29
|
+
- 每周一更新
|
|
30
|
+
- API文档: http://www.baostock.com/mainContent?file=hs300Stock.md
|
|
31
|
+
- SCALE: CrossSection
|
|
32
|
+
- TYPE: Static
|
|
33
|
+
- PAGINATE: 10000 rows / 100 pages
|
|
34
|
+
|
|
35
|
+
属性:
|
|
36
|
+
- name: str, 作业名称 'hs300index'。
|
|
37
|
+
- key: str, 作业键 '/baostock/hs300index'。
|
|
38
|
+
- session: Session, Baostock会话对象。
|
|
39
|
+
- source: TableInfo, 源表信息(BaoStock原始格式)。
|
|
40
|
+
- target: TableInfo, 目标表信息(xfintech格式)。
|
|
41
|
+
- params: Params, 查询参数(此API无需参数)。
|
|
42
|
+
- coolant: Coolant, 请求冷却控制。
|
|
43
|
+
- paginate: Paginate, 分页控制(pagesize=10000, pagelimit=100)。
|
|
44
|
+
- retry: Retry, 重试策略。
|
|
45
|
+
- cache: Cache, 缓存管理。
|
|
46
|
+
|
|
47
|
+
方法:
|
|
48
|
+
- run(): 执行作业,返回沪深300成分股DataFrame。
|
|
49
|
+
- _run(): 内部执行逻辑,处理参数并调用API。
|
|
50
|
+
- transform(data): 转换数据格式,将源格式转为目标格式。
|
|
51
|
+
- list_codes(): 返回所有股票代码列表。
|
|
52
|
+
- list_names(): 返回所有股票名称列表。
|
|
53
|
+
|
|
54
|
+
例子:
|
|
55
|
+
```python
|
|
56
|
+
from xfintech.data.source.baostock.session import Session
|
|
57
|
+
from xfintech.data.source.baostock.stock.hs300index import HS300Stock
|
|
58
|
+
|
|
59
|
+
session = Session()
|
|
60
|
+
|
|
61
|
+
# 获取沪深300成分股
|
|
62
|
+
job = HS300Stock(session=session)
|
|
63
|
+
df = job.run()
|
|
64
|
+
|
|
65
|
+
# 获取代码列表
|
|
66
|
+
codes = job.list_codes()
|
|
67
|
+
```
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
session: Session,
|
|
73
|
+
params: Optional[Params | Dict[str, Any]] = None,
|
|
74
|
+
coolant: Optional[Coolant | Dict[str, Any]] = None,
|
|
75
|
+
retry: Optional[Retry | Dict[str, Any]] = None,
|
|
76
|
+
cache: Optional[Cache | Dict[str, str] | bool] = None,
|
|
77
|
+
) -> None:
|
|
78
|
+
super().__init__(
|
|
79
|
+
name=NAME,
|
|
80
|
+
key=KEY,
|
|
81
|
+
session=session,
|
|
82
|
+
source=SOURCE,
|
|
83
|
+
target=TARGET,
|
|
84
|
+
params=params,
|
|
85
|
+
coolant=coolant,
|
|
86
|
+
paginate=PAGINATE,
|
|
87
|
+
retry=retry,
|
|
88
|
+
cache=cache,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _run(self) -> pd.DataFrame:
|
|
92
|
+
cached = self._load_cache()
|
|
93
|
+
if cached is not None:
|
|
94
|
+
return cached
|
|
95
|
+
|
|
96
|
+
payload = self.params.to_dict()
|
|
97
|
+
payload = self._parse_date_params(payload, keys=["date"])
|
|
98
|
+
# Fetch data from API
|
|
99
|
+
data = self._fetchall(
|
|
100
|
+
api=self.connection.query_hs300_stocks,
|
|
101
|
+
**payload,
|
|
102
|
+
)
|
|
103
|
+
result = self.transform(data)
|
|
104
|
+
self._save_cache(result)
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
def transform(
|
|
108
|
+
self,
|
|
109
|
+
data: pd.DataFrame,
|
|
110
|
+
) -> pd.DataFrame:
|
|
111
|
+
cols = self.target.list_column_names()
|
|
112
|
+
if data is None or data.empty:
|
|
113
|
+
return pd.DataFrame(columns=cols)
|
|
114
|
+
|
|
115
|
+
out = data.copy()
|
|
116
|
+
out["update_date"] = out["updateDate"].astype(str)
|
|
117
|
+
out["code"] = out["code"].astype(str)
|
|
118
|
+
out["name"] = out["code_name"].astype(str)
|
|
119
|
+
|
|
120
|
+
# Finalize output
|
|
121
|
+
out = out[cols].drop_duplicates()
|
|
122
|
+
out = out.sort_values(by=["code"])
|
|
123
|
+
out = out.reset_index(drop=True)
|
|
124
|
+
self.markpoint("transform[OK]")
|
|
125
|
+
return out
|
|
126
|
+
|
|
127
|
+
def list_codes(self) -> List[str]:
|
|
128
|
+
df = self.run()
|
|
129
|
+
return sorted(df["code"].unique().tolist())
|
|
130
|
+
|
|
131
|
+
def list_names(self) -> List[str]:
|
|
132
|
+
df = self.run()
|
|
133
|
+
return sorted(df["name"].unique().tolist())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Tests for hs300index module."""
|