用Python选一个自己的股票池!堪比资深的炒股选手!_python形态选股

一、Python所有方向的学习路线

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

二、学习软件

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

三、入门学习视频

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

    if end\_date\_ < start\_date\_:
        sys.exit("起始时间应该大于结束时间")

    if start\_date\_ > now:
        sys.exit("起始时间应该大于当前时间")

    if end\_date\_ > now:
        end\_date = now.strftime(DATE\_FORMAT)

except Exception:
    traceback.print\_exc("")
    sys.exit("传入的start\_date\[%s\]或end\_date\[%s\]时间格式不正确, 格式应该像20200101" % (start\_date, end\_date))

def worker(ts\_code):
    fn = partial(ts.pro\_bar, ts\_code=ts\_code, adj='qfq', start\_date=start\_date, end\_date=end\_date)
    return retry(fn)

pool = ThreadPoolExecutor(max\_workers=worker\_size)
print("准备开始获取 %s到%s 的股票数据" % (start\_date, end\_date))
# 不指定任何参数会获取5000条最近的数据,ts\_code会重复
day = pro.daily()

# 固定顺序,set是无法包装有序的
all\_ts\_code = list(set(day.ts\_code))
fs\_lst = \[\]

for ts\_code in all\_ts\_code:
    fs\_lst.append(pool.submit(worker, ts\_code))
    # break

for ts\_code, fs in zip(all\_ts\_code, fs\_lst):
    # 如果有异常或者结果为空的话
    if fs.exception() or not isinstance(fs.result(), pd.DataFrame):
        print(fs.exception())
        sys.exit("在交易日\[%s\]超过重试最大的次数也没有获取到数据" % trade\_date)

    df = fs.result()
    # %timeit df.sort\_index()
    # 192 µs ± 3.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

    # %timeit df.sort\_index(inplace=True)
    # 2.54 µs ± 177 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    df.sort\_index(inplace=True, ascending=False)

    if not isinstance(df, pd.DataFrame):
        sys.exit("在股票\[%s\]超过重试最大的次数也没有获取到数据" % ts\_code)

    save\_to\_csv({ts\_code: df}, data\_path=data\_path)
    if debug:
        print("股票\[%s\]历史数据下载保存完成" % ts\_code)

end\_time = datetime.now()
print("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*")
print("下载完成, 共花费时间%s" % (end\_time - start\_time))
print("\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*")

我感觉我写了好多注释以及一些方法之间性能对比,关于各个方法之间的性能对比大家还是需要注意的,因为虽然平时感受不出来,但是在较多次数的循环的时候就会发现性能差异了。


至此需要的历史数据就得到了。


如果对读写速度很在意的话,存储在sqlite数据库或者其他其他数据会快很多。


### 股票池的选择条件


无论是主观交易还是量化交易,选股的方式个人认为大概分为以下三类。


1. 技术选股  
 通过技术指标比如MACD, KDJ或者K线形态等技术指标来选股。
2. 基本面选股  
 通过财务报表或者一些金融指标来选股。
3. 消息选股  
 新闻消息或者小道消息选股。


本文主要集中在以下几种方式


1. 形态选股  
 通过k线的形态找到股票中的十字星等形态。
2. 交易额/流通股本选股  
 通过对交易额或者流通股本排序选择前面的股票。
3. 相似度选股  
 通过选定基准股票趋势,发现相似的股票。
4. 趋势选股  
 挑选最近六个月股票趋势向上的股票。


#### 形态选股


股票的形态多种多样,这里以选择股票中的黄昏(早晨)十字星为例。


黄昏十字星与早晨十字星的区别在于所处趋势不一样。


图示如下:


![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9wMS10dC5ieXRlaW1nLmNvbS9sYXJnZS9wZ2MtaW1hZ2UvYjlkZjE0ODA2NTliNDJiOWFkMzQ0YmVkNmMxODM0YmY?x-oss-process=image/format,png)


如果需要程序判断,那么这些长度需要量化,不能模棱两可。所以根据十字星的定义,量化如下:


实体长度: 超过2.5%才算长实体,而且上下影线不能超过0.3%


十字星: 实体长度不超过1.5%


趋势: 包括十字星在内往前数6根k线,其中的第1根k线收盘价均小于它后面4根收盘价为向上趋势,反之趋势向下,并且幅度超过2.5%。


这里我只是主观的量化没有任何交易经验的选择,如果不认同的话,可以自行修改代码。


代码如下:



def select_doji():
# fp = “by_ts_code/600249.SH-.csv”
fp = “by_ts_code/300130.SZ-.csv”
df = pd.read_csv(fp, index_col=“trade_date”, parse_dates=[“trade_date”])
df = df[[“open”, “high”, “low”, “close”]]

