本文作者:咔咔

R语言基金持仓实时变动

R语言基金持仓实时变动摘要: 由于金融数据提供商(如Wind、Bloomberg)的API通常非常昂贵且不对外开放,我们将主要使用公开、免费的财经数据API来实现这个流程,下面我将为你提供一个完整、可操作的R语...

由于金融数据提供商(如Wind、Bloomberg)的API通常非常昂贵且不对外开放,我们将主要使用公开、免费的财经数据API来实现这个流程,下面我将为你提供一个完整、可操作的R语言解决方案。

核心思路

  1. 数据源选择:选择稳定、免费的财经数据API,我们将主要使用 tidyquant 包(基于Yahoo Finance)和 RSQLite 包。
  2. 数据获取
    • 基金持仓数据:通过网页抓取或特定API获取基金的定期报告(如季报)中的持仓明细,这部分数据不是实时的,而是定期披露的(每季度更新一次),这是获取基金“持仓”信息的唯一可靠途径。
    • 基金净值/价格数据:这部分可以做到准实时(每5分钟或每日更新),用来反映基金本身的实时表现。
    • 持仓股票的实时行情数据:获取基金持仓的各个股票的实时价格,用于计算组合的实时市值和盈亏。
  3. 数据存储:使用本地SQLite数据库来存储历史数据,便于追踪变动和进行分析。
  4. 数据展示与分析:使用 ggplot2, plotly, DT 等包进行可视化展示和数据分析。

详细步骤与R代码实现

第一步:环境准备

安装并加载所需的R包。

R语言基金持仓实时变动
(图片来源网络,侵删)
# install.packages(c("tidyquant", "DBI", "RSQLite", "rvest", "jsonlite", "plotly", "ggplot2", "DT", "dplyr", "lubridate"))
library(tidyquant)
library(DBI)
library(RSQLite)
library(rvest)
library(jsonlite)
library(plotly)
library(ggplot2)
library(DT)
library(dplyr)
library(lubridate)

第二步:建立本地数据库

我们将创建一个SQLite数据库来存储我们获取的数据,这样下次运行时可以直接读取,无需重复抓取。

