0001宠物行业赛道企业筛选

在A股市场中,涉及宠物食品、医疗、用品等领域的上市公司,虽然没有完全专注于宠物产业的,但有一些企业直接参与相关行业,或者为这些行业提供产品、原材料或服务。以下是一些A股上市企业,及其在宠物相关赛道的布局:

1. 宠物食品相关公司

  • 佩蒂股份 (300673)
    佩蒂股份是国内宠物食品领域的领先企业,主要生产和销售宠物食品,涵盖狗粮、猫粮以及功能性宠物食品等。它主要出口欧美市场,国内市场也在快速扩展。
  • 中宠股份 (002891)
    中宠股份也是国内宠物食品的龙头企业之一,主营宠物食品的生产和销售,产品包括宠物主粮、宠物零食等。其产品出口多个国家,同时在国内也布局线上线下市场。

2. 宠物医疗相关公司

  • 瑞普生物 (300119)
    瑞普生物主营动物疫苗、兽药等,是中国动物保健领域的领军企业。尽管该公司以畜牧业为主要业务,但随着宠物医疗市场的快速发展,公司也在宠物疫苗和兽药市场有所布局。
  • 生物股份 (600201)
    生物股份是一家专注于动物疫苗的企业,虽然主要针对畜牧业,但其技术和研发能力也为宠物疫苗、宠物医疗保健品的开发提供了支持。
  • 普莱柯 (603566)
    普莱柯主要生产兽用疫苗、兽药及相关产品,其在宠物疾病防治方面有所布局。随着宠物医疗需求的增长,公司的兽医技术和产品可能在宠物领域获得进一步发展。

3. 宠物用品相关公司

  • 依依股份 (001206)
    依依股份是一家专注于宠物清洁用品的企业,生产和销售宠物尿垫、湿巾等清洁产品。它不仅为国内市场提供服务,还大量出口欧美国家,是宠物清洁用品赛道的代表企业之一。
  • 天元宠物 (301335)
    天元宠物主要从事宠物用品的设计、生产和销售,产品包括宠物床垫、宠物服装等宠物用品,出口市场较大。其产品线覆盖广泛,近年来在国内市场逐步发力。

4. 宠物服务、科技及电商平台相关公司

  • 芭田股份 (002170)
    芭田股份作为农业领域的一家企业,近年来通过并购和合作涉足宠物产业,尤其是宠物食品及宠物相关科技应用领域。
  • 创业慧康 (300451)
    创业慧康是一家医疗信息化企业,虽然主要服务于人类医疗行业,但随着宠物医疗信息化的需求增加,它的技术有可能应用于宠物医院的信息化管理系统中。

5. 原材料与配套服务相关公司

  • 海大集团 (002311)
    海大集团主要从事水产饲料、畜禽饲料等的生产。其丰富的饲料生产技术和供应链资源可以为宠物食品的生产提供原材料支持。
  • 大北农 (002385)
    大北农的主营业务是畜牧业和养殖业的饲料,但其技术和供应链在宠物食品领域也有扩展潜力,尤其是在高端宠物食品领域。
  • 通威股份 (600438)
    通威股份是水产饲料行业的龙头企业,同时也在涉足畜禽饲料,其饲料生产技术与研发能力可以为宠物食品的研发提供支持。

总结:

这些A股上市公司在宠物产业的不同细分领域中有所布局,尤其是宠物食品、医疗、用品以及相关的科技和服务。投资者可以根据具体的赛道选择对应的公司进行投资,或者关注这些企业未来在宠物产业链中的战略发展动向。此外,像瑞普生物、佩蒂股份等在宠物食品和医疗领域的布局较为成熟,是当前市场的主要参与者之一。

价值投资,构造我的交易理念

好久没有更新blog了,主要原因是最近在忙于搭建我的交易策略平台,我希望通过这个平台去记录我的每一次交易,让我的每一次交易有反思,当然更重要的是我希望在这个交易平台上可以快速筛选出优质的股票。

最近通过喜马拉雅的播客听了很多和股票交易相关的著作,比如:《股票大作手回忆录》、《海龟交易法则》、《交易心理分析》。 在听书的过程中我不断去反省自己的交易。