# k线数量
k\_size = 6

# 起始幅度大小
treand\_threshold = 0.025

# 长实体最小长度
entity\_length\_threshold = 0.025
# 长实体上下最大影线长度
entity\_shadow\_line\_length\_threshold = 0.03

# 十字星实体长度最大长度
doji\_entity\_length\_threshold = 0.015
# 十字星上下影线长度最小长度
# doji\_shadow\_line\_length\_threshold = 0.005

trend\_map = {1: "向上", -1: "向下"}

def up\_or\_down\_trend(row):
    """1代表向上, -1代表向下, 0代表震荡"""
    first = row\[0\]
    last = row\[-1\]

    if all(first > row\[1:\]) and first > (last \* treand\_threshold):
        return -1
    elif all(first < row\[1:\]) and last > (first \* treand\_threshold):
        return 1
    else:
        return 0

df\["trend"\] = df.close.rolling(k\_size).apply(up\_or\_down\_trend, raw=True)
df.fillna(value=0, inplace=True)

def k\_sharp(k):
    """返回k线的上下影线长度, 实体长度"""
    open\_, high, low, close = k\[:4\]
    if open\_ > close:
        upper\_line\_length = (high - open\_) / high
        lower\_line\_length = (close - low) / close
        entity\_length = (open\_ - close) / open\_
    else:
        upper\_line\_length = (high - close) / high
        lower\_line\_length = (open\_ - low) / open\_
        entity\_length = (close - open\_) / close

    return upper\_line\_length, lower\_line\_length, entity\_length

def is\_up\_or\_down\_doji(k\_lst, trend):
    # open, high, low, close
    if len(k\_lst) != 3:
        sys.exit("判断十字星需要三根K线")

    is\_ok = False
    k1, k2, k3 = k\_lst

    # 判断是否跳空
    # 通过high, close过于严格      
    if trend > 0:
        # 趋势向上时,最低点是否大于两个实体的最高价
        if k2\[0\] < k1\[1\] or k2\[0\] < k3\[1\]:
        # if k2\[0\] < k1\[1\] :
            return is\_ok
    else:
        # 趋势向下时,最高点是否小于两个实体的最高价
        if k2\[0\] > k1\[2\] or k2\[0\] > k3\[2\]:
            return is\_ok 

    k1\_sharp = k\_sharp(k1)
    # print("k1 sharp")
    # print(k1\_sharp)
    # 判断是否为长实体
    if (k1\_sharp\[2\] < entity\_length\_threshold 
        or k1\_sharp\[0\] > entity\_shadow\_line\_length\_threshold 
        or k1\_sharp\[1\] > entity\_shadow\_line\_length\_threshold):
        return is\_ok

    k3\_sharp = k\_sharp(k3)
    # print("k3 sharp")        
    # print(k3\_sharp)
    if (k3\_sharp\[2\] < entity\_length\_threshold 
        or k3\_sharp\[0\] > entity\_shadow\_line\_length\_threshold 
        or k3\_sharp\[1\] > entity\_shadow\_line\_length\_threshold):
        return is\_ok

    # print("ok")
    # 判断是否为十字星
    k2\_sharp = k\_sharp(k2)
    # print("k2 sharp")        
    # print(k2\_sharp)
    # 实体长度不超过0.2%, 上下影线长度超过0.6%, 如果规定上下影线的长度不太好找
    # if (k2\_sharp\[2\] > doji\_entity\_length\_threshold
    #     or k2\_sharp\[0\] < doji\_shadow\_line\_length\_threshold
    #     or k2\_sharp\[1\] < doji\_shadow\_line\_length\_threshold):

    if k2\_sharp\[2\] > doji\_entity\_length\_threshold:
        return is\_ok

    return True

df\_values = df.values
ret = \[\]

for index in range(len(df\_values)):
    if index < k\_size:
        continue

    trend = df\_values\[index - 1\]\[-1\]
    if trend == 0:
        continue

    val\_slice = slice(index-2, index+1)
    k\_lst = df\_values\[val\_slice\]
    if is\_up\_or\_down\_doji(k\_lst, trend):
        ret.append(index-1)
        print("在>>%s<<<找到趋势为\[%s\]的十字星" % (df.index\[index-1\], trend\_map\[trend\]))

if not ret:
    print("没有发现任何十字星")

ax = ohlc\_plot(df\[\["open", "high", "low", "close"\]\])
for i in ret:
    # print(i)
    mark\_x = df.index\[i\]
    # mark\_y = df.loc\[mark\_x\].low
    # print(mark\_x, mark\_y)
    ax.axvline(mark\_x)
