@elizaos/plugin-auto-trader 2.0.0-alpha.5 → 2.0.0-alpha.7

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.
Files changed (36) hide show
  1. package/dist/MeanReversionStrategy-OIZBZHQS.js +8 -0
  2. package/dist/MomentumBreakoutStrategy-24SLLRUG.js +8 -0
  3. package/dist/RandomStrategy-MW4XS7CM.js +8 -0
  4. package/dist/RuleBasedStrategy-7JDDIKO5.js +8 -0
  5. package/dist/{chunk-Z6SUPK5O.js → chunk-6QBYU4NX.js} +56 -34
  6. package/dist/chunk-6QBYU4NX.js.map +1 -0
  7. package/dist/{chunk-AS6N6A3H.js → chunk-DVZ7FRKE.js} +1 -1
  8. package/dist/chunk-DVZ7FRKE.js.map +1 -0
  9. package/dist/{chunk-KB2EBQUN.js → chunk-HTFXFKAF.js} +1 -1
  10. package/dist/chunk-HTFXFKAF.js.map +1 -0
  11. package/dist/{chunk-GYTZTIWK.js → chunk-HXJTH4SS.js} +23 -7
  12. package/dist/chunk-HXJTH4SS.js.map +1 -0
  13. package/dist/{chunk-7GI4G3ZN.js → chunk-MTAQ364B.js} +9 -3
  14. package/dist/chunk-MTAQ364B.js.map +1 -0
  15. package/dist/index.js +608 -4691
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.node-7OX5TYRS.js +885 -0
  18. package/dist/index.node-7OX5TYRS.js.map +1 -0
  19. package/package.json +3 -3
  20. package/dist/MeanReversionStrategy-VQJGG3DE.js +0 -9
  21. package/dist/MomentumBreakoutStrategy-O7I3EMGB.js +0 -9
  22. package/dist/RandomStrategy-GPGG56MV.js +0 -9
  23. package/dist/RuleBasedStrategy-7O47DGTU.js +0 -9
  24. package/dist/chunk-7GI4G3ZN.js.map +0 -1
  25. package/dist/chunk-AS6N6A3H.js.map +0 -1
  26. package/dist/chunk-GYTZTIWK.js.map +0 -1
  27. package/dist/chunk-KB2EBQUN.js.map +0 -1
  28. package/dist/chunk-PZ5AY32C.js +0 -10
  29. package/dist/chunk-PZ5AY32C.js.map +0 -1
  30. package/dist/chunk-Z6SUPK5O.js.map +0 -1
  31. package/dist/index.node-GDSZROYS.js +0 -3169
  32. package/dist/index.node-GDSZROYS.js.map +0 -1
  33. /package/dist/{MeanReversionStrategy-VQJGG3DE.js.map → MeanReversionStrategy-OIZBZHQS.js.map} +0 -0
  34. /package/dist/{MomentumBreakoutStrategy-O7I3EMGB.js.map → MomentumBreakoutStrategy-24SLLRUG.js.map} +0 -0
  35. /package/dist/{RandomStrategy-GPGG56MV.js.map → RandomStrategy-MW4XS7CM.js.map} +0 -0
  36. /package/dist/{RuleBasedStrategy-7O47DGTU.js.map → RuleBasedStrategy-7JDDIKO5.js.map} +0 -0
@@ -0,0 +1,8 @@
1
+ import {
2
+ MeanReversionStrategy
3
+ } from "./chunk-MTAQ364B.js";
4
+ import "./chunk-DVZ7FRKE.js";
5
+ export {
6
+ MeanReversionStrategy
7
+ };
8
+ //# sourceMappingURL=MeanReversionStrategy-OIZBZHQS.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ MomentumBreakoutStrategy
3
+ } from "./chunk-6QBYU4NX.js";
4
+ import "./chunk-DVZ7FRKE.js";
5
+ export {
6
+ MomentumBreakoutStrategy
7
+ };
8
+ //# sourceMappingURL=MomentumBreakoutStrategy-24SLLRUG.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ RandomStrategy
3
+ } from "./chunk-HTFXFKAF.js";
4
+ import "./chunk-DVZ7FRKE.js";
5
+ export {
6
+ RandomStrategy
7
+ };
8
+ //# sourceMappingURL=RandomStrategy-MW4XS7CM.js.map
@@ -0,0 +1,8 @@
1
+ import {
2
+ RuleBasedStrategy
3
+ } from "./chunk-HXJTH4SS.js";
4
+ import "./chunk-DVZ7FRKE.js";
5
+ export {
6
+ RuleBasedStrategy
7
+ };
8
+ //# sourceMappingURL=RuleBasedStrategy-7JDDIKO5.js.map
@@ -36,7 +36,9 @@ var MomentumBreakoutStrategy = class {
36
36
  if (potentialToken) {
37
37
  return potentialToken;
38
38
  }
39
- console.warn("[MomentumBreakout] Could not infer trading pair from portfolio");
39
+ console.warn(
40
+ "[MomentumBreakout] Could not infer trading pair from portfolio"
41
+ );
40
42
  return "UNKNOWN";
41
43
  }