实际上我很早就掌握了基本交易原理,例如:短期均线上穿和下穿就是趋势的形成。但我很少去遵循交易原则,这是由于我没有构造自己的交易理念。现在我们来回顾一下我曾经的交易理念:
1.筹码方法论:如果今天收盘获利的人很少,那我就买入,因为如果明天还要继续跌,那散户会强烈买入,形成支撑位,所以价格不会大跌—–适用于牛市上涨行情

2.炒热门:如果一只股票涨停了,有大量的买入,那我也买入,因为所有的人都想要买它,这个方法的弊端是没有一只股票会持续涨停,你买入一只涨停的股票期待它后市继续涨停,其实更多靠的是运气。

3.各种技术指标:macd、kdj、RSI、boll等,杂乱的指标会让人不知道应该相信谁,因为指标与指标之间会有冲突,比如:macd金叉的时候,kdj死叉、或者boll突破上轨。

后来我反思了一下,我想要什么样的交易理念? 这个交易理念的念头要从和一位同事的沟通中说起,他持有很多只股票,我问他你持有这么多股票,看盘不会很累吗? 他说:他是按照假设自己有1亿资本用来炒股的时候,会怎么买卖股票来进行持仓的。这个点对我的感触影响特别大。

我进入股市有幻想过自己能够赚1000万,或者更多的钱,但是从来没有认真的去思考过,假设自己能赚到1000万时,自己的持仓是多少?自己又是怎么赚到这1000万的。

我理解股市交易有三类人:

1、散户:资金少、专业性差、情绪化严重,这种人大多数都必将是我们所谓的股市韭菜,因为他们太想赚钱了,以至于对自己的每一次操作都希望是正确的,而且当他明明已经操作错误之后还不愿意承认错误,作出改变

2、游资:这类人其实是股市上最大的蛀虫,我从一开始进入股市就很反感游资,但我竟然有段时间还去追着游资跑,结果亏的一塌糊涂,从我进入股市开始,我对游资的理解就是,利用其强大的资金优势,制造情绪,从而收割韭菜。当然游资虽然不能每次都收割,但是大多数时候它们都能成功收割,游资的进场让一只股票热度快速上升,吸引大量散户跟进,而游资的离场则是大量的抛压产生,底部买盘不足,一堆高位套牢的散户哑巴吃黄连有苦难言。

3、机构:这里的机构其实我们可以划分为两类,一类是以企业户头进行买入的私募、公募、国家基金等、另外一类是遵循价值投资的真正意义上的交易者,例如:巴菲特;当然个人交易者最终都会走向机构载体,这是因为一旦交易规模上去了,就必须要企业化运营。如果让一个人像摩根的某个户头那样同时管理2000多只股票,我相信他一定会疯的。

那么我希望自己成为哪类人呢?

我思来想去,我觉得我要成为第三类人,即:机构。

我应该像机构那样去思考,如何进行股市交易,尤其是假设我希望我能赚1000万,或者面对股市波动的时不会像散户那样有太多的情绪心理,影响自己的健康。

基于此,我想了一下,我的交易系统和理念应该是这样的:

从我手上有10亿资本开始,我会怎么交易呢?答案是:

1.我会把10亿拆分成75%的长期价值投资和25%的短线操作。长期价值投资是指买入一只股票后持股周期在6个月到N年以上,而短线操作则是买入一只股票后3天到3个月就抛出。

2.从长线投资来讲,如果我有7.5亿,我应该怎么买股票呢?我肯定不会把7.5亿拿来去博2-3只股票,因为这样我就会变成游资,我会把7.5亿拆成若干份,在不同的赛道分别买入1-2只具有高成长潜力的股票,进行长期持有,当他的ma60上穿ma120的时候,我买入,或者是在更早期买入,而在他的ma60下穿ma120的时候。则抛掉手中3/4的股票,等下一次 ma60上穿ma120的时候再进行买入。当然具体要用日线级还是周线级的趋势线就涉及到具体的操作了。

