mock_data.py
"""
模拟数据生成模块
用于生成测试数据,以便程序在没有账号的情况下也能进行模拟测试
"""
import logging
import os
from typing import Dict, List, Any
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
logger = logging.getLogger("AIQuant.mock_data")
logger.setLevel(logging.INFO)
def generate_random_kline_data(
symbol: str,
start_date: str = "2023-01-01",
end_date: str = "2023-12-31",
frequency: str = "d",
volatility: float = 0.01,
trend: float = 0.0015
) -> pd.DataFrame:
"""
生成随机 K 线数据
Args:
symbol: 股票代码
start_date: 起始日期
end_date: 结束日期
frequency: 频率(d: 日线,h: 小时线,m: 分钟线)
volatility: 波动率
trend: 趋势强度
Returns:
生成的 K 线数据 DataFrame
"""
logger.info(f"生成 {symbol} 的模拟 K 线数据")
# 生成日期范围
start = datetime.strptime(start_date, "%Y-%m-%d")
end = datetime.strptime(end_date, "%Y-%m-%d")
days = (end - start).days + 1
if frequency == "d":
dates = [start + timedelta(days=i) for i in range(days)]
elif frequency == "h":
dates = [start + timedelta(hours=i) for i in range(days * 24)]
elif frequency == "m":
dates = [start + timedelta(minutes=i) for i in range(days * 24 * 60)]
else:
raise ValueError(f"不支持的频率: {frequency}")
# 生成随机价格序列
np.random.seed(hash(symbol) % 4294967296) # 使用股票代码生成随机种子
price = 10.0 + np.random.uniform(-5, 5) # 随机起始价格
prices = [price]
# 使用几何布朗运动模型生成价格序列
for i in range(1, len(dates)):
# 几何布朗运动公式:dS/S = μdt + σdW
drift = trend # 漂移项(预期收益率)
volatility_term = volatility * np.random.normal() # 波动率项
# 价格变化
dS = price * (drift + volatility_term)
price = price + dS
# 确保价格不会太离谱
price = max(0.01, price)
prices.append(price)
# 生成 OHLCV 数据
high = [p * (1 + np.random.uniform(0, volatility)) for p in prices]
low = [p * (1 - np.random.uniform(0, volatility)) for p in prices]
open_price = [
p * (1 + np.random.uniform(-volatility/2, volatility/2))
for p in prices
]
close = prices
volume = np.random.randint(1000000, 10000000, size=len(dates))
# 创建 DataFrame
data = {
"datetime": dates,
"open": open_price,
"high": high,
"low": low,
"close": close,
"volume": volume
}
df = pd.DataFrame(data)
df.set_index("datetime", inplace=True)
# 计算均线等基础指标
df["ma5"] = df["close"].rolling(window=5).mean()
df["ma10"] = df["close"].rolling(window=10).mean()
df["ma20"] = df["close"].rolling(window=20).mean()
df["ma60"] = df["close"].rolling(window=60).mean()
logger.info(f"生成成功,共 {len(df)} 条记录")
return df
def generate_stock_pool(n: int = 10) -> List[str]:
"""
生成股票池
Args:
n: 股票数量
Returns:
股票池列表
"""
logger.info(f"生成包含 {n} 只股票的股票池")
stock_pool = []
for i in range(n):
# 随机生成股票代码(模拟沪深交易所)
exchange = "SZ" if np.random.rand() > 0.5 else "SH"
code = str(np.random.randint(100000, 999999)).zfill(6)
stock_pool.append(f"{code}.{exchange}")
logger.info(f"股票池生成成功: {stock_pool}")
return stock_pool
def generate_mock_account_data() -> Dict[str, Any]:
"""
生成模拟账户数据
Returns:
模拟账户数据
"""
logger.info("生成模拟账户数据")
account_data = {
"account": "1234567890",
"balance": 1000000.0,
"available": 800000.0,
"margin": 200000.0,
"profit": 50000.0,
"positions": {
"000001.SZ": {
"symbol": "000001.SZ",
"name": "平安银行",
"volume": 10000,
"price": 12.50,
"value": 125000.0
},
"600036.SH": {
"symbol": "600036.SH",
"name": "招商银行",
"volume": 5000,
"price": 35.20,
"value": 176000.0
}
}
}
logger.info("账户数据生成成功")
return account_data
def cache_data_to_local(data: pd.DataFrame,
symbol: str,
frequency: str = "d") -> str:
"""
将数据缓存到本地
Args:
data: 数据
symbol: 股票代码
frequency: 频率
Returns:
缓存文件路径
"""
logger.info(f"将 {symbol} 的数据缓存到本地")
# 创建数据缓存目录
cache_dir = "data_cache"
if not os.path.exists(cache_dir):
os.makedirs(cache_dir, exist_ok=True)
# 生成文件名
filename = f"{symbol}_{frequency}_{datetime.now().strftime('%Y%m%d')}.csv"
filepath = os.path.join(cache_dir, filename)
# 保存数据
data.to_csv(filepath)
logger.info(f"数据缓存成功: {filepath}")
return filepath
def load_data_from_local(symbol: str, frequency: str = "d") -> pd.DataFrame:
"""
从本地加载数据
Args:
symbol: 股票代码
frequency: 频率
Returns:
加载的数据
"""
logger.info(f"从本地加载 {symbol} 的数据")
cache_dir = "data_cache"
if not os.path.exists(cache_dir):
logger.warning("数据缓存目录不存在")
return pd.DataFrame()
# 查找最新的缓存文件
files = [f for f in os.listdir(cache_dir) if f.startswith(
f"{symbol}_{frequency}") and f.endswith(".csv")]
if not files:
logger.warning(f"未找到 {symbol} 的缓存数据")
return pd.DataFrame()
# 按时间戳排序,获取最新的文件
files.sort(key=lambda x: os.path.getmtime(
os.path.join(cache_dir, x)), reverse=True)
latest_file = files[0]
filepath = os.path.join(cache_dir, latest_file)
# 加载数据
df = pd.read_csv(filepath, index_col=0, parse_dates=True)
logger.info(f"数据加载成功: {filepath}, 共 {len(df)} 条记录")
return df
# 测试函数
if __name__ == "__main__":
import os
# 测试生成 K 线数据
symbol = "000001.SZ"
kline_data = generate_random_kline_data(symbol)
print(f"K 线数据形状: {kline_data.shape}")
print(kline_data.head())
# 测试生成股票池
stock_pool = generate_stock_pool(5)
print(f"股票池: {stock_pool}")
# 测试生成账户数据
account_data = generate_mock_account_data()
print(f"账户数据: {account_data}")
# 测试数据缓存和加载
filepath = cache_data_to_local(kline_data, symbol)
loaded_data = load_data_from_local(symbol)
print(f"加载的数据形状: {loaded_data.shape}")
# 清理测试文件
if os.path.exists(filepath):
os.remove(filepath)