# 创建或连接到SQLite数据库
db_path <- "fund_portfolio.db"
con <- dbConnect(RSQLite::SQLite(), db_path)
# 创建表(如果不存在)
dbExecute(con, "
CREATE TABLE IF NOT EXISTS fund_holdings (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    fund_code TEXT,
    report_date TEXT,
    stock_code TEXT,
    stock_name TEXT,
    shares NUMERIC,
    market_value NUMERIC,
    proportion NUMERIC
)
")
dbExecute(con, "
CREATE TABLE IF NOT EXISTS daily_nav (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    fund_code TEXT,
    date TEXT,
    nav NUMERIC,
    nav_growth NUMERIC
)
")
dbExecute(con, "
CREATE TABLE IF NOT EXISTS stock_quotes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    stock_code TEXT,
    quote_date TEXT,
    open NUMERIC,
    high NUMERIC,
    low NUMERIC,
    close NUMERIC,
    volume NUMERIC
)
")
cat("数据库和表创建/连接成功,\n")

第三步:数据获取与存储

这是整个流程的核心,我们以一个真实的基金为例,比如易方达蓝筹精选混合(005827)

1 获取基金定期持仓数据(网页抓取)

我们使用 rvest 包从天天基金网抓取最新的季度持仓数据。注意:网页结构可能会变,此代码可能需要相应调整。

get_fund_holdings <- function(fund_code = "005827") {
  url <- paste0("https://fund.eastmoney.com/pingzhongdata/", fund_code, ".js")
  tryCatch({
    # 从JS文件中解析JSON数据
    js_content <- readLines(url, warn = FALSE)
    json_text <- gsub(".*?var fundUnitNetValueData = (.*?);", "\\1", js_content, perl = TRUE)
    # 解析最新的持仓数据(通常在`fundPortfolioStockTable`中)
    portfolio_data <- fromJSON(json_text)$fundPortfolioStockTable
    if (is.null(portfolio_data) || nrow(portfolio_data) == 0) {
      warning("未找到持仓数据,可能基金代码错误或数据未更新。")
      return(NULL)
    }
    # 数据清洗和转换
    holdings <- portfolio_data %>%
      select(股票代码, 股票名称, 持仓股数, 持仓市值, 占净值比例) %>%
      rename(
        stock_code = 股票代码,
        stock_name = 股票名称,
        shares = 持仓股数,
        market_value = 持仓市值,
        proportion = 占净值比例
      ) %>%
      mutate(
        fund_code = fund_code,
        report_date = as.character(Sys.Date()) # 这里简化处理,实际应从JS中提取报告期
      ) %>%
      filter(!is.na(stock_code)) # 过滤掉无效行
    return(holdings)
  }, error = function(e) {
    warning(paste("抓取持仓数据失败:", e$message))
    return(NULL)
  })
}
# 获取并存储持仓数据
fund_code <- "005827"
holdings <- get_fund_holdings(fund_code)
if (!is.null(holdings)) {
  # 写入数据库(如果数据已存在则先删除)
  dbExecute(con, paste0("DELETE FROM fund_holdings WHERE fund_code = '", fund_code, "'"))
  dbWriteTable(con, "fund_holdings", holdings, append = TRUE, row.names = FALSE)
  cat("持仓数据已成功更新到数据库,\n")
  # 在R环境中显示
  datatable(holdings, options = list(pageLength = 10, scrollX = TRUE))
} else {
  cat("未能获取到持仓数据,\n")
}

2 获取基金每日净值数据

使用 tidyquant 包的 tq_get() 函数,非常方便。

R语言基金持仓实时变动
(图片来源网络,侵删)
get_fund_nav <- function(fund_code = "005827") {
  # 注意:Yahoo Finance的代码可能需要转换,005827在雅虎上是110027.SS
  # 这是一个常见的难点,这里我们假设可以直接用
  # 对于国内基金,通常需要映射到一个有效的Yahoo代码
  # 我们这里用一个示例代码
  yf_code <- "110027.SS" # 示例代码,请务必确认正确
  nav_data <- tq_get(yf_code,
                     get = "stock.prices",
                     from = Sys.Date() - 365, # 获取一年数据
                     to = Sys.Date())
  if (nrow(nav_data) > 0) {
    nav_data <- nav_data %>%
      select(date, close) %>%
      rename(nav = close) %>%
      mutate(
        fund_code = fund_code,
        date = as.character(date),
        nav_growth = (nav - lag(nav)) / lag(nav) # 计算日增长率
      ) %>%
      filter(!is.na(nav_growth))
    return(nav_data)
  }
  return(NULL)
}
# 获取并存储净值数据
nav_data <- get_fund_nav(fund_code)
if (!is.null(nav_data)) {
  dbWriteTable(con, "daily_nav", nav_data, append = TRUE, row.names = FALSE)
  cat("基金净值数据已更新,\n")
  # 绘制净值走势图
  nav_data %>%
    plot_ly(x = ~date, y = ~nav, type = 'scatter', mode = 'lines', name = '基金净值') %>%
    layout(title = paste0(fund_code, " 净值走势"), xaxis = list(title = "日期"), yaxis = list(title = "净值"))
}

3 获取持仓股票的实时行情数据

同样使用 tidyquant,我们将从数据库中读取持仓股票列表,然后批量获取它们的行情。

get_stock_quotes <- function(stock_codes) {
  # Yahoo Finance的股票代码格式处理(添加.SS或.SZ)
  # 这是一个简化的处理,实际中需要更健壮的逻辑
  yf_codes <- paste0(stock_codes, ".SS")
  # 使用 tq_get 批量获取数据
  quotes <- tq_get(yf_codes,
                   get = "stock.prices",
                   from = Sys.Date() - 5, # 获取最近5天数据
                   to = Sys.Date())
  if (nrow(quotes) > 0) {
    quotes <- quotes %>%
      mutate(quote_date = as.character(date))
    return(quotes)
  }
  return(NULL)
}
# 从数据库读取最新的持仓股票代码
holdings_stocks <- dbGetQuery(con, paste0("SELECT DISTINCT stock_code FROM fund_holdings WHERE fund_code = '", fund_code, "'"))
stock_codes <- holdings_stocks$stock_code
if (length(stock_codes) > 0) {
  stock_quotes <- get_stock_quotes(stock_codes)
  if (!is.null(stock_quotes)) {
    # 写入数据库(先删除旧数据)
    dbExecute(con, "DELETE FROM stock_quotes WHERE quote_date = date('now', 'localtime')")
    dbWriteTable(con, "stock_quotes", stock_quotes, append = TRUE, row.names = FALSE)
    cat("持仓股票行情数据已更新,\n")
  }
}

第四步:数据分析与可视化

我们已经将数据存入数据库,接下来可以进行各种分析。

1 持仓分析(基于最新报告)

# 读取最新持仓数据
latest_holdings <- dbGetQuery(con, "
  SELECT * FROM fund_holdings
  WHERE (fund_code, report_date) IN (
    SELECT fund_code, MAX(report_date) FROM fund_holdings GROUP BY fund_code
  )
")
# 绘制持仓行业分布(假设数据中有行业列,这里我们用股票名称模拟)
# 在实际应用中,你需要通过股票代码去匹配行业信息
latest_holdings %>%
  mutate(industry = substr(stock_name, 1, 4)) %>% # 简单模拟,用股票名前4个字符作为行业
  group_by(industry) %>%
  summarise(total_proportion = sum(proportion)) %>%
  arrange(desc(total_proportion)) %>%
  plot_ly(labels = ~industry, values = ~total_proportion, type = 'pie', textinfo = 'label+percent') %>%
  layout(title = paste0(fund_code, " 持仓行业分布"))

2 实时组合市值估算(高级应用)

这是最接近“实时变动”的分析,我们需要将最新的持仓股票价格与持仓数量相乘,来估算组合的实时价值。

# 1. 读取最新持仓
latest_holdings_db <- dbGetQuery(con, "
  SELECT h.* FROM fund_holdings h
  JOIN (SELECT fund_code, MAX(report_date) as max_date FROM fund_holdings GROUP BY fund_code) latest
  ON h.fund_code = latest.fund_code AND h.report_date = latest.max_date
  WHERE h.fund_code = '005827'
")
# 2. 读取最新的股票价格
latest_quotes_db <- dbGetQuery(con, "
  SELECT DISTINCT sq.* FROM stock_quotes sq
  JOIN (
    SELECT stock_code, MAX(quote_date) as max_date FROM stock_quotes GROUP BY stock_code
  ) latest_q ON sq.stock_code = latest_q.stock_code AND sq.quote_date = latest_q.max_date
")
# 3. 合并数据并计算
portfolio_value <- latest_holdings_db %>%
  left_join(latest_quotes_db, by = "stock_code") %>%
  mutate(
    stock_value = shares * close, # 每只股票的持仓市值
    total_value = sum(stock_value, na.rm = TRUE) # 组合总市值
  ) %>%
  select(stock_name, stock_code, shares, close, proportion, stock_value) %>%
  arrange(desc(stock_value))
# 4. 显示结果
datatable(portfolio_value, options = list(pageLength = 10, scrollX = TRUE),
          caption = paste0("基于最新持仓和最新行情的实时组合估算 (估算总市值: ", round(portfolio_value$total_value[1], 2), " 亿元)"))
# 5. 可视化持仓市值分布
portfolio_value %>%
  plot_ly(x = ~stock_name, y = ~stock_value, type = 'bar', name = '持仓市值') %>%
  layout(title = "持仓个股实时市值分布", xaxis = list(title = "股票名称"), yaxis = list(title = "市值 (亿元)"), barmode = 'group')

重要注意事项与挑战

  1. 数据延迟性

    • 持仓数据:最大的限制在于持仓数据不是实时的,基金公司只在定期报告(季报、半年报、年报)中披露持仓,通常有2-3个月的延迟,你看到的永远是“过去”的持仓。
    • 行情数据:通过免费API获取的数据通常有15-20分钟的延迟,并非真正的“实时”,对于高频交易,这远远不够。
  2. 数据源的稳定性与准确性

    • 免费API(如Yahoo Finance)可能不稳定,随时可能停止服务或改变数据格式。
    • 网页抓取(rvest)非常脆弱,一旦天天基金网改版,代码就会失效,需要定期维护。
    • 股票代码映射(如将000001转换为SS)是一个常见且容易出错的环节。
  3. 数据清洗的复杂性

    • 从公开网页获取的数据格式通常不规整,需要大量的清洗和转换工作。
    • 基金可能持有债券、股指期货等非股票资产,本方案主要聚焦于股票。

虽然无法获得真正的“实时”持仓变动,但通过上述R语言流程,你可以构建一个非常强大的系统来:

  • 定期获取最新的基金持仓信息。
  • 追踪基金净值和持仓个股的历史表现。
  • 估算基于最新持仓和行情的准实时组合市值和盈亏情况。
  • 通过可视化,直观地分析基金的投资风格、行业分布和个股偏好。

这个方案为你提供了一个坚实的基础,你可以根据需要进一步扩展,例如增加更多基金、设置自动化定时任务(使用 RscriptcronTask Scheduler)、或者接入更专业的数据源。

文章版权及转载声明

作者:咔咔本文地址:https://jits.cn/content/30781.html发布于 03-24
文章转载或复制请以超链接形式并注明出处杰思科技・AI 股讯

阅读
分享

发表评论

快捷回复:

评论列表 (暂无评论,1人围观)参与讨论

还没有评论,来说两句吧...