3.剩下的2.5亿,我则会根据短期均线数据来进行交易,例如:ma5完成ma10和ma20的上穿时进行买入,然后ma5下穿ma10的时候卖出。看上去这样的交易,如果是人工操作也会很费精力,因为在中国的股市,1000万的买入就可以把一只市值低于50亿的股票拉涨停,这样的话2.5亿足够我买25只股票进行短线交易,但很显然,我不想做游资。不想去拉抬股价,所以程序化的交易变成了,下一步要去推进的事情。

好的~ 我们就按照这样的思路去执行交易策略吧,看看一年以后会变成什么样?

博一博单车变摩托的贪欲

大盘是空头的打压市场的加速器

2024年8月20日,大盘下跌4591家,上涨669家,这对于庄家控盘打压股价助力强劲

如果有空头打压市场我们为什么要逆势而行呢?

2024年8月19日“清研环境”跌停被打开,现在回想起来其实就是空头诱多,当日也有可能是多头抗争,最终在午后盘抗争失败

这里其实可以反省思考一个问题:那就是资本是逐利的,如果有空头在打压市场,资本应该跟着资本去做打压,把股价压低到一个合适的价位买入,然后再拉升,而不是而不是去与空头对抗,因为散户的资本是源源无法斗争的过游资/机构的

下面我们来反思一下 清研环境这只股票 是怎么操作失误,亏的一塌胡涂的

2024年8月15日做T,卖出了8月14日买入的500股

原本应该再8月16日也做T把8月15日买入的1400股卖出的。如果是这样操作的话

按照24.16卖出,也能盈利(24.16-21.95)*1400=3090元

第二次的贪欲,2024年8月16日的时候买入的6600股(17.42),如果在8月19日卖出

以21.45的价格卖出也能获利(21.45-17.42)*6600=26598元

然而最大的失误却是没有坚守原则,即在股价下跌过程中要赶紧抛售头寸而非继续买入

KDJ指标已经空头排列:变成了 DKJ,而我却还在持续的大笔买入,妄想着能够一夜暴富

最终在今日,2024年8月20日形成大额亏损。

眼看着主力打压股价,净流入吸筹,自己却无能为力

现在让我们来反向模拟一下主力的行为,总的来说就是几个点:

1.负面消息刺激:参见公司股价严重异常波动公告,最恶心的是在8月19日跌停以后,8月20日故意在同花顺置顶一个网页链接“即重复股价严重异常波动公告”–出处仅仅是“中信证券订阅号”,发布时间是2024年8月20日 08:53分

2.打压多头,具体操作方式为,在卖5,卖10等位置挂大单卖出,在买5,买10等位置挂大单买入,根据市场的买卖价序列调整挂单位置,如果出现多头势力。则抛售打压多头,从而使得股价无法正向上涨。

3.拆单低价买入,例如:竞价阶段跌停价20%抛售—抛售额度不足10万,同时以跌停价19%买入,由于市场缺乏主力的对抗,必然导致股价的大幅跳空,引发恐慌性抛售情绪。从而实现低价吸筹的目的

交易的失败和失败的交易

人的执念是最大的敌人,因为大部分时候人的执念都不会朝着有利的方向前进,例如:我们说减肥是一个有益身心健康的事情,而此刻人的执念,只需要是坚信每天保持1000卡路里的运动热量消耗,就能获得健康的身体。

但99.99%的人都无法坚持下来,也就是说 这种反人类的行为,因为有实现难度通常都很难被坚持下去。

然而在另外一个极端却很容易铸就“恶”的执念成长,比如:在股票交易中的

  • “跌跌不休、加仓不止”;
  • “昨天亏损的,今天一定会回调盈利”;
  • “这只股票多头行情,已经来临了”
  • “这篇报道有利于多头”

如此这般,不言而语,期望是一种最人懊恼的奢侈品。它给人以幸福的期望,却又常常给人以最沉重的打击,重点是被打击的次数多了,反而麻烦了,遁入了一种无限循环的自虐症状。这是让人非常难以理解的。

例如 下面这只股票,我曾经在它身上损失惨重,然而我居然因为执念,觉得多头已经来临,市场即将反转,是时候买入了。可最后我依然是继续亏损。再一次割肉离场。

