npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ghostfolio-finance-agent

v1.0.3

Published

LangGraph-powered finance agent for Ghostfolio. Drop-in AI layer for natural language portfolio queries, market data, tax estimates, and trade execution with conversation memory.

Readme

ghostfolio-finance-agent

A LangGraph-powered AI agent for natural language portfolio queries. Built on top of Ghostfolio but usable with any portfolio backend via the ports/adapters pattern.

npm version

Live demo: https://ghostfolio-agent-production-f5ad.up.railway.app
Eval dataset: evals/ — 55 cases, CI-ready


Features

  • Natural language portfolio queries via LangGraph ReAct agent
  • 8 tools: portfolio analysis, market data, transaction categorization, tax estimates, compliance checks, news sentiment, trade confirm, trade execute
  • Tiered LLM routing — Claude Haiku for simple lookups, Sonnet for complex reasoning
  • Redis-backed conversation memory (multi-turn)
  • Multi-layer caching: adapter (90s), router (5 min in-memory), LLM response (1h), COL/FX/news (Redis, 30/15 min)
  • Streaming responses via async generator (SSE-ready)
  • Confidence scoring + citations on every response
  • PII-safe LangSmith tracing
  • Ports/adapters architecture — bring your own data backend

Installation

npm install ghostfolio-finance-agent

Peer dependencies

npm install @langchain/anthropic @langchain/core @langchain/langgraph @langchain/langgraph-checkpoint-redis redis zod

Quick Start

1. Implement the port interfaces

The agent has no knowledge of your database or API. You implement three interfaces that tell it how to fetch data:

import type { IPortfolioPort, IMarketDataPort, IOrdersPort } from 'ghostfolio-finance-agent';

class MyPortfolioPort implements IPortfolioPort {
  async getDetails(params) {
    // fetch from your backend
    return { holdings: { ... }, hasErrors: false };
  }
  async getPerformance(params) {
    return { performance: { netPerformance: 1234, totalInvestment: 50000 } };
  }
}

class MyMarketDataPort implements IMarketDataPort {
  async getQuotes(identifiers) { ... }
  async getHistoricalData(symbol, from, to) { ... }
}

class MyOrdersPort implements IOrdersPort {
  async getOrders(params) { ... }
  async createOrder(params) { ... }
}

2. Run a query

import { runAgentGraph } from 'ghostfolio-finance-agent';
import { createClient } from 'redis';

const result = await runAgentGraph(
  'How is my portfolio performing this year?',
  'thread-id-123',
  {
    portfolioPort: new MyPortfolioPort(),
    marketDataPort: new MyMarketDataPort(),
    ordersPort: new MyOrdersPort(),
  },
  'user-id-456',
  { baseCurrency: 'USD' }
);

console.log(result.message);
// "Your portfolio has returned 12.4% year-to-date, with your largest
//  gain coming from AAPL (+28%). Your total portfolio value is $187,450."

console.log(result.confidence); // 0.92
console.log(result.toolCalls);  // [{ name: 'portfolio_analysis', success: true, durationMs: 841 }]

3. Stream a response

import { streamAgentGraph } from 'ghostfolio-finance-agent';

for await (const event of streamAgentGraph(query, threadId, ports, userId, config)) {
  if (event.type === 'token') process.stdout.write(event.content);
  if (event.type === 'tool_start') console.log(`Calling ${event.toolName}...`);
  if (event.type === 'done') console.log('\nFinal:', event.result);
}

Using Individual Tools

You can use tools standalone without the full agent:

import { createPortfolioAnalysisTool, createMarketDataTool } from 'ghostfolio-finance-agent';

const portfolioTool = createPortfolioAnalysisTool(portfolioPort, 'user-123', 'USD');
const result = await portfolioTool.invoke({ includePerformance: true });

HTTP Adapter Example (Ghostfolio backend)

If you're connecting to a running Ghostfolio instance, implement the ports as HTTP clients:

import axios from 'axios';
import type { IPortfolioPort } from 'ghostfolio-finance-agent';

class GhostfolioPortfolioPort implements IPortfolioPort {
  constructor(private baseUrl: string, private jwt: string) {}

  private get headers() {
    return { Authorization: `Bearer ${this.jwt}` };
  }

  async getDetails(params) {
    const { data } = await axios.get(
      `${this.baseUrl}/api/v1/portfolio/details`,
      { params, headers: this.headers }
    );
    return data;
  }

  async getPerformance(params) {
    const { data } = await axios.get(
      `${this.baseUrl}/api/v2/portfolio/performance`,
      { params, headers: this.headers }
    );
    return data;
  }
}

API Reference

runAgentGraph(query, threadId, ports, userId, config)

Runs the full ReAct agent loop and returns a structured result.

| Param | Type | Description | |---|---|---| | query | string | Natural language question | | threadId | string | Unique thread ID for conversation memory | | ports | AgentDataPorts | Your port implementations | | userId | string | User identifier | | config | AgentGraphConfig | Optional config (baseCurrency, hasPriorContext) |

Returns AgentRunResult:

