XQ-stock

import asyncio
import json
import os
import re
import time

import aiohttp
import requests
import pandas as pd


class Stock(object):
    def __init__(self, refresh_stock_pool: bool = False):
        """
        初始化
        :param refresh_stock_pool:  是否刷新股票池
        """
        self.headers = {
            'Authority': 'stock.xueqiu.com',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Origin': 'https://xueqiu.com',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Pragma': 'no-cache',
            'Sec-Fetch-Dest': 'document',
            'Sec-Fetch-Mode': 'navigate',
            'Sec-Fetch-Site': 'none',
            'Sec-Fetch-User': '?1',
            'Upgrade-Insecure-Requests': '1',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
            'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"macOS"',
        }
        # cookie可以写到redis中
        self.cookies = dict()
        # 初始化cookie
        self.set_cookie()
        self.stock_file = "./symbol_name.csv"
        if refresh_stock_pool or not os.path.exists(self.stock_file):
            self.get_all_stock_to_csv()

    def stp_t(self, timestamp: int) -> str:
        """
        时间戳转时间
        :param timestamp:
        :return:

        """
        # localtime /seconds
        timeArray = time.localtime(timestamp / 1000)
        otherStyleTime = time.strftime("%Y-%m-%d", timeArray)
        return otherStyleTime

    def set_cookie(self):
        def cookie1():
            response = requests.get('https://xueqiu.com/', headers=self.headers)
            return response.cookies.get("acw_tc")

        def cooke2():
            ck1 = cookie1()
            cookies = {
                'acw_tc': ck1,
            }
            params = {
                'md5__1038': 'QqGxcDnDyiitnD05o4+r=DRWxBWx9Al=Gn7oD',
            }

            response = requests.get('https://xueqiu.com/', params=params, cookies=cookies, headers=self.headers)
            print(response.cookies)
            return response

        ck = cooke2()
        self.cookies.update({'xq_a_token': ck.cookies.get("xq_a_token")})
        self.cookies.update({'u': ck.cookies.get("u")})
        self.cookies.update({'xq_r_token': ck.cookies.get("xq_r_token")})

        print(ck.cookies)

    def get_stock_detail(self, code: str):
        """
        获取股票详情
        :param code: SZ300911 股票代码
        :return:
        """
        params = {
            'symbol': code,
            'extend': 'detail',
        }
        response = requests.get('https://stock.xueqiu.com/v5/stock/quote.json', params=params, cookies=self.cookies,
                                headers=self.headers)
        print("code", response.status_code)
        if response.status_code != 200:
            self.set_cookie()
            response = requests.get('https://stock.xueqiu.com/v5/stock/quote.json', params=params, cookies=self.cookies,
                                    headers=self.headers)
        return response.text

    async def get_theme(self, code: str) -> str:
        """
        :param code: 600322.SH
        :return:
        """
        headers = {
            'Accept': '*/*',
            'Accept-Language': 'zh-CN,zh;q=0.9',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Origin': 'https://emweb.securities.eastmoney.com',
            'Pragma': 'no-cache',
            'Referer': 'https://emweb.securities.eastmoney.com/',
            'Sec-Fetch-Dest': 'empty',
            'Sec-Fetch-Mode': 'cors',
            'Sec-Fetch-Site': 'same-site',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
            'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"macOS"',
        }
        response = requests.get(
            'https://datacenter.eastmoney.com/securities/api/data/get?type=RPT_F10_CORETHEME_BOARDTYPE&sty=SECUCODE,SECURITY_CODE,SECURITY_NAME_ABBR,BOARD_CODE,BOARD_NAME,IS_PRECISE,BOARD_RANK,BOARD_TYPE&filter=(SECUCODE="%s")' % code,
            headers=headers,
        )
        result = json.loads(response.text)
        try:
            res = ""
            data = result.get("result").get("data")
            for d in data:
                res += d.get("BOARD_NAME") + ","
            return res
        except Exception as e:
            print(e)
            return ""

    def get_hs_rank_percent(self, page: str, size: str, order: str, orderby: str) -> list:
        """
        :param page: 页数
        :param size: 个数
        :param order: 排序的方式  desc  asc
        :param orderby: 排序的字段
        :return:
        """
        params = {
            'page': page,
            'size': size,
            'order': order,
            'orderby': orderby,
            'order_by': orderby,
            'market': 'CN',
            'type': 'sh_sz',
        }
        response = requests.get('https://stock.xueqiu.com/v5/stock/screener/quote/list.json', params=params,
                                cookies=self.cookies, headers=self.headers)
        if response.status_code != 200:
            self.set_cookie()
            response = requests.get('https://stock.xueqiu.com/v5/stock/screener/quote/list.json', params=params,
                                    cookies=self.cookies, headers=self.headers)
        all = []
        try:
            data = json.loads(response.text).get("data").get("list")
            for d in data:
                all.append(
                    {"name": d.get("name"), "code": d.get("symbol"), "现价": d.get("current"), "涨幅": d.get("percent"),
                     "市值/亿": float(d.get("market_capital")) / 10 ** 8, "成交额": d.get("amount"),
                     "成交量/股": d.get("volume"),
                     "流通股/股": d.get("float_shares"),
                     "成交量/流通量": d.get("volume") / d.get("float_shares") if d.get(
                         "float_shares") != 0 else "新股暂无流通量",
                     "换手率": d.get("turnover_rate"), "振幅": d.get("amplitude"),
                     "北向流入": d.get("north_net_inflow"),
                     "年初至今涨幅": d.get("current_year_percent"),
                     "上市至今涨幅": d.get("total_percent"),
                     })
        except Exception as e:
            print("", e)
        return all

    async def get_section_stock(self, symbol: str, begin: str, period: str, count: str, indicator: str):
        """
        获取数据
        :param symbol: SZ300813
        :param begin: 1669853231808
        :param period: period=day || week || month || quarter || year || 120m || 60m
        :param count:  -10 从开始向前的k线数量
        :param indicator: 指标 kline,pe,pb,ps,pcf,market_capital,agt,ggt,balance
        :return:
        """

        url = "https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol=%s&begin=%s&period=%s&type=before&count=%s&indicator=%s"

        response = requests.get(url % (symbol, begin, period, count, indicator), cookies=self.cookies,
                                headers=self.headers)
        if response.status_code != 200:
            self.set_cookie()
            response = requests.get(url % (symbol, begin, period, count, indicator), cookies=self.cookies,
                                    headers=self.headers)
        try:
            data = json.loads(response.text).get("data")
            col = data.get("column")
            item = data.get("item")
            if not item:
                return
            df = pd.DataFrame(item, columns=col)
            return df
        except Exception as e:
            print("", e)
        return pd.DataFrame

    def get_all_stock_to_csv(self, count: int = 5500):
        """
        初始化获取所有股票
        :param count:获取股票数量
        :return:
        """
        print("正在写入股票池")
        df_list = []
        for i in range(int(count / 50)):
            page = i + 1
            params = {
                'page': '%d' % page,
                'size': '50',
                'order': 'desc',
                'order_by': 'percent',
                'market': 'CN',
                'type': 'sh_sz',
            }
            response = requests.get(
                'https://stock.xueqiu.com/v5/stock/screener/quote/list.json',
                params=params,
                cookies=self.cookies,
                headers=self.headers,
            )
            data = json.loads(response.text)

            datas = data.get("data").get("list")
            if datas:
                for dt in datas:
                    sysbol = dt.get("symbol")
                    name = dt.get("name")
                    df_list.append([name, sysbol])
            else:
                break
        df = pd.DataFrame(df_list)
        df.to_csv(self.stock_file)
        print("写入股票池结束")

    async def stock_daily_strategy(self, name: str, code: str, data: pd.DataFrame, limit_up: int,
                                   energy_multiple: float, percent: float, amplitude: float,
                                   down: bool = False) -> dict:

        """
        数据清洗 可以自定义清洗
        :param name:  股票名称
        :param code:  股票代码
        :param data:  数据
        :param limit_up:  数量最少
        :param down:  是否跌停
        :param energy_multiple: 量能比最少
        :param percent: 涨幅限制
        :param amplitude: 振幅限制  负数则小于  正数则大于 绝对值
        :return:
        """
        # 是否爆量
        q_rate = data['volume'].max() / data['volume'].min()
        # 量能变化
        volume_chg = [i for i in data['volume'].values]
        # 换手率变化
        turnoverrate_chg = [i for i in data['turnoverrate'].values]
        # >=9.6个点涨幅
        if down:
            zt_data = {data['timestamp'][p[0]]: p[1] for p in data['percent'].items() if p[1] <= -5}
        else:
            zt_data = {data['timestamp'][p[0]]: p[1] for p in data['percent'].items() if p[1] >= 5}
        # 涨跌幅变化
        percent_chg = [i for i in data['percent'].values]

        # 振幅限制
        data["amplitude"] = (data["high"] - data["low"]) / data["open"]
        if amplitude > 0:
            amplitud_expectations = False
            percent_expectations = False
            for amp in data["amplitude"].items():
                # and abs(int(data["percent"][amp[0]][1])) <= amplitude * 100 / 2
                if amp[1] > amplitude:
                    amplitud_expectations = True
                if abs(int(data["percent"][amp[0]])) <= amplitude * 100 / 2:
                    percent_expectations = True
            if not (amplitud_expectations and percent_expectations):
                return
        # 几个交易日的总的涨跌幅度
        percent_sum = data['percent'].sum()
        if percent > 0:
            if percent_sum > percent:
                return
        if limit_up and energy_multiple:
            if limit_up < 0:
                if len(zt_data) == 0 and q_rate >= energy_multiple:
                    return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}
            else:
                if len(zt_data) >= limit_up and q_rate >= energy_multiple:
                    return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}
        elif limit_up:
            if limit_up < 0:
                if len(zt_data) == 0:
                    return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}
            else:
                if len(zt_data) >= limit_up:
                    return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}
        elif energy_multiple:
            if q_rate >= energy_multiple:
                return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}
        else:
            return {"name": name, "code": code, "涨跌幅变化": percent_chg, "总的涨跌幅": percent_sum}

    async def logic_name_symbol(self, name: str, symbol: str, trading_day: int, limit_up: int, energy_multiple: float,
                                percent: float, amplitude: float, down: bool = False):

        """
        :param trading_day: 几个交易日
        :param limit_up: 涨停数量最少
        :param energy_multiple: 量能比最少
        :param down:
        :return:
        """
        res_pd = await self.get_section_stock(symbol=symbol, begin=str(int(time.time() * 1000)), period='day',
                                              count='-' + str(trading_day),
                                              indicator='kline,pe,pb,ps,pcf,market_capital,agt,ggt,balance')
        res_pd["timestamp"] = res_pd["timestamp"].map(lambda timestamp: self.stp_t(timestamp))
        res = await self.stock_daily_strategy(name, symbol, res_pd, limit_up, energy_multiple, percent, amplitude, down)
        if res:
            # 获取核心题材
            code = symbol[2:] + "." + symbol[:2]
            theme = await self.get_theme(code)
            if "创业板综" not in theme:
                print(res)
                print(theme)


async def run():
    xueqiu = Stock()
    read_ = pd.read_csv(xueqiu.stock_file, 'r')
    tasks = []
    df = pd.DataFrame(read_)
    for data in df.values:
        data_list = data[0].split(",")
        name = data_list[1]
        symbol = data_list[2]
        task = asyncio.create_task(
            xueqiu.logic_name_symbol(name=name, symbol=symbol, trading_day=5, limit_up=1, energy_multiple=0, percent=0,
                                     amplitude=0, down=False))
        tasks.append(task)
    tasks = []
    await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(run())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值