28.88入手/27.62清仓,4700股

总体亏损:4700*(28.88-27.62)=5900元…..

血的教训 真的是一次不够,还要再来一次! 直至你完全认识到自己的错误!!!

行情在哪里?

我是2024-06-25开通的两融账户,具备了做空资质。

从2024-06-25到今日2024-08-12 过去不到2个月的时间,融券市场从最早的每天有1300多只实时券可以借入到现在的 只有不到200只券可以借入做空

融券做空的券源少了,按理来说越少人做空,市场行情应该往多头趋势发展的。

然而实际的情况却依然还是空头走势

那么问题来了,是真的出了大问题了吗?

根本就没有人知道!!!

不遵循交易规则的人总会被打脸

在空头趋势中融券做空,这叫顺势而为

空头趋势结束,多头行情开始,应该反手做多,这也是顺势而为

然而有的人总是因为各种心理问题无法坚守交易规则,最终损失惨重(有可能是做空亏损惨重,也有可能是多头行情来临之后没有反向做多导致丧失盈利机会)

让我们来看两组经典的案例:

人福医药的合同在2024年7月9日到期,到期后融券的难度加大,市场上的券源越来越少。按照基本的常理,券源缩减,空头的做空资源缺失,必将迎来多头的反攻。

最后一次做空人福医药是在2024年7月8日从此之前几乎很难在借到券源头

在券源不断较少的趋势下,人福医药在2024年7月11日迎来了多头大反攻,从此之后夺取趋势开始一发不可收拾

从7月11日至今(8月5日)人福医药已经上涨27.68%

下面我们再来看另外一个例子:燕京啤酒

2024年7月5日结掉合约

结掉合约当日的交易:35000股

燕京啤酒的股价在2024年7月11日

也就是在我结束做空的4个交易日后,多空强势反攻,当日上涨5.63%,从这刻起“燕京啤酒”开始了不可逆的多头趋势。从2024年7月11日算,截止到目前2024年8月5日。这只股票已经上涨17.014%。如果是在2024年7月10日买入至今则上涨18.71%

股市交易特别需要:“心性”。从没有交易章法到寻找出一个高胜率的交易章法,再到坚持自己的章法原则,是一个非常困难且痛苦的过程。

当你真正做到的时候,股市交易就不再那么让人懊恼,自身的心态也会更平和

在这一条路上,我还有很长一段路要走。因为我们做交易更多的时候不是和市场博弈,而是和自己的人性,和他人的人性做博弈。

你需要克服/控制自己的:恐惧、贪婪、无知、执念

这里的执念特指:在明显的空头趋势行情下,你是做多一方,但却抛弃数据事实固执的认为行情”今天收盘前就会反转”。或者是一些特别的幼稚理念,例如:保持高胜率(我在天齐锂业和同庆楼这两只股票上,最大的败笔就是这个幼稚的“保持高胜率”执念了,因为在这两只股票之前我的交易胜率超过96%,我不想因为这两只股票拉低我的交易胜率)。

Tushare 日线数据的获取

通过以下代码你可以获取著名的金融数据平台Tushare的股票日线交易数据,存入数据表

代码的优势在于:

  • 1.可以自定义数据的获取的范围,主要通过Define time period的日期参数可调整实现
  • 2.可以获取自己想要的股票数据,主要通过数据表stock_basic来确定ts_code范围(这里附加的前提就是你从Tushare获取基础信息时要按照自己的需求过滤不需要的数据,例如:不考虑ST、和自己没有交易资格的北交所数据等)
  • 3.Rate limiting和time.sleep进一步增强了再数据获取时的请求限制,并避免了因代码逻辑不严谨导致的数据请求丢失
  • 4.同时代码还具有batch_size的分批请求和tqdm的数据请求进度,以及数据是否已经存在的校验判断Function to check if data already exists
import pandas as pd
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from tqdm import tqdm
import concurrent.futures
import tushare as ts
from threading import Semaphore, Timer
import time

# Initialize tushare
ts.set_token('your_token')
pro = ts.pro_api()

