diff --git a/.env b/.env
index 0562205..5fa6270 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,4 @@
TUSHARE_TOKEN=f62b415de0a5a947fcb693b66cd299dd6242868bf04ad687800c7f3f
ALPHA_VANTAGE_KEY=2ROWPV7BMW6JSG0Y
+JQUANTS_REFRESH_TOKEN=eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.AzlbVPyDZZ1OQc35ICxAtQeb2j51DhSGy69dihg6lEznIgRNgHn5wmhFOnNz_Nh7z-1AJj66d2T4Naj76lRpHhXk5FCViUzZqaNkz9Q_TnoHt90Pp_Op-1cHORbjfCGpW3cvT9En6UZxodjDliRzqCpqJ52UCLLWUJfJIwyFy5-QcWTEOGOdVRXTHtihy-w7ylw7ocUj3gd34NLGPr45yG0fTmo7om_MWBPbVsWvWOjdvrEtee43xEuZTheqbRgU3IRxa7ff1zIRDSrbH-nWIBPsh-XXV2r2OZTBARairjnUA0Ikma224UF_m5acpPwcs4UwrbeKyq13K3N1f3ohDA.6Zi0U52Kps9h3ezu.RPeBbqcqaca9F8ZuSkLN8o5Kk-IARoHW5ij58DFjYpvc7-ZXhATPYOBHJKQ3tFL2fE08nnb9TMmjvErsF7MFxcxt1XnDfhbak55SFzw3yGfXhD6VqeLaSoPwcs_cFuXNtfQEeibyMpcUUj2dXltgc1axqIA1Lndyw9RMPOyH3b0wShyd7WCPiO7amktMUNDMkbgUDR6wYGEpWYxug9ySaGuu0JSAH6sDR2TRkuUOhgBfhsKIvqSQNahM5yRQpeJ0zvY3UOUxADyaKTTK_kNco9mMjoymQIK6eO30cCJva89hITytncWDxRCG5TsMuXJeqHkg2_RCZ6gckSZAYkiWmeXrw26qBOqZ8efV5aMo4D6NMLPVLyPeLJX3Vnpuzw_ySpMKewhmTmSaKZUXRWc06VXIln8YVZDAH7uGbkZHk4Gq0nDxH6xH7sSQROFOZlPuYeaJ5Ye07-_0QunvvVwCtvF69GpURukG8s7qQ-c6x5iWPJa0icommt6I6EHif3_X_oaHfcioQgjDD3zJIhKjHHZGp4yZhskuXhV3p-M9FiKeJFMhBK085UkopMFUHGa6p3DPmxA-UzVb8UbPqE4xvYrqujLg3rnwSOQjpnzLNcjVy9GhKVHqIpTTLtAmaXinkM4LExPfyVlGOAbE70q1C_VtEMIbwCNh5yd1JMo__WP-w_VQt9X_GF1k_8bUGLRJEUYIhuZX9cgZkFwiUCdSXOxBOZ6XM8vnTYj0J7bMkkbaJNIITzPEAz1HjbyN0N82os3aLq9PpMsay0QLREfkzKaWdjWs-Dj9mjJl7rl22GFe_wC1sT90W9iGMH8pjNsEU8byFXe_Gb6ZCzfWydk2eK1LRMW_N_IMg4oA-bp92dToAhiHci793mkDwDVqe_Yd06CClVZfX1cSuPZQfj2OwARQWqHrN6ZX7UnnHippvcvrJmqABDEHL-JyA5wcdCQOkHqQZsZvXTlDO6nyucTwOrI9jRStdjbFSpqDJV7unIVztEezzQFleLQ1bumMJwkvWHIqxM4js1d8xajFIBcvWC4BndlU0zp5Th39PShFb0_cmhEbhCSpzaofWW5-BLPmhIxyIAGbXKAR2mUwfBYnNzGdPffa3-EOiYR-CfYHFn9Vq7DZivcAj9FbMiOWaU5L-qU_ucpKOJ85gSUudVt59JMZWHniXdiURQUEuz3Q5ntCVM1Dhh2ir_-JHVpuK7VZ13HFgaMAE5zE4iIBltGo2XDT_5F1KsqoHVNtMnRSs8syN-kBsUEuQNwaZr5Jh6e4cmQKdHWRIycHMouYhTGiZHA5-R_lNtuipLyERQ-agaUGyZ7eqY6rDrk1Vsbbf_oAqfs_P3plB60jRQ.AkfSJSjSunnRAS2Ahrjx1Q
+IFIND_REFRESH_TOKEN=eyJzaWduX3RpbWUiOiIyMDI1LTExLTAzIDEwOjE2OjU4In0=.eyJ1aWQiOiI3MjMwNDQwNzciLCJ1c2VyIjp7ImFjY291bnQiOiJ3eGhsdHowMDEiLCJhdXRoVXNlckluZm8iOnsiRVRyYW5zZmVyIjp0cnVlLCJFZXhjZWxQYXllcnMiOiIxNzk4NjgzMDAyMDAwIn0sImNvZGVDU0kiOltdLCJjb2RlWnpBdXRoIjpbXSwiaGFzQUlQcmVkaWN0IjpmYWxzZSwiaGFzQUlUYWxrIjpmYWxzZSwiaGFzQ0lDQyI6ZmFsc2UsImhhc0NTSSI6ZmFsc2UsImhhc0V2ZW50RHJpdmUiOmZhbHNlLCJoYXNGVFNFIjpmYWxzZSwiaGFzRmFzdCI6ZmFsc2UsImhhc0Z1bmRWYWx1YXRpb24iOmZhbHNlLCJoYXNISyI6dHJ1ZSwiaGFzTE1FIjpmYWxzZSwiaGFzTGV2ZWwyIjpmYWxzZSwiaGFzUmVhbENNRSI6ZmFsc2UsImhhc1RyYW5zZmVyIjpmYWxzZSwiaGFzVVMiOmZhbHNlLCJoYXNVU0FJbmRleCI6ZmFsc2UsImhhc1VTREVCVCI6ZmFsc2UsIm1hcmtldEF1dGgiOnsiRENFIjpmYWxzZX0sIm1hcmtldENvZGUiOiIxNjszMjsxNDQ7MTc2OzExMjs4ODs0ODsxMjg7MTY4LTE7MTg0OzIwMDsyMTY7MTA0OzEyMDsxMzY7MjMyOzU2Ozk2OzE2MDs2NDsiLCJtYXhPbkxpbmUiOjEsIm5vRGlzayI6ZmFsc2UsInByb2R1Y3RUeXBlIjoiU1VQRVJDT01NQU5EUFJPRFVDVCIsInJlZnJlc2hUb2tlbiI6IiIsInJlZnJlc2hUb2tlbkV4cGlyZWRUaW1lIjoiMjAyNi0xMi0zMSAxMDoxMDowMiIsInNlc3NzaW9uIjoiNDI3MzdjMjIyN2I5ZTEwNzY5NmJiYWNmNzhmNzY1ODMiLCJzaWRJbmZvIjp7NjQ6IjExMTExMTExMTExMTExMTExMTExMTExMSIsMToiMTAxIiwyOiIxIiw2NzoiMTAxMTExMTExMTExMTExMTExMTExMTExIiwzOiIxIiw2OToiMTExMTExMTExMTExMTExMTExMTExMTExMSIsNToiMSIsNjoiMSIsNzE6IjExMTExMTExMTExMTExMTExMTExMTEwMCIsNzoiMTExMTExMTExMTEiLDg6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxIiwxMzg6IjExMTExMTExMTExMTExMTExMTExMTExMTEiLDEzOToiMTExMTExMTExMTExMTExMTExMTExMTExMSIsMTQwOiIxMTExMTExMTExMTExMTExMTExMTExMTExIiwxNDE6IjExMTExMTExMTExMTExMTExMTExMTExMTEiLDE0MjoiMTExMTExMTExMTExMTExMTExMTExMTExMSIsMTQzOiIxMSIsODA6IjExMTExMTExMTExMTExMTExMTExMTExMSIsODE6IjExMTExMTExMTExMTExMTExMTExMTExMSIsODI6IjExMTExMTExMTExMTExMTExMTExMDExMCIsODM6IjExMTExMTExMTExMTExMTExMTAwMDAwMCIsODU6IjAxMTExMTExMTExMTExMTExMTExMTExMSIsODc6IjExMTExMTExMDAxMTExMTAxMTExMTExMSIsODk6IjExMTExMTExMDExMDEwMDAwMDAwMTExMSIsOTA6IjExMTExMDExMTExMTExMTExMDAwMTExMTEwIiw5MzoiMTExMTExMTExMTExMTExMTEwMDAwMTExMSIsOTQ6IjExMTExMTExMTExMTExMTExMTExMTExMTEiLDk2OiIxMTExMTExMTExMTExMTExMTExMTExMTExIiw5OToiMTAwIiwxMDA6IjExMTEwMTExMTExMTExMTExMTAiLDEwMjoiMSIsNDQ6IjExIiwxMDk6IjEiLDUzOiIxMTExMTExMTExMTExMTExMTExMTExMTEiLDU0OiIxMTAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCIsNTc6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMTAwMDAwMDAwIiw2MjoiMTExMTExMTExMTExMTExMTExMTExMTExIiw2MzoiMTExMTExMTExMTExMTExMTExMTExMTExIn0sInRpbWVzdGFtcCI6IjE3NjIxMzYyMTgxMDIiLCJ0cmFuc0F1dGgiOmZhbHNlLCJ0dGxWYWx1ZSI6MCwidWlkIjoiNzIzMDQ0MDc3IiwidXNlclR5cGUiOiJGUkVFSUFMIiwid2lmaW5kTGltaXRNYXAiOnt9fX0=.432D186AC7B7C51EB66BEC42EDD5FB96A5C7EF03CE5D070B1BE5A409A46AFDBE
diff --git a/DOC/API手册/API接口 b/DOC/API手册/API接口
new file mode 100644
index 0000000..513c95e
--- /dev/null
+++ b/DOC/API手册/API接口
@@ -0,0 +1,25 @@
+资产负债表
+//基础函数-现金及现金等价物及短期投资(OAS);应收账款及票据(OAS);存货(OAS);固定资产净额(OAS);长期投资与长期应收款(OAS);商誉及无形资产(OAS);短期债务(OAS);短期借款(OAS);应付账款及票据(OAS);合同负债(流动)(OAS);预收款项(流动)(OAS);递延收入(流动)(OAS);长期债务(OAS);长期借款(OAS);资产合计(OAS);归属于母公司股东权益(OAS)-iFinD数据接口
+requestMethod:POST
+requestURL:https://quantapi.51ifind.com/api/v1/basic_data_service
+requestHeaders:{"Content-Type":"application/json","access_token":"c9967e4e36bcd9b726f39bd90ad9d0cd871799ef.signs_NzIzMDQ0MDc3"}
+formData:{"codes":"0700.HK,7203.T,AAPL.O","indipara":[{"indicator":"cash_equi_short_term_inve_oas","indiparams":["20241231","1","CNY"]},{"indicator":"accou_and_notes_recei_oas","indiparams":["20241231","1","CNY"]},{"indicator":"inventories_oas","indiparams":["20241231","1","CNY"]},{"indicator":"ppe_net_oas","indiparams":["20241231","1","CNY"]},{"indicator":"long_term_inv_and_receiv_oas","indiparams":["20241231","1","CNY"]},{"indicator":"goodwill_and_intasset_oas","indiparams":["20241231","1","CNY"]},{"indicator":"short_term_debt_oas","indiparams":["20241231","1","CNY"]},{"indicator":"short_term_borrowings_oas","indiparams":["20241231","1","CNY"]},{"indicator":"account_and_note_payable_oas","indiparams":["20241231","1","CNY"]},{"indicator":"contra_liabilities_current_oas","indiparams":["20241231","1","CNY"]},{"indicator":"advance_from_cust_current_oas","indiparams":["20241231","1","CNY"]},{"indicator":"defer_revenue_current_oas","indiparams":["20241231","1","CNY"]},{"indicator":"long_term_debt_oas","indiparams":["20241231","1","CNY"]},{"indicator":"long_term_borrowings_oas","indiparams":["20241231","1","CNY"]},{"indicator":"total_assets_oas","indiparams":["20241231","1","CNY"]},{"indicator":"equity_attri_to_companyowner_oas","indiparams":["20241231","1","CNY"]}]}
+
+//基础函数-营业收入(OAS);毛利(OAS);销售、一般及管理费用(OAS);销售及营销费用(OAS);一般及管理费用(OAS);研发费用(OAS);所得税费用(OAS);归属于普通股股东的净利润(OAS)-iFinD数据接口
+requestMethod:POST
+requestURL:https://quantapi.51ifind.com/api/v1/basic_data_service
+requestHeaders:{"Content-Type":"application/json","access_token":"c9967e4e36bcd9b726f39bd90ad9d0cd871799ef.signs_NzIzMDQ0MDc3"}
+formData:{"codes":"0700.HK,7203.T,AAPL.O","indipara":[{"indicator":"revenue_oas","indiparams":["20241231","1","CNY"]},{"indicator":"gross_profit_oas","indiparams":["20241231","1","CNY"]},{"indicator":"sga_expenses_oas","indiparams":["20241231","1","CNY"]},{"indicator":"selling_marketing_expenses_oas","indiparams":["20241231","1","CNY"]},{"indicator":"ga_expenses_oas","indiparams":["20241231","1","CNY"]},{"indicator":"rd_expenses_oas","indiparams":["20241231","1","CNY"]},{"indicator":"income_tax_expense_oas","indiparams":["20241231","1","CNY"]},{"indicator":"net_income_attri_to_common_sh_oas","indiparams":["20241231","1","CNY"]}]}
+
+//基础函数-经营活动现金流量净额(OAS);购买固定及无形资产(OAS);支付股利(OAS)-iFinD数据接口
+requestMethod:POST
+requestURL:https://quantapi.51ifind.com/api/v1/basic_data_service
+requestHeaders:{"Content-Type":"application/json","access_token":"c9967e4e36bcd9b726f39bd90ad9d0cd871799ef.signs_NzIzMDQ0MDc3"}
+formData:{"codes":"0700.HK,7203.T,AAPL.O","indipara":[{"indicator":"net_cash_flows_from_oa_oas","indiparams":["20241231","1","CNY"]},{"indicator":"purchase_of_ppe_and_ia_oas","indiparams":["20241231","1","CNY"]},{"indicator":"dividends_paid_oas","indiparams":["20241231","1","CNY"]}]}
+
+//基础函数-公司中文名称;会计年结日;上市日期-iFinD数据接口
+requestMethod:POST
+requestURL:https://quantapi.51ifind.com/api/v1/basic_data_service
+requestHeaders:{"Content-Type":"application/json","access_token":"c9967e4e36bcd9b726f39bd90ad9d0cd871799ef.signs_NzIzMDQ0MDc3"}
+formData:{"codes":"7203.T","indipara":[{"indicator":"corp_cn_name","indiparams":[]},{"indicator":"accounting_date","indiparams":[]},{"indicator":"ipo_date","indiparams":[]}]}
+
diff --git a/DOC/API手册/iFinD HTTP API 用户手册.pdf b/DOC/API手册/iFinD HTTP API 用户手册.pdf
new file mode 100644
index 0000000..64cdf08
Binary files /dev/null and b/DOC/API手册/iFinD HTTP API 用户手册.pdf differ
diff --git a/DOC/API手册/ifind_manual.txt b/DOC/API手册/ifind_manual.txt
new file mode 100644
index 0000000..ab5871f
--- /dev/null
+++ b/DOC/API手册/ifind_manual.txt
@@ -0,0 +1,7200 @@
+iFinD HTTP API 用户手册
+版本
+
+时间
+
+更新说明
+
+1.0
+
+2021-12-29
+
+初版发布
+
+2.0
+
+2024-03-29
+
+迭代版本
+
+2.1
+
+2025-08-25
+
+说明修正
+
+目录
+一、TOKEN获取与使用
+1、refresh_token说明
+2、access_token说明
+1)、获取当前有效的access_token
+2)、获取一个新的access_token
+3)、使用access_token向同花顺服务器取数
+二、各函数URL及formData生成逻辑
+1、基础数据
+2、日期序列
+3、历史行情
+4、高频序列
+5、实时行情
+6、日内快照
+7、经济数据库(EDB)
+8、专题报表函数
+9、组合管理
+(1)组合新建
+(2)组合导入
+1). 模板导入
+2).文件导入
+3).状态查询
+(3)现金存取
+(4)普通交易
+(5)交易流水
+(6)组合监控
+(7)持仓分析
+(8)绩效指标
+(9)风险指标
+10、智能选股
+11、基金实时估值(分钟)
+12、基金实时估值(日)
+13、日期查询函数
+14、日期偏移函数
+15、数据量查询
+16、错误信息查询
+17、证券代码证券简称转同花顺代码
+18、公告查询
+三、错误说明
+四、适用范围
+
+1 / 43
+
+五、版本管理
+iFinD HTTP API是对过去各语言SDK形式的一个补充,用户可以以API形式直接向同花顺服务器发送HTTP请求,运行环境不
+再需要下载SDK,从而使用户摆脱设备、语言、环境的限制。
+
+一、TOKEN获取与使用
+接口鉴权方案分为长期的refresh_token和短期的access_token。
+
+1、refresh_token说明:
+作用:refresh_token只用来请求当前有效的access_token或者获取一个新access_token。
+有效期:refresh_token与获取时账号到期日一致,如账号有续期或者权限变更,需要更新refresh_token来更新权限。
+获取方式:refresh_token可以通过Windows接口包中超级命令客户端"工具-refresh_token查询/更新"或者网页版本超级命令账号信息查看或者更新。
+注意:refresh_token更新后,所有环境过去的refresh_token、access_token均会失效,更refresh_token相当于更改HTTP接口
+的账号密码。
+
+2、access_token说明:
+作用:access_token用来直接向同花顺服务器请求数据。
+有效期:access_token会在初次生成的七天后失效。
+注意:单个access_token最多支持绑定20个IP。
+
+1)、获取当前有效的access_token
+请求参数
+项目
+
+传参说明
+
+URL
+
+https://quantapi.51ifind.com/api/v1/get_access_token
+
+requestMethod
+
+POST/GET
+
+requestHeaders
+
+{"Content-Type":"application/json","refresh_token":user_refresh_token}
+
+注:refresh_token放BODY也可
+
+2)、获取一个新的access_token
+获取一个新的access_token会造成所有旧的access_token失效
+请求参数
+项目
+
+传参说明
+
+URL
+
+https://quantapi.51ifind.com/api/v1/update_access_token
+
+requestMethod
+
+POST/GET
+
+requestHeaders
+
+{"Content-Type":"application/json","refresh_token":user_refresh_token}
+
+示例——使用python请求当前有效的access_token
+
+2 / 43
+
+import requests
+import json
+getAccessTokenUrl = 'https://quantapi.51ifind.com/api/v1/get_access_token '
+refreshToken ='eyJzaWduX3RpbWUiOiIyMDIxLTEyduX3RpbWUiO iIyMjI1In0=.eyJ1aWQiO
+iIxMDYxMDUwMDMifQ==.F4CBBBC230969B0F220F9D6ECB666A230969B0F220FFBBCDA4156A3B78A1BB896'
+getAccessTokenHeader = {"Content-Type":"application/json","refresh_token":refreshtoken}
+getAccessTokenResponse=requests.post(url=getAccessTokenUrl,headers=getAccessTokenHeader)
+accessToken = json.loads(getAccessTokenResponse.content)['data']['access_token'] print(accessToken)
+
+3)、使用access_token向同花顺服务器取数
+使用超级命令协助获取协议
+基础函数、日期序列函数、EDB函数、专题报表函数的指标与科目过多,很难把所有内容都集中在文档中,目前还是推荐用
+户使用Windows SDK接口包中的超级命令终端或者网页版本超级命令协助获取协议。
+
+协议说明
+requestMethod需要为POST
+requestHeaders需要包含{"Content-Type":"application/json","access_token":user_access_token}
+各函数的formData或者requestURL见下方协议或者使用超级命令生成
+请求参数需要统一处理为urlencode,请求参数压缩支持:Accept-Encoding: gzip,deflate
+返回内容统一为unicode编码
+示例——以Python请求300033实时行情为例
+# -*- coding: utf-8 -*import requests
+th sUrl = 'https://quantapi.51ifind.com/api/v1/real_time_quotation'
+accessToken = '12fe737bc2014f39f195a2b7b03e3b11ec63b66b'
+thsHeaders = {"Content-Type":"application/json","access_token":accessToken}
+thsPara = {"codes":"300033.SZ","indicators":"open,high,low,latest"}
+thsResponse = requests.post(url=thsUrl,json=thsPara,headers=thsHeaders)
+print(thsResponse.content)
+
+二、各函数URL及formData生成逻辑
+1、基础数据
+URL
+https://quantapi.51ifind.com/api/v1/basic_data_service
+
+formData
+
+3 / 43
+
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+indipara
+
+是
+
+各个指标及其相关参数,indicator代表指标英文名,indiparams代表
+该指标的用户层的参数,otherparams代表用户无需知晓但传输给服
+务端所需的其他参数。otherparams中sys用来标记服务端所需的
+name中为True的参数。推荐使用超级命令生成。
+
+见下方代码块
+
+示例
+para =
+{
+"codes": "300033.SZ,600030.SH",
+"indipara": [{
+"indicator": "ths_roe_stock",
+"indiparams": ["20241231"]
+}, {
+"indicator": "ths_roe_avg_by_ths_stock",
+"indiparams": ["20241231"]
+}]
+}
+
+该示例表示提取同花顺和中信证券在2024年年报的净资产收益率ROE;净资产收益率ROE(平均,同花顺计算)
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+2、日期序列
+URL
+https://quantapi.51ifind.com/api/v1/date_sequence
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+functionpara
+
+否
+
+key-value格式。所有key均取默认时,functionpara省略。
+
+见下方说明
+
+startdate
+
+是
+
+开始日期,支持"YYYYMMDD""YYYY-MM-DD""YYYY/MM/DD"三
+种日期格式
+
+"startdate":"2018-01-01"
+
+enddate
+
+是
+
+结束日期,支持"YYYYMMDD""YYYY-MM-DD""YYYY/MM/DD"三
+种日期格式
+
+"enddate":"2018-01-01"
+
+4 / 43
+
+key
+
+是否
+必须
+
+value
+
+示例
+
+indipara
+
+是
+
+各个指标及其相关参数,indicator代表指标英文名,indiparams
+代表该指标的用户层的参数,otherparams代表用户无需知晓但
+传输给服务端所需的其他参数。otherparams中sys用来标记服务
+端所需的name中为True的参数。推荐使用Windows超级命令生
+成。
+
+见下方代码块
+
+functionpara说明
+名称
+
+keys
+
+value说明
+
+省略时逻辑
+
+时间周期
+
+Interval
+
+D-日 W-周 M-月 Q-季 S-半年 Y-年
+
+D-日
+
+日期类型
+
+Days
+
+Tradedays-交易日 Alldays-日历日
+
+Tradedays-交易日
+
+非交易间隔处理
+
+Fill
+
+Previous-沿用之前数据 Blank-空值
+
+Previous-沿用之前数据
+
+示例
+para =
+{
+"codes": "300033.SZ,600030.SH",
+"startdate": "20230101",
+"enddate": "20241231",
+"functionpara": {
+"Days": "Alldays",
+"Fill": "Blank",
+"Interval": "Y"
+},
+"indipara": [{
+"indicator": "ths_total_equity_atoopc_stock",
+"indiparams": ["", "100"]
+}, {
+"indicator": "ths_regular_report_actual_dd_stock",
+"indiparams": [""]
+}]
+}
+
+该示例表示提取同花顺和中信证券在2023-24年年报的归属于母公司所有者权益合计;定期报告实际披露日期
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+3、历史行情
+URL
+
+5 / 43
+
+https://quantapi.51ifind.com/api/v1/cmd_history_quotation
+
+formData
+key
+
+是否必
+须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+indicators
+
+是
+
+半角逗号分隔的所有指标
+
+"indicators":"preClose,open"
+
+functionpara
+
+否
+
+key-value格式。所有key均取默认时,functionpara省略。
+
+见下方说明
+
+startdate
+
+是
+
+开始日期,支持"YYYYMMDD""YYYY-MMDD""YYYY/MM/DD"三种日期格式
+
+"startdate":"2018-01-01"
+
+enddate
+
+是
+
+结束日期,支持"YYYYMMDD""YYYY-MMDD""YYYY/MM/DD"三种日期格式
+
+"enddate":"2018-01-01"
+
+indicators参数说明
+指标名
+
+指标说明
+
+preClose
+
+前收盘价
+
+open
+
+开盘价
+
+high
+
+最高价
+
+low
+
+最低价
+
+close
+
+收盘价
+
+avgPrice
+
+均价
+
+change
+
+涨跌
+
+changeRatio
+
+涨跌幅
+
+volume
+
+成交量
+
+amount
+
+成交额
+
+turnoverRatio
+
+换手率
+
+transactionAmount
+
+成交笔数
+
+totalShares
+
+总股本
+
+totalCapital
+
+总市值
+
+floatSharesOfAShares
+
+A股流通股本
+
+floatSharesOfBShares
+
+B股流通股本
+
+floatCapitalOfAShares
+
+A股流通市值
+
+floatCapitalOfBShares
+
+B股流通市值
+
+pe_ttm
+
+市盈率(TTM)
+
+pe
+
+PE市盈率
+
+pb
+
+PB市净率
+
+ps
+
+PS市销率
+
+pcf
+
+PCF市现率
+
+ths_trading_status_stock
+
+交易状态
+
+ths_up_and_down_status_stock
+
+涨跌停状态
+
+ths_af_stock
+
+复权因子
+
+ths_vol_after_trading_stock
+
+盘后成交量
+
+ths_trans_num_after_trading_stock
+
+盘后成交笔数
+
+ths_amt_after_trading_stock
+
+盘后成交额
+
+指标备注
+
+6 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+ths_vaild_turnover_stock
+
+有效换手率
+
+netAssetValue
+
+单位净值
+
+基金专用
+
+adjustedNAV
+
+复权单位净值
+
+基金专用
+
+accumulatedNAV
+
+累计单位净值
+
+基金专用
+
+premium
+
+贴水
+
+基金专用
+
+premiumRatio
+
+贴水率
+
+基金专用
+
+estimatedPosition
+
+估算仓位
+
+基金专用
+
+floatCapital
+
+流通市值
+
+指数专用
+
+pe_ttm_index
+
+PE(TTM)
+
+指数专用
+
+pb_mrq
+
+PB(MRQ)
+
+指数专用
+
+pe_indexPublisher
+
+PE(指数发布方)
+
+指数专用
+
+yieldMaturity
+
+到期收益率
+
+债券专用
+
+remainingTerm
+
+剩余期限
+
+债券专用
+
+maxwellDuration
+
+麦氏久期
+
+债券专用
+
+modifiedDuration
+
+修正久期
+
+债券专用
+
+convexity
+
+凸性
+
+债券专用
+
+close_2330
+
+收盘价(23:30)
+
+外汇交易中心专用
+
+openInterest
+
+持仓量
+
+期权专用
+
+positionChange
+
+持仓变动
+
+期权专用
+
+preSettlement
+
+前结算价
+
+期货专用
+
+settlement
+
+结算价
+
+期货专用
+
+change_settlement
+
+涨跌(结算价)
+
+期货专用
+
+chg_settlement
+
+涨跌幅(结算价)
+
+期货专用
+
+openInterest
+
+持仓量
+
+期货专用
+
+positionChange
+
+持仓变动
+
+期货专用
+
+amplitude
+
+振幅
+
+期货专用
+
+functionpara参数说明
+名称
+
+keys
+
+value说明
+
+省略时逻辑
+
+时间周
+期
+
+Interval
+
+D-日 W-周 M-月 Q-季 S-半年 Y-年 同抽样周期二选一,返回周期汇总统计值
+
+D-日
+
+抽样周
+期
+
+SampleInterval
+
+D-日 W-周 M-月 Q-季 S-半年 Y-年 同时间周期二选一,返回周期最后一个交
+易日日频数据
+
+D-日
+
+复权方
+式
+
+CPS
+
+1-不复权 2-前复权(分红再投) 3-后复权(分红再投) 4-全流通前复权
+(分红再投) 5-全流通后复权(分红再投) 6-前复权(现金分红) 7-后复
+权(现金分红)
+
+1-不复权
+
+报价类
+型
+
+PriceType
+
+1-全价 2-净价 仅债券生效
+
+1-全价
+
+非交易
+间隔处
+理
+
+Fill
+
+Previous-沿用之前数据 Blank-空值 具体数值-自定义数值 Omit-缺省值
+
+Previous-沿用
+之前数据
+
+设定复
+权基点
+
+BaseDate
+
+复权基点日期,"YYYY-MM-DD"
+
+后复权按上市
+日,前复权按
+最新日
+
+货币
+
+Currency
+
+MHB-美元 GHB-港元 RMB-人民币 YSHB-原始货币
+
+YSHB-原始货
+币
+
+7 / 43
+
+示例
+para =
+{
+"codes": "300033.SZ,600030.SH",
+"indicators": "open,close,volume",
+"startdate": "2024-08-25",
+"enddate": "2025-08-25",
+"functionpara": {
+"Interval": "W",
+"CPS": "3",
+"Currency": "RMB",
+"Fill": "Blank"
+}
+}
+
+该示例表示提取同花顺和中信证券在20240825-20250825年周频率的开盘价、收盘价、成交量 后复权分红再投数据
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+4、高频序列
+URL
+https://quantapi.51ifind.com/api/v1/high_frequency
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+indicators
+
+是
+
+半角逗号分隔所有指标
+
+"indicators":"open,high"
+
+functionpara
+
+否
+
+key-value格式。所有key均取默认时,functionpara省略。技术
+指标额外在calculate生成,生成规则见下文。
+
+见下方代码块
+
+starttime
+
+是
+
+开始日期,支持"YYYYMMDD HH:mm:ss""YYYY-MM-DD
+HH:mm:ss""YYYY/MM/DD HH:mm:ss"三种时间格式
+
+"starttime":"2018-01-01
+09:15:00"
+
+endtime
+
+是
+
+结束日期,支持"YYYYMMDD HH:mm:ss""YYYY-MM-DD
+HH:mm:ss""YYYY/MM/DD HH:mm:ss"三种日期格式
+
+"endtime":"2018-01-01
+15:15:00"
+
+indicators参数说明
+指标名
+
+指标说明
+
+指标备注
+
+open
+
+开盘价
+
+通用
+
+8 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+high
+
+最高价
+
+通用
+
+low
+
+最低价
+
+通用
+
+close
+
+收盘价
+
+通用
+
+avgPrice
+
+均价
+
+通用
+
+volume
+
+成交量
+
+通用
+
+amount
+
+成交额
+
+通用
+
+change
+
+涨跌
+
+通用
+
+changeRatio
+
+涨跌幅
+
+通用
+
+turnoverRatio
+
+换手率
+
+通用
+
+sellVolume
+
+内盘
+
+通用
+
+buyVolume
+
+外盘
+
+通用
+
+changeRatio_accumulated
+
+涨跌幅(累计)
+
+股票,仅支持当天
+
+BBI
+
+BBI多空指数
+
+股票
+
+DDI
+
+DDI方向标准离差指数
+
+股票
+
+DMA
+
+DMA平均线差
+
+股票
+
+MA
+
+MA简单移动平均
+
+股票
+
+EXPMA
+
+EXPMA指数平均数
+
+股票
+
+MACD
+
+MACD指数平滑异同平均
+
+股票
+
+MTM
+
+MTM动力指标
+
+股票
+
+PRICEOSC
+
+PRICEOSC价格振荡指标
+
+股票
+
+TRIX
+
+TRIX三重指数平滑平均
+
+股票
+
+BIAS
+
+BIAS乖离率
+
+股票
+
+CCI
+
+CCI顺势指标
+
+股票
+
+DBCD
+
+DBCD异同离差乖离率
+
+股票
+
+DPO
+
+DPO区间震荡线
+
+股票
+
+KDJ
+
+KDJ随机指标
+
+股票
+
+LWR
+
+LWR威廉指标
+
+股票
+
+ROC
+
+ROC变动速率
+
+股票
+
+RSI
+
+RSI相对强弱指标
+
+股票
+
+SI
+
+SI摆动指标
+
+股票
+
+SRDM
+
+SRDM动向速度比率
+
+股票
+
+VROC
+
+VROC量变动速率
+
+股票
+
+VRSI
+
+VRSI量相对强弱
+
+股票
+
+WR
+
+WR威廉指标
+
+股票
+
+ARBR
+
+ARBR人气意愿指标
+
+股票
+
+CR
+
+CR能量指标
+
+股票
+
+PSY
+
+PSY心理指标
+
+股票
+
+VR
+
+VR成交量比率
+
+股票
+
+WAD
+
+WAD威廉聚散指标
+
+股票
+
+MFI
+
+MFI资金流向指标
+
+股票
+
+OBV
+
+OBV能量潮
+
+股票
+
+PVT
+
+PVT量价趋势指标
+
+股票
+
+WVAD
+
+WVAD威廉变异离散量
+
+股票
+
+9 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+BBIBOLL
+
+BBIBOLL多空布林线
+
+股票
+
+BOLL
+
+BOLL布林线
+
+股票
+
+CDP
+
+CDP逆势操作
+
+股票
+
+ENV
+
+ENV指标
+
+股票
+
+MIKE
+
+MIKE麦克指标
+
+股票
+
+LB
+
+量比
+
+股票
+
+VMA
+
+VMA量简单移动平均
+
+股票
+
+VMACD
+
+VMACD量指数平滑异同平均
+
+股票
+
+VOSC
+
+VOSC成交量震荡
+
+股票
+
+TAPI
+
+TAPI加权指数成交值
+
+股票
+
+VSTD
+
+VSTD成交量标准差
+
+股票
+
+ADTM
+
+ADTM动态买卖气指标
+
+股票
+
+MI
+
+MI动量指标
+
+股票
+
+MICD
+
+MICD异同离差动力指数
+
+股票
+
+RC
+
+RC变化率指数
+
+股票
+
+RCCD
+
+RCCD异同离差变化率指数
+
+股票
+
+SRMI
+
+SRMI(MI修正指标)
+
+股票
+
+DPTB
+
+DPTB大盘同步指标
+
+股票
+
+JDQS
+
+JDQS阶段强势指标
+
+股票
+
+JDRS
+
+JDRS阶段弱势指标
+
+股票
+
+ZDZB
+
+ZDZB筑底指标
+
+股票
+
+ATR
+
+ATR真实波幅
+
+股票
+
+MASS
+
+MASS梅丝线
+
+股票
+
+STD
+
+STD标准差
+
+股票
+
+VHF
+
+VHF纵横指标
+
+股票
+
+CVLT
+
+CVLT佳庆离散指标
+
+股票
+
+large_amt_timeline
+
+主力净流入金额(分时)
+
+股票
+
+active_buy_large_volume
+
+主动买入特大单量
+
+股票,同花顺指数
+
+active_sell_large_volume
+
+主动卖出特大单量
+
+股票,同花顺指数
+
+active_buy_main_volume
+
+主动买入大单量
+
+股票,同花顺指数
+
+active_sell_main_volume
+
+主动卖出大单量
+
+股票,同花顺指数
+
+active_buy_middle_volume
+
+主动买入中单量
+
+股票,同花顺指数
+
+active_sell_middle_volume
+
+主动卖出中单量
+
+股票,同花顺指数
+
+possitive_buy_large_volume
+
+被动买入特大单量
+
+股票,同花顺指数
+
+possitive_sell_large_volume
+
+被动卖出特大单量
+
+股票,同花顺指数
+
+possitive_buy_main_volume
+
+被动买入大单量
+
+股票,同花顺指数
+
+possitive_sell_main_volume
+
+被动卖出大单量
+
+股票,同花顺指数
+
+possitive_buy_middle_volume
+
+被动买入中单量
+
+股票,同花顺指数
+
+possitive_sell_middle_volume
+
+被动卖出中单量
+
+股票,同花顺指数
+
+active_buy_large_amount
+
+主动买入特大单金额
+
+股票,同花顺指数
+
+active_sell_large_amount
+
+主动卖出特大单金额
+
+股票,同花顺指数
+
+active_buy_main_amount
+
+主动买入大单金额
+
+股票,同花顺指数
+
+active_sell_main_amount
+
+主动卖出大单金额
+
+股票,同花顺指数
+
+10 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+possitive_buy_large_amount
+
+被动买入特大单金额
+
+股票,同花顺指数
+
+possitive_sell_large_amount
+
+被动卖出特大单金额
+
+股票,同花顺指数
+
+possitive_buy_main_amount
+
+被动买入大单金额
+
+股票,同花顺指数
+
+possitive_sell_main_amount
+
+被动卖出大单金额
+
+股票,同花顺指数
+
+active_buy_middle_amount
+
+主动买入中单金额
+
+股票,同花顺指数
+
+active_sell_middle_amount
+
+主动卖出中单金额
+
+股票,同花顺指数
+
+possitive_buy_middle_amount
+
+被动买入中单金额
+
+股票,同花顺指数
+
+possitive_sell_middle_amount
+
+被动卖出中单金额
+
+股票,同花顺指数
+
+openInterest
+
+持仓量
+
+期权,期货
+
+changeRatio_periodical
+
+涨跌幅(阶段)
+
+期权专用
+
+技术指标规则说明
+选择技术指标时,需同时在functionpara的calculate字段以indicators为key,以半角逗号拼接各个参数字符串为value。为下
+列特殊的参数额外使用下列英文名,其他的沿用下拉框英文值。
+indicators参数说明
+指标名
+
+指标说明
+
+指标备注
+
+BBI
+
+BBI多空指数
+
+{周期1},{周期2},{周期3},{周期4}
+
+DDI
+
+DDI方向标准离差指数
+
+{周期1},{周期2},{平滑因子},{周期3},{DDI or ADDI or AD}
+
+DMA
+
+DMA平均线差
+
+{短周期},{长周期},{周期},{DDD or AMA}
+
+MA
+
+MA简单移动平均
+
+{周期}
+
+EXPMA
+
+EXPMA指数平均数
+
+{周期}
+
+MACD
+
+MACD指数平滑异同平均
+
+{短周期},{长周期},{周期},{DIFF or DEA or MACD}
+
+MTM
+
+MTM动力指标
+
+{间隔周期},{周期},{MTM or MTMMA}
+
+PRICEOSC
+
+PRICEOSC价格振荡指标
+
+{短周期},{长周期}
+
+TRIX
+
+TRIX三重指数平滑平均
+
+{周期1},{周期2},{TRIX or TRMA}
+
+BIAS
+
+BIAS乖离率
+
+{周期}
+
+CCI
+
+CCI顺势指标
+
+{周期}
+
+DBCD
+
+DBCD异同离差乖离率
+
+{周期1},{周期2},{周期3},{DBCD or MM}
+
+DPO
+
+DPO区间震荡线
+
+{周期1},{周期2},{DPO or MADPO}
+
+KDJ
+
+KDJ随机指标
+
+{周期},{周期1},{周期2},{K or D or J}
+
+LWR
+
+LWR威廉指标
+
+{周期},{周期1},{周期2},{LWR1 or LWR2}
+
+ROC
+
+ROC变动速率
+
+{间隔周期},{周期},{ROC or ROCMA}
+
+RSI
+
+RSI相对强弱指标
+
+{周期}
+
+SI
+
+SI摆动指标
+
+SRDM
+
+SRDM动向速度比率
+
+{周期},{SRDM or ASRDM}
+
+VROC
+
+VROC量变动速率
+
+{周期}
+
+VRSI
+
+VRSI量相对强弱
+
+{周期}
+
+WR
+
+WR威廉指标
+
+{周期}
+
+ARBR
+
+ARBR人气意愿指标
+
+{周期},{AR or BR}
+
+CR
+
+CR能量指标
+
+{周期}
+
+PSY
+
+PSY心理指标
+
+{周期1},{周期2},{PSY or MAPSY}
+
+VR
+
+VR成交量比率
+
+{周期}
+
+11 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+WAD
+
+WAD威廉聚散指标
+
+{周期},{WAD or MAWAD}
+
+MFI
+
+MFI资金流向指标
+
+{周期}
+
+OBV
+
+OBV能量潮
+
+{OBV or OBV_XZ}
+
+PVT
+
+PVT量价趋势指标
+
+WVAD
+
+WVAD威廉变异离散量
+
+{周期1},{周期2},{WVAD or MAWVAD}
+
+BBIBOLL
+
+BBIBOLL多空布林线
+
+{周期},{宽带},{BBIBOLL or UPR or DWN}
+
+BOLL
+
+BOLL布林线
+
+{周期},{宽带},{MID or UPPER or LOWER}
+
+CDP
+
+CDP逆势操作
+
+{CDP or AH or AL or NH or NL}
+
+ENV
+
+ENV指标
+
+{周期},{UPPER or LOWER}
+
+MIKE
+
+MIKE麦克指标
+
+{周期},{WR or MR or SR or WS or MS or SS}
+
+LB
+
+量比
+
+{周期}
+
+VMA
+
+VMA量简单移动平均
+
+{周期}
+
+VMACD
+
+VMACD量指数平滑异同平均
+
+{短周期},{长周期},{周期},{DIFF or DEA or MACD}
+
+VOSC
+
+VOSC成交量震荡
+
+{短周期},{长周期}
+
+TAPI
+
+TAPI加权指数成交值
+
+{周期},{TAPI or MATAPI}
+
+VSTD
+
+VSTD成交量标准差
+
+{周期}
+
+ADTM
+
+ADTM动态买卖气指标
+
+{周期},{周期1},{ADTM or MAADTM}
+
+MI
+
+MI动量指标
+
+{周期},{A or MI}
+
+MICD
+
+MICD异同离差动力指数
+
+{周期},{周期1},{周期2},{DIF or MICD}
+
+RC
+
+RC变化率指数
+
+{周期}
+
+RCCD
+
+RCCD异同离差变化率指数
+
+{周期},{周期1},{周期2},{DIF or RCCD}
+
+SRMI
+
+SRMI(MI修正指标)
+
+{周期}
+
+DPTB
+
+DPTB大盘同步指标
+
+{周期},{000001 or 000010 or 399001 or 000300}
+
+JDQS
+
+JDQS阶段强势指标
+
+{周期},{000001 or 000010 or 399001 or 000300}
+
+JDRS
+
+JDRS阶段弱势指标
+
+{周期},{000001 or 000010 or 399001 or 000300}
+
+ZDZB
+
+ZDZB筑底指标
+
+{周期},{周期1},{周期2},{B or D}
+
+ATR
+
+ATR真实波幅
+
+{周期},{TR or ATR}
+
+MASS
+
+MASS梅丝线
+
+{周期1},{周期2}
+
+STD
+
+STD标准差
+
+{周期}
+
+VHF
+
+VHF纵横指标
+
+{周期}
+
+CVLT
+
+CVLT佳庆离散指标
+
+{周期}
+
+functionpara控件说明
+名称
+
+keys
+
+value说明
+
+设置
+时间
+区间开始
+时间
+
+Limitstart
+
+限定每个交易日数据的开始时间
+
+设置
+时间
+区间结束
+时间
+
+Limitend
+
+限定每个交易日数据的截止时间
+
+时间
+周期
+
+Interval
+
+1-1分钟 3-3分钟 5-5分钟 10-10分钟 15-15分钟 30-30分钟 60-60分钟
+
+省略时逻辑
+
+12 / 43
+
+1-1分钟
+
+名称
+
+keys
+
+value说明
+
+省略时逻辑
+
+非交
+易间
+隔处
+理
+
+Fill
+
+Previous-沿用之前数据 Blank-空值 具体数值-自定义数值 Original-不处理
+
+Original-不
+处理
+
+分红
+再投
+复权
+方式
+
+CPS
+
+后复权(分红方案计算)-backward1 前复权(交易所价格计算)-forward3 后复权
+(交易所价格计算)-backward3 全流通前复权(分红方案计算)-forward2 全流通
+后复权(分红方案计算)-backward2 全流通前复权(交易所价格计算)-forward4
+全流通后复权(交易所价格计算)-backward4 不复权-no
+
+no-不复权
+
+时间
+戳格
+式
+
+Timeformat
+
+BeiJingTime-北京时间 LocalTime-当地时间
+
+BeiJingTime北京时间
+
+设定
+复权
+基点
+
+BaseDate
+
+复权基点日期,"YYYY-MM-DD"
+
+后复权按上
+市日,前复
+权按最新日
+
+示例
+para =
+{
+"codes": "300033.SZ,600030.SH",
+"indicators": "open,high,SI,MACD,DPTB,OBV,KDJ",
+"starttime": "2018-01-01 09:15:00",
+"endtime": "2018-01-01 09:50:00",
+"functionpara":
+
+{
+
+"Interval": "1",
+"Fill":
+
+"Original",
+
+"calculate": {
+"SI": ""
+"MACD":
+
+"12,26,9,MACD",
+
+"DPTB": "7,000001",
+"OBV": "OBV_XZ",
+"KDJ": "9,3,3,K",
+}
+}
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+5、实时行情
+URL
+https://quantapi.51ifind.com/api/v1/real_time_quotation
+
+13 / 43
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+indicators
+
+是
+
+半角逗号分隔的所有指标
+
+"indicators":"open,high"
+
+functionpara
+
+否
+
+key-value格式。仅包含债券报价方式(pricetype)控件失效
+时,不生成,否则生成。
+
+见下方代码块
+
+indicators参数说明
+指标名
+
+指标说明
+
+指标备注
+
+tradeDate
+
+交易日期
+
+通用
+
+tradeTime
+
+交易时间
+
+通用
+
+preClose
+
+前收盘价
+
+通用
+
+open
+
+开盘价
+
+通用
+
+high
+
+最高价
+
+通用
+
+low
+
+最低价
+
+通用
+
+latest
+
+最新价
+
+通用
+
+latestAmount
+
+现额
+
+通用
+
+latestVolume
+
+现量
+
+通用
+
+avgPrice
+
+均价
+
+通用
+
+change
+
+涨跌
+
+通用
+
+changeRatio
+
+涨跌幅
+
+通用
+
+upperLimit
+
+涨停价
+
+通用
+
+downLimit
+
+跌停价
+
+通用
+
+amount
+
+成交额
+
+通用
+
+volume
+
+成交量
+
+通用
+
+turnoverRatio
+
+换手率
+
+通用
+
+sellVolume
+
+内盘
+
+通用
+
+buyVolume
+
+外盘
+
+通用
+
+totalBidVol
+
+委买十档总量
+
+股票
+
+totalAskVol
+
+委卖十档总量
+
+股票
+
+totalShares
+
+总股本
+
+股票
+
+totalCapital
+
+总市值
+
+股票
+
+pb
+
+市净率
+
+股票
+
+riseDayCount
+
+连涨天数
+
+股票
+
+suspensionFlag
+
+停牌标志
+
+股票
+
+tradeStatus
+
+交易状态
+
+股票
+
+chg_1min
+
+1分钟涨跌幅
+
+股票
+
+chg_3min
+
+3分钟涨跌幅
+
+股票
+
+chg_5min
+
+5分钟涨跌幅
+
+股票
+
+chg_5d
+
+5日涨跌幅
+
+股票
+
+chg_10d
+
+10日涨跌幅
+
+股票
+
+chg_20d
+
+20日涨跌幅
+
+股票
+
+chg_60d
+
+60日涨跌幅
+
+股票
+
+14 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+chg_120d
+
+120日涨跌幅
+
+股票
+
+chg_250d
+
+250日涨跌幅
+
+股票
+
+chg_year
+
+年初至今涨跌幅
+
+股票
+
+mv
+
+流通市值
+
+股票
+
+vol_ratio
+
+量比
+
+股票
+
+committee
+
+委比
+
+股票
+
+commission_diff
+
+委差
+
+股票
+
+pe_ttm
+
+市盈率TTM
+
+股票
+
+pbr_lf
+
+市净率LF
+
+股票
+
+swing
+
+振幅
+
+股票
+
+lastest_price
+
+最新成交价
+
+股票
+
+af_backward
+
+后复权因子(分红方案计算)
+
+股票
+
+bid10
+
+买10价
+
+股票
+
+bid9
+
+买9价
+
+股票
+
+bid8
+
+买8价
+
+股票
+
+bid7
+
+买7价
+
+股票
+
+bid6
+
+买6价
+
+股票
+
+bid5
+
+买5价
+
+股票
+
+bid4
+
+买4价
+
+股票
+
+bid3
+
+买3价
+
+股票
+
+bid2
+
+买2价
+
+股票
+
+bid1
+
+买1价
+
+股票
+
+ask1
+
+卖1价
+
+股票
+
+ask2
+
+卖2价
+
+股票
+
+ask3
+
+卖3价
+
+股票
+
+ask4
+
+卖4价
+
+股票
+
+ask5
+
+卖5价
+
+股票
+
+ask6
+
+卖6价
+
+股票
+
+ask7
+
+卖7价
+
+股票
+
+ask8
+
+卖8价
+
+股票
+
+ask9
+
+卖9价
+
+股票
+
+ask10
+
+卖10价
+
+股票
+
+bidSize10
+
+买10量
+
+股票
+
+bidSize9
+
+买9量
+
+股票
+
+bidSize8
+
+买8量
+
+股票
+
+bidSize7
+
+买7量
+
+股票
+
+bidSize6
+
+买6量
+
+股票
+
+bidSize5
+
+买5量
+
+股票
+
+bidSize4
+
+买4量
+
+股票
+
+bidSize3
+
+买3量
+
+股票
+
+bidSize2
+
+买2量
+
+股票
+
+bidSize1
+
+买1量
+
+股票
+
+askSize1
+
+卖1量
+
+股票
+
+15 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+askSize2
+
+卖2量
+
+股票
+
+askSize3
+
+卖3量
+
+股票
+
+askSize4
+
+卖4量
+
+股票
+
+askSize5
+
+卖5量
+
+股票
+
+askSize6
+
+卖6量
+
+股票
+
+askSize7
+
+卖7量
+
+股票
+
+askSize8
+
+卖8量
+
+股票
+
+askSize9
+
+卖9量
+
+股票
+
+askSize10
+
+卖10量
+
+股票
+
+avgBuyPrice
+
+均买价
+
+股票
+
+avgSellPrice
+
+均卖价
+
+股票
+
+totalBuyVolume
+
+总买量
+
+股票
+
+totalSellVolume
+
+总卖量
+
+股票
+
+transClassification
+
+成交分类
+
+股票
+
+transTimes
+
+成交次数
+
+股票
+
+mainInflow
+
+主力流入金额
+
+股票
+
+mainOutflow
+
+主力流出金额
+
+股票
+
+mainNetInflow
+
+主力净流入金额
+
+股票
+
+retailInflow
+
+散户流入金额
+
+股票
+
+retailOutflow
+
+散户流出金额
+
+股票
+
+retailNetInflow
+
+散户净流入金额
+
+股票
+
+largeInflow
+
+超大单流入金额
+
+股票
+
+largeOutflow
+
+超大单流出金额
+
+股票
+
+largeNetInflow
+
+超大单净流入金额
+
+股票
+
+bigInflow
+
+大单流入金额
+
+股票
+
+bigOutflow
+
+大单流出金额
+
+股票
+
+bigNetInflow
+
+大单净流入金额
+
+股票
+
+middleInflow
+
+中单流入金额
+
+股票
+
+middleOutflow
+
+中单流出金额
+
+股票
+
+middleNetInflow
+
+中单净流入金额
+
+股票
+
+smallInflow
+
+小单流入金额
+
+股票
+
+smallOutflow
+
+小单流出金额
+
+股票
+
+smallNetInflow
+
+小单净流入金额
+
+股票
+
+activeBuyLargeAmt
+
+主动买入特大单金额
+
+股票
+
+activeSellLargeAmt
+
+主动卖出特大单金额
+
+股票
+
+activeBuyMainAmt
+
+主动买入大单金额
+
+股票
+
+activeSellMainAmt
+
+主动卖出大单金额
+
+股票
+
+activeBuyMiddleAmt
+
+主动买入中单金额
+
+股票
+
+activeSellMiddleAmt
+
+主动卖出中单金额
+
+股票
+
+activeBuySmallAmt
+
+主动买入小单金额
+
+股票
+
+activeSellSmallAmt
+
+主动卖出小单金额
+
+股票
+
+possitiveBuyLargeAmt
+
+被动买入特大单金额
+
+股票
+
+possitiveSellLargeAmt
+
+被动卖出特大单金额
+
+股票
+
+16 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+possitiveBuyMainAmt
+
+被动买入大单金额
+
+股票
+
+possitiveSellMainAmt
+
+被动卖出大单金额
+
+股票
+
+possitiveBuyMiddleAmt
+
+被动买入中单金额
+
+股票
+
+possitiveSellMiddleAmt
+
+被动卖出中单金额
+
+股票
+
+possitiveBuySmallAmt
+
+被动买入小单金额
+
+股票
+
+possitiveSellSmallAmt
+
+被动卖出小单金额
+
+股票
+
+activeBuyLargeVol
+
+主动买入特大单量
+
+股票
+
+activeSellLargeVol
+
+主动卖出特大单量
+
+股票
+
+activeBuyMainVol
+
+主动买入大单量
+
+股票
+
+activeSellMainVol
+
+主动卖出大单量
+
+股票
+
+activeBuyMiddleVol
+
+主动买入中单量
+
+股票
+
+activeSellMiddleVol
+
+主动卖出中单量
+
+股票
+
+activeBuySmallVol
+
+主动买入小单量
+
+股票
+
+activeSellSmallVol
+
+主动卖出小单量
+
+股票
+
+possitiveBuyLargeVol
+
+被动买入特大单量
+
+股票
+
+possitiveSellLargeVol
+
+被动卖出特大单量
+
+股票
+
+possitiveBuyMainVol
+
+被动买入大单量
+
+股票
+
+possitiveSellMainVol
+
+被动卖出大单量
+
+股票
+
+possitiveBuyMiddleVol
+
+被动买入中单量
+
+股票
+
+possitiveSellMiddleVol
+
+被动卖出中单量
+
+股票
+
+possitiveBuySmallVol
+
+被动买入小单量
+
+股票
+
+possitiveSellSmallVol
+
+被动卖出小单量
+
+股票
+
+activebuy_volume
+
+主买总量
+
+股票
+
+activesell_volume
+
+主卖总量
+
+股票
+
+activebuy_amt
+
+主买总额
+
+股票
+
+activesell_amt
+
+主卖总额
+
+股票
+
+post_lastest
+
+盘后最新成交价
+
+股票
+
+post_latestVolume
+
+盘后现量
+
+股票
+
+post_volume
+
+盘后成交量
+
+股票
+
+post_amt
+
+盘后成交额
+
+股票
+
+post_dealnum
+
+盘后成交笔数
+
+股票
+
+priceDiff
+
+买卖价差
+
+港股专用
+
+sharesPerHand
+
+每手股数
+
+港股专用
+
+expiryDate
+
+到期日
+
+港股专用
+
+tradeStatus
+
+交易状态
+
+港股专用
+
+iopv
+
+IOPV (净值估值)
+
+基金专用
+
+premium
+
+折价
+
+基金专用
+
+riseCount
+
+上涨家数
+
+指数专用
+
+fallCount
+
+下跌家数
+
+指数专用
+
+upLimitCount
+
+涨停家数
+
+指数专用
+
+downLimitCount
+
+跌停家数
+
+指数专用
+
+suspensionCount
+
+停牌家数
+
+指数专用
+
+pure_bond_value_cb
+
+纯债价值
+
+指数专用
+
+17 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+surplus_term
+
+剩余期限(天)
+
+指数专用
+
+dealDirection
+
+成交方向
+
+期货期权专用
+
+dealtype
+
+成交性质
+
+期货期权专用
+
+impliedVolatility
+
+隐含波动率
+
+期权专用
+
+historyVolatility
+
+历史波动率
+
+期权专用
+
+delta
+
+Delta
+
+期权专用
+
+gamma
+
+Gamma
+
+期权专用
+
+vega
+
+Vega
+
+期权专用
+
+theta
+
+Theta
+
+期权专用
+
+rho
+
+Rho
+
+期权专用
+
+pre_open_interest
+
+前持仓量
+
+期权专用
+
+pre_implied_volatility
+
+前隐含波动率
+
+期权专用
+
+volume_pcr_total
+
+成交量pcr (品种)
+
+期权专用
+
+volume_pcr_month
+
+成交量pcr (同月)
+
+期权专用
+
+示例
+para = {
+"codes": "300033.SZ,600000.SH",
+"indicators":
+
+"open,high",
+
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+6、日内快照
+URL
+https://quantapi.51ifind.com/api/v1/snap_shot
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"300033.SZ,600030.SH"
+
+indicators
+
+是
+
+半角逗号分隔的所有指标
+
+"indicators":"open,high"
+
+starttime
+
+是
+
+开始日期,支持"YYYYMMDD HH:mm:ss""YYYY-MM-DD
+HH:mm:ss""YYYY/MM/DD HH:mm:ss"三种时间格式
+
+"starttime":"2018-01-01
+09:15:00"
+
+18 / 43
+
+key
+
+是否
+必须
+
+value
+
+示例
+
+endtime
+
+是
+
+结束日期,支持"YYYYMMDD HH:mm:ss""YYYY-MM-DD
+HH:mm:ss""YYYY/MM/DD HH:mm:ss"三种日期格式
+
+"endtime":"2018-01-01
+15:15:00"
+
+indicators参数说明
+指标名
+
+指标说明
+
+指标备注
+
+tradeDate
+
+交易日期
+
+股票
+
+tradeTime
+
+交易时间
+
+股票
+
+preClose
+
+前收盘价
+
+股票
+
+open
+
+开盘价
+
+股票
+
+high
+
+最高价
+
+股票
+
+low
+
+最低价
+
+股票
+
+latest
+
+现价
+
+股票
+
+amt
+
+成交额
+
+股票
+
+vol
+
+成交量
+
+股票
+
+amount
+
+累计成交额
+
+股票
+
+volume
+
+累计成交量
+
+股票
+
+tradeNum
+
+成交次数
+
+股票
+
+bid10
+
+买10价
+
+股票
+
+bid9
+
+买9价
+
+股票
+
+bid8
+
+买8价
+
+股票
+
+bid7
+
+买7价
+
+股票
+
+bid6
+
+买6价
+
+股票
+
+bid5
+
+买5价
+
+股票
+
+bid4
+
+买4价
+
+股票
+
+bid3
+
+买3价
+
+股票
+
+bid2
+
+买2价
+
+股票
+
+bid1
+
+买1价
+
+股票
+
+ask1
+
+卖1价
+
+股票
+
+ask2
+
+卖2价
+
+股票
+
+ask3
+
+卖3价
+
+股票
+
+ask4
+
+卖4价
+
+股票
+
+ask5
+
+卖5价
+
+股票
+
+ask6
+
+卖6价
+
+股票
+
+ask7
+
+卖7价
+
+股票
+
+ask8
+
+卖8价
+
+股票
+
+ask9
+
+卖9价
+
+股票
+
+ask10
+
+卖10价
+
+股票
+
+bidSize10
+
+买10量
+
+股票
+
+bidSize9
+
+买9量
+
+股票
+
+bidSize8
+
+买8量
+
+股票
+
+bidSize7
+
+买7量
+
+股票
+
+bidSize6
+
+买6量
+
+股票
+
+19 / 43
+
+指标名
+
+指标说明
+
+指标备注
+
+bidSize5
+
+买5量
+
+股票
+
+bidSize4
+
+买4量
+
+股票
+
+bidSize3
+
+买3量
+
+股票
+
+bidSize2
+
+买2量
+
+股票
+
+bidSize1
+
+买1量
+
+股票
+
+askSize1
+
+卖1量
+
+股票
+
+askSize2
+
+卖2量
+
+股票
+
+askSize3
+
+卖3量
+
+股票
+
+askSize4
+
+卖4量
+
+股票
+
+askSize5
+
+卖5量
+
+股票
+
+askSize6
+
+卖6量
+
+股票
+
+askSize7
+
+卖7量
+
+股票
+
+askSize8
+
+卖8量
+
+股票
+
+askSize9
+
+卖9量
+
+股票
+
+askSize10
+
+卖10量
+
+股票
+
+avgBuyPrice
+
+均买价
+
+股票
+
+avgSellPrice
+
+均卖价
+
+股票
+
+totalBuyVolume
+
+总买量
+
+股票
+
+totalSellVolume
+
+总卖量
+
+股票
+
+dealDirection
+
+成交方向(仅当日有效)
+
+股票、期货、期权
+
+dealtype
+
+成交性质(仅当日有效)
+
+期货、期权
+
+示例
+para = {
+"codes": "300033.SZ,600030.SH",
+"indicators": "open,high",
+"starttime": "2025-08-25 09:30:00",
+"endtime": "2025-08-25 15:00:00"
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、table(具体的数据内容)等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+7、经济数据库(EDB)
+URL
+
+20 / 43
+
+https://quantapi.51ifind.com/api/v1/edb_service
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+indicators
+
+是
+
+半角逗号分隔的所有指标,宏观指标过多,推荐使用
+Windows超级命令生成。
+
+"indicators":"M001620326,M002822183"
+
+functionpara
+
+否
+
+key-value格式,省略时不进行更新时间筛选。两个时间
+控件更新起始时间(startrtime)和更新结束时间
+(endrtime),不勾选时省略
+
+见下方代码块
+
+startdate
+
+是
+
+开始日期,支持"YYYYMMDD""YYYY-MMDD""YYYY/MM/DD"三种时间格式
+
+"startdate":"2018-01-01"
+
+enddate
+
+是
+
+结束日期,支持"YYYYMMDD""YYYY-MMDD""YYYY/MM/DD"三种日期格式
+
+"enddate":"2018-01-01"
+
+示例
+para = {
+"indicators": "M001620326,M002822183",
+"startdate":
+"enddate":
+
+"2018-01-01",
+"2018-01-01",
+
+"functionpara":
+
+{
+
+"startrtime": "2018-01-01 09:15:00",
+"end rtime": "2018-01-01 10:15:00",
+}
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括 ID、time 等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+8、专题报表函数
+URL
+https://quantapi.51ifind.com/api/v1/data_pool
+
+formData
+报表过多,推荐使用超级命令查看生成命令。
+key
+
+是否必
+须
+
+reportname
+
+是
+
+value
+
+示例
+"reportname":"p03341"
+
+21 / 43
+
+key
+
+是否必
+须
+
+value
+
+示例
+
+functionpara
+
+是
+
+key-value的参数,key按照过去的指
+标名称
+
+见下方代码块
+
+outputpara
+
+是
+
+半角逗号分隔的Y/N来控制是否显示
+该字段
+
+"outputpara":"date:Y,thscode:Y,security_name:Y,weight:Y"
+
+示例
+para = {
+"reportname": "p03341",
+"functionpara":
+
+{
+
+"sdate": "20210421",
+"edate": "20211119",
+"xmzt": "全部",
+"jcsslx": "全部",
+"jys": "全部"
+},
+"outputpara": "p03341_f001:Y,p03341_f002:Y"
+}
+
+提取‘REITs项目一览’ 报表函数数据,对应报表编码‘p03341’
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括p03341_f001、p03341_f002(具体的数据内容)等
+
+datatype
+
+指标格式
+
+报表函数暂为空,忽略
+
+inputParams
+
+输入参数
+
+报表函数暂为空,忽略
+
+outParams
+
+输出指标
+
+返回报表指标与中文名称,如:'p03291_f002': '同花顺代码'
+
+descrs
+
+输出信息
+
+如:'name': 'p03291_f001', 'type': 'DT_DATE', 'attrs': []
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+9、组合管理
+(1)组合新建
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+功能名称
+
+func
+
+是
+
+"func":"newportf"
+
+组合名称
+
+name
+
+是
+
+"name":"股债策略组合"
+
+所属分组
+
+group
+
+是
+
+"group": 11580
+
+22 / 43
+
+示例
+
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+业绩基准,基准代码和
+名称
+
+performbm
+
+否,默认
+填充沪深
+300
+
+键值对
+
+"performbm": {"code": "000300.SH",
+"name": "沪深300"}
+
+跌价基准,基准代码、
+基准名称、基准类型
+
+supbm
+
+否,省略
+时为空
+
+键值对
+
+"supbm": {"code":"000001.SH", "name":
+"上证指数", "benchmarkType": "1"}
+
+交易日
+
+tday
+
+否,默认
+国内交易
+所
+
+枚举值国内交易所、港
+股、美股、国内银行间
+
+"tday":"国内交易所"
+
+基准货币
+
+currency
+
+否,默认
+人民币
+
+枚举值CND、HKD、
+USD
+
+"currency":"CNY"
+
+融资利率%
+
+finacrate
+
+否,默认
+为空
+
+"finacrate":"7.5"
+
+融券利率%
+
+secrate
+
+否,默认
+为空
+
+"secrate":"5.5"
+
+组合说明
+
+info
+
+否,默认
+为空
+
+"info":"股票与债券结合的策略组合"
+
+示例
+para={
+"func": "newportf",
+"name": "股债联动",
+"group": 11580,
+"performbm": {
+"code": "000300.SH",
+"name": "沪深300"
+},
+"supbm":
+
+{
+
+"code": "",
+"name": "",
+},
+"tday": "国内交易所",
+"curency": "CNY",
+"finacrate": "",
+"secrate": "",
+"info": "股票与债券结合的策略组合"
+}
+
+(2)组合导入
+1). 模板导入
+通过读取组合文件的内容,进行上传完成组合导入。
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+importf
+
+"func": "importf"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+23 / 43
+
+名称
+
+key
+
+是否必须
+
+组合ID
+
+portfid
+
+是
+
+组合内容
+
+content
+
+是
+
+value
+
+示例
+"portfid": 161390
+
+二维表
+
+示例
+para
+
+= {
+
+"func": "importf",
+"name": "股债策略组合",
+"portfid": 161390,
+"content": [
+[
+"交易日期",
+"证券代码",
+"业务类型",
+"数量",
+"价格",
+"成交金额",
+"费用",
+"证券类型"
+],
+[
+"2020-03-30",
+"CNY",
+"现金存入",
+"",
+"",
+10000000,
+"",
+""
+],
+[
+"2020-04-01",
+"600000.SH",
+"买入",
+100,
+10.09,
+1009,
+5.225,
+"A股"
+],
+]
+}
+
+2).文件导入
+通过文件对象的形式提交,来实现组合导入。
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+fileimport
+
+"func": "fileimport"
+
+24 / 43
+
+名称
+
+key
+
+是否必须
+
+value
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+组合文件
+
+file
+
+是
+
+示例
+
+文件对象
+
+file:{本地文件}
+
+示例
+para = {
+"func": "fileimport",
+"name": "股债策略组合",
+"portid": 161930,
+"file": "股债策略组合内容.xlsx"
+}
+#file_object为待导入组合的文件对象
+files = {
+"file": ("股债策略组合内容.xlsx", open("C:\demo\股债策略组合内容.xlsx",'rb))
+}
+
+3).状态查询
+适用于大文件导入、导入历史持仓计算量较大的组合导入时,查询导入状态。
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+fileimport
+
+"func": "query_commit"
+
+组合ID
+
+portfid
+
+是
+
+组合文件
+
+jobid
+
+是
+
+"portfid": 161390
+"jobid":21
+
+文件导入后返回
+
+示例
+para
+
+=
+
+{
+
+"func": "query_commit",
+"portid": 161930,
+"jobid": 21
+}
+
+(3)现金存取
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+cashacs
+
+"func": "cashacs"
+
+25 / 43
+
+名称
+
+key
+
+是否必须
+
+value
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+功能参数
+
+functionpara
+
+是
+
+"functionpara": {"acesscls": "101", "amount": "10000"}
+
+示例
+
+functionpara说明
+名称
+
+key
+
+value
+
+省略时
+
+存取类型
+
+acesscls
+
+存入-不计入收益:101;取出-不计入收益:102
+
+不能省略
+
+现金数额
+
+amount
+
+不能省略
+
+示例
+para
+
+=
+
+{
+
+"func": "cashacs",
+"name": "bldptf5",
+"portfid": 161390,
+"functionpara": {
+"acesscls":"101",
+"amount":"10000"
+}
+}
+
+(4)普通交易
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+deal
+
+"func": "deal"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+功能参数
+
+functionpara
+
+是
+
+functionpara说明
+名称
+
+key
+
+value
+
+行情代码
+
+thscode
+
+交易方向
+
+direct
+
+标的名称
+
+codeName
+
+不能省略
+
+交易市场
+
+marketCode
+
+不能省略
+
+标的类型
+
+securityType
+
+不能省略
+
+成交价格
+
+price
+
+不能省略
+
+成交数量
+
+volume
+
+不能省略
+
+结算货币
+
+currency
+
+不能省略
+
+费用
+
+fee
+
+不能省略
+
+省略时
+不能省略
+
+买入: buy;卖出: sell
+
+不能省略
+
+26 / 43
+
+名称
+
+key
+
+value
+
+费率
+
+feep
+
+不能省略
+
+汇率
+
+rate
+
+不能省略
+
+分红方式
+
+bonus
+
+省略时
+
+适用基金,现金分红:1;红利再投资:2
+
+示例
+para
+
+=
+
+{
+
+"func": "deal",
+"name": "股债策略组合",
+"portfid": 161390,
+"functionpara": {
+"thscode": "300033",
+"direct": "buy",
+"codeName": "同花顺",
+"marketCode": "212100",
+"securityType": "001001",
+"price": 78.7,
+"volume": 100,
+"currency": "CNY",
+"fee": "0",
+"feep": 0,
+"rate": "1.00",
+"bonus": ""
+}
+}
+
+(5)交易流水
+目前支持最大时间区间为7天
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+query_exchange_records
+
+"func": "query_exchange_records"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+指标
+
+indicators
+
+是
+
+"indicators": "date,code,name,dealPrice"
+
+开始时间
+
+startdate
+
+是
+
+"startdate": "2022-10-18"
+
+结束时间
+
+enddate
+
+是
+
+"enddate": "2022-10-20"
+
+功能参数
+
+functionpara
+
+否
+
+"functionpara": {"keyword": ""}
+
+indicators说明
+指标名称
+
+英文名称
+
+交易日期
+
+date
+
+证券代码
+
+code
+
+备注
+
+27 / 43
+
+指标名称
+
+英文名称
+
+备注
+
+证券简称
+
+name
+
+成交价格
+
+dealPrice
+
+成交数量
+
+dealNumber
+
+发生金额
+
+realPrice
+
+业务名称
+
+businessName
+
+手续费
+
+serviceCharge
+
+证券类型
+
+type
+
+币种
+
+currency
+
+汇率
+
+exchangeRate
+
+市场
+
+marketName
+
+备注信息
+
+importType
+
+functionpara说明
+名称
+
+key
+
+value
+
+关键字
+
+keyword
+
+省略时
+默认为空
+
+示例
+para = {
+"func": "query_exchange_records",
+"name": "股债策略组合",
+"portfid": 161390,
+"indicators": "date,code,name,deal Price,dealNumber,real Price,businessName,serviceCharge,type,currenc
+y,exchangeRate,marketName,importType",
+"startdate": "2022-10-18",
+"enddate": "2022-10-20",
+"functionpara": {
+"keyword": "
+}
+}
+
+(6)组合监控
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+query_overview
+
+"func": "query_overview"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+指标
+
+indicators
+
+是
+
+indicators说明
+指标名称
+
+英文名称
+
+备注
+
+28 / 43
+
+指标名称
+
+英文名称
+
+备注
+
+资产分类
+
+category
+
+证券代码
+
+thscode
+
+证券简称
+
+stockName
+
+最新价格
+
+newPrice
+
+涨跌
+
+increase
+
+涨跌幅
+
+increseRate
+
+持仓数量
+
+number
+
+持仓市值
+
+marketValue
+
+最新权重
+
+weight
+
+当日盈亏
+
+todayProfit
+
+浮动盈亏
+
+floatProfit
+
+浮动盈亏率
+
+floatProfitRate
+
+累计盈亏
+
+totalProfit
+
+累计盈亏率
+
+totalProfitRate
+
+分红派息
+
+interestIncome
+
+已实现盈利
+
+realizedProfit
+
+成本价格
+
+positionPrice
+
+持仓成本
+
+positionCost
+
+保本价格
+
+breakevenPrice
+
+手续费
+
+serviceCharge
+
+币种
+
+moneyType
+
+汇率
+
+currentPrice
+
+更新时间
+
+updateTime
+
+示例
+para
+
+=
+
+{
+
+"func": "query_overview",
+"name": "股债策略组合",
+"portfid": 161390,
+"indicators": "category,th scode,stockName,newPrice,increase,increaseRate,number,marketValue,weight,t
+odayProfit,floatProfit,floatProfitRate,total Profit,total ProfitRate,interestIncome,real i zed
+Profit,positionPrice,positionCost,breakevenPrice,serviceCharge,moneyType,currentPr ices,updateTime"
+}
+
+(7)持仓分析
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+query_positions
+
+"func": "query_positions"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+29 / 43
+
+名称
+
+key
+
+是否必须
+
+组合ID
+
+portfid
+
+是
+
+指标
+
+indicators
+
+是
+
+功能参数
+
+functionpara
+
+是
+
+value
+
+示例
+"portfid": 161390
+
+"functionpara": {"penetrate": "false"}
+
+indicators说明
+指标名称
+
+英文名称
+
+备注
+
+证券类型
+
+categoryName
+
+证券名称
+
+securityName
+
+证券代码
+
+thsCode
+
+权重
+
+weight
+
+持仓市值
+
+marketPrice
+
+持仓成本
+
+cost
+
+浮动盈亏
+
+wavepl
+
+累计收益
+
+cumpl
+
+收盘价
+
+price
+
+涨跌幅
+
+increaseRate
+
+持仓数量
+
+amount
+
+持仓成本价
+
+costPrice
+
+functionpara说明
+名称
+
+key
+
+value
+
+省略时
+
+是否穿透
+
+penetrate
+
+不穿透: false;穿透: true
+
+不能省略
+
+示例
+para = {
+"func": "query_positions",
+"name": "股债策略组合",
+"portfid": 161390,
+"indicators": "categoryName,securityName,th sCode,weight,marketPrice,cost,wavepl,cumpl,price,increase
+Rate,
+amount,
+costPrice ",
+"date": "2022-10-19",
+"functionpara": {
+"penetrate": "false"
+}
+}
+
+(8)绩效指标
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+
+30 / 43
+
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+query_perform
+
+"func": "cashacs"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+日期
+
+date
+
+是
+
+适用于当日实时,"date": "2020-06-02";
+
+开始日期
+
+startdate
+
+是
+
+开始日期适用于区间"startdate": "2020-06-02"
+
+结束日期
+
+enddate
+
+是
+
+开始日期适用于区间"enddate": "2020-06-02"
+
+业绩基准
+
+performbm
+
+是
+
+"performbm": "000300"
+
+功能参数
+
+functionpara
+
+是
+
+"functionpara": {"pfclass": "utnv", "cycle": "day"}
+
+functionpara说明
+名称
+
+key
+
+value
+
+省略时
+
+业绩类型
+
+pfclass
+
+业绩表现: perform 净资产: nasset 组合净值: utnv
+
+不能省略
+
+周期
+
+cycle
+
+当日实时:rquota 日:day 周:week 月:month 半年:halfYear 年:year
+
+不能省略
+
+示例
+para = {
+"func": "query_perform",
+"name": "股债策略组合",
+"portfid": 161390,
+"performbm": "000300",
+"startdate": "2020-06-02",
+"enddate": "2022-10-20",
+"functionpara": {
+"pf class": "utnv",
+"cycle": "day"
+}
+}
+
+(9)风险指标
+URL
+https://quantapi.51ifind.com/api/v1/portfolio_manage
+
+formData
+名称
+
+key
+
+是否必须
+
+value
+
+示例
+
+功能名称
+
+func
+
+是
+
+query_risk_profits
+
+"func": "query_risk_profits"
+
+组合名称
+
+name
+
+否
+
+"name": "股债策略组合"
+
+组合ID
+
+portfid
+
+是
+
+"portfid": 161390
+
+指标
+
+indicators
+
+是
+
+"indicators": ["alpha,yield,annual_yield,sharpe_ratio"]
+
+开始日期
+
+startdate
+
+是
+
+"startdate": "2021-10-19"
+
+结束日期
+
+enddate
+
+是
+
+"enddate": "2022-10-19"
+
+功能参数
+
+functionpara
+
+是
+
+"functionpara": {"cycle": "day", "benchmark": "000300"}
+
+indicators说明
+
+31 / 43
+
+指标名称
+
+英文名称
+
+ALPHA
+
+ALPHA
+
+累计收益
+
+yield
+
+年化收益
+
+annual_yield
+
+夏普比率
+
+sharpe_ratio
+
+信息比率
+
+information_ratio
+
+索提诺比率
+
+sortino_ratio
+
+詹森阿尔法
+
+jensen_alpha
+
+特雷诺比率
+
+treynor_ratio
+
+胜率
+
+win_ratio
+
+正收益期数
+
+positiveMonth
+
+BETA
+
+beta
+
+年化波动率
+
+annual_volatility
+
+跟踪误差
+
+tracking_error
+
+下行风险
+
+downside_risk
+
+在险价值
+
+value_at_risk
+
+最大回撤
+
+max_drawdown
+
+最大回撤形成期
+
+maxdrawdownRepairNum
+
+最大回撤修复期
+
+maxdownNum
+
+连续下跌最大幅度
+
+max_cont_decline
+
+R-square
+
+rSquare
+
+备注
+
+functionpara说明
+名称
+
+key
+
+value
+
+省略时
+
+数据频率
+
+cycle
+
+日:day;周:week;月:month;季:season;年:year
+
+不能省略
+
+计算基准
+
+benchmark
+
+不能省略
+
+示例
+para
+
+=
+
+{
+
+"func": "query_risk_profits",
+"name": "股债策略组合",
+"portfid": 161390,
+"indicators": ["alpha,yield,annual_yield,sharpe_ratio"],
+"startdate": "2021-10-19",
+"enddate": "2022-10-19",
+"functionpara": {
+"cycle": "day",
+"benchmark": "000300"
+}
+}
+
+10、智能选股
+URL
+https://quantapi.51ifind.com/api/v1/smart_stock_picking
+
+32 / 43
+
+formData
+key
+
+是否必须
+
+value
+
+示例
+
+searchstring
+
+是
+
+搜索关键词
+
+"searchstring":"个股热度"
+
+searchtype
+
+是
+
+搜索类别
+
+"searchtype":"stock"
+
+示例
+para = {
+"search string": "个股热度",
+"searchtype": "stock"
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括 ID、time 等
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+inputParams
+
+输入参数
+
+返回输入的参数
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+11、基金实时估值(分钟)
+URL
+https://quantapi.51ifind.com/api/v1/fund_valuation
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"000001.OF,000003.OF"
+
+functionpara
+
+是
+
+key-value的参数
+
+见下方表格
+
+outputpara
+
+是
+
+半角逗号分隔的Y/N来控制
+是否显示该字段
+
+"changeRatioValuation:Y,realTimeValuation:Y,Deviation30TDays:Y"
+
+functionpara参数说明
+名称
+
+keys
+
+value说明
+
+省略时逻辑
+
+仅返回最新估值
+
+onlyLastest
+
+1-仅返回最新估值 0-返回时间区间估值
+
+不能省略
+
+开始时间
+
+beginTime
+
+仅返回最新估值可省略
+
+结束时间
+
+endTime
+
+仅返回最新估值可省略
+
+outputpara说明
+字段名称
+
+字段中文
+
+changeRatioValuation
+
+估值涨跌幅
+
+realTimeValuation
+
+基金实时估值
+
+33 / 43
+
+字段名称
+
+字段中文
+
+Deviation30TDays
+
+30交易日估算平均偏差(%)
+
+rank
+
+请求基金最新估值涨跌幅排名
+
+示例
+para = {
+"codes": "000001.OF,000003.OF",
+"functionpara": {
+"onlyLastest": "0",
+"beginTime": "2021-08-24 09:15:00",
+"endTime": "2021-08-24 15:15:00"
+},
+"outputpara": "date:Y,th scode:Y,security_name:Y,weight:Y"
+}
+
+输出:
+属性
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式
+
+tables
+
+结构体
+
+包括基金实时估值、估值涨跌幅、30日平均偏差等
+
+inputParams
+
+输入参数
+
+基金实时估值函数暂为空,忽略
+
+12、基金实时估值(日)
+URL
+https://quantapi.51ifind.com/api/v1/final_fund_valuation
+
+formData
+key
+
+是否必
+须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码
+
+"codes":"000001.OF,000003.OF"
+
+functionpara
+
+是
+
+key-value的参数,包括开始日期 beginDate,截
+止日期endDate
+
+见下方示例
+
+outputpara
+
+是
+
+半角逗号分隔的Y/N来控制是否显示该字段
+
+"finalValuation:Y,netAssetValue:Y,deviation:Y"
+
+outputpara说明
+字段名称
+
+字段中文
+
+finalValuation
+
+日最终估值
+
+netAssetValue
+
+日实际净值
+
+deviation
+
+估值相对净值偏差率(%)
+
+示例
+
+34 / 43
+
+para = {
+"codes": "000001.OF;000003.OF",
+"functionpara": {
+"beginDate": "2021-06-01",
+"endDate": "2021-09-02"
+},
+"finalValuation:Y,netAssetValue:Y,deviation:Y"
+}
+
+13、日期查询函数
+URL
+https://quantapi.51ifind.com/api/v1/get_trade_dates
+
+formData
+key
+
+是否必
+须
+
+value
+
+示例
+
+marketcode
+
+是
+
+见下方说明
+
+"marketcode":"212001"
+
+functionpara
+
+是
+
+key-value的参数
+
+见下方代码块
+
+startdate
+
+是
+
+开始日期,支持"YYYYMMDD""YYYY-MM-DD""YYYY/MM/DD"三种时
+间格式
+
+"startdate":"2018-0101"
+
+enddate
+
+是
+
+结束日期,支持"YYYYMMDD""YYYY-MM-DD""YYYY/MM/DD"三种日
+期格式
+
+"enddate":"2018-0101"
+
+marketcode说明
+交易所代码
+
+交易所名称
+
+212001
+
+上交所
+
+212100
+
+深交所
+
+212200
+
+港交所
+
+212020001
+
+中国金融期货交易所
+
+212020002
+
+上海黄金交易所
+
+212020003
+
+郑州商品交易所
+
+212020004
+
+大连商品交易所
+
+212004
+
+银行间债券市场
+
+212005
+
+代办转让市场
+
+212020006
+
+伦敦金属交易所(LME)
+
+212020007
+
+纽约商业期货交易所(NYMEX)
+
+212020008
+
+上海期货交易所
+
+212020010
+
+纽约商品交易所(COMEX)
+
+212020011
+
+纽约期货交易所(NYBOT)
+
+212020012
+
+芝加哥商品交易所(CBOT)
+
+212020013
+
+洲际交易所(ICE)
+
+212020014
+
+马来西亚衍生品交易所
+
+212020015
+
+芝加哥商业交易所(CME)
+
+212010
+
+美国纽约证券交易所
+
+35 / 43
+
+交易所代码
+
+交易所名称
+
+212011
+
+美国NASDAQ证券交易所
+
+212049
+
+美国证券交易所
+
+212050
+
+NYSE Arca
+
+212012
+
+英国伦敦证券交易所
+
+212013
+
+新加坡证券交易所
+
+212014
+
+荷兰阿姆斯特丹证券交易所
+
+212015
+
+挪威奥斯陆证券交易所
+
+212016
+
+澳大利亚证券交易所
+
+212017
+
+法国巴黎证券交易所
+
+212018
+
+比利时布鲁塞尔证券交易所
+
+212020016
+
+天津贵金属交易所
+
+212024
+
+德国法兰克福证券交易所
+
+212025
+
+日本东京证券交易所
+
+212026
+
+加拿大多伦多证券交易所
+
+212027
+
+韩国证券交易所
+
+212029
+
+马来西亚吉隆坡证券交易所
+
+212031
+
+马德里证券交易所
+
+212033
+
+墨西哥证券交易所
+
+212035
+
+瑞士证券交易所
+
+212036
+
+巴西圣保罗证券期货交易所
+
+212037
+
+瑞典斯德哥尔摩证券交易所
+
+212039
+
+台湾证券交易所
+
+212040
+
+泰国证券交易所
+
+212041
+
+奥地利维也纳证券交易所
+
+212045
+
+意大利米兰证券交易所
+
+212047
+
+印度尼西亚证券交易所
+
+212051
+
+美国IEX证券交易所
+
+212053
+
+新西兰证券交易所
+
+212055
+
+美国OTC市场
+
+212061
+
+菲律宾证券交易所
+
+212062
+
+孟买证券交易所
+
+212063
+
+布宜诺斯艾利斯证券交易所
+
+212203
+
+特拉维夫证券交易所
+
+212205
+
+莫斯科证券交易所
+
+212210
+
+BATS交易所
+
+functionpara说明
+对应字段
+
+字段类
+型
+
+是否可省
+略
+
+命令生成示例说明
+
+函数模式
+
+字符串
+
+不可
+
+查询区间日期 "mode":"1" 查询区间日期数目 "mode":"2"
+
+日期类型
+
+字符串
+
+不可
+
+交易日 "dateType":"0" 日历日 "dateType":"1"
+
+日期格式
+
+字符串
+
+不可
+
+YYYY-MM-DD "dateFormat":"0" YYYY/MM/DD "dateFormat":"1" YYYYMMDD
+"dateFormat":"2"
+
+36 / 43
+
+对应字段
+
+字段类
+型
+
+是否可省
+略
+
+命令生成示例说明
+
+时间周期
+
+字符串
+
+不可
+
+日 "period":"D" 周 "period":"W" 月 "period":"M" 季 "period":"Q" 半年 "period":"S"
+年 "period":"Y"
+
+时间周期偏
+移
+
+字符串
+
+不可
+
+时间周期正数第1日 "periodnum":"1" 时间周期倒数第1日 "periodnum":"-1"
+
+示例
+para = {
+"marketcode": "212001",
+"functionpara": {
+"mode": "1",
+"dateType": "0",
+"period": "D",
+"dateFormat": "0"
+},
+"startdate": "2025-09-10",
+"enddate": "2025-09-10"
+}
+
+14、日期偏移函数
+URL
+https://quantapi.51ifind.com/api/v1/get_trade_dates
+
+formData
+key
+
+是否必
+须
+
+value
+
+示例
+
+marketcode
+
+是
+
+见日期查询函数说明
+
+"marketcode":"212001"
+
+functionpara
+
+是
+
+key-value的参数
+
+见下方代码块
+
+startdate
+
+是
+
+基准日期,支持"YYYYMMDD""YYYY-MM-DD""YYYY/MM/DD"三种时
+间格式
+
+"startdate":"2018-0101"
+
+functionpara说明
+对应字段
+
+字段
+类型
+
+是否可
+省略
+
+省略时
+逻辑
+
+日期类型
+
+字符
+串
+
+不可
+
+交易日 "dateType":"0" 日历日 "dateType":"1"
+
+日期格式
+
+字符
+串
+
+不可
+
+YYYY-MM-DD "dateFormat":"0" YYYY/MM/DD "dateFormat":"1"
+YYYYMMDD "dateFormat":"2"
+
+前推后退
+
+字符
+串
+
+不可
+
+前推 "offset":"-5" 后推 "offset":"5"
+
+时间周期
+
+字符
+串
+
+不可
+
+日 "period":"D" 周 "period":"W" 月 "period":"M" 季 "period":"Q" 半年
+"period":"S" 年 "period":"Y"
+
+时间周期内
+偏移
+
+字符
+串
+
+可
+
+输出选项
+
+字符
+串
+
+不可
+
+默认
+
+命令生成示例说明
+
+默认 省略 时间周期正数第1日 "periodnum":"1" 时间周期倒数第1日
+"periodnum":"-1"
+所有日期 "output":"sequencedate" 单个日期 "output":"singledate"
+
+示例
+
+37 / 43
+
+para = {
+"marketcode": "212001",
+"functionpara": {
+"dateType": "0",
+"period": "D",
+"offset": "-1",
+"dateFormat": "0",
+"output": "sequencedate"
+},
+"startdate": "2025-09-10"
+}
+
+15、数据量查询
+无需参数,仅需要传入token访问url即可
+URL
+https://quantapi.51ifind.com/api/v1/get_data_volume
+
+示例
+Headers = {
+"Content-Type": "application/json",
+"access_token": "xxxxxxxxxx"
+}
+
+16、错误信息查询
+URL
+https://quantapi.51ifind.com/api/v1/get_error_message
+
+示例
+para
+
+=
+
+{
+
+"errorcode": -1
+}
+
+17、证券代码证券简称转同花顺代码
+URL
+https://quantapi.51ifind.com/api/v1/get_thscode
+
+formData
+key
+
+是否必须
+
+value
+
+示例
+
+seccode/secname
+
+是
+
+行情代码/简称
+
+"seccode":"000001"
+
+mode
+
+是
+
+seccode/secname
+
+"mode":"seccode"
+
+38 / 43
+
+key
+
+是否必须
+
+value
+
+示例
+
+sectype
+
+是
+
+证券类型
+
+"sectype":"001"
+
+market
+
+是
+
+市场
+
+"market":"212001"
+
+tradestatus
+
+是
+
+0,1,2
+
+"tradestatus":"0"
+
+isexact
+
+是
+
+0,1
+
+"isexact":"1"
+
+示例
+para
+
+=
+
+{
+
+"seccode": "300033",
+"functionpara": {
+"mode": "seccode",
+"sectype": "",
+"market": "",
+"tradestatus": "0",
+"isexact": "0"
+}
+}
+
+18、公告查询
+URL
+https://quantapi.51ifind.com/api/v1/report_query
+
+formData
+key
+
+是否
+必须
+
+value
+
+示例
+
+codes
+
+是
+
+半角逗号分隔的所有代码,如参数内容为空下面functionpara
+中mode参数板块必填
+
+"codes":"300033.SZ,600030.SH"
+
+functionpara
+
+否
+
+key-value格式。所有key均取默认时,functionpara省略。
+
+见下方说明
+
+outputpara
+
+是
+
+输出指标
+
+见下方说明
+
+functionpara说明
+名称
+
+keys
+
+value说明
+
+是否可
+省略
+
+示例
+
+提取方式
+
+mode
+
+allAStock-全部A股,allBond-全部债券 等按照证券
+板块全部代码提取
+
+可
+
+"mode":"allAStock"
+
+公告类型
+
+reportType
+
+903-全部;901002004-上市公告书 等
+
+可
+
+reportType:901
+
+公告开始
+日期
+
+beginrDate
+
+根据公告开始日期筛选
+
+可
+
+"beginrDate": "2024-09-10"
+
+公告截止
+日期
+
+endrDate
+
+根据公告截止日期筛选
+
+可
+
+"endrDate": "2025-09-10"
+
+发布开始
+时间
+
+begincTime
+
+根据发布时间筛选
+
+可
+
+"begincTime":"2023-09-10
+19:50:36"
+
+发布截止
+时间
+
+endcTime
+
+根据发布时间筛选
+
+可
+
+"endcTime":"2025-09-10
+20:50:36"
+
+开始seq
+
+beginSeq
+
+根据seq筛选
+
+可
+
+"beginSeq":"4569556291"
+
+截止seq
+
+endSeq
+
+根据seq筛选
+
+可
+
+"endSeq":"4679626676"
+
+39 / 43
+
+名称
+
+keys
+
+value说明
+
+是否可
+省略
+
+示例
+
+标题关键
+词
+
+keyWord
+
+根据公告标题关键词筛选
+
+可
+
+"keyWord":"半年度报告"
+
+outputpara说明
+名称
+
+value说明
+
+公告日期
+
+reportDate
+
+证券代码
+
+thscode
+
+证券简称
+
+secName
+
+发布时间
+
+ctime
+
+公告标题
+
+reportTitle
+
+公告链接
+
+pdfURL
+
+唯一标号
+
+seq
+
+注意:用户可以通过查询到的‘pdfURL’下载公告文件。
+示例
+para =
+{
+"codes": "300033.SZ,600000.SH",
+"functionpara": {
+"reportType": "901"
+},
+"beginrDate": "2024-09-10",
+"endrDate": "2025-09-10",
+"outputpara": "reportDate:Y,thscode:Y,secName:Y,ctime:Y,reportTitle:Y,pdfURL:Y,seq:Y"
+}
+
+输出:
+字段
+
+字段名称
+
+字段描述
+
+errorcode
+
+错误 ID
+
+代码运行错误码,errorcode =0表示代码运行正常。若为其他则需查找错误原因
+
+errmsg
+
+错误信息
+
+若 errorcode返回非空,此处会返回具体的错误信息
+
+tables
+
+结构体
+
+返回内容包括thscode、reportDate等outputpara选择返回的指标
+
+datatype
+
+指标格式
+
+返回获取数据的指标格式,目前本函数返回为空
+
+inputParams
+
+输入参数
+
+返回输入的参数,目前本函数返回为空
+
+perf
+
+处理时间
+
+返回请求命令整体耗时(ms)
+
+dataVol
+
+数据量
+
+返回当前命令消耗的数据量
+
+三、错误说明
+错误
+码
+
+错误信息
+
+错误提示
+
+-1010
+
+your account has been loggout out.
+
+token已失效
+
+-1000
+
+datasvr error!
+
+数据服务器错误
+
+-1001
+
+gwsvr error!
+
+网关服务器错误
+
+-1002
+
+timeout!
+
+超时
+
+40 / 43
+
+错误
+码
+
+错误信息
+
+错误提示
+
+-1003
+
+access-token can not be empty!
+
+数据服务器错误
+
+-1004
+
+datasvrhq error!
+
+传值不能为空
+
+-1005
+
+auth user error!
+
+用户验证错误
+
+-1201
+
+failed,please change your input condition.
+
+查询失败
+
+-1202
+
+there are errors in your parameters,please have a check.
+
+参数错误
+
+-1203
+
+parsing failed.
+
+解析失败
+
+-1300
+
+Not legal User
+
+token无效
+
+-1301
+
+Refresh_Token is expired or illegal
+
+refresh_token无效
+
+-1302
+
+Access_Token is expired or ilegal
+
+Access_Token无效
+
+-1303
+
+Device exceed limit
+
+access_token绑定超过20个IP
+
+-1305
+
+Exceeded the maximum number of token acquistions for the day
+
+每天请求token次数超过限制
+
+-4001
+
+no data.
+
+数据为空
+
+-4100
+
+please log in first!
+
+请先登录iFind
+
+-4101
+
+database execution error
+
+数据库执行错误
+
+-4102
+
+server internal error.
+
+服务端请求超时
+
+-4103
+
+unreasonable request! your account has been locked. please contact the saler
+to unlock
+
+超时请求过多,账号被锁
+
+-4201
+
+the data server is incorrect
+
+数据服务器取值错误
+
+-4203
+
+request format is wrong
+
+请求格式错误
+
+-4204
+
+wrong time format
+
+错误的时间格式
+
+-4205
+
+the start time can not be greater than the end time
+
+开始时间不能大于结束时间
+
+-4206
+
+include the wrong thscode
+
+含有错误的同花顺代码
+
+-4207
+
+sorry,currently we do not support bonds of this market.
+
+用户参数错误:不支持银行间债
+券
+
+-4208
+
+sorry, currently we just support kinds of securities of SSE, SZSE and CFFEX.
+
+目前仅支持上交所深交所
+
+-4209
+
+sorry, the startDate and endDate of Shopshot command should be the same,
+please have a check.
+
+起始、结束日期要求同一天
+
+-4210
+
+error happen with input parameters, please have a check.
+
+输入参数错误
+
+-4211
+
+sorry, there is no trading date in the date range, please have a check
+
+时间区间内无交易日
+
+-4212
+
+sorry, the input endDate is earlier than the listDates of the input security codes
+
+时间区间内股票未上市
+
+-4230
+
+you currently do not have permission for real-time Us stock market quotes
+
+没有美股实时行情权限
+
+-4213
+
+sorry, startDate can't later than endDate in the command, please have a check
+
+开始日期大于截止日期
+
+-4301
+
+sorry, your usage of basic data has exceeded 5 million this week.
+
+对不起,这周基础数据提取已
+经超过500万条
+
+-4302
+
+sorry, your usage of quote data has exceeded 150 million this week.
+
+对不起,这周报价数据提取已
+经超过1亿5千万条
+
+-4303
+
+sorry, your usage of EDB data has exceeded 5 million this week.
+
+对不起,这周EDB数据提取已
+经超过500万条
+
+-4317
+
+sorry, your usage of data has exceeded 1w this week.
+
+对不起,您本周数据量已超过
+1万
+
+-4318
+
+sorry, your usage of data has exceeded this month.
+
+对不起,本月使用量已经超限
+
+-4320
+
+sorry, your account must use the corresponding.
+
+抱歉,您的账户必须使用对应
+客户端
+
+-4321
+
+sorry, the free Acount can support requiring 10W data at most, please modify
+your input params!
+
+免费账号单次提取限制10万
+
+41 / 43
+
+错误
+码
+
+错误信息
+
+错误提示
+
+-4304
+
+sorry, the HighFrequeceSequence command can support requiring 200W data
+at most, please modify your input params
+
+单条命令请求数据量过大
+
+-4305
+
+sorry, the BasicData command can support requiring 20W data at most,
+please modify your input params
+
+单条命令请求数据量过大
+
+-4306
+
+sorry, the Snapshot command can support requiring 200W data at most,
+please modify your input params
+
+单条命令请求数据量过大
+
+-4319
+
+sorry, the free Acount can support requiring 5W data at most, please modify
+your input params
+
+免费用户单条命令请求数据量
+过大
+
+-4321
+
+sorry, the free Acount can support requiring 10W data at most, please modify
+your input params
+
+免费用户单条命令请求数据量
+过大
+
+-4322
+
+sorry, the free Acount can support requiring 1W data at most, please modify
+your input params
+
+免费用户单条命令请求数据量
+过大
+
+-4307
+
+data extraction is overrun.
+
+数据提取量超限
+
+-4308
+
+the range between startDate and endDate must be smaller than 1 month.
+Please check your input parameters.
+
+请求区间不能超过一个月
+
+-4309
+
+sorry, trial account can get 1 year data for authority limited, so as to acquire
+more data, please transfer it to formal account
+
+超出时间限制
+
+-4310
+
+sorry, trial account can get 1 month data for authority limited, so as to acquire
+more data, please transfer it to formal account
+
+超出时间限制
+
+-4311
+
+sorry, trial account can get 5 year data for authority limited, so as to acquire
+more data, please transfer it to formal account
+
+超出时间限制
+
+-4312
+
+sorry, the HistoryQuotes command can support requiring 200W data at most,
+please modify your input params
+
+超出200W限制
+
+-4313
+
+sorry,the interval should be smaller than 3 years,please change your startDate
+or endDate.
+
+对不起,开始时间与结束时间
+间隔不能超过3年
+
+-4314
+
+sorry,the interval should be smaller than 6 months,please change your
+startDate or endDate.
+
+对不起,开始时间与结束时间
+间隔不能超过6个月
+
+-4315
+
+sorry,the interval should be smaller than 3 months,please change your
+startDate or endDate.
+
+对不起,开始时间与结束时间
+间隔不能超过3个月
+
+-4316
+
+sorry,the interval should be smaller than 1 year,please change your startDate
+or endDate.
+
+对不起,开始时间与结束时间
+间隔不能超过1年
+
+-4400
+
+sorry, we just support 600 requests per minute.
+
+对不起,我们每分钟最多支持
+600条数据请求
+
+-5001
+
+sorry,data server parameter error.
+
+请求远程服务器参数错误
+
+-5002
+
+sorry,data server is busy now.
+
+查询失败
+
+-5003
+
+sorry,does not support the stock box selection calculation.
+
+不支持该股权查询
+
+-5004
+
+sorry,data process waiting timeout.
+
+等待超时
+
+-5005
+
+sorry, data calculation error.
+
+计算错误
+
+-5006
+
+sorry,data process query failed.
+
+查询失败
+
+-5007
+
+sorry,data process Waiting for calculation.
+
+等待计算
+
+-5008
+
+sorry,data process calculating.
+
+正在计算
+
+-5009
+
+sorry,must complete the last instruction request.
+
+必须完成上一次计算请求
+
+-5010
+
+sorry,only supports single code incoming.
+
+仅支持单代码传入
+
+-5100
+
+Sorry,account type is not supported.
+
+抱歉,您的账户类型不支持
+
+-5101
+
+Please confirm,you have not used the amount of date for the month.
+
+请确认,您尚未使用本月的数
+据量
+
+-5102
+
+Sorry,you have exceeded the maximum number of cleaes.
+
+抱歉,您已超过最大清零次数
+
+42 / 43
+
+错误
+码
+
+错误信息
+
+错误提示
+
+-5103
+
+Sorry,Do not allow accounts to operate in unbound mac code environments.
+
+抱歉,不允许账户在非绑定
+mac代码环境中运行
+
+-5104
+
+Sorry,this mac code has been bound .
+
+抱歉,该机器的mac已被绑定
+
+-5000
+
+please enter a reasonable expected dividend growth rate
+
+请输入合理的预期红利增长率
+数值
+
+四、适用范围
+本接口规范适用于同花顺数据接口与服务商端接口
+同花顺公司保留本接口最终解释权利
+
+五、版本管理
+版本信息体现在各函数的url中,新版本版本号逐渐向上累加,旧版本在有用户使用情况下保持不变
+
+43 / 43
+
+
\ No newline at end of file
diff --git a/JP-data.md b/JP-data.md
new file mode 100644
index 0000000..3c76fd2
--- /dev/null
+++ b/JP-data.md
@@ -0,0 +1,131 @@
+# 日本市场数据获取与指标计算说明 (JP-data)
+
+本文档详细记录了 `FA3` 项目中针对日本市场 (JP) 的数据获取来源、API 接口调用方式以及关键财务指标的计算逻辑。
+
+## 1. 数据获取 (API Fetching)
+
+所有数据均通过 **同花顺 iFinD API** 获取。为了节省 API 额度并提高准确性,我们在 `JpFetcher` 中采用了“按需点播”与“点对点抓取”的策略。
+
+### 1.1 公司基础信息 (Basic Info)
+
+* **接口**: `basic_data_service`
+* **用途**: 获取公司名称、上市日期及会计年结日,用于确定后续财报的抓取时间点。
+
+| 字段 | iFinD 指标 | 说明 |
+| :--- | :--- | :--- |
+| 公司简称 | `corp_cn_name` | 公司的中文简称 |
+| 上市日期 | `ipo_date` | 格式 YYYYMMDD |
+| 会计年结日 | `accounting_date` | 主要是 0331 或 1231 |
+
+### 1.2 财务报表 (Financial Statements)
+
+财务数据按**最近 5 个会计年度**逐年点播获取,统一货币单位为 **CNY (人民币)**。
+
+#### 利润表 (Income Statement)
+
+| 内部字段 | iFinD 指标 | 备注 |
+| :--- | :--- | :--- |
+| 营业收入 | `revenue_oas` | |
+| 毛利 | `gross_profit_oas` | |
+| SG&A 费用 | `sga_expenses_oas` | 销售、一般及管理费用 |
+| 销售费用 | `selling_marketing_expenses_oas`| |
+| 管理费用 | `ga_expenses_oas` | |
+| 研发费用 | `rd_expenses_oas` | |
+| 所得税 | `income_tax_expense_oas` | |
+| 归母净利润 | `net_income_attri_to_common_sh_oas` | |
+| 营业利润 | `operating_income_oas` | |
+
+#### 资产负债表 (Balance Sheet)
+
+| 内部字段 | iFinD 指标 | 备注 |
+| :--- | :--- | :--- |
+| 货币资金 | `cash_equi_short_term_inve_oas` | 现金及等价物+短期投资 |
+| 应收账款 | `accou_and_notes_recei_oas` | 应收账款及票据 |
+| 存货 | `inventories_oas` | |
+| 预付款项 | `prepayments_oas` | |
+| 固定资产 | `ppe_net_oas` | 不动产、厂房及设备净额 |
+| 长期投资 | `long_term_inv_and_receiv_oas` | 包含长期应收款 |
+| 商誉及无形 | `goodwill_and_intasset_oas` | **目前作为商誉指标使用** |
+| 短期借款 | `short_term_debt_oas` + `short_term_borrowings_oas` | 主要是 `short_term_debt_oas` |
+| 应付账款 | `account_and_note_payable_oas` | 应付账款及票据 |
+| 预收款项 | `advance_from_cust_current_oas` + `contra_liabilities_current_oas` | 包含合同负债 |
+| 长期借款 | `long_term_debt_oas` + `long_term_borrowings_oas` | |
+| 总资产 | `total_assets_oas` | |
+| 归母权益 | `equity_attri_to_companyowner_oas` | 净资产 |
+
+#### 现金流量表 (Cash Flow Statement)
+
+| 内部字段 | iFinD 指标 | 备注 |
+| :--- | :--- | :--- |
+| 经营净现金流 (OCF) | `net_cash_flows_from_oa_oas` | |
+| 资本开支 (Capex) | `purchase_of_ppe_and_ia_oas` | 购建资产支付的现金 |
+| 分红支付 | `dividends_paid_oas` | 现金流量表中的分红流出 |
+
+### 1.3 市场与分红数据 (Market & Events)
+
+* **当前股价**:
+ * **接口**: `date_sequence`
+ * **策略**: 获取**最近一个交易日**的 `pre_close` (收盘价),使用 `Fill: Previous` 防止节假日无数据。避免使用不稳定的实时接口。
+* **历史股价**:
+ * **接口**: `date_sequence`
+ * **策略**: 针对每个财报日期,单独点播当天的收盘价与市值。
+* **分红 (Dividends)**:
+ * **指标**: `annual_cum_dividend`
+ * **策略**: 按年逐年获取累计分红总额。
+* **回购 (Repurchases)**:
+ * **指标**: `repur_num_new`
+ * **策略**: 按会计年度范围获取回购金额。
+* **员工人数 (Employee Count)**:
+ * **指标**: `staff_num`
+ * **策略**: 针对每个财报日期(报告期末),单独获取当天的员工人数,获取最近 5 年数据。
+
+---
+
+## 2. 指标计算 (Indicator Calculation)
+
+以下指标由 `JP_Analyzer` 基于获取的原始数据计算得出。
+
+### 2.1 盈利能力与回报率
+
+* **ROE (净资产收益率)** = `归母净利润 / 归母股东权益`
+* **ROA (总资产收益率)** = `归母净利润 / 总资产`
+* **毛利率 (Gross Margin)** = `毛利 / 营业收入`
+* **净利率 (Net Margin)** = `归母净利润 / 营业收入`
+
+### 2.2 费用与税率
+
+* **SG&A 比例** = 优先使用 `SG&A费用 / 营业收入`;若无数据,则退化为 `(销售费用 + 管理费用) / 营业收入`。
+* **所得税率** = `所得税费用 / (归母净利润 + 所得税费用)` (由于 API 缺少利润总额,采用此公式进行估算)
+* **其他费用率** = `毛利率 - 净利率 - (SG&A比例 + 研发费率)` (倒挤差额)
+
+### 2.3 资产周转 (Turnover)
+
+* **存货周转天数** = `存货 * 365 / (营业收入 - 毛利)`
+* **应收周转天数** = `应收账款 * 365 / 营业收入`
+* **应付周转天数** = `应付账款 * 365 / (营业收入 - 毛利)`
+* **固定资产周转率** = `营业收入 / 固定资产`
+* **总资产周转率** = `营业收入 / 总资产`
+
+### 2.4 人均效率 (Efficiency)
+
+* **人均创收** = `营业收入 / 员工人数`
+* **人均创利** = `归母净利润 / 员工人数`
+
+### 2.5 估值与市场指标 (Valuation)
+
+* **股价** = 最近交易日收盘价 (CNY)
+* **市值 (Market Cap)** = `股价 * 总股本` (API 直接获取市值)
+* **PE (市盈率)** = 优先使用读取数据;若为空,则计算 `市值 / 归母净利润`
+* **PB (市净率)** = 优先使用读取数据;若为空,则计算 `市值 / 归母股东权益`
+* **股息率 (Dividend Yield)** = 优先使用读取数据;若为空,则计算 `年度分红总额 / 市值` (单位:%)
+
+### 2.6 现金流指标
+
+* **自由现金流 (FCF)** = `经营净现金流 (OCF) - 资本开支 (Capex)`
+ * *注: 资本开支取绝对值计算*
+
+## 3. 注意事项
+
+1. **货币单位**: 为了统一计算,所有从 iFinD 获取的财务数据均强制指定货币为 **CNY**。
+2. **API 额度优化**: 历史行情数据(股价、市值)均采用**单点日期查询**模式,避免全量下载时间序列数据,极大节省了 API 流量。
+3. **商誉处理**: iFinD 的日本数据中,`goodwill_and_intasset_oas` (商誉及无形资产) 被用作“商誉”的近似指标。
diff --git a/data/JP/7203.T/raw_balance_sheet_raw.csv b/data/JP/7203.T/raw_balance_sheet_raw.csv
new file mode 100644
index 0000000..676dae8
--- /dev/null
+++ b/data/JP/7203.T/raw_balance_sheet_raw.csv
@@ -0,0 +1,6 @@
+cash_equi_short_term_inve_oas,accou_and_notes_recei_oas,inventories_oas,ppe_net_oas,long_term_inv_and_receiv_oas,goodwill_and_intasset_oas,short_term_debt_oas,short_term_borrowings_oas,account_and_note_payable_oas,contra_liabilities_current_oas,advance_from_cust_current_oas,defer_revenue_current_oas,long_term_debt_oas,long_term_borrowings_oas,total_assets_oas,equity_attri_to_companyowner_oas,prepaid_expenses_current_oas,end_date
+770602568734.0099,672717680537.7899,222601652642.64,770534649125.2,1551773147819.29,65996075141.82001,766311143529.32,264536419696.63,195331566628.4,,,,1111662554527.74,1090303749762.66,4531266625864.5,1739130526363.0203,,20251231
+673911903173.7201,653991429222.8099,219892459842.32,706207728474.77,1529223836526.51,64712736968.73999,735603735832.15,262033523493.40997,182778724515.32,,,,1010111255957.04,991532329184.16,4302686389969.04,1633949315067.0898,,20241231
+477635263624.52997,568835847614.87,220157083414.62,679017649048.86,1399256001575.08,64621240636.259995,636611683247.87,237464934566.09,197586866202.22,,,,883327675351.22,863190476648.72,3843950930989.4004,1466055629270.9797,,20231231
+450020533384.27,501527244485.08997,199478948478.04,666871639206.68,1258066992832.73,62221924442.94001,584017390544.51,214278061895.22,165377438011.56,,,,799121378085.7101,780078838062.43,3533427626960.3896,1370068189906.21,,20221231
+551836394708.76,534674422943.86,171067544453.52,699030178408.64,1275497513656.26,65668094657.56,723361042847.0599,271324355670.68,174958464091.44,,,,796544782150.5,777959077825.36,3688290674447.6,1386329489996.98,,20211231
diff --git a/data/JP/7203.T/raw_basic_info_raw.csv b/data/JP/7203.T/raw_basic_info_raw.csv
new file mode 100644
index 0000000..c6dfed8
--- /dev/null
+++ b/data/JP/7203.T/raw_basic_info_raw.csv
@@ -0,0 +1,2 @@
+corp_cn_name,accounting_date,ipo_date
+丰田汽车公司,0331,19490516
diff --git a/data/JP/7203.T/raw_cash_flow_raw.csv b/data/JP/7203.T/raw_cash_flow_raw.csv
new file mode 100644
index 0000000..7ec5f12
--- /dev/null
+++ b/data/JP/7203.T/raw_cash_flow_raw.csv
@@ -0,0 +1,6 @@
+net_cash_flows_from_oa_oas,purchase_of_ppe_and_ia_oas,dividends_paid_oas,end_date
+178969573112.18,109455959341.89,54816352618.83,20251231
+200841649567.27002,104123484490.66,42026757357.03001,20241231
+152875921883.08,93041152405.08,37660829573.4,20231231
+194324560650.35,80564604452.59,37056092160.48,20221231
+161538913981.08,88396874949.0,37051283436.76,20211231
diff --git a/data/JP/7203.T/raw_employee_count_raw.csv b/data/JP/7203.T/raw_employee_count_raw.csv
new file mode 100644
index 0000000..874fbbd
--- /dev/null
+++ b/data/JP/7203.T/raw_employee_count_raw.csv
@@ -0,0 +1,6 @@
+date_str,employee_count
+20251231,383853.0
+20241231,380793.0
+20231231,375235.0
+20221231,372817.0
+20211231,366283.0
diff --git a/data/JP/7203.T/raw_historical_metrics_raw.csv b/data/JP/7203.T/raw_historical_metrics_raw.csv
new file mode 100644
index 0000000..6ec88b5
--- /dev/null
+++ b/data/JP/7203.T/raw_historical_metrics_raw.csv
@@ -0,0 +1,6 @@
+date_str,PE,PB,MarketCap,Price
+20251231,0.0,0.0,2448738372188.5,155.03262528
+20241231,0.0,0.0,2304821554513.2,145.9210753
+20231231,0.0,0.0,2129364298560.0,130.515840345
+20221231,0.0,0.0,1547352230179.7,94.842379375
+20211231,0.0,0.0,1900725057905.6,116.50177866
diff --git a/data/JP/7203.T/raw_income_statement_raw.csv b/data/JP/7203.T/raw_income_statement_raw.csv
new file mode 100644
index 0000000..24bee6a
--- /dev/null
+++ b/data/JP/7203.T/raw_income_statement_raw.csv
@@ -0,0 +1,6 @@
+revenue_oas,gross_profit_oas,sga_expenses_oas,selling_marketing_expenses_oas,ga_expenses_oas,rd_expenses_oas,income_tax_expense_oas,net_income_attri_to_common_sh_oas,operating_income_oas,end_date
+2325469810550.08,463675405650.25995,231519792582.04,,,,78658701055.45,230679099833.22,232155613068.22,20251231
+2153166031821.75,447308985862.81995,191722451947.16998,,,,90416803818.35,236105666501.67,255586486168.66,20241231
+1922115559352.3398,326593340023.28,185618670706.7,,,,60826238747.45,126814843028.94,140974617583.25,20231231
+1638044469062.6301,311727839723.57,155349243214.93,,,,58252135950.62,148778848619.9,156378648709.72998,20221231
+1612011299363.96,286237652149.16,156057638397.5,,,,38500249399.84,132994308201.74,130179954518.32,20211231
diff --git a/data/JP/7203.T/raw_market_metrics_raw.csv b/data/JP/7203.T/raw_market_metrics_raw.csv
new file mode 100644
index 0000000..fd5a3f3
--- /dev/null
+++ b/data/JP/7203.T/raw_market_metrics_raw.csv
@@ -0,0 +1,2 @@
+pe_ttm,pb
+,
diff --git a/data/JP/7203.T/report.html b/data/JP/7203.T/report.html
new file mode 100644
index 0000000..0a440e3
--- /dev/null
+++ b/data/JP/7203.T/report.html
@@ -0,0 +1,445 @@
+
+
+
+
+
+ 7203.T Financial Report
+
+
+
+
+
丰田汽车公司 (7203.T) - Financial Report
+
Report generated on: 2025-12-21
+
+
+
+
+ | 代码 |
+ 简称 |
+ 上市日期 |
+ PE |
+ PB |
+ 股息率(%) |
+
+
+
+
+ | 7203.T |
+ 丰田汽车公司 |
+ 1949-05-16 |
+ 0.00 |
+ 0.00 |
+ 0.00% |
+
+
+
+
+
+
+
+
+
+ | 指标 |
+ 2025A | 2024A | 2023A | 2022A | 2021A |
+
+
+
+ | 主要指标 | |
+| ROE | 13.26% | 14.45% | 8.65% | 10.86% | 9.59% |
+| ROA | 5.09% | 5.49% | 3.30% | 4.21% | 3.61% |
+| ROCE/ROIC | - | - | - | - | - |
+| 毛利率 | 19.94% | 20.77% | 16.99% | 19.03% | 17.76% |
+| 净利润率 | 9.92% | 10.97% | 6.60% | 9.08% | 8.25% |
+| 收入(亿) | 23,254.70 | 21,531.66 | 19,221.16 | 16,380.44 | 16,120.11 |
+| 收入增速 | 8.00% | 12.02% | 17.34% | 1.61% | - |
+| 净利润(亿) | 2,306.79 | 2,361.06 | 1,268.15 | 1,487.79 | 1,329.94 |
+| 净利润增速 | -2.30% | 86.18% | -14.76% | 11.87% | - |
+| 经营净现金流(亿) | 1,789.70 | 2,008.42 | 1,528.76 | 1,943.25 | 1,615.39 |
+| 资本开支(亿) | 1,094.56 | 1,041.23 | 930.41 | 805.65 | 883.97 |
+| 自由现金流(亿) | 695.14 | 967.18 | 598.35 | 1,137.60 | 731.42 |
+| 分红(亿) | 548.16 | 420.27 | 376.61 | 370.56 | 370.51 |
+| 回购(亿) | - | - | - | - | - |
+| 总资产(亿) | 45,312.67 | 43,026.86 | 38,439.51 | 35,334.28 | 36,882.91 |
+| 净资产(亿) | 17,391.31 | 16,339.49 | 14,660.56 | 13,700.68 | 13,863.29 |
+| 商誉(亿) | 659.96 | 647.13 | 646.21 | 622.22 | 656.68 |
+| 费用指标 | |
+| 销售费用率 | - | - | - | - | - |
+| 管理费用率 | - | - | - | - | - |
+| SG&A比例 | 9.96% | 8.90% | 9.66% | 9.48% | 9.68% |
+| 研发费用率 | - | - | - | - | - |
+| 其他费用率 | 0.06% | 0.90% | 0.74% | 0.46% | -0.17% |
+| 折旧费用占比 | - | - | - | - | - |
+| 所得税率 | 25.43% | 27.69% | 32.42% | 28.14% | 22.45% |
+| 资产占比 | |
+| 现金占比 | 17.01% | 15.66% | 12.43% | 12.74% | 14.96% |
+| 库存占比 | 4.91% | 5.11% | 5.73% | 5.65% | 4.64% |
+| 应收款占比 | 14.85% | 15.20% | 14.80% | 14.19% | 14.50% |
+| 预付款占比 | - | - | - | - | - |
+| 固定资产占比 | 17.00% | 16.41% | 17.66% | 18.87% | 18.95% |
+| 长期投资占比 | 34.25% | 35.54% | 36.40% | 35.60% | 34.58% |
+| 商誉占比 | 1.46% | 1.50% | 1.68% | 1.76% | 1.78% |
+| 其他资产占比 | 10.53% | 10.57% | 11.30% | 11.19% | 10.59% |
+| 应付款占比 | 4.31% | 4.25% | 5.14% | 4.68% | 4.74% |
+| 预收款占比 | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% |
+| 短期借款占比 | 16.91% | 17.10% | 16.56% | 16.53% | 19.61% |
+| 长期借款占比 | 48.59% | 46.52% | 45.44% | 44.69% | 42.69% |
+| 运营资产占比 | 15.45% | 16.06% | 15.39% | 15.16% | 14.39% |
+| 有息负债率 | 65.51% | 63.62% | 62.00% | 61.22% | 62.30% |
+| 周转能力 | |
+| 存货周转天数 | 43 | 47 | 50 | 54 | 47 |
+| 应收款周转天数 | 105 | 110 | 108 | 111 | 121 |
+| 应付款周转天数 | 38 | 39 | 45 | 45 | 48 |
+| 固定资产周转率 | 3.02 | 3.05 | 2.83 | 2.46 | 2.31 |
+| 总资产周转率 | 0.51 | 0.50 | 0.50 | 0.46 | 0.44 |
+| 人均效率 | |
+| 员工人数 | 383,853 | 380,793 | 375,235 | 372,817 | 366,283 |
+| 人均创收(万) | 605.82 | 565.44 | 512.24 | 439.37 | 440.10 |
+| 人均创利(万) | 60.10 | 62.00 | 33.80 | 39.91 | 36.31 |
+| 人均薪酬(万) | - | - | - | - | - |
+| 市场表现 | |
+| 股价 | - | 145.92 | 130.52 | 94.84 | 116.50 |
+| 市值(亿) | - | 23,048 | 21,294 | 15,474 | 19,007 |
+| PE | - | 9.76 | 16.79 | 10.40 | 14.29 |
+| PB | - | 1.41 | 1.45 | 1.13 | 1.37 |
+| 股东户数 | - | - | - | - | - |
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/JP/7203.T/report.md b/data/JP/7203.T/report.md
new file mode 100644
index 0000000..56d11a8
--- /dev/null
+++ b/data/JP/7203.T/report.md
@@ -0,0 +1,89 @@
+# 丰田汽车公司 (7203.T) - Financial Report
+*Report generated on: 2025-12-21*
+
+| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) |
+|:---|:---|:---|:---|:---|:---|
+| 7203.T | 丰田汽车公司 | 1949-05-16 | 0.00 | 0.00 | 0.00% |
+
+
+## 主要指标
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| ROE | 13.26% | 14.45% | 8.65% | 10.86% | 9.59% |
+| ROA | 5.09% | 5.49% | 3.30% | 4.21% | 3.61% |
+| ROCE/ROIC | - | - | - | - | - |
+| 毛利率 | 19.94% | 20.77% | 16.99% | 19.03% | 17.76% |
+| 净利润率 | 9.92% | 10.97% | 6.60% | 9.08% | 8.25% |
+| 收入(亿) | 23,254.70 | 21,531.66 | 19,221.16 | 16,380.44 | 16,120.11 |
+| 收入增速 | 8.00% | 12.02% | 17.34% | 1.61% | - |
+| 净利润(亿) | 2,306.79 | 2,361.06 | 1,268.15 | 1,487.79 | 1,329.94 |
+| 净利润增速 | -2.30% | 86.18% | -14.76% | 11.87% | - |
+| 经营净现金流(亿) | 1,789.70 | 2,008.42 | 1,528.76 | 1,943.25 | 1,615.39 |
+| 资本开支(亿) | 1,094.56 | 1,041.23 | 930.41 | 805.65 | 883.97 |
+| 自由现金流(亿) | 695.14 | 967.18 | 598.35 | 1,137.60 | 731.42 |
+| 分红(亿) | 548.16 | 420.27 | 376.61 | 370.56 | 370.51 |
+| 回购(亿) | - | - | - | - | - |
+| 总资产(亿) | 45,312.67 | 43,026.86 | 38,439.51 | 35,334.28 | 36,882.91 |
+| 净资产(亿) | 17,391.31 | 16,339.49 | 14,660.56 | 13,700.68 | 13,863.29 |
+| 商誉(亿) | 659.96 | 647.13 | 646.21 | 622.22 | 656.68 |
+
+
+## 费用指标
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| 销售费用率 | - | - | - | - | - |
+| 管理费用率 | - | - | - | - | - |
+| SG&A比例 | 9.96% | 8.90% | 9.66% | 9.48% | 9.68% |
+| 研发费用率 | - | - | - | - | - |
+| 其他费用率 | 0.06% | 0.90% | 0.74% | 0.46% | -0.17% |
+| 折旧费用占比 | - | - | - | - | - |
+| 所得税率 | 25.43% | 27.69% | 32.42% | 28.14% | 22.45% |
+
+
+## 资产占比
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| 现金占比 | 17.01% | 15.66% | 12.43% | 12.74% | 14.96% |
+| 库存占比 | 4.91% | 5.11% | 5.73% | 5.65% | 4.64% |
+| 应收款占比 | 14.85% | 15.20% | 14.80% | 14.19% | 14.50% |
+| 预付款占比 | - | - | - | - | - |
+| 固定资产占比 | 17.00% | 16.41% | 17.66% | 18.87% | 18.95% |
+| 长期投资占比 | 34.25% | 35.54% | 36.40% | 35.60% | 34.58% |
+| 商誉占比 | 1.46% | 1.50% | 1.68% | 1.76% | 1.78% |
+| 其他资产占比 | 10.53% | 10.57% | 11.30% | 11.19% | 10.59% |
+| 应付款占比 | 4.31% | 4.25% | 5.14% | 4.68% | 4.74% |
+| 预收款占比 | 0.00% | 0.00% | 0.00% | 0.00% | 0.00% |
+| 短期借款占比 | 16.91% | 17.10% | 16.56% | 16.53% | 19.61% |
+| 长期借款占比 | 48.59% | 46.52% | 45.44% | 44.69% | 42.69% |
+| 运营资产占比 | 15.45% | 16.06% | 15.39% | 15.16% | 14.39% |
+| 有息负债率 | 65.51% | 63.62% | 62.00% | 61.22% | 62.30% |
+
+
+## 周转能力
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| 存货周转天数 | 43 | 47 | 50 | 54 | 47 |
+| 应收款周转天数 | 105 | 110 | 108 | 111 | 121 |
+| 应付款周转天数 | 38 | 39 | 45 | 45 | 48 |
+| 固定资产周转率 | 3.02 | 3.05 | 2.83 | 2.46 | 2.31 |
+| 总资产周转率 | 0.51 | 0.50 | 0.50 | 0.46 | 0.44 |
+
+
+## 人均效率
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| 员工人数 | 383,853 | 380,793 | 375,235 | 372,817 | 366,283 |
+| 人均创收(万) | 605.82 | 565.44 | 512.24 | 439.37 | 440.10 |
+| 人均创利(万) | 60.10 | 62.00 | 33.80 | 39.91 | 36.31 |
+| 人均薪酬(万) | - | - | - | - | - |
+
+
+## 市场表现
+| 指标 | 2025A | 2024A | 2023A | 2022A | 2021A |
+|:---|--:|--:|--:|--:|--:|
+| 股价 | - | 145.92 | 130.52 | 94.84 | 116.50 |
+| 市值(亿) | - | 23,048 | 21,294 | 15,474 | 19,007 |
+| PE | - | 9.76 | 16.79 | 10.40 | 14.29 |
+| PB | - | 1.41 | 1.45 | 1.13 | 1.37 |
+| 股东户数 | - | - | - | - | - |
+
diff --git a/main.py b/main.py
index 2d67b19..a44f23a 100644
--- a/main.py
+++ b/main.py
@@ -4,17 +4,26 @@ from dotenv import load_dotenv
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
-from strategies.cn_strategy import CN_Strategy
-from strategies.us_strategy import US_Strategy
-from strategies.hk_strategy import HK_Strategy
+# from strategies.cn_strategy import CN_Strategy
+# from strategies.us_strategy import US_Strategy
+# from strategies.hk_strategy import HK_Strategy
+# from strategies.jp_strategy import JP_Strategy
def get_strategy(market, stock_code, tushare_token=None, av_key=None):
- if market.upper() == 'CN':
+ market = market.upper()
+ if market == 'CN':
+ from strategies.cn_strategy import CN_Strategy
return CN_Strategy(stock_code, tushare_token)
- elif market.upper() == 'US':
+ elif market == 'US':
+ from strategies.us_strategy import US_Strategy
return US_Strategy(stock_code, av_key)
- elif market.upper() == 'HK':
- return HK_Strategy(stock_code, tushare_token)
+ elif market == 'HK':
+ from strategies.hk_strategy import HK_Strategy
+ return HK_Strategy(stock_code, av_key)
+ elif market == 'JP':
+ from strategies.jp_strategy import JP_Strategy
+ ifind_token = os.getenv('IFIND_REFRESH_TOKEN')
+ return JP_Strategy(stock_code, ifind_token)
else:
raise ValueError(f"Unsupported market: {market}")
@@ -41,8 +50,12 @@ def main():
us_strategy.execute()
# Test HK
- hk_strategy = get_strategy('HK', '00700.HK', tushare_token)
+ hk_strategy = get_strategy('HK', '00700.HK', av_key=av_key)
hk_strategy.execute()
+ # Test JP
+ jp_strategy = get_strategy('JP', '7203') # Toyota
+ jp_strategy.execute()
+
if __name__ == "__main__":
main()
diff --git a/requirements.txt b/requirements.txt
index 2014466..8c09886 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,4 @@ tushare
alpha_vantage
python-dotenv
markdown
+jquants-api-client
diff --git a/src/analysis/__pycache__/base_analyzer.cpython-312.pyc b/src/analysis/__pycache__/base_analyzer.cpython-312.pyc
index 50f840a..4303bdc 100644
Binary files a/src/analysis/__pycache__/base_analyzer.cpython-312.pyc and b/src/analysis/__pycache__/base_analyzer.cpython-312.pyc differ
diff --git a/src/analysis/__pycache__/base_analyzer.cpython-313.pyc b/src/analysis/__pycache__/base_analyzer.cpython-313.pyc
index 03d99f5..3b4d463 100644
Binary files a/src/analysis/__pycache__/base_analyzer.cpython-313.pyc and b/src/analysis/__pycache__/base_analyzer.cpython-313.pyc differ
diff --git a/src/analysis/__pycache__/cn_analyzer.cpython-312.pyc b/src/analysis/__pycache__/cn_analyzer.cpython-312.pyc
index 6e71c93..e9488c3 100644
Binary files a/src/analysis/__pycache__/cn_analyzer.cpython-312.pyc and b/src/analysis/__pycache__/cn_analyzer.cpython-312.pyc differ
diff --git a/src/analysis/__pycache__/hk_analyzer.cpython-313.pyc b/src/analysis/__pycache__/hk_analyzer.cpython-313.pyc
index a566b73..a048176 100644
Binary files a/src/analysis/__pycache__/hk_analyzer.cpython-313.pyc and b/src/analysis/__pycache__/hk_analyzer.cpython-313.pyc differ
diff --git a/src/analysis/__pycache__/jp_analyzer.cpython-312.pyc b/src/analysis/__pycache__/jp_analyzer.cpython-312.pyc
new file mode 100644
index 0000000..f353f4c
Binary files /dev/null and b/src/analysis/__pycache__/jp_analyzer.cpython-312.pyc differ
diff --git a/src/analysis/__pycache__/jp_analyzer.cpython-313.pyc b/src/analysis/__pycache__/jp_analyzer.cpython-313.pyc
new file mode 100644
index 0000000..1659a54
Binary files /dev/null and b/src/analysis/__pycache__/jp_analyzer.cpython-313.pyc differ
diff --git a/src/analysis/base_analyzer.py b/src/analysis/base_analyzer.py
index 396b599..0bcf45d 100644
--- a/src/analysis/base_analyzer.py
+++ b/src/analysis/base_analyzer.py
@@ -6,7 +6,7 @@ class BaseAnalyzer:
self.market = market
self.mapping = {}
- def process_data(self, df_inc: pd.DataFrame, df_bal: pd.DataFrame, df_cf: pd.DataFrame, market_metrics: dict, historical_metrics: pd.DataFrame = None, df_div: pd.DataFrame = None, df_rep: pd.DataFrame = None) -> pd.DataFrame:
+ def process_data(self, df_inc: pd.DataFrame, df_bal: pd.DataFrame, df_cf: pd.DataFrame, market_metrics: dict, historical_metrics: pd.DataFrame = None, df_div: pd.DataFrame = None, df_rep: pd.DataFrame = None, df_emp: pd.DataFrame = None) -> pd.DataFrame:
df_inc = self._map_columns(df_inc, 'income')
df_bal = self._map_columns(df_bal, 'balance')
df_cf = self._map_columns(df_cf, 'cashflow')
@@ -19,17 +19,20 @@ class BaseAnalyzer:
return pd.DataFrame()
df_merged = df_inc
- if not df_bal.empty:
+ if not df_bal.empty and 'date_str' in df_bal.columns:
df_merged = pd.merge(df_merged, df_bal, on='date_str', how='outer', suffixes=('', '_bal'))
- if not df_cf.empty:
+ if not df_cf.empty and 'date_str' in df_cf.columns:
df_merged = pd.merge(df_merged, df_cf, on='date_str', how='outer', suffixes=('', '_cf'))
- if df_div is not None and not df_div.empty:
+ if df_div is not None and not df_div.empty and 'date_str' in df_div.columns:
df_merged = pd.merge(df_merged, df_div, on='date_str', how='left', suffixes=('', '_div'))
- if df_rep is not None and not df_rep.empty:
+ if df_rep is not None and not df_rep.empty and 'date_str' in df_rep.columns:
df_merged = pd.merge(df_merged, df_rep, on='date_str', how='left', suffixes=('', '_rep'))
+ if df_emp is not None and not df_emp.empty and 'date_str' in df_emp.columns:
+ df_merged = pd.merge(df_merged, df_emp, on='date_str', how='left', suffixes=('', '_emp'))
+
if df_merged.empty:
return pd.DataFrame()
diff --git a/src/analysis/hk_analyzer.py b/src/analysis/hk_analyzer.py
index 7c63d9e..26462be 100644
--- a/src/analysis/hk_analyzer.py
+++ b/src/analysis/hk_analyzer.py
@@ -7,43 +7,23 @@ class HK_Analyzer(BaseAnalyzer):
super().__init__('HK')
self.mapping = {
'income': {
- 'revenue': 'revenue',
- 'net_profit_attr_p': 'net_income',
- # Assuming other fields are similar to CN, will need verification
- 'total_cogs': 'total_costs',
- 'sell_exp': 'selling_exp',
- 'admin_exp': 'admin_exp',
- 'fin_exp': 'fin_exp',
- 'rd_exp': 'rd_exp',
- 'total_profit': 'total_profit',
- 'income_tax': 'income_tax'
+ 'totalRevenue': 'revenue', 'costOfRevenue': 'cogs', 'grossProfit': 'gross_profit',
+ 'sellingGeneralAndAdministrative': 'sga_exp',
+ 'researchAndDevelopment': 'rd_exp', 'interestExpense': 'fin_exp',
+ 'incomeBeforeTax': 'total_profit', 'incomeTaxExpense': 'income_tax', 'netIncome': 'net_income',
+ 'ebit': 'ebit',
},
'balance': {
- 'money_cap': 'cash',
- 'accounts_receiv': 'receivables',
- 'inventories': 'inventory',
- 'fix_assets': 'fixed_assets',
- 'total_assets': 'total_assets',
- 'goodwill': 'goodwill',
- 'prepayment': 'prepayment',
- 'lt_eqt_invest': 'lt_invest',
- 'oth_assets': 'other_assets',
- 'trad_asset': 'trading_assets',
- 'st_borr': 'short_term_debt',
- 'non_cur_liab_due_1y': 'short_term_debt_part',
- 'lt_borr': 'long_term_debt',
- 'bonds_payable': 'long_term_debt_bonds',
- 'total_liab': 'total_liabilities',
- 'total_share_holder_equity': 'total_equity',
- 'adv_receipts': 'adv_receipts',
- 'contract_liab': 'contract_liab',
- 'acct_payable': 'accounts_payable',
+ 'cashAndCashEquivalentsAtCarryingValue': 'cash', 'currentNetReceivables': 'receivables', 'inventory': 'inventory',
+ 'propertyPlantEquipment': 'fixed_assets', 'totalAssets': 'total_assets', 'goodwill': 'goodwill',
+ 'otherCurrentAssets': 'prepayment', 'longTermInvestments': 'lt_invest', 'otherNonCurrentAssets': 'other_assets',
+ 'shortTermDebt': 'short_term_debt', 'currentLongTermDebt': 'short_term_debt_part',
+ 'longTermDebt': 'long_term_debt', 'totalLiabilities': 'total_liabilities', 'totalShareholderEquity': 'total_equity',
+ 'currentAccountsPayable': 'accounts_payable', 'deferredRevenue': 'adv_receipts',
},
'cashflow': {
- 'n_cashflow_act': 'ocf',
- 'c_pay_acq_const_fiolta': 'capex',
- 'c_paid_div_prof_int': 'dividends',
- 'c_paid_to_for_empl': 'cash_paid_for_employees'
+ 'operatingCashflow': 'ocf', 'capitalExpenditures': 'capex', 'dividendPayout': 'dividends',
+ 'depreciationDepletionAndAmortization': 'depreciation'
}
}
@@ -52,11 +32,7 @@ class HK_Analyzer(BaseAnalyzer):
if 'short_term_debt' not in df.columns: df['short_term_debt'] = 0
if 'short_term_debt_part' in df.columns:
df['short_term_debt'] = df['short_term_debt'].fillna(0) + df['short_term_debt_part'].fillna(0)
-
- if 'long_term_debt' not in df.columns: df['long_term_debt'] = 0
- if 'long_term_debt_bonds' in df.columns:
- df['long_term_debt'] = df['long_term_debt'].fillna(0) + df['long_term_debt_bonds'].fillna(0)
-
+
if type == 'income':
if 'gross_profit' not in df.columns and 'revenue' in df.columns and 'cogs' in df.columns:
df['gross_profit'] = df['revenue'] - df['cogs']
@@ -66,7 +42,7 @@ class HK_Analyzer(BaseAnalyzer):
return df
def calculate_indicators(self, df_merged, market_metrics, historical_metrics):
-
+
def calc_yoy_growth(df, col):
growths = []
for idx, row in df.iterrows():
@@ -110,7 +86,7 @@ class HK_Analyzer(BaseAnalyzer):
df_merged['InvestedCapital'] = df_merged['total_equity'] + df_merged['InterestBearingDebt']
if 'ebit' in df_merged.columns:
df_merged['ROIC'] = self._safe_div(df_merged['ebit'], df_merged['InvestedCapital'])
-
+
# Cash Flow
if 'capex' in df_merged.columns:
df_merged['Capex'] = df_merged['capex'].abs()
@@ -118,22 +94,20 @@ class HK_Analyzer(BaseAnalyzer):
df_merged['FCF'] = df_merged['ocf'] - df_merged['Capex']
# Expenses
- if 'selling_exp' in df_merged.columns:
- df_merged['SellingRatio'] = self._safe_div(df_merged['selling_exp'], df_merged['revenue'])
- if 'admin_exp' in df_merged.columns:
- df_merged['AdminRatio'] = self._safe_div(df_merged['admin_exp'], df_merged['revenue'])
+ if 'sga_exp' in df_merged.columns:
+ df_merged['SgaRatio'] = self._safe_div(df_merged['sga_exp'], df_merged['revenue'])
if 'rd_exp' in df_merged.columns:
df_merged['RDRatio'] = self._safe_div(df_merged['rd_exp'], df_merged['revenue'])
- df_merged['SgaRatio'] = (df_merged.get('SellingRatio', 0)) + (df_merged.get('AdminRatio', 0))
if 'income_tax' in df_merged.columns and 'total_profit' in df_merged.columns:
df_merged['TaxRate'] = self._safe_div(df_merged['income_tax'], df_merged['total_profit'])
- # Other Expense Ratio (GrossMargin - Selling - Admin - RD - NetMargin)
+ # Other Expense Ratio
if 'GrossMargin' in df_merged.columns and 'NetMargin' in df_merged.columns:
other_ratio = df_merged['GrossMargin'] - df_merged['NetMargin']
- if 'SellingRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['SellingRatio'].fillna(0)
- if 'AdminRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['AdminRatio'].fillna(0)
- if 'RDRatio' in df_merged.columns: other_ratio = other_ratio - df_merged['RDRatio'].fillna(0)
+ if 'SgaRatio' in df_merged.columns:
+ other_ratio = other_ratio - df_merged['SgaRatio'].fillna(0)
+ if 'RDRatio' in df_merged.columns:
+ other_ratio = other_ratio - df_merged['RDRatio'].fillna(0)
df_merged['OtherExpenseRatio'] = other_ratio
# Depreciation Expense Ratio
@@ -158,12 +132,14 @@ class HK_Analyzer(BaseAnalyzer):
if 'lt_invest' in df_merged.columns: df_merged['LongTermInvestmentRatio'] = self._safe_div(df_merged['lt_invest'], assets)
if 'goodwill' in df_merged.columns: df_merged['GoodwillRatio'] = self._safe_div(df_merged['goodwill'], assets)
+ # Other Assets Ratio (as a residual)
known_assets_ratio = (series_or_zero('CashRatio') + series_or_zero('InventoryRatio') + series_or_zero('ReceivablesRatio') +
series_or_zero('PrepaymentRatio') + series_or_zero('FixedAssetsRatio') + series_or_zero('LongTermInvestmentRatio') +
series_or_zero('GoodwillRatio'))
df_merged['OtherAssetsRatio'] = 1 - known_assets_ratio
- adv = series_or_zero('adv_receipts') + series_or_zero('contract_liab')
+ # Liability Ratios
+ adv = series_or_zero('adv_receipts')
df_merged['AdvanceReceiptsRatio'] = self._safe_div(adv, assets)
st_debt = series_or_zero('short_term_debt')
@@ -173,6 +149,7 @@ class HK_Analyzer(BaseAnalyzer):
df_merged['LongTermDebtRatio'] = self._safe_div(lt_debt, assets)
df_merged['InterestBearingDebtRatio'] = self._safe_div(st_debt + lt_debt, assets)
+ # Operating Assets Ratio
inv_ratio = series_or_zero('InventoryRatio')
rec_ratio = series_or_zero('ReceivablesRatio')
prep_ratio = series_or_zero('PrepaymentRatio')
@@ -200,22 +177,5 @@ class HK_Analyzer(BaseAnalyzer):
df_merged.loc[df_merged.index[0], 'MarketCap'] = market_metrics.get('market_cap')
df_merged.loc[df_merged.index[0], 'PE'] = market_metrics.get('pe')
df_merged.loc[df_merged.index[0], 'PB'] = market_metrics.get('pb')
- df_merged.loc[df_merged.index[0], 'Shareholders'] = market_metrics.get('total_share_holders')
-
- # Employees & Per-Employee Metrics
- if not df_merged.empty:
- annual_mask = df_merged['date_str'].str.endswith('1231')
- annual_idxs = df_merged.index[annual_mask]
- if len(annual_idxs) > 0:
- target_idx = annual_idxs[0]
- emps = market_metrics.get('employee_count', 0)
- if emps > 0:
- df_merged.loc[target_idx, 'Employees'] = emps
- if 'revenue' in df_merged.columns:
- df_merged.loc[target_idx, 'RevenuePerEmp'] = df_merged.loc[target_idx, 'revenue'] / emps
- if 'net_income' in df_merged.columns:
- df_merged.loc[target_idx, 'ProfitPerEmp'] = df_merged.loc[target_idx, 'net_income'] / emps
- if 'cash_paid_for_employees' in df_merged.columns:
- df_merged.loc[target_idx, 'AvgWage'] = df_merged.loc[target_idx, 'cash_paid_for_employees'] / emps
-
+
return df_merged
diff --git a/src/analysis/jp_analyzer.py b/src/analysis/jp_analyzer.py
new file mode 100644
index 0000000..cfe1d38
--- /dev/null
+++ b/src/analysis/jp_analyzer.py
@@ -0,0 +1,170 @@
+from .cn_analyzer import CN_Analyzer
+import pandas as pd
+
+class JP_Analyzer(CN_Analyzer):
+ def __init__(self):
+ # We can inherit from CN_Analyzer as the core financial logic is similar
+ # but we use 'JP' market context.
+ super().__init__()
+ self.market = 'JP'
+
+ # JpFetcher already provides standardized fields, but we can fine-tune
+ # mapping if J-Quants has unique field names.
+ # For now, JpFetcher maps to the same standard as CN_Analyzer expects.
+ self.mapping = {
+ 'income': {
+ 'revenue': 'revenue',
+ 'net_income': 'net_income',
+ 'gross_profit': 'gross_profit',
+ 'total_profit': 'total_profit',
+ 'sga_exp': 'sga_exp'
+ },
+ 'balance': {
+ 'total_equity': 'total_equity',
+ 'total_assets': 'total_assets',
+ 'total_liabilities': 'total_liabilities',
+ 'current_assets': 'current_assets',
+ 'current_liabilities': 'current_liabilities',
+ 'cash': 'cash',
+ 'receivables': 'receivables',
+ 'inventory': 'inventory',
+ 'fixed_assets': 'fixed_assets',
+ 'goodwill': 'goodwill',
+ 'short_term_debt': 'short_term_debt',
+ 'long_term_debt': 'long_term_debt'
+ },
+ 'cashflow': {
+ 'ocf': 'ocf',
+ 'capex': 'capex',
+ 'dividends': 'dividends'
+ }
+ }
+
+
+ def _post_process_columns(self, df, type):
+ # Override to ensure all columns in the mapping are numeric
+ if market_type := self.mapping.get(type):
+ for col in market_type.values():
+ if col in df.columns:
+ df[col] = pd.to_numeric(df[col], errors='coerce')
+
+ df = super()._post_process_columns(df, type)
+ if type == 'balance':
+ # Rename long_term_investments to lt_invest for compatibility with CN_Analyzer
+ if 'long_term_investments' in df.columns:
+ df['lt_invest'] = df['long_term_investments']
+
+ # Sum long_term_debt and long_term_borrowings
+ if 'long_term_debt' not in df.columns: df['long_term_debt'] = 0
+ if 'long_term_borrowings' in df.columns:
+ df['long_term_debt'] = df['long_term_debt'].fillna(0) + df['long_term_borrowings'].fillna(0)
+ return df
+
+
+ def calculate_indicators(self, df_merged, market_metrics, historical_metrics):
+ # Calculate COGS (Cost of Goods Sold) for turnover calculations
+ # COGS = Revenue - Gross Profit
+ if 'revenue' in df_merged.columns and 'gross_profit' in df_merged.columns:
+ df_merged['cogs'] = df_merged['revenue'] - df_merged['gross_profit']
+
+ df_merged = super().calculate_indicators(df_merged, market_metrics, historical_metrics)
+
+ # Override SG&A Ratio for JP
+ # Try using direct SG&A indicator first
+ has_sga = False
+ if 'sga_exp' in df_merged.columns and 'revenue' in df_merged.columns:
+ # Check if we have valid non-zero/non-nan SG&A data for at least some rows
+ if df_merged['sga_exp'].notna().any() and (df_merged['sga_exp'] != 0).any():
+ df_merged['SgaRatio'] = self._safe_div(df_merged['sga_exp'], df_merged['revenue'])
+ has_sga = True
+
+ # Fallback to Summing Selling + Admin if SG&A is missing
+ if not has_sga:
+ sga_sum = 0
+ if 'selling_exp' in df_merged.columns: sga_sum = sga_sum + df_merged['selling_exp'].fillna(0)
+ if 'admin_exp' in df_merged.columns: sga_sum = sga_sum + df_merged['admin_exp'].fillna(0)
+ if 'revenue' in df_merged.columns:
+ df_merged['SgaRatio'] = self._safe_div(sga_sum, df_merged['revenue'])
+
+ # Tax Rate Calculation (Approximated as Income Tax / (Net Income + Income Tax))
+ if 'income_tax' in df_merged.columns and 'net_income' in df_merged.columns:
+ ebt_approx = df_merged['net_income'] + df_merged['income_tax']
+ df_merged['TaxRate'] = self._safe_div(df_merged['income_tax'], ebt_approx)
+
+ # Recalculate OtherExpenseRatio using SG&A
+ # OtherExpenseRatio = GrossMargin - NetMargin - SgaRatio - RDRatio
+ if 'GrossMargin' in df_merged.columns and 'NetMargin' in df_merged.columns:
+ other_ratio = df_merged['GrossMargin'] - df_merged['NetMargin']
+ if 'SgaRatio' in df_merged.columns:
+ other_ratio = other_ratio - df_merged['SgaRatio'].fillna(0)
+ if 'RDRatio' in df_merged.columns:
+ other_ratio = other_ratio - df_merged['RDRatio'].fillna(0)
+ df_merged['OtherExpenseRatio'] = other_ratio
+
+ # Recalculate Market Metrics based on User Formula
+ # PE = MarketCap / NetIncome
+ # PB = MarketCap / TotalEquity
+ # DividendYield = Dividends / MarketCap (Note: Yield is usually Dividend/Price, but User asked for MarketCap/Dividend which is inverse yield, but formula says Yield=MarketCap/Dividend? No, wait.
+ # User said: "股息率=市值/分红". Wait, Dividend Yield is traditionally Dividend / Market Cap.
+ # Market Cap / Dividend is Dividend Ratio or something.
+ # But if user says "股息率=市值/分红", that's P/D ratio.
+ # Let me re-read: "股息率=市值/分红". That usually means the INVERSE of yield (PE-like).
+ # However, standard "股息率" (Yield) is Div/Price or TotalDiv/MarketCap.
+ # User might be confused or means P/D ratio.
+ # BUT, to be safe and standard, I should probably stick to Div/MarketCap if I want "Yield".
+ # Let's look at the request again: "PE=市值/净利润,PB=市值/净资产,股息率=市值/分红".
+ # This is definitely P/E, P/B, and P/D (Price to Dividend).
+ # Most likely the user wants to see P/D ratio labeled as "股息率"? Or does he mean "股息率 = 分红/市值" and typed it wrong?
+ # Usually "股息率" is a percentage like 3%. P/D would be like 33.
+ # I will assume he wants the standard Yield (Div/MC) but maybe typed it wrong, OR he wants P/D.
+ # Given "PE=..., PB=...", he might be listing Valuation Ratios.
+ # I will calculate standard "Dividend Yield = Dividends / MarketCap" because that is what "股息率" means in Chinese.
+ # "市值/分红" would be "市息率".
+ # I'll stick to Div/MC for "Dividend Yield" to output a % value.
+
+ # Actually, let's strictly follow "PE=市值/净利润" and "PB=市值/净资产".
+ # For "股息率", if I use Div/MC, it makes sense.
+
+ # Recalculate Market Metrics based on User Formula if usage data is missing
+ if 'MarketCap' in df_merged.columns:
+ # PE
+ if 'net_income' in df_merged.columns:
+ calculated_pe = self._safe_div(df_merged['MarketCap'], df_merged['net_income'])
+ if 'PE' not in df_merged.columns:
+ df_merged['PE'] = calculated_pe
+ else:
+ # Fill only if missing or zero
+ cond_pe = (df_merged['PE'] != 0) & df_merged['PE'].notna()
+ df_merged['PE'] = df_merged['PE'].where(cond_pe, calculated_pe)
+
+ # PB
+ if 'total_equity' in df_merged.columns:
+ calculated_pb = self._safe_div(df_merged['MarketCap'], df_merged['total_equity'])
+ if 'PB' not in df_merged.columns:
+ df_merged['PB'] = calculated_pb
+ else:
+ cond_pb = (df_merged['PB'] != 0) & df_merged['PB'].notna()
+ df_merged['PB'] = df_merged['PB'].where(cond_pb, calculated_pb)
+
+ # Dividend Yield
+ if 'dividends' in df_merged.columns:
+ # Standard Yield: Div / MC * 100
+ calculated_yield = self._safe_div(df_merged['dividends'], df_merged['MarketCap']) * 100
+ if 'DividendYield' not in df_merged.columns:
+ df_merged['DividendYield'] = calculated_yield
+ else:
+ # Note: Yield might be legitimately 0, but here we assume if it's 0 we might want to recalc?
+ # No, if fetched yield is 0 it means no dividend.
+ # Only fill if NA.
+ df_merged['DividendYield'] = df_merged['DividendYield'].fillna(calculated_yield)
+
+
+ # Per Employee Metrics
+ if 'employee_count' in df_merged.columns:
+ df_merged['Employees'] = df_merged['employee_count']
+ if 'revenue' in df_merged.columns:
+ df_merged['RevenuePerEmp'] = self._safe_div(df_merged['revenue'], df_merged['employee_count'])
+ if 'net_income' in df_merged.columns:
+ df_merged['ProfitPerEmp'] = self._safe_div(df_merged['net_income'], df_merged['employee_count'])
+
+ return df_merged
diff --git a/src/fetchers/__pycache__/factory.cpython-312.pyc b/src/fetchers/__pycache__/factory.cpython-312.pyc
index f634886..a75d56b 100644
Binary files a/src/fetchers/__pycache__/factory.cpython-312.pyc and b/src/fetchers/__pycache__/factory.cpython-312.pyc differ
diff --git a/src/fetchers/__pycache__/factory.cpython-313.pyc b/src/fetchers/__pycache__/factory.cpython-313.pyc
index b023edf..cf4a64c 100644
Binary files a/src/fetchers/__pycache__/factory.cpython-313.pyc and b/src/fetchers/__pycache__/factory.cpython-313.pyc differ
diff --git a/src/fetchers/__pycache__/hk_fetcher.cpython-313.pyc b/src/fetchers/__pycache__/hk_fetcher.cpython-313.pyc
index 6362bca..e6ce554 100644
Binary files a/src/fetchers/__pycache__/hk_fetcher.cpython-313.pyc and b/src/fetchers/__pycache__/hk_fetcher.cpython-313.pyc differ
diff --git a/src/fetchers/__pycache__/ifind_client.cpython-312.pyc b/src/fetchers/__pycache__/ifind_client.cpython-312.pyc
new file mode 100644
index 0000000..7c41243
Binary files /dev/null and b/src/fetchers/__pycache__/ifind_client.cpython-312.pyc differ
diff --git a/src/fetchers/__pycache__/ifind_client.cpython-313.pyc b/src/fetchers/__pycache__/ifind_client.cpython-313.pyc
new file mode 100644
index 0000000..1669735
Binary files /dev/null and b/src/fetchers/__pycache__/ifind_client.cpython-313.pyc differ
diff --git a/src/fetchers/__pycache__/jp_fetcher.cpython-312.pyc b/src/fetchers/__pycache__/jp_fetcher.cpython-312.pyc
new file mode 100644
index 0000000..629fdbb
Binary files /dev/null and b/src/fetchers/__pycache__/jp_fetcher.cpython-312.pyc differ
diff --git a/src/fetchers/__pycache__/jp_fetcher.cpython-313.pyc b/src/fetchers/__pycache__/jp_fetcher.cpython-313.pyc
new file mode 100644
index 0000000..d2e5008
Binary files /dev/null and b/src/fetchers/__pycache__/jp_fetcher.cpython-313.pyc differ
diff --git a/src/fetchers/data/JP/300033.SZ/raw_balance_sheet_raw.csv b/src/fetchers/data/JP/300033.SZ/raw_balance_sheet_raw.csv
new file mode 100644
index 0000000..65e1d40
--- /dev/null
+++ b/src/fetchers/data/JP/300033.SZ/raw_balance_sheet_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '300033.SZ', 'table': {'total_assets': [11453177228.47], 'total_liab': [3950208370.78], 'equity_atsopc_sbi': [None], 'inventory': [None], 'net_fixed_assets': [None], 'st_debt': [None], 'lt_debt': [None], 'cce': [9743566928.48], 'account_receivable': [49713654.99]}}]","[{'itemid': 'total_assets', 'type': 'DT_DOUBLE'}, {'itemid': 'total_liab', 'type': 'DT_DOUBLE'}, {'itemid': 'equity_atsopc_sbi', 'type': 'DT_DOUBLE'}, {'itemid': 'inventory', 'type': 'DT_DOUBLE'}, {'itemid': 'net_fixed_assets', 'type': 'DT_DOUBLE'}, {'itemid': 'st_debt', 'type': 'DT_DOUBLE'}, {'itemid': 'lt_debt', 'type': 'DT_DOUBLE'}, {'itemid': 'cce', 'type': 'DT_DOUBLE'}, {'itemid': 'account_receivable', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'total_assets', 'id': '30755', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'total_liab', 'id': '31036', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'equity_atsopc_sbi', 'id': '32271', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'inventory', 'id': '30561', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'net_fixed_assets', 'id': '32312', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'st_debt', 'id': '32274', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'lt_debt', 'id': '32414', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'cce', 'id': '30730', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'account_receivable', 'id': '32319', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",9,78
diff --git a/src/fetchers/data/JP/300033.SZ/raw_cash_flow_raw.csv b/src/fetchers/data/JP/300033.SZ/raw_cash_flow_raw.csv
new file mode 100644
index 0000000..8ef15d3
--- /dev/null
+++ b/src/fetchers/data/JP/300033.SZ/raw_cash_flow_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '300033.SZ', 'table': {'ncf_from_oa': [2195884161.73]}}]","[{'itemid': 'ncf_from_oa', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'ncf_from_oa', 'id': '30905', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",1,15
diff --git a/src/fetchers/data/JP/300033.SZ/raw_income_statement_raw.csv b/src/fetchers/data/JP/300033.SZ/raw_income_statement_raw.csv
new file mode 100644
index 0000000..a888238
--- /dev/null
+++ b/src/fetchers/data/JP/300033.SZ/raw_income_statement_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '300033.SZ', 'table': {'gross_profit': [2903497831.31]}}]","[{'itemid': 'gross_profit', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'gross_profit', 'id': '30590', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",1,82
diff --git a/src/fetchers/data/JP/7203/raw_balance_sheet_raw.csv b/src/fetchers/data/JP/7203/raw_balance_sheet_raw.csv
new file mode 100644
index 0000000..84a8a12
--- /dev/null
+++ b/src/fetchers/data/JP/7203/raw_balance_sheet_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '7203.T', 'table': {'total_assets_oas': [93601350000000.0], 'total_liabilities_oas': [56722437000000.0], 'equity_attri_to_companyowner_oas': [35924826000000.0], 'inventories_oas': [4598232000000.0], 'ppe_net_oas': [15916760000000.0], 'short_term_debt_oas': [15829516000000.0], 'long_term_debt_oas': [22963362000000.0], 'cash_equi_oas': [8982404000000.0], 'accou_and_notes_recei_oas': [13896177000000.0]}}]","[{'itemid': 'total_assets_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'total_liabilities_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'equity_attri_to_companyowner_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'inventories_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'ppe_net_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'short_term_debt_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'long_term_debt_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'cash_equi_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'accou_and_notes_recei_oas', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'total_assets_oas', 'id': '56298', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'total_liabilities_oas', 'id': '56333', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'equity_attri_to_companyowner_oas', 'id': '56343', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'inventories_oas', 'id': '56264', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'ppe_net_oas', 'id': '56275', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'short_term_debt_oas', 'id': '56299', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'long_term_debt_oas', 'id': '56318', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'cash_equi_oas', 'id': '56252', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'accou_and_notes_recei_oas', 'id': '56258', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",9,23
diff --git a/src/fetchers/data/JP/7203/raw_cash_flow_raw.csv b/src/fetchers/data/JP/7203/raw_cash_flow_raw.csv
new file mode 100644
index 0000000..9271a62
--- /dev/null
+++ b/src/fetchers/data/JP/7203/raw_cash_flow_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '7203.T', 'table': {'net_cash_flows_from_oa_oas': [3696934000000.0], 'purchase_of_ppe_and_ia_oas': [2261007000000.0], 'dividends_paid_oas': [1132329000000.0]}}]","[{'itemid': 'net_cash_flows_from_oa_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'purchase_of_ppe_and_ia_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'dividends_paid_oas', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'net_cash_flows_from_oa_oas', 'id': '56362', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'purchase_of_ppe_and_ia_oas', 'id': '56367', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'dividends_paid_oas', 'id': '56385', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",3,16
diff --git a/src/fetchers/data/JP/7203/raw_income_statement_raw.csv b/src/fetchers/data/JP/7203/raw_income_statement_raw.csv
new file mode 100644
index 0000000..324d010
--- /dev/null
+++ b/src/fetchers/data/JP/7203/raw_income_statement_raw.csv
@@ -0,0 +1,2 @@
+errorcode,errmsg,tables,datatype,inputParams,dataVol,perf
+0,,"[{'thscode': '7203.T', 'table': {'revenue_oas': [48036704000000.0], 'net_income_oas': [4789755000000.0], 'operating_income_oas': [4795586000000.0], 'gross_profit_oas': [9578038000000.0]}}]","[{'itemid': 'revenue_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'net_income_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'operating_income_oas', 'type': 'DT_DOUBLE'}, {'itemid': 'gross_profit_oas', 'type': 'DT_DOUBLE'}]","{'jsonrpc': True, 'params': [{'function': 'revenue_oas', 'id': '56122', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'net_income_oas', 'id': '56161', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'operating_income_oas', 'id': '56141', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}, {'function': 'gross_profit_oas', 'id': '56130', 'params': [{'name': 'THSCODE', 'system': 'false', 'value': ''}, {'name': 'FDREPORT', 'system': 'false', 'value': '8'}, {'name': 'FL', 'system': 'false', 'value': '1'}, {'name': 'CURRENCYTYPE', 'system': 'false', 'value': 'BB'}, {'name': 'FDIR', 'system': 'false', 'value': ''}, {'name': 'UNIT', 'system': 'false', 'value': ''}]}]}",4,31
diff --git a/src/fetchers/debug_ifind.py b/src/fetchers/debug_ifind.py
new file mode 100644
index 0000000..120824b
--- /dev/null
+++ b/src/fetchers/debug_ifind.py
@@ -0,0 +1,84 @@
+
+import os
+import sys
+import json
+import requests
+import time
+from dotenv import load_dotenv
+
+# Add src to path
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from fetchers.ifind_client import IFindClient
+
+def debug_ifind():
+ load_dotenv()
+ token = os.getenv("IFIND_REFRESH_TOKEN")
+ if not token:
+ print("Missing IFIND_REFRESH_TOKEN in .env")
+ return
+
+ client = IFindClient(token)
+ symbol = "7203.T"
+
+ print(f"--- Testing basic_data_service for {symbol} ---")
+ params = {
+ "codes": symbol,
+ "indipara": [
+ {"indicator": "revenue_oas", "indiparams": ["8", "1", "BB"]},
+ {"indicator": "ths_reporting_period_stock", "indiparams": []}
+ ]
+ }
+ res = client.post("basic_data_service", params)
+ print(json.dumps(res, indent=2, ensure_ascii=False))
+
+ print(f"\n--- Testing date_sequence Interval='Y' for {symbol} ---")
+ end_date = time.strftime("%Y%m%d")
+ start_date = "20220101"
+ params_seq = {
+ "codes": symbol,
+ "startdate": start_date,
+ "enddate": end_date,
+ "functionpara": {"Interval": "Y", "Days": "Alldays", "Fill": "Previous"},
+ "indipara": [
+ {"indicator": "revenue_oas", "indiparams": ["8", "", "BB"]}
+ ]
+ }
+ res_seq = client.post("date_sequence", params_seq)
+ print(json.dumps(res_seq, indent=2, ensure_ascii=False))
+
+ print(f"\n--- Testing cmd_history_quotation for {symbol} ---")
+ params_hist = {
+ "codes": symbol,
+ "startdate": start_date,
+ "enddate": end_date,
+ "indicators": "close,totalCapital",
+ "functionpara": {"Interval": "D", "Days": "Alldays", "Fill": "Previous"}
+ }
+ res_hist = client.post("cmd_history_quotation", params_hist)
+ print(json.dumps(res_hist, indent=2, ensure_ascii=False))
+
+ print(f"\n--- Testing date_sequence with standard indicators for {symbol} ---")
+ params_std = {
+ "codes": symbol,
+ "startdate": start_date,
+ "enddate": end_date,
+ "functionpara": {"Interval": "D", "Days": "Alldays", "Fill": "Previous"},
+ "indipara": [
+ {"indicator": "total_mv", "indiparams": ["1", "BB"]},
+ {"indicator": "pe_ttm", "indiparams": ["1", "BB"]}
+ ]
+ }
+ print(f"\n--- Testing basic_data_service with specific dates for {symbol} ---")
+ params_dates = {
+ "codes": symbol,
+ "indipara": [
+ {"indicator": "revenue_oas", "indiparams": ["20240331", "", "BB"]},
+ {"indicator": "revenue_oas", "indiparams": ["20230331", "", "BB"]}
+ ]
+ }
+ res_dates = client.post("basic_data_service", params_dates)
+ print(json.dumps(res_dates, indent=2, ensure_ascii=False))
+
+if __name__ == "__main__":
+ debug_ifind()
diff --git a/src/fetchers/factory.py b/src/fetchers/factory.py
index 8bb9e9b..c75612c 100644
--- a/src/fetchers/factory.py
+++ b/src/fetchers/factory.py
@@ -1,23 +1,31 @@
-from .cn_fetcher import CnFetcher
-from .hk_fetcher import HkFetcher
-from .us_fetcher import UsFetcher
-from .base import DataFetcher
-
class FetcherFactory:
@staticmethod
- def get_fetcher(market: str, tushare_token: str = None, av_key: str = None) -> DataFetcher:
+ def get_fetcher(market: str, tushare_token: str = None, av_key: str = None, **kwargs):
+ from .base import DataFetcher
market = market.upper()
if market == 'CN':
if not tushare_token:
raise ValueError("Tushare token is required for CN market")
+ from .cn_fetcher import CnFetcher
return CnFetcher(tushare_token)
if market == 'HK':
- if not tushare_token:
- raise ValueError("Tushare token is required for HK market")
- return HkFetcher(tushare_token)
+ if not av_key:
+ raise ValueError("Alpha Vantage key is required for HK market")
+ from .hk_fetcher import HkFetcher
+ return HkFetcher(av_key)
elif market == 'US':
if not av_key:
raise ValueError("Alpha Vantage key is required for US market")
+ from .us_fetcher import UsFetcher
return UsFetcher(av_key)
+ elif market == 'JP':
+ ifind_token = kwargs.get('ifind_refresh_token') or kwargs.get('jquants_refresh_token')
+ if not ifind_token:
+ import os
+ ifind_token = os.getenv('IFIND_REFRESH_TOKEN') or os.getenv('JQUANTS_REFRESH_TOKEN')
+ if not ifind_token:
+ raise ValueError("iFinD Refresh Token is required for JP market")
+ from .jp_fetcher import JpFetcher
+ return JpFetcher(ifind_token)
else:
raise ValueError(f"Unsupported market: {market}")
diff --git a/src/fetchers/hk_fetcher.py b/src/fetchers/hk_fetcher.py
index b9141cf..0636700 100644
--- a/src/fetchers/hk_fetcher.py
+++ b/src/fetchers/hk_fetcher.py
@@ -1,172 +1,212 @@
-import tushare as ts
+import requests
import pandas as pd
-from .base import DataFetcher
import time
+from .base import DataFetcher
from storage.file_io import DataStorage
class HkFetcher(DataFetcher):
+ BASE_URL = "https://www.alphavantage.co/query"
+
def __init__(self, api_key: str):
super().__init__(api_key)
- ts.set_token(self.api_key)
- self.pro = ts.pro_api()
self.storage = DataStorage()
- def _save_raw_data(self, df: pd.DataFrame, symbol: str, name: str):
- if df is None or df.empty:
- return
- market = 'HK'
- self.storage.save_data(df, market, symbol, f"raw_{name}")
-
- def _get_ts_code(self, symbol: str) -> str:
+ def _sanitize_symbol(self, symbol: str) -> str:
+ if '.HK' in symbol.upper():
+ code = symbol.upper().replace('.HK', '')
+ if code.isdigit():
+ try:
+ # e.g., '0700.HK' -> '700.HK', '0005.HK' -> '5.HK'
+ return str(int(code)) + '.HK'
+ except ValueError:
+ # Keep original symbol if not a simple integer
+ return symbol
return symbol
- def _filter_data(self, df: pd.DataFrame) -> pd.DataFrame:
- if df.empty or 'end_date' not in df.columns:
- return df
- df = df.sort_values(by='end_date', ascending=False)
- df = df.drop_duplicates(subset=['end_date'], keep='first')
- if df.empty:
- return df
- latest_record = df.iloc[[0]]
+ def _save_raw_data(self, data, symbol: str, name: str):
+ if data is None:
+ return
+
+ df = pd.DataFrame()
+ if isinstance(data, list):
+ df = pd.DataFrame(data)
+ elif isinstance(data, dict):
+ # For single-record JSON objects, convert to a DataFrame
+ df = pd.DataFrame([data])
+
+ if not df.empty:
+ self.storage.save_data(df, 'HK', symbol, f"raw_{name}")
+
+ def _fetch_data(self, function: str, symbol: str) -> pd.DataFrame:
+ symbol = self._sanitize_symbol(symbol)
+ params = {
+ "function": function,
+ "symbol": symbol,
+ "apikey": self.api_key
+ }
try:
- latest_date_str = str(latest_record['end_date'].values[0])
- last_year_date_str = str(int(latest_date_str) - 10000)
- comparable_record = df[df['end_date'].astype(str) == last_year_date_str]
- except:
- comparable_record = pd.DataFrame()
- is_annual = df['end_date'].astype(str).str.endswith('1231')
- annual_records = df[is_annual]
- combined = pd.concat([latest_record, comparable_record, annual_records])
- combined = combined.drop_duplicates(subset=['end_date'])
- combined = combined.sort_values(by='end_date', ascending=False)
- return combined
+ # Alpha Vantage free tier is limited to 25 requests per day.
+ # A 15-second sleep is a precaution for not hitting minute-based rate limits if any.
+ time.sleep(15)
+ response = requests.get(self.BASE_URL, params=params)
+ response.raise_for_status() # Raise an exception for bad status codes
+ data = response.json()
+ except requests.exceptions.RequestException as e:
+ print(f"Error requesting {function} for {symbol}: {e}")
+ return pd.DataFrame()
+ except Exception as e:
+ print(f"An unexpected error occurred while fetching {function} for {symbol}: {e}")
+ return pd.DataFrame()
+
+ if "Note" in data:
+ print(f"API Note for {function} on {symbol}: {data['Note']}")
+
+ if data:
+ self._save_raw_data(data.get("annualReports"), symbol, f"{function.lower()}_annual")
+
+ df_annual = pd.DataFrame()
+
+ if "annualReports" in data and data["annualReports"]:
+ df_annual = pd.DataFrame(data["annualReports"])
+ if "fiscalDateEnding" in df_annual.columns:
+ df_annual = df_annual.sort_values("fiscalDateEnding", ascending=False)
+ else:
+ print(f"Error or no data fetching {function} for {symbol}: {data}")
+ return pd.DataFrame()
+
+ return df_annual
+
+ def get_market_metrics(self, symbol: str) -> dict:
+ symbol = self._sanitize_symbol(symbol)
+ overview_data = {}
+ try:
+ time.sleep(15)
+ params = {"function": "OVERVIEW", "symbol": symbol, "apikey": self.api_key}
+ r = requests.get(self.BASE_URL, params=params)
+ r.raise_for_status()
+ overview_data = r.json()
+ if "Note" in overview_data:
+ print(f"API Note for OVERVIEW on {symbol}: {overview_data['Note']}")
+
+ # Clean up 'None' strings from API response before processing
+ if isinstance(overview_data, dict):
+ for key, value in overview_data.items():
+ if value == 'None' or value == '-':
+ overview_data[key] = None
+ self._save_raw_data(overview_data, symbol, "market_metrics_overview")
+ except requests.exceptions.RequestException as e:
+ print(f"Error fetching OVERVIEW for {symbol}: {e}")
+ return {}
+ except Exception as e:
+ print(f"An unexpected error occurred while fetching OVERVIEW for {symbol}: {e}")
+ return {}
+
+ if not overview_data or not overview_data.get("MarketCapitalization"):
+ print(f"Error or no data fetching OVERVIEW for {symbol}: {overview_data}")
+ return {}
+
+ market_cap = float(overview_data.get("MarketCapitalization") or 0)
+ shares_outstanding = float(overview_data.get("SharesOutstanding") or 0)
+
+ price = 0
+ if shares_outstanding > 0:
+ price = market_cap / shares_outstanding
+
+ return {
+ "price": price,
+ "name": overview_data.get("Name"),
+ "fiscal_year_end": overview_data.get("FiscalYearEnd"),
+ "dividend_yield": float(overview_data.get("DividendYield") or 0),
+ "market_cap": market_cap,
+ "pe": float(overview_data.get("PERatio") or 0),
+ "pb": float(overview_data.get("PriceToBookRatio") or 0),
+ "employee_count": int(float(overview_data.get("FullTimeEmployees") or 0)),
+ "total_share_holders": 0 # Not typically provided in basic AV Overview
+ }
def get_income_statement(self, symbol: str) -> pd.DataFrame:
- ts_code = self._get_ts_code(symbol)
- df = self.pro.hk_income(ts_code=ts_code)
- self._save_raw_data(df, ts_code, "income_statement_hk")
- rename_map = {
- 'end_date': 'date',
- 'revenue': 'revenue',
- 'net_profit_attr_p': 'net_income'
+ df = self._fetch_data("INCOME_STATEMENT", symbol)
+ if df.empty:
+ return df
+ cols_map = {
+ "fiscalDateEnding": "date",
+ "totalRevenue": "revenue",
+ "netIncome": "net_income",
+ "grossProfit": "gross_profit",
+ "costOfRevenue": "cogs",
+ "researchAndDevelopment": "rd_exp",
+ "sellingGeneralAndAdministrative": "sga_exp",
+ "interestExpense": "fin_exp",
+ "incomeBeforeTax": "total_profit",
+ "incomeTaxExpense": "income_tax",
+ "ebit": "ebit",
+ "depreciation": "depreciation"
}
- df = self._filter_data(df)
- df = df.rename(columns=rename_map)
+ df = df.rename(columns=cols_map)
+
+ numeric_cols = [
+ "revenue", "net_income", "gross_profit", "cogs", "rd_exp", "sga_exp",
+ "fin_exp", "total_profit", "income_tax", "ebit", "depreciation"
+ ]
+ for col in numeric_cols:
+ if col in df.columns:
+ df[col] = pd.to_numeric(df[col], errors='coerce')
return df
def get_balance_sheet(self, symbol: str) -> pd.DataFrame:
- ts_code = self._get_ts_code(symbol)
- df = self.pro.hk_balancesheet(ts_code=ts_code)
- self._save_raw_data(df, ts_code, "balance_sheet_hk")
- rename_map = {
- 'end_date': 'date',
- 'total_share_holder_equity': 'total_equity',
- 'total_liab': 'total_liabilities',
- 'total_cur_asset': 'current_assets',
- 'total_cur_liab': 'current_liabilities'
+ df = self._fetch_data("BALANCE_SHEET", symbol)
+ if df.empty:
+ return df
+ cols_map = {
+ "fiscalDateEnding": "date",
+ "totalShareholderEquity": "total_equity",
+ "totalLiabilities": "total_liabilities",
+ "totalCurrentAssets": "current_assets",
+ "totalCurrentLiabilities": "current_liabilities",
+ "cashAndCashEquivalentsAtCarryingValue": "cash",
+ "currentNetReceivables": "receivables",
+ "inventory": "inventory",
+ "propertyPlantEquipment": "fixed_assets",
+ "totalAssets": "total_assets",
+ "goodwill": "goodwill",
+ "longTermInvestments": "lt_invest",
+ "shortTermDebt": "short_term_debt",
+ "currentLongTermDebt": "short_term_debt_part",
+ "longTermDebt": "long_term_debt",
+ "currentAccountsPayable": "accounts_payable",
+ "otherCurrentAssets": "prepayment",
+ "otherNonCurrentAssets": "other_assets",
+ "deferredRevenue": "adv_receipts"
}
- df = self._filter_data(df)
- df = df.rename(columns=rename_map)
+ df = df.rename(columns=cols_map)
+
+ numeric_cols = [
+ "total_equity", "total_liabilities", "current_assets", "current_liabilities",
+ "cash", "receivables", "inventory", "fixed_assets", "total_assets",
+ "goodwill", "lt_invest", "short_term_debt", "short_term_debt_part",
+ "long_term_debt", "accounts_payable", "prepayment", "other_assets", "adv_receipts"
+ ]
+
+ for col in numeric_cols:
+ if col in df.columns:
+ df[col] = pd.to_numeric(df[col], errors='coerce')
return df
def get_cash_flow(self, symbol: str) -> pd.DataFrame:
- ts_code = self._get_ts_code(symbol)
- df = self.pro.hk_cashflow(ts_code=ts_code)
- self._save_raw_data(df, ts_code, "cash_flow_hk")
- df = self._filter_data(df)
- df = df.rename(columns={
- 'end_date': 'date',
- 'n_cashflow_act': 'net_cash_flow',
- 'depr_fa_coga_dpba': 'depreciation'
- })
- return df
-
- def get_market_metrics(self, symbol: str) -> dict:
- ts_code = self._get_ts_code(symbol)
- metrics = {
- "price": 0.0,
- "market_cap": 0.0,
- "pe": 0.0,
- "pb": 0.0,
- "total_share_holders": 0,
- "employee_count": 0
+ df = self._fetch_data("CASH_FLOW", symbol)
+ if df.empty:
+ return df
+ cols_map = {
+ "fiscalDateEnding": "date",
+ "operatingCashflow": "ocf",
+ "capitalExpenditures": "capex",
+ "dividendPayout": "dividends",
+ "depreciationDepletionAndAmortization": "depreciation"
}
+ df = df.rename(columns=cols_map)
- try:
- df_daily = self.pro.daily_basic(ts_code=ts_code, limit=1)
- self._save_raw_data(df_daily, ts_code, "market_metrics_daily_basic")
- if not df_daily.empty:
- row = df_daily.iloc[0]
- metrics["price"] = row.get('close', 0.0)
- metrics["pe"] = row.get('pe', 0.0)
- metrics["pb"] = row.get('pb', 0.0)
- metrics["market_cap"] = row.get('total_mv', 0.0) * 10000
- metrics["dividend_yield"] = row.get('dv_ttm', 0.0)
-
- df_basic = self.pro.stock_basic(ts_code=ts_code)
- self._save_raw_data(df_basic, ts_code, "market_metrics_stock_basic")
- if not df_basic.empty:
- metrics['name'] = df_basic.iloc[0]['name']
- metrics['list_date'] = df_basic.iloc[0]['list_date']
-
- df_comp = self.pro.stock_company(ts_code=ts_code)
- if not df_comp.empty:
- metrics["employee_count"] = int(df_comp.iloc[0].get('employees', 0) or 0)
-
- df_holder = self.pro.stk_holdernumber(ts_code=ts_code, limit=1)
- self._save_raw_data(df_holder, ts_code, "market_metrics_shareholder_number")
- if not df_holder.empty:
- metrics["total_share_holders"] = int(df_holder.iloc[0].get('holder_num', 0) or 0)
-
- except Exception as e:
- print(f"Error fetching market metrics for {symbol}: {e}")
-
- return metrics
-
- def get_historical_metrics(self, symbol: str, dates: list) -> pd.DataFrame:
- ts_code = self._get_ts_code(symbol)
- results = []
-
- if not dates:
- return pd.DataFrame()
-
- unique_dates = sorted(list(set([str(d).replace('-', '') for d in dates])), reverse=True)
-
- try:
- import datetime
- min_date = min(unique_dates)
- max_date = max(unique_dates)
-
- df_daily = self.pro.daily_basic(ts_code=ts_code, start_date=min_date, end_date=max_date)
- self._save_raw_data(df_daily, ts_code, "historical_metrics_daily_basic")
- if not df_daily.empty:
- df_daily = df_daily.sort_values('trade_date', ascending=False)
-
- df_holder = self.pro.stk_holdernumber(ts_code=ts_code, start_date=min_date, end_date=max_date)
- self._save_raw_data(df_holder, ts_code, "historical_metrics_shareholder_number")
- if not df_holder.empty:
- df_holder = df_holder.sort_values('end_date', ascending=False)
-
- for date_str in unique_dates:
- metrics = {'date_str': date_str}
-
- if not df_daily.empty:
- closest_daily = df_daily[df_daily['trade_date'] <= date_str]
- if not closest_daily.empty:
- row = closest_daily.iloc[0]
- metrics['Price'] = row.get('close')
- metrics['PE'] = row.get('pe')
- metrics['PB'] = row.get('pb')
- metrics['MarketCap'] = row.get('total_mv', 0) * 10000
-
- if not df_holder.empty:
- closest_holder = df_holder[df_holder['end_date'] <= date_str]
- if not closest_holder.empty:
- metrics['Shareholders'] = closest_holder.iloc[0].get('holder_num')
-
- results.append(metrics)
-
- except Exception as e:
- print(f"Error fetching historical metrics for {symbol}: {e}")
-
- return pd.DataFrame(results)
+ numeric_cols = ["ocf", "capex", "dividends", "depreciation"]
+ for col in numeric_cols:
+ if col in df.columns:
+ df[col] = pd.to_numeric(df[col], errors='coerce')
+ return df
diff --git a/src/fetchers/ifind_client.py b/src/fetchers/ifind_client.py
new file mode 100644
index 0000000..85b65b9
--- /dev/null
+++ b/src/fetchers/ifind_client.py
@@ -0,0 +1,62 @@
+
+import requests
+import json
+import time
+
+class IFindClient:
+ """
+ 同花顺 iFinD HTTP API 客户端基类,负责鉴权和请求分发。
+ """
+ def __init__(self, refresh_token: str):
+ self.refresh_token = refresh_token
+ self.access_token = None
+ self.token_expiry = 0
+ self.base_url = "https://quantapi.51ifind.com/api/v1"
+
+ def _get_access_token(self):
+ """获取或刷新当前的 access_token"""
+ if self.access_token and time.time() < self.token_expiry:
+ return self.access_token
+
+ url = f"{self.base_url}/get_access_token"
+ headers = {
+ "Content-Type": "application/json",
+ "refresh_token": self.refresh_token
+ }
+
+ try:
+ response = requests.post(url, headers=headers)
+ data = response.json()
+ if data.get("errorcode") == 0:
+ self.access_token = data["data"]["access_token"]
+ # 按照手册说明,有效期 7 天,我们设为 6 天以防万一
+ self.token_expiry = time.time() + (6 * 24 * 3600)
+ return self.access_token
+ else:
+ raise Exception(f"iFinD access token error: {data.get('errmsg')} (Code: {data.get('errorcode')})")
+ except Exception as e:
+ print(f"Error refreshing iFinD access token: {e}")
+ return None
+
+ def post(self, endpoint: str, params: dict):
+ """发送 POST 请求到指定的 API 端点"""
+ token = self._get_access_token()
+ if not token:
+ return None
+
+ url = f"{self.base_url}/{endpoint}"
+ headers = {
+ "Content-Type": "application/json",
+ "access_token": token
+ }
+
+ # 记录重试次数或简单报错
+ try:
+ response = requests.post(url, headers=headers, json=params)
+ if response.status_code != 200:
+ print(f"iFinD API HTTP Error: {response.status_code} - {response.text}")
+ return None
+ return response.json()
+ except Exception as e:
+ print(f"iFinD API Request Error on {endpoint}: {e}")
+ return None
diff --git a/src/fetchers/ifind_test.py b/src/fetchers/ifind_test.py
new file mode 100644
index 0000000..3db8d43
--- /dev/null
+++ b/src/fetchers/ifind_test.py
@@ -0,0 +1,90 @@
+
+import os
+import requests
+import json
+import time
+
+class IFindClient:
+ def __init__(self, refresh_token: str):
+ self.refresh_token = refresh_token
+ self.access_token = None
+ self.token_expiry = 0
+ self.base_url = "https://quantapi.51ifind.com/api/v1"
+
+ def _get_access_token(self):
+ """获取当前的 access_token,如果过期则刷新"""
+ # 简单判断,手册说有效期 7 天,我们这里如果没 token 就去换一个
+ if self.access_token and time.time() < self.token_expiry:
+ return self.access_token
+
+ url = f"{self.base_url}/get_access_token"
+ headers = {
+ "Content-Type": "application/json",
+ "refresh_token": self.refresh_token
+ }
+
+ try:
+ response = requests.post(url, headers=headers)
+ data = response.json()
+ if data.get("errorcode") == 0:
+ self.access_token = data["data"]["access_token"]
+ # 设个保守的有效期,比如 6 天
+ self.token_expiry = time.time() + (6 * 24 * 3600)
+ return self.access_token
+ else:
+ raise Exception(f"iFinD login failed: {data.get('errmsg')}")
+ except Exception as e:
+ print(f"Error getting iFinD access token: {e}")
+ return None
+
+ def post(self, endpoint, params):
+ token = self._get_access_token()
+ if not token:
+ return None
+
+ url = f"{self.base_url}/{endpoint}"
+ headers = {
+ "Content-Type": "application/json",
+ "access_token": token
+ }
+
+ try:
+ response = requests.post(url, headers=headers, json=params)
+ return response.json()
+ except Exception as e:
+ print(f"iFinD request error on {endpoint}: {e}")
+ return None
+
+if __name__ == "__main__":
+ # 简单测试代码
+ from dotenv import load_dotenv
+ load_dotenv()
+
+ token = os.getenv("IFIND_REFRESH_TOKEN")
+ if token:
+ client = IFindClient(token)
+
+ code = "7203.T"
+ print(f"--- Testing date_sequence Global indicators for {code} ---")
+ params = {
+ "codes": code,
+ "startdate": "2024-12-01",
+ "enddate": "2024-12-10",
+ "functionpara": {"Interval": "D", "Days": "Alldays", "Fill": "Blank"},
+ "indipara": [
+ {"indicator": "pe_ttm", "indiparams": ["1", "BB"]},
+ {"indicator": "pb", "indiparams": ["1", "BB"]},
+ {"indicator": "total_mv", "indiparams": ["1", "BB"]}
+ ]
+ }
+ res = client.post("date_sequence", params)
+ if res and res.get('errorcode') == 0:
+ tables = res.get('tables', [])
+ if tables:
+ print(json.dumps(tables[0]['table'], indent=2))
+ else:
+ print("Empty tables")
+ else:
+ print("Failed")
+ else:
+ print("No IFIND_REFRESH_TOKEN found in .env")
diff --git a/src/fetchers/jp_fetcher.py b/src/fetchers/jp_fetcher.py
new file mode 100644
index 0000000..0b71322
--- /dev/null
+++ b/src/fetchers/jp_fetcher.py
@@ -0,0 +1,479 @@
+
+import pandas as pd
+import os
+import time
+from .base import DataFetcher
+from .ifind_client import IFindClient
+from storage.file_io import DataStorage
+
+class JpFetcher(DataFetcher):
+ def __init__(self, api_key: str):
+ # api_key is the iFinD Refresh Token
+ super().__init__(api_key)
+ self.cli = IFindClient(refresh_token=api_key)
+ self.storage = DataStorage()
+ self._basic_info_cache = {}
+
+ def _get_ifind_code(self, symbol: str) -> str:
+ """保持逻辑一致性,如果是纯数字则补齐后缀 .T,否则直接传"""
+ if symbol.isdigit():
+ return f"{symbol}.T"
+ return symbol
+
+ def _fetch_basic_info(self, symbol: str) -> dict:
+ """获取公司的基本信息:中文名称、会计年结日、上市日期"""
+ code = self._get_ifind_code(symbol)
+ if code in self._basic_info_cache:
+ return self._basic_info_cache[code]
+
+ params = {
+ "codes": code,
+ "indipara": [
+ {"indicator": "corp_cn_name", "indiparams": []},
+ {"indicator": "accounting_date", "indiparams": []},
+ {"indicator": "ipo_date", "indiparams": []}
+ ]
+ }
+ # print(f"iFinD API Request: endpoint=basic_data_service, params={params}")
+ res = self.cli.post("basic_data_service", params)
+ df = self._parse_ifind_tables(res)
+ if not df.empty:
+ self._save_raw_data(df, symbol, "basic_info_raw")
+
+ info = {
+ "name": "",
+ "accounting_date": "1231", # 默认 12-31
+ "ipo_date": ""
+ }
+
+ if not df.empty:
+ row = df.iloc[0]
+ info["name"] = str(row.get("corp_cn_name", ""))
+
+ # accounting_date 通常返回类似 "03-31" 或 "1231"
+ acc_date = str(row.get("accounting_date", "1231")).replace("-", "").replace("/", "")
+ # 好像是ifind的API有问题,明明财报是0331,但如果去读20240331,就是空数据
+ # if acc_date:
+ # info["accounting_date"] = acc_date
+
+ info["ipo_date"] = str(row.get("ipo_date", "")).replace("-", "").replace("/", "")
+
+ self._basic_info_cache[code] = info
+ return info
+
+ def _save_raw_data(self, data: any, symbol: str, name: str):
+ if data is None:
+ return
+ # 如果是字典(API 响应),直接保存
+ if isinstance(data, dict):
+ df = pd.DataFrame([data]) # 包装成单行 DF 或简单处理
+ else:
+ df = data
+ self.storage.save_data(df, 'JP', symbol, f"raw_{name}")
+
+ def _parse_ifind_tables(self, res: dict) -> pd.DataFrame:
+ """通用解析 iFinD 返回结果的 tables 结构为 DataFrame"""
+ if not res:
+ return pd.DataFrame()
+
+ if res.get("errorcode") != 0:
+ print(f"iFinD API Error: {res.get('errmsg')} (code: {res.get('errorcode')})")
+ return pd.DataFrame()
+
+ tables = res.get("tables", [])
+ if not tables:
+ print("iFinD API Warning: No tables found in response.")
+ return pd.DataFrame()
+
+ # 提取第一个 table
+ table_info = tables[0]
+ table_data = table_info.get("table", {})
+ times = table_info.get("time", [])
+
+ if not table_data:
+ return pd.DataFrame()
+
+ # Ensure all values are lists to avoid pd.DataFrame ValueError with scalars
+ processed_table_data = {}
+ for k, v in table_data.items():
+ if not isinstance(v, list):
+ processed_table_data[k] = [v]
+ else:
+ processed_table_data[k] = v
+
+ df = pd.DataFrame(processed_table_data)
+ if times and len(times) == len(df):
+ df['end_date'] = [str(t).replace('-', '').replace('/', '').split(' ')[0] for t in times]
+ elif times and len(df) == 1:
+ df['end_date'] = str(times[0]).replace('-', '').replace('/', '').split(' ')[0]
+
+ # If still no end_date, look for it in columns
+ if 'end_date' not in df.columns:
+ for col in ['time', 'date', 'trade_date', 'REPORT_DATE']:
+ if col in df.columns:
+ df['end_date'] = df[col].astype(str).str.replace('-', '').str.replace('/', '').str.split(' ').str[0]
+ break
+
+ return df
+
+ def _filter_data(self, df: pd.DataFrame) -> pd.DataFrame:
+ if df.empty or 'end_date' not in df.columns:
+ return df
+
+ df = df.sort_values(by='end_date', ascending=False)
+ df = df.drop_duplicates(subset=['end_date'], keep='first')
+
+ if df.empty:
+ return df
+
+ latest_record = df.iloc[[0]]
+ try:
+ latest_date_str = str(latest_record['end_date'].values[0])
+ # Handle YoY logic: YYYYMMDD -> (YYYY-1)MMDD
+ last_year_date_str = str(int(latest_date_str) - 10000)
+ comparable_record = df[df['end_date'].astype(str) == last_year_date_str]
+ except:
+ comparable_record = pd.DataFrame()
+
+ # 对齐 CN 逻辑,日本公司虽然多是 0331 截止
+ is_annual = df['end_date'].astype(str).str.endswith('0331') | df['end_date'].astype(str).str.endswith('1231')
+ annual_records = df[is_annual]
+
+ combined = pd.concat([latest_record, comparable_record, annual_records])
+ combined = combined.drop_duplicates(subset=['end_date'])
+ combined = combined.sort_values(by='end_date', ascending=False)
+ return combined
+
+ def _fetch_financial_data_annual(self, symbol: str, indicator_configs: list) -> pd.DataFrame:
+ """通用获取历年会计年结日的财务数据 (CNY 结算)"""
+ code = self._get_ifind_code(symbol)
+ basic_info = self._fetch_basic_info(symbol)
+ acc_date = basic_info.get("accounting_date", "1231")
+
+ current_year = int(time.strftime("%Y"))
+ all_dfs = []
+
+ # 获取最近 5 年的数据,精准定位会计年结日
+ for i in range(5):
+ target_year = current_year - i
+ target_date = f"{target_year}{acc_date}"
+
+ params = {
+ "codes": code,
+ "indipara": [
+ {"indicator": item["indicator"], "indiparams": [target_date, item.get("type", "1"), "CNY"]}
+ for item in indicator_configs
+ ]
+ }
+ # print(f"iFinD API Request: endpoint=basic_data_service, params={params}")
+ res = self.cli.post("basic_data_service", params)
+ df = self._parse_ifind_tables(res)
+ if not df.empty:
+ # 强制设置 end_date 以防 API 返回不一致
+ df['end_date'] = target_date
+ all_dfs.append(df)
+
+ if not all_dfs:
+ return pd.DataFrame()
+
+ return pd.concat(all_dfs, ignore_index=True)
+
+ def get_income_statement(self, symbol: str) -> pd.DataFrame:
+ indicators = [
+ {"indicator": "revenue_oas"},
+ {"indicator": "gross_profit_oas"},
+ {"indicator": "sga_expenses_oas"},
+ {"indicator": "selling_marketing_expenses_oas"},
+ {"indicator": "ga_expenses_oas"},
+ {"indicator": "rd_expenses_oas"},
+ {"indicator": "income_tax_expense_oas"},
+ {"indicator": "net_income_attri_to_common_sh_oas"},
+ {"indicator": "operating_income_oas"}
+ ]
+
+ df = self._fetch_financial_data_annual(symbol, indicators)
+ if df.empty: return df
+ self._save_raw_data(df, symbol, "income_statement_raw")
+
+ rename_map = {
+ 'revenue_oas': 'revenue',
+ 'gross_profit_oas': 'gross_profit',
+ 'sga_expenses_oas': 'sga_exp',
+ 'selling_marketing_expenses_oas': 'selling_marketing_exp',
+ 'ga_expenses_oas': 'ga_exp',
+ 'rd_expenses_oas': 'rd_exp',
+ 'income_tax_expense_oas': 'income_tax',
+ 'net_income_attri_to_common_sh_oas': 'net_income',
+ 'operating_income_oas': 'operating_profit'
+ }
+
+ df_filtered = df.rename(columns=rename_map)
+
+ # 数值转换
+ for col in df_filtered.columns:
+ if col not in ['date', 'end_date']:
+ df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
+
+ return self._filter_data(df_filtered)
+
+ def get_balance_sheet(self, symbol: str) -> pd.DataFrame:
+ indicators = [
+ {"indicator": "cash_equi_short_term_inve_oas"},
+ {"indicator": "accou_and_notes_recei_oas"},
+ {"indicator": "inventories_oas"},
+ {"indicator": "ppe_net_oas"},
+ {"indicator": "long_term_inv_and_receiv_oas"},
+ {"indicator": "goodwill_and_intasset_oas"},
+ {"indicator": "short_term_debt_oas"},
+ {"indicator": "short_term_borrowings_oas"},
+ {"indicator": "account_and_note_payable_oas"},
+ {"indicator": "contra_liabilities_current_oas"},
+ {"indicator": "advance_from_cust_current_oas"},
+ {"indicator": "defer_revenue_current_oas"},
+ {"indicator": "long_term_debt_oas"},
+ {"indicator": "long_term_borrowings_oas"},
+ {"indicator": "total_assets_oas"},
+ {"indicator": "equity_attri_to_companyowner_oas"},
+ {"indicator": "prepaid_expenses_current_oas"}
+ ]
+
+ df = self._fetch_financial_data_annual(symbol, indicators)
+ if df.empty: return df
+ self._save_raw_data(df, symbol, "balance_sheet_raw")
+
+ rename_map = {
+ 'cash_equi_short_term_inve_oas': 'cash',
+ 'accou_and_notes_recei_oas': 'receivables',
+ 'inventories_oas': 'inventory',
+ 'ppe_net_oas': 'fixed_assets',
+ 'long_term_inv_and_receiv_oas': 'long_term_investments',
+ 'goodwill_and_intasset_oas': 'goodwill',
+ 'short_term_debt_oas': 'short_term_debt',
+ 'short_term_borrowings_oas': 'short_term_borrowings',
+ 'account_and_note_payable_oas': 'accounts_payable',
+ 'contra_liabilities_current_oas': 'contract_liabilities',
+ 'advance_from_cust_current_oas': 'advances_from_customers',
+ 'defer_revenue_current_oas': 'deferred_revenue',
+ 'long_term_debt_oas': 'long_term_debt',
+ 'long_term_borrowings_oas': 'long_term_borrowings',
+ 'total_assets_oas': 'total_assets',
+ 'equity_attri_to_companyowner_oas': 'total_equity',
+ 'prepaid_expenses_current_oas': 'prepayment'
+ }
+
+ df_filtered = df.rename(columns=rename_map)
+
+ # 如果没有负债合计,用资产减权益
+ if 'total_liabilities' not in df_filtered.columns or df_filtered['total_liabilities'].isnull().all():
+ if 'total_assets' in df_filtered.columns and 'total_equity' in df_filtered.columns:
+ df_filtered['total_liabilities'] = df_filtered['total_assets'] - df_filtered['total_equity']
+
+ for col in df_filtered.columns:
+ if col not in ['date', 'end_date']:
+ df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
+
+ return self._filter_data(df_filtered)
+
+ def get_cash_flow(self, symbol: str) -> pd.DataFrame:
+ indicators = [
+ {"indicator": "net_cash_flows_from_oa_oas"},
+ {"indicator": "purchase_of_ppe_and_ia_oas"},
+ {"indicator": "dividends_paid_oas"}
+ ]
+
+ df = self._fetch_financial_data_annual(symbol, indicators)
+ if df.empty: return df
+ self._save_raw_data(df, symbol, "cash_flow_raw")
+
+ rename_map = {
+ 'net_cash_flows_from_oa_oas': 'ocf',
+ 'purchase_of_ppe_and_ia_oas': 'capex',
+ 'dividends_paid_oas': 'dividends'
+ }
+
+ df_filtered = df.rename(columns=rename_map)
+
+ for col in df_filtered.columns:
+ if col not in ['date', 'end_date']:
+ df_filtered[col] = pd.to_numeric(df_filtered[col], errors='coerce')
+
+ if 'capex' in df_filtered.columns:
+ df_filtered['capex'] = df_filtered['capex'].abs()
+
+ return self._filter_data(df_filtered)
+
+ def get_market_metrics(self, symbol: str) -> dict:
+ """获取公司基本信息(名称、上市日期等静态数据)"""
+ basic_info = self._fetch_basic_info(symbol)
+
+ metrics = {
+ "name": basic_info.get("name", ""),
+ "list_date": basic_info.get("ipo_date", "")
+ }
+
+ return metrics
+
+ def get_historical_metrics(self, symbol: str, dates: list) -> pd.DataFrame:
+ """获取历史日期的收盘价和市值 (通过 cmd_history_quotation)"""
+ code = self._get_ifind_code(symbol)
+ if not dates: return pd.DataFrame()
+
+ results = []
+ # get_historical_metrics里面不要拿所有日期数据了,而是一个一个数据拿
+ for d in dates:
+ d_str = str(d).replace('-', '').replace('/', '')
+ fmt_d = f"{d_str[:4]}-{d_str[4:6]}-{d_str[6:]}" if len(d_str) == 8 else d_str
+
+ params = {
+ "codes": code,
+ "startdate": fmt_d,
+ "enddate": fmt_d,
+ "functionpara": {"Interval": "D", "Days": "Alldays", "Fill": "Previous"},
+ "indipara": [
+ {"indicator": "pre_close", "indiparams": ["", "0", "CNY"]},
+ {"indicator": "market_value", "indiparams": ["", "CNY"]}
+ ]
+ }
+
+ # print(f"iFinD API Request: endpoint=date_sequence, params={params}")
+ res = self.cli.post("date_sequence", params)
+ df_seq = self._parse_ifind_tables(res)
+
+ metrics = {'date_str': d_str, 'PE': 0.0, 'PB': 0.0, 'MarketCap': 0.0, 'Price': 0.0}
+
+ if not df_seq.empty:
+ # 找到最接近该日期且不晚于该日期的记录
+ match = df_seq[df_seq['end_date'] <= d_str].tail(1) if 'end_date' in df_seq.columns else df_seq.tail(1)
+ if not match.empty:
+ if 'pre_close' in match.columns:
+ metrics['Price'] = float(match['pre_close'].iloc[0] or 0.0)
+ if 'market_value' in match.columns:
+ metrics['MarketCap'] = float(match['market_value'].iloc[0] or 0.0)
+ results.append(metrics)
+
+ df_hist = pd.DataFrame(results)
+ self._save_raw_data(df_hist, symbol, "historical_metrics_raw")
+ return df_hist
+
+ def get_dividends(self, symbol: str) -> pd.DataFrame:
+ """获取历年年度累计分红记录 (逐年获取)"""
+ code = self._get_ifind_code(symbol)
+ basic_info = self._fetch_basic_info(symbol)
+ acc_date = basic_info.get("accounting_date", "1231")
+
+ current_year = int(time.strftime("%Y"))
+ results = []
+
+ # 获取最近 5 年的数据
+ for i in range(5):
+ year_str = str(current_year - i)
+ params = {
+ "codes": code,
+ "indipara": [
+ {"indicator": "annual_cum_dividend", "indiparams": [year_str, "CNY"]}
+ ]
+ }
+ # print(f"iFinD API Request: endpoint=basic_data_service, params={params}")
+ res = self.cli.post("basic_data_service", params)
+ df = self._parse_ifind_tables(res)
+
+ if not df.empty and 'annual_cum_dividend' in df.columns:
+ val = df['annual_cum_dividend'].iloc[0]
+ if pd.notna(val) and val != 0:
+ results.append({
+ 'date_str': f"{year_str}{acc_date}",
+ 'dividends': float(val)
+ })
+
+ if not results:
+ return pd.DataFrame()
+
+ df_div = pd.DataFrame(results)
+ self._save_raw_data(df_div, symbol, "dividends_raw")
+ return df_div
+
+ def get_repurchases(self, symbol: str) -> pd.DataFrame:
+ """获取历年年度回购记录 (从 repur_num_new 获取)"""
+ code = self._get_ifind_code(symbol)
+ basic_info = self._fetch_basic_info(symbol)
+ acc_date = basic_info.get("accounting_date", "1231")
+ mm = acc_date[:2]
+ dd = acc_date[2:]
+
+ # 为了对应日期格式 YYYY-MM-DD
+ fmt_mm_dd = f"{mm}-{dd}"
+
+ current_year = int(time.strftime("%Y"))
+ results = []
+
+ # 获取最近 5 年的数据
+ for i in range(5):
+ target_year = current_year - i
+ start_date = f"{target_year - 1}-{fmt_mm_dd}"
+ end_date = f"{target_year}-{fmt_mm_dd}"
+
+ params = {
+ "codes": code,
+ "indipara": [
+ {"indicator": "repur_num_new", "indiparams": [start_date, end_date, "1"]}
+ ]
+ }
+ # print(f"iFinD API Request: endpoint=basic_data_service, params={params}")
+ res = self.cli.post("basic_data_service", params)
+ df = self._parse_ifind_tables(res)
+
+ if not df.empty and 'repur_num_new' in df.columns:
+ val = df['repur_num_new'].iloc[0]
+ if pd.notna(val) and val != 0:
+ results.append({
+ 'date_str': f"{target_year}{acc_date}",
+ 'repurchases': float(val)
+ })
+
+ if not results:
+ return pd.DataFrame()
+
+
+ df_repur = pd.DataFrame(results)
+ self._save_raw_data(df_repur, symbol, "repurchases_raw")
+ return df_repur
+
+ def get_employee_count(self, symbol: str) -> pd.DataFrame:
+ """获取历年员工人数"""
+ code = self._get_ifind_code(symbol)
+ basic_info = self._fetch_basic_info(symbol)
+ acc_date = basic_info.get("accounting_date", "1231")
+ mm = acc_date[:2]
+ dd = acc_date[2:]
+
+ current_year = int(time.strftime("%Y"))
+ results = []
+
+ # 获取最近 5 年的数据
+ for i in range(5):
+ target_year = current_year - i
+ target_date = f"{target_year}-{mm}-{dd}"
+
+ params = {
+ "codes": code,
+ "indipara": [
+ {"indicator": "staff_num", "indiparams": [target_date]}
+ ]
+ }
+ res = self.cli.post("basic_data_service", params)
+ df = self._parse_ifind_tables(res)
+
+ if not df.empty and 'staff_num' in df.columns:
+ val = df['staff_num'].iloc[0]
+ if pd.notna(val) and val != 0:
+ results.append({
+ 'date_str': f"{target_year}{acc_date}",
+ 'employee_count': float(val)
+ })
+
+ if not results:
+ return pd.DataFrame()
+
+ df_emp = pd.DataFrame(results)
+ self._save_raw_data(df_emp, symbol, "employee_count_raw")
+ return df_emp
diff --git a/src/fetchers/test_ifind_jp.py b/src/fetchers/test_ifind_jp.py
new file mode 100644
index 0000000..a946278
--- /dev/null
+++ b/src/fetchers/test_ifind_jp.py
@@ -0,0 +1,63 @@
+
+import os
+import sys
+import pandas as pd
+from dotenv import load_dotenv
+
+# Add src to path
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from fetchers.jp_fetcher import JpFetcher
+
+def test_jp_fetcher():
+ load_dotenv()
+ token = os.getenv("IFIND_REFRESH_TOKEN")
+ if not token:
+ print("Missing IFIND_REFRESH_TOKEN in .env")
+ return
+
+ fetcher = JpFetcher(token)
+ symbol = "7203" # Toyota
+
+ print(f"--- Fetching Market Metrics for {symbol} ---")
+ metrics = fetcher.get_market_metrics(symbol)
+ print(metrics)
+
+ print(f"\n--- Fetching Income Statement for {symbol} ---")
+ df_is = fetcher.get_income_statement(symbol)
+ if not df_is.empty:
+ print(df_is.head())
+ else:
+ print("Income Statement is empty!")
+
+ print(f"\n--- Fetching Balance Sheet for {symbol} ---")
+ df_bs = fetcher.get_balance_sheet(symbol)
+ if not df_bs.empty:
+ print(df_bs.head())
+ else:
+ print("Balance Sheet is empty!")
+
+ print(f"\n--- Fetching Cash Flow for {symbol} ---")
+ df_cf = fetcher.get_cash_flow(symbol)
+ if not df_cf.empty:
+ print(df_cf.head())
+ else:
+ print("Cash Flow is empty!")
+
+ print(f"\n--- Fetching Historical Metrics for {symbol} ---")
+ dates = ["2023-12-21", "2024-06-30"]
+ df_hist = fetcher.get_historical_metrics(symbol, dates)
+ if not df_hist.empty:
+ print(df_hist)
+ else:
+ print("Historical Metrics is empty!")
+
+ print(f"\n--- Fetching Dividends for {symbol} ---")
+ df_div = fetcher.get_dividends(symbol)
+ if not df_div.empty:
+ print(df_div.head())
+ else:
+ print("Dividends empty or not found.")
+
+if __name__ == "__main__":
+ test_jp_fetcher()
diff --git a/src/reporting/__pycache__/hk_report_generator.cpython-313.pyc b/src/reporting/__pycache__/hk_report_generator.cpython-313.pyc
index 3ff833a..b125272 100644
Binary files a/src/reporting/__pycache__/hk_report_generator.cpython-313.pyc and b/src/reporting/__pycache__/hk_report_generator.cpython-313.pyc differ
diff --git a/src/reporting/__pycache__/jp_report_generator.cpython-312.pyc b/src/reporting/__pycache__/jp_report_generator.cpython-312.pyc
new file mode 100644
index 0000000..75da5e7
Binary files /dev/null and b/src/reporting/__pycache__/jp_report_generator.cpython-312.pyc differ
diff --git a/src/reporting/__pycache__/jp_report_generator.cpython-313.pyc b/src/reporting/__pycache__/jp_report_generator.cpython-313.pyc
new file mode 100644
index 0000000..4e83b10
Binary files /dev/null and b/src/reporting/__pycache__/jp_report_generator.cpython-313.pyc differ
diff --git a/src/reporting/hk_report_generator.py b/src/reporting/hk_report_generator.py
index 5e9ea8a..ba6015b 100644
--- a/src/reporting/hk_report_generator.py
+++ b/src/reporting/hk_report_generator.py
@@ -27,8 +27,6 @@ class HK_ReportGenerator(BaseReporter):
('goodwill', '商誉(亿)', 'currency_yi')
],
"费用指标": [
- ('SellingRatio', '销售费用率', 'percent'),
- ('AdminRatio', '管理费用率', 'percent'),
('SgaRatio', 'SG&A比例', 'percent'),
('RDRatio', '研发费用率', 'percent'),
('OtherExpenseRatio', '其他费用率', 'percent'),
@@ -57,50 +55,23 @@ class HK_ReportGenerator(BaseReporter):
('PayablesDays', '应付款周转天数', 'int'),
('FixedAssetsTurnover', '固定资产周转率', 'float'),
('TotalAssetTurnover', '总资产周转率', 'float'),
- ],
- "人均效率": [
- ('Employees', '员工人数', 'int'),
- ('RevenuePerEmp', '人均创收(万)', 'currency_wan'),
- ('ProfitPerEmp', '人均创利(万)', 'currency_wan'),
- ('AvgWage', '人均薪酬(万)', 'currency_wan'),
- ],
- "市场表现": [
- ('Price', '股价', 'float'),
- ('MarketCap', '市值(亿)', 'currency_yi_market'),
- ('PE', 'PE', 'float'),
- ('PB', 'PB', 'float'),
- ('Shareholders', '股东户数', 'int'),
]
}
- def _preprocess_data(self, df, market):
- df = super()._preprocess_data(df, market)
- if not df.empty:
- dates = pd.to_datetime(df['date_str'], format='%Y%m%d')
- latest_year = dates.dt.year.max()
- is_annual = dates.dt.month == 12
- is_latest_year = dates.dt.year == latest_year
- df = df[is_annual | is_latest_year]
- return df
-
def _generate_md_company_info(self, symbol, metrics, market):
today_str = datetime.date.today().strftime("%Y-%m-%d")
name = metrics.get('name', '')
- raw_list_date = metrics.get('list_date', '')
- if isinstance(raw_list_date, str) and len(raw_list_date) == 8:
- list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}"
- else:
- list_date = raw_list_date
+ fiscal_year_end = metrics.get('fiscal_year_end', '')
pe = metrics.get('pe', 0)
pb = metrics.get('pb', 0)
- div = metrics.get('dividend_yield', 0)
+ div_yield = metrics.get('dividend_yield', 0) * 100
md = []
md.append(f"# {name} ({symbol}) - Financial Report")
md.append(f"*Report generated on: {today_str}*\n")
- md.append("| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) |")
+ md.append("| 代码 | 简称 | 财报日期 | PE | PB | 股息率(%) |")
md.append("|:---|:---|:---|:---|:---|:---|")
- md.append(f"| {symbol} | {name} | {list_date} | {pe:.2f} | {pb:.2f} | {div:.2f}% |")
+ md.append(f"| {symbol} | {name} | {fiscal_year_end} | {pe:.2f} | {pb:.2f} | {div_yield:.2f}% |")
return "\n".join(md)
def generate_report(self, df_analysis, symbol, market, metrics, output_dir):
@@ -128,14 +99,10 @@ class HK_ReportGenerator(BaseReporter):
def _build_html_content(self, symbol, metrics, headers, df):
today_str = datetime.date.today().strftime("%Y-%m-%d")
name = metrics.get('name') or symbol
- raw_list_date = metrics.get('list_date', '')
- if isinstance(raw_list_date, str) and len(raw_list_date) == 8:
- list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}"
- else:
- list_date = raw_list_date or "-"
+ fiscal_year_end = metrics.get('fiscal_year_end') or "-"
pe = metrics.get('pe', 0) or 0
pb = metrics.get('pb', 0) or 0
- div = metrics.get('dividend_yield', 0) or 0
+ div_yield = (metrics.get('dividend_yield', 0) or 0) * 100
company_table = f"""
@@ -143,7 +110,7 @@ class HK_ReportGenerator(BaseReporter):
| 代码 |
简称 |
- 上市日期 |
+ 财报日期 |
PE |
PB |
股息率(%) |
@@ -153,10 +120,10 @@ class HK_ReportGenerator(BaseReporter):
| {symbol} |
{name} |
- {list_date} |
+ {fiscal_year_end} |
{pe:.2f} |
{pb:.2f} |
- {div:.2f}% |
+ {div_yield:.2f}% |
diff --git a/src/reporting/jp_report_generator.py b/src/reporting/jp_report_generator.py
new file mode 100644
index 0000000..a9f69f8
--- /dev/null
+++ b/src/reporting/jp_report_generator.py
@@ -0,0 +1,588 @@
+from .base_generator import BaseReporter
+import pandas as pd
+import datetime
+import os
+import markdown
+
+class JP_ReportGenerator(BaseReporter):
+ def __init__(self):
+ super().__init__()
+ # Use the same indicators and labels as CN
+ self.indicators = {
+ "主要指标": [
+ ('ROE', 'ROE', 'percent'),
+ ('ROA', 'ROA', 'percent'),
+ ('ROIC', 'ROCE/ROIC', 'percent'),
+ ('GrossMargin', '毛利率', 'percent'),
+ ('NetMargin', '净利润率', 'percent'),
+ ('revenue', '收入(亿)', 'currency_yi'),
+ ('RevenueGrowth', '收入增速', 'percent_color'),
+ ('net_income', '净利润(亿)', 'currency_yi'),
+ ('NetIncomeGrowth', '净利润增速', 'percent_color'),
+ ('ocf', '经营净现金流(亿)', 'currency_yi_color'),
+ ('Capex', '资本开支(亿)', 'currency_yi'),
+ ('FCF', '自由现金流(亿)', 'currency_yi_compare'),
+ ('dividends', '分红(亿)', 'currency_yi'),
+ ('repurchases', '回购(亿)', 'currency_yi'),
+ ('total_assets', '总资产(亿)', 'currency_yi'),
+ ('total_equity', '净资产(亿)', 'currency_yi'),
+ ('goodwill', '商誉(亿)', 'currency_yi')
+ ],
+ "费用指标": [
+ ('SellingRatio', '销售费用率', 'percent'),
+ ('AdminRatio', '管理费用率', 'percent'),
+ ('SgaRatio', 'SG&A比例', 'percent'),
+ ('RDRatio', '研发费用率', 'percent'),
+ ('OtherExpenseRatio', '其他费用率', 'percent'),
+ ('DepreciationRatio', '折旧费用占比', 'percent'),
+ ('TaxRate', '所得税率', 'percent'),
+ ],
+ "资产占比": [
+ ('CashRatio', '现金占比', 'percent_alert_30'),
+ ('InventoryRatio', '库存占比', 'percent'),
+ ('ReceivablesRatio', '应收款占比', 'percent'),
+ ('PrepaymentRatio', '预付款占比', 'percent'),
+ ('FixedAssetsRatio', '固定资产占比', 'percent'),
+ ('LongTermInvestmentRatio', '长期投资占比', 'percent'),
+ ('GoodwillRatio', '商誉占比', 'percent'),
+ ('OtherAssetsRatio', '其他资产占比', 'percent'),
+ ('PayablesRatio', '应付款占比', 'percent'),
+ ('AdvanceReceiptsRatio', '预收款占比', 'percent'),
+ ('ShortTermDebtRatio', '短期借款占比', 'percent'),
+ ('LongTermDebtRatio', '长期借款占比', 'percent'),
+ ('OperatingAssetsRatio', '运营资产占比', 'percent'),
+ ('InterestBearingDebtRatio', '有息负债率', 'percent'),
+ ],
+ "周转能力": [
+ ('InventoryDays', '存货周转天数', 'int'),
+ ('ReceivablesDays', '应收款周转天数', 'int_alert_90'),
+ ('PayablesDays', '应付款周转天数', 'int'),
+ ('FixedAssetsTurnover', '固定资产周转率', 'float'),
+ ('TotalAssetTurnover', '总资产周转率', 'float'),
+ ],
+ "人均效率": [
+ ('Employees', '员工人数', 'int'),
+ ('RevenuePerEmp', '人均创收(万)', 'currency_wan'),
+ ('ProfitPerEmp', '人均创利(万)', 'currency_wan'),
+ ('AvgWage', '人均薪酬(万)', 'currency_wan'),
+ ],
+ "市场表现": [
+ ('Price', '股价', 'float'),
+ ('MarketCap', '市值(亿)', 'currency_yi_market'),
+ ('PE', 'PE', 'float'),
+ ('PB', 'PB', 'float'),
+ ('Shareholders', '股东户数', 'int'),
+ ]
+ }
+
+ def _preprocess_data(self, df, market):
+ df = super()._preprocess_data(df, market)
+ if not df.empty:
+ # For JP, usually ends in March (0331) or Dec (1231)
+ dates = pd.to_datetime(df['date_str'], format='%Y%m%d')
+ latest_year = dates.dt.year.max()
+ # We keep annual reports (typically 0331 or 1231) and the absolute latest record
+ is_march = dates.dt.month == 3
+ is_dec = dates.dt.month == 12
+ is_latest = df.index == df.index[0] # Assumes sorted descending
+ df = df[is_march | is_dec | is_latest]
+ return df
+
+ def _format_period_label(self, date_value):
+ if pd.isna(date_value):
+ return "-"
+ date_str = str(date_value)
+ if len(date_str) != 8:
+ return date_str
+ year = date_str[:4]
+ month = date_str[4:6]
+ day = date_str[6:]
+ try:
+ month_int = int(month)
+ day_int = int(day)
+ except ValueError:
+ return f"{year}A"
+
+ # For Japanese annual reports, show only year + 'A'
+ return f"{year}A"
+
+ def _get_headers(self, df):
+ return [self._format_period_label(date_value) for date_value in df['date_str']]
+
+ def _generate_md_company_info(self, symbol, metrics, market):
+ today_str = datetime.date.today().strftime("%Y-%m-%d")
+ name = metrics.get('name', '')
+ raw_list_date = metrics.get('list_date', '')
+ if isinstance(raw_list_date, str) and len(raw_list_date) == 8:
+ list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}"
+ else:
+ list_date = raw_list_date
+ pe = metrics.get('pe', 0) or 0
+ pb = metrics.get('pb', 0) or 0
+ div = metrics.get('dividend_yield', 0) or 0
+ md = []
+ md.append(f"# {name} ({symbol}) - Financial Report")
+ md.append(f"*Report generated on: {today_str}*\n")
+ md.append("| 代码 | 简称 | 上市日期 | PE | PB | 股息率(%) |")
+ md.append("|:---|:---|:---|:---|:---|:---|")
+ md.append(f"| {symbol} | {name} | {list_date} | {pe:.2f} | {pb:.2f} | {div:.2f}% |")
+ return "\n".join(md)
+
+ def generate_report(self, df_analysis, symbol, market, metrics, output_dir):
+ md_content = self._generate_markdown_content(df_analysis, market, symbol, metrics)
+ os.makedirs(output_dir, exist_ok=True)
+ md_path = os.path.join(output_dir, "report.md")
+ with open(md_path, "w", encoding='utf-8') as f:
+ f.write(md_content)
+
+ df_for_html = df_analysis.copy() if isinstance(df_analysis, pd.DataFrame) else pd.DataFrame()
+ if not df_for_html.empty:
+ df_for_html = self._preprocess_data(df_for_html, market)
+ headers = self._get_headers(df_for_html)
+ else:
+ headers = []
+ html_content = self._build_html_content(symbol, metrics, headers, df_for_html)
+ # Re-use the exact same styled HTML from CN_ReportGenerator
+ final_html = self.to_html(symbol, html_content)
+
+ html_path = os.path.join(output_dir, "report.html")
+ with open(html_path, "w", encoding='utf-8') as f:
+ f.write(final_html)
+
+ def _build_html_content(self, symbol, metrics, headers, df):
+ # Implementation identical to CN_ReportGenerator for style consistency
+ today_str = datetime.date.today().strftime("%Y-%m-%d")
+ name = metrics.get('name') or symbol
+ raw_list_date = metrics.get('list_date', '')
+ if isinstance(raw_list_date, str) and len(raw_list_date) == 8:
+ list_date = f"{raw_list_date[:4]}-{raw_list_date[4:6]}-{raw_list_date[6:]}"
+ else:
+ list_date = raw_list_date or "-"
+ pe = metrics.get('pe', 0) or 0
+ pb = metrics.get('pb', 0) or 0
+ div = metrics.get('dividend_yield', 0) or 0
+
+ company_table = f"""
+
+
+
+ | 代码 |
+ 简称 |
+ 上市日期 |
+ PE |
+ PB |
+ 股息率(%) |
+
+
+
+
+ | {symbol} |
+ {name} |
+ {list_date} |
+ {pe:.2f} |
+ {pb:.2f} |
+ {div:.2f}% |
+
+
+
+ """
+
+ if df is None or df.empty or not headers:
+ metrics_table = "暂无可用财务指标
"
+ else:
+ header_cells = "".join([f"{header} | " for header in headers])
+ data_column_count = max(len(headers), 1)
+ rows_html = []
+ for group_name, items in self.indicators.items():
+ rows_html.append(
+ f""
+ f"| {group_name} | "
+ f" | "
+ "
"
+ )
+ for key, label, fmt_type in items:
+ value_cells = [f"{label} | "]
+ for _, row_series in df.iterrows():
+ value_cells.append(f"{self._format_value(row_series.get(key), fmt_type)} | ")
+ row_class = "other-assets-row" if key == 'OtherAssetsRatio' else ""
+ if row_class:
+ rows_html.append(f"{''.join(value_cells)}
")
+ else:
+ rows_html.append(f"{''.join(value_cells)}
")
+ rows_markup = "\n".join(rows_html)
+ metrics_table = f"""
+
+
+
+ | 指标 |
+ {header_cells}
+
+
+
+ {rows_markup}
+
+
+ """
+
+ html_sections = [
+ f"{name} ({symbol}) - Financial Report
",
+ f"Report generated on: {today_str}
",
+ company_table,
+ '',
+ metrics_table
+ ]
+ return "\n".join(html_sections)
+
+ def to_html(self, symbol, html_content):
+ # This is a literal copy of the CSS/JS from CN_ReportGenerator to ensure identical style
+ styled_html = '''
+
+
+
+
+ {symbol} Financial Report
+
+
+
+
+ {html_content}
+
+
+
+
+ '''
+ final_html = styled_html.replace('{symbol}', symbol).replace('{html_content}', html_content)
+ return final_html
diff --git a/src/strategies/__pycache__/hk_strategy.cpython-313.pyc b/src/strategies/__pycache__/hk_strategy.cpython-313.pyc
index ac38d87..2a34bb2 100644
Binary files a/src/strategies/__pycache__/hk_strategy.cpython-313.pyc and b/src/strategies/__pycache__/hk_strategy.cpython-313.pyc differ
diff --git a/src/strategies/__pycache__/jp_strategy.cpython-312.pyc b/src/strategies/__pycache__/jp_strategy.cpython-312.pyc
new file mode 100644
index 0000000..c7e3525
Binary files /dev/null and b/src/strategies/__pycache__/jp_strategy.cpython-312.pyc differ
diff --git a/src/strategies/__pycache__/jp_strategy.cpython-313.pyc b/src/strategies/__pycache__/jp_strategy.cpython-313.pyc
new file mode 100644
index 0000000..0c38220
Binary files /dev/null and b/src/strategies/__pycache__/jp_strategy.cpython-313.pyc differ
diff --git a/src/strategies/hk_strategy.py b/src/strategies/hk_strategy.py
index 4712b01..ffa7954 100644
--- a/src/strategies/hk_strategy.py
+++ b/src/strategies/hk_strategy.py
@@ -6,10 +6,10 @@ from storage.file_io import DataStorage
import os
class HK_Strategy(BaseStrategy):
- def __init__(self, stock_code, tushare_token):
+ def __init__(self, stock_code, av_key):
super().__init__(stock_code)
- self.tushare_token = tushare_token
- self.fetcher = FetcherFactory.get_fetcher('HK', self.tushare_token)
+ self.av_key = av_key
+ self.fetcher = FetcherFactory.get_fetcher('HK', av_key=self.av_key)
self.analyzer = HK_Analyzer()
self.reporter = HK_ReportGenerator()
self.storage = DataStorage()
diff --git a/src/strategies/jp_strategy.py b/src/strategies/jp_strategy.py
new file mode 100644
index 0000000..5c32957
--- /dev/null
+++ b/src/strategies/jp_strategy.py
@@ -0,0 +1,79 @@
+from .base_strategy import BaseStrategy
+from fetchers.factory import FetcherFactory
+from analysis.jp_analyzer import JP_Analyzer
+from reporting.jp_report_generator import JP_ReportGenerator
+from storage.file_io import DataStorage
+import os
+
+class JP_Strategy(BaseStrategy):
+ def __init__(self, stock_code, ifind_refresh_token):
+ super().__init__(stock_code)
+ self.refresh_token = ifind_refresh_token
+ self.fetcher = FetcherFactory.get_fetcher('JP', ifind_refresh_token=self.refresh_token)
+ self.analyzer = JP_Analyzer()
+ self.reporter = JP_ReportGenerator()
+ self.storage = DataStorage()
+ self.raw_data = {}
+ self.analysis_result = None
+
+ def fetch_data(self):
+ print(f"Fetching data for JP market, stock: {self.stock_code}")
+ # Fetch Financial Statements
+ self.raw_data['income'] = self.fetcher.get_income_statement(self.stock_code)
+ self.raw_data['balance'] = self.fetcher.get_balance_sheet(self.stock_code)
+ self.raw_data['cashflow'] = self.fetcher.get_cash_flow(self.stock_code)
+
+ # Rename 'end_date' to 'date' for analyzer compatibility (BaseAnalyzer expects 'date' to create 'date_str')
+ for key in ['income', 'balance', 'cashflow']:
+ if not self.raw_data[key].empty and 'end_date' in self.raw_data[key].columns:
+ self.raw_data[key] = self.raw_data[key].rename(columns={'end_date': 'date'})
+
+ # Fetch Market Metrics (Real-time and static)
+ self.raw_data['metrics'] = self.fetcher.get_market_metrics(self.stock_code)
+
+ # Fetch Historical Metrics (Price and Market Cap)
+ # We'll use some representative dates or the last 3 years of month-ends if needed.
+ # For consistency with CN, let's grab the dates from the income statement or simple defaults.
+ dates = []
+ if not self.raw_data['income'].empty and 'date' in self.raw_data['income'].columns:
+ dates = self.raw_data['income']['date'].tolist()
+
+ self.raw_data['historical_metrics'] = self.fetcher.get_historical_metrics(self.stock_code, dates)
+
+ # Fetch Dividends
+ self.raw_data['dividends'] = self.fetcher.get_dividends(self.stock_code)
+
+ # Fetch Repurchases
+ self.raw_data['repurchases'] = self.fetcher.get_repurchases(self.stock_code)
+
+ # Fetch Employee Count
+ self.raw_data['employee_count'] = self.fetcher.get_employee_count(self.stock_code)
+
+ def analyze_data(self):
+ print(f"Analyzing data for JP market, stock: {self.stock_code}")
+ self.analysis_result = self.analyzer.process_data(
+ self.raw_data['income'],
+ self.raw_data['balance'],
+ self.raw_data['cashflow'],
+ self.raw_data['metrics'],
+ self.raw_data.get('historical_metrics'),
+ self.raw_data.get('dividends'),
+ self.raw_data.get('repurchases'),
+ self.raw_data.get('employee_count')
+ )
+
+ def generate_report(self):
+ print(f"Generating report for JP market, stock: {self.stock_code}")
+ if self.analysis_result is not None and not self.analysis_result.empty:
+ output_dir = os.path.join("data", 'JP', self.stock_code)
+ self.reporter.generate_report(
+ df_analysis=self.analysis_result,
+ symbol=self.stock_code,
+ market='JP',
+ metrics=self.raw_data['metrics'],
+ output_dir=output_dir
+ )
+ else:
+ print("No analysis result to generate report.")
+
+import pandas as pd # Import needed for the placeholder DataFrames