深入浅出Python量化交易实战chapter2

本文介绍了使用Python实现的四种简单股票交易策略:低买高卖、单一移动平均、双移动平均和海龟策略。通过示例展示了如何设置交易信号、回测资产表现和绘制交易图表。尽管这些策略在给定时间段内并未盈利,但展示了基本的交易逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

(1)低买高卖简单回测

(2)单一移动平均策略

(3)双移动平均策略

(4)海龟策略 

(1)低买高卖简单回测

“低买高卖”策略。每次都在相对低点买入,并且在相对高点卖出(跌了买,涨了卖)。下面进行简单回测:

1.导入库

# 导入库
from pandas_datareader import data as dt
import yfinance as yf
yf.pdr_override()

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

 2.获取数据

start_date = '2020-01-01'
end_date = '2020-03-20'

# 提取数据:中国平安股票数据
zgpa = dt.get_data_yahoo('601318.ss',start_date,end_date)
zgpa.head()

输出: 

 3.设定交易信号

# 交易信号
zgpa_signanl = pd.DataFrame(index = zgpa.index)      # 日期index
zgpa_signanl['price'] = zgpa['Adj Close']            # 调整价格作为股票价格
zgpa_signanl['diff'] = zgpa_signanl['price'].diff()   #股价变化
zgpa_signanl = zgpa_signanl.fillna(0.0)              #第一个空值填充为0
zgpa_signanl['signal'] = np.where(zgpa_signanl['diff']>=0,0,1) #股价上涨或不变记为0,下跌记为1
zgpa_signanl['order'] = zgpa_signanl['signal'].diff()*100      #买入/卖出1手(100股),股价上涨:卖出,股价下跌:买入
zgpa_signanl.head()

输出:

4.回测

# 回测
initial_cash = 20000.00  # 初始资金2万元
# 股票市值
zgpa_signanl['stock'] = zgpa_signanl['order']*zgpa_signanl['price']
# 剩余现金=初始现金-现金流变化的累加
zgpa_signanl['cash'] = initial_cash -(zgpa_signanl['order'].diff()*zgpa_signanl['price']).cumsum()
#总资产=股票+现金
zgpa_signanl['total'] = zgpa_signanl['stock'] +zgpa_signanl['cash']

# 绘图
plt.figure(figsize=(8,5))
plt.plot(zgpa_signanl['total'],label = 'total')  #总资产
plt.plot(zgpa_signanl['order'].cumsum()*zgpa_signanl['price'],'--',label = 'stock value') #持仓股票市值
plt.grid(True)
plt.legend(loc='center right')
plt.show()

输出:

(2)单一移动平均策略

当股价上升且向上穿过N日的均线时,说明股价向上突破,此时下单买入;当股价下降且向下穿过N日均线,说明股价出现下跌趋势,此时下单卖出。或者当M日均价上升穿过N日的均线(M < N)时,说明股票处上升的趋势,下单买入;反之,当M日均价下降且穿过N日均线时,说明股票处于下降趋势,下单卖出。
 

1.这里指标使用10日均线:

period = 10 #10日均线
avg_10=[]   # 储存10天的价格
avg_value=[]  # 储存10天价格的均值
for price in zgpa['Adj Close']:
    avg_10.append(price)
    if len(avg_10) >period:
        del avg_10[0]
    avg_value.append(np.mean(avg_10))

# 将10日均价写到股票价格数据表中
zgpa = zgpa.assign(avg_10 = pd.Series(avg_value, index= zgpa.index))
zgpa.head()

输出:
 

2.画个10日均线的图

plt.figure(figsize=(10,6))
plt.plot(zgpa['Adj Close'],lw = 2,c ='k',label = 'stock price')  # stock price,股价
plt.plot(zgpa['avg_10'],'--',lw =2 ,c ='b',label = 'avg_10')     # avg_10,10日均价
plt.grid(True)
plt.legend()
plt.show()

输出:

(3)双移动平均策略

使用两条均线判断股价未来的走势。一条是长期均线(如20日均线),另一条是短期均线(如5日均线)。这种策略基于这样一种假设:股票价格的动量会朝着短期均线的方向移动。当短期均线穿过过长期均线,超过长期移动平均线时,动量将向上,此时股价可能会上涨。然而,如果短期均线的移动方向相反,则股价可能下跌。

1.策略代码:

strategy = pd.DataFrame(index=zgpa.index)
strategy['signal'] = 0  # 存储交易信号
strategy['avg_5'] = zgpa['Adj Close'].rolling(5).mean()      # 5日均价
strategy['avg_10'] = zgpa['Adj Close'].rolling(10).mean()    # 10日均价
strategy['signal'] = np.where(strategy['avg_5']>strategy['avg_10'],1,0) # 5日均价avg_5>10日均价avg_10时动量向上,股价可能上涨,标记为1(avg_5<avg_10时,股价可能下跌,标记为0)
strategy['order'] = strategy['signal'].diff() # 信号从0变1,买入,从1变0,卖出
strategy

输出:

2.画个图看看该策略在哪里买/卖

plt.figure(figsize=(10,5))
plt.plot(zgpa['Adj Close'],lw = 2,label = 'price')  # stock price
plt.plot(strategy['avg_5'],lw = 2,ls ='--',label = 'avg5')  # stock price
plt.plot(strategy['avg_10'],lw = 2,ls ='-.',label = 'avg10')  # stock price
plt.scatter(strategy.loc[strategy['order']==1].index,
            zgpa['Adj Close'][strategy['order']==1],
            marker='^',s = 80, c = 'r', label = 'Buy')  # 标出买入信号