# Create database connection
engine = create_engine('mysql+pymysql://username:password@localhost:3306/stock')
Session = sessionmaker(bind=engine)
session = Session()

# Get stock list with list_date
query = "SELECT ts_code, list_date FROM stock.stock_basic"
stock_list = pd.read_sql(query, engine)

# Define time period
default_start_date = '20240802'
end_date = '20240802'

# Rate limiting
CALLS = 1500 # 每分钟最大请求次数
RATE_LIMIT = 60 # seconds
BATCH_SIZE = 1000 # 每次请求的股票代码数量

# Create a semaphore to limit the number of API calls
semaphore = Semaphore(CALLS)

# Function to periodically release the semaphore
def release_semaphore():
    for _ in range(CALLS):
        semaphore.release()

# Start timer to release semaphore every minute
Timer(RATE_LIMIT, release_semaphore, []).start()

# Function to check if data already exists
def data_exists(ts_code, trade_date):
    query = f"SELECT 1 FROM daily WHERE ts_code='{ts_code}' AND trade_date='{trade_date}' LIMIT 1"
    result = pd.read_sql(query, engine)
    return not result.empty

# Define retry decorator
def retry():
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(10):
                try:
                    result = func(*args, **kwargs)
                    return result
                except Exception as e:
                    print(f"Error occurred: {e}. Retrying...")
                    time.sleep(30)
            raise Exception("Maximum retries exceeded. Function failed.")
        return wrapper
    return decorator

# Define function to fetch and insert stock data
@retry()
def fetch_and_insert_stock_data(ts_code, list_date, end_date, engine):
    # Determine start_date based on list_date
    start_date = max(list_date.replace('-', ''), default_start_date)

    # Check if data already exists
    if data_exists(ts_code, end_date):
        return f"Data already exists for {ts_code} on {end_date}"

    # Wait on semaphore to respect rate limiting
    semaphore.acquire()
    try:
        # Fetch data from tushare
        stock_data = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date)

        # If data is not empty, insert it into the database
        if not stock_data.empty:
            # Map API fields to database fields
            stock_data.rename(columns={'vol': 'volume'}, inplace=True)
            stock_data.to_sql('daily', engine, if_exists='append', index=False, method='multi')
        return ts_code
    finally:
        # Release the semaphore after the operation
        semaphore.release()

# Initialize progress bar
total_stocks = len(stock_list)
pbar = tqdm(total=total_stocks, desc="Fetching stock data")

def process_batch(batch):
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = {
            executor.submit(fetch_and_insert_stock_data, row['ts_code'], row['list_date'], end_date, engine): row['ts_code']
            for index, row in batch.iterrows()
        }
        for future in concurrent.futures.as_completed(futures):
            ts_code = futures[future]
            try:
                data = future.result()
                pbar.update(1)
            except Exception as exc:
                print(f'{ts_code} generated an exception: {exc}')

# Process stocks in batches
batch_size = BATCH_SIZE
for i in range(0, total_stocks, batch_size):
    batch = stock_list.iloc[i:i + batch_size]
    process_batch(batch)
    time.sleep(60) # Wait to respect rate limits

# Close progress bar
pbar.close()

股市交易,学会使用数据进行市场行情研究和是否买入/卖出判断,要比通过有心人捏造并通过媒体报道的消息更具有参考意义。

前提是你要能看得懂“数据的内涵意义”!

综合MA/EMA/RSI以及成交量数据的股票趋势判断代码

最终呈现的效果

这个代码的优势是可以清楚的看到当前的短期动能EMA20比较MA20处于上涨趋势还是下跌趋势。针对这部分逻辑你可以尝试不同的指标来构造结果,例如用EMA20和EMA60 或者用EMA60和EMA120来做比较之类,具体的情况根据个人的数据敏感度偏好自由抉择。不同的指标最终会体现出不同的波动趋势可能性(短期与长期)

# 添加EMA>MA和EMA<MA的标记
ax1.fill_between(df['trade_date'], df['close'].min(), df['close'].max(),where=(df['EMA20'] > df['MA20']), color='green', alpha=0.1, label='Uptrend (EMA20 > MA20)')
ax1.fill_between(df['trade_date'], df['close'].min(), df['close'].max(),where=(df['EMA20'] < df['MA20']), color='red', alpha=0.1, label='Downtrend (EMA20 < MA20)')

