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