{
  message: string;
  toolCalls: ToolCallRecord[];
  citations: CitationRecord[];
  confidence: number;      // 0.0–1.0
  warnings: string[];
  tokenUsage: TokenUsage;
  newConversationId: string;
}

streamAgentGraph(query, threadId, ports, userId, config)

Async generator that yields StreamEvent objects as the agent works:

type StreamEvent =
  | { type: 'tool_start'; toolName: string }
  | { type: 'tool_end'; toolName: string; success: boolean }
  | { type: 'token'; content: string }
  | { type: 'done'; result: AgentRunResult }
  | { type: 'error'; error: string };

Port interfaces

  • IPortfolioPortgetDetails(), getPerformance()
  • IMarketDataPortgetQuotes(), getHistoricalData()
  • IOrdersPortgetOrders(), createOrder()

Setup

1. Get an Anthropic API key

The agent uses Claude as its LLM. You need your own key — the package does not include one.

  1. Sign up at console.anthropic.com
  2. Go to API Keys → Create Key
  3. Copy the key (starts with sk-ant-api03-...)

Cost estimate: A typical portfolio query costs $0.001–$0.005. Simple queries use Claude Haiku (~$0.0008/query); complex multi-step queries use Claude Sonnet (~$0.005/query). See Anthropic pricing.

2. Set up Redis

Redis is required for conversation memory (multi-turn chat). You have several options:

Local (development):

docker run -d -p 6379:6379 redis/redis-stack-server:latest
# REDIS_URL=redis://localhost:6379

Railway (recommended for production): Add a Redis Stack service to your Railway project. Use the public TCP URL from the Railway dashboard.

Important: Use redis-stack-server, not plain redis-server. The LangGraph checkpoint library requires the JSON and Search modules that only ship with Redis Stack.

Upstash / Redis Cloud: Any Redis 6+ instance works. Use the full connection URL including credentials:

REDIS_URL=redis://:yourpassword@your-host:6379

3. Configure environment variables

Create a .env file (never commit this):

# Required
ANTHROPIC_API_KEY=sk-ant-api03-...     # From console.anthropic.com
REDIS_URL=redis://localhost:6379        # Your Redis connection URL

# Optional — LangSmith tracing (free tier available)
LANGSMITH_API_KEY=lsv2_pt_...          # From smith.langchain.com
LANGSMITH_TRACING_ENABLED=true

# Optional — conversation TTL
AGENT_MEMORY_TTL_DAYS=7                # How long to keep conversation history

# Optional — trading (see docs/TRADING_SAFETY.md)
ORDER_DATA_SOURCE=MANUAL               # MANUAL = any symbol (stocks + ETFs) accepted by Ghostfolio
TRADE_MAX_VALUE=50000                  # Max single order value in USD

Environment variable reference

| Variable | Required | Description | |---|---|---| | ANTHROPIC_API_KEY | Yes | Claude API key from console.anthropic.com | | REDIS_URL | Yes | Redis Stack connection URL for conversation memory | | LANGSMITH_API_KEY | No | LangSmith tracing key from smith.langchain.com | | LANGSMITH_TRACING_ENABLED | No | Set to true to enable LangSmith tracing (default: false) | | AGENT_MEMORY_TTL_DAYS | No | Days to retain conversation history in Redis (default: 7) | | ORDER_DATA_SOURCE | No | Data source for order creation (default: MANUAL). MANUAL lets any symbol (ETFs + stocks) work; Ghostfolio must have MANUAL in DATA_SOURCES. See TRADING_SAFETY.md. | | TRADE_MAX_VALUE | No | Max single order value in USD (default: 50000). Orders above this are rejected. |


Caching

The NestJS deployment uses several caches to reduce latency and API cost:

| Layer | Storage | TTL | Key / scope | |-------|---------|-----|-------------| | Ghostfolio adapter | Redis | 90s | agent:adapter:{kind}:{userId}:{hash(params)} — portfolio details/performance, market quotes/historical, orders list | | Query router | In-memory | 5 min | Normalized query string → complexity + tool set (saves one Haiku call per repeated query) | | LLM response | Redis | 1h | agent:llm:{sha256(conversationId\|query)} — full assistant result for non-streaming chat() only | | Cost-of-living / exchange rate | Redis | 30 min | agent:cache:col:*, agent:cache:exchange-rate:latest — shared across instances | | News sentiment | Redis | 15 min | agent:cache:news:{symbol}:{limit} |

All caches are optional from a correctness perspective: on Redis errors or misses, the agent falls back to live calls. The package library (runAgentGraph / streamAgentGraph) does not include the adapter or LLM caches; those are part of the NestJS AgentService in this repo.


Eval Dataset

A 55-case evaluation dataset is published in evals/:

# Validate dataset (no API calls)
npx ts-node evals/run-evals.ts --dry-run

# Run against your agent (CI gate: exits 1 if <80% pass)
npx ts-node evals/run-evals.ts --endpoint https://your-agent.com --token $JWT

Full Deployment

For a complete NestJS + Next.js deployment of this agent as a standalone service, see the deployment guide or fork this repo and deploy to Railway.


License

MIT