@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.
@@ -0,0 +1,276 @@
1
+ // src/strategies/RuleBasedStrategy.ts
2
+ import * as ti from "technicalindicators";
3
+ var DEFAULT_TRADE_SIZE_PERCENTAGE = 0.01;
4
+ var DEFAULT_FIXED_TRADE_QUANTITY = 1;
5
+ var DEFAULT_MIN_INDICATOR_DATA_POINTS = 20;
6
+ var MIN_TRADE_QUANTITY_THRESHOLD = 1e-8;
7
+ var RuleBasedStrategy = class {
8
+ id = "rule-based-v1";
9
+ name = "Rule-Based Trading Strategy";
10
+ description = "Makes trading decisions based on technical indicators and thresholds.";
11
+ params = {
12
+ rules: [],
13
+ tradeSizePercentage: DEFAULT_TRADE_SIZE_PERCENTAGE,
14
+ fixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY,
15
+ minIndicatorDataPoints: DEFAULT_MIN_INDICATOR_DATA_POINTS,
16
+ indicators: ["sma", "ema", "rsi", "macd"],
17
+ buyConditions: {
18
+ rsi: { threshold: 30, condition: "below" },
19
+ macd: { threshold: 0, condition: "above" }
20
+ },
21
+ sellConditions: {
22
+ rsi: { threshold: 70, condition: "above" },
23
+ macd: { threshold: 0, condition: "below" }
24
+ },
25
+ riskSettings: {
26
+ maxPositionSize: 0.05,
27
+ stopLossPercentage: 0.02,
28
+ takeProfitPercentage: 0.05
29
+ }
30
+ };
31
+ // The actual TI library is now used directly.
32
+ indicators = ti;
33
+ isReady() {
34
+ return true;
35
+ }
36
+ configure(params) {
37
+ console.error(
38
+ "[RuleBasedStrategy DIAG] configure called. Incoming params:",
39
+ JSON.stringify(params)
40
+ );
41
+ console.error(
42
+ "[RuleBasedStrategy DIAG] configure: this.params BEFORE merge:",
43
+ JSON.stringify(this.params)
44
+ );
45
+ if (!params.rules || params.rules.length === 0) {
46
+ throw new Error("At least one rule must be configured.");
47
+ }
48
+ params.rules.forEach((rule) => {
49
+ if (rule.rsiPeriod !== void 0 && rule.rsiPeriod <= 0)
50
+ throw new Error("RSI period must be positive.");
51
+ if (rule.shortMAPeriod !== void 0 && rule.shortMAPeriod <= 0)
52
+ throw new Error("Short MA period must be positive.");
53
+ if (rule.longMAPeriod !== void 0 && rule.longMAPeriod <= 0)
54
+ throw new Error("Long MA period must be positive.");
55
+ if (rule.type === "SMA_CROSSOVER" || rule.type === "EMA_CROSSOVER") {
56
+ if (!rule.shortMAPeriod || !rule.longMAPeriod) {
57
+ throw new Error("Short and Long MA periods are required for crossover rules.");
58
+ }
59
+ if (rule.shortMAPeriod >= rule.longMAPeriod) {
60
+ throw new Error("Short MA period must be less than Long MA period for crossovers.");
61
+ }
62
+ if (!rule.maType) {
63
+ console.warn(
64
+ `[RuleBasedStrategy] maType not specified for crossover rule, defaulting to SMA.`
65
+ );
66
+ rule.maType = "SMA";
67
+ }
68
+ }
69
+ if (rule.rsiOverbought !== void 0 && (rule.rsiOverbought <= 0 || rule.rsiOverbought > 100))
70
+ throw new Error("RSI overbought must be between 0 and 100");
71
+ if (rule.rsiOversold !== void 0 && (rule.rsiOversold <= 0 || rule.rsiOversold > 100))
72
+ throw new Error("RSI oversold must be between 0 and 100");
73
+ if (rule.rsiOversold && rule.rsiOverbought && rule.rsiOversold >= rule.rsiOverbought)
74
+ throw new Error("RSI oversold must be less than RSI overbought");
75
+ });
76
+ if (params.tradeSizePercentage !== void 0 && (params.tradeSizePercentage <= 0 || params.tradeSizePercentage > 1)) {
77
+ throw new Error("tradeSizePercentage must be between 0 (exclusive) and 1 (inclusive).");
78
+ }
79
+ if (params.fixedTradeQuantity !== void 0 && params.fixedTradeQuantity <= 0) {
80
+ throw new Error("fixedTradeQuantity must be positive.");
81
+ }
82
+ if (params.minIndicatorDataPoints !== void 0 && params.minIndicatorDataPoints < 1) {
83
+ throw new Error("minIndicatorDataPoints must be at least 1.");
84
+ }
85
+ const tempParams = { ...this.params, ...params };
86
+ const currentRulesLongestPeriod = tempParams.rules.reduce(
87
+ (max, r) => Math.max(max, r.longMAPeriod || 0, r.rsiPeriod || 0),
88
+ 0
89
+ );
90
+ const pointsNeededByCurrentRules = currentRulesLongestPeriod > 0 ? currentRulesLongestPeriod + 1 : 1;
91
+ if (params.minIndicatorDataPoints !== void 0) {
92
+ if (params.minIndicatorDataPoints < pointsNeededByCurrentRules) {
93
+ console.warn(
94
+ `[RuleBasedStrategy] User-defined minIndicatorDataPoints (${params.minIndicatorDataPoints}) is less than required by current rules (${pointsNeededByCurrentRules}). Adjusting to ${pointsNeededByCurrentRules}.`
95
+ );
96
+ tempParams.minIndicatorDataPoints = pointsNeededByCurrentRules;
97
+ } else {
98
+ tempParams.minIndicatorDataPoints = params.minIndicatorDataPoints;
99
+ }
100
+ } else {
101
+ tempParams.minIndicatorDataPoints = Math.max(
102
+ DEFAULT_MIN_INDICATOR_DATA_POINTS,
103
+ pointsNeededByCurrentRules
104
+ );
105
+ }
106
+ if (params.indicators) {
107
+ this.params.indicators = params.indicators;
108
+ }
109
+ if (params.buyConditions) {
110
+ this.params.buyConditions = { ...params.buyConditions };
111
+ }
112
+ if (params.sellConditions) {
113
+ this.params.sellConditions = { ...params.sellConditions };
114
+ }
115
+ if (params.riskSettings) {
116
+ this.params.riskSettings = {
117
+ ...this.params.riskSettings,
118
+ ...params.riskSettings
119
+ };
120
+ }
121
+ this.params = tempParams;
122
+ console.error(
123
+ "[RuleBasedStrategy DIAG] configure: this.params AFTER merge and adjustments:",
124
+ JSON.stringify(this.params)
125
+ );
126
+ }
127
+ calculateIndicators(ohlcvData, rules) {
128
+ const minDataPointsForAnyCalc = rules.reduce((minOverall, r) => {
129
+ const periodReq = Math.max(r.longMAPeriod || 0, r.rsiPeriod || 0, r.macdSlowPeriod || 0);
130
+ return Math.min(minOverall, periodReq > 0 ? periodReq + 1 : Infinity);
131
+ }, Infinity);
132
+ if (ohlcvData.length < (this.params.minIndicatorDataPoints ?? DEFAULT_MIN_INDICATOR_DATA_POINTS) || ohlcvData.length < minDataPointsForAnyCalc) {
133
+ return {};
134
+ }
135
+ const closePrices = ohlcvData.map((d) => d.close);
136
+ const results = {};
137
+ const rsiRule = rules.find((r) => r.type === "RSI" && r.rsiPeriod);
138
+ if (rsiRule?.rsiPeriod && closePrices.length >= rsiRule.rsiPeriod) {
139
+ const rsiResult = this.indicators.rsi({
140
+ values: closePrices,
141
+ period: rsiRule.rsiPeriod
142
+ });
143
+ if (rsiResult.length > 0) {
144
+ results.rsi = rsiResult[rsiResult.length - 1];
145
+ }
146
+ }
147
+ const crossoverRules = rules.filter(
148
+ (r) => (r.type === "SMA_CROSSOVER" || r.type === "EMA_CROSSOVER") && r.shortMAPeriod && r.longMAPeriod
149
+ );
150
+ for (const rule of crossoverRules) {
151
+ if (rule.shortMAPeriod && rule.longMAPeriod && closePrices.length >= rule.longMAPeriod + 1) {
152
+ const maIndicator = rule.maType === "EMA" ? this.indicators.ema : this.indicators.sma;
153
+ const shortMA = maIndicator({
154
+ values: closePrices,
155
+ period: rule.shortMAPeriod
156
+ });
157
+ const longMA = maIndicator({
158
+ values: closePrices,
159
+ period: rule.longMAPeriod
160
+ });
161
+ if (shortMA.length >= 2 && longMA.length >= 2) {
162
+ if (rule.maType === "EMA") {
163
+ results.emaShort = shortMA[shortMA.length - 1];
164
+ results.prevEmaShort = shortMA[shortMA.length - 2];
165
+ results.emaLong = longMA[longMA.length - 1];
166
+ results.prevEmaLong = longMA[longMA.length - 2];
167
+ } else {
168
+ results.smaShort = shortMA[shortMA.length - 1];
169
+ results.prevSmaShort = shortMA[shortMA.length - 2];
170
+ results.smaLong = longMA[longMA.length - 1];
171
+ results.prevSmaLong = longMA[longMA.length - 2];
172
+ }
173
+ }
174
+ }
175
+ }
176
+ const macdRule = rules.find((r) => r.type === "MACD_CROSS");
177
+ if (macdRule?.macdFastPeriod && macdRule.macdSlowPeriod && macdRule.macdSignalPeriod && closePrices.length >= macdRule.macdSlowPeriod) {
178
+ const macdResult = this.indicators.macd({
179
+ values: closePrices,
180
+ fastPeriod: macdRule.macdFastPeriod,
181
+ slowPeriod: macdRule.macdSlowPeriod,
182
+ signalPeriod: macdRule.macdSignalPeriod,
183
+ SimpleMAOscillator: false,
184
+ SimpleMASignal: false
185
+ });
186
+ if (macdResult.length >= 2) {
187
+ results.macd = macdResult[macdResult.length - 1];
188
+ results.prevMacd = macdResult[macdResult.length - 2];
189
+ }
190
+ }
191
+ return results;
192
+ }
193
+ async decide(params) {
194
+ const { marketData, portfolioSnapshot } = params;
195
+ if (!marketData.priceData || marketData.priceData.length < 20) {
196
+ return null;
197
+ }
198
+ const indicators = this.calculateIndicators(marketData.priceData, this.params.rules);
199
+ let shouldBuy = false;
200
+ let shouldSell = false;
201
+ let buyReason = "";
202
+ let sellReason = "";
203
+ for (const rule of this.params.rules) {
204
+ switch (rule.type) {
205
+ case "RSI":
206
+ if (indicators.rsi !== void 0) {
207
+ if (rule.rsiOversold && indicators.rsi < rule.rsiOversold && rule.action === "BUY" /* BUY */) {
208
+ shouldBuy = true;
209
+ buyReason = `RSI oversold (${indicators.rsi.toFixed(2)} < ${rule.rsiOversold})`;
210
+ }
211
+ if (rule.rsiOverbought && indicators.rsi > rule.rsiOverbought && rule.action === "SELL" /* SELL */) {
212
+ shouldSell = true;
213
+ sellReason = `RSI overbought (${indicators.rsi.toFixed(2)} > ${rule.rsiOverbought})`;
214
+ }
215
+ }
216
+ break;
217
+ case "VOLUME":
218
+ break;
219
+ }
220
+ }
221
+ const assetSymbol = Object.keys(portfolioSnapshot.holdings).find(
222
+ (key) => key !== "USDC" && portfolioSnapshot.holdings[key] > 0
223
+ ) || "SOL";
224
+ const pair = `${assetSymbol}/USDC`;
225
+ if (shouldBuy) {
226
+ const cashHolding = portfolioSnapshot.holdings.USDC || 0;
227
+ if (cashHolding > 10 && marketData.currentPrice) {
228
+ const quantity = this.calculateTradeQuantity(marketData, portfolioSnapshot);
229
+ if (quantity && quantity > MIN_TRADE_QUANTITY_THRESHOLD) {
230
+ return {
231
+ pair,
232
+ action: "BUY" /* BUY */,
233
+ quantity: parseFloat(quantity.toFixed(8)),
234
+ orderType: "MARKET" /* MARKET */,
235
+ timestamp: Date.now(),
236
+ reason: buyReason || "Technical indicators suggest buy signal"
237
+ };
238
+ }
239
+ }
240
+ }
241
+ if (shouldSell) {
242
+ const holding = portfolioSnapshot.holdings[assetSymbol] || 0;
243
+ if (holding > 0) {
244
+ return {
245
+ pair,
246
+ action: "SELL" /* SELL */,
247
+ quantity: holding,
248
+ orderType: "MARKET" /* MARKET */,
249
+ timestamp: Date.now(),
250
+ reason: sellReason || "Technical indicators suggest sell signal"
251
+ };
252
+ }
253
+ }
254
+ return null;
255
+ }
256
+ calculateTradeQuantity(marketData, portfolioSnapshot) {
257
+ const usdcBalance = portfolioSnapshot.holdings.USDC || 0;
258
+ if (this.params.tradeSizePercentage && usdcBalance > 0 && marketData.currentPrice && marketData.currentPrice > 0) {
259
+ const capitalToUse = usdcBalance * this.params.tradeSizePercentage;
260
+ const quantity = capitalToUse / marketData.currentPrice;
261
+ if (quantity < MIN_TRADE_QUANTITY_THRESHOLD) {
262
+ return null;
263
+ }
264
+ return quantity;
265
+ } else if (this.params.fixedTradeQuantity && this.params.fixedTradeQuantity > 0) {
266
+ return this.params.fixedTradeQuantity;
267
+ } else {
268
+ return 0.1;
269
+ }
270
+ }
271
+ };
272
+
273
+ export {
274
+ RuleBasedStrategy
275
+ };
276
+ //# sourceMappingURL=chunk-GYTZTIWK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/RuleBasedStrategy.ts"],"sourcesContent":["import * as ti 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\n// Mock/placeholder for technical indicator calculation results\ninterface TAResults {\n rsi?: number;\n smaShort?: number;\n smaLong?: number;\n prevSmaShort?: number;\n prevSmaLong?: number;\n emaShort?: number;\n emaLong?: number;\n prevEmaShort?: number;\n prevEmaLong?: number;\n macd?: {\n MACD?: number;\n signal?: number;\n histogram?: number;\n };\n prevMacd?: {\n MACD?: number;\n signal?: number;\n histogram?: number;\n };\n // ema?: number[]; // Keep as array if EMA logic will also be array-based initially\n // macd?: { macd: number[]; signal: number[]; histogram: number[] };\n // 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 type: \"RSI\" | \"SMA_CROSSOVER\" | \"EMA_CROSSOVER\" | \"VOLUME\" | \"PRICE_ACTION\" | \"MACD_CROSS\";\n // RSI\n rsiPeriod?: number;\n rsiOverbought?: number; // e.g., 70 (for sell)\n rsiOversold?: number; // e.g., 30 (for buy)\n // SMA/EMA\n shortMAPeriod?: number;\n longMAPeriod?: number;\n maType?: \"SMA\" | \"EMA\"; // Specify MA type for crossover rules\n // VOLUME\n minVolume24h?: number;\n // PRICE_ACTION\n priceBreaksNDayHigh?: number; // N days for high (e.g., 7-day high)\n priceBreaksNDayLow?: number; // N days for low\n // MACD\n macdFastPeriod?: number;\n macdSlowPeriod?: number;\n macdSignalPeriod?: number;\n\n action: TradeType; // BUY or SELL if this condition is met\n}\n\nexport interface StopLossTakeProfitConfig {\n stopLossPercentage?: number; // e.g., 0.05 for 5%\n takeProfitPercentage?: number; // e.g., 0.10 for 10%\n}\n\nexport interface RuleBasedStrategyParams {\n rules: RuleCondition[];\n stopLossTakeProfit?: StopLossTakeProfitConfig;\n /** Percentage of available capital for a trade, or fixed quantity if capital/price info is missing */\n tradeSizePercentage?: number;\n fixedTradeQuantity?: number;\n /** Minimum number of data points required for indicators to be considered valid */\n minIndicatorDataPoints?: number;\n indicators?: string[];\n buyConditions?: Record<string, { threshold: number; condition: string }>;\n sellConditions?: Record<string, { threshold: number; condition: string }>;\n riskSettings?: {\n maxPositionSize?: number;\n stopLossPercentage?: number;\n takeProfitPercentage?: number;\n };\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 public readonly id = \"rule-based-v1\";\n public readonly name = \"Rule-Based Trading Strategy\";\n public readonly description =\n \"Makes trading decisions based on technical indicators and thresholds.\";\n\n private params: RuleBasedStrategyParams = {\n rules: [],\n tradeSizePercentage: DEFAULT_TRADE_SIZE_PERCENTAGE,\n fixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY,\n minIndicatorDataPoints: DEFAULT_MIN_INDICATOR_DATA_POINTS,\n indicators: [\"sma\", \"ema\", \"rsi\", \"macd\"],\n buyConditions: {\n rsi: { threshold: 30, condition: \"below\" },\n macd: { threshold: 0, condition: \"above\" },\n },\n sellConditions: {\n rsi: { threshold: 70, condition: \"above\" },\n macd: { threshold: 0, condition: \"below\" },\n },\n riskSettings: {\n maxPositionSize: 0.05,\n stopLossPercentage: 0.02,\n takeProfitPercentage: 0.05,\n },\n };\n\n // The actual TI library is now used directly.\n private indicators: typeof ti = ti;\n\n isReady(): boolean {\n return true; // Rule-based strategy is always ready\n }\n\n configure(params: RuleBasedStrategyParams): void {\n console.error(\n \"[RuleBasedStrategy DIAG] configure called. Incoming params:\",\n JSON.stringify(params),\n );\n console.error(\n \"[RuleBasedStrategy DIAG] configure: this.params BEFORE merge:\",\n JSON.stringify(this.params),\n );\n if (!params.rules || params.rules.length === 0) {\n throw new Error(\"At least one rule must be configured.\");\n }\n\n // Validate individual rule parameters first\n params.rules.forEach((rule) => {\n if (rule.rsiPeriod !== undefined && rule.rsiPeriod <= 0)\n throw new Error(\"RSI period must be positive.\");\n if (rule.shortMAPeriod !== undefined && rule.shortMAPeriod <= 0)\n throw new Error(\"Short MA period must be positive.\");\n if (rule.longMAPeriod !== undefined && rule.longMAPeriod <= 0)\n throw new Error(\"Long MA period must be positive.\");\n if (rule.type === \"SMA_CROSSOVER\" || rule.type === \"EMA_CROSSOVER\") {\n if (!rule.shortMAPeriod || !rule.longMAPeriod) {\n throw new Error(\"Short and Long MA periods are required for crossover rules.\");\n }\n if (rule.shortMAPeriod >= rule.longMAPeriod) {\n throw new Error(\"Short MA period must be less than Long MA period for crossovers.\");\n }\n if (!rule.maType) {\n console.warn(\n `[RuleBasedStrategy] maType not specified for crossover rule, defaulting to SMA.`,\n );\n rule.maType = \"SMA\";\n }\n }\n if (rule.rsiOverbought !== undefined && (rule.rsiOverbought <= 0 || rule.rsiOverbought > 100))\n throw new Error(\"RSI overbought must be between 0 and 100\");\n if (rule.rsiOversold !== undefined && (rule.rsiOversold <= 0 || rule.rsiOversold > 100))\n throw new Error(\"RSI oversold must be between 0 and 100\");\n if (rule.rsiOversold && rule.rsiOverbought && rule.rsiOversold >= rule.rsiOverbought)\n throw new Error(\"RSI oversold must be less than RSI overbought\");\n });\n\n // Validate overall strategy parameters from input params\n if (\n params.tradeSizePercentage !== undefined &&\n (params.tradeSizePercentage <= 0 || params.tradeSizePercentage > 1)\n ) {\n throw new Error(\"tradeSizePercentage must be between 0 (exclusive) and 1 (inclusive).\");\n }\n if (params.fixedTradeQuantity !== undefined && params.fixedTradeQuantity <= 0) {\n throw new Error(\"fixedTradeQuantity must be positive.\");\n }\n // Validate minIndicatorDataPoints from input params before merging params\n if (params.minIndicatorDataPoints !== undefined && params.minIndicatorDataPoints < 1) {\n throw new Error(\"minIndicatorDataPoints must be at least 1.\");\n }\n\n const tempParams = { ...this.params, ...params }; // Merge incoming params over current defaults/settings\n\n const currentRulesLongestPeriod = tempParams.rules.reduce(\n (max, r) => Math.max(max, r.longMAPeriod || 0, r.rsiPeriod || 0),\n 0,\n );\n const pointsNeededByCurrentRules =\n currentRulesLongestPeriod > 0 ? currentRulesLongestPeriod + 1 : 1;\n\n if (params.minIndicatorDataPoints !== undefined) {\n if (params.minIndicatorDataPoints < pointsNeededByCurrentRules) {\n console.warn(\n `[RuleBasedStrategy] User-defined minIndicatorDataPoints (${params.minIndicatorDataPoints}) is less than required by current rules (${pointsNeededByCurrentRules}). Adjusting to ${pointsNeededByCurrentRules}.`,\n );\n tempParams.minIndicatorDataPoints = pointsNeededByCurrentRules;\n } else {\n tempParams.minIndicatorDataPoints = params.minIndicatorDataPoints;\n }\n } else {\n tempParams.minIndicatorDataPoints = Math.max(\n DEFAULT_MIN_INDICATOR_DATA_POINTS,\n pointsNeededByCurrentRules,\n );\n }\n\n if (params.indicators) {\n this.params.indicators = params.indicators;\n }\n if (params.buyConditions) {\n this.params.buyConditions = { ...params.buyConditions };\n }\n if (params.sellConditions) {\n this.params.sellConditions = { ...params.sellConditions };\n }\n if (params.riskSettings) {\n this.params.riskSettings = {\n ...this.params.riskSettings,\n ...params.riskSettings,\n };\n }\n\n this.params = tempParams; // Assign fully validated and adjusted params\n console.error(\n \"[RuleBasedStrategy DIAG] configure: this.params AFTER merge and adjustments:\",\n JSON.stringify(this.params),\n );\n }\n\n private calculateIndicators(ohlcvData: OHLCV[], rules: RuleCondition[]): TAResults {\n // No need to check for this.indicators, we are using the imported library directly.\n\n const minDataPointsForAnyCalc = rules.reduce((minOverall, r) => {\n const periodReq = Math.max(r.longMAPeriod || 0, r.rsiPeriod || 0, r.macdSlowPeriod || 0);\n return Math.min(minOverall, periodReq > 0 ? periodReq + 1 : Infinity);\n }, Infinity);\n\n if (\n ohlcvData.length <\n (this.params.minIndicatorDataPoints ?? DEFAULT_MIN_INDICATOR_DATA_POINTS) ||\n ohlcvData.length < minDataPointsForAnyCalc\n ) {\n return {};\n }\n\n const closePrices = ohlcvData.map((d) => d.close);\n const results: TAResults = {};\n\n // --- RSI Calculation ---\n const rsiRule = rules.find((r) => r.type === \"RSI\" && r.rsiPeriod);\n if (rsiRule?.rsiPeriod && closePrices.length >= rsiRule.rsiPeriod) {\n const rsiResult = this.indicators.rsi({\n values: closePrices,\n period: rsiRule.rsiPeriod,\n });\n if (rsiResult.length > 0) {\n results.rsi = rsiResult[rsiResult.length - 1];\n }\n }\n\n // --- MA Crossover Calculation (SMA & EMA) ---\n const crossoverRules = rules.filter(\n (r) =>\n (r.type === \"SMA_CROSSOVER\" || r.type === \"EMA_CROSSOVER\") &&\n r.shortMAPeriod &&\n r.longMAPeriod,\n );\n\n for (const rule of crossoverRules) {\n if (rule.shortMAPeriod && rule.longMAPeriod && closePrices.length >= rule.longMAPeriod + 1) {\n const maIndicator = rule.maType === \"EMA\" ? this.indicators.ema : this.indicators.sma;\n\n const shortMA = maIndicator({\n values: closePrices,\n period: rule.shortMAPeriod,\n });\n const longMA = maIndicator({\n values: closePrices,\n period: rule.longMAPeriod,\n });\n\n if (shortMA.length >= 2 && longMA.length >= 2) {\n if (rule.maType === \"EMA\") {\n results.emaShort = shortMA[shortMA.length - 1];\n results.prevEmaShort = shortMA[shortMA.length - 2];\n results.emaLong = longMA[longMA.length - 1];\n results.prevEmaLong = longMA[longMA.length - 2];\n } else {\n results.smaShort = shortMA[shortMA.length - 1];\n results.prevSmaShort = shortMA[shortMA.length - 2];\n results.smaLong = longMA[longMA.length - 1];\n results.prevSmaLong = longMA[longMA.length - 2];\n }\n }\n }\n }\n\n // --- MACD Calculation ---\n const macdRule = rules.find((r) => r.type === \"MACD_CROSS\");\n if (\n macdRule?.macdFastPeriod &&\n macdRule.macdSlowPeriod &&\n macdRule.macdSignalPeriod &&\n closePrices.length >= macdRule.macdSlowPeriod\n ) {\n const macdResult = this.indicators.macd({\n values: closePrices,\n fastPeriod: macdRule.macdFastPeriod,\n slowPeriod: macdRule.macdSlowPeriod,\n signalPeriod: macdRule.macdSignalPeriod,\n SimpleMAOscillator: false,\n SimpleMASignal: false,\n });\n if (macdResult.length >= 2) {\n results.macd = macdResult[macdResult.length - 1];\n results.prevMacd = macdResult[macdResult.length - 2];\n }\n }\n\n return results;\n }\n\n async decide(params: {\n marketData: StrategyContextMarketData;\n agentState: AgentState;\n portfolioSnapshot: PortfolioSnapshot;\n agentRuntime?: any;\n }): Promise<TradeOrder | null> {\n const { marketData, portfolioSnapshot } = params;\n\n if (!marketData.priceData || marketData.priceData.length < 20) {\n return null; // Not enough data for indicators\n }\n\n const indicators = this.calculateIndicators(marketData.priceData, this.params.rules);\n\n // Check each rule to see if any conditions are met\n let shouldBuy = false;\n let shouldSell = false;\n let buyReason = \"\";\n let sellReason = \"\";\n\n for (const rule of this.params.rules) {\n switch (rule.type) {\n case \"RSI\":\n if (indicators.rsi !== undefined) {\n if (\n rule.rsiOversold &&\n indicators.rsi < rule.rsiOversold &&\n rule.action === TradeType.BUY\n ) {\n shouldBuy = true;\n buyReason = `RSI oversold (${indicators.rsi.toFixed(2)} < ${rule.rsiOversold})`;\n }\n if (\n rule.rsiOverbought &&\n indicators.rsi > rule.rsiOverbought &&\n rule.action === TradeType.SELL\n ) {\n shouldSell = true;\n sellReason = `RSI overbought (${indicators.rsi.toFixed(2)} > ${rule.rsiOverbought})`;\n }\n }\n break;\n case \"VOLUME\":\n // Volume rules are not currently implemented in the indicator calculation\n // so they should not trigger\n break;\n // Add other rule types as needed\n }\n }\n\n // Extract asset from portfolio snapshot or use a default\n const assetSymbol =\n Object.keys(portfolioSnapshot.holdings).find(\n (key) => key !== \"USDC\" && portfolioSnapshot.holdings[key] > 0,\n ) || \"SOL\";\n\n const pair = `${assetSymbol}/USDC`;\n\n // Execute buy if conditions are met\n if (shouldBuy) {\n const cashHolding = portfolioSnapshot.holdings.USDC || 0;\n if (cashHolding > 10 && marketData.currentPrice) {\n const quantity = this.calculateTradeQuantity(marketData, portfolioSnapshot);\n\n if (quantity && quantity > MIN_TRADE_QUANTITY_THRESHOLD) {\n return {\n pair,\n action: TradeType.BUY,\n quantity: parseFloat(quantity.toFixed(8)),\n orderType: OrderType.MARKET,\n timestamp: Date.now(),\n reason: buyReason || \"Technical indicators suggest buy signal\",\n };\n }\n }\n }\n\n // Execute sell if conditions are met\n if (shouldSell) {\n const holding = portfolioSnapshot.holdings[assetSymbol] || 0;\n if (holding > 0) {\n return {\n pair,\n action: TradeType.SELL,\n quantity: holding,\n orderType: OrderType.MARKET,\n timestamp: Date.now(),\n reason: sellReason || \"Technical indicators suggest sell signal\",\n };\n }\n }\n\n return null;\n }\n\n private calculateTradeQuantity(\n marketData: StrategyContextMarketData,\n portfolioSnapshot: PortfolioSnapshot,\n ): number | null {\n // Use USDC balance from portfolio snapshot\n const usdcBalance = portfolioSnapshot.holdings.USDC || 0;\n\n if (\n this.params.tradeSizePercentage &&\n usdcBalance > 0 &&\n marketData.currentPrice &&\n marketData.currentPrice > 0\n ) {\n const capitalToUse = usdcBalance * this.params.tradeSizePercentage;\n const quantity = capitalToUse / marketData.currentPrice;\n if (quantity < MIN_TRADE_QUANTITY_THRESHOLD) {\n return null;\n }\n return quantity;\n } else if (this.params.fixedTradeQuantity && this.params.fixedTradeQuantity > 0) {\n return this.params.fixedTradeQuantity;\n } else {\n // Default to small fixed quantity\n return 0.1;\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ // ../../../node_modules/uuid/dist-node/native.js
2
+ import { randomUUID } from "crypto";
3
+ var native_default = { randomUUID };
4
+
5
+ // ../../../node_modules/uuid/dist-node/rng.js
6
+ import { randomFillSync } from "crypto";
7
+ var rnds8Pool = new Uint8Array(256);
8
+ var poolPtr = rnds8Pool.length;
9
+ function rng() {
10
+ if (poolPtr > rnds8Pool.length - 16) {
11
+ randomFillSync(rnds8Pool);
12
+ poolPtr = 0;
13
+ }
14
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
15
+ }
16
+
17
+ // ../../../node_modules/uuid/dist-node/stringify.js
18
+ var byteToHex = [];
19
+ for (let i = 0; i < 256; ++i) {
20
+ byteToHex.push((i + 256).toString(16).slice(1));
21
+ }
22
+ function unsafeStringify(arr, offset = 0) {
23
+ return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
24
+ }
25
+
26
+ // ../../../node_modules/uuid/dist-node/v4.js
27
+ function _v4(options, buf, offset) {
28
+ options = options || {};
29
+ const rnds = options.random ?? options.rng?.() ?? rng();
30
+ if (rnds.length < 16) {
31
+ throw new Error("Random bytes length must be >= 16");
32
+ }
33
+ rnds[6] = rnds[6] & 15 | 64;
34
+ rnds[8] = rnds[8] & 63 | 128;
35
+ if (buf) {
36
+ offset = offset || 0;
37
+ if (offset < 0 || offset + 16 > buf.length) {
38
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
39
+ }
40
+ for (let i = 0; i < 16; ++i) {
41
+ buf[offset + i] = rnds[i];
42
+ }
43
+ return buf;
44
+ }
45
+ return unsafeStringify(rnds);
46
+ }
47
+ function v4(options, buf, offset) {
48
+ if (native_default.randomUUID && !buf && !options) {
49
+ return native_default.randomUUID();
50
+ }
51
+ return _v4(options, buf, offset);
52
+ }
53
+ var v4_default = v4;
54
+
55
+ export {
56
+ v4_default
57
+ };
58
+ //# sourceMappingURL=chunk-JCQL2EFG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../node_modules/uuid/dist-node/native.js","../../../../node_modules/uuid/dist-node/rng.js","../../../../node_modules/uuid/dist-node/stringify.js","../../../../node_modules/uuid/dist-node/v4.js"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nexport default { randomUUID };\n","import { randomFillSync } from 'node:crypto';\nconst rnds8Pool = new Uint8Array(256);\nlet poolPtr = rnds8Pool.length;\nexport default function rng() {\n if (poolPtr > rnds8Pool.length - 16) {\n randomFillSync(rnds8Pool);\n poolPtr = 0;\n }\n return rnds8Pool.slice(poolPtr, (poolPtr += 16));\n}\n","import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction _v4(options, buf, offset) {\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n return _v4(options, buf, offset);\n}\nexport default v4;\n"],"mappings":";AAAA,SAAS,kBAAkB;AAC3B,IAAO,iBAAQ,EAAE,WAAW;;;ACD5B,SAAS,sBAAsB;AAC/B,IAAM,YAAY,IAAI,WAAW,GAAG;AACpC,IAAI,UAAU,UAAU;AACT,SAAR,MAAuB;AAC1B,MAAI,UAAU,UAAU,SAAS,IAAI;AACjC,mBAAe,SAAS;AACxB,cAAU;AAAA,EACd;AACA,SAAO,UAAU,MAAM,SAAU,WAAW,EAAG;AACnD;;;ACRA,IAAM,YAAY,CAAC;AACnB,SAAS,IAAI,GAAG,IAAI,KAAK,EAAE,GAAG;AAC1B,YAAU,MAAM,IAAI,KAAO,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACpD;AACO,SAAS,gBAAgB,KAAK,SAAS,GAAG;AAC7C,UAAQ,UAAU,IAAI,SAAS,CAAC,CAAC,IAC7B,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,MACA,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,MACA,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,MACA,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,UAAU,IAAI,SAAS,CAAC,CAAC,IACzB,MACA,UAAU,IAAI,SAAS,EAAE,CAAC,IAC1B,UAAU,IAAI,SAAS,EAAE,CAAC,IAC1B,UAAU,IAAI,SAAS,EAAE,CAAC,IAC1B,UAAU,IAAI,SAAS,EAAE,CAAC,IAC1B,UAAU,IAAI,SAAS,EAAE,CAAC,IAC1B,UAAU,IAAI,SAAS,EAAE,CAAC,GAAG,YAAY;AACjD;;;ACvBA,SAAS,IAAI,SAAS,KAAK,QAAQ;AAC/B,YAAU,WAAW,CAAC;AACtB,QAAM,OAAO,QAAQ,UAAU,QAAQ,MAAM,KAAK,IAAI;AACtD,MAAI,KAAK,SAAS,IAAI;AAClB,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACvD;AACA,OAAK,CAAC,IAAK,KAAK,CAAC,IAAI,KAAQ;AAC7B,OAAK,CAAC,IAAK,KAAK,CAAC,IAAI,KAAQ;AAC7B,MAAI,KAAK;AACL,aAAS,UAAU;AACnB,QAAI,SAAS,KAAK,SAAS,KAAK,IAAI,QAAQ;AACxC,YAAM,IAAI,WAAW,mBAAmB,MAAM,IAAI,SAAS,EAAE,0BAA0B;AAAA,IAC3F;AACA,aAAS,IAAI,GAAG,IAAI,IAAI,EAAE,GAAG;AACzB,UAAI,SAAS,CAAC,IAAI,KAAK,CAAC;AAAA,IAC5B;AACA,WAAO;AAAA,EACX;AACA,SAAO,gBAAgB,IAAI;AAC/B;AACA,SAAS,GAAG,SAAS,KAAK,QAAQ;AAC9B,MAAI,eAAO,cAAc,CAAC,OAAO,CAAC,SAAS;AACvC,WAAO,eAAO,WAAW;AAAA,EAC7B;AACA,SAAO,IAAI,SAAS,KAAK,MAAM;AACnC;AACA,IAAO,aAAQ;","names":[]}
@@ -0,0 +1,100 @@
1
+ // src/strategies/RandomStrategy.ts
2
+ var DEFAULT_TRADE_ATTEMPT_PROBABILITY = 0.1;
3
+ var DEFAULT_BUY_PROBABILITY = 0.5;
4
+ var DEFAULT_MAX_TRADE_SIZE_PERCENTAGE = 0.01;
5
+ var DEFAULT_FIXED_TRADE_QUANTITY = 1;
6
+ var MIN_TRADE_QUANTITY_THRESHOLD = 1e-8;
7
+ var RandomStrategy = class {
8
+ id = "random-v1";
9
+ name = "Random Trading Strategy";
10
+ description = "Makes random buy or sell decisions based on configured probabilities.";
11
+ params = {
12
+ tradeAttemptProbability: DEFAULT_TRADE_ATTEMPT_PROBABILITY,
13
+ buyProbability: DEFAULT_BUY_PROBABILITY,
14
+ maxTradeSizePercentage: DEFAULT_MAX_TRADE_SIZE_PERCENTAGE,
15
+ fixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY
16
+ };
17
+ useFixedQuantity = false;
18
+ configure(params) {
19
+ if (params.tradeAttemptProbability !== void 0) {
20
+ if (params.tradeAttemptProbability < 0 || params.tradeAttemptProbability > 1) {
21
+ throw new Error("tradeAttemptProbability must be between 0 and 1.");
22
+ }
23
+ this.params.tradeAttemptProbability = params.tradeAttemptProbability;
24
+ }
25
+ if (params.buyProbability !== void 0) {
26
+ if (params.buyProbability < 0 || params.buyProbability > 1) {
27
+ throw new Error("buyProbability must be between 0 and 1.");
28
+ }
29
+ this.params.buyProbability = params.buyProbability;
30
+ }
31
+ if (params.maxTradeSizePercentage !== void 0) {
32
+ if (params.maxTradeSizePercentage < 0 || params.maxTradeSizePercentage > 1) {
33
+ throw new Error("maxTradeSizePercentage must be between 0 and 1.");
34
+ }
35
+ this.params.maxTradeSizePercentage = params.maxTradeSizePercentage;
36
+ this.useFixedQuantity = false;
37
+ }
38
+ if (params.fixedTradeQuantity !== void 0) {
39
+ if (params.fixedTradeQuantity <= 0) {
40
+ throw new Error("fixedTradeQuantity must be positive.");
41
+ }
42
+ this.params.fixedTradeQuantity = params.fixedTradeQuantity;
43
+ if (params.maxTradeSizePercentage === void 0) {
44
+ this.useFixedQuantity = true;
45
+ }
46
+ }
47
+ }
48
+ isReady() {
49
+ return true;
50
+ }
51
+ async decide(params) {
52
+ const { marketData, portfolioSnapshot } = params;
53
+ if (Math.random() >= (this.params.tradeAttemptProbability ?? DEFAULT_TRADE_ATTEMPT_PROBABILITY)) {
54
+ return null;
55
+ }
56
+ const tradeType = Math.random() < (this.params.buyProbability ?? DEFAULT_BUY_PROBABILITY) ? "BUY" /* BUY */ : "SELL" /* SELL */;
57
+ let quantity;
58
+ if (this.useFixedQuantity && this.params.fixedTradeQuantity && this.params.fixedTradeQuantity > 0) {
59
+ quantity = this.params.fixedTradeQuantity;
60
+ } else if (!this.useFixedQuantity && this.params.maxTradeSizePercentage && portfolioSnapshot.totalValue > 0 && marketData.currentPrice && marketData.currentPrice > 0) {
61
+ const tradeValue = portfolioSnapshot.totalValue * this.params.maxTradeSizePercentage;
62
+ quantity = tradeValue / marketData.currentPrice;
63
+ } else {
64
+ const defaultPercentage = 0.01;
65
+ const tradeValue = portfolioSnapshot.totalValue * defaultPercentage;
66
+ quantity = marketData.currentPrice > 0 ? tradeValue / marketData.currentPrice : 0.01;
67
+ }
68
+ if (!quantity || quantity <= MIN_TRADE_QUANTITY_THRESHOLD) {
69
+ return null;
70
+ }
71
+ const assetSymbol = Object.keys(portfolioSnapshot.holdings).find(
72
+ (key) => key !== "USDC" && portfolioSnapshot.holdings[key] > 0
73
+ ) || "SOL";
74
+ const pair = `${assetSymbol}/USDC`;
75
+ if (tradeType === "SELL" /* SELL */) {
76
+ const holding = portfolioSnapshot.holdings[assetSymbol] || 0;
77
+ if (holding < quantity) {
78
+ return null;
79
+ }
80
+ }
81
+ const roundedQuantity = parseFloat(quantity.toFixed(8));
82
+ if (roundedQuantity <= MIN_TRADE_QUANTITY_THRESHOLD) {
83
+ return null;
84
+ }
85
+ return {
86
+ pair,
87
+ action: tradeType,
88
+ quantity: roundedQuantity,
89
+ orderType: "MARKET" /* MARKET */,
90
+ // Random strategy uses market orders for simplicity
91
+ timestamp: Date.now(),
92
+ reason: "Random trading decision"
93
+ };
94
+ }
95
+ };
96
+
97
+ export {
98
+ RandomStrategy
99
+ };
100
+ //# sourceMappingURL=chunk-KB2EBQUN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strategies/RandomStrategy.ts"],"sourcesContent":["import {\n type AgentState,\n OrderType,\n type PortfolioSnapshot,\n type StrategyContextMarketData,\n type TradeOrder,\n TradeType,\n type TradingStrategy,\n} from \"../types.ts\";\n\nexport interface RandomStrategyParams {\n /**\n * Probability (0.0 to 1.0) of attempting a trade at any given `shouldExecute` call.\n * Default: 0.1 (10% chance)\n */\n tradeAttemptProbability?: number;\n /**\n * Probability (0.0 to 1.0) of the attempted trade being a BUY order. SELL is 1 - this value.\n * Default: 0.5 (50% chance of BUY, 50% chance of SELL)\n */\n buyProbability?: number;\n /**\n * Maximum percentage of available capital to use for a single trade.\n * Expressed as a decimal (e.g., 0.05 for 5%).\n * Default: 0.01 (1% of available capital)\n * Requires `agentState.availableCapital` to be set.\n */\n maxTradeSizePercentage?: number;\n /**\n * Fixed quantity to trade if `maxTradeSizePercentage` is not used or capital is not available.\n * If this is set, `maxTradeSizePercentage` might be ignored or used as a cap.\n * Default: 1 (e.g., 1 unit of the base asset)\n */\n fixedTradeQuantity?: 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 public readonly id = \"random-v1\";\n public readonly name = \"Random Trading Strategy\";\n public readonly description =\n \"Makes random buy or sell decisions based on configured probabilities.\";\n\n private params: RandomStrategyParams = {\n tradeAttemptProbability: DEFAULT_TRADE_ATTEMPT_PROBABILITY,\n buyProbability: DEFAULT_BUY_PROBABILITY,\n maxTradeSizePercentage: DEFAULT_MAX_TRADE_SIZE_PERCENTAGE,\n fixedTradeQuantity: DEFAULT_FIXED_TRADE_QUANTITY,\n };\n\n private useFixedQuantity = false;\n\n configure(params: RandomStrategyParams): void {\n if (params.tradeAttemptProbability !== undefined) {\n if (params.tradeAttemptProbability < 0 || params.tradeAttemptProbability > 1) {\n throw new Error(\"tradeAttemptProbability must be between 0 and 1.\");\n }\n this.params.tradeAttemptProbability = params.tradeAttemptProbability;\n }\n if (params.buyProbability !== undefined) {\n if (params.buyProbability < 0 || params.buyProbability > 1) {\n throw new Error(\"buyProbability must be between 0 and 1.\");\n }\n this.params.buyProbability = params.buyProbability;\n }\n if (params.maxTradeSizePercentage !== undefined) {\n if (params.maxTradeSizePercentage < 0 || params.maxTradeSizePercentage > 1) {\n throw new Error(\"maxTradeSizePercentage must be between 0 and 1.\");\n }\n this.params.maxTradeSizePercentage = params.maxTradeSizePercentage;\n // If percentage is explicitly set, prefer it over fixed quantity\n this.useFixedQuantity = false;\n }\n if (params.fixedTradeQuantity !== undefined) {\n if (params.fixedTradeQuantity <= 0) {\n throw new Error(\"fixedTradeQuantity must be positive.\");\n }\n this.params.fixedTradeQuantity = params.fixedTradeQuantity;\n // Only use fixed quantity if percentage is not explicitly set in this configure call\n if (params.maxTradeSizePercentage === undefined) {\n this.useFixedQuantity = true;\n }\n }\n }\n\n isReady(): boolean {\n return true; // Random strategy is always ready\n }\n\n async decide(params: {\n marketData: StrategyContextMarketData;\n agentState: AgentState;\n portfolioSnapshot: PortfolioSnapshot;\n agentRuntime?: any;\n }): Promise<TradeOrder | null> {\n const { marketData, portfolioSnapshot } = params;\n\n if (\n Math.random() >= (this.params.tradeAttemptProbability ?? DEFAULT_TRADE_ATTEMPT_PROBABILITY)\n ) {\n return null; // No trade attempt this time\n }\n\n const tradeType: TradeType =\n Math.random() < (this.params.buyProbability ?? DEFAULT_BUY_PROBABILITY)\n ? TradeType.BUY\n : TradeType.SELL;\n\n // Calculate quantity\n let quantity: number | undefined;\n if (\n this.useFixedQuantity &&\n this.params.fixedTradeQuantity &&\n this.params.fixedTradeQuantity > 0\n ) {\n // Use fixed quantity when explicitly set\n quantity = this.params.fixedTradeQuantity;\n } else if (\n !this.useFixedQuantity &&\n this.params.maxTradeSizePercentage &&\n portfolioSnapshot.totalValue > 0 &&\n marketData.currentPrice &&\n marketData.currentPrice > 0\n ) {\n // Calculate percentage-based quantity\n const tradeValue = portfolioSnapshot.totalValue * this.params.maxTradeSizePercentage;\n quantity = tradeValue / marketData.currentPrice;\n } else {\n // Default: 1% of portfolio or minimal amount\n const defaultPercentage = 0.01;\n const tradeValue = portfolioSnapshot.totalValue * defaultPercentage;\n quantity = marketData.currentPrice > 0 ? tradeValue / marketData.currentPrice : 0.01;\n }\n\n // Check minimum quantity threshold\n if (!quantity || quantity <= MIN_TRADE_QUANTITY_THRESHOLD) {\n return null;\n }\n\n // Extract asset from portfolio snapshot or use a default\n const assetSymbol =\n Object.keys(portfolioSnapshot.holdings).find(\n (key) => key !== \"USDC\" && portfolioSnapshot.holdings[key] > 0,\n ) || \"SOL\";\n\n const pair = `${assetSymbol}/USDC`;\n\n // For SELL orders, check if we have sufficient holdings\n if (tradeType === TradeType.SELL) {\n const holding = portfolioSnapshot.holdings[assetSymbol] || 0;\n if (holding < quantity) {\n return null;\n }\n }\n\n // Apply precision limit\n const roundedQuantity = parseFloat(quantity.toFixed(8));\n\n // Check again after rounding\n if (roundedQuantity <= MIN_TRADE_QUANTITY_THRESHOLD) {\n return null;\n }\n\n return {\n pair,\n action: tradeType,\n quantity: roundedQuantity,\n orderType: OrderType.MARKET, // Random strategy uses market orders for simplicity\n timestamp: Date.now(),\n reason: \"Random trading decision\",\n };\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
10
+ //# sourceMappingURL=chunk-PZ5AY32C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}