@elizaos/plugin-auto-trader 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # ElizaOS AutoTrader Plugin (`plugin-auto-trader`)
2
+
3
+ ## 1. Overview
4
+
5
+ The `plugin-auto-trader` is an ElizaOS plugin designed to provide autonomous and configurable cryptocurrency trading capabilities. It allows agents to execute trading strategies, simulate these strategies against historical data, and analyze their performance.
6
+
7
+ This plugin is built with a modular approach, enabling the easy addition of new trading strategies and data sources.
8
+
9
+ ## 2. Key Features
10
+
11
+ * **Pluggable Trading Strategy Framework:**
12
+ * A core `TradingStrategy` interface that all strategies implement.
13
+ * `StrategyRegistryService` for discovering and managing available strategies.
14
+ * **Implemented Trading Strategies:**
15
+ * **`RandomStrategy`**: Makes random buy/sell decisions. Useful as a baseline.
16
+ * Configurable: `tradeAttemptProbability`, `buyProbability`, `maxTradeSizePercentage`, `fixedTradeQuantity`.
17
+ * **`RuleBasedStrategy`**: Executes trades based on technical indicators and market conditions.
18
+ * Configurable: A list of `RuleCondition` objects (RSI, SMA/EMA Crossover, Volume triggers), stop-loss/take-profit settings, trade sizing.
19
+ * *Note: Currently uses mock calculations for TI. Integration with a library like `technicalindicators` is planned.*
20
+ * **`LLMStrategy`**: Leverages a Large Language Model (LLM) to make trading decisions.
21
+ * Configurable: `modelName`, `systemPrompt`, `customPromptPrefix/Suffix`, trade sizing defaults.
22
+ * Builds a detailed prompt based on market data and agent state.
23
+ * Parses JSON responses from the LLM.
24
+ * *Note: Requires integration with a core ElizaOS LLM service.*
25
+ * **Simulation Engine:**
26
+ * **`HistoricalDataService`**: Fetches and caches historical OHLCV data.
27
+ * Supports a `mockSource` for testing and a conceptual `birdeye` API integration (requires `BIRDEYE_API_KEY` environment variable).
28
+ * Conceptual filesystem-based caching (actual file I/O is placeholder).
29
+ * **`SimulationService`**: Runs backtests of trading strategies against historical data.
30
+ * Simulates trade execution, including transaction costs and basic slippage.
31
+ * Tracks portfolio value, P&L (including realized P&L per closing trade), and individual trades.
32
+ * **Benchmarking & Reporting:**
33
+ * **`PerformanceReportingService`**: Generates detailed performance reports (`SimulationReport`).
34
+ * Calculates key metrics: Total P&L (absolute and percentage), Win/Loss Ratio, Average Win/Loss size, Max Drawdown, Total Trades, Buy & Hold P&L benchmark.
35
+ * *Note: Advanced metrics like Sharpe/Sortino Ratio are placeholders.*
36
+
37
+ ## 3. Installation & Setup (Conceptual)
38
+
39
+ This plugin is intended to be part of the ElizaOS ecosystem.
40
+
41
+ 1. Ensure `plugin-auto-trader` is included in your ElizaOS plugins directory.
42
+ 2. The plugin will be initialized during ElizaOS startup via its `src/index.ts` (`initializePlugin` function).
43
+ 3. **API Keys (Required for some features):**
44
+ * For Birdeye data via `HistoricalDataService`: Set the `BIRDEYE_API_KEY` environment variable.
45
+ 4. **External Libraries (Future):**
46
+ * When `RuleBasedStrategy` is updated to use a real Technical Indicators library (e.g., `technicalindicators`), it will need to be installed: `npm install technicalindicators` (or `bun install`).
47
+
48
+ ## 4. How to Use (Conceptual - via direct service calls for now)
49
+
50
+ Currently, interaction with the plugin's services would typically be done programmatically within another ElizaOS plugin or service that has access to the auto-trader's registered services.
51
+
52
+ ### 4.1. Accessing Services
53
+
54
+ Services are registered with ElizaOS runtime (conceptually) during plugin initialization. You would typically retrieve them using `runtime.getService('serviceName')`:
55
+
56
+ * `runtime.getService('auto-trader/StrategyRegistryService')`
57
+ * `runtime.getService('auto-trader/HistoricalDataService')`
58
+ * `runtime.getService('auto-trader/SimulationService')`
59
+ * `runtime.getService('auto-trader/PerformanceReportingService')`
60
+
61
+ *(Note: The exact service names depend on the runtime's registration mechanism. The plugin currently attempts to register service classes.)*
62
+
63
+ ### 4.2. Listing Available Strategies
64
+
65
+ ```typescript
66
+ // const strategyRegistry = runtime.getService('auto-trader/StrategyRegistryService') as StrategyRegistryService;
67
+ // const strategies = strategyRegistry.listStrategies();
68
+ // console.log(strategies.map(s => s.name));
69
+ ```
70
+
71
+ ### 4.3. Running a Simulation
72
+
73
+ Use the `SimulationService` to run a backtest:
74
+
75
+ ```typescript
76
+ // const simulationService = runtime.getService('auto-trader/SimulationService') as SimulationService;
77
+ // const historicalDataService = runtime.getService('auto-trader/HistoricalDataService') as DefaultHistoricalDataService;
78
+ // const strategyRegistry = runtime.getService('auto-trader/StrategyRegistryService') as StrategyRegistryService;
79
+ // const performanceReporting = new PerformanceReportingService(); // Or get from runtime if stateful
80
+
81
+ // Ensure services are available, then:
82
+ // const simService = new SimulationService(strategyRegistry, historicalDataService, performanceReporting);
83
+
84
+ const simulationParams = {
85
+ strategyId: 'random-v1', // or 'rule-based-v1', 'llm-v1'
86
+ strategyParams: {
87
+ // Strategy-specific parameters, e.g., for RandomStrategy:
88
+ tradeAttemptProbability: 0.5,
89
+ buyProbability: 0.6,
90
+ fixedTradeQuantity: 2,
91
+ // e.g., for RuleBasedStrategy:
92
+ // rules: [{ type: 'RSI', rsiPeriod: 14, rsiOversold: 30, action: 'BUY' }],
93
+ // stopLossTakeProfit: { stopLossPercentage: 0.05 }
94
+ },
95
+ symbol: 'SOL/USDC', // Use a token address for Birdeye, e.g., 'So11111111111111111111111111111111111111112'
96
+ timeframe: '1h',
97
+ startDate: new Date('2023-01-01T00:00:00Z'),
98
+ endDate: new Date('2023-03-01T00:00:00Z'),
99
+ initialCapital: 10000,
100
+ transactionCostPercentage: 0.001, // 0.1%
101
+ slippagePercentage: { marketOrder: 0.0005 }, // 0.05%
102
+ dataApiSource: 'mockSource', // or 'birdeye' (if API key is set)
103
+ };
104
+
105
+ // async function runTestSimulation() {
106
+ // try {
107
+ // const report = await simulationService.runBacktest(simulationParams);
108
+ // console.log('Simulation Report:', JSON.stringify(report, null, 2));
109
+ // console.log('P&L:', report.metrics.totalPnlPercentage * 100, '%');
110
+ // console.log('Buy & Hold P&L:', report.metrics.buyAndHoldPnlPercentage * 100, '%');
111
+ // } catch (error) {
112
+ // console.error('Simulation failed:', error);
113
+ // }
114
+ // }
115
+ // runTestSimulation();
116
+ ```
117
+
118
+ ### 4.4. Strategy Configuration Parameters
119
+
120
+ * **`RandomStrategyParams`**: `tradeAttemptProbability`, `buyProbability`, `maxTradeSizePercentage`, `fixedTradeQuantity`.
121
+ * **`RuleBasedStrategyParams`**: `rules` (array of `RuleCondition`), `stopLossTakeProfit`, `tradeSizePercentage`, `fixedTradeQuantity`, `minIndicatorDataPoints`.
122
+ * `RuleCondition`: `type` (`RSI`, `SMA_CROSSOVER`, etc.), `action` (`BUY`/`SELL`), and type-specific params (e.g., `rsiPeriod`, `shortMAPeriod`, `longMAPeriod`, `maType`).
123
+ * **`LLMStrategyParams`**: `modelName`, `systemPrompt`, `customPromptPrefix`, `customPromptSuffix`, `maxTokens`, `temperature`, `defaultTradeSizePercentage`, `defaultFixedTradeQuantity`, `structuredOutputSchema`.
124
+
125
+ ## 5. Current Limitations & Placeholders
126
+
127
+ * **Real Data Source Integration**: `HistoricalDataService` currently uses mock data for most sources. Birdeye integration is conceptual and requires a valid API key. Full implementation of other sources (CoinMarketCap, Jupiter, CCXT) is pending.
128
+ * **Real Technical Indicators**: `RuleBasedStrategy` uses mock calculations for technical indicators. It needs to be integrated with a library like `technicalindicators`.
129
+ * **Real LLM Service**: `LLMStrategy` requires integration with a functional ElizaOS core LLM service.
130
+ * **Order Execution Service**: No real implementation for live/paper trading yet; only simulation.
131
+ * **Advanced Metrics**: Sharpe Ratio, Sortino Ratio, and other advanced financial metrics are placeholders.
132
+ * **Filesystem Cache**: The filesystem cache in `HistoricalDataService` is conceptual; actual file I/O operations are commented out.
133
+
134
+ ## 6. Future Enhancements (from original spec)
135
+
136
+ * More sophisticated strategies (arbitrage, ML-based).
137
+ * Portfolio-level strategies.
138
+ * Walk-forward optimization.
139
+ * Live paper trading & real money trading.
140
+ * Strategy marketplace integration.
141
+ * Visual backtesting tools.
142
+
143
+ This README provides a starting point and will be updated as the plugin evolves.
@@ -0,0 +1,9 @@
1
+ import {
2
+ MeanReversionStrategy
3
+ } from "./chunk-BSXTWI5Q.js";
4
+ import "./chunk-AS6N6A3H.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+ export {
7
+ MeanReversionStrategy
8
+ };
9
+ //# sourceMappingURL=MeanReversionStrategy-4ZBKT4XN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ MomentumBreakoutStrategy
3
+ } from "./chunk-Z6SUPK5O.js";
4
+ import "./chunk-AS6N6A3H.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+ export {
7
+ MomentumBreakoutStrategy
8
+ };
9
+ //# sourceMappingURL=MomentumBreakoutStrategy-O7I3EMGB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ RandomStrategy
3
+ } from "./chunk-KB2EBQUN.js";
4
+ import "./chunk-AS6N6A3H.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+ export {
7
+ RandomStrategy
8
+ };
9
+ //# sourceMappingURL=RandomStrategy-GPGG56MV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ RuleBasedStrategy
3
+ } from "./chunk-GYTZTIWK.js";
4
+ import "./chunk-AS6N6A3H.js";
5
+ import "./chunk-PZ5AY32C.js";
6
+ export {
7
+ RuleBasedStrategy
8
+ };
9
+ //# sourceMappingURL=RuleBasedStrategy-7O47DGTU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,18 @@
1
+ // src/types.ts
2
+ var TradeType = /* @__PURE__ */ ((TradeType2) => {
3
+ TradeType2["BUY"] = "BUY";
4
+ TradeType2["SELL"] = "SELL";
5
+ return TradeType2;
6
+ })(TradeType || {});
7
+ var OrderType = /* @__PURE__ */ ((OrderType2) => {
8
+ OrderType2["MARKET"] = "MARKET";
9
+ OrderType2["LIMIT"] = "LIMIT";
10
+ OrderType2["STOP"] = "STOP";
11
+ return OrderType2;
12
+ })(OrderType || {});
13
+
14
+ export {
15
+ TradeType,
16
+ OrderType
17
+ };
18
+ //# sourceMappingURL=chunk-AS6N6A3H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["// Import core types\nimport type { IAgentRuntime as AgentRuntime, UUID } from \"@elizaos/core\";\n\n// Define wallet and token types locally since they're not exported from @elizaos/core\nexport interface TokenBalance {\n mint: string;\n balance: string;\n decimals: number;\n uiAmount: number;\n}\n\nexport interface TokenData {\n address: string;\n symbol: string;\n name: string;\n decimals: number;\n price?: number;\n volume24h?: number;\n liquidity?: number;\n}\n\nexport interface WalletAsset {\n mint: string;\n balance: number;\n decimals: number;\n uiAmount: number;\n}\n\nexport interface IWalletService {\n getBalance(): Promise<number>;\n getTokenBalances(): Promise<TokenBalance[]>;\n}\n\n// #region --- Portfolio and Trading Data Interfaces ---\n\n/**\n * Core types for simulations and backtesting\n */\n\nexport interface OHLCV {\n timestamp: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volume: number;\n}\n\nexport interface StrategyContextMarketData {\n currentPrice: number;\n lastPrices: number[];\n indicators?: { [indicatorName: string]: number };\n priceData?: OHLCV[]; // Full price data for custom analysis\n}\n\nexport interface AgentState {\n portfolioValue: number;\n volatility: number;\n confidenceLevel: number;\n recentTrades: number;\n lastAction?: string;\n sentiment?: number;\n}\n\nexport interface TradingStrategy {\n id: string;\n name: string;\n description: string;\n decide(params: {\n marketData: StrategyContextMarketData;\n agentState: AgentState;\n portfolioSnapshot: PortfolioSnapshot;\n agentRuntime?: AgentRuntime;\n }): Promise<TradeOrder | null>;\n initialize?(agentRuntime?: AgentRuntime): Promise<void>;\n isReady(): boolean;\n configure?(params: any): void;\n}\n\nexport enum TradeType {\n BUY = \"BUY\",\n SELL = \"SELL\",\n}\n\nexport enum OrderType {\n MARKET = \"MARKET\",\n LIMIT = \"LIMIT\",\n STOP = \"STOP\",\n}\n\nexport interface TradeOrder {\n pair: string;\n action: TradeType;\n quantity: number;\n orderType: OrderType;\n price?: number;\n stopPrice?: number;\n timestamp: number;\n reason?: string;\n}\n\nexport interface Trade extends TradeOrder {\n executedPrice: number;\n executedTimestamp: number;\n fees: number;\n feeCurrency?: string; // Add optional feeCurrency\n tradeId?: string; // Add optional tradeId\n realizedPnl?: number; // Profit or loss realized on this trade (typically for SELL trades)\n}\n\nexport interface PortfolioSnapshot {\n timestamp: number;\n holdings: { [assetSymbol: string]: number };\n totalValue: number;\n}\n\nexport interface PerformanceMetrics {\n totalPnlAbsolute: number;\n totalPnlPercentage: number;\n sharpeRatio?: number;\n sortinoRatio?: number;\n winLossRatio: number;\n averageWinAmount: number;\n averageLossAmount: number;\n maxDrawdown: number;\n totalTrades: number;\n winningTrades: number;\n losingTrades: number;\n firstAssetPrice?: number;\n lastAssetPrice?: number;\n buyAndHoldPnlPercentage?: number;\n}\n\nexport interface SimulationReport {\n strategy: string;\n pair: string;\n startDate: number;\n endDate: number;\n trades: Trade[];\n portfolioSnapshots: PortfolioSnapshot[];\n finalPortfolioValue: number;\n metrics: PerformanceMetrics;\n}\n\n// HistoricalDataService\nexport interface HistoricalDataService {\n fetchData(\n pair: string,\n interval: string,\n startDate: Date,\n endDate: Date,\n dataSource: string,\n ): Promise<OHLCV[]>;\n}\n\n// #endregion --- Portfolio and Trading Data Interfaces ---\n\n// #region --- Service & Other Interfaces ---\n\nexport interface TradeSimulationResult {\n isValid: boolean;\n reason?: string;\n updatedBalance?: number;\n updatedPortfolio?: { [assetSymbol: string]: PortfolioAssetHolding };\n}\n\n// Wallet portfolio for auto-trader\nexport interface WalletPortfolio {\n totalUsd: string;\n totalSol?: string;\n items: Array<{\n name: string;\n address: string;\n symbol: string;\n decimals: number;\n balance: string;\n uiAmount: string;\n priceUsd: string;\n valueUsd: string;\n valueSol?: string;\n }>;\n}\n\nexport interface PortfolioAssetHolding {\n mint: string;\n balance: number;\n decimals: number;\n uiAmount: number;\n averagePrice: number;\n symbol?: string;\n assetAddress: string;\n}\n\nexport interface Position {\n id: UUID;\n tokenAddress: string;\n amount: number;\n entryPrice: number;\n currentPrice?: number;\n unrealizedPnl?: number;\n realizedPnl?: number;\n}\n\nexport interface WalletOperationResult {\n success: boolean;\n transactionHash?: string;\n error?: string;\n}\n\nexport interface PortfolioHolding {\n tokenAddress: string;\n tokenSymbol: string;\n tokenName: string;\n amount: number;\n decimals: number;\n usdValue: number;\n tokenPriceUsd: number;\n}\n\nexport interface TrackedPosition {\n tokenAddress: string;\n symbol: string;\n entryPrice: number;\n currentPrice: number;\n quantity: number;\n value: number;\n pnl: number;\n pnlPercentage: number;\n}\n\n// Strategy-specific parameters\nexport interface RandomStrategyParams {\n buyProbability?: number;\n sellProbability?: number;\n maxTradeSize?: number;\n minTradeSize?: number;\n randomSeed?: number;\n}\n\nexport interface RuleBasedStrategyParams {\n indicators?: string[];\n buyConditions?: {\n [indicator: string]: { threshold: number; condition: \"above\" | \"below\" };\n };\n sellConditions?: {\n [indicator: string]: { threshold: number; condition: \"above\" | \"below\" };\n };\n riskSettings?: {\n maxPositionSize?: number;\n stopLossPercentage?: number;\n takeProfitPercentage?: number;\n };\n}\n\nexport interface LLMStrategyParams {\n modelName?: string;\n customPromptPrefix?: string;\n customPromptSuffix?: string;\n maxTokens?: number;\n temperature?: number;\n defaultTradeSizePercentage?: number;\n defaultFixedTradeQuantity?: number;\n structuredOutputSchema?: any;\n systemPrompt?: string;\n}\n\n// #endregion --- Service & Other Interfaces ---\n"],"mappings":";AA+EO,IAAK,YAAL,kBAAKA,eAAL;AACL,EAAAA,WAAA,SAAM;AACN,EAAAA,WAAA,UAAO;AAFG,SAAAA;AAAA,GAAA;AAKL,IAAK,YAAL,kBAAKC,eAAL;AACL,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,WAAQ;AACR,EAAAA,WAAA,UAAO;AAHG,SAAAA;AAAA,GAAA;","names":["TradeType","OrderType"]}
@@ -0,0 +1,162 @@
1
+ // src/strategies/MeanReversionStrategy.ts
2
+ import * as talib from "technicalindicators";
3
+ var MeanReversionStrategy = class {
4
+ id = "mean-reversion-strategy";
5
+ name = "MeanReversionStrategy";
6
+ description = "A strategy that trades on mean reversion patterns using Bollinger Bands and RSI";
7
+ config;
8
+ initialized = false;
9
+ runtime = null;
10
+ constructor(config) {
11
+ this.config = {
12
+ // Default configuration
13
+ bbPeriod: 20,
14
+ bbStdDev: 2,
15
+ rsiPeriod: 14,
16
+ rsiOversold: 30,
17
+ rsiOverbought: 70,
18
+ positionSizePercent: 0.02,
19
+ stopLossPercent: 0.03,
20
+ takeProfitPercent: 0.02,
21
+ minVolatility: 0.01,
22
+ maxVolatility: 0.05,
23
+ minVolumeRatio: 1.2,
24
+ bbEntryThreshold: 0.95,
25
+ rsiConfirmation: true,
26
+ ...config
27
+ };
28
+ }
29
+ async initialize(runtime) {
30
+ this.runtime = runtime;
31
+ this.initialized = true;
32
+ console.log(`[${this.name}] Initialized with config:`, this.config);
33
+ }
34
+ isReady() {
35
+ return this.initialized;
36
+ }
37
+ async decide(params) {
38
+ const { marketData, agentState, portfolioSnapshot } = params;
39
+ const { priceData, currentPrice } = marketData;
40
+ if (!priceData || priceData.length < Math.max(this.config.bbPeriod, this.config.rsiPeriod) + 10) {
41
+ return null;
42
+ }
43
+ const closes = priceData.map((c) => c.close);
44
+ const _highs = priceData.map((c) => c.high);
45
+ const _lows = priceData.map((c) => c.low);
46
+ const volumes = priceData.map((c) => c.volume);
47
+ const bb = this.calculateBollingerBands(closes);
48
+ const rsi = this.calculateRSI(closes);
49
+ const volatility = agentState.volatility;
50
+ const volumeRatio = this.calculateVolumeRatio(volumes);
51
+ if (!bb || !rsi || rsi.length === 0) return null;
52
+ const currentRSI = rsi[rsi.length - 1];
53
+ const { upper, lower, middle } = bb;
54
+ if (volatility < this.config.minVolatility || volatility > this.config.maxVolatility) {
55
+ console.log(
56
+ `[${this.name}] Volatility ${volatility.toFixed(4)} outside range [${this.config.minVolatility}, ${this.config.maxVolatility}]`
57
+ );
58
+ return null;
59
+ }
60
+ if (volumeRatio < this.config.minVolumeRatio) {
61
+ console.log(
62
+ `[${this.name}] Volume ratio ${volumeRatio.toFixed(2)} below minimum ${this.config.minVolumeRatio}`
63
+ );
64
+ return null;
65
+ }
66
+ const distanceFromUpper = (upper - currentPrice) / currentPrice;
67
+ const distanceFromLower = (currentPrice - lower) / currentPrice;
68
+ const distanceFromMiddle = Math.abs(currentPrice - middle) / middle;
69
+ if (currentPrice <= lower * (1 + (1 - this.config.bbEntryThreshold))) {
70
+ const rsiCondition = !this.config.rsiConfirmation || currentRSI < this.config.rsiOversold;
71
+ if (rsiCondition) {
72
+ const positionSize = this.calculatePositionSize(portfolioSnapshot.totalValue, currentPrice);
73
+ console.log(`[${this.name}] BUY SIGNAL - Price at lower BB, RSI: ${currentRSI.toFixed(2)}`);
74
+ return {
75
+ action: "BUY" /* BUY */,
76
+ orderType: "MARKET" /* MARKET */,
77
+ pair: "SOL/USDC",
78
+ // This should come from context
79
+ quantity: positionSize,
80
+ reason: `Mean reversion buy: Price ${distanceFromLower.toFixed(2)}% below lower BB, RSI: ${currentRSI.toFixed(2)}`,
81
+ timestamp: Date.now()
82
+ };
83
+ }
84
+ }
85
+ if (currentPrice >= upper * (1 - (1 - this.config.bbEntryThreshold))) {
86
+ const rsiCondition = !this.config.rsiConfirmation || currentRSI > this.config.rsiOverbought;
87
+ if (rsiCondition) {
88
+ const currentHolding2 = portfolioSnapshot.holdings.SOL || 0;
89
+ if (currentHolding2 > 0) {
90
+ console.log(
91
+ `[${this.name}] SELL SIGNAL - Price at upper BB, RSI: ${currentRSI.toFixed(2)}`
92
+ );
93
+ return {
94
+ action: "SELL" /* SELL */,
95
+ orderType: "MARKET" /* MARKET */,
96
+ pair: "SOL/USDC",
97
+ quantity: currentHolding2,
98
+ reason: `Mean reversion sell: Price ${distanceFromUpper.toFixed(2)}% above upper BB, RSI: ${currentRSI.toFixed(2)}`,
99
+ timestamp: Date.now()
100
+ };
101
+ }
102
+ }
103
+ }
104
+ const currentHolding = portfolioSnapshot.holdings.SOL || 0;
105
+ if (currentHolding > 0 && distanceFromMiddle < 0.01) {
106
+ console.log(`[${this.name}] Price returned to mean, exiting position`);
107
+ return {
108
+ action: "SELL" /* SELL */,
109
+ orderType: "MARKET" /* MARKET */,
110
+ pair: "SOL/USDC",
111
+ quantity: currentHolding,
112
+ reason: `Price returned to mean (${distanceFromMiddle.toFixed(2)}% from middle BB)`,
113
+ timestamp: Date.now()
114
+ };
115
+ }
116
+ return null;
117
+ }
118
+ calculateBollingerBands(closes) {
119
+ if (closes.length < this.config.bbPeriod) return null;
120
+ const bb = talib.BollingerBands.calculate({
121
+ period: this.config.bbPeriod,
122
+ values: closes,
123
+ stdDev: this.config.bbStdDev
124
+ });
125
+ if (!bb || bb.length === 0) return null;
126
+ const lastBB = bb[bb.length - 1];
127
+ return {
128
+ upper: lastBB.upper,
129
+ middle: lastBB.middle,
130
+ lower: lastBB.lower
131
+ };
132
+ }
133
+ calculateRSI(closes) {
134
+ const rsi = talib.RSI.calculate({
135
+ period: this.config.rsiPeriod,
136
+ values: closes
137
+ });
138
+ return rsi;
139
+ }
140
+ calculateVolumeRatio(volumes) {
141
+ if (volumes.length < 20) return 0;
142
+ const recentVolume = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5;
143
+ const avgVolume = volumes.slice(-20).reduce((a, b) => a + b, 0) / 20;
144
+ return avgVolume > 0 ? recentVolume / avgVolume : 0;
145
+ }
146
+ calculatePositionSize(portfolioValue, price) {
147
+ const positionValue = portfolioValue * this.config.positionSizePercent;
148
+ return positionValue / price;
149
+ }
150
+ updateConfig(config) {
151
+ this.config = { ...this.config, ...config };
152
+ console.log(`[${this.name}] Config updated:`, this.config);
153
+ }
154
+ getConfig() {
155
+ return { ...this.config };
156
+ }
157
+ };
158
+
159
+ export {
160
+ MeanReversionStrategy
161
+ };
162
+ //# sourceMappingURL=chunk-BSXTWI5Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/MeanReversionStrategy.ts"],"sourcesContent":["import type { AgentRuntime } from \"@elizaos/core\";\nimport * as talib from \"technicalindicators\";\nimport {\n type AgentState,\n type OHLCV,\n OrderType,\n type PortfolioSnapshot,\n type StrategyContextMarketData,\n type TradeOrder,\n TradeType,\n type TradingStrategy,\n} from \"../types.ts\";\n\nexport interface MeanReversionConfig {\n // Bollinger Bands settings\n bbPeriod: number;\n bbStdDev: number;\n\n // RSI settings\n rsiPeriod: number;\n rsiOversold: number;\n rsiOverbought: number;\n\n // Risk management\n positionSizePercent: number;\n stopLossPercent: number;\n takeProfitPercent: number;\n\n // Market condition filters\n minVolatility: number;\n maxVolatility: number;\n minVolumeRatio: number;\n\n // Entry/exit thresholds\n bbEntryThreshold: number; // How far outside BB for entry\n rsiConfirmation: boolean;\n}\n\nexport class MeanReversionStrategy implements TradingStrategy {\n public readonly id = \"mean-reversion-strategy\";\n public readonly name = \"MeanReversionStrategy\";\n public readonly description =\n \"A strategy that trades on mean reversion patterns using Bollinger Bands and RSI\";\n private config: MeanReversionConfig;\n private initialized = false;\n private runtime: AgentRuntime | null = null;\n\n constructor(config?: Partial<MeanReversionConfig>) {\n this.config = {\n // Default configuration\n bbPeriod: 20,\n bbStdDev: 2,\n rsiPeriod: 14,\n rsiOversold: 30,\n rsiOverbought: 70,\n positionSizePercent: 0.02,\n stopLossPercent: 0.03,\n takeProfitPercent: 0.02,\n minVolatility: 0.01,\n maxVolatility: 0.05,\n minVolumeRatio: 1.2,\n bbEntryThreshold: 0.95,\n rsiConfirmation: true,\n ...config,\n };\n }\n\n async initialize(runtime: AgentRuntime): Promise<void> {\n this.runtime = runtime;\n this.initialized = true;\n console.log(`[${this.name}] Initialized with config:`, this.config);\n }\n\n isReady(): boolean {\n return this.initialized;\n }\n\n async decide(params: {\n marketData: StrategyContextMarketData;\n agentState: AgentState;\n portfolioSnapshot: PortfolioSnapshot;\n agentRuntime?: AgentRuntime;\n }): Promise<TradeOrder | null> {\n const { marketData, agentState, portfolioSnapshot } = params;\n const { priceData, currentPrice } = marketData;\n\n if (\n !priceData ||\n priceData.length < Math.max(this.config.bbPeriod, this.config.rsiPeriod) + 10\n ) {\n return null;\n }\n\n // Extract price arrays\n const closes = priceData.map((c: OHLCV) => c.close);\n const _highs = priceData.map((c: OHLCV) => c.high);\n const _lows = priceData.map((c: OHLCV) => c.low);\n const volumes = priceData.map((c: OHLCV) => c.volume);\n\n // Calculate indicators\n const bb = this.calculateBollingerBands(closes);\n const rsi = this.calculateRSI(closes);\n const volatility = agentState.volatility;\n const volumeRatio = this.calculateVolumeRatio(volumes);\n\n if (!bb || !rsi || rsi.length === 0) return null;\n\n const currentRSI = rsi[rsi.length - 1];\n const { upper, lower, middle } = bb;\n\n // Market condition checks\n if (volatility < this.config.minVolatility || volatility > this.config.maxVolatility) {\n console.log(\n `[${this.name}] Volatility ${volatility.toFixed(4)} outside range [${this.config.minVolatility}, ${this.config.maxVolatility}]`,\n );\n return null;\n }\n\n if (volumeRatio < this.config.minVolumeRatio) {\n console.log(\n `[${this.name}] Volume ratio ${volumeRatio.toFixed(2)} below minimum ${this.config.minVolumeRatio}`,\n );\n return null;\n }\n\n // Check for mean reversion opportunities\n const distanceFromUpper = (upper - currentPrice) / currentPrice;\n const distanceFromLower = (currentPrice - lower) / currentPrice;\n const distanceFromMiddle = Math.abs(currentPrice - middle) / middle;\n\n // Buy signal: Price near lower band + RSI oversold\n if (currentPrice <= lower * (1 + (1 - this.config.bbEntryThreshold))) {\n const rsiCondition = !this.config.rsiConfirmation || currentRSI < this.config.rsiOversold;\n\n if (rsiCondition) {\n const positionSize = this.calculatePositionSize(portfolioSnapshot.totalValue, currentPrice);\n\n console.log(`[${this.name}] BUY SIGNAL - Price at lower BB, RSI: ${currentRSI.toFixed(2)}`);\n\n return {\n action: TradeType.BUY,\n orderType: OrderType.MARKET,\n pair: \"SOL/USDC\", // This should come from context\n quantity: positionSize,\n reason: `Mean reversion buy: Price ${distanceFromLower.toFixed(2)}% below lower BB, RSI: ${currentRSI.toFixed(2)}`,\n timestamp: Date.now(),\n };\n }\n }\n\n // Sell signal: Price near upper band + RSI overbought\n if (currentPrice >= upper * (1 - (1 - this.config.bbEntryThreshold))) {\n const rsiCondition = !this.config.rsiConfirmation || currentRSI > this.config.rsiOverbought;\n\n if (rsiCondition) {\n const currentHolding = portfolioSnapshot.holdings.SOL || 0;\n\n if (currentHolding > 0) {\n console.log(\n `[${this.name}] SELL SIGNAL - Price at upper BB, RSI: ${currentRSI.toFixed(2)}`,\n );\n\n return {\n action: TradeType.SELL,\n orderType: OrderType.MARKET,\n pair: \"SOL/USDC\",\n quantity: currentHolding,\n reason: `Mean reversion sell: Price ${distanceFromUpper.toFixed(2)}% above upper BB, RSI: ${currentRSI.toFixed(2)}`,\n timestamp: Date.now(),\n };\n }\n }\n }\n\n // Exit position if price returns to mean\n const currentHolding = portfolioSnapshot.holdings.SOL || 0;\n if (currentHolding > 0 && distanceFromMiddle < 0.01) {\n console.log(`[${this.name}] Price returned to mean, exiting position`);\n\n return {\n action: TradeType.SELL,\n orderType: OrderType.MARKET,\n pair: \"SOL/USDC\",\n quantity: currentHolding,\n reason: `Price returned to mean (${distanceFromMiddle.toFixed(2)}% from middle BB)`,\n timestamp: Date.now(),\n };\n }\n\n return null;\n }\n\n private calculateBollingerBands(\n closes: number[],\n ): { upper: number; middle: number; lower: number } | null {\n if (closes.length < this.config.bbPeriod) return null;\n\n const bb = talib.BollingerBands.calculate({\n period: this.config.bbPeriod,\n values: closes,\n stdDev: this.config.bbStdDev,\n });\n\n if (!bb || bb.length === 0) return null;\n\n const lastBB = bb[bb.length - 1];\n return {\n upper: lastBB.upper,\n middle: lastBB.middle,\n lower: lastBB.lower,\n };\n }\n\n private calculateRSI(closes: number[]): number[] {\n const rsi = talib.RSI.calculate({\n period: this.config.rsiPeriod,\n values: closes,\n });\n return rsi;\n }\n\n private calculateVolumeRatio(volumes: number[]): number {\n if (volumes.length < 20) return 0;\n\n const recentVolume = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5;\n const avgVolume = volumes.slice(-20).reduce((a, b) => a + b, 0) / 20;\n\n return avgVolume > 0 ? recentVolume / avgVolume : 0;\n }\n\n private calculatePositionSize(portfolioValue: number, price: number): number {\n const positionValue = portfolioValue * this.config.positionSizePercent;\n return positionValue / price;\n }\n\n updateConfig(config: Partial<MeanReversionConfig>): void {\n this.config = { ...this.config, ...config };\n console.log(`[${this.name}] Config updated:`, this.config);\n }\n\n getConfig(): MeanReversionConfig {\n return { ...this.config };\n }\n}\n"],"mappings":"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","names":["currentHolding"]}