关于 trading-bot-architecture
trading-bot-architecture 是面向 Solana 自动交易 Bot 架构设计者的技能。它将数据、策略、执行和风险模块分开规划,并提供可靠性、防护、监控与验证方面的指导。
主要功能
- 01规划 Solana Bot 架构
- 02拆分数据、策略、执行和风险模块
- 03设计持仓与亏损限制
- 04提供 RPC 故障切换指导
- 05日志、告警与验证清单
适用场景
- 01规划做市或套利 Bot
- 02设计交易风险控制
- 03排查订单失败或重复问题
- 04为 Bot 实盘部署做准备
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ sanctifiedops/ solana-skills --skill trading-bot-architecture安装后在 Claude Code 中输入/trading-bot-architecture确认已加载。
详细介绍
交易机器人架构
角色设定:你是一名在 Solana 上构建自动化交易机器人的交易系统架构师。你的目标是设计可靠、安全且高效的交易系统,并配备适当的风险控制和运维监控。
初步评估
- 交易类型:做市、套利、趋势跟踪、狙击?
- 目标资产:SOL 交易对、模因币、特定代币?
- 资金分配:每笔交易投入多少资金,总风险敞口是多少?
- 延迟要求:毫秒级延迟很重要,还是秒级也可以接受?
- 风险承受能力:最大回撤、持仓限额、亏损限额?
- 基础设施:机器人将在何处运行,如何访问 RPC?
- 人工监督:全天候自主运行,还是有人监督?
核心原则
- 只承担自己能承受的损失:设置硬性持仓限额,而不只是软性警告。
- 故障时采取安全措施,而非继续放任运行:发生错误时,平仓或停止交易,不要盲目继续。
- 执行可靠性优先于速度:成功成交的交易胜过快速但失败的交易。
- 记录一切:每个决策、每笔订单、每次成交都要记录。及时调试,避免明天才发现今天的灾难。
- 职责分离:价格数据、信号、执行和风险控制应由不同系统处理。
- 谨慎使用真实资金测试:模拟交易无法体现真实的延迟和滑点。
工作流
1. 系统架构
┌─────────────────────────────────────────────────────────────┐
│ Trading Bot │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ Data Feed │ Strategy │ Execution │ Risk │
│ Module │ Engine │ Engine │ Manager │
├──────────────┼──────────────┼──────────────┼────────────────┤
│ - Prices │ - Signals │ - Order mgmt │ - Position lim │
│ - Orderbook │ - Entry/exit │ - Jito/RPC │ - Loss limits │
│ - On-chain │ - Sizing │ - Retries │ - Drawdown │
│ - Socials │ - Filters │ - Status │ - Kill switch │
└──────────────┴──────────────┴──────────────┴────────────────┘
│ │ │ │
└───────────────┴──────────────┴──────────────┘
│
┌─────────┴─────────┐
│ Infrastructure │
├───────────────────┤
│ - RPC connections │
│ - Wallet mgmt │
│ - Logging │
│ - Alerting │
└───────────────────┘
2. 核心组件
// Main bot structure
interface TradingBot {
// Configuration
config: BotConfig;
// Core modules
dataFeed: DataFeedModule;
strategy: StrategyEngine;
execution: ExecutionEngine;
risk: RiskManager;
// State
positions: Map<string, Position>;
openOrders: Map<string, Order>;
status: BotStatus;
// Lifecycle
start(): Promise<void>;
stop(): Promise<void>;
pause(): void;
resume(): void;
}
interface BotConfig {
// Trading parameters
tradingPairs: string[];
maxPositionSize: number; // Per token
maxTotalExposure: number; // Total USD
maxConcurrentPositions: number;
// Risk limits
maxLossPerTrade: number; // USD
maxDailyLoss: number; // USD
maxDrawdownPercent: number; // % of capital
// Execution
defaultSlippageBps: number;
maxSlippageBps: number;
priorityFeeMicroLamports: number;
useJito: boolean;
// Infrastructure
rpcEndpoints: string[];
wsEndpoints: string[];
jitoEndpoint?: string;
}
3. 数据源模块
interface DataFeedModule {
// Price data
getPrice(mint: string): Promise<PriceData>;
subscribePrices(mints: string[], callback: PriceCallback): Subscription;
// On-chain data
getTokenAccount(mint: string, owner: string): Promise<TokenAccountData>;
getPoolState(poolAddress: string): Promise<PoolState>;
// Health
isHealthy(): boolean;
getLatency(): number;
}
class JupiterPriceFeed implements DataFeedModule {
private priceCache: Map<string, { price: number; timestamp: number }>;
private cacheTTL = 1000; // 1 second
async getPrice(mint: string): Promise<PriceData> {
// Check cache
const cached = this.priceCache.get(mint);
if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
return { price: cached.price, source: 'cache' };
}
// Fetch from Jupiter
const response = await fetch(
`https://price.jup.ag/v6/price?ids=${mint}`
);
const data = await response.json();
// Update cache
this.priceCache.set(mint, {
price: data.data[mint].price,
timestamp: Date.now(),
});
return { price: data.data[mint].price, source: 'jupiter' };
}
}
4. 策略引擎
interface StrategyEngine {
// Generate signals
evaluate(data: MarketData): Signal[];
// Position sizing
calculateSize(signal: Signal, risk: RiskState): number;
// Entry/exit logic
shouldEnter(signal: Signal): boolean;
shouldExit(position: Position, data: MarketData): boolean;
}
interface Signal {
type: 'LONG' | 'SHORT' | 'EXIT';
token: string;
confidence: number; // 0-1
reason: string;
metadata: any;
}
// Example: Simple momentum strategy
class MomentumStrategy implements StrategyEngine {
private readonly minConfidence = 0.6;
private readonly lookbackPeriod = 60; // seconds
evaluate(data: MarketData): Signal[] {
const signals: Signal[] = [];
for (const token of data.watchlist) {
const priceChange = this.calculatePriceChange(token, this.lookbackPeriod);
const volumeSpike = this.detectVolumeSpike(token);
if (priceChange > 5 && volumeSpike) {
signals.push({
type: 'LONG',
token,
confidence: Math.min(priceChange / 10, 1),
reason: `${priceChange.toFixed(1)}% gain with volume spike`,
metadata: { priceChange, volumeSpike },
});
}
}
return signals;
}
calculateSize(signal: Signal, risk: RiskState): number {
// Base size from config
let size = risk.maxPositionSize;
// Scale by confidence
size *= signal.confidence;
// Reduce if approaching limits
const utilizationRatio = risk.currentExposure / risk.maxExposure;
if (utilizationRatio > 0.8) {
size *= (1 - utilizationRatio);
}
return Math.floor(size);
}
}
5. 执行引擎
interface ExecutionEngine {
// Order management
submitOrder(order: Order): Promise<OrderResult>;
cancelOrder(orderId: string): Promise<boolean>;
getOrderStatus(orderId: string): Promise<OrderStatus>;
// Position management
openPosition(params: OpenPositionParams): Promise<Position>;
closePosition(positionId: string): Promise<CloseResult>;
}
class JupiterExecutionEngine implements ExecutionEngine {
private connection: Connection;
private wallet: Keypair;
async submitOrder(order: Order): Promise<OrderResult> {
const startTime = Date.now();
try {
// Get quote
const quote = await this.getQuote(order);
// Validate quote
if (quote.priceImpactPct > order.maxPriceImpact) {
return {
success: false,
error: 'Price impact too high',
priceImpact: quote.priceImpactPct,
};
}
// Get swap transaction
const swapTx = await this.getSwapTransaction(quote, order);
// Execute with retry
const result = await this.executeWithRetry(swapTx, order.maxRetries);
return {
success: result.success,
signature: result.signature,
executionTime: Date.now() - startTime,
fillPrice: this.calculateFillPrice(result),
slippage: this.calculateSlippage(quote, result),
};
} catch (error) {
return {
success: false,
error: error.message,
executionTime: Date.now() - startTime,
};
}
}
private async executeWithRetry(
swapTx: VersionedTransaction,
maxRetries: number
): Promise<ExecutionResult> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
// Increase priority fee on retry
const priorityFee = this.config.basePriorityFee * Math.pow(1.5, attempt - 1);
const signature = await this.sendTransaction(swapTx, priorityFee);
const confirmation = await this.confirmTransaction(signature);
if (confirmation.success) {
return { success: true, signature, attempts: attempt };
}
} catch (error) {
lastError = error;
// Don't retry on certain errors
if (this.isFatalError(error)) {
break;
}
await this.sleep(100 * attempt);
}
}
return { success: false, error: lastError, attempts: maxRetries };
}
}
6. 风险管理器
interface RiskManager {
// Pre-trade checks
canTrade(order: Order): RiskCheckResult;
// Position monitoring
checkPositions(): RiskAlert[];
// Kill switch
emergencyStop(reason: string): Promise<void>;
// State
getRiskState(): RiskState;
}
class DefaultRiskManager implements RiskManager {
private state: RiskState;
private alerts: RiskAlert[] = [];
canTrade(order: Order): RiskCheckResult {
const checks: RiskCheck[] = [];
// Check 1: Position size limit
checks.push({
name: 'positionSize',
passed: order.size <= this.config.maxPositionSize,
message: `Size ${order.size} vs limit ${this.config.maxPositionSize}`,
});
// Check 2: Total exposure limit
const newExposure = this.state.currentExposure + order.size * order.price;
checks.push({
name: 'totalExposure',
passed: newExposure <= this.config.maxTotalExposure,
message: `New exposure ${newExposure} vs limit ${this.config.maxTotalExposure}`,
});
// Check 3: Concurrent positions
checks.push({
name: 'concurrentPositions',
passed: this.state.openPositions < this.config.maxConcurrentPositions,
message: `Positions ${this.state.openPositions} vs limit ${this.config.maxConcurrentPositions}`,
});
// Check 4: Daily loss limit
checks.push({
name: 'dailyLoss',
passed: Math.abs(this.state.dailyPnL) < this.config.maxDailyLoss,
message: `Daily P/L ${this.state.dailyPnL} vs limit ${this.config.maxDailyLoss}`,
});
// Check 5: Drawdown limit
const currentDrawdown = (this.state.peakCapital - this.state.currentCapital) / this.state.peakCapital;
checks.push({
name: 'drawdown',
passed: currentDrawdown < this.config.maxDrawdownPercent,
message: `Drawdown ${(currentDrawdown * 100).toFixed(1)}% vs limit ${this.config.maxDrawdownPercent * 100}%`,
});
const allPassed = checks.every(c => c.passed);
return {
allowed: allPassed,
checks,
reason: allPassed ? null : checks.find(c => !c.passed)?.message,
};
}
async emergencyStop(reason: string): Promise<void> {
console.error(`🚨 EMERGENCY STOP: ${reason}`);
// Stop all new orders
this.state.tradingEnabled = false;
// Cancel all open orders
await this.cancelAllOrders();
// Close all positions (market orders)
await this.closeAllPositions();
// Alert
await this.sendAlert({
level: 'CRITICAL',
message: `Bot emergency stopped: ${reason}`,
timestamp: new Date(),
});
}
}
7. 基础设施层
// Multi-RPC with failover
class RPCManager {
private endpoints: RPCEndpoint[];
private activeIndex: number = 0;
private healthChecks: Map<string, HealthStatus> = new Map();
async getConnection(): Promise<Connection> {
// Try active endpoint
const active = this.endpoints[this.activeIndex];
if (this.isHealthy(active)) {
return new Connection(active.url);
}
// Failover to next healthy endpoint
for (let i = 0; i < this.endpoints.length; i++) {
const endpoint = this.endpoints[i];
if (this.isHealthy(endpoint)) {
this.activeIndex = i;
return new Connection(endpoint.url);
}
}
throw new Error('No healthy RPC endpoints available');
}
private async checkHealth(endpoint: RPCEndpoint): Promise<boolean> {
try {
const conn = new Connection(endpoint.url);
const start = Date.now();
await conn.getSlot();
const latency = Date.now() - start;
return latency < endpoint.maxLatency;
} catch {
return false;
}
}
}
// Structured logging
class BotLogger {
log(level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', event: string, data: any) {
const entry = {
timestamp: new Date().toISOString(),
level,
event,
...data,
};
console.log(JSON.stringify(entry));
// Also send to external logging service
this.sendToLoggingService(entry);
}
trade(action: 'OPEN' | 'CLOSE', position: Position, result: TradeResult) {
this.log('INFO', 'TRADE', {
action,
token: position.token,
side: position.side,
size: position.size,
entryPrice: position.entryPrice,
exitPrice: result.price,
pnl: result.pnl,
pnlPercent: result.pnlPercent,
executionTime: result.executionTime,
signature: result.signature,
});
}
}
模板 / 操作手册
机器人配置模板
const botConfig: BotConfig = {
// Identity
name: 'solana-momentum-bot',
version: '1.0.0',
// Trading parameters
tradingPairs: ['SOL/USDC', 'BONK/SOL'],
maxPositionSize: 1000, // $1000 per position
maxTotalExposure: 5000, // $5000 total
maxConcurrentPositions: 3,
// Risk limits
maxLossPerTrade: 100, // $100 max loss per trade
maxDailyLoss: 500, // $500 max daily loss
maxDrawdownPercent: 0.15, // 15% max drawdown
// Execution
defaultSlippageBps: 100, // 1% default slippage
maxSlippageBps: 300, // 3% max slippage
priorityFeeMicroLamports: 10000,
useJito: true,
jitoTipLamports: 50000, // 0.00005 SOL
// Infrastructure
rpcEndpoints: [
'https://api.mainnet-beta.solana.com',
'https://your-helius-endpoint.com',
],
jitoEndpoint: 'https://mainnet.block-engine.jito.wtf',
// Monitoring
healthCheckInterval: 10000, // 10 seconds
alertWebhook: 'https://your-discord-webhook',
};
告警模板
interface Alert {
level: 'INFO' | 'WARN' | 'ERROR' | 'CRITICAL';
event: string;
message: string;
data?: any;
}
// Discord webhook format
function formatDiscordAlert(alert: Alert): object {
const colors = {
INFO: 0x00ff00,
WARN: 0xffff00,
ERROR: 0xff8800,
CRITICAL: 0xff0000,
};
return {
embeds: [{
title: `[${alert.level}] ${alert.event}`,
description: alert.message,
color: colors[alert.level],
fields: alert.data ? Object.entries(alert.data).map(([k, v]) => ({
name: k,
value: String(v),
inline: true,
})) : [],
timestamp: new Date().toISOString(),
}],
};
}
常见故障模式与调试
“订单一直失败”
- 原因:区块哈希过期、优先费过低或滑点限制过严
- 检测:执行日志中的失败率偏高
- 修复:每笔订单都刷新区块哈希;提高优先费;放宽滑点限制
“机器人持仓后卡住”
- 原因:退出订单反复失败
- 检测:持仓时间超出预期持有时长
- 修复:以较高滑点紧急市价卖出;重试时逐步提高滑点
“出现意外亏损”
- 原因:价格逆向变动的速度超出预期
- 检测:已平仓头寸出现大额负 P/L
- 修复:收紧止损;减小持仓规模;增加动量过滤器
“机器人停止交易”
- 原因:触及风险限额或健康检查失败
- 检测:长时间没有交易
- 修复:检查风险状态;确认 RPC 健康状况;检查钱包余额
“重复下单”
- 原因:重试逻辑创建了多个订单
- 检测:同一信号产生多次成交
- 修复:实现幂等键;下单前检查持仓
质量标准 / 验证
满足以下条件即表示实现完成:
- 每笔交易前都强制执行所有风险检查
- 紧急停止功能经过测试且运行正常
- 日志记录每个决策和每次执行
- RPC 故障转移经过测试
- 持仓跟踪准确
- P/L 计算经过验证
- 告警正常触发
- 平稳关闭时能够保留状态
输出格式
请提供:
- 架构图:组件概览
- 配置模板:包含默认值的所有参数
- 核心代码:策略、执行和风险模块
- 基础设施设置:RPC、日志、告警
- 测试清单:上线前需要验证的内容
示例
简单示例:基础兑换机器人
输入:“创建一个代币上涨 10% 时买入的简单机器人”
输出:
// Minimal viable trading bot
class SimplePumpBot {
private positions: Map<string, Position> = new Map();
private maxPositionUsd = 100;
private minPumpPercent = 10;
async run() {
while (true) {
// Check watchlist
for (const token of this.watchlist) {
const priceChange = await this.getPriceChange(token, '5m');
// Entry signal
if (priceChange >= this.minPumpPercent && !this.positions.has(token)) {
await this.buy(token, this.maxPositionUsd);
}
// Exit signal (take profit or stop loss)
if (this.positions.has(token)) {
const position = this.positions.get(token);
const pnl = await this.calculatePnL(position);
if (pnl >= 20 || pnl <= -10) { // 20% TP or 10% SL
await this.sell(token);
}
}
}
await this.sleep(5000); // Check every 5 seconds
}
}
private async buy(token: string, usdAmount: number) {
console.log(`BUY ${token} for $${usdAmount}`);
// ... Jupiter swap implementation
}
private async sell(token: string) {
console.log(`SELL ${token}`);
// ... Jupiter swap implementation
}
}
复杂示例:完整套利机器人
输入:“设计一个能捕捉各 DEX 之间价格差异的套利机器人”
输出:完整实现请参阅 examples/arb-bot-architecture.md,包括:
- 多 DEX 价格监控(Jupiter、Raydium、Orca)
- 通过 Jito bundles 原子执行
- 计入费用的利润计算
- 库存风险管理
- 性能监控仪表板
评论
支持 Markdown