数据来源东方财富网,仅供个人学习交流使用,严禁用于各种违法违规行为。
Part1:定义一个爬虫方法
import os
import requests
import pandas as pd
import time
# 基金代码列表
fund_codes = [
# '009272'
# ,
'008205'
# ,'011062'
# ,'900050'
# ,'004043'
# ,'013752'
# ,'009267'
# ,'006962'
# ,'010324'
# ,'160514'
]
start_date = '2022-01-01'
end_date = '2024-11-18'
模拟一个浏览器头,并定义一个爬虫方法:
def fetch_fund_data(fund_code, start_date, end_date):
base_url = "https://api.fund.eastmoney.com/f10/lsjz"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36',
'Referer': 'https://fund.eastmoney.com/',
'Accept': 'application/json, text/javascript, */*; q=0.01',
}
page = 1
all_data = []
while True:
params = {
'fundCode': fund_code,
'pageIndex': page,
'pageSize': 20,
'startDate': start_date,
'endDate': end_date,
}
response = requests.get(base_url, headers=headers, params=params)
# 检查请求是否成功
if response.status_code != 200:
print(f"Request failed for fund {fund_code} with status code:", response.status_code)
break
data = response.json()
# 检查数据结构
if 'Data' not in data or 'LSJZList' not in data['Data']:
print(f"Unexpected data format for fund {fund_code}.")
break
if not data['Data']['LSJZList']:
break
all_data.extend(data['Data']['LSJZList'])
page += 1
time.sleep(0.2) # 延时避免频繁请求,可以通过调整间隔
return all_data
def save_to_csv(fund_data, fund_code):
# 生成文件名
filename = f"fund_{fund_code}_data.csv"
downloads_folder = os.path.join(os.path.expanduser("~"), "Downloads")
file_path = os.path.join(downloads_folder, filename)
# 将数据保存到个人电脑的downloads下载文件夹
df = pd.DataFrame(fund_data)
df.to_csv(file_path, index=False, encoding='utf-8-sig')
print(f"Data for fund {fund_code} saved to {file_path}")
调度方法,存储爬取基金数据。
# 遍历每个基金代码,获取数据并保存
for fund_code in fund_codes:
print(f"Fetching data for fund {fund_code}...")
fund_data = fetch_fund_data(fund_code, start_date, end_date)
if fund_data: # 检查是否有数据
save_to_csv(fund_data, fund_code)
else:
print(f"No data fetched for fund {fund_code}.")
Fetching data for fund 008205…
Data for fund 008205 saved to
第一步结束,把爬取的信息存储在本地的某个文件夹(这里我定义的是windows电脑的下载文件夹),可以自行改动。
Part2:对爬取数据的基金净值做分析,计算不同周期下的最高最低收益率,并返回两类收益率和对应的买入卖出时间点
def calculate_max_min_returns(file_path, date_column, value_column, fund_code, holding_period_months=12):
"""
计算指定基金在至少持有一定月数后的最高和最低回报率。
参数:
- file_path (str): CSV 文件的路径。
- date_column (str): 数据记录日期的列名。
- value_column (str): 基金累计净值的列名。
- fund_code (str): 基金代码(用于输出提示)。
- holding_period_months (int): 至少持有的月份数,默认为12个月。
返回:
- max_return, min_return, best_buy_date, best_sell_date, worst_buy_date, worst_sell_date
"""
# 加载基金净值数据
df = pd.read_csv(file_path)
# 格式化日期字段和排序
df[date_column] = pd.to_datetime(df[date_column])
df.sort_values(by=date_column, inplace=True)
# 初始化最大和最小回报率变量
max_return = float('-inf')
min_return = float('inf')
best_buy_date = None
best_sell_date = None
worst_buy_date = None
worst_sell_date = None
# 遍历每一个买入日期
for i, buy_row in df.iterrows():
buy_date = buy_row[date_column]
buy_value = buy_row[value_column]
# 找到买入日期至少 holding_period_months 个月后的所有交易日
months_later_date = buy_date + pd.DateOffset(months=holding_period_months)
sell_candidates = df[df[date_column] >= months_later_date]
if sell_candidates.empty:
break # 如果没有满足条件的日期,跳出循环
# 遍历所有符合条件的卖出日期
for _, sell_row in sell_candidates.iterrows():
sell_date = sell_row[date_column]
sell_value = sell_row[value_column]
# 计算回报率
return_rate = (sell_value - buy_value) / buy_value
# 更新最高回报
if return_rate > max_return:
max_return = return_rate
best_buy_date = buy_date
best_sell_date = sell_date
# 更新最低回报
if return_rate < min_return:
min_return = return_rate
worst_buy_date = buy_date
worst_sell_date = sell_date
# 输出结果
print(f"基金代码: {fund_code}")
print(f"最高回报率: {max_return * 100:.2f}% (买入日期: {best_buy_date.date()}, 卖出日期: {best_sell_date.date()})")
print(f"最低回报率: {min_return * 100:.2f}% (买入日期: {worst_buy_date.date()}, 卖出日期: {worst_sell_date.date()})")
# 当作为参数传入别的函数时,解除注释内容
# return max_return, min_return, best_buy_date, best_sell_date, worst_buy_date, worst_sell_date
调度一下后,
# 批量计算每个基金的最大和最小回报
for fund_code in fund_codes:
file_path = os.path.join(os.path.expanduser("~"), "Downloads", f"fund_{fund_code}_data.csv")
# 检查文件是否存在
if not os.path.exists(file_path):
print(f"Warning: 未找到基金 {fund_code} 的净值信息文件 {file_path},跳过该基金。")
continue # 跳过当前基金,进入下一个循环
# # 输出当前基金代码,并调用计算函数
# 参数:
# - file_path (str): CSV 文件的路径。
# - date_column (str): 数据记录日期的列名。
# - value_column (str): 基金累计净值的列名。
# - fund_code (str): 基金代码(用于输出提示)。
# - holding_period_months (int): 至少持有的月份数,默认为12个月。
print(f"\n计算基金 {fund_code} 的回报情况:")
calculate_max_min_returns(file_path, date_column='FSRQ', value_column='LJJZ', fund_code=fund_code, holding_period_months=12)
计算基金 008205 的回报情况:
基金代码: 008205
最高回报率: 8.37% (买入日期: 2022-01-04, 卖出日期: 2024-11-15)
最低回报率: 1.06% (买入日期: 2022-01-28, 卖出日期: 2023-01-30)
支持提交多个基金代码,支持设定不同时间范围内的基金变化。灵活度很高,欢迎交流。