盈亏如何实时更新?深入Qt交易系统的核心计算逻辑,构建毫秒级盈亏响应
一、盈亏计算概述
实时盈亏是交易系统最核心的展示数据,需要根据持仓、市价、成本等多个维度实时计算。本文从数据结构到算法实现,完整解析盈亏计算系统。
1.1 盈亏类型
| 盈亏类型 | 计算公式 | 更新时机 |
|---|---|---|
| 浮动盈亏 | (市价 - 成本价) × 持仓量 | 市价变化 |
| 当日盈亏 | (市价 - 昨收价) × 持仓量 | 市价变化 |
| 已实现盈亏 | Σ(卖出价 - 成本价) × 卖出量 | 成交回报 |
| 总盈亏 | 浮动盈亏 + 已实现盈亏 | 实时 |
1.2 数据依赖关系
行情数据 ──┬── 持仓盈亏计算 ──┬── 账户总盈亏
│ │
持仓数据 ──┘ │
│
成交数据 ────── 已实现盈亏 ────┘
二、核心数据结构
2.1 持仓与盈亏模型
cpp
// Position.h
class Position : public QObject
{
Q_OBJECT
Q_PROPERTY(QString symbol READ symbol CONSTANT)
Q_PROPERTY(int64_t totalQty READ totalQty NOTIFY qtyChanged)
Q_PROPERTY(int64_t availableQty READ availableQty NOTIFY qtyChanged)
Q_PROPERTY(double costPrice READ costPrice CONSTANT)
Q_PROPERTY(double marketPrice READ marketPrice NOTIFY priceChanged)
Q_PROPERTY(double floatingPnL READ floatingPnL NOTIFY pnlChanged)
Q_PROPERTY(double dayPnL READ dayPnL NOTIFY pnlChanged)
Q_PROPERTY(double realizedPnL READ realizedPnL CONSTANT)
public:
explicit Position(const QString &symbol, QObject *parent = nullptr);
// 基本信息
QString symbol() const { return m_symbol; }
int64_t totalQty() const { return m_totalQty; }
int64_t availableQty() const { return m_availableQty; }
double costPrice() const { return m_costPrice; }
double marketPrice() const { return m_marketPrice; }
// 盈亏计算
double floatingPnL() const;
double dayPnL() const;
double realizedPnL() const { return m_realizedPnL; }
// 操作接口
void updateMarketPrice(double price);
void updateCost(double tradePrice, int64_t tradeQty, bool isBuy);
void applyTrade(double tradePrice, int64_t tradeQty, bool isBuy);
void setPreClosePrice(double price) { m_preClosePrice = price; }
signals:
void qtyChanged();
void priceChanged();
void pnlChanged();
private:
QString m_symbol;
int64_t m_totalQty = 0;
int64_t m_availableQty = 0;
double m_costPrice = 0.0; // 成本价
double m_marketPrice = 0.0; // 市价
double m_preClosePrice = 0.0; // 昨收价
double m_realizedPnL = 0.0; // 已实现盈亏
double m_totalCost = 0.0; // 总成本
};
2.2 盈亏计算实现
cpp
// Position.cpp
double Position::floatingPnL() const
{
if (m_totalQty <= 0 || m_costPrice <= 0) return 0.0;
return (m_marketPrice - m_costPrice) * m_totalQty;
}
double Position::dayPnL() const
{
if (m_totalQty <= 0 || m_preClosePrice <= 0) return 0.0;
return (m_marketPrice - m_preClosePrice) * m_totalQty;
}
void Position::updateMarketPrice(double price)
{
if (qFuzzyCompare(m_marketPrice, price)) return;
m_marketPrice = price;
emit priceChanged();
emit pnlChanged();
}
void Position::applyTrade(double tradePrice, int64_t tradeQty, bool isBuy)
{
if (isBuy) {
// 买入:增加持仓,更新成本
double newCost = m_totalCost + tradePrice * tradeQty;
m_totalQty += tradeQty;
m_availableQty += tradeQty;
m_totalCost = newCost;
m_costPrice = m_totalCost / m_totalQty;
} else {
// 卖出:减少持仓,计算已实现盈亏
int64_t sellQty = qMin(tradeQty, m_totalQty);
double pnl = (tradePrice - m_costPrice) * sellQty;
m_totalQty -= sellQty;
m_availableQty -= sellQty;
m_totalCost = m_costPrice * m_totalQty;
m_realizedPnL += pnl;
if (m_totalQty == 0) {
m_costPrice = 0;
m_totalCost = 0;
}
}
emit qtyChanged();
emit pnlChanged();
}
三、账户盈亏聚合
3.1 账户模型
cpp
// Account.h
class Account : public QObject
{
Q_OBJECT
Q_PROPERTY(double totalAssets READ totalAssets NOTIFY assetsChanged)
Q_PROPERTY(double availableCash READ availableCash NOTIFY cashChanged)
Q_PROPERTY(double marketValue READ marketValue NOTIFY assetsChanged)
Q_PROPERTY(double totalFloatingPnL READ totalFloatingPnL NOTIFY pnlChanged)
Q_PROPERTY(double totalDayPnL READ totalDayPnL NOTIFY pnlChanged)
Q_PROPERTY(double totalRealizedPnL READ totalRealizedPnL NOTIFY pnlChanged)
Q_PROPERTY(double totalPnL READ totalPnL NOTIFY pnlChanged)
public:
static Account* instance();
// 持仓管理
Position* getOrCreatePosition(const QString &symbol);
void removePosition(const QString &symbol);
QList<Position*> allPositions() const;
// 查询接口
double totalAssets() const;
double availableCash() const { return m_availableCash; }
double marketValue() const;
double totalFloatingPnL() const;
double totalDayPnL() const;
double totalRealizedPnL() const;
double totalPnL() const;
// 更新接口
void updateCash(double cash);
void updateMarketPrice(const QString &symbol, double price);
void applyTrade(const QString &symbol, double price, int64_t qty, bool isBuy);
signals:
void assetsChanged();
void cashChanged();
void pnlChanged();
void positionAdded(const QString &symbol);
void positionRemoved(const QString &symbol);
private:
Account();
QMap<QString, Position*> m_positions;
double m_availableCash = 0.0;
double m_frozenCash = 0.0;
};
double Account::marketValue() const
{
double value = 0.0;
for (auto *pos : m_positions) {
value += pos->marketPrice() * pos->totalQty();
}
return value;
}
double Account::totalAssets() const
{
return m_availableCash + m_frozenCash + marketValue();
}
double Account::totalFloatingPnL() const
{
double pnl = 0.0;
for (auto *pos : m_positions) {
pnl += pos->floatingPnL();
}
return pnl;
}
double Account::totalPnL() const
{
return totalFloatingPnL() + totalRealizedPnL();
}
3.2 实时更新机制
cpp
// PnLUpdater.h
class PnLUpdater : public QObject
{
Q_OBJECT
public:
explicit PnLUpdater(QObject *parent = nullptr);
void subscribePosition(Position *position);
void unsubscribePosition(Position *position);
void updateAllPositions(const QMap<QString, double> &prices);
private slots:
void onMarketData(const MarketData &data);
private:
QHash<QString, Position*> m_subscribedPositions;
};
void PnLUpdater::onMarketData(const MarketData &data)
{
Position *pos = m_subscribedPositions.value(data.symbol);
if (pos) {
pos->updateMarketPrice(data.lastPrice);
}
}
void PnLUpdater::updateAllPositions(const QMap<QString, double> &prices)
{
for (auto it = prices.begin(); it != prices.end(); ++it) {
Position *pos = m_subscribedPositions.value(it.key());
if (pos) {
pos->updateMarketPrice(it.value());
}
}
}
四、UI展示实现
4.1 盈亏展示模型
cpp
// PnLModel.h
class PnLModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Column {
ColSymbol = 0,
ColQuantity,
ColCostPrice,
ColMarketPrice,
ColFloatingPnL,
ColDayPnL,
ColRealizedPnL,
ColPnLPercent,
ColCount
};
explicit PnLModel(QObject *parent = nullptr);
// QAbstractTableModel接口
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
// 数据管理
void setPositions(const QList<Position*> &positions);
void updatePosition(Position *position);
private slots:
void onPositionChanged();
private:
QList<Position*> m_positions;
QHash<Position*, QMetaObject::Connection> m_connections;
};
QVariant PnLModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() >= m_positions.size()) {
return QVariant();
}
Position *pos = m_positions[index.row()];
if (role == Qt::DisplayRole) {
switch (index.column()) {
case ColSymbol:
return pos->symbol();
case ColQuantity:
return pos->totalQty();
case ColCostPrice:
return QString::number(pos->costPrice(), 'f', 2);
case ColMarketPrice:
return QString::number(pos->marketPrice(), 'f', 2);
case ColFloatingPnL:
return QString::number(pos->floatingPnL(), 'f', 2);
case ColDayPnL:
return QString::number(pos->dayPnL(), 'f', 2);
case ColRealizedPnL:
return QString::number(pos->realizedPnL(), 'f', 2);
case ColPnLPercent:
if (pos->costPrice() > 0) {
double percent = (pos->marketPrice() - pos->costPrice()) / pos->costPrice() * 100;
return QString::number(percent, 'f', 2) + "%";
}
return "0.00%";
}
}
if (role == Qt::ForegroundRole) {
// 盈亏颜色
switch (index.column()) {
case ColFloatingPnL:
case ColDayPnL:
case ColRealizedPnL:
case ColPnLPercent:
double pnl = 0;
if (index.column() == ColFloatingPnL) pnl = pos->floatingPnL();
else if (index.column() == ColDayPnL) pnl = pos->dayPnL();
else if (index.column() == ColRealizedPnL) pnl = pos->realizedPnL();
else pnl = pos->marketPrice() - pos->costPrice();
if (pnl > 0) return QColor(255, 0, 0); // 红色盈利
if (pnl < 0) return QColor(0, 128, 0); // 绿色亏损
return QColor(128, 128, 128); // 灰色持平
}
}
return QVariant();
}
4.2 总盈亏展示
cpp
// AccountSummaryWidget.h
class AccountSummaryWidget : public QWidget
{
Q_OBJECT
public:
explicit AccountSummaryWidget(QWidget *parent = nullptr);
void setAccount(Account *account);
private slots:
void updateDisplay();
private:
void setupUi();
Account *m_account = nullptr;
QLabel *m_totalAssetsLabel;
QLabel *m_availableCashLabel;
QLabel *m_marketValueLabel;
QLabel *m_floatingPnLLabel;
QLabel *m_dayPnLLabel;
QLabel *m_realizedPnLLabel;
QLabel *m_totalPnLLabel;
};
void AccountSummaryWidget::updateDisplay()
{
if (!m_account) return;
m_totalAssetsLabel->setText(QString::number(m_account->totalAssets(), 'f', 2));
m_availableCashLabel->setText(QString::number(m_account->availableCash(), 'f', 2));
m_marketValueLabel->setText(QString::number(m_account->marketValue(), 'f', 2));
double floatingPnL = m_account->totalFloatingPnL();
m_floatingPnLLabel->setText(QString::number(floatingPnL, 'f', 2));
m_floatingPnLLabel->setStyleSheet(floatingPnL >= 0 ? "color: red;" : "color: green;");
double dayPnL = m_account->totalDayPnL();
m_dayPnLLabel->setText(QString::number(dayPnL, 'f', 2));
m_dayPnLLabel->setStyleSheet(dayPnL >= 0 ? "color: red;" : "color: green;");
m_realizedPnLLabel->setText(QString::number(m_account->totalRealizedPnL(), 'f', 2));
m_totalPnLLabel->setText(QString::number(m_account->totalPnL(), 'f', 2));
}
五、性能优化
5.1 批量更新优化
cpp
// 批量更新市价,减少信号触发
void Account::batchUpdatePrices(const QMap<QString, double> &prices)
{
// 先阻止信号
blockSignals(true);
for (auto it = prices.begin(); it != prices.end(); ++it) {
Position *pos = m_positions.value(it.key());
if (pos) {
pos->blockSignals(true);
pos->updateMarketPrice(it.value());
pos->blockSignals(false);
}
}
blockSignals(false);
// 统一发送一次信号
emit assetsChanged();
emit pnlChanged();
}
5.2 异步计算
cpp
// 对于大量持仓,使用异步计算
QFuture<double> Account::calculateTotalPnLAsync()
{
return QtConcurrent::run([this]() {
double total = 0.0;
for (auto *pos : m_positions) {
total += pos->floatingPnL() + pos->realizedPnL();
}
return total;
});
}
六、总结
实时盈亏计算核心要点:
- 数据模型:Position类封装持仓与盈亏计算逻辑
- 聚合计算:Account类聚合所有持仓的盈亏
- 实时更新:行情推送触发盈亏重算
- UI展示:Model-View模式高效更新界面
《注:若有发现问题欢迎大家提出来纠正》
以上仅为技术分享参考,不构成投资建议