在金融市场中,股票价格的波动受到多种因素的影响,包括经济数据、公司业绩和政策变化等。为了分析和预测股票价格的走势,投资者可以借助各种技术指标和分析方法,如:金叉死叉、相对强弱指标(RSI)和移动平均线(MACD)等技术指标。这些指标可以辅助投资者识别股票价格的趋势和反转点,从而指导投资者进行买卖决策。
然而,需要注意的是,技术指标和分析方法并不能完全预测股票价格的走势,它们只是提供了一种辅助工具。技术指标也可能存在滞后性和误导性的问题,因此投资者应谨慎使用,并结合基本面分析和市场情绪等因素进行综合判断。投资买卖股票存在一定的风险,可能导致投资者遭受巨大损失。尽管买卖股票有亏有赚是常见的现象,但通过科学的投资方法和风险管理策略,投资者仍然可以提高投资的成功率和盈利能力。
温馨提醒:本文分析仅供参考,谨慎根据指标进行买卖股票,还需结合其余指标分析。
目录
1、导入基本库
2、导入数据
3、交易信号设置
4、交易买卖位可视化
5、股票买卖操作--回测
6、策略累积收益
7、回测评估
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
plt.rcParams['font.sans-serif']='SimHei'
plt.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings('ignore')
pd.set_option('display.unicode.east_asian_width',True)
# 需要打开掘金后运行
import talib as ta
from __future__ import print_function, absolute_import, unicode_literals
from gm.api import *
## 终端开启,设置token
set_token('5ba568ac759242c0471cb0fe330cd04214fb0b5b')
天泽掘金量化平台下载:
链接:https://pan.baidu.com/s/1Nul-x4gEe6GhrMF_gWjyUQ
提取码:1901
本文选取了长安汽车(SZSE.000625)股票,时间数据为2020年1月1日到2023年6月7日,数据来源于“天泽掘金量化平台”中导入,导入前需要打开平台才可导入。
# 获取股票数据
def get_stock_data(symbol, fre , start_date , end_date):
# 掘金获取
data=history(symbol, frequency = fre, start_time = start_date, end_time = end_date , df = True)
return data
# 获取数据函数调用
symbol = 'SZSE.000625'
fre = '1d'
start_date = '2020-01-01'
end_date = '2023-06-07'
data = get_stock_data(symbol, fre , start_date , end_date)
data.head(2)
输出结果为:
数据简单处理
# 数据简单处理
data = data[['bob','close']]
data['bob'] = data['bob'].dt.date
data.head(1)
输出结果:
设置短期为5日均线,长期为20日均线
# 金叉死叉交易信号
def golden_death_cross_signal(data, short_window, long_window):
'''
data:DateFrame数据集,有close收盘价指标
short_window :短期移动窗口数
long_window :长期移动窗口数
(若 短期窗口平均值 > 长期窗口平均值 --- 金叉买入,买卖信号为 1 ;
若 短期窗口平均值 < 长期窗口平均值 --- 死叉卖出,买卖信号为 -1 )
'''
# 移动平均线
data['MA_short'] = data['close'].rolling(window=short_window).mean()
data['MA_long'] = data['close'].rolling(window=long_window).mean()
# 交易信号生成
data['Signal'] = 0
data.loc[data['MA_short'] > data['MA_long'], 'Signal'] = 1 # 金叉信号
data.loc[data['MA_short'] < data['MA_long'], 'Signal'] = -1 # 死叉信号
return data
# 交易信号函数调用
short_window = 5
long_window = 20
data = golden_death_cross_signal(data, short_window, long_window)
df = data.copy()
df.tail()
输出结果:
# 策略交易判断
def golden_death_cross_judge(data):
'''
data:DataFrame数据集,要求有Signal买卖信号指标
(该函数是确定买入和卖出的时间点,方便可视化进行分析)
'''
buy_signals = []
sell_signals = []
for i in range(1, len(data)):
if data['Signal'][i] == 1 and data['Signal'][i-1] == -1:
buy_signals.append(data.index[i])
elif data['Signal'][i] == -1 and data['Signal'][i-1] == 1:
sell_signals.append(data.index[i])
return buy_signals, sell_signals
# 交易判断函数调用
buy_signals, sell_signals = golden_death_cross_judge(data)
# 买信号
buy = pd.DataFrame(buy_signals,columns=['buy'])
buy['buy_close'] = 0
buy = buy.set_index('buy')
for i in buy.index:
buy.loc[i,'buy_close'] = df.loc[i,'close']
# 卖信号
sell = pd.DataFrame(sell_signals,columns=['sell'])
sell['sell_close'] = 0
sell = sell.set_index('sell')
for i in sell.index:
sell.loc[i,'sell_close'] = df.loc[i,'close']
plt.figure(figsize=(18, 14),dpi=200)
plt.subplot(211)
# 双均线折线图
plt.plot(data['bob'],data['MA_short'],label='MA5')
plt.plot(data['bob'],data['MA_long'],label='MA20')
plt.legend(fontsize='larger')
plt.title('双均线 [5日均线 和 20日均线] 折线图')
plt.subplot(212)
plt.plot(df.index, df['close'], label='close',c='orange')
# 买卖信号绘图
plt.plot(buy.index,buy['buy_close'], '^', markersize=8, color='r', label='Buy')
plt.plot(sell.index,sell['sell_close'], 'v', markersize=8, color='green', label='Sell')
plt.title('买入和卖出信号图')
plt.legend()
plt.show()
输出结果:
假设投资人的初始资金为100000元,每次买卖都以全仓进行买和卖。在买卖操作中涉及到了手续费,即持股资金除以10000(注:不足5元按5元收费)。
# 依据信号 进行 买卖股票
def buy_sell_stock(df,initial_capitals):
a = 0 # 统计买入次数
b = 0 # 统计卖出次数
'''
df:DateFrame数据集,需要有close、Signal、return三列指标。
close是收盘价,Signal是买卖信号指标
return是收益率 【计算方法:(当日收盘价-昨日收盘价)/昨日收盘价】
initial_capitals:初始资金
'''
# 定义资金
initial_capital = initial_capitals # 初始资金
position = 0 # 股票持仓
df['Position'] = np.nan # 持有数量
df['Return'] = np.nan # 策略回报率
df['Holdings Funds'] = np.nan # 持有的资金(股票仓位)
df['Balance Funds'] = np.nan # 余额资金
df['Total Funds'] = np.nan # 总资金
for i, row in enumerate(df.iterrows()):
index, data = row
'''
row是元组(a,b)的形式
a是列索引,赋值给index
b是该索引对应的行数据,赋值给data
'''
# 金叉,买入股票
if data['Signal'] == 1:
# 当前没有持仓
if position == 0:
# 买入的股数
position = int(initial_capital // (data['close']*100)*100)
# 指标计算
'''df.at[]:准确定位一个单元格'''
df.at[index, 'Position'] = position
df.at[index, 'Return'] = 0
df.at[index, 'Holdings Funds'] = position * data['close']
Balance_Funds = initial_capital - df.at[index, 'Holdings Funds']
df.at[index, 'Balance Funds'] = Balance_Funds
# 手续费计算 --- 买入资金的万分之一(5元起步)
if df.at[index, 'Holdings Funds']/10000 < 5:
service_cost = 5
else:
service_cost = round(df.at[index, 'Holdings Funds']/10000,2)
# 总资金计算
df.at[index, 'Total Funds'] = df.at[index, 'Holdings Funds'] + Balance_Funds - service_cost
# 买入信息 输出
time = df.at[index,'bob']
buying = df.at[index,'close']
balance =round(df.at[index, 'Total Funds'],2)
print(f'\n买入时间:{time} , 买入{position}股 , 买入价{buying}元 , 手续费{service_cost}元 , 剩余{balance}元')
a = a+1
# 已有持仓,无需操作
else:
df.at[index, 'Position'] = df.at[index-1, 'Position']
df.at[index, 'Return'] = df.at[index, 'return']
df.at[index, 'Holdings Funds'] = df.at[index, 'Position'] * df.at[index, 'close']
df.at[index, 'Balance Funds'] = df.at[index-1, 'Balance Funds']
df.at[index, 'Total Funds'] = df.at[index, 'Holdings Funds'] + Balance_Funds
# 死叉,卖出股票
elif data['Signal'] == -1:
# 当前有持仓
if position > 0:
# 指标计算
df.at[index, 'Position'] = 0
df.at[index, 'Return'] = df.at[index, 'return']
df.at[index, 'Holdings Funds'] = 0
df.at[index, 'Balance Funds'] = 0
# 清仓获得的资金
Clearing_Funds = df.at[index, 'close']* df.at[index-1, 'Position']
# 手续费计算 --- 买入资金的万分之一(5元起步)
if Clearing_Funds/10000 < 5:
service_cost = 5
else:
service_cost = round(Clearing_Funds/10000,2)
# 总资金计算
df.at[index, 'Total Funds'] = Clearing_Funds + df.at[index-1, 'Balance Funds'] - service_cost
initial_capital = df.at[index, 'Total Funds']
# 卖出输出
time = df.at[index,'bob']
selling = df.at[index,'close']
total = round(df.at[index, 'Total Funds'],2)
print(f'卖出时间:{time} , 卖出{position}股 , 卖出价{selling}元 , 手续费{service_cost}元 , 总额{total}元')
# 股数归零
position = 0
b = b+1
else: # 没有持仓,无需操作
df.at[index, 'Position'] = 0
df.at[index, 'Return'] = 0
df.at[index, 'Holdings Funds'] = 0
df.at[index, 'Balance Funds'] = 0
df.at[index, 'Total Funds'] = df.at[index - 1, 'Total Funds']
#买卖次数统计
if a > b:
print(f'\033[1;31m\n买入次数为{a}次,卖出次数为{b}次,当前为持仓状态。\033[0m')
else:
print(f'\033[1;31m\n买入次数为{a}次,卖出次数为{b}次,当前为空仓状态。\033[0m')
return df
# 调用买卖函数策略
initial_capitals =100000
df = buy_sell_stock(df,initial_capitals)
输出结果:
可以发现共交易了21次,最后赚取了86495.9元。
假设另外一策略为买入后不进行操作,为了更好的与机器学习模型的策略作出对比。现在通过计算“一直持有”策略和“指标分析”策略的累积收益率,通过折线图直观分析他们之间的对比,
# 策略累积收益
df['Strategy_return'] = (df['Total Funds']-initial_capitals)/initial_capitals
# 持仓累积收益 --- (不操作)
begin_buy = df[df['Total Funds'].notnull()]['close'].values[0] # 策略开始买时的价格
df['Cumulative_return'] = (df['close']-begin_buy)/begin_buy # 假设此处不考虑手续费
# 两者累积收益对比
plt.figure(figsize=(16,8),dpi=150)
plt.plot(df['bob'],df['Cumulative_return'],label = '始终持有策略收益率',c='green')
plt.plot(df['bob'],df['Strategy_return'], label = '金叉死叉策略收益率',c='orange')
plt.title('不同策略的累积收益率')
plt.legend()
plt.show()
# 导出df文件f
#df.to_excel(f'金死叉策略 - {symbol}.xlsx')
#df
输出结果:
可以明显发现两者策略到最后都是盈利的,但机器学习的策略更优与一直持有的策略。在机器学习模型中,不凡看到有些累积收益是直线,那些是属于空仓时期,那时没有持有股票,自然也没有收益的累积变化。为了更好的评估机器学习策略的效果,通过夏普比率和最大回撤的计算。其中夏普比率越大越好,最大回撤越小越好!
# 夏普比率
def sharpe_ratio(data):
data_new = data.dropna()
return_mean = np.mean(data_new['Strategy_return']) # 回报率均值
r = 0.05 # 无风险利率
return_std = np.std(data_new['Return']) # 回报波动率
# 夏普比率
XiaPu_rate = (return_mean - r)/return_std
print('夏普比率为:',XiaPu_rate)
sharpe_ratio(df)
输出结果:
# 最大回撤
def Max_Drawdown(data):
'''data:数组、列表格式'''
Max_Fund = data[0]
Max_drawdown = 0
for i in range(1, len(data)):
if data[i] > Max_Fund:
Max_Fund = data[i] # 最大的值
else:
drawdown = (Max_Fund - data[i]) / Max_Fund # 回撤计算
if drawdown > Max_drawdown:
Max_drawdown = drawdown
Max_index = np.argmax(data[:i] == Max_Fund) # 最大的值对应的索引
Min_index = i # 最小的值对应的索引
Min_Fund =data[i] # 最小的值
return Max_drawdown, Max_Fund,Min_Fund,Max_index,Min_index
#最大回撤函数调用
df_new = df['Total Funds'].dropna().reset_index(drop=True)
df_new = df_new.tolist()
Max_drawdown, Max_Fund,Min_Fund,Max_index,Min_index = Max_Drawdown(df_new)
print(f'最大值是:{Max_Fund} ; 最小值是:{Min_Fund}')
print(f'最大回撤是:{Max_drawdown:.2%}')
输出结果:
本文结束,若存在错误或者存在不足,欢迎指正!
本文仅供参考,投资有风险,切勿将该分析方法盲目用到实际之中!