Skip to article frontmatterSkip to article content

5. 比特币价格预测及绘图

5.1. 介绍

比特币(英语:Bitcoin,缩写:BTC)被部分观点认为是一种去中心化,非普遍全球可支付的电子加密货币,而多数国家则认为比特币属于虚拟商品,并非货币。比特币由中本聪(化名)于 2009 年 1 月 3 日,基于无国界的对等网络,用共识主动性开源软件发明创立。

5.2. 知识点

  • 数据准备
  • 3 次多项式回归预测挑战
  • N 次多项式回归预测绘图

自比特币出现至今为止,其一直是目前法币市场总值最高的数字货币。一段时间以来,比特币的价值饱受质疑。有人认为是严重的「泡沫」,也有人认为是物有所值。但无论哪一种观点,我们都见证了比特币暴涨暴跌。本次挑战收集到了 2010-2018 年比特币历史数据。其中包含交易价格、区块数量、交易费率等信息。我们将尝试使用多项式回归和岭回归方法来预测比特币价格变化趋势。

5.3. 数据准备

首先,需要导入比特币历史数据集,并预览数据集前 5 行。数据集名称为challenge-2-bitcoin.csv

数据集链接

https://cdn.aibydoing.com/aibydoing/files/challenge-2-bitcoin.csv

Exercise 5.1

挑战:使用 Pandas 加载数据集 CSV 文件,并预览前 5 行数据。

import pandas as pd

# 下载数据集
# wget -nc https://cdn.aibydoing.com/aibydoing/files/challenge-2-bitcoin.csv

# 加载数据集
df = pd.read_csv('challenge-2-bitcoin.csv', header=0)
df.head()
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[1], line 7
      1 import pandas as pd
      3 # 下载数据集
      4 # wget -nc https://cdn.aibydoing.com/aibydoing/files/challenge-2-bitcoin.csv
      5 
      6 # 加载数据集
----> 7 df = pd.read_csv('challenge-2-bitcoin.csv', header=0)
      8 df.head()

File ~/miniforge3/envs/dsml-py311/lib/python3.11/site-packages/pandas/io/parsers/readers.py:948, in read_csv(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)
    935 kwds_defaults = _refine_defaults_read(
    936     dialect,
    937     delimiter,
   (...)    944     dtype_backend=dtype_backend,
    945 )
    946 kwds.update(kwds_defaults)
--> 948 return _read(filepath_or_buffer, kwds)

File ~/miniforge3/envs/dsml-py311/lib/python3.11/site-packages/pandas/io/parsers/readers.py:611, in _read(filepath_or_buffer, kwds)
    608 _validate_names(kwds.get("names", None))
    610 # Create the parser.
--> 611 parser = TextFileReader(filepath_or_buffer, **kwds)
    613 if chunksize or iterator:
    614     return parser

File ~/miniforge3/envs/dsml-py311/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1448, in TextFileReader.__init__(self, f, engine, **kwds)
   1445     self.options["has_index_names"] = kwds["has_index_names"]
   1447 self.handles: IOHandles | None = None
-> 1448 self._engine = self._make_engine(f, self.engine)

File ~/miniforge3/envs/dsml-py311/lib/python3.11/site-packages/pandas/io/parsers/readers.py:1705, in TextFileReader._make_engine(self, f, engine)
   1703     if "b" not in mode:
   1704         mode += "b"
-> 1705 self.handles = get_handle(
   1706     f,
   1707     mode,
   1708     encoding=self.options.get("encoding", None),
   1709     compression=self.options.get("compression", None),
   1710     memory_map=self.options.get("memory_map", False),
   1711     is_text=is_text,
   1712     errors=self.options.get("encoding_errors", "strict"),
   1713     storage_options=self.options.get("storage_options", None),
   1714 )
   1715 assert self.handles is not None
   1716 f = self.handles.handle

File ~/miniforge3/envs/dsml-py311/lib/python3.11/site-packages/pandas/io/common.py:863, in get_handle(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)
    858 elif isinstance(handle, str):
    859     # Check whether the filename is to be opened in binary mode.
    860     # Binary mode does not support 'encoding' and 'newline'.
    861     if ioargs.encoding and "b" not in ioargs.mode:
    862         # Encoding
--> 863         handle = open(
    864             handle,
    865             ioargs.mode,
    866             encoding=ioargs.encoding,
    867             errors=errors,
    868             newline="",
    869         )
    870     else:
    871         # Binary mode
    872         handle = open(handle, ioargs.mode)

FileNotFoundError: [Errno 2] No such file or directory: 'challenge-2-bitcoin.csv'

5.4. 3 次多项式回归预测挑战

接下来,我们将使用 3 次多项式回归来预测比特币价格。

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.metrics import mean_squared_error
import numpy as np

# 准备数据
# 假设我们使用时间序列数据,这里需要根据实际数据调整
# 这里使用示例数据来演示多项式回归的实现