42
44
  async decide(params) {
@@ -53,43 +55,58 @@ var MomentumBreakoutStrategy = class {
53
55
  }
54
56
  if (!priceData || priceData.length < 100) {
55
57
  if (this.debugMode && this.tradeAttempts === 0) {
56
- console.log(`[MomentumBreakout] Not enough data: ${priceData?.length || 0} candles`);
58
+ console.log(
59
+ `[MomentumBreakout] Not enough data: ${priceData?.length || 0} candles`
60
+ );
57
61
  }
58
62
  return null;
59
63
  }
60
64
  const tradingPair = this.inferTradingPair(portfolioSnapshot);
61
65
  const indicators = this.calculateMomentumIndicators(priceData);
62
66
  const holdings = Object.entries(portfolioSnapshot.holdings);
63
- const assetHolding = holdings.find(([key, value]) => key !== "USDC" && value > 0);
67
+ const assetHolding = holdings.find(
68
+ ([key, value]) => key !== "USDC" && value > 0
69
+ );
64
70
  const hasPosition = assetHolding && assetHolding[1] > 0;
65
71
  const assetSymbol = assetHolding ? assetHolding[0] : null;
66
72
  if (hasPosition && this.activePosition && assetSymbol) {
67
- return this.managePosition(currentPrice, indicators, assetSymbol, portfolioSnapshot);
73
+ return this.managePosition(
74
+ currentPrice,
75
+ indicators,
76
+ assetSymbol,
77
+ portfolioSnapshot
78
+ );
68
79
  }
69
80
  const shouldEnter = this.shouldEnter(indicators, currentPrice);
70
81
  if (this.debugMode && this.tradeAttempts < 5) {
71
82
  const conditions = this.evaluateEntryConditions(indicators);
72
- console.log(`[MomentumBreakout] Early evaluation #${this.tradeAttempts + 1}:`, {
73
- shouldEnter,
74
- price: currentPrice.toFixed(6),
75
- indicators: {
76
- priceChange5m: `${(indicators.priceChange5m * 100).toFixed(3)}%`,
77
- priceChange15m: `${(indicators.priceChange15m * 100).toFixed(3)}%`,
78
- volumeRatio: indicators.volumeRatio.toFixed(2),
79
- trend: indicators.trendDirection,
80
- adx: indicators.adx.toFixed(1)
81
- },
82
- conditions: {
83
- momentum: conditions.hasMomentum,
84
- volume: conditions.hasVolume,
85
- trend: conditions.trendAligned,
86
- notAtResistance: conditions.goodEntry,
87
- metCount: `${conditions.metConditions}/4`
83
+ console.log(
84
+ `[MomentumBreakout] Early evaluation #${this.tradeAttempts + 1}:`,
85
+ {
86
+ shouldEnter,
87
+ price: currentPrice.toFixed(6),
88
+ indicators: {
89
+ priceChange5m: `${(indicators.priceChange5m * 100).toFixed(3)}%`,
90
+ priceChange15m: `${(indicators.priceChange15m * 100).toFixed(3)}%`,
91
+ volumeRatio: indicators.volumeRatio.toFixed(2),
92
+ trend: indicators.trendDirection,
93
+ adx: indicators.adx.toFixed(1)
94
+ },
95
+ conditions: {
96
+ momentum: conditions.hasMomentum,
97
+ volume: conditions.hasVolume,
98
+ trend: conditions.trendAligned,
99
+ notAtResistance: conditions.goodEntry,
100
+ metCount: `${conditions.metConditions}/4`
101
+ }
88
102
  }
89
- });
103
+ );
90
104
  }
91
105
  if (!hasPosition && shouldEnter) {
92
- const positionSize = this.calculatePositionSize(portfolioSnapshot.totalValue, currentPrice);
106
+ const positionSize = this.calculatePositionSize(
107
+ portfolioSnapshot.totalValue,
108
+ currentPrice
109
+ );
93
110
  if (positionSize > 1e-3) {
94
111
  this.activePosition = {
95
112
  entryPrice: currentPrice,
@@ -122,17 +139,20 @@ var MomentumBreakoutStrategy = class {
122
139
  this.tradeAttempts++;
123
140
  if (this.debugMode && this.tradeAttempts % 200 === 0) {
124
141
  const conditions = this.evaluateEntryConditions(indicators);
125
- console.log(`[MomentumBreakout] Periodic evaluation #${this.tradeAttempts}:`, {
126
- shouldEnter: conditions.metConditions >= 2,
127
- price: currentPrice.toFixed(6),
128
- conditions: {
129
- momentum: conditions.hasMomentum,
130
- volume: conditions.hasVolume,
131
- trend: conditions.trendAligned,
132
- notAtResistance: conditions.goodEntry,
133
- metCount: `${conditions.metConditions}/4`
142
+ console.log(
143
+ `[MomentumBreakout] Periodic evaluation #${this.tradeAttempts}:`,
144
+ {
145
+ shouldEnter: conditions.metConditions >= 2,
146
+ price: currentPrice.toFixed(6),
147
+ conditions: {
148
+ momentum: conditions.hasMomentum,
149
+ volume: conditions.hasVolume,
150
+ trend: conditions.trendAligned,
151
+ notAtResistance: conditions.goodEntry,
152
+ metCount: `${conditions.metConditions}/4`
153
+ }
134
154
  }
135
- });
155
+ );
136
156
  }
137
157
  return null;
138
158
  }
@@ -297,7 +317,9 @@ var MomentumBreakoutStrategy = class {
297
317
  if (candles.length < period + 1) return 0;
298
318
  const priceChanges = [];
299
319
  for (let i = 1; i < candles.length; i++) {
300
- priceChanges.push(Math.abs(candles[i].close - candles[i - 1].close) / candles[i - 1].close);
320
+ priceChanges.push(
321
+ Math.abs(candles[i].close - candles[i - 1].close) / candles[i - 1].close
322
+ );
301
323
  }
302
324
  const avgChange = priceChanges.reduce((a, b) => a + b) / priceChanges.length;
303
325
  return Math.min(avgChange * 1e3, 100);
@@ -307,4 +329,4 @@ var MomentumBreakoutStrategy = class {
307
329
  export {
308
330
  MomentumBreakoutStrategy
309
331
  };
310
- //# sourceMappingURL=chunk-Z6SUPK5O.js.map
332
+ //# sourceMappingURL=chunk-6QBYU4NX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/MomentumBreakoutStrategy.ts"],"sourcesContent":["import {\n\ttype AgentState,\n\ttype OHLCV,\n\tOrderType,\n\ttype PortfolioSnapshot,\n\ttype StrategyContextMarketData,\n\ttype TradeOrder,\n\tTradeType,\n\ttype TradingStrategy,\n} from \"../types.ts\";\n\ninterface MomentumIndicators {\n\t// Price momentum\n\tpriceChange1h: number;\n\tpriceChange5m: number;\n\tpriceChange15m: number;\n\n\t// Volume analysis\n\tvolumeRatio: number;\n\tvolumeSpike: boolean;\n\tvolumeTrend: \"increasing\" | \"decreasing\" | \"stable\";\n\n\t// Volatility\n\tatr: number;\n\tvolatilityPercentile: number;\n\n\t// Trend strength\n\tadx: number;\n\ttrendDirection: \"bullish\" | \"bearish\" | \"neutral\";\n\ttrendStrength: number;\n\n\t// Market structure\n\tresistance: number;\n\tsupport: number;\n\tnearResistance: boolean;\n\tnearSupport: boolean;\n}\n\nexport class MomentumBreakoutStrategy implements TradingStrategy {\n\tpublic readonly id = \"momentum-breakout-v1\";\n\tpublic readonly name = \"Momentum Breakout Strategy\";\n\tpublic readonly description =\n\t\t\"Captures momentum moves in volatile meme coins with volume confirmation\";\n\n\t// Strategy parameters - adjusted for more realistic trading\n\tprivate readonly minVolumeRatio = 1.1; // Further reduced\n\tprivate readonly minPriceChange = 0.002; // Reduced to 0.2%\n\tprivate readonly maxRiskPerTrade = 0.02; // Increased to 2% for more trades\n\tprivate readonly profitTarget = 0.01; // Reduced to 1%\n\tprivate readonly stopLoss = 0.005; // Reduced to 0.5%\n\n\t// Position tracking\n\tprivate activePosition: {\n\t\tentryPrice: number;\n\t\tentryTime: number;\n\t\thighestPrice: number;\n\t\tpair: string; // Add pair tracking\n\t} | null = null;\n\n\t// Add debug mode\n\tprivate debugMode = true; // Enable by default for testing\n\tprivate tradeAttempts = 0;\n\tprivate hasLoggedStart = false;\n\n\tisReady(): boolean {\n\t\treturn true;\n\t}\n\n\tprivate inferTradingPair(portfolioSnapshot: PortfolioSnapshot): string {\n\t\t// The SimulationService uses token addresses as keys in holdings\n\t\t// We need to find the non-USDC key which represents the token being traded\n\t\tconst holdings = Object.keys(portfolioSnapshot.holdings);\n\n\t\t// Look for existing position first\n\t\tconst existingPosition = holdings.find(\n\t\t\t(key) => key !== \"USDC\" && portfolioSnapshot.holdings[key] > 0,\n\t\t);\n\n\t\tif (existingPosition) {\n\t\t\treturn existingPosition;\n\t\t}\n\n\t\t// If no position, look for any non-USDC key (even with 0 balance)\n\t\t// This happens when we have historical trades but currently flat\n\t\tconst potentialToken = holdings.find((key) => key !== \"USDC\");\n\n\t\tif (potentialToken) {\n\t\t\treturn potentialToken;\n\t\t}\n\n\t\t// If we only have USDC, we're likely at the start of a simulation\n\t\t// The token address should be inferred from the context\n\t\t// For now, we'll return a placeholder that will be replaced\n\t\tconsole.warn(\n\t\t\t\"[MomentumBreakout] Could not infer trading pair from portfolio\",\n\t\t);\n\t\treturn \"UNKNOWN\";\n\t}\n\n\tasync decide(params: {\n\t\tmarketData: StrategyContextMarketData;\n\t\tagentState: AgentState;\n\t\tportfolioSnapshot: PortfolioSnapshot;\n\t\tagentRuntime?: any;\n\t}): Promise<TradeOrder | null> {\n\t\tconst { marketData, portfolioSnapshot } = params;\n\t\tconst { priceData, currentPrice } = marketData;\n\n\t\t// Log initial info once\n\t\tif (!this.hasLoggedStart && this.debugMode) {\n\t\t\tconsole.log(`[MomentumBreakout] Strategy started:`, {\n\t\t\t\tminPriceChange: `${(this.minPriceChange * 100).toFixed(1)}%`,\n\t\t\t\tminVolumeRatio: this.minVolumeRatio,\n\t\t\t\tdataPoints: priceData?.length || 0,\n\t\t\t\tinitialPrice: currentPrice,\n\t\t\t});\n\t\t\tthis.hasLoggedStart = true;\n\t\t}\n\n\t\t// Need at least 100 candles for analysis\n\t\tif (!priceData || priceData.length < 100) {\n\t\t\tif (this.debugMode && this.tradeAttempts === 0) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`[MomentumBreakout] Not enough data: ${priceData?.length || 0} candles`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t// Infer the trading pair from the portfolio\n\t\tconst tradingPair = this.inferTradingPair(portfolioSnapshot);\n\n\t\t// Calculate momentum indicators\n\t\tconst indicators = this.calculateMomentumIndicators(priceData);\n\n\t\t// Get current position - look for any non-USDC holding\n\t\tconst holdings = Object.entries(portfolioSnapshot.holdings);\n\t\tconst assetHolding = holdings.find(\n\t\t\t([key, value]) => key !== \"USDC\" && value > 0,\n\t\t);\n\t\tconst hasPosition = assetHolding && assetHolding[1] > 0;\n\t\tconst assetSymbol = assetHolding ? assetHolding[0] : null;\n\n\t\t// Position management\n\t\tif (hasPosition && this.activePosition && assetSymbol) {\n\t\t\treturn this.managePosition(\n\t\t\t\tcurrentPrice,\n\t\t\t\tindicators,\n\t\t\t\tassetSymbol,\n\t\t\t\tportfolioSnapshot,\n\t\t\t);\n\t\t}\n\n\t\t// Entry logic - look for momentum breakout\n\t\tconst shouldEnter = this.shouldEnter(indicators, currentPrice);\n\n\t\t// Log first few evaluations for debugging\n\t\tif (this.debugMode && this.tradeAttempts < 5) {\n\t\t\tconst conditions = this.evaluateEntryConditions(indicators);\n\t\t\tconsole.log(\n\t\t\t\t`[MomentumBreakout] Early evaluation #${this.tradeAttempts + 1}:`,\n\t\t\t\t{\n\t\t\t\t\tshouldEnter,\n\t\t\t\t\tprice: currentPrice.toFixed(6),\n\t\t\t\t\tindicators: {\n\t\t\t\t\t\tpriceChange5m: `${(indicators.priceChange5m * 100).toFixed(3)}%`,\n\t\t\t\t\t\tpriceChange15m: `${(indicators.priceChange15m * 100).toFixed(3)}%`,\n\t\t\t\t\t\tvolumeRatio: indicators.volumeRatio.toFixed(2),\n\t\t\t\t\t\ttrend: indicators.trendDirection,\n\t\t\t\t\t\tadx: indicators.adx.toFixed(1),\n\t\t\t\t\t},\n\t\t\t\t\tconditions: {\n\t\t\t\t\t\tmomentum: conditions.hasMomentum,\n\t\t\t\t\t\tvolume: conditions.hasVolume,\n\t\t\t\t\t\ttrend: conditions.trendAligned,\n\t\t\t\t\t\tnotAtResistance: conditions.goodEntry,\n\t\t\t\t\t\tmetCount: `${conditions.metConditions}/4`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tif (!hasPosition && shouldEnter) {\n\t\t\tconst positionSize = this.calculatePositionSize(\n\t\t\t\tportfolioSnapshot.totalValue,\n\t\t\t\tcurrentPrice,\n\t\t\t);\n\n\t\t\tif (positionSize > 0.001) {\n\t\t\t\tthis.activePosition = {\n\t\t\t\t\tentryPrice: currentPrice,\n\t\t\t\t\tentryTime: Date.now(),\n\t\t\t\t\thighestPrice: currentPrice,\n\t\t\t\t\tpair: tradingPair,\n\t\t\t\t};\n\n\t\t\t\tconsole.log(`[MomentumBreakout] 🎯 BUY SIGNAL:`, {\n\t\t\t\t\tprice: currentPrice,\n\t\t\t\t\tpositionSize: positionSize.toFixed(4),\n\t\t\t\t\ttotalValue: portfolioSnapshot.totalValue.toFixed(2),\n\t\t\t\t\tindicators: {\n\t\t\t\t\t\tpriceChange5m: `${(indicators.priceChange5m * 100).toFixed(2)}%`,\n\t\t\t\t\t\tpriceChange15m: `${(indicators.priceChange15m * 100).toFixed(2)}%`,\n\t\t\t\t\t\tvolumeRatio: indicators.volumeRatio.toFixed(2),\n\t\t\t\t\t\ttrend: indicators.trendDirection,\n\t\t\t\t\t\tadx: indicators.adx.toFixed(1),\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\treturn {\n\t\t\t\t\taction: TradeType.BUY,\n\t\t\t\t\tpair: tradingPair,\n\t\t\t\t\tquantity: positionSize,\n\t\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\treason: `Momentum breakout: ${(indicators.priceChange5m * 100).toFixed(1)}% move on ${indicators.volumeRatio.toFixed(1)}x volume`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Log entry evaluation periodically for debugging\n\t\tthis.tradeAttempts++;\n\t\tif (this.debugMode && this.tradeAttempts % 200 === 0) {\n\t\t\tconst conditions = this.evaluateEntryConditions(indicators);\n\t\t\tconsole.log(\n\t\t\t\t`[MomentumBreakout] Periodic evaluation #${this.tradeAttempts}:`,\n\t\t\t\t{\n\t\t\t\t\tshouldEnter: conditions.metConditions >= 2,\n\t\t\t\t\tprice: currentPrice.toFixed(6),\n\t\t\t\t\tconditions: {\n\t\t\t\t\t\tmomentum: conditions.hasMomentum,\n\t\t\t\t\t\tvolume: conditions.hasVolume,\n\t\t\t\t\t\ttrend: conditions.trendAligned,\n\t\t\t\t\t\tnotAtResistance: conditions.goodEntry,\n\t\t\t\t\t\tmetCount: `${conditions.metConditions}/4`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate evaluateEntryConditions(indicators: MomentumIndicators) {\n\t\tconst hasMomentum =\n\t\t\tindicators.priceChange5m > this.minPriceChange &&\n\t\t\tindicators.priceChange15m > -0.01; // Allow more negative 15m\n\n\t\tconst hasVolume =\n\t\t\tindicators.volumeRatio > this.minVolumeRatio ||\n\t\t\t(indicators.volumeRatio > 1.0 && indicators.volumeTrend === \"increasing\");\n\n\t\tconst trendAligned =\n\t\t\t(indicators.trendDirection === \"bullish\" && indicators.adx > 15) || // Reduced ADX requirement\n\t\t\tindicators.priceChange5m > this.minPriceChange * 1.5; // Lower threshold\n\n\t\tconst goodEntry =\n\t\t\t!indicators.nearResistance || indicators.priceChange5m > 0.005; // Allow entry near resistance if strong momentum\n\n\t\tconst conditions = [hasMomentum, hasVolume, trendAligned, goodEntry];\n\t\tconst metConditions = conditions.filter((c) => c).length;\n\n\t\treturn {\n\t\t\thasMomentum,\n\t\t\thasVolume,\n\t\t\ttrendAligned,\n\t\t\tgoodEntry,\n\t\t\tmetConditions,\n\t\t};\n\t}\n\n\tprivate calculateMomentumIndicators(priceData: OHLCV[]): MomentumIndicators {\n\t\tconst currentPrice = priceData[priceData.length - 1].close;\n\n\t\t// Price momentum over different timeframes\n\t\tconst price5mAgo =\n\t\t\tpriceData[Math.max(0, priceData.length - 5)]?.close || currentPrice;\n\t\tconst price15mAgo =\n\t\t\tpriceData[Math.max(0, priceData.length - 15)]?.close || currentPrice;\n\t\tconst price60mAgo =\n\t\t\tpriceData[Math.max(0, priceData.length - 60)]?.close || currentPrice;\n\n\t\tconst priceChange5m = (currentPrice - price5mAgo) / price5mAgo;\n\t\tconst priceChange15m = (currentPrice - price15mAgo) / price15mAgo;\n\t\tconst priceChange1h = (currentPrice - price60mAgo) / price60mAgo;\n\n\t\t// Volume analysis\n\t\tconst recentVolumes = priceData.slice(-20).map((c) => c.volume);\n\t\tconst avgVolume =\n\t\t\trecentVolumes.reduce((a, b) => a + b) / recentVolumes.length;\n\t\tconst currentVolume = priceData[priceData.length - 1].volume;\n\t\tconst volumeRatio = currentVolume / avgVolume;\n\n\t\t// Volume trend\n\t\tconst volumeTrend5 = recentVolumes.slice(-5).reduce((a, b) => a + b) / 5;\n\t\tconst volumeTrend10 = recentVolumes.slice(-10).reduce((a, b) => a + b) / 10;\n\t\tlet volumeTrend: \"increasing\" | \"decreasing\" | \"stable\";\n\n\t\tif (volumeTrend5 > volumeTrend10 * 1.2) {\n\t\t\tvolumeTrend = \"increasing\";\n\t\t} else if (volumeTrend5 < volumeTrend10 * 0.8) {\n\t\t\tvolumeTrend = \"decreasing\";\n\t\t} else {\n\t\t\tvolumeTrend = \"stable\";\n\t\t}\n\n\t\t// ATR for volatility\n\t\tconst atr = this.calculateATR(priceData.slice(-14));\n\t\tconst atrPercentage = atr / currentPrice;\n\n\t\t// ADX for trend strength\n\t\tconst adx = this.calculateADX(priceData.slice(-20));\n\n\t\t// Market structure\n\t\tconst recentHighs = priceData.slice(-20).map((c) => c.high);\n\t\tconst recentLows = priceData.slice(-20).map((c) => c.low);\n\t\tconst resistance = Math.max(...recentHighs);\n\t\tconst support = Math.min(...recentLows);\n\n\t\t// Trend direction\n\t\tconst ema9 = this.calculateEMA(\n\t\t\tpriceData.slice(-20).map((c) => c.close),\n\t\t\t9,\n\t\t);\n\t\tconst ema21 = this.calculateEMA(\n\t\t\tpriceData.slice(-30).map((c) => c.close),\n\t\t\t21,\n\t\t);\n\n\t\tlet trendDirection: \"bullish\" | \"bearish\" | \"neutral\";\n\t\tif (ema9 > ema21 * 1.01 && priceChange15m > 0) {\n\t\t\ttrendDirection = \"bullish\";\n\t\t} else if (ema9 < ema21 * 0.99 && priceChange15m < 0) {\n\t\t\ttrendDirection = \"bearish\";\n\t\t} else {\n\t\t\ttrendDirection = \"neutral\";\n\t\t}\n\n\t\treturn {\n\t\t\tpriceChange1h,\n\t\t\tpriceChange5m,\n\t\t\tpriceChange15m,\n\t\t\tvolumeRatio,\n\t\t\tvolumeSpike: volumeRatio > 3,\n\t\t\tvolumeTrend,\n\t\t\tatr,\n\t\t\tvolatilityPercentile: atrPercentage > 0.02 ? 0.8 : 0.5,\n\t\t\tadx,\n\t\t\ttrendDirection,\n\t\t\ttrendStrength: (Math.abs(priceChange15m) * adx) / 25,\n\t\t\tresistance,\n\t\t\tsupport,\n\t\t\tnearResistance: (resistance - currentPrice) / currentPrice < 0.01,\n\t\t\tnearSupport: (currentPrice - support) / currentPrice < 0.01,\n\t\t};\n\t}\n\n\tprivate shouldEnter(\n\t\tindicators: MomentumIndicators,\n\t\t_currentPrice: number,\n\t): boolean {\n\t\tconst conditions = this.evaluateEntryConditions(indicators);\n\t\treturn conditions.metConditions >= 2; // Reduced from 3 to 2 out of 4\n\t}\n\n\tprivate managePosition(\n\t\tcurrentPrice: number,\n\t\tindicators: MomentumIndicators,\n\t\tassetSymbol: string,\n\t\tportfolio: PortfolioSnapshot,\n\t): TradeOrder | null {\n\t\tif (!this.activePosition) return null;\n\n\t\tconst { entryPrice, highestPrice } = this.activePosition;\n\t\tconst profitPercent = (currentPrice - entryPrice) / entryPrice;\n\t\tconst drawdownFromHigh = (highestPrice - currentPrice) / highestPrice;\n\n\t\t// Update highest price\n\t\tif (currentPrice > highestPrice) {\n\t\t\tthis.activePosition.highestPrice = currentPrice;\n\t\t}\n\n\t\t// Exit conditions\n\t\tlet shouldExit = false;\n\t\tlet exitReason = \"\";\n\n\t\t// 1. Hit profit target\n\t\tif (profitPercent >= this.profitTarget) {\n\t\t\tshouldExit = true;\n\t\t\texitReason = `Profit target reached: +${(profitPercent * 100).toFixed(1)}%`;\n\t\t}\n\n\t\t// 2. Stop loss\n\t\telse if (profitPercent <= -this.stopLoss) {\n\t\t\tshouldExit = true;\n\t\t\texitReason = `Stop loss triggered: ${(profitPercent * 100).toFixed(1)}%`;\n\t\t}\n\n\t\t// 3. Trailing stop (if profit > 1.5%)\n\t\telse if (profitPercent > 0.015 && drawdownFromHigh > 0.01) {\n\t\t\tshouldExit = true;\n\t\t\texitReason = `Trailing stop: -${(drawdownFromHigh * 100).toFixed(1)}% from high`;\n\t\t}\n\n\t\t// 4. Momentum reversal\n\t\telse if (indicators.priceChange5m < -0.01 && indicators.volumeRatio > 2) {\n\t\t\tshouldExit = true;\n\t\t\texitReason = \"Momentum reversal detected\";\n\t\t}\n\n\t\tif (shouldExit) {\n\t\t\tthis.activePosition = null;\n\n\t\t\treturn {\n\t\t\t\taction: TradeType.SELL,\n\t\t\t\tpair: `${assetSymbol}/USDC`,\n\t\t\t\tquantity: portfolio.holdings[assetSymbol],\n\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t\treason: exitReason,\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate calculatePositionSize(\n\t\tportfolioValue: number,\n\t\tcurrentPrice: number,\n\t): number {\n\t\t// Risk-based position sizing\n\t\t// We want to risk maxRiskPerTrade of our portfolio\n\t\t// If we hit our stop loss, we should lose exactly that amount\n\n\t\t// Calculate the position value that would result in our max risk if stopped out\n\t\tconst riskAmount = portfolioValue * this.maxRiskPerTrade;\n\t\tconst positionValue = riskAmount / this.stopLoss;\n\n\t\t// But we can't use more than a reasonable portion of our portfolio\n\t\t// Use the lesser of our risk-based size or 25% of portfolio\n\t\tconst maxPositionValue = portfolioValue * 0.25;\n\t\tconst actualPositionValue = Math.min(positionValue, maxPositionValue);\n\n\t\tconst quantity = actualPositionValue / currentPrice;\n\n\t\tif (this.debugMode) {\n\t\t\tconsole.log(`[MomentumBreakout] Position sizing:`, {\n\t\t\t\tportfolioValue: portfolioValue.toFixed(2),\n\t\t\t\triskAmount: riskAmount.toFixed(2),\n\t\t\t\tcalculatedPositionValue: positionValue.toFixed(2),\n\t\t\t\tactualPositionValue: actualPositionValue.toFixed(2),\n\t\t\t\tcurrentPrice: currentPrice.toFixed(6),\n\t\t\t\tquantity: quantity.toFixed(4),\n\t\t\t});\n\t\t}\n\n\t\treturn quantity;\n\t}\n\n\tprivate calculateATR(candles: OHLCV[]): number {\n\t\tif (candles.length < 2) return 0;\n\n\t\tconst trueRanges = [];\n\t\tfor (let i = 1; i < candles.length; i++) {\n\t\t\tconst highLow = candles[i].high - candles[i].low;\n\t\t\tconst highClose = Math.abs(candles[i].high - candles[i - 1].close);\n\t\t\tconst lowClose = Math.abs(candles[i].low - candles[i - 1].close);\n\t\t\ttrueRanges.push(Math.max(highLow, highClose, lowClose));\n\t\t}\n\n\t\treturn trueRanges.reduce((a, b) => a + b) / trueRanges.length;\n\t}\n\n\tprivate calculateEMA(values: number[], period: number): number {\n\t\tif (values.length < period) return values[values.length - 1];\n\n\t\tconst multiplier = 2 / (period + 1);\n\t\tlet ema = values.slice(0, period).reduce((a, b) => a + b) / period;\n\n\t\tfor (let i = period; i < values.length; i++) {\n\t\t\tema = (values[i] - ema) * multiplier + ema;\n\t\t}\n\n\t\treturn ema;\n\t}\n\n\tprivate calculateADX(candles: OHLCV[], period: number = 14): number {\n\t\tif (candles.length < period + 1) return 0;\n\n\t\t// Simplified ADX calculation\n\t\tconst priceChanges = [];\n\t\tfor (let i = 1; i < candles.length; i++) {\n\t\t\tpriceChanges.push(\n\t\t\t\tMath.abs(candles[i].close - candles[i - 1].close) /\n\t\t\t\t\tcandles[i - 1].close,\n\t\t\t);\n\t\t}\n\n\t\tconst avgChange =\n\t\t\tpriceChanges.reduce((a, b) => a + b) / priceChanges.length;\n\t\treturn Math.min(avgChange * 1000, 100); // Scale to 0-100\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -15,4 +15,4 @@ export {
15
15
  TradeType,
16
16
  OrderType
17
17
  };
18
- //# sourceMappingURL=chunk-AS6N6A3H.js.map
18
+ //# sourceMappingURL=chunk-DVZ7FRKE.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\tmint: string;\n\tbalance: string;\n\tdecimals: number;\n\tuiAmount: number;\n}\n\nexport interface TokenData {\n\taddress: string;\n\tsymbol: string;\n\tname: string;\n\tdecimals: number;\n\tprice?: number;\n\tvolume24h?: number;\n\tliquidity?: number;\n}\n\nexport interface WalletAsset {\n\tmint: string;\n\tbalance: number;\n\tdecimals: number;\n\tuiAmount: number;\n}\n\nexport interface IWalletService {\n\tgetBalance(): Promise<number>;\n\tgetTokenBalances(): 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\ttimestamp: number;\n\topen: number;\n\thigh: number;\n\tlow: number;\n\tclose: number;\n\tvolume: number;\n}\n\nexport interface StrategyContextMarketData {\n\tcurrentPrice: number;\n\tlastPrices: number[];\n\tindicators?: { [indicatorName: string]: number };\n\tpriceData?: OHLCV[]; // Full price data for custom analysis\n}\n\nexport interface AgentState {\n\tportfolioValue: number;\n\tvolatility: number;\n\tconfidenceLevel: number;\n\trecentTrades: number;\n\tlastAction?: string;\n\tsentiment?: number;\n}\n\nexport interface TradingStrategy {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\tdecide(params: {\n\t\tmarketData: StrategyContextMarketData;\n\t\tagentState: AgentState;\n\t\tportfolioSnapshot: PortfolioSnapshot;\n\t\tagentRuntime?: AgentRuntime;\n\t}): Promise<TradeOrder | null>;\n\tinitialize?(agentRuntime?: AgentRuntime): Promise<void>;\n\tisReady(): boolean;\n\tconfigure?(params: any): void;\n}\n\nexport enum TradeType {\n\tBUY = \"BUY\",\n\tSELL = \"SELL\",\n}\n\nexport enum OrderType {\n\tMARKET = \"MARKET\",\n\tLIMIT = \"LIMIT\",\n\tSTOP = \"STOP\",\n}\n\nexport interface TradeOrder {\n\tpair: string;\n\taction: TradeType;\n\tquantity: number;\n\torderType: OrderType;\n\tprice?: number;\n\tstopPrice?: number;\n\ttimestamp: number;\n\treason?: string;\n}\n\nexport interface Trade extends TradeOrder {\n\texecutedPrice: number;\n\texecutedTimestamp: number;\n\tfees: number;\n\tfeeCurrency?: string; // Add optional feeCurrency\n\ttradeId?: string; // Add optional tradeId\n\trealizedPnl?: number; // Profit or loss realized on this trade (typically for SELL trades)\n}\n\nexport interface PortfolioSnapshot {\n\ttimestamp: number;\n\tholdings: { [assetSymbol: string]: number };\n\ttotalValue: number;\n}\n\nexport interface PerformanceMetrics {\n\ttotalPnlAbsolute: number;\n\ttotalPnlPercentage: number;\n\tsharpeRatio?: number;\n\tsortinoRatio?: number;\n\twinLossRatio: number;\n\taverageWinAmount: number;\n\taverageLossAmount: number;\n\tmaxDrawdown: number;\n\ttotalTrades: number;\n\twinningTrades: number;\n\tlosingTrades: number;\n\tfirstAssetPrice?: number;\n\tlastAssetPrice?: number;\n\tbuyAndHoldPnlPercentage?: number;\n}\n\nexport interface SimulationReport {\n\tstrategy: string;\n\tpair: string;\n\tstartDate: number;\n\tendDate: number;\n\ttrades: Trade[];\n\tportfolioSnapshots: PortfolioSnapshot[];\n\tfinalPortfolioValue: number;\n\tmetrics: PerformanceMetrics;\n}\n\n// HistoricalDataService\nexport interface HistoricalDataService {\n\tfetchData(\n\t\tpair: string,\n\t\tinterval: string,\n\t\tstartDate: Date,\n\t\tendDate: Date,\n\t\tdataSource: string,\n\t): Promise<OHLCV[]>;\n}\n\n// #endregion --- Portfolio and Trading Data Interfaces ---\n\n// #region --- Service & Other Interfaces ---\n\nexport interface TradeSimulationResult {\n\tisValid: boolean;\n\treason?: string;\n\tupdatedBalance?: number;\n\tupdatedPortfolio?: { [assetSymbol: string]: PortfolioAssetHolding };\n}\n\n// Wallet portfolio for auto-trader\nexport interface WalletPortfolio {\n\ttotalUsd: string;\n\ttotalSol?: string;\n\titems: Array<{\n\t\tname: string;\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tdecimals: number;\n\t\tbalance: string;\n\t\tuiAmount: string;\n\t\tpriceUsd: string;\n\t\tvalueUsd: string;\n\t\tvalueSol?: string;\n\t}>;\n}\n\nexport interface PortfolioAssetHolding {\n\tmint: string;\n\tbalance: number;\n\tdecimals: number;\n\tuiAmount: number;\n\taveragePrice: number;\n\tsymbol?: string;\n\tassetAddress: string;\n}\n\nexport interface Position {\n\tid: UUID;\n\ttokenAddress: string;\n\tamount: number;\n\tentryPrice: number;\n\tcurrentPrice?: number;\n\tunrealizedPnl?: number;\n\trealizedPnl?: number;\n}\n\nexport interface WalletOperationResult {\n\tsuccess: boolean;\n\ttransactionHash?: string;\n\terror?: string;\n}\n\nexport interface PortfolioHolding {\n\ttokenAddress: string;\n\ttokenSymbol: string;\n\ttokenName: string;\n\tamount: number;\n\tdecimals: number;\n\tusdValue: number;\n\ttokenPriceUsd: number;\n}\n\nexport interface TrackedPosition {\n\ttokenAddress: string;\n\tsymbol: string;\n\tentryPrice: number;\n\tcurrentPrice: number;\n\tquantity: number;\n\tvalue: number;\n\tpnl: number;\n\tpnlPercentage: number;\n}\n\n// Strategy-specific parameters\nexport interface RandomStrategyParams {\n\tbuyProbability?: number;\n\tsellProbability?: number;\n\tmaxTradeSize?: number;\n\tminTradeSize?: number;\n\trandomSeed?: number;\n}\n\nexport interface RuleBasedStrategyParams {\n\tindicators?: string[];\n\tbuyConditions?: {\n\t\t[indicator: string]: { threshold: number; condition: \"above\" | \"below\" };\n\t};\n\tsellConditions?: {\n\t\t[indicator: string]: { threshold: number; condition: \"above\" | \"below\" };\n\t};\n\triskSettings?: {\n\t\tmaxPositionSize?: number;\n\t\tstopLossPercentage?: number;\n\t\ttakeProfitPercentage?: number;\n\t};\n}\n\nexport interface LLMStrategyParams {\n\tmodelName?: string;\n\tcustomPromptPrefix?: string;\n\tcustomPromptSuffix?: string;\n\tmaxTokens?: number;\n\ttemperature?: number;\n\tdefaultTradeSizePercentage?: number;\n\tdefaultFixedTradeQuantity?: number;\n\tstructuredOutputSchema?: any;\n\tsystemPrompt?: string;\n}\n\n// #endregion --- Service & Other Interfaces ---\n"],"mappings":";AA+EO,IAAK,YAAL,kBAAKA,eAAL;AACN,EAAAA,WAAA,SAAM;AACN,EAAAA,WAAA,UAAO;AAFI,SAAAA;AAAA,GAAA;AAKL,IAAK,YAAL,kBAAKC,eAAL;AACN,EAAAA,WAAA,YAAS;AACT,EAAAA,WAAA,WAAQ;AACR,EAAAA,WAAA,UAAO;AAHI,SAAAA;AAAA,GAAA;","names":["TradeType","OrderType"]}
@@ -97,4 +97,4 @@ var RandomStrategy = class {
97
97
  export {
98
98
  RandomStrategy
99
99
  };
100
- //# sourceMappingURL=chunk-KB2EBQUN.js.map
100
+ //# sourceMappingURL=chunk-HTFXFKAF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/RandomStrategy.ts"],"sourcesContent":["import {\n\ttype AgentState,\n\tOrderType,\n\ttype PortfolioSnapshot,\n\ttype StrategyContextMarketData,\n\ttype TradeOrder,\n\tTradeType,\n\ttype TradingStrategy,\n} from \"../types.ts\";\n\nexport interface RandomStrategyParams {\n\t/**\n\t * Probability (0.0 to 1.0) of attempting a trade at any given `shouldExecute` call.\n\t * Default: 0.1 (10% chance)\n\t */\n\ttradeAttemptProbability?: number;\n\t/**\n\t * Probability (0.0 to 1.0) of the attempted trade being a BUY order. SELL is 1 - this value.\n\t * Default: 0.5 (50% chance of BUY, 50% chance of SELL)\n\t */\n\tbuyProbability?: number;\n\t/**\n\t * Maximum percentage of available capital to use for a single trade.\n\t * Expressed as a decimal (e.g., 0.05 for 5%).\n\t * Default: 0.01 (1% of available capital)\n\t * Requires `agentState.availableCapital` to be set.\n\t */\n\tmaxTradeSizePercentage?: number;\n\t/**\n\t * Fixed quantity to trade if `maxTradeSizePercentage` is not used or capital is not available.\n\t * If this is set, `maxTradeSizePercentage` might be ignored or used as a cap.\n\t * Default: 1 (e.g., 1 unit of the base asset)\n\t */\n\tfixedTradeQuantity?: number;\n}\n\nconst DEFAULT_TRADE_ATTEMPT_PROBABILITY = 0.1;\nconst DEFAULT_BUY_PROBABILITY = 0.5;\nconst DEFAULT_MAX_TRADE_SIZE_PERCENTAGE = 0.01; // 1%\nconst DEFAULT_FIXED_TRADE_QUANTITY = 1;\nconst MIN_TRADE_QUANTITY_THRESHOLD = 1e-8; // Define a threshold for minimum tradeable quantity\n\nexport class RandomStrategy implements TradingStrategy {\n\tpublic readonly id = \"random-v1\";\n\tpublic readonly name = \"Random Trading Strategy\";\n\tpublic readonly description =\n\t\t\"Makes random buy or sell decisions based on configured probabilities.\";\n\n\tprivate params: RandomStrategyParams = {\n\t\ttradeAttemptProbability: DEFAULT_TRADE_ATTEMPT_PROBABILITY,\n\t\tbuyProbability: DEFAULT_BUY_PROBABILITY,\n\t\tmaxTradeSizePercentage: DEFAULT_MAX_TRADE_SIZE_PERCENTAGE,\n\t\tfixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY,\n\t};\n\n\tprivate useFixedQuantity = false;\n\n\tconfigure(params: RandomStrategyParams): void {\n\t\tif (params.tradeAttemptProbability !== undefined) {\n\t\t\tif (\n\t\t\t\tparams.tradeAttemptProbability < 0 ||\n\t\t\t\tparams.tradeAttemptProbability > 1\n\t\t\t) {\n\t\t\t\tthrow new Error(\"tradeAttemptProbability must be between 0 and 1.\");\n\t\t\t}\n\t\t\tthis.params.tradeAttemptProbability = params.tradeAttemptProbability;\n\t\t}\n\t\tif (params.buyProbability !== undefined) {\n\t\t\tif (params.buyProbability < 0 || params.buyProbability > 1) {\n\t\t\t\tthrow new Error(\"buyProbability must be between 0 and 1.\");\n\t\t\t}\n\t\t\tthis.params.buyProbability = params.buyProbability;\n\t\t}\n\t\tif (params.maxTradeSizePercentage !== undefined) {\n\t\t\tif (\n\t\t\t\tparams.maxTradeSizePercentage < 0 ||\n\t\t\t\tparams.maxTradeSizePercentage > 1\n\t\t\t) {\n\t\t\t\tthrow new Error(\"maxTradeSizePercentage must be between 0 and 1.\");\n\t\t\t}\n\t\t\tthis.params.maxTradeSizePercentage = params.maxTradeSizePercentage;\n\t\t\t// If percentage is explicitly set, prefer it over fixed quantity\n\t\t\tthis.useFixedQuantity = false;\n\t\t}\n\t\tif (params.fixedTradeQuantity !== undefined) {\n\t\t\tif (params.fixedTradeQuantity <= 0) {\n\t\t\t\tthrow new Error(\"fixedTradeQuantity must be positive.\");\n\t\t\t}\n\t\t\tthis.params.fixedTradeQuantity = params.fixedTradeQuantity;\n\t\t\t// Only use fixed quantity if percentage is not explicitly set in this configure call\n\t\t\tif (params.maxTradeSizePercentage === undefined) {\n\t\t\t\tthis.useFixedQuantity = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tisReady(): boolean {\n\t\treturn true; // Random strategy is always ready\n\t}\n\n\tasync decide(params: {\n\t\tmarketData: StrategyContextMarketData;\n\t\tagentState: AgentState;\n\t\tportfolioSnapshot: PortfolioSnapshot;\n\t\tagentRuntime?: any;\n\t}): Promise<TradeOrder | null> {\n\t\tconst { marketData, portfolioSnapshot } = params;\n\n\t\tif (\n\t\t\tMath.random() >=\n\t\t\t(this.params.tradeAttemptProbability ?? DEFAULT_TRADE_ATTEMPT_PROBABILITY)\n\t\t) {\n\t\t\treturn null; // No trade attempt this time\n\t\t}\n\n\t\tconst tradeType: TradeType =\n\t\t\tMath.random() < (this.params.buyProbability ?? DEFAULT_BUY_PROBABILITY)\n\t\t\t\t? TradeType.BUY\n\t\t\t\t: TradeType.SELL;\n\n\t\t// Calculate quantity\n\t\tlet quantity: number | undefined;\n\t\tif (\n\t\t\tthis.useFixedQuantity &&\n\t\t\tthis.params.fixedTradeQuantity &&\n\t\t\tthis.params.fixedTradeQuantity > 0\n\t\t) {\n\t\t\t// Use fixed quantity when explicitly set\n\t\t\tquantity = this.params.fixedTradeQuantity;\n\t\t} else if (\n\t\t\t!this.useFixedQuantity &&\n\t\t\tthis.params.maxTradeSizePercentage &&\n\t\t\tportfolioSnapshot.totalValue > 0 &&\n\t\t\tmarketData.currentPrice &&\n\t\t\tmarketData.currentPrice > 0\n\t\t) {\n\t\t\t// Calculate percentage-based quantity\n\t\t\tconst tradeValue =\n\t\t\t\tportfolioSnapshot.totalValue * this.params.maxTradeSizePercentage;\n\t\t\tquantity = tradeValue / marketData.currentPrice;\n\t\t} else {\n\t\t\t// Default: 1% of portfolio or minimal amount\n\t\t\tconst defaultPercentage = 0.01;\n\t\t\tconst tradeValue = portfolioSnapshot.totalValue * defaultPercentage;\n\t\t\tquantity =\n\t\t\t\tmarketData.currentPrice > 0\n\t\t\t\t\t? tradeValue / marketData.currentPrice\n\t\t\t\t\t: 0.01;\n\t\t}\n\n\t\t// Check minimum quantity threshold\n\t\tif (!quantity || quantity <= MIN_TRADE_QUANTITY_THRESHOLD) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Extract asset from portfolio snapshot or use a default\n\t\tconst assetSymbol =\n\t\t\tObject.keys(portfolioSnapshot.holdings).find(\n\t\t\t\t(key) => key !== \"USDC\" && portfolioSnapshot.holdings[key] > 0,\n\t\t\t) || \"SOL\";\n\n\t\tconst pair = `${assetSymbol}/USDC`;\n\n\t\t// For SELL orders, check if we have sufficient holdings\n\t\tif (tradeType === TradeType.SELL) {\n\t\t\tconst holding = portfolioSnapshot.holdings[assetSymbol] || 0;\n\t\t\tif (holding < quantity) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Apply precision limit\n\t\tconst roundedQuantity = parseFloat(quantity.toFixed(8));\n\n\t\t// Check again after rounding\n\t\tif (roundedQuantity <= MIN_TRADE_QUANTITY_THRESHOLD) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn {\n\t\t\tpair,\n\t\t\taction: tradeType,\n\t\t\tquantity: roundedQuantity,\n\t\t\torderType: OrderType.MARKET, // Random strategy uses market orders for simplicity\n\t\t\ttimestamp: Date.now(),\n\t\t\treason: \"Random trading decision\",\n\t\t};\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -54,10 +54,14 @@ var RuleBasedStrategy = class {
54
54
  throw new Error("Long MA period must be positive.");
55
55
  if (rule.type === "SMA_CROSSOVER" || rule.type === "EMA_CROSSOVER") {
56
56
  if (!rule.shortMAPeriod || !rule.longMAPeriod) {
57
- throw new Error("Short and Long MA periods are required for crossover rules.");
57
+ throw new Error(
58
+ "Short and Long MA periods are required for crossover rules."
59
+ );
58
60
  }
59
61
  if (rule.shortMAPeriod >= rule.longMAPeriod) {
60
- throw new Error("Short MA period must be less than Long MA period for crossovers.");
62
+ throw new Error(
63
+ "Short MA period must be less than Long MA period for crossovers."
64
+ );
61
65
  }
62
66
  if (!rule.maType) {
63
67
  console.warn(
@@ -74,7 +78,9 @@ var RuleBasedStrategy = class {
74
78
  throw new Error("RSI oversold must be less than RSI overbought");
75
79
  });
76
80
  if (params.tradeSizePercentage !== void 0 && (params.tradeSizePercentage <= 0 || params.tradeSizePercentage > 1)) {
77
- throw new Error("tradeSizePercentage must be between 0 (exclusive) and 1 (inclusive).");
81
+ throw new Error(
82
+ "tradeSizePercentage must be between 0 (exclusive) and 1 (inclusive)."
83
+ );
78
84
  }
79
85
  if (params.fixedTradeQuantity !== void 0 && params.fixedTradeQuantity <= 0) {
80
86
  throw new Error("fixedTradeQuantity must be positive.");
@@ -126,7 +132,11 @@ var RuleBasedStrategy = class {
126
132
  }
127
133
  calculateIndicators(ohlcvData, rules) {
128
134
  const minDataPointsForAnyCalc = rules.reduce((minOverall, r) => {
129
- const periodReq = Math.max(r.longMAPeriod || 0, r.rsiPeriod || 0, r.macdSlowPeriod || 0);
135
+ const periodReq = Math.max(
136
+ r.longMAPeriod || 0,
137
+ r.rsiPeriod || 0,
138
+ r.macdSlowPeriod || 0
139
+ );
130
140
  return Math.min(minOverall, periodReq > 0 ? periodReq + 1 : Infinity);
131
141
  }, Infinity);
132
142
  if (ohlcvData.length < (this.params.minIndicatorDataPoints ?? DEFAULT_MIN_INDICATOR_DATA_POINTS) || ohlcvData.length < minDataPointsForAnyCalc) {
@@ -195,7 +205,10 @@ var RuleBasedStrategy = class {
195
205
  if (!marketData.priceData || marketData.priceData.length < 20) {
196
206
  return null;
197
207
  }
198
- const indicators = this.calculateIndicators(marketData.priceData, this.params.rules);
208
+ const indicators = this.calculateIndicators(
209
+ marketData.priceData,
210
+ this.params.rules
211
+ );
199
212
  let shouldBuy = false;
200
213
  let shouldSell = false;
201
214
  let buyReason = "";
@@ -225,7 +238,10 @@ var RuleBasedStrategy = class {
225
238
  if (shouldBuy) {
226
239
  const cashHolding = portfolioSnapshot.holdings.USDC || 0;
227
240
  if (cashHolding > 10 && marketData.currentPrice) {
228
- const quantity = this.calculateTradeQuantity(marketData, portfolioSnapshot);
241
+ const quantity = this.calculateTradeQuantity(
242
+ marketData,
243
+ portfolioSnapshot
244
+ );
229
245
  if (quantity && quantity > MIN_TRADE_QUANTITY_THRESHOLD) {
230
246
  return {
231
247
  pair,
@@ -273,4 +289,4 @@ var RuleBasedStrategy = class {
273
289
  export {
274
290
  RuleBasedStrategy
275
291
  };
276
- //# sourceMappingURL=chunk-GYTZTIWK.js.map
292
+ //# sourceMappingURL=chunk-HXJTH4SS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/RuleBasedStrategy.ts"],"sourcesContent":["import * as ti from \"technicalindicators\";\nimport {\n\ttype AgentState,\n\ttype OHLCV,\n\tOrderType,\n\ttype PortfolioSnapshot,\n\ttype StrategyContextMarketData,\n\ttype TradeOrder,\n\tTradeType,\n\ttype TradingStrategy,\n} from \"../types.ts\";\n\n// Mock/placeholder for technical indicator calculation results\ninterface TAResults {\n\trsi?: number;\n\tsmaShort?: number;\n\tsmaLong?: number;\n\tprevSmaShort?: number;\n\tprevSmaLong?: number;\n\temaShort?: number;\n\temaLong?: number;\n\tprevEmaShort?: number;\n\tprevEmaLong?: number;\n\tmacd?: {\n\t\tMACD?: number;\n\t\tsignal?: number;\n\t\thistogram?: number;\n\t};\n\tprevMacd?: {\n\t\tMACD?: number;\n\t\tsignal?: number;\n\t\thistogram?: number;\n\t};\n\t// ema?: number[]; // Keep as array if EMA logic will also be array-based initially\n\t// macd?: { macd: number[]; signal: number[]; histogram: number[] };\n\t// Add other indicators as needed\n}\n\n// Assume a technicalindicators-like library structure\n// This is a conceptual placeholder for actual library usage.\n// REMOVED - no longer needed\n// interface TechnicalIndicatorsLib { ... }\n// const tiLibrary: TechnicalIndicatorsLib | null = null;\n\nexport interface RuleCondition {\n\ttype:\n\t\t| \"RSI\"\n\t\t| \"SMA_CROSSOVER\"\n\t\t| \"EMA_CROSSOVER\"\n\t\t| \"VOLUME\"\n\t\t| \"PRICE_ACTION\"\n\t\t| \"MACD_CROSS\";\n\t// RSI\n\trsiPeriod?: number;\n\trsiOverbought?: number; // e.g., 70 (for sell)\n\trsiOversold?: number; // e.g., 30 (for buy)\n\t// SMA/EMA\n\tshortMAPeriod?: number;\n\tlongMAPeriod?: number;\n\tmaType?: \"SMA\" | \"EMA\"; // Specify MA type for crossover rules\n\t// VOLUME\n\tminVolume24h?: number;\n\t// PRICE_ACTION\n\tpriceBreaksNDayHigh?: number; // N days for high (e.g., 7-day high)\n\tpriceBreaksNDayLow?: number; // N days for low\n\t// MACD\n\tmacdFastPeriod?: number;\n\tmacdSlowPeriod?: number;\n\tmacdSignalPeriod?: number;\n\n\taction: TradeType; // BUY or SELL if this condition is met\n}\n\nexport interface StopLossTakeProfitConfig {\n\tstopLossPercentage?: number; // e.g., 0.05 for 5%\n\ttakeProfitPercentage?: number; // e.g., 0.10 for 10%\n}\n\nexport interface RuleBasedStrategyParams {\n\trules: RuleCondition[];\n\tstopLossTakeProfit?: StopLossTakeProfitConfig;\n\t/** Percentage of available capital for a trade, or fixed quantity if capital/price info is missing */\n\ttradeSizePercentage?: number;\n\tfixedTradeQuantity?: number;\n\t/** Minimum number of data points required for indicators to be considered valid */\n\tminIndicatorDataPoints?: number;\n\tindicators?: string[];\n\tbuyConditions?: Record<string, { threshold: number; condition: string }>;\n\tsellConditions?: Record<string, { threshold: number; condition: string }>;\n\triskSettings?: {\n\t\tmaxPositionSize?: number;\n\t\tstopLossPercentage?: number;\n\t\ttakeProfitPercentage?: number;\n\t};\n}\n\nconst DEFAULT_TRADE_SIZE_PERCENTAGE = 0.01; // 1%\nconst DEFAULT_FIXED_TRADE_QUANTITY = 1;\nconst DEFAULT_MIN_INDICATOR_DATA_POINTS = 20;\nconst MIN_TRADE_QUANTITY_THRESHOLD = 1e-8; // Added threshold\n\nexport class RuleBasedStrategy implements TradingStrategy {\n\tpublic readonly id = \"rule-based-v1\";\n\tpublic readonly name = \"Rule-Based Trading Strategy\";\n\tpublic readonly description =\n\t\t\"Makes trading decisions based on technical indicators and thresholds.\";\n\n\tprivate params: RuleBasedStrategyParams = {\n\t\trules: [],\n\t\ttradeSizePercentage: DEFAULT_TRADE_SIZE_PERCENTAGE,\n\t\tfixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY,\n\t\tminIndicatorDataPoints: DEFAULT_MIN_INDICATOR_DATA_POINTS,\n\t\tindicators: [\"sma\", \"ema\", \"rsi\", \"macd\"],\n\t\tbuyConditions: {\n\t\t\trsi: { threshold: 30, condition: \"below\" },\n\t\t\tmacd: { threshold: 0, condition: \"above\" },\n\t\t},\n\t\tsellConditions: {\n\t\t\trsi: { threshold: 70, condition: \"above\" },\n\t\t\tmacd: { threshold: 0, condition: \"below\" },\n\t\t},\n\t\triskSettings: {\n\t\t\tmaxPositionSize: 0.05,\n\t\t\tstopLossPercentage: 0.02,\n\t\t\ttakeProfitPercentage: 0.05,\n\t\t},\n\t};\n\n\t// The actual TI library is now used directly.\n\tprivate indicators: typeof ti = ti;\n\n\tisReady(): boolean {\n\t\treturn true; // Rule-based strategy is always ready\n\t}\n\n\tconfigure(params: RuleBasedStrategyParams): void {\n\t\tconsole.error(\n\t\t\t\"[RuleBasedStrategy DIAG] configure called. Incoming params:\",\n\t\t\tJSON.stringify(params),\n\t\t);\n\t\tconsole.error(\n\t\t\t\"[RuleBasedStrategy DIAG] configure: this.params BEFORE merge:\",\n\t\t\tJSON.stringify(this.params),\n\t\t);\n\t\tif (!params.rules || params.rules.length === 0) {\n\t\t\tthrow new Error(\"At least one rule must be configured.\");\n\t\t}\n\n\t\t// Validate individual rule parameters first\n\t\tparams.rules.forEach((rule) => {\n\t\t\tif (rule.rsiPeriod !== undefined && rule.rsiPeriod <= 0)\n\t\t\t\tthrow new Error(\"RSI period must be positive.\");\n\t\t\tif (rule.shortMAPeriod !== undefined && rule.shortMAPeriod <= 0)\n\t\t\t\tthrow new Error(\"Short MA period must be positive.\");\n\t\t\tif (rule.longMAPeriod !== undefined && rule.longMAPeriod <= 0)\n\t\t\t\tthrow new Error(\"Long MA period must be positive.\");\n\t\t\tif (rule.type === \"SMA_CROSSOVER\" || rule.type === \"EMA_CROSSOVER\") {\n\t\t\t\tif (!rule.shortMAPeriod || !rule.longMAPeriod) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Short and Long MA periods are required for crossover rules.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (rule.shortMAPeriod >= rule.longMAPeriod) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"Short MA period must be less than Long MA period for crossovers.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (!rule.maType) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t`[RuleBasedStrategy] maType not specified for crossover rule, defaulting to SMA.`,\n\t\t\t\t\t);\n\t\t\t\t\trule.maType = \"SMA\";\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (\n\t\t\t\trule.rsiOverbought !== undefined &&\n\t\t\t\t(rule.rsiOverbought <= 0 || rule.rsiOverbought > 100)\n\t\t\t)\n\t\t\t\tthrow new Error(\"RSI overbought must be between 0 and 100\");\n\t\t\tif (\n\t\t\t\trule.rsiOversold !== undefined &&\n\t\t\t\t(rule.rsiOversold <= 0 || rule.rsiOversold > 100)\n\t\t\t)\n\t\t\t\tthrow new Error(\"RSI oversold must be between 0 and 100\");\n\t\t\tif (\n\t\t\t\trule.rsiOversold &&\n\t\t\t\trule.rsiOverbought &&\n\t\t\t\trule.rsiOversold >= rule.rsiOverbought\n\t\t\t)\n\t\t\t\tthrow new Error(\"RSI oversold must be less than RSI overbought\");\n\t\t});\n\n\t\t// Validate overall strategy parameters from input params\n\t\tif (\n\t\t\tparams.tradeSizePercentage !== undefined &&\n\t\t\t(params.tradeSizePercentage <= 0 || params.tradeSizePercentage > 1)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t\"tradeSizePercentage must be between 0 (exclusive) and 1 (inclusive).\",\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\tparams.fixedTradeQuantity !== undefined &&\n\t\t\tparams.fixedTradeQuantity <= 0\n\t\t) {\n\t\t\tthrow new Error(\"fixedTradeQuantity must be positive.\");\n\t\t}\n\t\t// Validate minIndicatorDataPoints from input params before merging params\n\t\tif (\n\t\t\tparams.minIndicatorDataPoints !== undefined &&\n\t\t\tparams.minIndicatorDataPoints < 1\n\t\t) {\n\t\t\tthrow new Error(\"minIndicatorDataPoints must be at least 1.\");\n\t\t}\n\n\t\tconst tempParams = { ...this.params, ...params }; // Merge incoming params over current defaults/settings\n\n\t\tconst currentRulesLongestPeriod = tempParams.rules.reduce(\n\t\t\t(max, r) => Math.max(max, r.longMAPeriod || 0, r.rsiPeriod || 0),\n\t\t\t0,\n\t\t);\n\t\tconst pointsNeededByCurrentRules =\n\t\t\tcurrentRulesLongestPeriod > 0 ? currentRulesLongestPeriod + 1 : 1;\n\n\t\tif (params.minIndicatorDataPoints !== undefined) {\n\t\t\tif (params.minIndicatorDataPoints < pointsNeededByCurrentRules) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[RuleBasedStrategy] User-defined minIndicatorDataPoints (${params.minIndicatorDataPoints}) is less than required by current rules (${pointsNeededByCurrentRules}). Adjusting to ${pointsNeededByCurrentRules}.`,\n\t\t\t\t);\n\t\t\t\ttempParams.minIndicatorDataPoints = pointsNeededByCurrentRules;\n\t\t\t} else {\n\t\t\t\ttempParams.minIndicatorDataPoints = params.minIndicatorDataPoints;\n\t\t\t}\n\t\t} else {\n\t\t\ttempParams.minIndicatorDataPoints = Math.max(\n\t\t\t\tDEFAULT_MIN_INDICATOR_DATA_POINTS,\n\t\t\t\tpointsNeededByCurrentRules,\n\t\t\t);\n\t\t}\n\n\t\tif (params.indicators) {\n\t\t\tthis.params.indicators = params.indicators;\n\t\t}\n\t\tif (params.buyConditions) {\n\t\t\tthis.params.buyConditions = { ...params.buyConditions };\n\t\t}\n\t\tif (params.sellConditions) {\n\t\t\tthis.params.sellConditions = { ...params.sellConditions };\n\t\t}\n\t\tif (params.riskSettings) {\n\t\t\tthis.params.riskSettings = {\n\t\t\t\t...this.params.riskSettings,\n\t\t\t\t...params.riskSettings,\n\t\t\t};\n\t\t}\n\n\t\tthis.params = tempParams; // Assign fully validated and adjusted params\n\t\tconsole.error(\n\t\t\t\"[RuleBasedStrategy DIAG] configure: this.params AFTER merge and adjustments:\",\n\t\t\tJSON.stringify(this.params),\n\t\t);\n\t}\n\n\tprivate calculateIndicators(\n\t\tohlcvData: OHLCV[],\n\t\trules: RuleCondition[],\n\t): TAResults {\n\t\t// No need to check for this.indicators, we are using the imported library directly.\n\n\t\tconst minDataPointsForAnyCalc = rules.reduce((minOverall, r) => {\n\t\t\tconst periodReq = Math.max(\n\t\t\t\tr.longMAPeriod || 0,\n\t\t\t\tr.rsiPeriod || 0,\n\t\t\t\tr.macdSlowPeriod || 0,\n\t\t\t);\n\t\t\treturn Math.min(minOverall, periodReq > 0 ? periodReq + 1 : Infinity);\n\t\t}, Infinity);\n\n\t\tif (\n\t\t\tohlcvData.length <\n\t\t\t\t(this.params.minIndicatorDataPoints ??\n\t\t\t\t\tDEFAULT_MIN_INDICATOR_DATA_POINTS) ||\n\t\t\tohlcvData.length < minDataPointsForAnyCalc\n\t\t) {\n\t\t\treturn {};\n\t\t}\n\n\t\tconst closePrices = ohlcvData.map((d) => d.close);\n\t\tconst results: TAResults = {};\n\n\t\t// --- RSI Calculation ---\n\t\tconst rsiRule = rules.find((r) => r.type === \"RSI\" && r.rsiPeriod);\n\t\tif (rsiRule?.rsiPeriod && closePrices.length >= rsiRule.rsiPeriod) {\n\t\t\tconst rsiResult = this.indicators.rsi({\n\t\t\t\tvalues: closePrices,\n\t\t\t\tperiod: rsiRule.rsiPeriod,\n\t\t\t});\n\t\t\tif (rsiResult.length > 0) {\n\t\t\t\tresults.rsi = rsiResult[rsiResult.length - 1];\n\t\t\t}\n\t\t}\n\n\t\t// --- MA Crossover Calculation (SMA & EMA) ---\n\t\tconst crossoverRules = rules.filter(\n\t\t\t(r) =>\n\t\t\t\t(r.type === \"SMA_CROSSOVER\" || r.type === \"EMA_CROSSOVER\") &&\n\t\t\t\tr.shortMAPeriod &&\n\t\t\t\tr.longMAPeriod,\n\t\t);\n\n\t\tfor (const rule of crossoverRules) {\n\t\t\tif (\n\t\t\t\trule.shortMAPeriod &&\n\t\t\t\trule.longMAPeriod &&\n\t\t\t\tclosePrices.length >= rule.longMAPeriod + 1\n\t\t\t) {\n\t\t\t\tconst maIndicator =\n\t\t\t\t\trule.maType === \"EMA\" ? this.indicators.ema : this.indicators.sma;\n\n\t\t\t\tconst shortMA = maIndicator({\n\t\t\t\t\tvalues: closePrices,\n\t\t\t\t\tperiod: rule.shortMAPeriod,\n\t\t\t\t});\n\t\t\t\tconst longMA = maIndicator({\n\t\t\t\t\tvalues: closePrices,\n\t\t\t\t\tperiod: rule.longMAPeriod,\n\t\t\t\t});\n\n\t\t\t\tif (shortMA.length >= 2 && longMA.length >= 2) {\n\t\t\t\t\tif (rule.maType === \"EMA\") {\n\t\t\t\t\t\tresults.emaShort = shortMA[shortMA.length - 1];\n\t\t\t\t\t\tresults.prevEmaShort = shortMA[shortMA.length - 2];\n\t\t\t\t\t\tresults.emaLong = longMA[longMA.length - 1];\n\t\t\t\t\t\tresults.prevEmaLong = longMA[longMA.length - 2];\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.smaShort = shortMA[shortMA.length - 1];\n\t\t\t\t\t\tresults.prevSmaShort = shortMA[shortMA.length - 2];\n\t\t\t\t\t\tresults.smaLong = longMA[longMA.length - 1];\n\t\t\t\t\t\tresults.prevSmaLong = longMA[longMA.length - 2];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- MACD Calculation ---\n\t\tconst macdRule = rules.find((r) => r.type === \"MACD_CROSS\");\n\t\tif (\n\t\t\tmacdRule?.macdFastPeriod &&\n\t\t\tmacdRule.macdSlowPeriod &&\n\t\t\tmacdRule.macdSignalPeriod &&\n\t\t\tclosePrices.length >= macdRule.macdSlowPeriod\n\t\t) {\n\t\t\tconst macdResult = this.indicators.macd({\n\t\t\t\tvalues: closePrices,\n\t\t\t\tfastPeriod: macdRule.macdFastPeriod,\n\t\t\t\tslowPeriod: macdRule.macdSlowPeriod,\n\t\t\t\tsignalPeriod: macdRule.macdSignalPeriod,\n\t\t\t\tSimpleMAOscillator: false,\n\t\t\t\tSimpleMASignal: false,\n\t\t\t});\n\t\t\tif (macdResult.length >= 2) {\n\t\t\t\tresults.macd = macdResult[macdResult.length - 1];\n\t\t\t\tresults.prevMacd = macdResult[macdResult.length - 2];\n\t\t\t}\n\t\t}\n\n\t\treturn results;\n\t}\n\n\tasync decide(params: {\n\t\tmarketData: StrategyContextMarketData;\n\t\tagentState: AgentState;\n\t\tportfolioSnapshot: PortfolioSnapshot;\n\t\tagentRuntime?: any;\n\t}): Promise<TradeOrder | null> {\n\t\tconst { marketData, portfolioSnapshot } = params;\n\n\t\tif (!marketData.priceData || marketData.priceData.length < 20) {\n\t\t\treturn null; // Not enough data for indicators\n\t\t}\n\n\t\tconst indicators = this.calculateIndicators(\n\t\t\tmarketData.priceData,\n\t\t\tthis.params.rules,\n\t\t);\n\n\t\t// Check each rule to see if any conditions are met\n\t\tlet shouldBuy = false;\n\t\tlet shouldSell = false;\n\t\tlet buyReason = \"\";\n\t\tlet sellReason = \"\";\n\n\t\tfor (const rule of this.params.rules) {\n\t\t\tswitch (rule.type) {\n\t\t\t\tcase \"RSI\":\n\t\t\t\t\tif (indicators.rsi !== undefined) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trule.rsiOversold &&\n\t\t\t\t\t\t\tindicators.rsi < rule.rsiOversold &&\n\t\t\t\t\t\t\trule.action === TradeType.BUY\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tshouldBuy = true;\n\t\t\t\t\t\t\tbuyReason = `RSI oversold (${indicators.rsi.toFixed(2)} < ${rule.rsiOversold})`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\trule.rsiOverbought &&\n\t\t\t\t\t\t\tindicators.rsi > rule.rsiOverbought &&\n\t\t\t\t\t\t\trule.action === TradeType.SELL\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tshouldSell = true;\n\t\t\t\t\t\t\tsellReason = `RSI overbought (${indicators.rsi.toFixed(2)} > ${rule.rsiOverbought})`;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"VOLUME\":\n\t\t\t\t\t// Volume rules are not currently implemented in the indicator calculation\n\t\t\t\t\t// so they should not trigger\n\t\t\t\t\tbreak;\n\t\t\t\t// Add other rule types as needed\n\t\t\t}\n\t\t}\n\n\t\t// Extract asset from portfolio snapshot or use a default\n\t\tconst assetSymbol =\n\t\t\tObject.keys(portfolioSnapshot.holdings).find(\n\t\t\t\t(key) => key !== \"USDC\" && portfolioSnapshot.holdings[key] > 0,\n\t\t\t) || \"SOL\";\n\n\t\tconst pair = `${assetSymbol}/USDC`;\n\n\t\t// Execute buy if conditions are met\n\t\tif (shouldBuy) {\n\t\t\tconst cashHolding = portfolioSnapshot.holdings.USDC || 0;\n\t\t\tif (cashHolding > 10 && marketData.currentPrice) {\n\t\t\t\tconst quantity = this.calculateTradeQuantity(\n\t\t\t\t\tmarketData,\n\t\t\t\t\tportfolioSnapshot,\n\t\t\t\t);\n\n\t\t\t\tif (quantity && quantity > MIN_TRADE_QUANTITY_THRESHOLD) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpair,\n\t\t\t\t\t\taction: TradeType.BUY,\n\t\t\t\t\t\tquantity: parseFloat(quantity.toFixed(8)),\n\t\t\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t\treason: buyReason || \"Technical indicators suggest buy signal\",\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Execute sell if conditions are met\n\t\tif (shouldSell) {\n\t\t\tconst holding = portfolioSnapshot.holdings[assetSymbol] || 0;\n\t\t\tif (holding > 0) {\n\t\t\t\treturn {\n\t\t\t\t\tpair,\n\t\t\t\t\taction: TradeType.SELL,\n\t\t\t\t\tquantity: holding,\n\t\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\treason: sellReason || \"Technical indicators suggest sell signal\",\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate calculateTradeQuantity(\n\t\tmarketData: StrategyContextMarketData,\n\t\tportfolioSnapshot: PortfolioSnapshot,\n\t): number | null {\n\t\t// Use USDC balance from portfolio snapshot\n\t\tconst usdcBalance = portfolioSnapshot.holdings.USDC || 0;\n\n\t\tif (\n\t\t\tthis.params.tradeSizePercentage &&\n\t\t\tusdcBalance > 0 &&\n\t\t\tmarketData.currentPrice &&\n\t\t\tmarketData.currentPrice > 0\n\t\t) {\n\t\t\tconst capitalToUse = usdcBalance * this.params.tradeSizePercentage;\n\t\t\tconst quantity = capitalToUse / marketData.currentPrice;\n\t\t\tif (quantity < MIN_TRADE_QUANTITY_THRESHOLD) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn quantity;\n\t\t} else if (\n\t\t\tthis.params.fixedTradeQuantity &&\n\t\t\tthis.params.fixedTradeQuantity > 0\n\t\t) {\n\t\t\treturn this.params.fixedTradeQuantity;\n\t\t} else {\n\t\t\t// Default to small fixed quantity\n\t\t\treturn 0.1;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -5,6 +5,7 @@ var MeanReversionStrategy = class {
5
5
  name = "MeanReversionStrategy";
6
6
  description = "A strategy that trades on mean reversion patterns using Bollinger Bands and RSI";
7
7
  config;
8
+ runtime;
8
9
  initialized = false;
9
10
  constructor(config) {
10
11
  this.config = {
@@ -68,8 +69,13 @@ var MeanReversionStrategy = class {
68
69
  if (currentPrice <= lower * (1 + (1 - this.config.bbEntryThreshold))) {
69
70
  const rsiCondition = !this.config.rsiConfirmation || currentRSI < this.config.rsiOversold;
70
71
  if (rsiCondition) {
71
- const positionSize = this.calculatePositionSize(portfolioSnapshot.totalValue, currentPrice);
72
- console.log(`[${this.name}] BUY SIGNAL - Price at lower BB, RSI: ${currentRSI.toFixed(2)}`);
72
+ const positionSize = this.calculatePositionSize(
73
+ portfolioSnapshot.totalValue,
74
+ currentPrice
75
+ );
76
+ console.log(
77
+ `[${this.name}] BUY SIGNAL - Price at lower BB, RSI: ${currentRSI.toFixed(2)}`
78
+ );
73
79
  return {
74
80
  action: "BUY" /* BUY */,
75
81
  orderType: "MARKET" /* MARKET */,
@@ -158,4 +164,4 @@ var MeanReversionStrategy = class {
158
164
  export {
159
165
  MeanReversionStrategy
160
166
  };
161
- //# sourceMappingURL=chunk-7GI4G3ZN.js.map
167
+ //# sourceMappingURL=chunk-MTAQ364B.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\ttype AgentState,\n\ttype OHLCV,\n\tOrderType,\n\ttype PortfolioSnapshot,\n\ttype StrategyContextMarketData,\n\ttype TradeOrder,\n\tTradeType,\n\ttype TradingStrategy,\n} from \"../types.ts\";\n\nexport interface MeanReversionConfig {\n\t// Bollinger Bands settings\n\tbbPeriod: number;\n\tbbStdDev: number;\n\n\t// RSI settings\n\trsiPeriod: number;\n\trsiOversold: number;\n\trsiOverbought: number;\n\n\t// Risk management\n\tpositionSizePercent: number;\n\tstopLossPercent: number;\n\ttakeProfitPercent: number;\n\n\t// Market condition filters\n\tminVolatility: number;\n\tmaxVolatility: number;\n\tminVolumeRatio: number;\n\n\t// Entry/exit thresholds\n\tbbEntryThreshold: number; // How far outside BB for entry\n\trsiConfirmation: boolean;\n}\n\nexport class MeanReversionStrategy implements TradingStrategy {\n\tpublic readonly id = \"mean-reversion-strategy\";\n\tpublic readonly name = \"MeanReversionStrategy\";\n\tpublic readonly description =\n\t\t\"A strategy that trades on mean reversion patterns using Bollinger Bands and RSI\";\n\tprivate config: MeanReversionConfig;\n\tprivate runtime?: AgentRuntime;\n\tprivate initialized = false;\n\n\tconstructor(config?: Partial<MeanReversionConfig>) {\n\t\tthis.config = {\n\t\t\t// Default configuration\n\t\t\tbbPeriod: 20,\n\t\t\tbbStdDev: 2,\n\t\t\trsiPeriod: 14,\n\t\t\trsiOversold: 30,\n\t\t\trsiOverbought: 70,\n\t\t\tpositionSizePercent: 0.02,\n\t\t\tstopLossPercent: 0.03,\n\t\t\ttakeProfitPercent: 0.02,\n\t\t\tminVolatility: 0.01,\n\t\t\tmaxVolatility: 0.05,\n\t\t\tminVolumeRatio: 1.2,\n\t\t\tbbEntryThreshold: 0.95,\n\t\t\trsiConfirmation: true,\n\t\t\t...config,\n\t\t};\n\t}\n\n\tasync initialize(runtime: AgentRuntime): Promise<void> {\n\t\tthis.runtime = runtime;\n\t\tthis.initialized = true;\n\t\tconsole.log(`[${this.name}] Initialized with config:`, this.config);\n\t}\n\n\tisReady(): boolean {\n\t\treturn this.initialized;\n\t}\n\n\tasync decide(params: {\n\t\tmarketData: StrategyContextMarketData;\n\t\tagentState: AgentState;\n\t\tportfolioSnapshot: PortfolioSnapshot;\n\t\tagentRuntime?: AgentRuntime;\n\t}): Promise<TradeOrder | null> {\n\t\tconst { marketData, agentState, portfolioSnapshot } = params;\n\t\tconst { priceData, currentPrice } = marketData;\n\n\t\tif (\n\t\t\t!priceData ||\n\t\t\tpriceData.length <\n\t\t\t\tMath.max(this.config.bbPeriod, this.config.rsiPeriod) + 10\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Extract price arrays\n\t\tconst closes = priceData.map((c: OHLCV) => c.close);\n\t\tconst _highs = priceData.map((c: OHLCV) => c.high);\n\t\tconst _lows = priceData.map((c: OHLCV) => c.low);\n\t\tconst volumes = priceData.map((c: OHLCV) => c.volume);\n\n\t\t// Calculate indicators\n\t\tconst bb = this.calculateBollingerBands(closes);\n\t\tconst rsi = this.calculateRSI(closes);\n\t\tconst volatility = agentState.volatility;\n\t\tconst volumeRatio = this.calculateVolumeRatio(volumes);\n\n\t\tif (!bb || !rsi || rsi.length === 0) return null;\n\n\t\tconst currentRSI = rsi[rsi.length - 1];\n\t\tconst { upper, lower, middle } = bb;\n\n\t\t// Market condition checks\n\t\tif (\n\t\t\tvolatility < this.config.minVolatility ||\n\t\t\tvolatility > this.config.maxVolatility\n\t\t) {\n\t\t\tconsole.log(\n\t\t\t\t`[${this.name}] Volatility ${volatility.toFixed(4)} outside range [${this.config.minVolatility}, ${this.config.maxVolatility}]`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\tif (volumeRatio < this.config.minVolumeRatio) {\n\t\t\tconsole.log(\n\t\t\t\t`[${this.name}] Volume ratio ${volumeRatio.toFixed(2)} below minimum ${this.config.minVolumeRatio}`,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\n\t\t// Check for mean reversion opportunities\n\t\tconst distanceFromUpper = (upper - currentPrice) / currentPrice;\n\t\tconst distanceFromLower = (currentPrice - lower) / currentPrice;\n\t\tconst distanceFromMiddle = Math.abs(currentPrice - middle) / middle;\n\n\t\t// Buy signal: Price near lower band + RSI oversold\n\t\tif (currentPrice <= lower * (1 + (1 - this.config.bbEntryThreshold))) {\n\t\t\tconst rsiCondition =\n\t\t\t\t!this.config.rsiConfirmation || currentRSI < this.config.rsiOversold;\n\n\t\t\tif (rsiCondition) {\n\t\t\t\tconst positionSize = this.calculatePositionSize(\n\t\t\t\t\tportfolioSnapshot.totalValue,\n\t\t\t\t\tcurrentPrice,\n\t\t\t\t);\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t`[${this.name}] BUY SIGNAL - Price at lower BB, RSI: ${currentRSI.toFixed(2)}`,\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\taction: TradeType.BUY,\n\t\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\t\tpair: \"SOL/USDC\", // This should come from context\n\t\t\t\t\tquantity: positionSize,\n\t\t\t\t\treason: `Mean reversion buy: Price ${distanceFromLower.toFixed(2)}% below lower BB, RSI: ${currentRSI.toFixed(2)}`,\n\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Sell signal: Price near upper band + RSI overbought\n\t\tif (currentPrice >= upper * (1 - (1 - this.config.bbEntryThreshold))) {\n\t\t\tconst rsiCondition =\n\t\t\t\t!this.config.rsiConfirmation || currentRSI > this.config.rsiOverbought;\n\n\t\t\tif (rsiCondition) {\n\t\t\t\tconst currentHolding = portfolioSnapshot.holdings.SOL || 0;\n\n\t\t\t\tif (currentHolding > 0) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[${this.name}] SELL SIGNAL - Price at upper BB, RSI: ${currentRSI.toFixed(2)}`,\n\t\t\t\t\t);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\taction: TradeType.SELL,\n\t\t\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\t\t\tpair: \"SOL/USDC\",\n\t\t\t\t\t\tquantity: currentHolding,\n\t\t\t\t\t\treason: `Mean reversion sell: Price ${distanceFromUpper.toFixed(2)}% above upper BB, RSI: ${currentRSI.toFixed(2)}`,\n\t\t\t\t\t\ttimestamp: Date.now(),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Exit position if price returns to mean\n\t\tconst currentHolding = portfolioSnapshot.holdings.SOL || 0;\n\t\tif (currentHolding > 0 && distanceFromMiddle < 0.01) {\n\t\t\tconsole.log(`[${this.name}] Price returned to mean, exiting position`);\n\n\t\t\treturn {\n\t\t\t\taction: TradeType.SELL,\n\t\t\t\torderType: OrderType.MARKET,\n\t\t\t\tpair: \"SOL/USDC\",\n\t\t\t\tquantity: currentHolding,\n\t\t\t\treason: `Price returned to mean (${distanceFromMiddle.toFixed(2)}% from middle BB)`,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate calculateBollingerBands(\n\t\tcloses: number[],\n\t): { upper: number; middle: number; lower: number } | null {\n\t\tif (closes.length < this.config.bbPeriod) return null;\n\n\t\tconst bb = talib.BollingerBands.calculate({\n\t\t\tperiod: this.config.bbPeriod,\n\t\t\tvalues: closes,\n\t\t\tstdDev: this.config.bbStdDev,\n\t\t});\n\n\t\tif (!bb || bb.length === 0) return null;\n\n\t\tconst lastBB = bb[bb.length - 1];\n\t\treturn {\n\t\t\tupper: lastBB.upper,\n\t\t\tmiddle: lastBB.middle,\n\t\t\tlower: lastBB.lower,\n\t\t};\n\t}\n\n\tprivate calculateRSI(closes: number[]): number[] {\n\t\tconst rsi = talib.RSI.calculate({\n\t\t\tperiod: this.config.rsiPeriod,\n\t\t\tvalues: closes,\n\t\t});\n\t\treturn rsi;\n\t}\n\n\tprivate calculateVolumeRatio(volumes: number[]): number {\n\t\tif (volumes.length < 20) return 0;\n\n\t\tconst recentVolume = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5;\n\t\tconst avgVolume = volumes.slice(-20).reduce((a, b) => a + b, 0) / 20;\n\n\t\treturn avgVolume > 0 ? recentVolume / avgVolume : 0;\n\t}\n\n\tprivate calculatePositionSize(portfolioValue: number, price: number): number {\n\t\tconst positionValue = portfolioValue * this.config.positionSizePercent;\n\t\treturn positionValue / price;\n\t}\n\n\tupdateConfig(config: Partial<MeanReversionConfig>): void {\n\t\tthis.config = { ...this.config, ...config };\n\t\tconsole.log(`[${this.name}] Config updated:`, this.config);\n\t}\n\n\tgetConfig(): MeanReversionConfig {\n\t\treturn { ...this.config };\n\t}\n}\n"],"mappings":"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","names":["currentHolding"]}