Pandas案例练习-股票分析


目录:

需求:股票分析

  • 使用tushare包获取某股票的历史行情数据。
  • 输出该股票所有收盘比开盘上涨3%以上的日期。
  • 输出该股票所有开盘比前日收盘跌幅超过2%的日期。
  • 假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?

  • tushare财经数据接口包

    • pip install tushare

In [1]:

import tushare as ts
import pandas as pd
from pandas import DataFrame,Series
import numpy as np

In [2]:

#获取某只股票的历史行情数据
#code:字符串形式的股票代码
df = ts.get_k_data(code='600519',start='2000-01-01')
df
本接口即将停止更新请尽快使用Pro版接口https://tushare.pro/document/2

Out[2]:

date open close high low volume code
0 2001-08-27 -91.359 -91.174 -90.778 -91.654 406318.00 600519
1 2001-08-28 -91.274 -90.941 -90.916 -91.341 129647.79 600519
2 2001-08-29 -90.920 -91.027 -90.916 -91.076 53252.75 600519
3 2001-08-30 -91.044 -90.899 -90.826 -91.094 48013.06 600519
4 2001-08-31 -90.890 -90.915 -90.806 -90.952 23231.48 600519
... ... ... ... ... ... ... ...
4906 2022-03-09 1764.100 1779.180 1805.000 1721.110 56434.00 600519
4907 2022-03-10 1808.000 1794.430 1821.000 1791.000 36462.00 600519
4908 2022-03-11 1766.000 1769.010 1777.000 1718.080 47727.00 600519
4909 2022-03-14 1730.000 1700.000 1750.000 1700.000 49007.00 600519
4910 2022-03-15 1650.000 1603.000 1685.000 1603.000 89056.00 600519

4911 rows × 7 columns

In [3]:

#将互联网上获取的股票数据存储到本地
df.to_csv('./maotai.csv')#调用to_xxx方法将df中的数据写入到本地进行存储

In [4]:

#将本地存储的数据读入到df
df = pd.read_csv('./maotai.csv')
df.head()

Out[4]:

Unnamed: 0 date open close high low volume code
0 0 2001-08-27 -91.359 -91.174 -90.778 -91.654 406318.00 600519
1 1 2001-08-28 -91.274 -90.941 -90.916 -91.341 129647.79 600519
2 2 2001-08-29 -90.920 -91.027 -90.916 -91.076 53252.75 600519
3 3 2001-08-30 -91.044 -90.899 -90.826 -91.094 48013.06 600519
4 4 2001-08-31 -90.890 -90.915 -90.806 -90.952 23231.48 600519

In [5]:

#需要对读取出来的数据进行相关的处理

In [6]:

#删除df中指定的一列
df.drop(labels='Unnamed: 0',axis=1,inplace=True)

In [7]:

#查看每一列的数据类型
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4911 entries, 0 to 4910
Data columns (total 7 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   date    4911 non-null   object 
 1   open    4911 non-null   float64
 2   close   4911 non-null   float64
 3   high    4911 non-null   float64
 4   low     4911 non-null   float64
 5   volume  4911 non-null   float64
 6   code    4911 non-null   int64  
dtypes: float64(5), int64(1), object(1)
memory usage: 268.7+ KB

In [8]:

#将date列转为时间序列类型
df['date'] = pd.to_datetime(df['date'])

In [9]:

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 4911 entries, 0 to 4910
Data columns (total 7 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   date    4911 non-null   datetime64[ns]
 1   open    4911 non-null   float64       
 2   close   4911 non-null   float64       
 3   high    4911 non-null   float64       
 4   low     4911 non-null   float64       
 5   volume  4911 non-null   float64       
 6   code    4911 non-null   int64         
dtypes: datetime64[ns](1), float64(5), int64(1)
memory usage: 268.7 KB

In [10]:

#将date列作为源数据的行索引
df.set_index('date',inplace=True)

In [11]:

df.head()

Out[11]:

open close high low volume code
date
2001-08-27 -91.359 -91.174 -90.778 -91.654 406318.00 600519
2001-08-28 -91.274 -90.941 -90.916 -91.341 129647.79 600519
2001-08-29 -90.920 -91.027 -90.916 -91.076 53252.75 600519
2001-08-30 -91.044 -90.899 -90.826 -91.094 48013.06 600519
2001-08-31 -90.890 -90.915 -90.806 -90.952 23231.48 600519

In [12]:

#输出该股票所有收盘比开盘上涨3%以上的日期
#伪代码:(收盘-开盘)/开盘 > 0.03
(df['open'] - df['close']) / df['open'] > 0.03

#在分析的过程中如果产生了boolean值则下一步马上将布尔值作为源数据的行索引
 #如果布尔值作为df的行索引,则可以取出true对应的行数据,忽略false对应的行数据
df.loc[(df['open'] - df['close']) / df['open'] > 0.03] #获取了True对应的行数据(满足需求的行数据)

df.loc[(df['open'] - df['close']) / df['open'] > 0.03].index #df的行数据

Out[12]:

DatetimeIndex(['2006-05-29', '2006-06-12', '2006-10-09', '2006-10-25',
               '2006-11-14', '2006-11-16', '2006-11-29', '2006-11-30',
               '2006-12-11', '2006-12-14',
               ...
               '2021-07-26', '2021-07-27', '2021-07-29', '2021-08-17',
               '2021-08-26', '2021-10-18', '2021-12-29', '2022-01-13',
               '2022-01-28', '2022-03-07'],
              dtype='datetime64[ns]', name='date', length=680, freq=None)

In [13]:

#输出该股票所有开盘比前日收盘跌幅超过2%的日期
#伪代码:(开盘-前日收盘)/前日收盘 < -0.02
(df['open'] - df['close'].shift(1))/df['close'].shift(1) < -0.02
#将布尔值作为源数据的行索引取出True对应的行数据
df.loc[(df['open'] - df['close'].shift(1))/df['close'].shift(1) < -0.02]

df.loc[(df['open'] - df['close'].shift(1))/df['close'].shift(1) < -0.02].index

Out[13]:

DatetimeIndex(['2006-02-13', '2006-04-17', '2006-04-18', '2006-04-19',
               '2006-04-20', '2006-05-25', '2006-05-30', '2006-12-27',
               '2007-01-04', '2007-01-22',
               ...
               '2020-03-13', '2020-03-23', '2020-10-26', '2021-02-26',
               '2021-03-04', '2021-04-28', '2021-08-20', '2021-11-01',
               '2022-03-14', '2022-03-15'],
              dtype='datetime64[ns]', name='date', length=377, freq=None)
  • 需求:假如我从2010年1月1日开始,每月第一个交易日买入1手股票,每年最后一个交易日卖出所有股票,到今天为止,我的收益如何?
  • 分析:
    • 时间节点:2010-2020
    • 一手股票:100支股票
    • 买:
      • 一个完整的年需要买入1200支股票
    • 卖:
      • 一个完整的年需要卖出1200支股票
    • 买卖股票的单价:
      • 开盘价

In [14]:

new_df = df['2010-01':'2020-02']
new_df

Out[14]:

open close high low volume code
date
2010-01-04 35.594 34.047 35.594 33.573 44304.88 600519
2010-01-05 34.835 33.671 35.219 33.340 31513.18 600519
2010-01-06 33.333 31.657 33.716 31.319 39889.03 600519
2010-01-07 31.657 29.373 31.980 27.991 48825.55 600519
2010-01-08 29.584 28.081 29.584 26.654 36702.09 600519
... ... ... ... ... ... ...
2020-02-24 1069.182 1050.862 1073.682 1049.182 38650.00 600519
2020-02-25 1041.682 1038.492 1045.772 1031.562 38385.00 600519
2020-02-26 1025.682 1037.382 1047.682 1018.682 43560.00 600519
2020-02-27 1039.682 1051.072 1058.672 1039.682 34498.00 600519
2020-02-28 1033.982 1020.682 1045.682 1013.652 49946.00 600519

2461 rows × 6 columns

In [15]:

new_df.head(2)

Out[15]:

open close high low volume code
date
2010-01-04 35.594 34.047 35.594 33.573 44304.88 600519
2010-01-05 34.835 33.671 35.219 33.340 31513.18 600519

In [16]:

#买股票:找每个月的第一个交易日对应的行数据(捕获到开盘价)==》每月的第一行数据
#根据月份从原始数据中提取指定的数据
#每月第一个交易日对应的行数据
df_monthly = new_df.resample('M').first()#数据的重新取样
df_monthly

Out[16]:

open close high low volume code
date
2010-01-31 35.594 34.047 35.594 33.573 44304.88 600519
2010-02-28 33.250 33.258 33.776 31.845 29655.94 600519
2010-03-31 31.424 31.267 32.176 31.079 21734.74 600519
2010-04-30 25.662 26.624 26.954 25.647 23980.83 600519
2010-05-31 2.529 3.017 3.708 1.702 23975.16 600519
... ... ... ... ... ... ...
2019-10-31 1116.682 1130.782 1143.682 1115.692 31045.00 600519
2019-11-30 1144.682 1148.682 1155.632 1136.182 22811.00 600519
2019-12-31 1081.882 1096.682 1103.702 1081.882 30784.00 600519
2020-01-31 1091.682 1093.682 1108.742 1079.682 148099.00 600519
2020-02-29 948.682 967.602 974.362 943.682 123442.00 600519

122 rows × 6 columns

In [17]:

#买入股票花费的总金额
cost = df_monthly['open'].sum()*100
cost

Out[17]:

3245315.6

In [18]:

#卖出股票到手的钱
#特殊情况:2020年买入的股票卖不出去
new_df.resample('A').last()
#将2020年最后一行切出去
df_yearly = new_df.resample('A').last()[:-1]
df_yearly

Out[18]:

open close high low volume code
date
2010-12-31 43.847 45.440 45.711 43.284 46084.0 600519
2011-12-31 68.243 68.739 70.045 66.011 29460.0 600519
2012-12-31 88.579 85.034 89.811 82.827 51914.0 600519
2013-12-31 20.074 23.694 24.463 18.835 57546.0 600519
2014-12-31 89.937 93.591 93.937 89.391 46269.0 600519
2015-12-31 143.406 143.376 144.686 143.006 19673.0 600519
2016-12-31 257.967 265.507 266.647 257.967 34687.0 600519
2017-12-31 656.144 635.634 664.644 629.744 76038.0 600519
2018-12-31 512.443 539.153 545.543 509.143 63678.0 600519
2019-12-31 1146.682 1146.682 1151.682 1140.192 22588.0 600519

In [19]:

#卖出股票到手的钱
resv = df_yearly['open'].sum()*1200
resv

Out[19]:

3632786.4000000004

In [20]:

#最后手中剩余的股票需要估量其价值计算到总收益中
#使用昨天的收盘价作为剩余股票的单价
last_monry = 200*new_df['close'][-1]

In [21]:

#计算总收益
resv+last_monry-cost

Out[21]:

591607.2000000002

需求:双均线策略制定

  • 使用tushare包获取某股票的历史行情数据

In [22]:

df = pd.read_csv('./maotai.csv').drop(labels='Unnamed: 0',axis=1)
df

Out[22]:

date open close high low volume code
0 2001-08-27 -91.359 -91.174 -90.778 -91.654 406318.00 600519
1 2001-08-28 -91.274 -90.941 -90.916 -91.341 129647.79 600519
2 2001-08-29 -90.920 -91.027 -90.916 -91.076 53252.75 600519
3 2001-08-30 -91.044 -90.899 -90.826 -91.094 48013.06 600519
4 2001-08-31 -90.890 -90.915 -90.806 -90.952 23231.48 600519
... ... ... ... ... ... ... ...
4906 2022-03-09 1764.100 1779.180 1805.000 1721.110 56434.00 600519
4907 2022-03-10 1808.000 1794.430 1821.000 1791.000 36462.00 600519
4908 2022-03-11 1766.000 1769.010 1777.000 1718.080 47727.00 600519
4909 2022-03-14 1730.000 1700.000 1750.000 1700.000 49007.00 600519
4910 2022-03-15 1650.000 1603.000 1685.000 1603.000 89056.00 600519

4911 rows × 7 columns

In [23]:

#将date列转为时间序列且将其作为源数据的行索引
df['date'] = pd.to_datetime(df['date'])

In [24]:

df.set_index('date',inplace=True)

In [25]:

df.head()

Out[25]:

open close high low volume code
date
2001-08-27 -91.359 -91.174 -90.778 -91.654 406318.00 600519
2001-08-28 -91.274 -90.941 -90.916 -91.341 129647.79 600519
2001-08-29 -90.920 -91.027 -90.916 -91.076 53252.75 600519
2001-08-30 -91.044 -90.899 -90.826 -91.094 48013.06 600519
2001-08-31 -90.890 -90.915 -90.806 -90.952 23231.48 600519
  • 计算该股票历史数据的5日均线和30日均线
    • 什么是均线?
      • 对于每一个交易日,都可以计算出前N天的移动平均值,然后把这些移动平均值连起来,成为一条线,就叫做N日移动平均线。移动平均线常用线有5天、10天、30天、60天、120天和240天的指标。
        • 5天和10天的是短线操作的参照指标,称做日均线指标;
        • 30天和60天的是中期均线指标,称做季均线指标;
        • 120天和240天的是长期均线指标,称做年均线指标。
    • 均线计算方法:MA=(C1+C2+C3+...+Cn)/N C:某日收盘价 N:移动平均周期(天数)

In [26]:

ma5 = df['close'].rolling(5).mean()
ma30 = df['close'].rolling(30).mean()

In [27]:

ma5

Out[27]:

date
2001-08-27          NaN
2001-08-28          NaN
2001-08-29          NaN
2001-08-30          NaN
2001-08-31     -90.9912
                ...    
2022-03-09    1763.9760
2022-03-10    1762.8620
2022-03-11    1760.5640
2022-03-14    1759.1640
2022-03-15    1729.1240
Name: close, Length: 4911, dtype: float64

In [28]:

import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(ma5[50:180])
plt.plot(ma30[50:180])

Out[28]:

[<matplotlib.lines.Line2D at 0x21df8d5fb20>]

img

  • 分析输出所有金叉日期和死叉日期
    • 股票分析技术中的金叉和死叉,可以简单解释为:
      • 分析指标中的两根线,一根为短时间内的指标线,另一根为较长时间的指标线。
      • 如果短时间的指标线方向拐头向上,并且穿过了较长时间的指标线,这种状态叫“金叉”;
      • 如果短时间的指标线方向拐头向下,并且穿过了较长时间的指标线,这种状态叫“死叉”;
      • 一般情况下,出现金叉后,操作趋向买入;死叉则趋向卖出。当然,金叉和死叉只是分析指标之一,要和其他很多指标配合使用,才能增加操作的准确性。

In [29]:

ma5 = ma5[30:]
ma30 = ma30[30:]

In [30]:

s1 = ma5 < ma30
s2 = ma5 > ma30

In [31]:

df = df[30:]

In [32]:

death_ex = s1 & s2.shift(1) #判定死叉的条件
df.loc[death_ex] #死叉对应的行数据
death_date = df.loc[death_ex].index

In [33]:

golden_ex = ~(s1 | s2.shift(1))#判定金叉的条件
golden_date = df.loc[golden_ex].index #金叉的时间

image.png

  • 如果我从假如我从2010年1月1日开始,初始资金为100000元,金叉尽量买入,死叉全部卖出,则到今天为止,我的炒股收益率如何?
  • 分析:
    • 买卖股票的单价使用开盘价
    • 买卖股票的时机
    • 最终手里会有剩余的股票没有卖出去
      • 会有。如果最后一天为金叉,则买入股票。估量剩余股票的价值计算到总收益。
        • 剩余股票的单价就是用最后一天的收盘价。

In [34]:

s1 = Series(data=1,index=golden_date) #1作为金叉的标识
s2 = Series(data=0,index=death_date) #0作为死叉的标识

s = s1.append(s2)
s = s.sort_index() #存储的是金叉和死叉对应的时间

In [35]:

s = s['2010':'2020']##存储的是金叉和死叉对应的时间

In [36]:

first_monry = 100000 #本金,不变
money = first_monry #可变的,买股票话的钱和卖股票收入的钱都从该变量中进行操作
hold = 0 #持有股票的数量(股数:100股=1手)

for i in range(0,len(s)): #i表示的s这个Series中的隐式索引
    #i = 0(死叉:卖) = 1(金叉:买)
    if s[i] == 1:#金叉的时间
        #基于100000的本金尽可能多的去买入股票
        #获取股票的单价(金叉时间对应的行数据中的开盘价)
        time = s.index[i] #金叉的时间
        p = df.loc[time]['open'] #股票的单价
        hand_count = money // (p*100) #使用100000最多买入多少手股票
        hold = hand_count * 100 

        money -= (hold * p) #将买股票话的钱从money中减去
    else:
        #将买入的股票卖出去

        #找出卖出股票的单价
        death_time = s.index[i]
        p_death = df.loc[death_time]['open'] #卖股票的单价
        money += (p_death * hold) #卖出的股票收入加入到money
        hold = 0

#如何判定最后一天为金叉还是死叉
last_monry = hold * df['close'][-1] #剩余股票的价值

#总收益
money + last_monry - first_monry

Out[36]:

32376838.699999996