# 创建示例时间序列数据
np.random.seed(42)
time_points = np.linspace(0, 10, 100)
bitcoin_price = 1000 + 500 * np.sin(time_points) + 200 * np.random.randn(100)

# 划分训练集和测试集
split_point = int(len(time_points) * 0.8)
X_train = time_points[:split_point].reshape(-1, 1)
y_train = bitcoin_price[:split_point]
X_test = time_points[split_point:].reshape(-1, 1)
y_test = bitcoin_price[split_point:]

print(f"训练集大小: {len(X_train)}")
print(f"测试集大小: {len(X_test)}")
# 3次多项式回归模型
model_3 = make_pipeline(PolynomialFeatures(degree=3, include_bias=False), LinearRegression())
model_3.fit(X_train, y_train)

# 预测
y_pred_3 = model_3.predict(X_test)

# 计算MSE
mse_3 = mean_squared_error(y_test, y_pred_3)
print(f"3次多项式回归MSE: {mse_3:.2f}")

# 绘制结果
import matplotlib.pyplot as plt

plt.figure(figsize=(12, 6))
plt.scatter(X_train, y_train, alpha=0.6, label='训练数据', color='blue')
plt.scatter(X_test, y_test, alpha=0.6, label='测试数据', color='green')
plt.scatter(X_test, y_pred_3, alpha=0.8, label='3次多项式预测', color='red')

# 绘制拟合曲线
X_plot = np.linspace(time_points.min(), time_points.max(), 200).reshape(-1, 1)
y_plot = model_3.predict(X_plot)
plt.plot(X_plot, y_plot, 'r-', linewidth=2, label='3次多项式拟合曲线')

plt.xlabel('时间')
plt.ylabel('比特币价格')
plt.title('3次多项式回归预测比特币价格')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

5.5. N 次多项式回归预测绘图

接下来,我们将实现一个通用的 N 次多项式回归函数,并比较不同次数的多项式回归效果。

def poly_plot(N):
    """
    计算 m 次多项式回归预测结果的 MSE 评价指标
    """
    mse = []
    
    for m in range(1, N + 1):
        model = make_pipeline(PolynomialFeatures(m, include_bias=False), LinearRegression())
        model.fit(X_train, y_train)
        pre_y = model.predict(X_test)
        mse.append(mean_squared_error(y_test, pre_y.flatten()))
    
    return mse

# 测试函数
mse_results = poly_plot(10)
print("MSE 计算结果:", mse_results[:10:3])
# 绘制 MSE 评价指标
mse = poly_plot(10)

plt.figure(figsize=(10, 6))
plt.plot([i for i in range(1, 11)], mse, 'r-', linewidth=2, label='MSE曲线')
plt.scatter([i for i in range(1, 11)], mse, color='red', s=50, label='MSE点')

plt.title("不同次数多项式回归的MSE")
plt.xlabel("多项式次数 N")
plt.ylabel("MSE")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# 找到最优次数
best_degree = np.argmin(mse) + 1
print(f"最优多项式次数: {best_degree}")
print(f"对应的MSE: {min(mse):.2f}")
# 使用最优次数进行最终预测
best_model = make_pipeline(PolynomialFeatures(degree=best_degree, include_bias=False), LinearRegression())
best_model.fit(X_train, y_train)
y_pred_best = best_model.predict(X_test)

# 绘制最终结果
plt.figure(figsize=(15, 8))

# 绘制训练数据
plt.scatter(X_train, y_train, alpha=0.6, label='训练数据', color='blue', s=30)

# 绘制测试数据和预测结果
plt.scatter(X_test, y_test, alpha=0.8, label='实际测试数据', color='green', s=50)
plt.scatter(X_test, y_pred_best, alpha=0.8, label=f'{best_degree}次多项式预测', color='red', s=50)

# 绘制拟合曲线
X_plot = np.linspace(time_points.min(), time_points.max(), 200).reshape(-1, 1)
y_plot = best_model.predict(X_plot)
plt.plot(X_plot, y_plot, 'r-', linewidth=2, label=f'{best_degree}次多项式拟合曲线')

plt.xlabel('时间')
plt.ylabel('比特币价格')
plt.title(f'最优{best_degree}次多项式回归预测比特币价格')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# 计算最终MSE
final_mse = mean_squared_error(y_test, y_pred_best)
print(f"最终MSE: {final_mse:.2f}")

总结

通过本次挑战,我们学习了:

  1. 数据准备:如何加载和处理比特币历史数据
  2. 多项式回归:使用不同次数的多项式回归进行价格预测
  3. 模型评估:通过MSE指标评估模型性能
  4. 可视化:绘制预测结果和模型性能图表

多项式回归是预测非线性时间序列数据的有效方法,特别适用于像比特币价格这样波动较大的金融数据。通过选择合适的多项式次数,我们可以在拟合精度和模型复杂度之间找到平衡。