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");
  }
}