-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsignal_engine.cpp
More file actions
133 lines (116 loc) · 4.43 KB
/
Copy pathsignal_engine.cpp
File metadata and controls
133 lines (116 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "signal_engine.h"
#include <sstream>
#include <iomanip>
#include <cmath>
#include <algorithm>
namespace hft {
SignalEngine::SignalEngine(Config cfg) : cfg_(std::move(cfg)) {}
void SignalEngine::on_trade(const TradeEvent& ev)
{
TradeRecord r;
r.ts_us = ev.ts_us;
r.price = ev.price;
r.qty = ev.qty;
r.is_buy = !ev.is_buyer_maker;
trades_.push_back(r);
evict_old_trades(ev.ts_us);
}
void SignalEngine::evict_old_trades(uint64_t now_us)
{
uint64_t cutoff = (now_us > cfg_.flow_window_us)
? now_us - cfg_.flow_window_us : 0;
while (!trades_.empty() && trades_.front().ts_us < cutoff)
trades_.pop_front();
}
SignalFlowStats SignalEngine::current_flow_stats(uint64_t now_us) const
{
uint64_t cutoff = (now_us > cfg_.flow_window_us)
? now_us - cfg_.flow_window_us : 0;
SignalFlowStats fs{};
double bn = 0.0, sn = 0.0;
for (const auto& t : trades_) {
if (t.ts_us < cutoff) continue;
if (t.is_buy) { fs.buy_volume += t.qty; bn += t.price * t.qty; }
else { fs.sell_volume += t.qty; sn += t.price * t.qty; }
++fs.count;
}
double total = fs.buy_volume + fs.sell_volume;
fs.trade_flow = (total > 0.0) ? (fs.buy_volume - fs.sell_volume) / total : 0.0;
fs.vwap_buy = (fs.buy_volume > 0.0) ? bn / fs.buy_volume : 0.0;
fs.vwap_sell = (fs.sell_volume > 0.0) ? sn / fs.sell_volume : 0.0;
return fs;
}
SignalSnapshot::SpreadRegime SignalEngine::classify_spread(double spread_bps) const
{
if (spread_bps <= cfg_.tight_spread_bps) return SignalSnapshot::SpreadRegime::TIGHT;
if (spread_bps >= cfg_.wide_spread_bps) return SignalSnapshot::SpreadRegime::WIDE;
return SignalSnapshot::SpreadRegime::NORMAL;
}
double SignalEngine::compute_composite(const SignalSnapshot& s) const
{
double mp_signal = 0.0;
if (s.mid > 0.0) {
double norm = s.microprice_offset_bps / 2.0;
mp_signal = std::max(-1.0, std::min(1.0, norm));
}
double flow = s.trade_flow;
if (cfg_.min_flow_volume > 0.0 &&
(s.buy_volume + s.sell_volume) < cfg_.min_flow_volume)
flow = 0.0;
double raw = cfg_.w_obi_l1 * s.obi_L1
+ cfg_.w_obi_l5 * s.obi_L5
+ cfg_.w_microprice * mp_signal
+ cfg_.w_trade_flow * flow;
double w_sum = cfg_.w_obi_l1 + cfg_.w_obi_l5 + cfg_.w_microprice + cfg_.w_trade_flow;
return std::max(-1.0, std::min(1.0, (w_sum > 0.0) ? raw / w_sum : 0.0));
}
SignalSnapshot SignalEngine::compute(const OrderBook& book,
uint64_t local_ts_us,
uint64_t exchange_ts_ms)
{
evict_old_trades(local_ts_us);
SignalSnapshot s{};
s.ts_us = local_ts_us;
s.exchange_ts_ms = exchange_ts_ms;
auto bst = book.stats();
if (!bst) return s;
s.best_bid = bst->best_bid;
s.best_ask = bst->best_ask;
s.mid = bst->mid;
s.spread = bst->spread;
s.spread_bps = bst->spread_bps;
auto mp = book.microprice();
s.microprice = mp.value_or(s.mid);
s.microprice_offset_bps = (s.mid > 0.0)
? (s.microprice - s.mid) / s.mid * 10000.0 : 0.0;
auto obi = [&](int n) { return book.obi(n).value_or(0.0); };
s.obi_L1 = obi(1);
s.obi_L3 = obi(3);
s.obi_L5 = obi(5);
s.obi_L10 = obi(10);
auto fs = current_flow_stats(local_ts_us);
s.buy_volume = fs.buy_volume;
s.sell_volume = fs.sell_volume;
s.trade_flow = fs.trade_flow;
s.vwap_buy = fs.vwap_buy;
s.vwap_sell = fs.vwap_sell;
s.trade_count = fs.count;
s.spread_regime = classify_spread(s.spread_bps);
s.composite = compute_composite(s);
return s;
}
std::string SignalSnapshot::to_string() const
{
std::ostringstream ss;
ss << std::fixed
<< "[" << exchange_ts_ms << "]"
<< " mid=" << std::setprecision(2) << mid
<< " sprd=" << std::setprecision(4) << spread_bps << "bps"
<< " obi=" << std::showpos << std::setprecision(3) << obi_L1 << std::noshowpos
<< " mp=" << std::showpos << std::setprecision(4) << microprice_offset_bps << std::noshowpos << "bps"
<< " flow=" << std::showpos << std::setprecision(3) << trade_flow << std::noshowpos
<< " sig=" << std::showpos << std::setprecision(3) << composite << std::noshowpos
<< " [" << spread_regime_str() << "]\n";
return ss.str();
}
} // namespace hft