feat: add sentiment analyst agent
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { OpenRouterClient } from "../lib/openrouter";
|
||||
import type { AnalystReport, AgentSignal, SignalType } from "../types/agents";
|
||||
|
||||
export interface SentimentConfig {
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface SentimentData {
|
||||
headlines: string[];
|
||||
source?: "news" | "social" | "stocktwits";
|
||||
}
|
||||
|
||||
export class SentimentAnalyst {
|
||||
private client: OpenRouterClient;
|
||||
private model: string;
|
||||
|
||||
constructor(client: OpenRouterClient, config?: SentimentConfig) {
|
||||
this.client = client;
|
||||
this.model = config?.model ?? "google/gemini-2.0-flash-exp:free";
|
||||
}
|
||||
|
||||
getModel(): string {
|
||||
return this.model;
|
||||
}
|
||||
|
||||
async analyze(ticker: string, data: SentimentData): Promise<AnalystReport> {
|
||||
const messages = [
|
||||
{
|
||||
role: "system" as const,
|
||||
content:
|
||||
"You are a sentiment analyst. Analyze the headlines for the given ticker and provide a bullish, bearish, or neutral signal with reasoning. Respond in JSON format with 'signal', 'confidence', and 'reasoning' fields.",
|
||||
},
|
||||
{
|
||||
role: "user" as const,
|
||||
content: `Analyze ${ticker} sentiment from ${data.source ?? "news"}:\n${data.headlines.join("\n")}`,
|
||||
},
|
||||
];
|
||||
|
||||
const response = await this.client.createChatCompletion(
|
||||
messages,
|
||||
this.model
|
||||
);
|
||||
|
||||
const parsedResponse = response as {
|
||||
choices?: Array<{ message?: { content?: string } }>;
|
||||
};
|
||||
const content = parsedResponse.choices?.[0]?.message?.content ?? "";
|
||||
|
||||
let signal: SignalType = "neutral";
|
||||
let confidence = 0.5;
|
||||
let reasoning = content;
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(content);
|
||||
if (
|
||||
parsed.signal === "bullish" ||
|
||||
parsed.signal === "bearish" ||
|
||||
parsed.signal === "neutral"
|
||||
) {
|
||||
signal = parsed.signal;
|
||||
confidence = parsed.confidence ?? 0.5;
|
||||
reasoning = parsed.reasoning ?? content;
|
||||
}
|
||||
} catch {
|
||||
const lowerContent = content.toLowerCase();
|
||||
if (lowerContent.includes("bullish")) signal = "bullish";
|
||||
else if (lowerContent.includes("bearish")) signal = "bearish";
|
||||
}
|
||||
|
||||
const agentSignal: AgentSignal = {
|
||||
agent: "sentiment",
|
||||
signal,
|
||||
confidence,
|
||||
reasoning,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
return {
|
||||
analyst: "sentiment",
|
||||
report: content,
|
||||
signal: agentSignal,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user