feat: full project overhaul — market maker, sniper, WebSocket watcher, terminal UI
- Rename project to polymarket-terminal - Add Market Maker bot (src/mm.js) with on-chain CTF split/merge/redeem via Gnosis Safe - Add Orderbook Sniper bot (src/sniper.js) with multi-asset GTC low-price orders - Add WebSocket watcher (src/services/wsWatcher.js) for real-time RTDS trade events - Add terminal dashboard UI (src/ui/dashboard.js) using blessed - Add CTF contract helpers (src/services/ctf.js) for splitPosition, mergePositions, redeemPositions - Add mmDetector, mmExecutor, sniperDetector, sniperExecutor services - Add simStats utility for dry-run P&L tracking - Translate all Indonesian-language strings to professional English across all files - Rewrite README.md in English with full setup guide, configuration reference, and architecture overview - Rewrite AGENT.MD in English as comprehensive AI agent and developer reference - Update package.json name, description, scripts, and keywords Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>
This commit is contained in:
+48
-22
@@ -1,34 +1,60 @@
|
||||
const COLORS = {
|
||||
reset: '\x1b[0m',
|
||||
bright: '\x1b[1m',
|
||||
dim: '\x1b[2m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
// ANSI color codes (used in normal terminal mode)
|
||||
const A = {
|
||||
reset: '\x1b[0m',
|
||||
dim: '\x1b[2m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m',
|
||||
white: '\x1b[37m',
|
||||
cyan: '\x1b[36m',
|
||||
};
|
||||
|
||||
function timestamp() {
|
||||
// Blessed tag pairs (used when dashboard is active)
|
||||
const B = {
|
||||
red: ['{red-fg}', '{/red-fg}'],
|
||||
green: ['{green-fg}', '{/green-fg}'],
|
||||
yellow: ['{yellow-fg}', '{/yellow-fg}'],
|
||||
blue: ['{blue-fg}', '{/blue-fg}'],
|
||||
magenta: ['{magenta-fg}', '{/magenta-fg}'],
|
||||
cyan: ['{cyan-fg}', '{/cyan-fg}'],
|
||||
};
|
||||
|
||||
let outputFn = null; // When set, all log goes here (blessed dashboard mode)
|
||||
|
||||
function ts() {
|
||||
return new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||||
}
|
||||
|
||||
function formatMsg(level, color, emoji, ...args) {
|
||||
const ts = timestamp();
|
||||
const prefix = `${COLORS.dim}[${ts}]${COLORS.reset} ${color}${emoji} ${level}${COLORS.reset}`;
|
||||
console.log(prefix, ...args);
|
||||
function stringify(args) {
|
||||
return args.map((a) => (a && typeof a === 'object' ? JSON.stringify(a) : String(a))).join(' ');
|
||||
}
|
||||
|
||||
function log(ansiColor, bColor, emoji, level, ...args) {
|
||||
const msg = stringify(args);
|
||||
if (outputFn) {
|
||||
const [open, close] = bColor;
|
||||
outputFn(`{gray-fg}[${ts()}]{/gray-fg} ${open}${emoji} ${level}${close} ${msg}`);
|
||||
} else {
|
||||
process.stdout.write(
|
||||
`${A.dim}[${ts()}]${A.reset} ${ansiColor}${emoji} ${level}${A.reset} ${msg}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const logger = {
|
||||
info: (...args) => formatMsg('INFO', COLORS.blue, 'ℹ️ ', ...args),
|
||||
success: (...args) => formatMsg('SUCCESS', COLORS.green, '✅', ...args),
|
||||
warn: (...args) => formatMsg('WARN', COLORS.yellow, '⚠️ ', ...args),
|
||||
error: (...args) => formatMsg('ERROR', COLORS.red, '❌', ...args),
|
||||
trade: (...args) => formatMsg('TRADE', COLORS.magenta, '📊', ...args),
|
||||
watch: (...args) => formatMsg('WATCH', COLORS.cyan, '👀', ...args),
|
||||
money: (...args) => formatMsg('MONEY', COLORS.green, '💰', ...args),
|
||||
info: (...a) => log(A.blue, B.blue, 'ℹ️ ', 'INFO', ...a),
|
||||
success: (...a) => log(A.green, B.green, '✅', 'SUCCESS', ...a),
|
||||
warn: (...a) => log(A.yellow, B.yellow, '⚠️ ', 'WARN', ...a),
|
||||
error: (...a) => log(A.red, B.red, '❌', 'ERROR', ...a),
|
||||
trade: (...a) => log(A.magenta, B.magenta, '📊', 'TRADE', ...a),
|
||||
watch: (...a) => log(A.cyan, B.cyan, '👀', 'WATCH', ...a),
|
||||
money: (...a) => log(A.green, B.green, '💰', 'MONEY', ...a),
|
||||
|
||||
/** Call once after initDashboard() to redirect all logs to the TUI */
|
||||
setOutput(fn) {
|
||||
outputFn = fn;
|
||||
},
|
||||
};
|
||||
|
||||
export default logger;
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { readState, writeState } from './state.js';
|
||||
|
||||
const SIM_FILE = 'sim_stats.json';
|
||||
|
||||
function defaultStats() {
|
||||
return {
|
||||
startTime: new Date().toISOString(),
|
||||
totalBuys: 0,
|
||||
totalResolved: 0,
|
||||
wins: 0,
|
||||
losses: 0,
|
||||
closedPnl: 0,
|
||||
closedPositions: [],
|
||||
};
|
||||
}
|
||||
|
||||
export function getSimStats() {
|
||||
return readState(SIM_FILE, defaultStats());
|
||||
}
|
||||
|
||||
export function recordSimBuy() {
|
||||
const stats = getSimStats();
|
||||
stats.totalBuys = (stats.totalBuys || 0) + 1;
|
||||
writeState(SIM_FILE, stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record result of a resolved simulation position
|
||||
* @param {Object} position - the position object
|
||||
* @param {'WIN'|'LOSS'} result
|
||||
* @param {number} pnl - realized P&L in USDC
|
||||
* @param {number} returned - USDC returned
|
||||
*/
|
||||
export function recordSimResult(position, result, pnl, returned) {
|
||||
const stats = getSimStats();
|
||||
stats.totalResolved = (stats.totalResolved || 0) + 1;
|
||||
if (result === 'WIN') stats.wins = (stats.wins || 0) + 1;
|
||||
else stats.losses = (stats.losses || 0) + 1;
|
||||
stats.closedPnl = ((stats.closedPnl || 0) + pnl);
|
||||
|
||||
stats.closedPositions = stats.closedPositions || [];
|
||||
stats.closedPositions.push({
|
||||
market: position.market,
|
||||
outcome: position.outcome,
|
||||
totalCost: position.totalCost,
|
||||
shares: position.shares,
|
||||
returned,
|
||||
pnl,
|
||||
result,
|
||||
closedAt: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Keep last 100 entries
|
||||
if (stats.closedPositions.length > 100) {
|
||||
stats.closedPositions = stats.closedPositions.slice(-100);
|
||||
}
|
||||
|
||||
writeState(SIM_FILE, stats);
|
||||
}
|
||||
|
||||
export function resetSimStats() {
|
||||
writeState(SIM_FILE, defaultStats());
|
||||
}
|
||||
Reference in New Issue
Block a user