199 lines
5.7 KiB
TypeScript
199 lines
5.7 KiB
TypeScript
import { BaseSkill, type SkillInput, type SkillResult, type SkillMetadata } from './base-skill.js';
|
|
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
import type { FastifyBaseLogger } from 'fastify';
|
|
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
|
|
|
/**
|
|
* Market analysis skill implementation
|
|
*
|
|
* See market-analysis.skill.md for full documentation
|
|
*/
|
|
export class MarketAnalysisSkill extends BaseSkill {
|
|
constructor(logger: FastifyBaseLogger, model?: BaseChatModel) {
|
|
super(logger, model);
|
|
}
|
|
|
|
getMetadata(): SkillMetadata {
|
|
return {
|
|
name: 'market-analysis',
|
|
description: 'Analyze market conditions for a given ticker and timeframe',
|
|
version: '1.0.0',
|
|
author: 'Dexorder AI Platform',
|
|
tags: ['market-data', 'analysis', 'trading'],
|
|
};
|
|
}
|
|
|
|
getParametersSchema(): Record<string, unknown> {
|
|
return {
|
|
type: 'object',
|
|
required: ['ticker', 'period'],
|
|
properties: {
|
|
ticker: {
|
|
type: 'string',
|
|
description: 'Market identifier (e.g., "BINANCE:BTC/USDT")',
|
|
},
|
|
period: {
|
|
type: 'string',
|
|
enum: ['1h', '4h', '1d', '1w'],
|
|
description: 'Analysis period',
|
|
},
|
|
startTime: {
|
|
type: 'number',
|
|
description: 'Start timestamp in microseconds',
|
|
},
|
|
endTime: {
|
|
type: 'number',
|
|
description: 'End timestamp in microseconds',
|
|
},
|
|
indicators: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
description: 'Additional indicators to include',
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
validateInput(parameters: Record<string, unknown>): boolean {
|
|
if (!parameters.ticker || typeof parameters.ticker !== 'string') {
|
|
return false;
|
|
}
|
|
if (!parameters.period || typeof parameters.period !== 'string') {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async execute(input: SkillInput): Promise<SkillResult> {
|
|
this.logStart(input);
|
|
|
|
if (!this.validateInput(input.parameters)) {
|
|
return this.error('Invalid parameters: ticker and period are required');
|
|
}
|
|
|
|
try {
|
|
const ticker = input.parameters.ticker as string;
|
|
const period = input.parameters.period as string;
|
|
const indicators = (input.parameters.indicators as string[]) || [];
|
|
|
|
// 1. Fetch OHLC data from Iceberg
|
|
// TODO: Implement Iceberg query
|
|
// const ohlcData = await this.fetchOHLCData(ticker, period, startTime, endTime);
|
|
const ohlcData = this.getMockOHLCData(); // Placeholder
|
|
|
|
// 2. Calculate technical indicators
|
|
const analysis = this.calculateAnalysis(ohlcData, indicators);
|
|
|
|
// 3. Generate natural language analysis using LLM
|
|
let narrativeAnalysis = '';
|
|
if (this.model) {
|
|
narrativeAnalysis = await this.generateNarrativeAnalysis(
|
|
ticker,
|
|
period,
|
|
analysis
|
|
);
|
|
}
|
|
|
|
const result = this.success({
|
|
ticker,
|
|
period,
|
|
timeRange: {
|
|
start: ohlcData.startTime,
|
|
end: ohlcData.endTime,
|
|
},
|
|
trend: analysis.trend,
|
|
priceChange: analysis.priceChange,
|
|
volumeProfile: analysis.volumeProfile,
|
|
supportLevels: analysis.supportLevels,
|
|
resistanceLevels: analysis.resistanceLevels,
|
|
indicators: analysis.indicators,
|
|
analysis: narrativeAnalysis,
|
|
});
|
|
|
|
this.logEnd(result);
|
|
return result;
|
|
} catch (error) {
|
|
const result = this.error(error as Error);
|
|
this.logEnd(result);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate technical analysis from OHLC data
|
|
*/
|
|
private calculateAnalysis(
|
|
ohlcData: any,
|
|
_requestedIndicators: string[]
|
|
): any {
|
|
// TODO: Implement proper technical analysis
|
|
// This is a simplified placeholder
|
|
|
|
const priceChange = ((ohlcData.close - ohlcData.open) / ohlcData.open) * 100;
|
|
const trend = priceChange > 1 ? 'bullish' : priceChange < -1 ? 'bearish' : 'neutral';
|
|
|
|
return {
|
|
trend,
|
|
priceChange,
|
|
volumeProfile: {
|
|
average: ohlcData.avgVolume,
|
|
recent: ohlcData.currentVolume,
|
|
trend: ohlcData.currentVolume > ohlcData.avgVolume ? 'increasing' : 'decreasing',
|
|
},
|
|
supportLevels: [ohlcData.low * 0.98, ohlcData.low * 0.95],
|
|
resistanceLevels: [ohlcData.high * 1.02, ohlcData.high * 1.05],
|
|
indicators: {},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generate natural language analysis using LLM
|
|
*/
|
|
private async generateNarrativeAnalysis(
|
|
ticker: string,
|
|
period: string,
|
|
analysis: any
|
|
): Promise<string> {
|
|
if (!this.model) {
|
|
return 'LLM not available for narrative analysis';
|
|
}
|
|
|
|
const systemPrompt = `You are a professional market analyst.
|
|
Provide concise, actionable market analysis based on technical data.
|
|
Focus on key insights and avoid jargon.`;
|
|
|
|
const userPrompt = `Analyze the following market data for ${ticker} (${period}):
|
|
|
|
Trend: ${analysis.trend}
|
|
Price Change: ${analysis.priceChange.toFixed(2)}%
|
|
Volume: ${analysis.volumeProfile.trend}
|
|
Support Levels: ${analysis.supportLevels.join(', ')}
|
|
Resistance Levels: ${analysis.resistanceLevels.join(', ')}
|
|
|
|
Provide a 2-3 sentence analysis suitable for a trading decision.`;
|
|
|
|
const response = await this.model.invoke([
|
|
new SystemMessage(systemPrompt),
|
|
new HumanMessage(userPrompt),
|
|
]);
|
|
|
|
return response.content as string;
|
|
}
|
|
|
|
/**
|
|
* Mock OHLC data (placeholder until Iceberg integration)
|
|
*/
|
|
private getMockOHLCData(): any {
|
|
return {
|
|
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000,
|
|
endTime: Date.now(),
|
|
open: 50000,
|
|
high: 52000,
|
|
low: 49000,
|
|
close: 51500,
|
|
avgVolume: 1000000,
|
|
currentVolume: 1200000,
|
|
};
|
|
}
|
|
}
|