Adds a stock data cache layer and indicator helpers (MA/RSI/MACD), expands the MarketDataProvider interface with candles, fundamentals, related-data, and quant analysis, and threads tags + source attribution through the shared types. Updates affected agent tests. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { WatchlistStore } from "../src/agent/watchlistStore";
|
|
|
|
let tempDir: string | undefined;
|
|
|
|
afterEach(async () => {
|
|
if (tempDir) {
|
|
await rm(tempDir, { force: true, recursive: true });
|
|
tempDir = undefined;
|
|
}
|
|
});
|
|
|
|
describe("WatchlistStore", () => {
|
|
it("adds normalized stocks once and persists them", async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "stock-agent-"));
|
|
const path = join(tempDir, "watchlist.json");
|
|
const store = new WatchlistStore(path);
|
|
|
|
await store.add("aapl", "Apple", ["Core", " AI ", "Core"]);
|
|
await store.add("AAPL", "Duplicate");
|
|
await store.add("00700.HK", "Tencent");
|
|
|
|
expect(await store.list()).toEqual([
|
|
expect.objectContaining({ display: "AAPL", market: "US", name: "Apple", tags: ["Core", "AI"] }),
|
|
expect.objectContaining({ display: "00700.HK", market: "HK", name: "Tencent", tags: [] })
|
|
]);
|
|
|
|
const reloaded = new WatchlistStore(path);
|
|
expect(await reloaded.list()).toHaveLength(2);
|
|
});
|
|
|
|
it("removes stocks by any supported symbol form", async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "stock-agent-"));
|
|
const store = new WatchlistStore(join(tempDir, "watchlist.json"));
|
|
|
|
await store.add("600519", "Kweichow Moutai");
|
|
await store.remove("600519.SS");
|
|
|
|
expect(await store.list()).toEqual([]);
|
|
});
|
|
|
|
it("updates tags for a persisted stock", async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "stock-agent-"));
|
|
const store = new WatchlistStore(join(tempDir, "watchlist.json"));
|
|
|
|
await store.add("MSFT", "Microsoft");
|
|
await store.updateTags("MSFT.US", ["cloud", "quality"]);
|
|
|
|
expect((await store.list())[0]).toEqual(expect.objectContaining({ tags: ["cloud", "quality"] }));
|
|
});
|
|
|
|
it("canonicalizes persisted China ETF exchange suffixes", async () => {
|
|
tempDir = await mkdtemp(join(tmpdir(), "stock-agent-"));
|
|
const store = new WatchlistStore(join(tempDir, "watchlist.json"));
|
|
|
|
await store.add("512480.SZ", "Semiconductor ETF");
|
|
|
|
expect((await store.list())[0]).toEqual(expect.objectContaining({ display: "512480.SS", id: "512480.SS" }));
|
|
});
|
|
});
|