feat: initial polymarket copy trade tool

- Watcher: polls Data API for trader activity
- Executor: buy/sell with market orders + retry logic
- Position manager: JSON-based state tracking
- Auto-sell: limit orders at profit target
- Redeemer: check & redeem winning positions on-chain
- Config: env-based settings with validation
- DRY_RUN mode for safe testing
This commit is contained in:
direkturcrypto
2026-02-22 15:38:13 +07:00
commit 7c7fad45f3
16 changed files with 3211 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
const COLORS = {
reset: '\x1b[0m',
bright: '\x1b[1m',
dim: '\x1b[2m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
};
function timestamp() {
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);
}
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),
};
export default logger;
+48
View File
@@ -0,0 +1,48 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DATA_DIR = path.join(__dirname, '..', '..', 'data');
// Ensure data directory exists
if (!fs.existsSync(DATA_DIR)) {
fs.mkdirSync(DATA_DIR, { recursive: true });
}
/**
* Read JSON state file
* @param {string} filename - File name (e.g., "positions.json")
* @param {*} defaultValue - Default value if file doesn't exist
* @returns {*} Parsed JSON data
*/
export function readState(filename, defaultValue = {}) {
const filePath = path.join(DATA_DIR, filename);
try {
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(data);
}
} catch (err) {
console.error(`Error reading state file ${filename}:`, err.message);
}
return defaultValue;
}
/**
* Write JSON state file (atomic write via temp file)
* @param {string} filename - File name (e.g., "positions.json")
* @param {*} data - Data to write
*/
export function writeState(filename, data) {
const filePath = path.join(DATA_DIR, filename);
const tempPath = filePath + '.tmp';
try {
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
fs.renameSync(tempPath, filePath);
} catch (err) {
console.error(`Error writing state file ${filename}:`, err.message);
// Clean up temp file if rename failed
try { fs.unlinkSync(tempPath); } catch (_) { }
}
}