-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnalyticsModule.java
More file actions
103 lines (85 loc) · 4.16 KB
/
Copy pathAnalyticsModule.java
File metadata and controls
103 lines (85 loc) · 4.16 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
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
public class AnalyticsModule extends Thread {
private MatchingEngineUI ui;
// Cumulative counters — updated by matching engine thread
private final LongAdder totalTrades = new LongAdder();
private final LongAdder totalVolumeUnits = new LongAdder(); // qty traded
// We track volume as cents to avoid floating point in atomics
private final LongAdder totalVolumeCents = new LongAdder();
// Book state — updated by matching engine after each order
private volatile double bestBid = 0;
private volatile double bestAsk = 0;
private volatile int bidBookDepth = 0; // number of unmatched bids
private volatile int askBookDepth = 0; // number of unmatched asks
// For throughput calculation
private long lastTradeSnapshot = 0;
public void setUI(MatchingEngineUI ui) { this.ui = ui; }
private long lastSnapshotTimeMs = System.currentTimeMillis();
private volatile boolean running = true;
public AnalyticsModule() {
super("AnalyticsModule");
setDaemon(true);
}
// ── Called by MatchingEngine ──────────────────────────────────────────────
public void recordTrade(double price, int qty) {
totalTrades.increment();
totalVolumeUnits.add(qty);
totalVolumeCents.add(Math.round(price * qty * 100));
}
public void updateBookState(double bestBid, double bestAsk,
int bidDepth, int askDepth) {
this.bestBid = bestBid;
this.bestAsk = bestAsk;
this.bidBookDepth = bidDepth;
this.askBookDepth = askDepth;
}
public void shutdown() {
this.running = false;
interrupt();
}
// ── Background printer ────────────────────────────────────────────────────
@Override
public void run() {
while (running) {
try {
Thread.sleep(2000);
printSnapshot();
} catch (InterruptedException e) {
break;
}
}
printSnapshot(); // final snapshot on shutdown
}
private void printSnapshot() {
long trades = totalTrades.sum();
double volumeUSD = totalVolumeCents.sum() / 100.0;
double spread = (bestBid > 0 && bestAsk > 0) ? (bestAsk - bestBid) : -1;
int totalDepth = bidBookDepth + askBookDepth;
double imbalance = totalDepth > 0
? ((double)(bidBookDepth - askBookDepth) / totalDepth) * 100.0
: 0;
long nowMs = System.currentTimeMillis();
long deltaMs = Math.max(1, nowMs - lastSnapshotTimeMs);
long deltaTrades = trades - lastTradeSnapshot;
double tradesPerSec = deltaTrades / (deltaMs / 1000.0);
lastTradeSnapshot = trades;
lastSnapshotTimeMs = nowMs;
System.out.println("\n─────────── Analytics Snapshot ───────────");
System.out.printf(" Trades total : %,d%n", trades);
System.out.printf(" Volume (USD) : $%,.2f%n", volumeUSD);
System.out.printf(" Throughput : %,.0f trades/sec%n", tradesPerSec);
if (spread >= 0)
System.out.printf(" Spread : $%.4f%n", spread);
System.out.printf(" Book depth : %d bids / %d asks%n", bidBookDepth, askBookDepth);
System.out.printf(" Order imbal. : %.1f%%%s%n",
Math.abs(imbalance),
imbalance > 5 ? " (BUY heavy)" :
imbalance < -5 ? " (SELL heavy)" : " (balanced)");
System.out.println("──────────────────────────────────────────");
if (ui != null) {
ui.updateAnalytics(trades, volumeUSD, tradesPerSec, spread, bidBookDepth, askBookDepth, imbalance);
}
}
}