plt.scatter(strategy.loc[strategy['order']==-1].index,
            zgpa['Adj Close'][strategy['order']==-1],
            marker='v',s = 80, c = 'g', label = 'Sell')  # 标出卖出信号
plt.legend()
plt.grid(True)
plt.show()

输出:

3.对该策略回测:

## 回测
initial_cash = 20000.00  # 初始资金2万元
positions = pd.DataFrame(index = strategy.index).fillna(0)
positions['stock'] = strategy['signal']*100 # 买卖1手100股
portfolio = pd.DataFrame()
portfolio['stock value'] = positions.multiply(zgpa['Adj Close'], axis = 0) # 持仓股票*股价=股票市值
# multiply 对应位置上两个对象元素的乘积
order = positions.diff()  # 下单数量
# 剩余资金 = 初始资金 - 下单金额的总和
portfolio['cash'] = initial_cash - order.multiply(zgpa['Adj Close'], axis = 0).cumsum()
# 总资产 = 剩余资金 + 持仓股票市值
portfolio['total'] = portfolio['cash'] + portfolio['stock value']

portfolio

输出(看看portfolio):

画个图看下:

plt.figure(figsize=(10,5))
plt.plot(portfolio['total'], lw=2, label = 'total')  # 总资产
plt.plot(portfolio['stock value'], lw=2, label = 'stock value')  # 持仓股票市值
plt.legend()
plt.grid(True)
plt.show()

该时间区间市场下跌,该策略总体上还是亏钱的。

(4)海龟策略 

在股价超过过去N个交易日的股价最高点时买入,在股价低于过去N个交易日的股价最低点时卖出。上述的若干个最高点和最低点会组成一个通道,称“唐奇安通道”。

使用过去N天的股价最高点和过去N天的股价最低点生成唐奇安通道。一般来说,N会设置为20。不过因为我们下载的股票数据时间范围跨度比较小,所以选择了使用过去5日的股价最高点和最低点来进行演示。

1.策略信号:

turtle = pd.DataFrame(index= zgpa.index)
turtle['high'] = zgpa['Adj Close'].shift(1).rolling(5).max()
turtle['low'] = zgpa['Adj Close'].shift(1).rolling(5).min()
turtle['buy'] = zgpa['Adj Close'] > turtle['high'] #股价突破上沿,买入
turtle['sell'] = zgpa['Adj Close'] < turtle['low'] #股价突破下沿,卖出
turtle

输出:

2.根据交易信号和仓位进行下单
交易信号为“买入”且空仓时,才下买入订单;交易信号为“卖出”且有持仓股票时,才会下卖出订单

turtle['orders'] = 0 # 初始订单状态为0
position = 0   # 初始仓位0
for k in range(len(turtle)):
    if turtle.buy[k] and position == 0: # 买入信号为true且空仓时买入1手:
        turtle.orders[k] = 1
        position = 1
    elif turtle.sell[k] and position > 0:
        turtle.orders[k] = -1
        position = 0

turtle

输出:

3.画图看下买/卖时机:

plt.figure(figsize=(10, 5))# 设置画布的尺寸0
plt.plot(zgpa['Adj Close'], lw=2, label='stock') #股价折线图
# 唐奇安通道的上沿
plt.plot(turtle['high'], lw=2,  ls='--',c='r', label='high')
# 唐奇安通道的下沿
plt.plot(turtle['low'], lw=2,  ls='--',c='g', label='low')
#将买入信号用正三角标识
plt.scatter(
            turtle.loc[turtle.orders == 1].index,
            zgpa['Adj Close'][turtle.orders == 1],
            marker='^', s=80, c='r', label='buy')
#将卖出信号用倒三角标识
plt.scatter(turtle.loc[turtle.orders == -1].index,
            zgpa['Adj Close'][turtle.orders == -1],
            marker='v', s=80, c='g', label='shell')

plt.legend()
plt.grid()
plt.show()

输出:

4.回测:

# 回测

# 初始资金
initial_cash = 20000
# 创建新的数据表,序号和turtle数据表一致
positions = pd.DataFrame(index=turtle.index).fillna(0.0)
# 每次交易为1手(100股),仓位既买单和卖单的累积加和
positions['stock'] = 100 * turtle['orders'].cumsum()
# 创建投资组合数据表
portfolio = positions.multiply(zgpa['Adj Close'], axis=0)
# 持仓市值为持仓股票数乘以股价
portfolio['holding_values'] = positions.multiply(zgpa['Adj Close'], axis=0)
# 计算出仓位的变化
pos_diff = positions.diff()
# 剩余的现金是初始资金减去仓位变化产生的现金流累计加和
portfolio['cash'] = initial_cash - (pos_diff.multiply(zgpa['Adj Close'], axis=0)).cumsum()
# 总资产既持仓股票市值加剩余现金
portfolio['total'] = portfolio['cash'] + portfolio['holding_values']
portfolio

输出:

画图看下:


#画图
plt.figure(figsize=(10, 5))
plt.plot(portfolio['total'], label='total')
plt.plot(portfolio['holding_values'], '--', label='holding_values') #持仓股票是市值
plt.legend()
plt.grid()
plt.show()

输出:

可以看到该策略还是亏钱(最后剩余19549元)的。但是比双移动平均策略(剩余19413元)赔的少一些,表明该策略更优。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值