通常情况下如果当前价格趋势处于红色色块,若没有特别重大的利好,带来多头的强势入场,大概率这只股票是要持续下跌的。

完整的代码如下

import pandas as pd
import matplotlib.pyplot as plt
from sqlalchemy import create_engine
import matplotlib.font_manager as fm
from matplotlib.widgets import Button
import ta  # 导入ta库

# 数据库连接设置
engine = create_engine('mysql+pymysql://username:password@localhost:3306/stock')

current_index = 0  # 当前显示的股票索引

def fetch_stock_data(ts_code):
    # 查询股票数据
    query = f"SELECT * FROM stock.daily WHERE ts_code = '{ts_code}'"
    df = pd.read_sql(query, engine)
    # 转换日期格式
    df['trade_date'] = pd.to_datetime(df['trade_date'])
    # 按照日期排序
    df = df.sort_values(by='trade_date')
    return df

def fetch_stock_name(ts_code):
    # 查询股票名称
    query = f"SELECT ts_code, name FROM stock.stock_basic WHERE ts_code = '{ts_code}'"
    df = pd.read_sql(query, engine)
    if not df.empty:
        return df['name'].values[0]
    else:
        return "Unknown"

def fetch_all_ts_codes():
    query = "SELECT DISTINCT ts_code FROM stock.daily"
    df = pd.read_sql(query, engine)
    return df['ts_code'].tolist()

def fetch_defined_ts_codes():
    query = "SELECT DISTINCT ts_code FROM stock.ambush_stock"
    df = pd.read_sql(query, engine)
    return df['ts_code'].tolist()

# 计算RSI
def calculate_rsi(data, window):
    rsi = ta.momentum.RSIIndicator(close=data, window=window)
    return rsi.rsi()

