This article summarizes a sub-project of a stock analysis software that I participated in before (only part of the code is placed). The project is implemented with QT, and the stock historical data is stored in an excel file.
The goal of the subproject is to find a time period similar to the recent stock data based on the provided stock historical data, and to predict the trend based on the historical data. Key features include:
- (1) Display of stock data in any time period
- (2) Search for similar historical data
- (3) Prediction of the future trend of stocks
- (4) Update stock data
- (5) Import/store corresponding data
The following is a conceptual summary of the above functions:
- Display of stock data
Use candle charts to display stock data, including current stock prices, trading volume, trends, etc., as shown in the following figure:
/**************Price Chart (Candle Chart)******************/
//data link
QCandlestickSeries *acmeSeries = new QCandlestickSeries();
acmeSeries->setName( name );
acmeSeries->setIncreasingColor(QColor(Qt::red));
acmeSeries->setDecreasingColor(QColor(Qt::green));
//acmeSeries->setBodyOutlineVisible(false);
//acmeSeries->setBodyWidth(0.01);
//Information displayed on the horizontal axis
QStringList categories;
for( i=0;i<stock_chart->date.size();i++ )
categories.append( stock_chart->date.at(i) );
//Add the data of the last trading days
add_chart_set( stock_chart,acmeSeries );
//Create Chart
QChart *chart = new QChart();
chart->addSeries(acmeSeries);
//chart->setTitle( name );
chart->setAnimationOptions(QChart::SeriesAnimations);
chart->createDefaultAxes();
QBarCategoryAxis *axisX = qobject_cast<QBarCategoryAxis *>(chart->axes(Qt::Horizontal).at(0));
axisX->setCategories(categories);
axisX->setLabelsAngle( 90 );
axisX->setLabelsFont( QFont("Times",7) );
QValueAxis *axisY = qobject_cast<QValueAxis *>(chart->axes(Qt::Vertical).at(0));
axisY->setMax(axisY->max() * 1.01);
axisY->setMin(axisY->min() * 0.99);
chart->legend()->setVisible(false);
//chart->legend()->setAlignment(Qt::AlignBottom);
stock_chart->chart_view->setChart( chart );
/**************Price Chart (Candle Chart)******************/
/**************Trading Volume Chart******************/
QBarSet *set0 = new QBarSet("Jane");
for( i=0;i<stock_chart->date.size();i++ )
*set0 << stock_chart->volumes.at(i).toDouble()/100000;
QBarSeries *series = new QBarSeries();
series->append(set0);
QChart *chart2 = new QChart();
chart2->addSeries(series);
//chart2->setTitle("Simple barchart example");
chart2->setAnimationOptions(QChart::SeriesAnimations);
chart2->createDefaultAxes();
QBarCategoryAxis *axisX2 = qobject_cast<QBarCategoryAxis *>(chart2->axes(Qt::Horizontal).at(0));
axisX2->setCategories(categories);
axisX2->setLabelsAngle( 90 );
axisX2->setLabelsFont( QFont("Times",7) );
axisX2->setLabelsVisible(false);
QValueAxis *axisY2 = qobject_cast<QValueAxis *>(chart2->axes(Qt::Vertical).at(0));
axisY2->setMax(axisY2->max() * 1.01);
axisY2->setMin(axisY2->min() * 0.99);
//axisY2->setLabelsVisible(false);
chart2->legend()->setVisible(false);
//chart2->legend()->setAlignment(Qt::AlignBottom);
stock_chart->chart_volume->setChart( chart2 );
/**************Trading Volume Chart******************/
(2) Search for similar historical data
Because searching historical data is a time-consuming operation, it is necessary to create a sub-thread dedicated to searching and connect control signals, such as start and end, etc. When the data that meets the conditions is found, the search_resul signal is generated and sent to the main thread. Process in thread:
/******************Create a child thread******************/
child_thread = new QThread();
child_process = new ProcessObject();
child_process->moveToThread( child_thread );
connect( child_thread,SIGNAL(finished()),child_process,SLOT(deleteLater()) );
connect( child_thread,SIGNAL(finished()),child_thread,SLOT(deleteLater()) );
connect( this,SIGNAL(start_search(QString,MultString,ListDouble,bool)),
child_process,SLOT(reply_start_search(QString,MultString,ListDouble,bool)) );
connect( child_process,SIGNAL(search_result(QString,ListInt,ListDouble)),
this,SLOT(reply_search_result(QString,ListInt,ListDouble)) );
connect( this,SIGNAL(stop_search()),child_process ,SLOT(reply_stop_search()) );
child_thread->start();
The search algorithm uses a similarity algorithm. The algorithm itself is not difficult. The main problem is how to use the data.
double similarity(QList<double> data1,QList<double> data2)
{
if( data1.isEmpty() || data2.isEmpty() )
return 0;
if( data1.size() != data2.size() )
return 0;
if( data1.size() < 1 )
return 0;
int i;
int _size = data1.size();
double r1,r2,r3,avg1,avg2,var1,var2;
r1 = r2 = var1 = var2 = 0;
//average value
for( i=0;i<_size;i++ )
{
r1 += data1[i];
r2 += data2[i];
}
avg1 = r1/_size;
avg2 = r2/_size;
//variance
r1 = r2 = 0;
for( i=0;i<_size;i++ )
{
r1 += ( data1[i] - avg1 )*( data1[i] - avg1 );
r2 += ( data2[i] - avg2 )*( data2[i] - avg2 );
}
var1 = 1/sqrt( r1/(_size -1) );
var2 = 1/sqrt( r2/(_size -1) );
//data normalization
QList<double> std_data1,std_data2;
std_data1.clear();
std_data2.clear();
for( i=0;i<_size;i++ )
{
std_data1.append ( ( data1[i] - avg1)*var1 );
std_data2.append ( ( data2[i] - avg2)*var2 );
}
//Calculate correlation, consine
r1 = r2 = r3 = 0;
for( i=0;i<_size;i++ )
{
r1 = r1 + std_data1[i]*std_data2[i];
r2 = r2 + std_data1[i]*std_data1[i];
r3 = r3 + std_data2[i]*std_data2[i];
}
return (r1/sqrt(r2*r3));
}
The figure below is the interface for searching stock data. The result of the search includes the start position, end time (subscript in excel), similarity, and the similarity can be adjusted by yourself:
(3) Prediction of the future trend of stocks
The future trend forecast is calculated based on the time period found in the historical data. Because the historical data has already obtained the trend data, the forecast is relatively simple. The picture below is the forecast picture. Red indicates the predicted data, and green indicates the actual The data (because the data is not updated, the actual data is not displayed):
(4) Update stock data
Click the update button on the interface to update the stock data. Obtain stock data on Sina.com, and it can be automatically updated once after 15:00. The network part uses QNetworkManager to send a request.
//Get the name, for example, 003300.xls, the obtained is 003300
QString _name = stock_names.takeFirst();
_name = _name.split('.').at(0).trimmed();
if (_name.split('.').isEmpty())
return;
//Construct URL, note: changing datalen can get more time data
//100 is equivalent to getting half a year's data, temporarily take 50 here
//完整的:QString url = "http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=sh000001&scale=240&ma=no&datalen=100";
QString url = "http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=";
if( _name.at(0)=='0' )//Start with 0, Shenzhen Stock Exchange
url = url + "sz";
else //Other cases should start with 6, Shanghai Stock Exchange
url = url + "sh";
url = url + _name;
url = url + "&scale=240&ma=no&datalen=50";
//Send a network request (update the first stock, refer to reply_network_finished() for updates of other stocks
QNetworkRequest request;
request.setUrl( QUrl(url) );
QNetworkReply *reply = network_manager->get(request);
connect( reply,SIGNAL( finished() ),this,SLOT( reply_network_finished() ) );
(5) Data storage
Data is stored in json files. The storage content includes the found data, predicted data, etc.
//The time corresponding to the source data
QJsonArray _src_days;
for (i = 0;i < info.src_day.size();i ++)
_src_days.append(info.src_day.at(i));
jsobject.insert("src_day",QJsonValue(_src_days));
//The stock price corresponding to the source data
QJsonArray _src_prices;
for (i = 0;i < info.src_price.size();i ++)
_src_prices.append(info.src_price.at(i));
jsobject.insert("src_price",QJsonValue(_src_prices));
//The time corresponding to the target data
QJsonArray _dst_days;
for (i = 0;i < info.dst_day.size();i ++)
_dst_days.append(info.dst_day.at(i));
jsobject.insert("dst_day",QJsonValue(_dst_days));
//The stock price corresponding to the target data
QJsonArray _dst_prices;
for (i = 0;i < info.dst_price.size();i ++)
_dst_prices.append(info.dst_price.at(i));
jsobject.insert("dst_price",QJsonValue(_dst_prices));