Skip to content

Commit

Permalink
Merge pull request #7 from 1nchaos/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
1nchaos authored Jun 19, 2023
2 parents 221854d + 6d9fd49 commit d8dc7a3
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 165 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
>
> 专注股票行情数据,为了保证数据的高可用性,采用多数据源融合切换。
>
> 目标:支持个人量化行情的需要;众人拾柴火焰高。
> 目标:支持个人量化行情的需要;众人拾柴火焰高,欢迎加入
## 一、快速开始

Expand Down Expand Up @@ -130,7 +130,7 @@ print(res_df)
| 分红信息 | stock.market.get_dividend() | 获取单只股票的分红信息 | |
| 股票行情 | stock.market.get_market() | 获取单只股票的行情信息-日、周、月 k线 | |
| | stock.market.get_market_min() | 获取单个股票的今日分时行情 | 只能获取当天 |
| | stock.market.list_market_current() | 获取多个股票最新行情信息 | 实时行情 |
| | stock.market.list_market_current() | 获取多个股票最新行情信息 | 实时行情<br />数据源:2个,源新浪和腾讯 |
| 概念行情 | stock.market.get_market_concept_ths() | 获取单个概念的行情信息-日、周、月 k线 | 目前只有同花顺相关概念行情,<br />获取概念行情时,<br />请注意传入参数是指数代码还是概念代码,<br />指数代码8开头,index_code |
| | stock.market.get_market_concept_min_ths() | 获取同花顺概念行情-当日分时 | 只能获取当天 |
| | stock.market.get_market_concept_current_ths() | 获取同花顺当前的概念行情 | 实时行情 |
Expand Down Expand Up @@ -168,15 +168,16 @@ print(res_df)
| 百度股市通 | [股市通](https://gushitong.baidu.com/) | 科技让投资更简单 |
| 东方财富 | [数据中心](https://data.eastmoney.com/center/)[行情中心](http://quote.eastmoney.com/center/) | 财经门户 |
| 腾讯理财 | [行情中心](https://stockapp.finance.qq.com/mstats/#) | |
| 新浪财经 | [新浪财经](https://finance.sina.com.cn/stock/) | 门户网站 |

***--------------------------------------------------------------感谢各位大厂提供的数据-------------------------------------------------***
***--------------------------------------------感谢各位大厂提供的数据----------------------------------------------***

## 四、 其它参考

主要记录查阅过的项目和相关平台,并对此项目产生了深远印象,特此鸣谢。

| [akshare](https://gitee.com/mirrors/akshare) | [聚宽量化](https://www.joinquant.com/) | [baostock](http://baostock.com/baostock/index.php/Python%E5%BC%80%E5%8F%91%E8%B5%84%E6%BA%90) | [MyData](http://api.mairui.club/hsdata.html) | |
| -------------------------------------------- | -------------------------------------- | ------------------------------------------------------------ | -------------------------------------------- | ---- |
| [akshare](https://gitee.com/mirrors/akshare) | [聚宽量化](https://www.joinquant.com/) | [baostock](http://baostock.com/baostock/index.php/Python%E5%BC%80%E5%8F%91%E8%B5%84%E6%BA%90) | [MyData](http://api.mairui.club/hsdata.html) |
| -------------------------------------------- | -------------------------------------- | ------------------------------------------------------------ | -------------------------------------------- |

## 五、发布计划

Expand Down
4 changes: 2 additions & 2 deletions adata/__version__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

VERSION = (0, 0, 23)
VERSION = (0, 0, 24)
# PRERELEASE = None # alpha, beta or rc
PRERELEASE = 'beta' # alpha, beta or rc
REVISION = None
Expand All @@ -15,7 +15,7 @@ def generate_version(version, prerelease=None, revision=None):
return "".join(version_parts)

__title__ = "adata"
__description__ = "A Data,A Stock,ETF,Bond,Quant"
__description__ = "A Data,A Stock,ETF,Bond,Quant,Stock Market,K Line"
__url__ = "https://github.com/1nchaos/adata"
__version__ = generate_version(VERSION, prerelease=PRERELEASE, revision=REVISION)
__author__ = "1nchaos"
Expand Down
4 changes: 2 additions & 2 deletions adata/common/utils/cookie.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from py_mini_racer import py_mini_racer


def ths_cookie():
def ths_cookie(js_path="ths.js"):
"""获取同花顺cookie"""
js_code = py_mini_racer.MiniRacer()
js_content = _get_file_content_ths(file_path="ths.js")
js_content = _get_file_content_ths(file_path=js_path)
js_code.eval(js_content)
return 'v=' + js_code.call("v")

Expand Down
1 change: 1 addition & 0 deletions adata/stock/market/stock_market/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .stock_market import StockMarket
from .stock_market_qq import StockMarketQQ
from .stock_market_sina import StockMarketSina
from .stock_market_baidu import StockMarketBaiDu
154 changes: 20 additions & 134 deletions adata/stock/market/stock_market/stock_market.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
# -*- coding: utf-8 -*-
"""
@desc: readme
@desc: 股票行情
@author: 1nchaos
@time: 2023/3/29
@log: change log
TODO 数据返回类型转换
"""

import time

import pandas as pd

from adata.common.headers import baidu_headers
from adata.common.utils import requests
from adata.stock.market.stock_market import *


class StockMarket(object):
"""
股票行情 TODO 数据返回类型转换
股票行情
"""
__MARKET_COLUMNS = ['trade_time', 'open', 'close', 'volume', 'high', 'low', 'amount', 'change', 'change_pct',
'turnover_ratio', 'pre_close']
__MARKET_MIN_COLUMNS = ['stock_code', 'trade_time', 'price', 'change', 'change_pct', 'volume', 'avg_price',
'amount']
__MARKET_CURRENT_COLUMNS = ['stock_code', 'short_name', 'price', 'change', 'change_pct', 'volume', 'amount']

def __init__(self) -> None:
super().__init__()
Expand All @@ -37,146 +29,40 @@ def get_market(self, stock_code: str = '000001', start_date='1990-01-01', k_type
:param adjust_type: k线复权类型:0.不复权;1.前复权;2.后复权 默认:1 前复权 (目前:只有前复权,作为股票交易已经可用)
:return: k线行情数据
"""
return self.__market_baidu(stock_code, start_date, k_type)
return StockMarketBaiDu().get_market(stock_code=stock_code, start_date=start_date, k_type=k_type)

def get_market_min(self, stock_code: str = '000001'):
"""
获取单个股票的今日分时行情
:param stock_code: 股票代码
:return: 当日分钟行情数据
"""
return self.__market_baidu_today_min(stock_code)
return StockMarketBaiDu().get_market_min(stock_code=stock_code)

def list_market_current(self, code_list=None):
"""
获取多个股票最新行情信息
:param code_list: 股票代码
:return: 当前最新的行情价格信息
stock_code: 股票代码
short_name: 股票简称
price: 当前价格(元)
change: 涨跌额(元)
change_pct: 涨跌幅(%)
volume: 成交量(股)
amount: 成交金额(元)
"""
if code_list is None:
return pd.DataFrame()
return StockMarketSina().list_market_current(code_list=code_list)

def __market_baidu(self, stock_code, start_date, k_type=1):
"""
获取百度的股票行情数据
web: https://gushitong.baidu.com/stock/ab-002926
url:quotation_fiveday_ab 5日分时,quotation_kline_ab K线, quotation_minute_ab 当日分钟
k线
https://finance.pae.baidu.com/selfselect/getstockquotation?all=1&isIndex=false&isBk=false&isBlock=false&
isFutures=false&isStock=true&newFormat=1&group=quotation_kline_ab&finClientType=pc&
code=002926&start_time=2018-02-05 00:00:00&ktype=1
分钟
https://finance.pae.baidu.com/selfselect/getstockquotation?
all=1&code=601318&isIndex=false&isBk=false&isBlock=false&isFutures=false&isStock=true&newFormat=1&
group=quotation_minute_ab&finClientType=pc
"ma5均价","ma5成交量","ma10均价","ma10成交量","ma20均价","ma20成交量"
:param stock_code: 6位股票代码
:param start_date: 开始时间
:param k_type: k线类型:1.日;2.周;3.月
# :param adjust_type: k线复权类型:0.不复权;1.前复权;2.后复权 默认:1 前复权 TODO
:return: k线行情数据:"时间戳", "时间","开盘","收盘","成交量","最高","最低","成交额","涨跌额","涨跌幅","换手率","昨收"
"""
# 1. 请求接口 url
api_url = f"https://finance.pae.baidu.com/selfselect/getstockquotation?all=1&isIndex=false&isBk=false&" \
f"isBlock=false&isFutures=false&isStock=true&newFormat=1&group=quotation_kline_ab&finClientType=pc&" \
f"code={stock_code}&start_time={start_date} 00:00:00&ktype={k_type}"

res_json = None
for i in range(3):
res = requests.request('get', api_url, headers=baidu_headers.json_headers, proxies={})
# 2. 校验请求结果数据
res_json = res.json()
if res_json['ResultCode'] == '0':
break
time.sleep(2)
# 3.解析数据
# 3.1 空数据时返回为空
result = res_json['Result']
if not result:
return pd.DataFrame(data=[], columns=self.__MARKET_COLUMNS)

# 3.2. 正常解析数据
keys = res_json['Result']['newMarketData']['keys']
market_data = res_json['Result']['newMarketData']['marketData']
market_data_list = str(market_data).split(';')
data = []
for one in market_data_list:
data.append(one.split(','))

# 4. 封装数据
rename_columns = {'turnoverratio': 'turnover_ratio', 'preClose': 'pre_close', 'range': 'change',
'ratio': 'change_pct', 'time': 'trade_time'}
result_df = pd.DataFrame(data=data, columns=keys).rename(columns=rename_columns)[self.__MARKET_COLUMNS]
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')
result_df.replace('--', None, inplace=True)
result_df.replace('', None, inplace=True)
result_df['change'] = result_df['change'].str.replace('+', '', regex=True)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True)
# 5. 数据清洗,剔除成交量为0的异常数据
result_df['amount'] = result_df['amount'].astype(float)
result_df = result_df[result_df['amount'] > 0]
result_df.replace('--', None, inplace=True)
result_df.replace('', None, inplace=True)
result_df['change'] = result_df['change'].str.replace('+', '', regex=True).astype(float)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True).astype(float)
return result_df

def __market_baidu_today_min(self, stock_code):
"""
获取百度的股票行情数据
web: https://gushitong.baidu.com/stock/ab-002926
url: https://finance.pae.baidu.com/selfselect/getstockquotation?
all=1&code=601318&isIndex=false&isBk=false&isBlock=false&isFutures=false&isStock=true&newFormat=1
&group=quotation_minute_ab&finClientType=pc
time, price, ratio, increase, volume, avgPrice, amount, timeKey, datetime, oriAmount
:param stock_code: 6位股票代码
:return: k线行情数据:"时间","价格","涨跌率","涨幅","均价","成交量", "成交额"
"""
# 1. 请求接口 url
api_url = f"https://finance.pae.baidu.com/selfselect/getstockquotation?all=1&isIndex=false&isBk=false&" \
f"isBlock=false&isFutures=false&isStock=true&newFormat=1&group=quotation_minute_ab&" \
f"finClientType=pc&code={stock_code}"

res_json = None
for i in range(3):
res = requests.request('get', api_url, headers=baidu_headers.json_headers, proxies={})
# 2. 校验请求结果数据
res_json = res.json()
if res_json['ResultCode'] == '0':
break
time.sleep(2)
# 3.解析数据
# 3.1 空数据时返回为空
result = res_json['Result']
if not result:
return pd.DataFrame(data=[], columns=self.__MARKET_MIN_COLUMNS)

# 3.2. 正常解析数据
market_data_list = res_json['Result']['priceinfo']

# 4. 封装数据
field = ['time', 'price', 'ratio', 'increase', 'volume', 'avgPrice', 'amount', 'timeKey', 'datetime',
'oriAmount']
rename_columns = {'avgPrice': 'avg_price', 'oriAmount': 'ori_amount', 'ratio': 'change_pct',
'increase': 'change'}
result_df = pd.DataFrame(data=market_data_list, columns=field).rename(columns=rename_columns)
result_df['amount'] = result_df['ori_amount']
result_df['stock_code'] = stock_code
# 这里是分钟均价,数据存在四舍五入的情况
result_df['volume'] = result_df['volume'].astype(int) * 100
result_df['trade_time'] = pd.to_datetime(result_df['time'], unit='s', utc=True).dt.tz_convert(
'Asia/Shanghai')
result_df['trade_time'] = pd.to_datetime(result_df['trade_time']).dt.strftime("%Y-%m-%d %H:%M:%S")
result_df['trade_date'] = result_df['trade_time'].str[:10]
result_df['change'] = result_df['change'].str.replace('+', '', regex=True).astype(float)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True).astype(float)
return result_df[self.__MARKET_MIN_COLUMNS]
# 1. 先查询新浪
df = StockMarketSina().list_market_current(code_list=code_list)
# 2. 然后腾讯
if df.empty:
df = StockMarketQQ().list_market_current(code_list=code_list)
return df


if __name__ == '__main__':
# print(StockMarket().get_market(stock_code='000001', start_date='2021-01-01', k_type=1))
# print(StockMarket().get_market_min(stock_code='000001'))
print(StockMarket().get_market(stock_code='000001', start_date='2021-01-01', k_type=1))
print(StockMarket().get_market_min(stock_code='000001'))
print(StockMarket().list_market_current(code_list=['000001', '600001', '000795', '872925']))
20 changes: 6 additions & 14 deletions adata/stock/market/stock_market/stock_market_baidu.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from adata.common.headers import baidu_headers
from adata.common.utils import requests
from stock_market_template import StockMarketTemplate
from adata.stock.market.stock_market.stock_market_template import StockMarketTemplate


class StockMarketBaiDu(StockMarketTemplate):
Expand All @@ -25,14 +25,6 @@ class StockMarketBaiDu(StockMarketTemplate):
def __init__(self) -> None:
super().__init__()

def get_market_min(self, stock_code: str = '000001'):
"""
获取单个股票的今日分时行情
:param stock_code: 股票代码
:return: 当日分钟行情数据
"""
return self.__market_baidu_today_min(stock_code)

def get_market(self, stock_code: str = '000001', start_date='1990-01-01', k_type=1, adjust_type: int = 1):
"""
获取百度的股票行情数据
Expand Down Expand Up @@ -100,7 +92,7 @@ def get_market(self, stock_code: str = '000001', start_date='1990-01-01', k_type
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True).astype(float)
return result_df

def __market_baidu_today_min(self, stock_code):
def get_market_min(self, stock_code: str = '000001'):
"""
获取百度的股票行情数据
web: https://gushitong.baidu.com/stock/ab-002926
Expand Down Expand Up @@ -143,15 +135,15 @@ def __market_baidu_today_min(self, stock_code):
result_df['stock_code'] = stock_code
# 这里是分钟均价,数据存在四舍五入的情况
result_df['volume'] = result_df['volume'].astype(int) * 100
result_df['trade_time'] = pd.to_datetime(result_df['time'], unit='s', utc=True).dt.tz_convert(
'Asia/Shanghai')
result_df['trade_time'] = pd.to_datetime(result_df['time'], unit='s', utc=True).dt.tz_convert('Asia/Shanghai')
result_df['trade_time'] = pd.to_datetime(result_df['trade_time']).dt.strftime("%Y-%m-%d %H:%M:%S")
result_df['trade_date'] = result_df['trade_time'].str[:10]
result_df['change'] = result_df['change'].str.replace('+', '', regex=True).astype(float)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True).astype(float)
result_df['change_pct'] = result_df['change_pct'].str.replace('+', '', regex=True) \
.str.replace('%', '', regex=True).astype(float)
return result_df[self._MARKET_MIN_COLUMNS]


if __name__ == '__main__':
print(StockMarketBaiDu().get_market(stock_code='000001', start_date='2021-01-01', k_type=1))
# print(StockMarketBaiDu().get_market_min(stock_code='000001'))
print(StockMarketBaiDu().get_market_min(stock_code='000001'))
39 changes: 38 additions & 1 deletion adata/stock/market/stock_market/stock_market_qq.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
@time: 2023/6/19
@log: change log
"""
import pandas as pd

from .stock_market_template import StockMarketTemplate
from adata.common import requests
from adata.stock.market.stock_market.stock_market_template import StockMarketTemplate


class StockMarketQQ(StockMarketTemplate):
Expand All @@ -26,6 +28,41 @@ def list_market_current(self, code_list=None):
:param code_list: 股票代码
:return: 当前最新的行情价格信息
"""
api_url = f"https://qt.gtimg.cn/r=0.5979076524724433&q="
for code in code_list:
if code.startswith('0') or code.startswith('3'):
api_url += 's_sz' + code + ','
elif code.startswith('6') or code.startswith('9'):
api_url += 's_sh' + code + ','
elif code.startswith('4') or code.startswith('8'):
api_url += 's_bj' + code + ','

# 1.请求接口
res = requests.request('get', api_url, headers={})

# 2. 判断结果是否正确
if len(res.text) < 1 or res.status_code != 200:
return pd.DataFrame(data=[], columns=self._MARKET_CURRENT_COLUMNS)
# 3.解析数据

# 正常解析数据 v_s_sz000936="51~华西股份~000936~12.60~1.15~10.04~69137~8711~~111.64~GP-A";
data_list = res.text.split(';')
data = []
for data_str in data_list:
if len(data_str) < 8:
continue
code = data_str.split('~')
if len(code) == 11:
data.append(code[1:8])

# 4. 封装数据
data_columns = ['short_name', 'stock_code', 'price', 'change', 'change_pct', 'volume', 'amount']
result_df = pd.DataFrame(data=data, columns=data_columns)
# 单位:手,万元
mask = result_df['stock_code'].str.startswith(('0', '3', '6', '9'))
result_df.loc[mask, 'volume'] = result_df['volume'].astype(int) * 100
result_df.loc[mask, 'amount'] = result_df['amount'].astype(float) * 10000
return result_df[self._MARKET_CURRENT_COLUMNS]


if __name__ == '__main__':
Expand Down
3 changes: 2 additions & 1 deletion adata/stock/market/stock_market/stock_market_sina.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@

from adata.common.headers import sina_headers
from adata.common.utils import requests
from .stock_market_template import StockMarketTemplate
from adata.stock.market.stock_market.stock_market_template import StockMarketTemplate


class StockMarketSina(StockMarketTemplate):
"""
新浪股票行情
"""

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

Expand Down
Loading

0 comments on commit d8dc7a3

Please sign in to comment.