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,582 @@
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.minuteline.constant import (
11
+ KEY,
12
+ NAME,
13
+ SOURCE,
14
+ TARGET,
15
+ )
16
+ from xfintech.data.source.baostock.stock.minuteline.minuteline import Minuteline
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_history_k_data_plus = 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
+ "date": ["2026-01-10", "2026-01-10", "2026-01-09"],
48
+ "time": ["093500000", "094000000", "150000000"],
49
+ "code": ["sh.600000", "sh.600000", "sz.000001"],
50
+ "open": ["10.50", "10.60", "8.20"],
51
+ "high": ["10.80", "10.70", "8.45"],
52
+ "low": ["10.30", "10.40", "8.00"],
53
+ "close": ["10.75", "10.65", "8.35"],
54
+ "volume": ["6250000", "5500000", "4900000"],
55
+ "amount": ["672500000", "585500000", "406000000"],
56
+ "adjustflag": ["3", "3", "3"],
57
+ "turn": ["", "", ""],
58
+ "tradestatus": ["1", "1", "1"],
59
+ "pctChg": ["", "", ""],
60
+ "isST": ["0", "0", "0"],
61
+ }
62
+ )
63
+
64
+
65
+ # ============================================================================
66
+ # Initialization Tests
67
+ # ============================================================================
68
+
69
+
70
+ def test_minuteline_initialization_basic(mock_session):
71
+ """Test basic initialization"""
72
+ job = Minuteline(session=mock_session)
73
+
74
+ assert job.name == NAME
75
+ assert job.key == KEY
76
+ assert job.source == SOURCE
77
+ assert job.target == TARGET
78
+
79
+
80
+ def test_minuteline_initialization_with_params(mock_session):
81
+ """Test initialization with params"""
82
+ params = {"code": "sh.600000", "start_date": "2026-01-01"}
83
+ job = Minuteline(session=mock_session, params=params)
84
+
85
+ assert job.params.code == "sh.600000"
86
+ assert job.params.start_date == "2026-01-01"
87
+
88
+
89
+ def test_minuteline_initialization_with_all_components(mock_session):
90
+ """Test initialization with all components"""
91
+ params = {"code": "sh.600000", "frequency": "5", "adjustflag": "3"}
92
+ coolant = Coolant(interval=0.2)
93
+ retry = Retry(retry=3)
94
+ cache = Cache(path="/tmp/test_cache")
95
+
96
+ job = Minuteline(
97
+ session=mock_session,
98
+ params=params,
99
+ coolant=coolant,
100
+ retry=retry,
101
+ cache=cache,
102
+ )
103
+
104
+ assert job.params.code == "sh.600000"
105
+ assert job.coolant.interval == 0.2
106
+ assert job.retry.retry == 3
107
+ assert job.cache is not None
108
+ assert isinstance(job.cache, Cache)
109
+
110
+
111
+ def test_minuteline_name_and_key():
112
+ """Test name and key constants"""
113
+ assert NAME == "minuteline"
114
+ assert KEY == "/baostock/minuteline"
115
+
116
+
117
+ def test_minuteline_source_schema():
118
+ """Test source schema has all required columns"""
119
+ assert SOURCE is not None
120
+ assert SOURCE.desc == "A股分钟线行情数据(BaoStock格式)"
121
+
122
+ column_names = SOURCE.columns
123
+ assert "date" in column_names
124
+ assert "time" in column_names
125
+ assert "code" in column_names
126
+ assert "open" in column_names
127
+ assert "high" in column_names
128
+ assert "low" in column_names
129
+ assert "close" in column_names
130
+ assert "volume" in column_names
131
+ assert "amount" in column_names
132
+ assert "adjustflag" in column_names
133
+
134
+
135
+ def test_minuteline_target_schema():
136
+ """Test target schema has all required columns"""
137
+ assert TARGET is not None
138
+ assert TARGET.desc == "A股分钟线行情数据(xfintech格式)"
139
+
140
+ column_names = TARGET.columns
141
+ assert "code" in column_names
142
+ assert "date" in column_names
143
+ assert "time" in column_names
144
+ assert "open" in column_names
145
+ assert "high" in column_names
146
+ assert "low" in column_names
147
+ assert "close" in column_names
148
+ assert "volume" in column_names
149
+ assert "amount" in column_names
150
+ assert "adjustflag" in column_names
151
+
152
+
153
+ # ============================================================================
154
+ # Transform Tests
155
+ # ============================================================================
156
+
157
+
158
+ def test_minuteline_transform_basic(mock_session, sample_source_data):
159
+ """Test basic data transformation"""
160
+ job = Minuteline(session=mock_session)
161
+ result = job.transform(sample_source_data)
162
+
163
+ assert len(result) == 3
164
+ assert "code" in result.columns
165
+ assert "date" in result.columns
166
+ assert "time" in result.columns
167
+ assert result.iloc[0]["code"] == "sh.600000"
168
+ assert result.iloc[0]["date"] == "2026-01-10"
169
+
170
+
171
+ def test_minuteline_transform_field_types(mock_session, sample_source_data):
172
+ """Test field type conversions"""
173
+ job = Minuteline(session=mock_session)
174
+ result = job.transform(sample_source_data)
175
+
176
+ # Check string fields
177
+ assert isinstance(result.iloc[0]["code"], str)
178
+ assert isinstance(result.iloc[0]["date"], str)
179
+ assert isinstance(result.iloc[0]["time"], str)
180
+ assert isinstance(result.iloc[0]["adjustflag"], str)
181
+
182
+ # Check numeric fields (pandas converts to numpy types)
183
+ assert pd.api.types.is_numeric_dtype(result["open"])
184
+ assert pd.api.types.is_numeric_dtype(result["high"])
185
+ assert pd.api.types.is_numeric_dtype(result["low"])
186
+ assert pd.api.types.is_numeric_dtype(result["close"])
187
+ assert pd.api.types.is_numeric_dtype(result["volume"])
188
+ assert pd.api.types.is_numeric_dtype(result["amount"])
189
+
190
+
191
+ def test_minuteline_transform_price_fields(mock_session, sample_source_data):
192
+ """Test price field transformations"""
193
+ job = Minuteline(session=mock_session)
194
+ result = job.transform(sample_source_data)
195
+
196
+ row = result.iloc[0]
197
+ assert row["open"] == 10.50
198
+ assert row["high"] == 10.80
199
+ assert row["low"] == 10.30
200
+ assert row["close"] == 10.75
201
+
202
+
203
+ def test_minuteline_transform_volume_fields(mock_session, sample_source_data):
204
+ """Test volume field transformations"""
205
+ job = Minuteline(session=mock_session)
206
+ result = job.transform(sample_source_data)
207
+
208
+ row = result.iloc[0]
209
+ assert row["volume"] == 6250000.0
210
+ assert row["amount"] == 672500000.0
211
+
212
+
213
+ def test_minuteline_transform_empty_data(mock_session):
214
+ """Test transform with empty data"""
215
+ job = Minuteline(session=mock_session)
216
+
217
+ # Test with None
218
+ result = job.transform(None)
219
+ assert result.empty
220
+ assert len(result.columns) == len(TARGET.columns)
221
+
222
+ # Test with empty DataFrame
223
+ empty_df = pd.DataFrame()
224
+ result = job.transform(empty_df)
225
+ assert result.empty
226
+ assert len(result.columns) == len(TARGET.columns)
227
+
228
+
229
+ def test_minuteline_transform_invalid_data(mock_session):
230
+ """Test transform with invalid numeric values"""
231
+ data = pd.DataFrame(
232
+ {
233
+ "date": ["2026-01-10", "invalid_date"],
234
+ "time": ["093500000", "094000000"],
235
+ "code": ["sh.600000", "sh.600001"],
236
+ "open": ["10.50", "invalid"],
237
+ "high": ["10.80", "10.70"],
238
+ "low": ["10.30", "10.40"],
239
+ "close": ["10.75", "10.65"],
240
+ "volume": ["6250000", "5500000"],
241
+ "amount": ["672500000", "585500000"],
242
+ "adjustflag": ["3", "3"],
243
+ }
244
+ )
245
+ job = Minuteline(session=mock_session)
246
+ result = job.transform(data)
247
+
248
+ # Should handle invalid data gracefully
249
+ assert len(result) == 2
250
+ assert pd.isna(result.iloc[1]["open"]) # Invalid numeric
251
+
252
+
253
+ def test_minuteline_transform_duplicate_removal(mock_session):
254
+ """Test that duplicates are removed"""
255
+ data = pd.DataFrame(
256
+ {
257
+ "date": ["2026-01-10", "2026-01-10", "2026-01-10"],
258
+ "time": ["093500000", "093500000", "094000000"],
259
+ "code": ["sh.600000", "sh.600000", "sh.600000"],
260
+ "open": ["10.50", "10.50", "10.60"],
261
+ "high": ["10.80", "10.80", "10.70"],
262
+ "low": ["10.30", "10.30", "10.40"],
263
+ "close": ["10.75", "10.75", "10.65"],
264
+ "volume": ["6250000", "6250000", "5500000"],
265
+ "amount": ["672500000", "672500000", "585500000"],
266
+ "adjustflag": ["3", "3", "3"],
267
+ }
268
+ )
269
+ job = Minuteline(session=mock_session)
270
+ result = job.transform(data)
271
+
272
+ # Duplicates should be removed
273
+ assert len(result) == 2
274
+
275
+
276
+ def test_minuteline_transform_sorting(mock_session):
277
+ """Test that result is sorted by code, date, and time"""
278
+ data = pd.DataFrame(
279
+ {
280
+ "date": ["2026-01-10", "2026-01-09", "2026-01-10"],
281
+ "time": ["094000000", "150000000", "093500000"],
282
+ "code": ["sh.600000", "sz.000001", "sh.600000"],
283
+ "open": ["10.60", "8.20", "10.50"],
284
+ "high": ["10.70", "8.45", "10.80"],
285
+ "low": ["10.40", "8.00", "10.30"],
286
+ "close": ["10.65", "8.35", "10.75"],
287
+ "volume": ["5500000", "4900000", "6250000"],
288
+ "amount": ["585500000", "406000000", "672500000"],
289
+ "adjustflag": ["3", "3", "3"],
290
+ }
291
+ )
292
+ job = Minuteline(session=mock_session)
293
+ result = job.transform(data)
294
+
295
+ # Should be sorted by code, date, then time
296
+ assert result.iloc[0]["code"] == "sh.600000"
297
+ assert result.iloc[0]["time"] == "093500000"
298
+ assert result.iloc[1]["code"] == "sh.600000"
299
+ assert result.iloc[1]["time"] == "094000000"
300
+ assert result.iloc[2]["code"] == "sz.000001"
301
+
302
+
303
+ # ============================================================================
304
+ # Run Tests
305
+ # ============================================================================
306
+
307
+
308
+ def test_minuteline_run_basic(mock_session, sample_source_data):
309
+ """Test basic run method"""
310
+ job = Minuteline(session=mock_session)
311
+
312
+ with patch.object(job, "_fetchall", return_value=sample_source_data):
313
+ result = job.run()
314
+
315
+ assert isinstance(result, pd.DataFrame)
316
+ assert len(result) == 3
317
+ assert "code" in result.columns
318
+ assert "date" in result.columns
319
+ assert "time" in result.columns
320
+
321
+
322
+ def test_minuteline_run_with_code(mock_session, sample_source_data):
323
+ """Test run with code parameter"""
324
+ filtered_data = sample_source_data[sample_source_data["code"] == "sh.600000"]
325
+
326
+ job = Minuteline(session=mock_session, params={"code": "sh.600000"})
327
+
328
+ with patch.object(job, "_fetchall", return_value=filtered_data):
329
+ result = job.run()
330
+
331
+ assert len(result) == 2
332
+ assert result["code"].iloc[0] == "sh.600000"
333
+
334
+
335
+ def test_minuteline_run_with_frequency(mock_session, sample_source_data):
336
+ """Test run with frequency parameter"""
337
+ job = Minuteline(session=mock_session, params={"code": "sh.600000", "frequency": "15"})
338
+
339
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
340
+ job.run()
341
+ call_kwargs = mock_fetchall.call_args[1]
342
+ assert call_kwargs["frequency"] == "15"
343
+
344
+
345
+ def test_minuteline_run_with_adjustflag(mock_session, sample_source_data):
346
+ """Test run with adjustflag parameter"""
347
+ job = Minuteline(session=mock_session, params={"code": "sh.600000", "adjustflag": "1"})
348
+
349
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
350
+ job.run()
351
+ call_kwargs = mock_fetchall.call_args[1]
352
+ assert call_kwargs["adjustflag"] == "1"
353
+
354
+
355
+ def test_minuteline_run_with_date_range_string(mock_session, sample_source_data):
356
+ """Test run with start_date and end_date as strings"""
357
+ job = Minuteline(
358
+ session=mock_session,
359
+ params={"code": "sh.600000", "start_date": "2026-01-01", "end_date": "2026-01-31"},
360
+ )
361
+
362
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
363
+ job.run()
364
+
365
+ call_kwargs = mock_fetchall.call_args[1]
366
+ assert call_kwargs["start_date"] == "2026-01-01"
367
+ assert call_kwargs["end_date"] == "2026-01-31"
368
+
369
+
370
+ def test_minuteline_run_fields_parameter(mock_session, sample_source_data):
371
+ """Test that fields parameter is correctly constructed"""
372
+ job = Minuteline(session=mock_session, params={"code": "sh.600000"})
373
+
374
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
375
+ job.run()
376
+
377
+ call_kwargs = mock_fetchall.call_args[1]
378
+ assert "fields" in call_kwargs
379
+ fields = call_kwargs["fields"].split(",")
380
+ assert "date" in fields
381
+ assert "time" in fields
382
+ assert "code" in fields
383
+ assert "open" in fields
384
+ assert "close" in fields
385
+
386
+
387
+ def test_minuteline_run_calls_transform(mock_session, sample_source_data):
388
+ """Test that run calls transform"""
389
+ job = Minuteline(session=mock_session)
390
+
391
+ with patch.object(job, "_fetchall", return_value=sample_source_data):
392
+ with patch.object(job, "transform", wraps=job.transform) as mock_transform:
393
+ job.run()
394
+
395
+ mock_transform.assert_called_once()
396
+
397
+
398
+ # ============================================================================
399
+ # Cache Tests
400
+ # ============================================================================
401
+
402
+
403
+ def test_minuteline_cache_persistence(mock_session, sample_source_data):
404
+ """Test that cache persists across runs"""
405
+ job = Minuteline(session=mock_session, cache=True)
406
+
407
+ with patch.object(job, "_load_cache", return_value=None) as mock_load:
408
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
409
+ # First run - fetches data and caches it
410
+ result1 = job.run()
411
+ assert mock_fetchall.call_count == 1
412
+ assert mock_load.call_count == 1
413
+
414
+ # Second run - _load_cache still returns None, so _fetchall called again
415
+ result2 = job.run()
416
+ assert mock_fetchall.call_count == 2
417
+ assert mock_load.call_count == 2
418
+
419
+ pd.testing.assert_frame_equal(result1, result2)
420
+
421
+
422
+ def test_minuteline_params_identifier_uniqueness(mock_session):
423
+ """Test that different params create different cache keys"""
424
+ job1 = Minuteline(session=mock_session, params={"code": "sh.600000", "frequency": "5"}, cache=True)
425
+ job2 = Minuteline(session=mock_session, params={"code": "sh.600000", "frequency": "15"}, cache=True)
426
+
427
+ assert job1.params.identifier != job2.params.identifier
428
+
429
+
430
+ def test_minuteline_without_cache(mock_session, sample_source_data):
431
+ """Test that minuteline works correctly without cache"""
432
+ job = Minuteline(session=mock_session, cache=False)
433
+
434
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
435
+ job.run()
436
+ job.run()
437
+
438
+ # Should fetch twice (no caching)
439
+ assert mock_fetchall.call_count == 2
440
+
441
+
442
+ # ============================================================================
443
+ # Date Parsing Tests
444
+ # ============================================================================
445
+
446
+
447
+ def test_minuteline_date_parsing_yyyymmdd(mock_session, sample_source_data):
448
+ """Test date parsing from YYYYMMDD format"""
449
+ job = Minuteline(
450
+ session=mock_session,
451
+ params={"code": "sh.600000", "start_date": "20260101", "end_date": "20260131"},
452
+ )
453
+
454
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
455
+ job.run()
456
+
457
+ call_kwargs = mock_fetchall.call_args[1]
458
+ # Should convert YYYYMMDD to YYYY-MM-DD
459
+ assert call_kwargs["start_date"] == "2026-01-01"
460
+ assert call_kwargs["end_date"] == "2026-01-31"
461
+
462
+
463
+ def test_minuteline_date_parsing_hyphen_format(mock_session, sample_source_data):
464
+ """Test date parsing preserves YYYY-MM-DD format"""
465
+ job = Minuteline(
466
+ session=mock_session,
467
+ params={"code": "sh.600000", "start_date": "2026-01-01", "end_date": "2026-01-31"},
468
+ )
469
+
470
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
471
+ job.run()
472
+
473
+ call_kwargs = mock_fetchall.call_args[1]
474
+ assert call_kwargs["start_date"] == "2026-01-01"
475
+ assert call_kwargs["end_date"] == "2026-01-31"
476
+
477
+
478
+ # ============================================================================
479
+ # Integration Tests
480
+ # ============================================================================
481
+
482
+
483
+ def test_minuteline_full_workflow(mock_session, sample_source_data):
484
+ """Test complete workflow from initialization to data retrieval"""
485
+ job = Minuteline(
486
+ session=mock_session,
487
+ params={
488
+ "code": "sh.600000",
489
+ "start_date": "2026-01-01",
490
+ "end_date": "2026-01-31",
491
+ "frequency": "5",
492
+ "adjustflag": "3",
493
+ },
494
+ cache=False,
495
+ )
496
+
497
+ with patch.object(job, "_fetchall", return_value=sample_source_data):
498
+ result = job.run()
499
+
500
+ assert len(result) > 0
501
+ assert "code" in result.columns
502
+ assert "date" in result.columns
503
+ assert "time" in result.columns
504
+
505
+
506
+ def test_minuteline_large_dataset_handling(mock_session):
507
+ """Test handling of large datasets"""
508
+ # Create large dataset
509
+ large_data = pd.DataFrame(
510
+ {
511
+ "date": ["2026-01-10"] * 1000,
512
+ "time": [f"{str(i).zfill(9)}" for i in range(1000)],
513
+ "code": ["sh.600000"] * 1000,
514
+ "open": ["10.50"] * 1000,
515
+ "high": ["10.80"] * 1000,
516
+ "low": ["10.30"] * 1000,
517
+ "close": ["10.75"] * 1000,
518
+ "volume": ["6250000"] * 1000,
519
+ "amount": ["672500000"] * 1000,
520
+ "adjustflag": ["3"] * 1000,
521
+ }
522
+ )
523
+
524
+ job = Minuteline(session=mock_session, cache=False)
525
+
526
+ with patch.object(job, "_fetchall", return_value=large_data):
527
+ result = job.run()
528
+
529
+ assert len(result) == 1000
530
+
531
+
532
+ def test_minuteline_handles_missing_fields(mock_session):
533
+ """Test handling of data with some missing fields"""
534
+ data = pd.DataFrame(
535
+ {
536
+ "date": ["2026-01-10"],
537
+ "time": ["093500000"],
538
+ "code": ["sh.600000"],
539
+ "open": ["10.50"],
540
+ "high": ["10.80"],
541
+ "low": ["10.30"],
542
+ "close": ["10.75"],
543
+ "volume": [None], # Missing volume
544
+ "amount": [None], # Missing amount
545
+ "adjustflag": ["3"],
546
+ }
547
+ )
548
+
549
+ job = Minuteline(session=mock_session, cache=False)
550
+
551
+ with patch.object(job, "_fetchall", return_value=data):
552
+ result = job.run()
553
+
554
+ assert len(result) == 1
555
+ assert pd.isna(result.iloc[0]["volume"])
556
+ assert pd.isna(result.iloc[0]["amount"])
557
+
558
+
559
+ def test_minuteline_different_frequencies(mock_session, sample_source_data):
560
+ """Test different frequency options"""
561
+ frequencies = ["5", "15", "30", "60"]
562
+
563
+ for freq in frequencies:
564
+ job = Minuteline(session=mock_session, params={"code": "sh.600000", "frequency": freq})
565
+
566
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
567
+ job.run()
568
+ call_kwargs = mock_fetchall.call_args[1]
569
+ assert call_kwargs["frequency"] == freq
570
+
571
+
572
+ def test_minuteline_different_adjustflags(mock_session, sample_source_data):
573
+ """Test different adjustflag options"""
574
+ adjustflags = ["1", "2", "3"] # 1=后复权, 2=前复权, 3=不复权
575
+
576
+ for flag in adjustflags:
577
+ job = Minuteline(session=mock_session, params={"code": "sh.600000", "adjustflag": flag})
578
+
579
+ with patch.object(job, "_fetchall", return_value=sample_source_data) as mock_fetchall:
580
+ job.run()
581
+ call_kwargs = mock_fetchall.call_args[1]
582
+ assert call_kwargs["adjustflag"] == flag
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from xfintech.data.source.tushare.stock.stock.constant import (
4
+ KEY,
5
+ NAME,
6
+ PAGINATE,
7
+ SOURCE,
8
+ TARGET,
9
+ )
10
+ from xfintech.data.source.tushare.stock.stock.stock import Stock
11
+
12
+ __all__ = [
13
+ "Stock",
14
+ "KEY",
15
+ "NAME",
16
+ "PAGINATE",
17
+ "SOURCE",
18
+ "TARGET",
19
+ ]
@@ -0,0 +1,55 @@
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_all_stock"
10
+ URL = "http://www.baostock.com/mainContent?file=StockBasicInfoAPI.md"
11
+ ARGS = {
12
+ "day": {
13
+ "type": ColumnKind.STRING,
14
+ "required": "N",
15
+ "desc": "指定交易日(YYYYMMDD),默认当前交易日",
16
+ },
17
+ }
18
+
19
+ # Exported constants
20
+ NAME = "stock"
21
+ KEY = "/baostock/stock"
22
+ PAGINATE = Paginate(
23
+ pagesize=10000,
24
+ pagelimit=100,
25
+ )
26
+ SOURCE = TableInfo(
27
+ desc="上市股票基本信息(Baostock格式)",
28
+ meta={
29
+ "provider": PROVIDER,
30
+ "source": SOURCE_NAME,
31
+ "url": URL,
32
+ "args": ARGS,
33
+ "type": "partitioned",
34
+ "scale": "crosssection",
35
+ },
36
+ columns=[
37
+ ColumnInfo(name="code", kind=ColumnKind.STRING, desc="TS代码"),
38
+ ColumnInfo(name="tradeStatus", kind=ColumnKind.STRING, desc="交易状态"),
39
+ ColumnInfo(name="code_name", kind=ColumnKind.STRING, desc="股票名称"),
40
+ ],
41
+ )
42
+ TARGET = TableInfo(
43
+ desc="上市公司基本信息(xfintech格式)",
44
+ meta={
45
+ "key": KEY,
46
+ "name": NAME,
47
+ "type": "partitioned",
48
+ "scale": "crosssection",
49
+ },
50
+ columns=[
51
+ ColumnInfo(name="code", kind=ColumnKind.STRING, desc="TS代码"),
52
+ ColumnInfo(name="trade_status", kind=ColumnKind.STRING, desc="交易状态"),
53
+ ColumnInfo(name="name", kind=ColumnKind.STRING, desc="股票名称"),
54
+ ],
55
+ )