def plot_stock_data(df, stock_name, ts_code):
    plt.style.use('seaborn-v0_8-whitegrid')  # 设置图表样式
    global fig, ax1, ax2, ax3, check_buttons, lines, text_objects  # 定义全局变量

    ax1.clear()
    ax2.clear()
    ax3.clear()

    # 使用 ta 库计算移动平均线和 EMA
    df['MA5'] = ta.trend.SMAIndicator(df['close'], window=5).sma_indicator()
    df['EMA5'] = ta.trend.EMAIndicator(df['close'], window=5).ema_indicator()
    df['MA20'] = ta.trend.SMAIndicator(df['close'], window=20).sma_indicator()
    df['EMA20'] = ta.trend.EMAIndicator(df['close'], window=20).ema_indicator()
    df['MA60'] = ta.trend.SMAIndicator(df['close'], window=60).sma_indicator()
    df['EMA60'] = ta.trend.EMAIndicator(df['close'], window=60).ema_indicator()

    df['RSI'] = calculate_rsi(df['close'], 14)

    # 计算成交量移动平均线
    df['VOL_MA5'] = ta.trend.SMAIndicator(df['volume'], window=5).sma_indicator()
    df['VOL_MA10'] = ta.trend.SMAIndicator(df['volume'], window=10).sma_indicator()

    # 设置字体以支持中文显示
    font_path = '/System/Library/Fonts/STHeiti Medium.ttc'  # 确认字体路径
    prop = fm.FontProperties(fname=font_path)
    plt.rcParams['font.sans-serif'] = prop.get_name()
    plt.rcParams['axes.unicode_minus'] = False

    # 第一张子图:Close Price 和 MA
    l1, = ax1.plot(df['trade_date'], df['close'], label='Close Price', color='black')
    l2, = ax1.plot(df['trade_date'], df['MA5'], label='MA5', color='blue')
    l3, = ax1.plot(df['trade_date'], df['EMA5'], label='EMA5', color='cyan')
    l4, = ax1.plot(df['trade_date'], df['MA20'], label='MA20', color='green')
    l5, = ax1.plot(df['trade_date'], df['EMA20'], label='EMA20', color='lime')
    l6, = ax1.plot(df['trade_date'], df['MA60'], label='MA60', color='red')
    l7, = ax1.plot(df['trade_date'], df['EMA60'], label='EMA60', color='magenta')
    ax1.set_ylabel('Price', fontsize=12)
    ax1.set_title(f'Stock Price and Moving Averages - {stock_name}', fontsize=14)
    ax1.legend(fontsize=10, bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, frameon=False)

    # 添加 EMA > MA 和 EMA < MA 的标记
    ax1.fill_between(df['trade_date'], df['close'].min(), df['close'].max(),
                     where=(df['EMA20'] > df['MA20']), color='green', alpha=0.1, label='Uptrend (EMA20 > MA20)')
    ax1.fill_between(df['trade_date'], df['close'].min(), df['close'].max(),
                     where=(df['EMA20'] < df['MA20']), color='red', alpha=0.1, label='Downtrend (EMA20 < MA20)')

    # 第二张子图:RSI
    l8, = ax2.plot(df['trade_date'], df['RSI'], label='RSI', color='blue')
    ax2.axhline(70, color='red', linestyle='--')
    ax2.axhline(30, color='green', linestyle='--')
    ax2.set_ylabel('RSI', fontsize=12)
    ax2.set_title(f'Relative Strength Index - {ts_code}', fontsize=14)
    ax2.legend(fontsize=10, loc='lower right', borderaxespad=0, frameon=False)

    # 第三张子图:成交量柱状图和成交量MA
    l9 = ax3.bar(df['trade_date'], df['volume'], label='volume', color='lightgray')
    l10, = ax3.plot(df['trade_date'], df['VOL_MA5'], label='MA5', color='blue')
    l11, = ax3.plot(df['trade_date'], df['VOL_MA10'], label='MA10', color='orange')
    ax3.set_xlabel('Date', fontsize=12)
    ax3.set_ylabel('volume', fontsize=12)
    ax3.set_title(f'volume and Moving Averages', fontsize=14)
    ax3.legend(fontsize=10, bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, frameon=False)

    # 清除旧的CheckButtons和文本对象
    if 'check_buttons' in globals():
        for btn in check_buttons:
            btn.ax.remove()
        check_buttons.clear()

    if 'text_objects' in globals():
        for txt in text_objects:
            txt.remove()
        text_objects.clear()

    # 添加CheckButtons控件
    labels = ['Close Price', 'MA5', 'EMA5', 'MA20', 'EMA20', 'MA60', 'EMA60', 'RSI', 'MA5 volume', 'MA10 volume']
    lines = [l1, l2, l3, l4, l5, l6, l7, l8, l10, l11]
    visibility = [True] * len(lines)

    check_buttons = []
    for i, label in enumerate(labels):
        ax_check = plt.axes([0.05 + i * 0.09, 0.93, 0.08, 0.05])
        button = Button(ax_check, label, color='lightgrey', hovercolor='grey')
        button.on_clicked(lambda event, index=i: toggle_visibility(index))
        check_buttons.append(button)

    # 添加文本对象以显示指标值
    text_objects = []
    for i, label in enumerate(labels):
        ax_text = plt.axes([0.05 + i * 0.09, 0.88, 0.08, 0.04], facecolor='white')  # 设置背景色为白色
        ax_text.axis('off')  # 隐藏坐标轴
        text = ax_text.text(0.5, 0.5, "", transform=ax_text.transAxes, ha="center", va="center", fontsize=8)
        text_objects.append(text)

    def toggle_visibility(index):
        lines[index].set_visible(not lines[index].get_visible())
        update_indicators_text()
        plt.draw()

    def update_indicators_text():
        for i, line in enumerate(lines):
            if line.get_visible():
                ydata = line.get_ydata()
                if len(ydata) > 0:
                    text_objects[i].set_text(f"{ydata[-1]:.2f}")
                else:
                    text_objects[i].set_text("")
            else:
                text_objects[i].set_text("")

    update_indicators_text()

    plt.draw()  # 更新图表


# 更新图表数据
def update_plot():
    global current_index
    ts_code = all_ts_codes[current_index]
    df = fetch_stock_data(ts_code)
    stock_name = fetch_stock_name(ts_code)
    plot_stock_data(df, stock_name, ts_code)

