113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
/* TRADINGGRAPH related file */
|
|
|
|
import { OpenRouterClient } from "../lib/openrouter";
|
|
import { FundamentalsAnalyst } from "./fundamentals";
|
|
import { TechnicalAnalyst } from "./technical";
|
|
import { SentimentAnalyst } from "./sentiment";
|
|
import { BullishResearcher, BearishResearcher } from "./researchers";
|
|
import { Trader } from "./trader";
|
|
import type { AnalystReport, DebateRound, TradingDecision, AgentSignal, ExecutionPlan } from "../types/agents";
|
|
|
|
export interface GraphStep {
|
|
step: "analysts" | "debate" | "trader" | "execution";
|
|
data: AnalystReport[] | DebateRound[] | TradingDecision | ExecutionPlan;
|
|
}
|
|
|
|
export class TradingGraph {
|
|
private client: OpenRouterClient;
|
|
private model: string;
|
|
private fundamentalsAnalyst: FundamentalsAnalyst;
|
|
private technicalAnalyst: TechnicalAnalyst;
|
|
private sentimentAnalyst: SentimentAnalyst;
|
|
private bullishResearcher: BullishResearcher;
|
|
private bearishResearcher: BearishResearcher;
|
|
private trader: Trader;
|
|
|
|
constructor(client: OpenRouterClient, model?: string) {
|
|
this.client = client;
|
|
this.model = model ?? "openai/gpt-oss-120b:free";
|
|
|
|
this.fundamentalsAnalyst = new FundamentalsAnalyst(client, { model: this.model });
|
|
this.technicalAnalyst = new TechnicalAnalyst(client, { model: this.model });
|
|
this.sentimentAnalyst = new SentimentAnalyst(client, { model: this.model });
|
|
this.bullishResearcher = new BullishResearcher(client, this.model);
|
|
this.bearishResearcher = new BearishResearcher(client, this.model);
|
|
this.trader = new Trader(client, this.model);
|
|
}
|
|
|
|
async propagate(
|
|
ticker: string,
|
|
input: {
|
|
financialData: string;
|
|
technicalData: { prices: number[]; sma: number; ema: number; rsi: number; macd: number };
|
|
sentimentData: { headlines: string[]; source?: "news" | "social" | "stocktwits" };
|
|
}
|
|
): Promise<TradingDecision> {
|
|
|
|
|
|
const reports = await this.runAnalysts(ticker, input);
|
|
const debates = await this.runDebate(ticker, reports);
|
|
const decision = await this.trader.decide(ticker, reports, debates);
|
|
|
|
|
|
|
|
|
|
// Build workflow steps for observability. Include an execution step when selling.
|
|
const steps: GraphStep[] = [
|
|
{ step: "analysts", data: reports },
|
|
{ step: "debate", data: debates },
|
|
{ step: "trader", data: decision },
|
|
];
|
|
|
|
if (decision.executionPlan) {
|
|
steps.push({ step: "execution", data: decision.executionPlan });
|
|
|
|
}
|
|
|
|
// Log steps for debugging; external systems can be extended to consume GraphStep sequence.
|
|
|
|
|
|
return decision;
|
|
}
|
|
|
|
private async runAnalysts(
|
|
ticker: string,
|
|
input: {
|
|
financialData: string;
|
|
technicalData: { prices: number[]; sma: number; ema: number; rsi: number; macd: number };
|
|
sentimentData: { headlines: string[]; source?: "news" | "social" | "stocktwits" };
|
|
}
|
|
): Promise<AnalystReport[]> {
|
|
|
|
|
|
const [fundamentals, technical, sentiment] = await Promise.all([
|
|
this.fundamentalsAnalyst.analyze(ticker, input.financialData),
|
|
this.technicalAnalyst.analyze(ticker, input.technicalData),
|
|
this.sentimentAnalyst.analyze(ticker, input.sentimentData),
|
|
]);
|
|
|
|
|
|
|
|
return [fundamentals, technical, sentiment];
|
|
}
|
|
|
|
private async runDebate(ticker: string, reports: AnalystReport[]): Promise<DebateRound[]> {
|
|
|
|
|
|
const [bullish, bearish] = await Promise.all([
|
|
this.bullishResearcher.research(ticker, reports),
|
|
this.bearishResearcher.research(ticker, reports),
|
|
]);
|
|
|
|
|
|
|
|
return [
|
|
{
|
|
bullishView: bullish.bullishView,
|
|
bearishView: bearish.bearishView,
|
|
researcher: "bullish",
|
|
},
|
|
];
|
|
}
|
|
}
|