#include "etfstockinfo.h" #include "ui_etfstockinfo.h" #include #include #include #include #include #include #include #include #include ETFStockInfo::ETFStockInfo(QWidget *parent) : QMainWindow(parent), ui(new Ui::ETFStockInfo) { ui->setupUi(this); m_model_market = new QStandardItemModel(0,17,this); m_model_position = new QStandardItemModel(0,7,this); ui->tableView_market->setModel(m_model_market); // ui->tableView_position->setModel(m_model_position); m_customModel = new CustomSortProxyModel; // 可自定义排序的模型 m_customModel->setSourceModel(m_model_position); // 设置源模型 ui->tableView_position->setModel(m_customModel); // 表格列排序 ui->tableView_position->setSortingEnabled(true); QStringList labels_market; labels_market<<"基金代码"<<"基金名称"<<"成交额(亿)"<<"涨跌额"<<"累计净值"<<"etf类型"<<"涨跌幅"<<"成交量"<<"市价" <<"年涨跌幅"<<"粉丝"<<"市值(亿)"<<"手续费"<<"交易量-单批"<<"单位净值"<<"总股数"<<"时间"; m_model_market->setHorizontalHeaderLabels(labels_market); QStringList labels_position; labels_position<<"股票名"<<"股票代码"<<"机构名称"<<"持股数(亿)"<<"持股比例%"<<"截止日期"<<"基金数量(家)"; m_model_position->setHorizontalHeaderLabels(labels_position); // 处理数据爬取的操作 // 模拟浏览器的参数 QString userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"; m_cookie = "__utma=1.731742638.1647403301.1699341909.1700229030.32; device_id=196eef62baf016c7d95a22752d9bdbab; smidV2=20240414233939e95389ecf7ecd2f4d08524ce770aacd500753aa68e9640320; s=c611de27gr; cookiesu=651726298794778; xq_a_token=220b0abef0fac476d076c9f7a3938b7edac35f48; xqat=220b0abef0fac476d076c9f7a3938b7edac35f48; xq_r_token=1d46f0ed628506486164e5055a4993f9b54b2f4c; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOi0xLCJpc3MiOiJ1YyIsImV4cCI6MTcyOTIxMjc4NCwiY3RtIjoxNzI3NDkwOTU3MjA0LCJjaWQiOiJkOWQwbjRBWnVwIn0.C_GmKEhTaaioDMLWkgZyMXDl4duYEVmsdJHsTi7gbcNz0Tohc-uxHsaw0yBT5k-qmbrJ_RaLMCSxy06v14-R3dwL-MsiKRHxHa5qvQZN4BjEgvPRkqqvPgE_fkPLte8qQOEgd5iVkhr-4mjip0-9WCeXYiH7DygxFOBXGlgoPtpPzAtOTm5TWJmXh0ipDsIZxfNOl8jipXYaIdkv_kqLul5gqiBi5qqnwONDa24Zx-Kvpm8ySWiPFBLzZBqTuRBs4oKAMpSdOiYGLVL7dcSDDZyWqAexmrN4f19hkmd6gBHL4dCczRMDGYc1e98sQtlbZ5lgeEuuM24jjcuwCxsxXQ; u=651726298794778; Hm_lvt_1db88642e346389874251b5a1eded6e3=1727099939,1727251802,1727488707,1727491020; HMACCOUNT=1628106D67895387; acw_tc=2760828017275244258732552e9880f861be6db0c40facbdd5f223490decc2; acw_sc__v2=66f7ee8290dc3f63112948801ef331b8c97ccb35; Hm_lpvt_1db88642e346389874251b5a1eded6e3=1727524485; .thumbcache_f24b8bbe5a5934237bbc0eda20c1b6e7=t00N841S/BEpxTGOoJrbm0blWik12om0ew/whcq/V2DNtyEA8um7J+yzeGOli+6iP/TrvrH0YKH2kHlsmDb5EQ%3D%3D; ssxmod_itna=YqAOBKYve+x0ODfxBcDB4DKM7RtAA4454DkDIEC+GAqGNK3DZDiqAPGhDC8RzeL4Ko7+e2aeQvSeopd5pYDk0Ge5TB0PIjaIDB3DEx06TCCQxiiSDCeDIDWeDiDG4Gm4qGtDpxG=DjDytZ9TtDm4GWGqDmDGYBWqDgDYQDGwIXD7QDIqtW07tQDDNQKpAKDiYeHlL5uMRMtOrK7DtDjdTD/3+kZCbCcPwVFeFO=nPcDB6wxBjZRq00Un+g4mNqLYf4IDAxQuYKtgheYYoAfDhTKhhLSGx4tGY4+GDKSiMS2DDAIvdKeD; ssxmod_itna2=YqAOBKYve+x0ODfxBcDB4DKM7RtAA4454DkDIEC+GDA69mqD/YttDFhxMltFKApT7bCMH+bytp7GQQyCbBWukeAF3+jxeN2wLjbQAFcdwbeNeoohjQ4qtEnLg4TKyX2LqVL=CGaV=GqZZqbDby8DFnFbgWiZEH8zoBzBCbzqWeqwoPq7/TpL=gIRY8sVhEpnWWHQU8sz+S8=+8oRerHq0wtQnSzyQDK7Dmvr3a+VY7fezRmxKF=bDeqexYUbuuzw2eR3In9evW6tzHbQY6vp=AIOUc9l6vc0vOl9plD6D07zGGQ41uxpii2Y5s7KvYQDWGYExD7=DYKKeD=="; QByteArray cookieByte = m_cookie.toUtf8(); QList list; list.push_back(QNetworkCookie(cookieByte)); QVariant var; var.setValue(list); // 设置要访问的网址 m_request.setUrl(QUrl("https://xueqiu.com/hq#hot")); // 设置请求头,用户代理,用来模拟浏览器 m_request.setHeader(QNetworkRequest::UserAgentHeader,userAgent); // 设置cookie //m_request.setHeader(QNetworkRequest::CookieHeader, var); // 查看manager都支持哪些协议 qDebug()<<"支持的协议:"<dateTimeEdit->setDateTime(QDateTime::currentDateTime()); ui->dateTimeEdit_3->setDateTime(QDateTime::currentDateTime()); } ETFStockInfo::~ETFStockInfo() { delete ui; } void ETFStockInfo::initMySQL() { //添加一个数据库 db=QSqlDatabase::addDatabase("QMYSQL"); //括号内要写出数据库的类型 //设置数据库 db.setHostName("127.0.0.1"); //设置数据库的主机ip //设置数据库的用户名 db.setUserName("root"); //设置数据库的密码 db.setPassword("root"); //这个就是安装MySQL时设置的密码 //设置数据库的名字 db.setDatabaseName("stock_plan"); //打开数据库(已经安装过mysql驱动了) if(db.open()==false){ QMessageBox::warning(this,"waring",db.lastError().text()); }else{ qDebug()<<"mysql conn ok"; } } void ETFStockInfo::getEtfInfo(QByteArray &buffer) { m_model_market->setRowCount(0); // qDebug()<<"ETF:"< rowItems; rowItems.append(new QStandardItem(symbol)); rowItems.append(new QStandardItem(name)); rowItems.append(new QStandardItem(QString::number(amount))); rowItems.append(new QStandardItem(QString::number(chg))); rowItems.append(new QStandardItem(QString::number(acc_unit_nav))); rowItems.append(new QStandardItem(etf_types)); QStandardItem *percentItem = new QStandardItem(QString::number(percent)); if(percent > 0){ percentItem->setData(QColor("red"),Qt::DecorationRole); // 添加一个装饰的颜色为红色 percentItem->setData(QColor("red"),Qt::TextColorRole); // 将字体颜色设置为红色 rowItems.at(0)->setData(QColor("red"),Qt::TextColorRole); // 将股票名字设置为红色 } else if(percent < 0){ percentItem->setData(QColor("green"),Qt::BackgroundColorRole); rowItems.at(0)->setData(QColor("green"),Qt::TextColorRole); } rowItems.append(percentItem); rowItems.append(new QStandardItem(QString::number(volume))); rowItems.append(new QStandardItem(QString::number(current))); rowItems.append(new QStandardItem(QString::number(current_year_percent))); rowItems.append(new QStandardItem(QString::number(followers))); rowItems.append(new QStandardItem(QString::number(market_capital,'f',2))); rowItems.append(new QStandardItem(QString::number(premium_rate,'f',2))); rowItems.append(new QStandardItem(QString::number(lot_size))); rowItems.append(new QStandardItem(QString::number(unit_nav))); rowItems.append(new QStandardItem(QString::number(total_shares))); rowItems.append(new QStandardItem(QString::number(timestamp))); // 添加一行数据项到模型中 //m_model->appendRow(rowItems); m_model_market->appendRow(rowItems); // 保存etf基金的代号和名字 m_codeNamesEtf[symbol] = name; } } } /* 响应情况 {"data":{"chg_date":"2025中报","fund_items":[{"to_float_shares_ratio":32.27,"org_name_or_fund_name":"全部合计","held_num":214204071}, {"to_float_shares_ratio":1.7792,"org_name_or_fund_name":"华泰柏瑞沪深300交易型开放式指数证券投资基金","held_num":11809096}, {"to_float_shares_ratio":1.6554,"org_name_or_fund_name":"华夏国证半导体芯片交易型开放式指数证券投资基金","held_num":10987221}, */ void ETFStockInfo::getStockFundPosition(QByteArray &buffer,QString url) { int index = url.indexOf(QRegularExpression("[A-Z]{2}\\d{6}")); qDebug()<<"index:"< 0){ name = m_codeNames[symbol]; } // m_model_position->setRowCount(0); // qDebug()<<"ETF:"< items; items.append(new QStandardItem(name)); items.append(new QStandardItem(symbol)); items.append(new QStandardItem(org_name_or_fund_name)); items.append(new QStandardItem(QString::number(held_num))); items.append(new QStandardItem(QString::number(to_float_shares_ratio))); items.append(new QStandardItem(chg_date)); items.append(new QStandardItem(QString::number(count))); m_model_position->appendRow(items); } } } void ETFStockInfo::getStockOne(QByteArray &buffer) { QJsonDocument jd = QJsonDocument::fromJson(buffer); if(jd.isObject()){ QJsonObject jObject = jd.object(); QJsonArray jArr = jObject.value("data").toObject().value("item").toArray(); // 通过键值对取值 int cnt = jArr.count(); qDebug()<<"数组size:"<readAll(); // 下面使用JSON进行数据处理 if(reply->url() == QUrl("https://xueqiu.com/")){ qDebug()<<"发现首页url"; qDebug()<url().toString().indexOf("https://stock.xueqiu.com/v5/stock/chart/kline.json") != -1){ qDebug()<<"查看个股情况:"; getStockOne(buffer); }else if(reply->url().toString().indexOf("https://stock.xueqiu.com/v5/stock/screener/quote/list.json?page=1&size=100&order=desc&order_by=percent&market=CN&ind_code") != -1){ qDebug()<<"根据行业获取股票信息"<url(); QString industryCode = reply->url().toString().split("=").back(); qDebug()<<"行业信息:"<url().toString().indexOf("https://stock.xueqiu.com/v5/stock/screener/quote/list.json") != -1){ qDebug()<<"获取所有沪深股票 可以干活了"; // getStockAllCode(buffer); }else if(reply->url().toString().indexOf("https://stock.xueqiu.com/v5/stock/screener/industries.json") != -1){ qDebug()<<"获取行业信息"; // getIndustryInfo(buffer); }else if(reply->url().toString().indexOf("https://stock.xueqiu.com/v5/stock/screener/fund/list.json") != -1){ qDebug()<<"获取到ETF基金信息"; getEtfInfo(buffer); }else if(reply->url().toString().indexOf("https://stock.xueqiu.com/v5/stock/f10/cn/org_holding/detail.json") != -1){ qDebug()<<"获取到股票基金持仓信息"; qDebug()<<"基金持仓 url:"<url().toString(); getStockFundPosition(buffer,reply->url().toString()); } reply->deleteLater(); } void ETFStockInfo::on_pushButton_market_clicked() { QString url = "https://stock.xueqiu.com/v5/stock/screener/fund/list.json?page=1&size=3000&order=desc&order_by=percent&type=18&parent_type=1"; m_request.setUrl(QUrl(url)); m_manager.get(m_request); } // 查询基金持仓情况 按总量来查询 void ETFStockInfo::on_pushButton_position_search_clicked() { QString type = ui->comboBox_type->currentText(); QString count_rank = ui->comboBox_rank->currentText(); // 前N名 QString url = "https://stock.xueqiu.com/v5/stock/f10/cn/org_holding/detail.json?symbol=SH603986×tamp=1759161600000&extend=true"; // 拿出前N名,进行请求,然后获取对应股票的基金持仓情况 // m_request.setUrl(QUrl(url)); // m_manager.get(m_request); emit sendSymbolNums(count_rank.toInt()); // 发获取股票代号的信号 } void ETFStockInfo::saveCodeNames(QMap &cns) { m_codeNames = cns; // 拷贝股票代号和名字过来 qDebug()<<__FUNCTION__<<"收到股票代号和名字的信息"; } void ETFStockInfo::getStockFundPosSlot(QString symbol) { QStringList tts = {"1759161600000","1751212800000","1743350400000"}; for(auto &tt:tts){ QString timestamp = tt; QString url = QString("https://stock.xueqiu.com/v5/stock/f10/cn/org_holding/detail.json?symbol=%1×tamp=%2&extend=true").arg(symbol).arg(timestamp); m_request.setUrl(QUrl(url)); m_manager.get(m_request); } } void ETFStockInfo::on_pushButton_search_date_clicked() { // 按日期查询数据库,了解趋势 QString cur_date = ui->dateTimeEdit->date().toString("yyyy-MM-dd"); QString sql = QString("select * from etf_day_info where time_trade = '%1' order by percent desc").arg(cur_date); QSqlQuery que(db); if(que.exec(sql)){ qDebug()<<"select ok"; m_model_market->setRowCount(0); // 重置模型行数 int rows = 0; while (que.next()) { QString code = que.value(1).toString(); QString name = que.value(2).toString(); QString amount = QString::number(que.value(3).toDouble()/100000000); QString chg = QString::number(que.value(4).toDouble()); QString percent = que.value(5).toString(); QString volume = que.value(6).toString(); QString close = que.value(7).toString(); QString time_trade = que.value(8).toString(); // QString turnover_rate = que.value(7).toString(); // QString pe_ttm = que.value(8).toString(); // QString amount_rank = que.value(9).toString(); // m_modelDatas.append({name,code,market_capital,percent,close,amount,volume,turnover_rate,pe_ttm,amount_rank}); QList items; items.append(new QStandardItem(code)); items.append(new QStandardItem(name)); items.append(new QStandardItem(amount)); items.append(new QStandardItem(chg)); items.append(new QStandardItem("0")); items.append(new QStandardItem("0")); QStandardItem *percentItem = new QStandardItem(percent); if(percent.toDouble() > 0){ percentItem->setData(QColor("red"),Qt::DecorationRole); // 添加一个装饰的颜色为红色 percentItem->setData(QColor("red"),Qt::TextColorRole); // 将字体颜色设置为红色 items.at(0)->setData(QColor("red"),Qt::TextColorRole); // 将股票名字设置为红色 } else if(percent.toDouble() < 0){ percentItem->setData(QColor("green"),Qt::BackgroundColorRole); items.at(0)->setData(QColor("green"),Qt::TextColorRole); } items.append(percentItem); items.append(new QStandardItem(volume)); items.append(new QStandardItem(close)); items.append(new QStandardItem(time_trade)); // items.append(new QStandardItem(pe_ttm)); // items.append(new QStandardItem(amount_rank)); m_model_market->appendRow(items); rows++; } qDebug()<<"查询到行数:"<comboBox_days->currentText().toInt(); if(m_days == 0) return; if(m_codeNamesEtf.size() > 0){ qint64 cur_time = QDateTime::currentMSecsSinceEpoch(); for(auto code:m_codeNamesEtf.keys()){ QString url = QString("https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol=%1&begin=%2&period=day&type=before&count=-%3&indicator=kline,pe,market_capital,ma") .arg(code).arg(QString::number(cur_time)).arg(m_days); m_request.setUrl(QUrl(url)); m_manager.get(m_request); } qDebug()<<"发起请求完成"; }else{ // 如果为空,则需要去刷新一下最新的情况,获取基金信息 on_pushButton_market_clicked(); } } void ETFStockInfo::on_checkBox_update_clicked() { if(ui->checkBox_update->isChecked()){ ui->pushButton_update->setEnabled(true); }else{ ui->pushButton_update->setEnabled(false); } }