# 获取所有ts_code
all_ts_codes = fetch_all_ts_codes()

# 通过SQL查询动态获取股票代码
#defined_ts_codes = fetch_defined_ts_codes()
defined_ts_codes = ['002138.SZ', '603017.SH']
# 过滤所有股票代码,只保留defined_ts_codes中的代码
all_ts_codes = [code for code in all_ts_codes if code in defined_ts_codes]

def plot_next_stock(event=None):
    global current_index
    ts_code = all_ts_codes[current_index]
    df = fetch_stock_data(ts_code)
    stock_name = fetch_stock_name(ts_code)
    plot_stock_data(df, stock_name, ts_code)

# 初始化图表和按钮
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15.12, 8.55), sharex=True)  # 设置图表大小
fig.subplots_adjust(hspace=0.3, right=0.85, top=0.85, bottom=0.15, left=0.06)  # 设置子图间距和边距

# 添加上一个和下一个按钮
axprev = plt.axes([0.6, 0.01, 0.1, 0.05])  # 调整按钮位置
axnext = plt.axes([0.71, 0.01, 0.1, 0.05])  # 调整按钮位置
bnext = Button(axnext, 'Next')
bprev = Button(axprev, 'Previous')

def next(event):
    global current_index
    current_index += 1
    if current_index >= len(all_ts_codes):
        current_index = 0
    update_plot()

def prev(event):
    global current_index
    current_index -= 1
    if current_index < 0:
        current_index = len(all_ts_codes) - 1
    update_plot()

bnext.on_clicked(next)
bprev.on_clicked(prev)

# 显示第一个股票的图表
plot_next_stock()
plt.show()

针对代码需要改进的点以及需要的数据支持

  • 1.需要从https://www.tushare.pro/ 先获取到日线数据存入自己的数据表
  • 2.针对代码关于字体路径的部分,我是mac电脑做了特殊处理,Windows系统用户可根据自己的实际情况进行调节
  • 3.defined_ts_codes部分定义支持数据表全量ts_code数据计算和展示“此方式比较耗费资源,且在预览切换过程中股票数量太多会有很多无效的数据浪费时间”,也可以手工定义多个ts_code聚焦到自己关注的那一小部分股票,从而提升数据判断效率

入市以来最大的单股亏损

不得不说在我保持盈利模式之前,更多靠的是运气,因为那时的我还不知道什么叫技术指标,分不清什么叫空头趋势,什么叫多头趋势,全仰仗自己的简单认知:

我的筹码价格比大多数人低,我的亏损概率就小/亏损幅度就小

然而当事实啪啪打脸的时候,你再也无法坚信自己的认知是正确的,你开始怀疑,开始疯狂的加仓减少损失,然而这种缺乏止损理念的行为却只会让你越陷越深,亏损越来越大。

曾经的简单可靠盈利模型

昨日收盘价低于90%持仓成本的次日买入股票
买入当日股价大涨,次日逢高抛售

踩在市场行情趋势上【多头】,傻子都能有很大概率盈利!

大盘当日也大涨(因为无知,以为自己买入”中金公司”盈利的原因,自己是‘股神’)

当市场行情的趋势退去,误把运气当“神技”的人总会被市场狠狠的教训

周线EMA5<EMA10<EMA60,同时收盘价也明显的低于90%持仓成本【强烈买入信号】
越跌越加仓,期望以此来降低持股成本,在上涨行情时赶紧跑路或大赚一笔
最终的结果是实在扛不住了,割肉以后“空头行情”依然没停,继续下跌4周,才开始横盘

这堂课的学费不便宜,交了20万!

从这一刻起我开始反思,开始学习,学会了空头排列、多头排列、简单移动平均线(SMA)、指数移动平均线(EMA)、MACD、KDJ、RSI、Boll、涨跌(放缩量)

也许要花很多时间才能把亏损赚回来,也许再也赚不回来了(最坏的结果),但至少此时此刻我知道自己的交易失败是因为什么! 以后再也不会犯这样的错(或者极少会再犯这样的错)