Загрузка данных


function normalizeCandleTime(candle: Candle): Candle {
  return {
    ...candle,
    time: Math.floor(
      candle.time > 1e10 ? candle.time / 1000 : candle.time,
    ),
  };
}





public getDataSource =
  (
    indicativeData?: ChartIndicativeData,
    cb?: (timeframe: Timeframes) => void,
  ) =>
  async (
    timeframe: Timeframes,
    symbolId: string,
    until?: Candle,
  ): Promise<Candle[] | null> => {
    const symbol = getRequestSymbol(symbolId);

    if (!symbol) {
      return null;
    }

    cb?.(timeframe);

    const interval = moexChartTimeConverter(timeframe);
    const cursor = until ? normalizeCandleTime(until) : undefined;
    const countBack = 2000;

    const to = cursor
      ? cursor.time - 1
      : Math.floor(Date.now() / 1000);

    const from = Math.max(
      0,
      to - countBack * getTimeframeSeconds(timeframe),
    );

    const data = await requestBars({
      currencyPair: symbol.replaceAll(':', '.'),
      interval,
      periodParams: {
        firstDataRequest: !cursor,
        from,
        to,
        countBack,
      },
      ticker: symbol,
      indicativeData,
    });

    const validData = data
      .map(normalizeCandleTime)
      .filter(isValidCandle)
      .filter((candle) => !cursor || candle.time < cursor.time)
      .sort((first, second) => first.time - second.time);

    if (validData.length === 0) {
      return null;
    }

    if (moexChartToIssTimeframe(timeframe) === timeframe) {
      if (!until) {
        const lastCandle = validData.at(-1);

        this.realtimeStates.set(symbol, {
          previousTime: lastCandle?.time ?? null,
          candles: lastCandle ? [lastCandle] : [],
          sessionStart: null,
        });
      }

      return validData;
    }

    const { candles, state } = timeframeConvolution(
      validData,
      timeframe,
    );

    if (!until) {
      this.realtimeStates.set(symbol, state);
    }

    return candles;
  };






private async updateRealtimeSymbol(
  symbolId: string,
  timeframe: Timeframes,
  update: (symbolId: string, candle: Candle) => void,
  indicativeData?: ChartIndicativeData,
): Promise<void> {
  const symbol = getRequestSymbol(symbolId);

  if (!symbol) {
    return;
  }

  const response = await requestRealtimeBars({
    currencyPair: symbol.replaceAll(':', '.'),
    interval: moexChartTimeConverter(timeframe),
    ticker: symbol,
    indicativeData,
  });

  if (!response) {
    return;
  }

  const data = normalizeCandleTime(response);

  if (!isValidCandle(data)) {
    return;
  }

  const currentState = this.realtimeStates.get(symbol) ?? {
    previousTime: null,
    candles: [],
    sessionStart: null,
  };

  const currentCandle = currentState.candles.find(
    (candle) => candle.time === data.time,
  );

  if (isSameCandle(currentCandle, data)) {
    return;
  }

  if (moexChartToIssTimeframe(timeframe) === timeframe) {
    this.realtimeStates.set(symbol, {
      previousTime: data.time,
      candles: [data],
      sessionStart: null,
    });

    update(symbol, data);

    return;
  }

  const { candle, state } = realtimeConvolution(
    timeframe,
    data,
    currentState,
  );

  this.realtimeStates.set(symbol, state);

  if (candle) {
    update(symbol, candle);
  }
}