get_ml_rsi
Calculate ML-enhanced RSI values using the K-Nearest Neighbors algorithm, providing adaptive overbought/oversold levels and improved accuracy through pattern recognition on Bybit MCP Server.
Instructions
Get ML-enhanced RSI using K-Nearest Neighbors algorithm for pattern recognition. Provides adaptive overbought/oversold levels and enhanced RSI values based on historical pattern similarity.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | Yes | Category of the instrument | |
| featureCount | No | Number of features to use 1-5 (default: 3) | |
| interval | Yes | Kline interval | |
| knnLookback | No | Historical period for pattern matching (default: 100) | |
| knnNeighbors | No | Number of neighbors for KNN algorithm (default: 5) | |
| limit | No | Number of data points to return (default: 200) | |
| mlWeight | No | ML influence weight 0-1 (default: 0.4) | |
| rsiLength | No | RSI calculation period (default: 14) | |
| smoothingMethod | No | Smoothing method to apply (default: none) | |
| symbol | Yes | Trading pair symbol (e.g., 'BTCUSDT') |
Implementation Reference
- src/tools/GetMLRSI.ts:135-216 (handler)Main handler function that orchestrates the entire get_ml_rsi tool execution: input validation, data fetching, RSI calculation, feature extraction, KNN processing, smoothing, and response formatting.async toolCall(request: z.infer<typeof CallToolRequestSchema>): Promise<CallToolResult> { const startTime = Date.now() try { this.logInfo("Starting get_ml_rsi tool call") // Parse and validate input const validationResult = inputSchema.safeParse(request.params.arguments) if (!validationResult.success) { const errorDetails = validationResult.error.errors.map(err => ({ field: err.path.join('.'), message: err.message, code: err.code })) throw new Error(`Invalid input: ${JSON.stringify(errorDetails)}`) } const args = validationResult.data // Fetch kline data const klineData = await this.fetchKlineData(args) if (klineData.length < args.rsiLength + args.knnLookback) { throw new Error(`Insufficient data. Need at least ${args.rsiLength + args.knnLookback} data points, got ${klineData.length}`) } // Calculate standard RSI const closePrices = klineData.map(k => k.close) const rsiValues = calculateRSI(closePrices, args.rsiLength) if (rsiValues.length === 0) { throw new Error("Failed to calculate RSI values") } // Extract features for all data points const allFeatures: FeatureVector[] = [] for (let i = 0; i < klineData.length; i++) { const features = extractFeatures(klineData, i, rsiValues, args.featureCount, args.knnLookback) allFeatures.push(features || { rsi: rsiValues[i] || 0 }) } // Configure KNN const knnConfig: KNNConfig = { neighbors: args.knnNeighbors, lookbackPeriod: args.knnLookback, mlWeight: args.mlWeight, featureCount: args.featureCount } // Apply KNN enhancement const knnResults = batchProcessKNN(rsiValues, allFeatures, klineData, knnConfig) // Apply smoothing if requested const smoothedResults = this.applySmoothingToResults(knnResults, rsiValues, args.smoothingMethod) // Format response data const responseData = this.formatMLRSIData(klineData, rsiValues, smoothedResults, args.limit) const calculationTime = Date.now() - startTime const response: MLRSIResponse = { symbol: args.symbol, interval: args.interval, data: responseData, metadata: { mlEnabled: true, featuresUsed: this.getFeatureNames(args.featureCount), smoothingApplied: args.smoothingMethod, calculationTime, rsiLength: args.rsiLength, knnConfig } } this.logInfo(`ML-RSI calculation completed in ${calculationTime}ms`) return this.formatResponse(response) } catch (error) { this.logInfo(`ML-RSI calculation failed: ${error instanceof Error ? error.message : String(error)}`) return this.handleError(error) } }
- src/tools/GetMLRSI.ts:23-36 (schema)Zod schema defining and validating the input parameters for the get_ml_rsi tool.const inputSchema = z.object({ symbol: z.string() .min(1, "Symbol is required") .regex(/^[A-Z0-9]+$/, "Symbol must contain only uppercase letters and numbers"), category: z.enum(["spot", "linear", "inverse"]), interval: z.enum(["1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"]), rsiLength: z.number().min(2).max(50).optional().default(14), knnNeighbors: z.number().min(1).max(50).optional().default(5), knnLookback: z.number().min(20).max(500).optional().default(100), mlWeight: z.number().min(0).max(1).optional().default(0.4), featureCount: z.number().min(1).max(5).optional().default(3), smoothingMethod: z.enum(["none", "kalman", "alma", "double_ema"]).optional().default("none"), limit: z.number().min(50).max(1000).optional().default(200) })
- src/tools/GetMLRSI.ts:68-133 (schema)MCP-standard tool definition object containing name, description, and JSON input schema for get_ml_rsi.toolDefinition: Tool = { name: this.name, description: "Get ML-enhanced RSI using K-Nearest Neighbors algorithm for pattern recognition. Provides adaptive overbought/oversold levels and enhanced RSI values based on historical pattern similarity.", inputSchema: { type: "object", properties: { symbol: { type: "string", description: "Trading pair symbol (e.g., 'BTCUSDT')", pattern: "^[A-Z0-9]+$" }, category: { type: "string", description: "Category of the instrument", enum: ["spot", "linear", "inverse"] }, interval: { type: "string", description: "Kline interval", enum: ["1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"] }, rsiLength: { type: "number", description: "RSI calculation period (default: 14)", minimum: 2, maximum: 50 }, knnNeighbors: { type: "number", description: "Number of neighbors for KNN algorithm (default: 5)", minimum: 1, maximum: 50 }, knnLookback: { type: "number", description: "Historical period for pattern matching (default: 100)", minimum: 20, maximum: 500 }, mlWeight: { type: "number", description: "ML influence weight 0-1 (default: 0.4)", minimum: 0, maximum: 1 }, featureCount: { type: "number", description: "Number of features to use 1-5 (default: 3)", minimum: 1, maximum: 5 }, smoothingMethod: { type: "string", description: "Smoothing method to apply (default: none)", enum: ["none", "kalman", "alma", "double_ema"] }, limit: { type: "number", description: "Number of data points to return (default: 200)", minimum: 50, maximum: 1000 } }, required: ["symbol", "category", "interval"] } }
- src/utils/toolLoader.ts:23-81 (registration)Dynamic loader that discovers, imports, instantiates, and validates all tool classes in src/tools (including GetMLRSI), adding valid ones to the tools array.export async function loadTools(): Promise<BaseToolImplementation[]> { try { const toolsPath = await findToolsPath() const files = await fs.readdir(toolsPath) const tools: BaseToolImplementation[] = [] for (const file of files) { if (!isToolFile(file)) { continue } try { const modulePath = `file://${join(toolsPath, file)}` const { default: ToolClass } = await import(modulePath) if (!ToolClass || typeof ToolClass !== 'function') { console.warn(JSON.stringify({ type: "warning", message: `Invalid tool class in ${file}` })) continue } const tool = new ToolClass() if ( tool instanceof BaseToolImplementation && tool.name && tool.toolDefinition && typeof tool.toolCall === "function" ) { tools.push(tool) console.info(JSON.stringify({ type: "info", message: `Loaded tool: ${tool.name}` })) } else { console.warn(JSON.stringify({ type: "warning", message: `Invalid tool implementation in ${file}` })) } } catch (error) { console.error(JSON.stringify({ type: "error", message: `Error loading tool from ${file}: ${error instanceof Error ? error.message : String(error)}` })) } } return tools } catch (error) { console.error(JSON.stringify({ type: "error", message: `Failed to load tools: ${error instanceof Error ? error.message : String(error)}` })) return [] } }
- src/index.ts:129-152 (registration)Initializes the MCP server, loads all tools via toolLoader (including get_ml_rsi), creates toolsMap for dispatch, sets up ListTools and CallTool request handlers that use the loaded tools.async function main() { try { // Validate environment configuration validateEnv() const tools = await loadTools() toolsMap = createToolsMap(tools) if (tools.length === 0) { console.log(JSON.stringify(formatJsonRpcMessage( "warning", "No tools were loaded. Server will start but may have limited functionality." ))) } else { console.log(JSON.stringify(formatJsonRpcMessage( "info", `Loaded ${tools.length} tools: ${tools.map(t => t.name).join(", ")}` ))) } const transport = new StdioServerTransport() await server.connect(transport) console.log(JSON.stringify(formatJsonRpcMessage(