Bot & Automation
Treding Forex Ai
/root/hermes-projects/Treding Forex Ai
src/services/storage/jsonStore.ts
text
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import type { ExecutedTrade, OrderIntent, WatchlistEntry } from "../../domain/types";
interface StoreShape {
pendingOrders: OrderIntent[];
paperTrades: ExecutedTrade[];
watchlists: WatchlistEntry[];
}
export class JsonStore {
public constructor(private readonly dataDir: string) {}
public async savePendingOrder(order: OrderIntent): Promise<void> {
const store = await this.readStore();
const nextOrders = store.pendingOrders.filter((item) => item.id !== order.id).concat(order);
await this.writeStore({ ...store, pendingOrders: nextOrders });
}
public async getPendingOrder(orderId: string): Promise<OrderIntent | null> {
const store = await this.readStore();
return store.pendingOrders.find((order) => order.id === orderId) ?? null;
}
public async removePendingOrder(orderId: string): Promise<void> {
const store = await this.readStore();
await this.writeStore({
...store,
pendingOrders: store.pendingOrders.filter((order) => order.id !== orderId)
});
}
public async appendPaperTrade(trade: ExecutedTrade): Promise<void> {
const store = await this.readStore();
await this.writeStore({
...store,
paperTrades: store.paperTrades.concat(trade)
});
}
public async listOpenPaperTrades(chatId: number): Promise<ExecutedTrade[]> {
const store = await this.readStore();
return store.paperTrades.filter(
(trade) => trade.chatId === chatId && trade.status === "executed" && !trade.closedAt
);
}
public async listTodayPaperTrades(chatId: number): Promise<ExecutedTrade[]> {
const today = new Date().toISOString().slice(0, 10);
const store = await this.readStore();
return store.paperTrades.filter(
(trade) => trade.chatId === chatId && trade.executedAt.startsWith(today)
);
}
public async listAllOpenTrades(): Promise<ExecutedTrade[]> {
const store = await this.readStore();
return store.paperTrades.filter(
(trade) => trade.status === "executed" && !trade.closedAt
);
}
public async closeTrade(
orderId: string,
closePrice: number,
pnl: number
): Promise<ExecutedTrade | null> {
const store = await this.readStore();
const index = store.paperTrades.findIndex((trade) => trade.id === orderId);
if (index === -1) {
return null;
}
const updated: ExecutedTrade = {
...store.paperTrades[index]!,
closedAt: new Date().toISOString(),
closePrice,
pnl
};
const nextTrades = [...store.paperTrades];
nextTrades[index] = updated;
await this.writeStore({ ...store, paperTrades: nextTrades });
return updated;
}
public async addWatchlist(chatId: number, symbolCode: string): Promise<void> {
const store = await this.readStore();
const exists = store.watchlists.some(
(entry) => entry.chatId === chatId && entry.symbolCode === symbolCode
);
if (exists) {
return;
}
await this.writeStore({
...store,
watchlists: store.watchlists.concat({
chatId,
symbolCode,
addedAt: new Date().toISOString()
})
});
}
public async listWatchlist(chatId: number): Promise<WatchlistEntry[]> {
const store = await this.readStore();
return store.watchlists.filter((entry) => entry.chatId === chatId);
}
private async readStore(): Promise<StoreShape> {
await mkdir(this.dataDir, { recursive: true });
const path = this.storePath();
try {
const raw = await readFile(path, "utf8");
return JSON.parse(raw) as StoreShape;
} catch {
return {
pendingOrders: [],
paperTrades: [],
watchlists: []
};
}
}
private async writeStore(store: StoreShape): Promise<void> {
await mkdir(this.dataDir, { recursive: true });
await writeFile(this.storePath(), JSON.stringify(store, null, 2), "utf8");
}
private storePath(): string {
return join(this.dataDir, "store.json");
}
}