1Branch0Tags
GL
glucryptoFix exact Hyperliquid symbols
typescript
import { clampHyperliquidFloat, clampHyperliquidInt, } from "opentool/adapters/hyperliquid"; import { DEFAULT_BB_PERIOD, DEFAULT_BB_STD_DEV, DEFAULT_DONCHIAN_PERIOD, DEFAULT_EMA_PERIOD, DEFAULT_MA_CROSS_FAST, DEFAULT_MA_CROSS_SLOW, DEFAULT_RSI_OVERBOUGHT, DEFAULT_RSI_OVERSOLD, DEFAULT_RSI_PRESET, DEFAULT_SMA_PERIOD, LIMITS, RSI_PRESETS, type IndicatorType, type SignalBotConfig, } from "../config"; import { computeBollinger } from "../indicators/computeBollinger"; import { computeDonchian } from "../indicators/computeDonchian"; import { computeEma } from "../indicators/computeEma"; import { computeMacd } from "../indicators/computeMacd"; import { computeMaCross } from "../indicators/computeMaCross"; import { computeRsi } from "../indicators/computeRsi"; import { computeSma } from "../indicators/computeSma"; import type { Bar } from "../types"; export function resolveRsiThresholds(config: SignalBotConfig) { const priceConfig = config.price ?? {}; const rsiPreset = priceConfig.rsiPreset ?? DEFAULT_RSI_PRESET; const rsiDefaults = RSI_PRESETS[rsiPreset] ?? RSI_PRESETS[DEFAULT_RSI_PRESET]; const overbought = clampHyperliquidFloat( priceConfig.rsi?.overbought, 1, 100, rsiDefaults?.overbought ?? DEFAULT_RSI_OVERBOUGHT, ); const oversold = clampHyperliquidFloat( priceConfig.rsi?.oversold, 1, 100, rsiDefaults?.oversold ?? DEFAULT_RSI_OVERSOLD, ); return { rsiPreset, overbought, oversold }; } export function buildIndicatorDecisionOutput(params: { config: SignalBotConfig; bars: Bar[]; indicator: IndicatorType; }): Record<string, unknown> { const { config, bars, indicator } = params; const closes = bars.map((bar) => bar.close); const currentPrice = closes[closes.length - 1] ?? 0; const output: Record<string, unknown> = {}; const priceConfig = config.price ?? {}; if (indicator === "rsi") { const { overbought, oversold } = resolveRsiThresholds(config); const value = computeRsi(closes); const signal = value === null ? "unknown" : value >= overbought ? "overbought" : value <= oversold ? "oversold" : "neutral"; output.rsi = { value, signal, overbought, oversold }; return output; } if (indicator === "macd") { const result = computeMacd(closes); const signal = result === null ? "unknown" : result.histogram > 0 ? "bullish" : result.histogram < 0 ? "bearish" : "neutral"; output.macd = result ? { ...result, signal } : { macd: null, signalLine: null, histogram: null, signal }; return output; } if (indicator === "bb") { const period = clampHyperliquidInt( priceConfig.bollinger?.period, LIMITS.bollinger.periodMin, LIMITS.bollinger.periodMax, DEFAULT_BB_PERIOD, ); const stdDev = clampHyperliquidFloat( priceConfig.bollinger?.stdDev, LIMITS.bollinger.stdDevMin, LIMITS.bollinger.stdDevMax, DEFAULT_BB_STD_DEV, ); const result = computeBollinger(closes, period, stdDev); const signal = result === null ? "unknown" : currentPrice > result.upper ? "overbought" : currentPrice < result.lower ? "oversold" : "neutral"; output.bb = result ? { ...result, signal, period, stdDev } : { upper: null, middle: null, lower: null, signal, period, stdDev }; return output; } if (indicator === "sma") { const period = clampHyperliquidInt( priceConfig.movingAverage?.type === "sma" ? priceConfig.movingAverage?.period : DEFAULT_SMA_PERIOD, LIMITS.movingAverage.min, LIMITS.movingAverage.max, DEFAULT_SMA_PERIOD, ); const value = computeSma(closes, period); const signal = value === null ? "unknown" : currentPrice > value ? "above" : currentPrice < value ? "below" : "at"; output.sma = { value, period, signal }; return output; } if (indicator === "ema") { const period = clampHyperliquidInt( priceConfig.movingAverage?.type === "ema" ? priceConfig.movingAverage?.period : DEFAULT_EMA_PERIOD, LIMITS.movingAverage.min, LIMITS.movingAverage.max, DEFAULT_EMA_PERIOD, ); const value = computeEma(closes, period); const signal = value === null ? "unknown" : currentPrice > value ? "above" : currentPrice < value ? "below" : "at"; output.ema = { value, period, signal }; return output; } if (indicator === "ma-cross") { const type = priceConfig.maCross?.type ?? "sma"; const slowPeriod = clampHyperliquidInt( priceConfig.maCross?.slowPeriod, LIMITS.maCross.slowMin, LIMITS.maCross.slowMax, DEFAULT_MA_CROSS_SLOW, ); const fastCandidate = clampHyperliquidInt( priceConfig.maCross?.fastPeriod, LIMITS.maCross.fastMin, LIMITS.maCross.fastMax, DEFAULT_MA_CROSS_FAST, ); const fastPeriod = fastCandidate >= slowPeriod ? Math.max( LIMITS.maCross.fastMin, Math.min(slowPeriod - 1, LIMITS.maCross.fastMax), ) : fastCandidate; const result = computeMaCross(closes, type, fastPeriod, slowPeriod); output.maCross = result ? { ...result, type, fastPeriod, slowPeriod } : { type, fastPeriod, slowPeriod, fast: null, slow: null, signal: "unknown", }; return output; } const period = clampHyperliquidInt( priceConfig.donchian?.period, LIMITS.donchian.min, LIMITS.donchian.max, DEFAULT_DONCHIAN_PERIOD, ); const result = computeDonchian(bars, period); const signal = result === null ? "unknown" : currentPrice > result.upper ? "bullish" : currentPrice < result.lower ? "bearish" : "neutral"; output.donchian = result ? { ...result, period, signal } : { upper: null, lower: null, period, signal }; return output; } export function buildSignalIndicatorsOutput(config: SignalBotConfig, bars: Bar[]) { const output: Record<string, unknown> = {}; for (const indicator of config.indicators) { Object.assign( output, buildIndicatorDecisionOutput({ config, bars, indicator, }), ); } return output; }