2.6.3 使用模拟数据进行训练
在本项目中,原计划使用强化学习来优化我的交易策略,以提高在从HMM采样的模拟数据上的表现。然而鉴目前的模型,我认为这可能效果不佳。原因在于,HMM生成的样本与HMM训练时使用的价格数据并不相似。特别是,HMM在隐藏状态之间切换得太快,使得其整体行为比实际比特币数据更一致。接下来将尝试选择买入/卖出阈值,以在从HMM采样的模拟数据上优化策略的表现,然后在实际测试期间对该策略进行评估。结果表明,该策略在中等较高的摩擦率下通常不会采取任何操作,因此其效果远不如在真实数据上优化阈值所得到的策略。
(1)下面代码用于生成模拟的价格数据,基于HMM模型的参数和状态转移概率来模拟5000天的回报率,并将这些回报率转化为价格数据。最终,代码将生成的模拟价格数据绘制为对数尺度图,以便于观察模拟数据的趋势和波动。
s=0 # 初始状态
r=0 # 初始回报率
n=5000 # 模拟的天数
Rsampled=np.zeros(n) # 用于存储生成的回报率数据
for t in range(n):
# 根据HMM的转移概率选择下一个状态
s=np.random.choice(range(3), p=hmm.TP[:, s])
# 根据当前状态生成回报率,并加入噪声
r=hmm.beta*r + hmm.MU[s] + np.random.randn() * hmm.sig
Rsampled[t] = r
# 将回报率转化为价格数据
Psampled=np.exp(np.cumsum(Rsampled))
plt.semilogy(Psampled)
plt.ylabel('从HMM采样的模拟价格数据')
plt.xlabel('天数')
plt.show()
上述代码的实现流程如下:
- 初始化变量:设置初始状态和回报率变量。
- 模拟数据生成:利用HMM的转移概率和参数,生成5000天的模拟价格数据。
- 状态转移:根据HMM的状态转移概率从当前状态选择下一个状态。
- 价格回报率生成:根据当前状态生成回报率,并加入噪声。
- 累积价格:将生成的回报率累积并计算价格数据。
- 绘图:绘制模拟价格数据的对数图,以展示生成的数据随时间的变化,如图2-12所示。
图2-12 模拟价格数据的对数图
(2)下面代码旨在使用模拟的价格数据(Psampled)来优化买入和卖出的策略。首先,代码通过back_train_pol函数计算不同摩擦率下的回报率和交易次数。接着,它通过对不同摩擦率下的策略进行回测,找到最佳的买入和卖出阈值,并绘制这些阈值以及对应的优化策略回报。最后,将优化策略的回报与简单的比特币投资的回报进行比较,展示摩擦率对策略性能的影响。
R, T = back_train_pol(hmm, Psampled)
Back_train_Return = np.zeros(100)
Buy_Price = np.zeros(100)
Sell_Price = np.zeros(100)
for i in range(100):
f = friction[i]
FR = np.log(R) + T * np.log(1 - f)
ix = np.argmax(FR)
i1 = np.mod(ix, res)
i0 = int((ix - i1) / res)
Back_train_Return[i] = np.exp(FR[i0, i1])
Buy_Price[i] = X[i0]
Sell_Price[i] = X[i1]
plt.plot(friction, Buy_Price, label='Buy threshold')
plt.plot(friction, Sell_Price, label='Sell threshold')
plt.xlabel('Friction rate')
plt.ylabel('Expected abnormal rate')
plt.legend()
plt.show()
plt.plot(friction, Back_train_Return, label='Optimized buy/sell policy')
plt.plot([0, np.max(friction)], [Psampled[-1] / Psampled[0], Psampled[-1] / Psampled[0]], label='Bitcoin investment')
plt.ylim([0, 1.1 * np.max(Back_train_Return)])
plt.xlabel('Friction rate')
plt.ylabel('Back testing return')
plt.legend()
plt.show()
执行上述代码后会绘制两个可视化图表,展示不同摩擦率下的优化策略性能:
- 买入/卖出阈值图:如图2-13所示,显示不同摩擦率下的买入和卖出阈值(Buy_Price 和 Sell_Price),横坐标为摩擦率,纵坐标为预期异常收益率。这张图帮助你理解在不同摩擦率下,优化策略选择的买入和卖出点。
- 优化策略回报图:如图2-14所示,显示不同摩擦率下的优化买入/卖出策略的回报(Back_train_Return),并将其与简单比特币投资的回报进行比较。图中会显示:优化策略的回报随摩擦率的变化、作为基准的比特币投资的回报(常数线,表示在没有进行优化策略下的投资回报)。
图2-13 买入/卖出阈值图
图2-14 优化策略回报图
这些图表将帮助我们评估不同摩擦率下优化策略的表现,并与传统比特币投资策略进行对比,从而更好地理解摩擦率对策略性能的影响。
(3)定义回测函数 back_test_pol,用于测试不同的买入和卖出策略在给定价格数据上的表现。函数 back_test_pol 输入了一个模型 mdl、摩擦率数组 friction、买入和卖出阈值 Buy_Price 和 Sell_Price 以及价格数据 Prices。它输出了不同策略下的投资组合状态 Portfolios 和价值 Value。投资组合状态记录了每个策略在每一天的现金和比特币持有量,价值则表示这些持有量的总价值。通过这个函数,用户可以评估不同策略在测试数据上的表现。
# 回测函数
def back_test_pol(mdl, friction, Buy_Price, Sell_Price, Prices):
P = mdl.get_hidden_state_probabilities(Prices) # 获取每个隐藏状态的概率
EAR = mdl.get_expected_abnormal_rates(Prices) # 获取预期异常收益率
n = P.shape[0] # 数据点数量
Portfolios = np.zeros((100, n, 2)) # 初始化投资组合状态数组
Value = np.zeros((100, n)) # 初始化投资组合价值数组
for i in range(100):
buy = Buy_Price[i] # 当前策略的买入阈值
sell = Sell_Price[i] # 当前策略的卖出阈值
rate = 1 - friction[i] # 计算有效资金比例
Portfolios[i, 0, 0] = 1 # 初始时全现金
a = 'c' # 初始状态为现金
for t in range(n-1):
if a == 'c' and EAR[t] > buy:
Portfolios[i, t, 1] = rate * Portfolios[i, t, 0] / Prices[t] # 买入比特币
Portfolios[i, t, 0] = 0 # 现金变为0
a = 'b' # 进入比特币状态
if a == 'b' and EAR[t] < sell:
Portfolios[i, t, 0] = rate * Portfolios[i, t, 1] * Prices[t] # 卖出比特币
Portfolios[i, t, 1] = 0 # 比特币变为0
a = 'c' # 进入现金状态
Portfolios[i, t+1] = Portfolios[i, t] # 记录当前状态
Value[i] = Portfolios[i, :, 0] + Portfolios[i, :, 1] * Prices # 计算总价值
return Portfolios, Value
# 执行回测
Portfolios1, V1 = back_test_pol(hmm, friction, Buy_Price, Sell_Price, BC_Prices)
(4)下面代码用于绘制不同摩擦率下的回测收益图,比较了三种策略的表现:前一天趋势跟随策略(previous day trend follower)、基于HMM优化的买入/卖出策略(back-train optimized HMM buy/sell policy)和简单的比特币投资(Bitcoin investment)。通过对比这些策略在不同摩擦率下的回测收益,用可视化图表展示了各策略在测试期内的表现,并帮助确定最佳的交易策略。
# 计算测试期内的交易次数
ntrades_in_test_period0 = np.sum(Portfolio0[train_end:-1] * Portfolio0[train_end+1:] > 0)
# 绘制图表
plt.semilogy(friction, V0[-1] / V0[train_end] * np.power(1 - friction, ntrades_in_test_period0), label='previous day trend follower')
# 绘制基于HMM优化的买入/卖出策略的回测结果
plt.semilogy(friction, V1[:, -1] / V1[:, train_end], label='back-train optimized HMM buy/sell policy')
# 绘制比特币投资的回测结果
plt.semilogy([0, np.max(friction)], [BC_Prices[-1] / BC_Prices[train_end], BC_Prices[-1] / BC_Prices[train_end]], label='Bitcoin investment')
# 添加图例和标签
plt.legend()
plt.xlabel('friction rate')
plt.ylabel('back testing return')
plt.show()
执行效果如图2-14所示,对上述代码的具体说明如下所示:
- ntrades_in_test_period0 计算了在测试期内前一天趋势跟随策略的交易次数。
- plt.semilogy 绘制了摩擦率与回测收益之间的关系图。对数刻度用于展示不同摩擦率下的收益。
- label 参数为图例提供标签以区分不同策略。
图2-14 三种策略的回测收益图
(5)下面代码用于绘制模拟数据下的投资组合价值随时间变化的图表,比较了使用HMM优化的买入/卖出策略(HMM buy/sell policy)和简单的比特币投资(Bitcoin investment)在0.5%市场摩擦下的表现。
i = 25
# 绘制投资组合价值相对于初始值的变化
plt.plot(range(train_end, ndays), V1[i, train_end:] / V1[i, train_end], label='HMM buy/sell policy')
plt.plot(range(train_end, ndays), BC_Prices[train_end:] / BC_Prices[train_end], label='Bitcoin investment')
# 设置图表标签和标题
plt.xlabel('day')
plt.ylabel('portfolio value')
plt.title('simulation with 0.5% market friction')
# 显示图例
plt.legend()
plt.show()
执行效果如图2-15所示,展示了两种策略的投资组合价值相对于初始值的变化情况,可以帮助我们分析不同策略在给定摩擦率下的效果。
图2-15 在0.5%市场摩擦下的表现
(6)下面代码绘制了在1%市场摩擦条件下,投资组合价值随时间变化的可视化图表。比较了使用HMM优化的买入/卖出策略(HMM buy/sell policy)与简单的比特币投资(Bitcoin investment)的表现。
i = 50
# 绘制投资组合价值相对于初始值的变化
plt.plot(range(train_end, ndays), V1[i, train_end:] / V1[i, train_end], label='HMM buy/sell policy')
plt.plot(range(train_end, ndays), BC_Prices[train_end:] / BC_Prices[train_end], label='Bitcoin investment')
# 设置图表标签和标题
plt.xlabel('day')
plt.ylabel('portfolio value')
plt.title('simulation with 1% market friction')
# 显示图例
plt.legend()
plt.show()
执行效果如图2-16所示
图2-16 在1%市场摩擦下的表现
(7)下面代码绘制了在1.5%市场摩擦条件下,投资组合价值随时间变化的可视化图表,比较了使用HMM优化的买入/卖出策略(HMM buy/sell policy)与简单的比特币投资(Bitcoin investment)的表现。
i = 75
# 绘制投资组合价值相对于初始值的变化
plt.plot(range(train_end, ndays), V1[i, train_end:] / V1[i, train_end], label='HMM buy/sell policy')
plt.plot(range(train_end, ndays), BC_Prices[train_end:] / BC_Prices[train_end], label='Bitcoin investment')
# 设置图表标签和标题
plt.xlabel('day')
plt.ylabel('portfolio value')
plt.title('simulation with 1.5% market friction')
# 显示图例
plt.legend()
plt.show()
执行效果如图2-17所示
图2-17 在1.5%市场摩擦下的表现
2.6.4 结论
事实证明,本项目中实现的交易策略在市场摩擦较小的情况下表现得相当好。隐马尔可夫模型(HMM)能够检测数据中的上涨和下跌趋势,这可以用于做出交易决策。在当前形式下,我们的方法将HMM作为特征提取设备,而不是作为预测模型。如果希望进一步改进模型,以便获得一个可以模拟更接近真实时间序列数据的HMM,并允许一些更有趣的预测模型和/或强化学习。
- 一个可能的改进方法是对负对数似然(NLL)优化施加约束或正则化,以尝试获得在隐藏状态之间转换更缓慢的模型。
- 也可以尝试使用不同的HMM结构,在使用两个隐藏状态时,模型更倾向于表示增长和快速增长,而不是增长和衰退。如果没有 βri−1 类型的项,HMM的状态过于简单,无法做出精细的决策。