| | |
| | | #include <QMessageBox> |
| | | #include <QDebug> |
| | | #include <QDateTime> |
| | | #include <QNetworkCookie> |
| | | #include <QNetworkReply> |
| | | #include <QJsonDocument> |
| | | #include <QJsonObject> |
| | | #include <QJsonArray> |
| | | #include <QDateTime> |
| | | |
| | | #pragma execution_character_set("utf-8") |
| | | |
| | | HistoryData::HistoryData(QWidget *parent) : |
| | | QMainWindow(parent), |
| | |
| | | { |
| | | ui->setupUi(this); |
| | | |
| | | // 注册元类型 |
| | | qRegisterMetaType<QVector<QVector<QString>>>("QVector<QVector<QString>>"); |
| | | qRegisterMetaType<QVector<QVector<QString>>>("QVector<QVector<QString>> &"); |
| | | |
| | | // ui相关 |
| | | ui->dateEdit->setDate(QDate::currentDate()); |
| | | ui->dateEdit_2->setDate(QDate::currentDate()); |
| | | |
| | | m_model = new QStandardItemModel(1,9,this); |
| | | |
| | | m_customModel = new CustomSortProxyModel; // 可自定义排序的模型 |
| | | m_customModel->setSourceModel(m_model); // 设置源模型 |
| | | |
| | | //ui->tableView->setModel(m_model); |
| | | ui->tableView->setModel(m_customModel); |
| | | // 表格列排序 |
| | | ui->tableView->setSortingEnabled(true); |
| | | |
| | | QStringList labels; |
| | | labels<<"股票名字"<<"股票代号"<<"市值(亿)"<<"涨跌幅"<<"收盘价"<<"成交额(亿)"<<"成交量"<<"换手率"<<"PE_TTM"; |
| | | m_model->setHorizontalHeaderLabels(labels); |
| | | |
| | | initMySQL(); // 初始化MySQL |
| | | |
| | | // 处理数据爬取的操作 |
| | | // 模拟浏览器的参数 |
| | | 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<QNetworkCookie> list; |
| | | list.push_back(QNetworkCookie(cookieByte)); |
| | | |
| | | QVariant var; |
| | | var.setValue(list); |
| | | // 设置要访问的网址 |
| | | m_request.setUrl(QUrl("https://xueqiu.com/?md5__1038=QqGxcDnDyiitnD05o4%2Br%3Di%3De0KDtYqCDRWOoD")); |
| | | // 设置请求头,用户代理,用来模拟浏览器 |
| | | m_request.setHeader(QNetworkRequest::UserAgentHeader,userAgent); |
| | | // 设置cookie |
| | | //m_request.setHeader(QNetworkRequest::CookieHeader, var); |
| | | |
| | | // 查看manager都支持哪些协议 |
| | | qDebug()<<"历史分析--支持的协议:"<<m_manager.supportedSchemes(); |
| | | // 关联信号槽,当请求管理对象完全打开网页之后,会将数据给槽函数处理 |
| | | connect(&m_manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(showAplyData(QNetworkReply*))); |
| | | |
| | | // 去请求首页 |
| | | m_manager.get(m_request); |
| | | |
| | | // 股票池相关 |
| | | m_poolModel = new QStandardItemModel(1,9,this); |
| | | |
| | | QStringList poolLabel; |
| | | poolLabel<<"id"<<"股票名字"<<"股票代号"<<"监控记录时间"<<"监控买入价格"<<"当前价格"<<"盈亏百分比"<<"监控卖出价格"<<"监控卖出时间"; |
| | | m_poolModel->setHorizontalHeaderLabels(poolLabel); |
| | | ui->tableView_2->setModel(m_poolModel); |
| | | |
| | | |
| | | // 股票池定时刷新 |
| | | m_poolTimer = new QTimer(this); |
| | | connect(m_poolTimer,SIGNAL(timeout()),this,SLOT(poolTimerSlot())); |
| | | m_poolTimer->start(10*1000); |
| | | } |
| | | |
| | | HistoryData::~HistoryData() |
| | |
| | | { |
| | | // 把5000支股票挨个获取到,然后进行数据保存,存到表格中 |
| | | // 更新的时候,默认支持10年的数据查询 |
| | | if(m_days <= 0) return; |
| | | |
| | | QString time_tar=QString::number(QDateTime::currentMSecsSinceEpoch()); |
| | | QString dayCnt=QString::number(3000); |
| | | QString dayCnt=QString::number(m_days); |
| | | QString type = "day"; // day week month |
| | | for(auto code:m_codeNames.keys()){ |
| | | QString url = QString("https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol=%1&begin=%2&period=%3&type=before&count=-%4&indicator=kline,pe,market_capital,ma").arg(code) |
| | | .arg(time_tar).arg(type).arg(dayCnt); |
| | | qDebug()<<"url:"<<url; |
| | | //qDebug()<<"url:"<<url; |
| | | // 发起请求 爬取数据 |
| | | m_request.setUrl(QUrl(url)); |
| | | m_manager.get(m_request); |
| | | //break; // 测试用 |
| | | |
| | | } |
| | | // 更新时间 |
| | | QString curDate = QDate::currentDate().toString("yyyy-MM-dd"); |
| | | QString sql = QString("insert into days_info (log_time) values ('%1')").arg(curDate); |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | qDebug()<<"update log time ok:"<<curDate; |
| | | }else{ |
| | | qDebug()<<"update log time fail:"<<curDate; |
| | | } |
| | | |
| | | } |
| | |
| | | m_codeNames = codeNames; |
| | | qDebug()<<"size:"<<m_codeNames.size(); |
| | | } |
| | | |
| | | void HistoryData::showAplyData(QNetworkReply *reply) |
| | | { |
| | | qDebug()<<"history收到响应"; |
| | | // 将响应的数据,一把读取完,放到字节数组里面来处理 |
| | | QByteArray buffer = reply->readAll(); |
| | | |
| | | // 将数据写到文件中,方便观察数据内容 |
| | | // QFile file("data_history.txt"); |
| | | // file.open(QIODevice::ReadWrite | QIODevice::Text); |
| | | // QTextStream out(&file); |
| | | // out << buffer << endl; |
| | | // file.close(); |
| | | |
| | | // 下面使用JSON进行数据处理 |
| | | if(reply->url().toString().indexOf("https://xueqiu.com/") != -1){ |
| | | qDebug()<<"发现首页url"; |
| | | qDebug()<<buffer; |
| | | |
| | | }else if(reply->url().toString().indexOf("https://stock.xueqiu.com/v5/stock/chart/kline.json") != -1){ |
| | | qDebug()<<"查看个股情况:"; |
| | | getOneStock(buffer); |
| | | |
| | | } |
| | | } |
| | | |
| | | /* |
| | | {"data":{"symbol":"SZ301633","column": |
| | | ["timestamp","volume","open","high","low","close","chg","percent","turnoverrate","amount","volume_post","amount_post","ma5","ma10","ma20","ma30","pe","market_capital"], |
| | | "item":[[1730908800000,10787401,187.0,187.0,133.1,137.13,99.19,261.44,77.5,1.608757505E9,1300,178269.0,null,null,null,null,67.0447,7.6353984E9], |
| | | [1730995200000,8348116,145.8,158.5,130.06,132.9,-4.23,-3.08,59.97,1.19303472E9,1000,132900.0,null,null,null,null,64.9765,7.399872E9], |
| | | */ |
| | | void HistoryData::getOneStock(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:"<<cnt; |
| | | QString symbol = jObject.value("data").toObject().value("symbol").toString(); |
| | | QString code = symbol; // 股票代号 |
| | | QString name = m_codeNames[code]; |
| | | qDebug()<<"历史:"<<code<<symbol; |
| | | |
| | | // 以批量写入的方式来获取多年数据 |
| | | QString sql = "insert into stock_day_info (name,code,market_capital,percent,open,high,low,close,amount,volume,turnover_rate,pe_ttm,time_trade) values "; |
| | | //qDebug()<<"sql 1:"<<sql; |
| | | for(int i=0;i<cnt;++i){ |
| | | qint64 timestamp = jArr.at(i).toArray().at(0).toVariant().toLongLong(); |
| | | double open = jArr.at(i).toArray().at(2).toVariant().toDouble(); |
| | | double high = jArr.at(i).toArray().at(3).toVariant().toDouble(); |
| | | double low = jArr.at(i).toArray().at(4).toVariant().toDouble(); |
| | | double close = jArr.at(i).toArray().at(5).toVariant().toDouble(); |
| | | double percent = jArr.at(i).toArray().at(7).toVariant().toDouble(); |
| | | double turnoverrate = jArr.at(i).toArray().at(8).toVariant().toDouble(); |
| | | double amount = jArr.at(i).toArray().at(9).toVariant().toDouble(); |
| | | double pe_ttm = jArr.at(i).toArray().at(16).toVariant().toDouble(); |
| | | double market_capital = jArr.at(i).toArray().at(17).toVariant().toDouble(); |
| | | long long volume = jArr.at(i).toArray().at(1).toVariant().toLongLong()/100; |
| | | |
| | | //long long amount = jArr.at(i).toArray().at(9).toVariant().toLongLong()/100000000; // 亿 |
| | | QString curDateTime = QDateTime::fromMSecsSinceEpoch(timestamp).toString("yyyy-MM-dd"); |
| | | //qDebug()<<curDateTime<<close<<volume<<amount; |
| | | |
| | | // 处理数据保存到模型中 |
| | | QString vals = QString("('%1','%2',%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,'%13')").arg(name).arg(code).arg(QString::number(market_capital)) |
| | | .arg(QString::number(percent)).arg(QString::number(open)).arg(QString::number(high)).arg(QString::number(low)). |
| | | arg(QString::number(close)).arg(QString::number(amount)).arg(QString::number(volume)).arg(QString::number(turnoverrate)) |
| | | .arg(QString::number(pe_ttm)).arg(curDateTime); |
| | | if(i != cnt - 1){ |
| | | vals.append(","); |
| | | } |
| | | sql.append(vals); |
| | | //qDebug()<<"sql 3:"<<sql; |
| | | |
| | | } |
| | | qDebug()<<"over:"<<sql.size(); |
| | | //qDebug()<<"sql 2:"<<sql; // 太大了 显示不出来 |
| | | |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | qDebug()<<"insert ok"; |
| | | }else{ |
| | | qDebug()<<"insert fail"<<que.lastError().text(); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | void HistoryData::on_pushButton_search_clicked() |
| | | { |
| | | // 提示查询等待 |
| | | ui->statusbar->showMessage("查询中...请耐心等待结果..."); |
| | | |
| | | QString searchTime = ui->dateEdit->text(); |
| | | qDebug()<<"查询的时间:"<<searchTime; |
| | | QString sql = QString("select name,code,market_capital,percent,close,amount,volume,turnover_rate,pe_ttm from stock_day_info where time_trade = '%1'").arg(searchTime); |
| | | qDebug()<<"sql:"<<sql; |
| | | |
| | | m_modelDatas.clear(); // 清空数组中的数据 |
| | | |
| | | qint64 tt = QDateTime::currentMSecsSinceEpoch(); |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | qDebug()<<"select ok"; |
| | | m_model->setRowCount(0); // 重置模型行数 |
| | | int rows = 0; |
| | | while (que.next()) { |
| | | QString name = que.value(0).toString(); |
| | | QString code = que.value(1).toString(); |
| | | QString market_capital = QString::number(que.value(2).toDouble()/100000000); |
| | | QString percent = que.value(3).toString(); |
| | | QString close = que.value(4).toString(); |
| | | QString amount = QString::number(que.value(5).toDouble()/100000000); |
| | | QString volume = que.value(6).toString(); |
| | | QString turnover_rate = que.value(7).toString(); |
| | | QString pe_ttm = que.value(8).toString(); |
| | | |
| | | m_modelDatas.append({name,code,market_capital,percent,close,amount,volume,turnover_rate,pe_ttm}); |
| | | |
| | | QList<QStandardItem*> items; |
| | | items.append(new QStandardItem(name)); |
| | | items.append(new QStandardItem(code)); |
| | | items.append(new QStandardItem(market_capital)); |
| | | //items.append(new QStandardItem(percent)); |
| | | 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(close)); |
| | | items.append(new QStandardItem(amount)); |
| | | items.append(new QStandardItem(volume)); |
| | | items.append(new QStandardItem(turnover_rate)); |
| | | items.append(new QStandardItem(pe_ttm)); |
| | | m_model->appendRow(items); |
| | | rows++; |
| | | } |
| | | qDebug()<<"查询到行数:"<<rows; |
| | | |
| | | }else{ |
| | | qDebug()<<"select fail"<<que.lastError().text(); |
| | | } |
| | | // 最后提示完成 |
| | | qint64 need = QDateTime::currentMSecsSinceEpoch()-tt; |
| | | ui->statusbar->showMessage(QString("耗时:%1 毫秒,查询完成.").arg(QString::number(need)),10*1000); |
| | | ui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); |
| | | //emit sendHistoryModel(m_modelDatas); // 给信息展示发模型 |
| | | |
| | | ui->textBrowser->append(QString("查询日期:%1 耗时: %2 毫秒 查到条数:%3").arg(searchTime).arg(QString::number(need)).arg(QString::number(m_model->rowCount()))); |
| | | } |
| | | |
| | | void HistoryData::on_pushButton_2_clicked() |
| | | { |
| | | //emit sendHistoryModel(m_modelDatas); // 给信息展示发模型 |
| | | emit sendHistoryModel(m_model); |
| | | } |
| | | |
| | | void HistoryData::on_pushButton_addStock_clicked() |
| | | { |
| | | QString name = ui->lineEdit->text(); |
| | | QString code = ui->lineEdit_2->text(); |
| | | QString price = ui->lineEdit_3->text(); |
| | | QString in_date = ui->dateEdit_2->text(); |
| | | qDebug()<<name<<code<<price<<in_date; |
| | | |
| | | QString sql = QString("insert into stock_pool (name,code,in_price,in_time) values('%1','%2',%3,'%4')").arg(name).arg(code).arg(price).arg(in_date); |
| | | qDebug()<<"sql:"<<sql; |
| | | |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | qDebug()<<"insert ok"; |
| | | poolTimerSlot(); // 立马刷新股票池的模型,刷新视图 |
| | | }else{ |
| | | qDebug()<<"insert fail"; |
| | | } |
| | | } |
| | | |
| | | void HistoryData::poolTimerSlot() |
| | | { |
| | | QString sql = "select * from stock_pool where state=1"; |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | qDebug()<<"select ok:"<<QDateTime::currentDateTime(); |
| | | m_poolModel->setRowCount(0); |
| | | while (que.next()) { |
| | | QList<QStandardItem*> items; |
| | | for(int i=0;i<9;++i){ |
| | | QString item = que.value(i).toString(); |
| | | QStandardItem *it = new QStandardItem(item); |
| | | if(i==6){ |
| | | if(item.toDouble() > 0){ |
| | | it->setData(QColor("red"),Qt::DecorationRole); // 添加一个装饰的颜色为红色 |
| | | it->setData(QColor("red"),Qt::TextColorRole); // 将字体颜色设置为红色 |
| | | items.at(1)->setData(QColor("red"),Qt::TextColorRole); // 将股票名字设置为红色 |
| | | }else if(item.toDouble() < 0){ |
| | | it->setData(QColor("green"),Qt::BackgroundColorRole); |
| | | items.at(1)->setData(QColor("green"),Qt::TextColorRole); |
| | | } |
| | | } |
| | | items.append(it); |
| | | } |
| | | m_poolModel->appendRow(items); // 整行添加 效率更高 |
| | | |
| | | } |
| | | }else{ |
| | | qDebug()<<"select fail"; |
| | | } |
| | | m_poolTimer->setInterval(60*1000); |
| | | } |
| | | |
| | | void HistoryData::on_checkBox_clicked() |
| | | { |
| | | QString dt = "2025-02-10"; |
| | | QString sql = "select log_time from days_info order by log_time desc"; |
| | | QSqlQuery que(db); |
| | | if(que.exec(sql)){ |
| | | if (que.next()) { |
| | | dt = que.value(0).toString(); |
| | | }else{ |
| | | qDebug()<<"查询结果集为空"; |
| | | return; |
| | | } |
| | | }else{ |
| | | qDebug()<<"查询失败"; |
| | | return ; |
| | | } |
| | | |
| | | if(ui->checkBox->isChecked()){ |
| | | ui->pushButton_update->setEnabled(true); |
| | | }else{ |
| | | ui->pushButton_update->setEnabled(false); |
| | | } |
| | | |
| | | |
| | | |
| | | QDate d1 = QDate::fromString(dt,"yyyy-MM-dd"); |
| | | QDate d2 = QDate::currentDate(); |
| | | int days = 0; // 经过了多少个周内的天数 |
| | | for(QDate begin = d1.addDays(1);begin <= d2;begin = begin.addDays(1)){ |
| | | if(begin.dayOfWeek() != Qt::Saturday && begin.dayOfWeek() != Qt::Sunday){ |
| | | days++; |
| | | } |
| | | } |
| | | qDebug()<<"days:"<<days; |
| | | m_days = days; // 刷新时间 |
| | | |
| | | |
| | | |
| | | } |