plt.show()

return ret

结果如下:


![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9wMS10dC5ieXRlaW1nLmNvbS9sYXJnZS9wZ2MtaW1hZ2UvNzVlYWUyYmVmYTk2NDUwZDlkNTBlYTU0YWViODRiNGY?x-oss-process=image/format,png)


说实话标准的不太好找,所以代码的条件设置不是很严格。


形态选股很有意思,以后有机会单独出一篇文章。


#### 交易额/流通股本选股


通过交易额过滤,选择股票最近一百个交易日的成交额平均值,然后进行排序。


交易额排序



def select_by_amount(data_path, top_size=20):
# 或许交易日比较好
day_range = 100

all\_df = load\_all\_local\_data(data\_path, tail\_size=day\_range)
ret = \[\]
for ts\_code, df in all\_df.items():
    df\["amount\_avg"\] = df.amount.rolling(day\_range).mean()
    amount\_avg = df\["amount\_avg"\]\[-1\]

    # NaN的布尔值为True, 所以需要np.isnan判断
    if np.isnan(amount\_avg):
        continue

    ret.append((ts\_code, amount\_avg))

ret.sort(key=lambda x:x\[1\])
print("交易额排名前%s的结果如下" % top\_size)
print(ret\[-top\_size:\])
return ret\[-top\_size:\]

结果如下。



[(‘002714.SZ’, 2162871.9387400015), (‘000002.SZ’, 2294273.739109999), (‘002460.SZ’, 2372975.2177099977), (‘600745.SH’, 2461243.4350499995), (‘601990.SH’, 2551214.2825800003), (‘603986.SH’, 2679324.4020000002), (‘600703.SH’, 2759167.96963), (‘002456.SZ’, 2759314.642220001), (‘000651.SZ’, 2782179.0211499995), (‘000100.SZ’, 2853012.22389), (‘002185.SZ’, 3149773.6836400027), (‘000858.SZ’, 3168871.2845699983), (‘002475.SZ’, 3392269.47999), (‘300750.SZ’, 3511882.7076800014), (‘600030.SH’, 4039920.83439), (‘000725.SZ’, 4189700.4488400007), (‘300059.SZ’, 4447245.855129998), (‘600519.SH’, 4448082.42745), (‘601318.SH’, 4892206.13003), (‘000063.SZ’, 5128169.27109)]


流通市值排序



def select_by_float_market_value(trade_date, top_size=20):
df = pro.daily_basic(ts_code=‘’, trade_date=trade_date, fields=“ts_code,close,float_share”)

ret = \[\]
for row in df.values:
    ts\_code = row\[0\]
    float\_market\_value = row\[1\] \* row\[2\]

    if np.isnan(float\_market\_value) or not float\_market\_value:
        continue

    ret.append((ts\_code, float\_market\_value))

ret.sort(key=lambda x:x\[1\])
print("流通市值名前%s的结果如下" % top\_size)
print(ret\[-top\_size:\])
return ret\[-top\_size:\]

结果如下



交易额排名前20的结果如下
[(‘000002.SZ’, 24443367.828188002), (‘000001.SZ’, 25072232.462559998), (‘601088.SH’, 26237241.386405), (‘600000.SH’, 28497216.593586), (‘601166.SH’, 30952865.369478), (‘000651.SZ’, 33204757.629185997), (‘603288.SH’, 36720702.433056), (‘600900.SH’, 36894000.0), (‘000333.SZ’, 39063915.66100799), (‘600028.SH’, 40612052.69455), (‘600276.SH’, 42312945.987192), (‘000858.SZ’, 54753808.6884), (‘601628.SH’, 54786707.43), (‘600036.SH’, 69375140.114727), (‘601857.SH’, 70759948.006466), (‘601988.SH’, 72081806.077332), (‘601318.SH’, 75958643.459976), (‘601288.SH’, 99096634.04564801), (‘601398.SH’, 136154167.33219498), (‘600519.SH’, 166848191.796)]


这种方式比较简单,但是一般还需要一些其他的条件相互配合。


**另外怕大家不会使用,直接给大家准备了写好的,直接下载打开即可使用!**  
 **源码放在百度云盘上了需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】**



![](https://img-blog.csdnimg.cn/img_convert/d440a77c65cd426371caeade1a63e197.png)/> 
 



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**


**另外怕大家不会使用,直接给大家准备了写好的,直接下载打开即可使用!**  
 **源码放在百度云盘上了需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】**



[外链图片转存中...(img-Yvqilmww-1715645412485)]/> 
 



**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值