Files
polymarket-terminal/src/services/sniperExecutor.js
T
direkturcrypto 526076fe6e 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>
2026-02-23 23:03:06 +07:00

92 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* sniperExecutor.js
* Places GTC limit BUY orders at a very low price on both sides of a market.
*
* Strategy:
* - For each market detected by sniperDetector, place two GTC BUY orders:
* UP token at $SNIPER_PRICE × SNIPER_SHARES shares
* DOWN token at $SNIPER_PRICE × SNIPER_SHARES shares
* - Orders sit in the orderbook. If someone panic-dumps below the price,
* the order fills and becomes redeemable if that side wins.
* - GTC orders expire automatically when the market closes — no cleanup needed.
*
* Cost per market: SNIPER_PRICE × SNIPER_SHARES × 2 sides
* e.g. $0.01 × 5 × 2 = $0.10 per market, $0.30 for 3 assets per 5-min slot
*/
import { Side, OrderType } from '@polymarket/clob-client';
import config from '../config/index.js';
import { getClient } from './client.js';
import logger from '../utils/logger.js';
// In-memory tracking of placed snipe orders (for TUI status panel)
const activeSnipes = []; // { asset, side, question, orderId, price, shares, cost, potentialPayout }
export function getActiveSnipes() {
return [...activeSnipes];
}
export async function executeSnipe(market) {
const { asset, conditionId, question, yesTokenId, noTokenId, tickSize, negRisk } = market;
const label = question.slice(0, 40);
const sim = config.dryRun ? '[SIM] ' : '';
const sides = [
{ name: 'UP', tokenId: yesTokenId },
{ name: 'DOWN', tokenId: noTokenId },
];
logger.info(`SNIPER: ${sim}${asset.toUpperCase()} — "${label}" | $${config.sniperPrice} × ${config.sniperShares}sh each side`);
for (const { name, tokenId } of sides) {
if (config.dryRun) {
const cost = config.sniperPrice * config.sniperShares;
logger.trade(`SNIPER[SIM]: ${asset.toUpperCase()} ${name} @ $${config.sniperPrice} × ${config.sniperShares}sh | cost $${cost.toFixed(3)} | payout $${config.sniperShares} if wins`);
activeSnipes.push({
asset: asset.toUpperCase(),
side: name,
question: label,
orderId: `sim-${Date.now()}-${tokenId.slice(-6)}`,
price: config.sniperPrice,
shares: config.sniperShares,
cost,
potentialPayout: config.sniperShares,
});
continue;
}
const client = getClient();
try {
const res = await client.createAndPostOrder(
{
tokenID: tokenId,
side: Side.BUY,
price: config.sniperPrice,
size: config.sniperShares,
},
{ tickSize, negRisk },
OrderType.GTC,
);
if (res?.success) {
const cost = config.sniperPrice * config.sniperShares;
logger.trade(`SNIPER: ${asset.toUpperCase()} ${name} @ $${config.sniperPrice} × ${config.sniperShares}sh | cost $${cost.toFixed(3)} | order ${res.orderID}`);
activeSnipes.push({
asset: asset.toUpperCase(),
side: name,
question: label,
orderId: res.orderID,
price: config.sniperPrice,
shares: config.sniperShares,
cost,
potentialPayout: config.sniperShares,
});
} else {
logger.warn(`SNIPER: ${asset.toUpperCase()} ${name} order failed — ${res?.errorMsg || 'unknown'}`);
}
} catch (err) {
logger.error(`SNIPER: ${asset.toUpperCase()} ${name} error — ${err.message}`);
}
}
}