Enhance TUI dashboard: add transaction IDs, fix window summary, and handle multiple trades per second

- Add transaction hash to trade display (first 10 chars + ...)
- Improve window summary rendering with better error handling and fallbacks
- Implement trade queue to handle multiple trades arriving per second
- Process queue on each render tick to display all trades
- Add processTradeQueue() method to move queued trades to recent trades
- Improve window summary display formatting with arrows and colors
This commit is contained in:
Alexis Bruneteau 2025-12-07 13:18:42 +01:00
parent 135059d1d0
commit de52b1a65d
2 changed files with 56 additions and 7 deletions

View File

@ -102,6 +102,7 @@ class PolymarketCopyTradingBot {
market: trade.marketName || trade.market,
shares: trade.size,
price: trade.price,
txHash: trade.txHash,
});
// Get current balance with timeout

View File

@ -18,6 +18,7 @@ interface DashboardData {
shares: number;
price: number;
status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'SIMULATED';
txHash?: string;
};
recentTrades: Array<{
timestamp: string;
@ -26,6 +27,7 @@ interface DashboardData {
shares: number;
price: number;
status: 'SUCCESS' | 'FAILED' | 'SIMULATED';
txHash?: string;
}>;
heartbeat: string;
}
@ -39,6 +41,15 @@ export class TUIDashboard {
private tradesHistoryBox: blessed.Widgets.BoxElement;
private statusBox: blessed.Widgets.BoxElement;
private maxHistory: number;
private tradeQueue: Array<{
timestamp: string;
type: 'BUY' | 'SELL';
outcome: string;
market: string;
shares: number;
price: number;
txHash?: string;
}> = [];
private data: DashboardData = {
status: 'RUNNING',
botAddress: '',
@ -212,13 +223,40 @@ export class TUIDashboard {
market: string;
shares: number;
price: number;
txHash?: string;
}) {
// Add to queue to handle multiple trades per second
this.tradeQueue.push(trade);
// Always update currentTrade for display in the current trade panel
this.data.currentTrade = {
...trade,
status: 'PENDING',
};
}
processTradeQueue() {
// Move all queued trades to recent trades history
while (this.tradeQueue.length > 0) {
const trade = this.tradeQueue.shift();
if (trade) {
this.data.recentTrades.unshift({
timestamp: trade.timestamp,
type: trade.type,
outcome: trade.outcome,
shares: trade.shares,
price: trade.price,
status: 'SIMULATED',
txHash: trade.txHash,
});
}
}
// Keep last N trades (screen-aware) so the history panel can fill the page
if (this.data.recentTrades.length > (this.maxHistory || 100)) {
this.data.recentTrades.splice(this.maxHistory || 100);
}
}
updateTradeStatus(status: 'SUCCESS' | 'FAILED') {
if (this.data.currentTrade) {
// Allow SIMULATED flows by accepting a string and mapping accordingly
@ -243,6 +281,7 @@ export class TUIDashboard {
price: this.data.currentTrade.price,
// If status is not SUCCESS/FAILED, keep it as-is (e.g., SIMULATED)
status: (this.data.currentTrade as any).status as 'SUCCESS' | 'FAILED' | 'SIMULATED',
txHash: this.data.currentTrade.txHash,
});
// Keep last N trades (screen-aware) so the history panel can fill the page
@ -314,16 +353,23 @@ export class TUIDashboard {
for (let i = 0; i < 2; i++) {
const ws = currentWindow - i * 900;
const summary = storage.getWindowSummary(ws);
const up = summary['Up'] ? `${summary['Up'].count} @ ${summary['Up'].avgPrice?.toFixed(4)}` : '0';
const down = summary['Down'] ? `${summary['Down'].count} @ ${summary['Down'].avgPrice?.toFixed(4)}` : '0';
// Check both capitalized and original case for outcomes
const upData = summary['Up'] || summary['up'] || summary['UP'];
const downData = summary['Down'] || summary['down'] || summary['DOWN'];
const up = upData ? `{bold}${upData.count.toFixed(2)}{/} @ ${upData.avgPrice?.toFixed(4)}` : '{dim}0{/}';
const down = downData ? `{bold}${downData.count.toFixed(2)}{/} @ ${downData.avgPrice?.toFixed(4)}` : '{dim}0{/}';
const label = new Date(ws * 1000).toLocaleTimeString();
lines.push(`{bold}Window:{/} ${label}`);
lines.push(`{green-fg}Up:{/} ${up} {red-fg}Down:{/} ${down}`);
lines.push(`{cyan-fg}{bold}Window:{/}{/} ${label}`);
lines.push(`{green-fg}Up:{/} ${up} {red-fg}Down:{/} ${down}`);
if (i < 1) lines.push('');
}
this.windowSummaryBox.setContent(lines.join('\n'));
if (lines.length === 0) {
this.windowSummaryBox.setContent('{grey-fg}Waiting for trades...{/}');
} else {
this.windowSummaryBox.setContent(lines.join('\n'));
}
} catch (e) {
this.windowSummaryBox.setContent('{grey-fg}No window data{/}');
this.windowSummaryBox.setContent(`{grey-fg}Error loading window data{/}`);
}
if (this.data.recentTrades.length === 0) {
@ -335,11 +381,12 @@ export class TUIDashboard {
const statusColor = trade.status === 'SUCCESS' ? 'green-fg' : trade.status === 'SIMULATED' ? 'cyan-fg' : 'red-fg';
const typeSymbol = trade.type === 'BUY' ? '▲' : '▼';
const typeColor = trade.type === 'BUY' ? 'green-fg' : 'red-fg';
const txId = trade.txHash ? trade.txHash.substring(0, 10) + '...' : 'N/A';
return (
`${statusSymbol} {${statusColor}}${trade.status}{/} | ` +
`{${typeColor}}${typeSymbol} ${trade.type}{/} {bold}${trade.shares.toFixed(2)}{/} ${trade.outcome} @ ${trade.price.toFixed(4)} | ` +
`{grey-fg}${trade.timestamp}{/}`
`{grey-fg}${trade.timestamp} | Tx: ${txId}{/}`
);
})
.join('\n');
@ -365,6 +412,7 @@ export class TUIDashboard {
tick() {
this.updateHeartbeat();
this.processTradeQueue();
this.render();
}