Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #78

Merged
merged 3 commits into from
Jul 5, 2024
Merged

Dev #78

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ master

专注股票量化数据,为Ai(爱)发电,向阳而生。

2.3.0 (2024-07-06)
------------------
1. 新增:股票:百度的概念接口。
2. 新增:股票:龙虎榜单列表接口。
3. 修复:股票:资金流,成立日期格式等bug。

2.2.0 (2024-07-01)
------------------
1. 新增:股票:资金流接口。
Expand All @@ -24,11 +30,6 @@ master
1. 新增:基金ETF行情接口。
2. 新增:股票股东信息接口。

1.2.4 (2024-02-02)
------------------
1. 修复:修复概念返回为空bug。
2. flag:保卫3000点失败,继续保卫2500点。

......
------------------

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ print(res_df)
| **热度榜单** | sentiment.hot.pop_rank_100_east | 东方财富人气100榜单 | 来源:[东方财富](http://guba.eastmoney.com/rank/) |
| | sentiment.hot.hot_rank_100_ths() | 同花顺热度100排行榜 | 来源:[同花顺](https://dq.10jqka.com.cn/fuyao/hot_list_data/out/hot_list/v1/stock?stock_type=a&type=hour&list_type=normal) |
| | sentiment.hot.hot_concept_20_ths() | 同花顺热门概念板块20排行榜 | 来源:[同花顺](https://dq.10jqka.com.cn/fuyao/hot_list_data/out/hot_list/v1/stock?stock_type=a&type=hour&list_type=normal) |
| | sentiment.hot.list_a_list_daily() | 龙虎榜单列表 | 来源:[东方财富](https://data.eastmoney.com/stock/lhb/yyb/10033779.html) |
| 其它数据排期中 | TODO | 若您有相关资源可以一起参与贡献 | |

## 三、[数据源](https://adata.30006124.xyz/dataSource.html)
Expand Down
2 changes: 1 addition & 1 deletion adata/__version__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

VERSION = (2, 2, 0)
VERSION = (2, 3, 0)
PRERELEASE = None # alpha, beta or rc
REVISION = None

Expand Down
1 change: 1 addition & 0 deletions adata/common/utils/unit_conver.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def convert_to_yuan(input_dict):
unit_multipliers = {'亿': 100000000, '万': 10000}

for key, value in input_dict.items():
input_dict[key] = value.replace('元', '')
if isinstance(value, str) and any(unit in value for unit in unit_multipliers.keys()):
number, unit = re.findall(r'([-+]?\d*\.\d+|\d+)([亿万]?)', value)[0]
number = float(number)
Expand Down
29 changes: 19 additions & 10 deletions adata/sentiment/alist.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
class AList(BaseThs):
"""龙虎榜单"""

__A_LIST_DAILY_COLUMNS = ['trade_date', 'short_name', 'stock_code', 'close', 'change_cpt', 'turnover_ratio',
'a_net_amount', 'a_buy_amount', 'a_sell_amount', 'a_amount', 'amount',
'net_amount_rate', 'a_amount_rate', 'reason']

# 东方财富人气榜
def list_a_list_daily(self, report_date=None):
"""
Expand All @@ -35,16 +39,20 @@ def list_a_list_daily(self, report_date=None):
# 2. 请求数据
text = requests.request(method='post', url=url).text
res = json.loads(text[text.index('{'):-2])
if res['result'] is None:
return pd.DataFrame()
df = pd.DataFrame(res['result']["data"])

# 3. 解析封装数据 TODO
rename = {'f2': 'price', 'f3': 'change_pct', 'f12': 'stock_code', 'f14': 'short_name', }
rank_df = pd.rename(columns=rename)
rank_df["change_pct"] = pd.to_numeric(rank_df["change_pct"], errors="coerce")
rank_df["price"] = pd.to_numeric(rank_df["price"], errors="coerce")
rank_df["change"] = rank_df["price"] * rank_df["change_pct"] / 100
rank_df["rank"] = range(1, len(rank_df) + 1)
return rank_df[["rank", "stock_code", "short_name", "price", "change", "change_pct"]]
# 3. 解析封装数据
rename = {'SECURITY_CODE': 'stock_code', 'SECURITY_NAME_ABBR': 'short_name', 'TRADE_DATE': 'trade_date',
'CLOSE_PRICE': 'close', 'CHANGE_RATE': 'change_cpt', 'TURNOVERRATE': 'turnover_ratio',
'BILLBOARD_NET_AMT': 'a_net_amount', 'BILLBOARD_BUY_AMT': 'a_buy_amount',
'BILLBOARD_SELL_AMT': 'a_sell_amount', 'BILLBOARD_DEAL_AMT': 'a_amount',
'ACCUM_AMOUNT': 'amount', 'DEAL_NET_RATIO': 'net_amount_rate', 'DEAL_AMOUNT_RATIO': 'a_amount_rate',
'EXPLANATION': 'reason', }
df = df.rename(columns=rename)
df['trade_date'] = pd.to_datetime(df['trade_date']).dt.strftime('%Y-%m-%d')
df['short_name'] = df['short_name'].str.replace(' ', '')
return df[self.__A_LIST_DAILY_COLUMNS]

def get_a_list(self, stock_code, report_date=None):
"""
Expand All @@ -56,4 +64,5 @@ def get_a_list(self, stock_code, report_date=None):


if __name__ == '__main__':
AList().list_a_list_daily()
print(AList().list_a_list_daily(report_date='2024-07-04'))
print(AList().list_a_list_daily())
4 changes: 2 additions & 2 deletions adata/sentiment/hot.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
"""
import pandas as pd

from adata.common.base.base_ths import BaseThs
from adata.common.headers import ths_headers
from adata.common.utils import requests
from adata.sentiment.alist import AList


class Hot(BaseThs):
class Hot(AList):
"""热门榜单"""

# 东方财富人气榜
Expand Down
3 changes: 2 additions & 1 deletion adata/stock/info/concept/stock_concept.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
@author: 1nchaos
@date: 2023/3/30 16:17
"""
from adata.stock.info.concept.stock_concept_baidu import StockConceptBaidu
from adata.stock.info.concept.stock_concept_east import StockConceptEast
from adata.stock.info.concept.stock_concept_ths import StockConceptThs


class StockConcept(StockConceptThs, StockConceptEast):
class StockConcept(StockConceptThs, StockConceptEast, StockConceptBaidu):

def __init__(self) -> None:
super().__init__()
67 changes: 67 additions & 0 deletions adata/stock/info/concept/stock_concept_baidu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
"""
@summary: 股票概念
东方财富股票概念

https://data.eastmoney.com/bkzj/gn.html

单个股票的所有概念板块
https://datacenter.eastmoney.com/securities/api/data/v1/get?reportName=RPT_F10_CORETHEME_BOARDTYPE&columns=SECUCODE%2CSECURITY_CODE%2CSECURITY_NAME_ABBR%2CNEW_BOARD_CODE%2CBOARD_NAME%2CSELECTED_BOARD_REASON%2CIS_PRECISE%2CBOARD_RANK%2CBOARD_YIELD%2CDERIVE_BOARD_CODE&quoteColumns=f3~05~NEW_BOARD_CODE~BOARD_YIELD&filter=(SECUCODE%3D%22600138.SH%22)(IS_PRECISE%3D%221%22)&pageNumber=1&pageSize=&sortTypes=1&sortColumns=BOARD_RANK&source=HSF10&client=PC&v=0029565688091059528
@author: 1nchaos
@date: 2023/3/30 16:17
"""

import json
from urllib.parse import parse_qs

import pandas as pd

from adata.common import requests
from adata.common.headers import baidu_headers
from adata.stock.info.concept.stock_concept_template import StockConceptTemplate


class StockConceptBaidu(StockConceptTemplate):
"""
股票概念
"""

def __init__(self) -> None:
super().__init__()

def get_concept_baidu(self, stock_code='000001'):
"""
根据股票代码获取,股票所属的所有的概念信息
https://finance.pae.baidu.com/api/getrelatedblock?stock=[{"code":"300059","market":"ab","type":"stock"}]&finClientType=pc
:param stock_code: 股票代码
:return: 概念信息
"""
# 1. 请求参数封装
code_list = []
if isinstance(stock_code, str):
stock_code = [stock_code]
for code in stock_code:
code_list.append({"code": code, "market": "ab", "type": "stock"})
url = f"""https://finance.pae.baidu.com/api/getrelatedblock?stock={json.dumps(code_list)}&finClientType=pc"""
res_json = requests.request('get', url, headers=baidu_headers.json_headers, proxies={}).json()

# 1. 返回结果判断
if not res_json['Result']:
return pd.DataFrame(data=[], columns=self._CONCEPT_INFO_COLUMNS)

# 2. 正常返回数据结果封装
res_json = res_json['Result']
data = []
for key, value in res_json.items():
for concept_type in value:
if concept_type['name'] == '概念':
for _ in concept_type['list']:
data.append({'stock_code': key, 'concept_code': parse_qs(_['xcx_query']).get('code', '')[0],
'name': _['name'],
'reason': '', 'source': '百度股市通'})
result_df = pd.DataFrame(data=data, columns=self._CONCEPT_INFO_COLUMNS)
return result_df


if __name__ == '__main__':
print(StockConceptBaidu().get_concept_baidu(stock_code=["600020", '300059', '300033']).to_string())
10 changes: 5 additions & 5 deletions adata/stock/info/stock_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ def index_constituent(self, index_code=None, wait_time=None):
:param wait_time: 等待时间:毫秒;表示每个请求的间隔时间,主要用于防止请求太频繁的限制。
:return: ['index_code', 'stock_code', 'short_name']
"""
# res = self.__index_constituent_sina(index_code=index_code)
# if not res.empty:
# return res
res = self.__index_constituent_baidu(index_code=index_code)
if not res.empty:
return res
return self.__index_constituent_ths(index_code=index_code, wait_time=wait_time)

def __index_constituent_ths(self, index_code=None, wait_time=None):
Expand Down Expand Up @@ -245,5 +245,5 @@ def __index_constituent_sina(self, index_code=None, wait_time=None):

if __name__ == '__main__':
print(StockIndex().all_index_code())
print(StockIndex().index_constituent(index_code='000033'))
print(StockIndex().index_constituent(index_code='399387', wait_time=158))
# print(StockIndex().index_constituent(index_code='000033'))
# print(StockIndex().index_constituent(index_code='399387', wait_time=158))
2 changes: 1 addition & 1 deletion adata/stock/info/stock_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ def get_stock_shares(self, stock_code: str = '000033', is_history=True):


if __name__ == '__main__':
print(StockInfo().get_stock_shares(stock_code='600001', is_history=True))
print(StockInfo().get_stock_shares(stock_code='300033', is_history=True))
4 changes: 2 additions & 2 deletions adata/stock/market/capital_flow/stock_capital_flow_baidu.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def get_capital_flow(self, stock_code: str = '000001', start_date=None, end_date
url = f"https://finance.pae.baidu.com/vapi/v1/fundsortlist?" \
f"code={stock_code}&market=ab&finance_type=stock&tab=day&" \
f"from=history&date={end_date}&pn=0&rn=20&finClientType=pc"
res = requests.request('get', url, headers= baidu_headers.json_headers, proxies={})
res = requests.request('get', url, headers=baidu_headers.json_headers, proxies={})
data_list = res.json()["Result"]["content"]
if len(data_list) == 0:
break
Expand Down Expand Up @@ -102,4 +102,4 @@ def get_capital_flow(self, stock_code: str = '000001', start_date=None, end_date

if __name__ == '__main__':
print(StockCapitalFlowBaidu().get_capital_flow_min(stock_code='300059'))
print(StockCapitalFlowBaidu().get_capital_flow(stock_code='300059'))
print(StockCapitalFlowBaidu().get_capital_flow(stock_code='300059', start_date='2024-01-01', end_date='2024-04-01'))
5 changes: 3 additions & 2 deletions adata/stock/market/stock_market/stock_market_baidu.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ def get_market(self, stock_code: str = '000001', start_date='1990-01-01', k_type
result_df['stock_code'] = stock_code
result_df['trade_date'] = result_df['trade_time']
result_df['trade_time'] = pd.to_datetime(result_df['trade_time']).dt.strftime('%Y-%m-%d %H:%M:%S')
# 5. 数据清洗,剔除成交量为0的异常数据
# 5. 数据清洗,剔除成交量且成交额为0的异常数据
result_df.replace('--', None, inplace=True)
result_df.replace('', None, inplace=True)
result_df['amount'] = result_df['amount'].astype(float)
result_df = result_df[result_df['amount'] > 0]
result_df['volume'] = result_df['volume'].astype(float)
result_df = result_df[(result_df['amount'] > 0) | (result_df['volume'] > 0)]
result_df['change'] = result_df['change'].str.replace('+', '').astype(float)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '').astype(float)
return result_df
Expand Down
48 changes: 48 additions & 0 deletions tests/adata_test/sentiment/sentiment_hot_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import unittest

import adata


class SentimentHotTestCase(unittest.TestCase):

@classmethod
def setUpClass(cls) -> None:
print("----STAR执行舆情-热门-函数测试用例STAR----")

@classmethod
def tearDownClass(cls) -> None:
print("----END执行舆情-热门-函数测试用例END----")

def setUp(self):
pass

def tearDown(self):
pass

def test_pop_rank_100_east(self):
print("开始测试:pop_rank_100_east")
df = adata.sentiment.hot.pop_rank_100_east()
print(df)
self.assertEqual(True, len(df) == 100)

def test_hot_rank_100_ths(self):
print("开始测试:hot_rank_100_ths")
df = adata.sentiment.hot.hot_rank_100_ths()
print(df)
self.assertEqual(True, len(df) == 100)

def test_hot_concept_20_ths(self):
print("开始测试:hot_concept_20_ths")
df = adata.sentiment.hot.hot_concept_20_ths()
print(df)
self.assertEqual(True, len(df) == 20)

def test_list_a_list_daily(self):
print("开始测试:list_a_list_daily")
df = adata.sentiment.hot.list_a_list_daily(report_date='2024-07-04')
print(df)
self.assertEqual(True, len(df) >= 20)


if __name__ == '__main__':
unittest.main()
Loading