274 lines
7.4 KiB
C++
274 lines
7.4 KiB
C++
#include "qorderprocessor.h"
|
||
#include <QJsonDocument>
|
||
#include <QJsonArray>
|
||
#include <QDebug>
|
||
#include <immintrin.h>
|
||
#include <algorithm>
|
||
|
||
#include "qlogmanager.h"
|
||
|
||
QOrderProcessor::QOrderProcessor(QObject *parent)
|
||
: QObject(parent)
|
||
{
|
||
int idealThreads = QThread::idealThreadCount();
|
||
//m_threadPool.setMaxThreadCount(qBound(4, idealThreads, 16));
|
||
m_threadPool.setMaxThreadCount(qBound(4, 6, 8));
|
||
}
|
||
|
||
QOrderProcessor::~QOrderProcessor()
|
||
{
|
||
m_threadPool.waitForDone();
|
||
}
|
||
|
||
void QOrderProcessor::setProcessingEnabled(bool enabled)
|
||
{
|
||
QMutexLocker lock(&m_dataMutex);
|
||
m_enabled = enabled;
|
||
}
|
||
|
||
|
||
double QOrderProcessor::sumQuantity(const QVector<OrderBookEntry>& items)const {
|
||
return std::accumulate(items.begin(), items.end(), 0.0,
|
||
[](double sum, const OrderBookEntry& item) {
|
||
return sum + item.volume;
|
||
});
|
||
}
|
||
|
||
/*
|
||
1. 找到列表中的最大值和次大值
|
||
2. 最大值是次大值的指定倍数
|
||
*/
|
||
OrderBookEntry QOrderProcessor::findMaxVolumeItemEx(const QVector<OrderBookEntry>& items, double volumeRatio) const
|
||
{
|
||
if (items.isEmpty())
|
||
return{};
|
||
|
||
// 特殊情况处理:只有一个元素时无法比较倍数
|
||
if (items.size() == 1) {
|
||
return volumeRatio <= 1.0 ? items[0] : OrderBookEntry{};
|
||
}
|
||
|
||
// 初始化最大值和次大值
|
||
const OrderBookEntry* maxItem = &items[0];
|
||
const OrderBookEntry* secondMaxItem = nullptr;
|
||
double secondMaxVolume = -1.0;
|
||
|
||
// 遍历查找最大值和次大值
|
||
for (int i = 1; i < items.size(); ++i) {
|
||
const OrderBookEntry& item = items[i];
|
||
|
||
if (item.volume > maxItem->volume) {
|
||
// 更新次大值为原最大值
|
||
secondMaxItem = maxItem;
|
||
secondMaxVolume = maxItem->volume;
|
||
|
||
// 更新最大值
|
||
maxItem = &item;
|
||
}
|
||
else if (!secondMaxItem || item.volume > secondMaxVolume) {
|
||
// 更新次大值
|
||
secondMaxItem = &item;
|
||
secondMaxVolume = item.volume;
|
||
}
|
||
}
|
||
|
||
// 检查是否满足倍数条件
|
||
constexpr double EPSILON = 1e-9;
|
||
if (secondMaxItem && maxItem->volume + EPSILON >= secondMaxVolume * volumeRatio) {
|
||
return *maxItem;
|
||
}
|
||
|
||
return OrderBookEntry{};
|
||
}
|
||
|
||
QVector<BigOrderInfo> QOrderProcessor::findMaxVolumeItem(const OrderBookData & data) const
|
||
{
|
||
bool findBigOrder = false;
|
||
BigOrderInfo bigOrderInfo;
|
||
QVector<BigOrderInfo> bigOrderInfoList;
|
||
float volume = m_replyCodeQuantity[data.code];
|
||
const OrderBookEntry* maxItem;
|
||
QVector<OrderBookEntry> items = data.asks;
|
||
for (int i = 0; i < items.size(); i++) {
|
||
// 同一个价格挡位的订单中股票数量大于,阈值才有可能是大单
|
||
if (volume < items[i].volume) {
|
||
for (int j = 0; j< items[i].orderCount; j++)
|
||
{
|
||
try {
|
||
if (items[i].details.size() == 0) continue;
|
||
if (volume < items[i].details[j].volume)
|
||
{
|
||
findBigOrder = true;
|
||
|
||
bigOrderInfo.nBigOrderType = 0;
|
||
bigOrderInfo.isBigOrder = findBigOrder;
|
||
bigOrderInfo.code = data.code;
|
||
bigOrderInfo.name = data.name;
|
||
bigOrderInfo.orderId = items[i].details[j].orderId;
|
||
bigOrderInfo.price = items[i].price;
|
||
bigOrderInfo.volume = items[i].details[j].volume;
|
||
bigOrderInfo.level = i + 1;
|
||
bigOrderInfo.svrRecvTime = data.askTime;
|
||
|
||
bigOrderInfoList.append(bigOrderInfo);
|
||
}
|
||
}
|
||
catch (const char* msg) {
|
||
qDebug() << "err findMax";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
items = data.bids;
|
||
for (int i = 0; i < items.size(); i++) {
|
||
// 同一个价格挡位的订单中股票数量大于,阈值才有可能是大单
|
||
if (volume < items[i].volume) {
|
||
for (int j = 0; j< items[i].orderCount; j++)
|
||
{
|
||
try {
|
||
if (items[i].details.size() == 0) continue;
|
||
if (volume < items[i].details[j].volume)
|
||
{
|
||
findBigOrder = true;
|
||
bigOrderInfo.nBigOrderType = 1;
|
||
bigOrderInfo.isBigOrder = findBigOrder;
|
||
bigOrderInfo.code = data.code;
|
||
bigOrderInfo.name = data.name;
|
||
bigOrderInfo.orderId = items[i].details[j].orderId;
|
||
bigOrderInfo.price = items[i].price;
|
||
bigOrderInfo.volume = items[i].details[j].volume;
|
||
bigOrderInfo.svrRecvTime = data.bidTime;
|
||
|
||
bigOrderInfoList.append(bigOrderInfo);
|
||
}
|
||
}
|
||
catch (const char* msg) {
|
||
qDebug() << "err findMax";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return bigOrderInfoList;
|
||
}
|
||
//
|
||
//// 1.简单粗暴,直接通过数量阈值进行筛选,遍历全部价格档位
|
||
//// 大单检测接口,返回 QVector<BigOrderInfo>, 暂时找到一个就返回
|
||
//QVector<BigOrderInfo> QOrderProcessor::findMaxVolumeItem(const OrderBookData& data) const;
|
||
//{
|
||
// if (items.isEmpty())
|
||
// return{};
|
||
// bool findBigOrder = false;
|
||
// QVector<BigOrderInfo> bigOrderInfoList;
|
||
//
|
||
// float volume = m_replyCodeQuantity[data.code];
|
||
// const OrderBookEntry* maxItem;
|
||
// for (int i = 0; i < items.size(); i++) {
|
||
// // 同一个价格挡位的订单中股票数量大于,阈值才有可能是大单
|
||
// if (volume < items[i].volume) {
|
||
// for (int j = 0; j< items[i].orderCount; j++)
|
||
// {
|
||
// try {
|
||
// if (items[i].details.size() == 0) continue;
|
||
// if (volume < items[i].details[j].volume)
|
||
// {
|
||
// findBigOrder = true;
|
||
//
|
||
// bigOrderInfo.isBigOrder = findBigOrder;
|
||
// bigOrderInfo.code = data.code;
|
||
// bigOrderInfo.name = data.name;
|
||
// bigOrderInfo.orderId = items[i].details[j].orderId;
|
||
// bigOrderInfo.price = items[i].price;
|
||
// bigOrderInfo.volume = items[i].details[j].volume;
|
||
// bigOrderInfo.svrRecvTime = data.askTime;
|
||
//
|
||
// return bigOrderInfo;
|
||
// }
|
||
// }
|
||
// catch (const char* msg) {
|
||
// qDebug() << "err findMax";
|
||
// }
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// 大单检测,分为两步:
|
||
// 1、判断股票数量是否达到大单的阈值,未达到时直接返回
|
||
// 2、判断订单详情是否达到大单阈值
|
||
|
||
// 下一步增加摆盘数据统计,大单占全部摆盘数量的比例,计算出来一个置信度
|
||
|
||
// 使用指针遍历避免拷贝
|
||
// 这里是找到当前摆盘数据中,每一档价位数量最大的
|
||
//const OrderBookEntry* maxItem = &items[0];
|
||
//for (int i = 1; i < items.size(); ++i) {
|
||
// if (items[i].volume > maxItem->volume) {
|
||
// maxItem = &items[i];
|
||
// }
|
||
//}
|
||
|
||
// 添加业务规则验证(如果需要)
|
||
// if (!isValidMaxVolumeItem(*maxItem, items))
|
||
// return {};
|
||
|
||
// return bigOrderInfoList;
|
||
//}
|
||
|
||
OrderBookEntry QOrderProcessor::findMinPriceItem(const QVector<OrderBookEntry>& items) const
|
||
{
|
||
if (items.isEmpty()) return{};
|
||
|
||
auto it = std::min_element(items.begin(), items.end(),
|
||
[](const OrderBookEntry& a, const OrderBookEntry& b) {
|
||
return a.price < b.price;
|
||
});
|
||
return it != items.end() ? *it : OrderBookEntry{};
|
||
}
|
||
|
||
QVector<BigOrderInfo> QOrderProcessor::findExtremeOrders(const OrderBookData& data) const
|
||
{
|
||
return findMaxVolumeItem(data);
|
||
}
|
||
|
||
/*
|
||
// 设计一个单独的大单检测引擎
|
||
// 包括:历史数据分析、订单分析、成交记录分析
|
||
*/
|
||
void QOrderProcessor::internalProcess(const OrderBookData& orderData)
|
||
{
|
||
if (!m_enabled || orderData.isEmpty()) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const auto result = findExtremeOrders(orderData);
|
||
|
||
for (int i = 0; i < result.size(); i++)
|
||
{
|
||
if(result.at(i).isBigOrder)
|
||
emit maxOrderReady(result.at(i));
|
||
}
|
||
}
|
||
catch (const std::exception& e) {
|
||
emit errorOccurred(orderData.code, QString::fromUtf8(e.what()));
|
||
}
|
||
}
|
||
|
||
void QOrderProcessor::processOrderBook(const Qot_UpdateOrderBook::Response &stRsp)
|
||
{
|
||
QtConcurrent::run(&m_threadPool, [this, stRsp]() {
|
||
try {
|
||
OrderBookData orderBook;
|
||
parser.parse(stRsp.SerializeAsString().c_str(), stRsp.ByteSize(), orderBook);
|
||
|
||
internalProcess(orderBook);
|
||
}
|
||
catch (const std::exception& e) {
|
||
emit errorOccurred("UNKNOWN", QString::fromUtf8(e.what()));
|
||
}
|
||
});
|
||
}
|
||
|
||
|