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

+ + + + + + + + + + + + + + + + + + + + + + +
代码简称上市日期PEPB股息率(%)
7203.T丰田汽车公司1949-05-160.000.000.00%
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
指标2025A2024A2023A2022A2021A
ROE13.26%14.45%8.65%10.86%9.59%
ROA5.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.7021,531.6619,221.1616,380.4416,120.11
收入增速8.00%12.02%17.34%1.61%-
净利润(亿)2,306.792,361.061,268.151,487.791,329.94
净利润增速-2.30%86.18%-14.76%11.87%-
经营净现金流(亿)1,789.702,008.421,528.761,943.251,615.39
资本开支(亿)1,094.561,041.23930.41805.65883.97
自由现金流(亿)695.14967.18598.351,137.60731.42
分红(亿)548.16420.27376.61370.56370.51
回购(亿)-----
总资产(亿)45,312.6743,026.8638,439.5135,334.2836,882.91
净资产(亿)17,391.3116,339.4914,660.5613,700.6813,863.29
商誉(亿)659.96647.13646.21622.22656.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%
存货周转天数4347505447
应收款周转天数105110108111121
应付款周转天数3839454548
固定资产周转率3.023.052.832.462.31
总资产周转率0.510.500.500.460.44
员工人数383,853380,793375,235372,817366,283
人均创收(万)605.82565.44512.24439.37440.10
人均创利(万)60.1062.0033.8039.9136.31
人均薪酬(万)-----
股价-145.92130.5294.84116.50
市值(亿)-23,04821,29415,47419,007
PE-9.7616.7910.4014.29
PB-1.411.451.131.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): - + @@ -153,10 +120,10 @@ class HK_ReportGenerator(BaseReporter): - + - +
代码 简称上市日期财报日期 PE PB 股息率(%)
{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""" + + + + + + + + + + + + + + + + + + + + + +
代码简称上市日期PEPB股息率(%)
{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