Этот код можно запустить в Jupyter Lab

```
import numpy as np
import pandas as pd
import pywt
from pymongo import MongoClient
from multiprocessing import Pool, cpu_count
from functools import partial
from tqdm import tqdm

# Предварительная настройка
MONGO_USERNAME = "root"
MONGO_PASSWORD = "example"
MONGO_HOST = "mongodb"          
MONGO_PORT = 27017 
MONGO_URI = f"mongodb://{MONGO_USERNAME}:{MONGO_PASSWORD}@{MONGO_HOST}:{MONGO_PORT}/admin"

mongo_client = MongoClient(MONGO_URI)
db = mongo_client['cwt']

ROWS_FROM_CWT = [0, 5, 10, 20, 30, 40, 50, 70, 90, 100, 140, 180]
WAVELETS = ["gaus1", "gaus2", "gaus4", "gaus5", "gaus8", "mexh", "morl"]
SCALE = np.arange(1, 196)
DATA_POINTS = [100, 500]

STEP_BETWEEN_IDX = {
    "15m": 10,
    "30m": 8,
    "1h": 4,
    "2h": 2,
    "4h": 1,
    "1d": 1
}

# Функции для расчёта
def calculate_percent_change(close):
    return (close[-1] - close[0]) / close[0] * 100

def normalize_array(arr):
    return (arr - np.mean(arr)) / np.std(arr)

def prepare_data(df):
    dates = df['Date'].values
    closes = df['Close'].values
    return dates, closes

def calculate_cwt_data(idx, data_points, wavelet_name, scales, closes, dates, rows_from_cwt):
    close_slice = closes[idx:idx + data_points]
    percent_change = calculate_percent_change(close_slice)
    stat_date = dates[idx]
    end_date = dates[idx + data_points - 1]
    
    # Выполняем непрерывное вейвлет-преобразование для обновленных данных
    coefficients, _ = pywt.cwt(close_slice, scales, wavelet_name)

    # Нормализация данных
    coefficients = normalize_array(coefficients)

    # Отбираем только нужные строки
    selected_coeffs = coefficients[rows_from_cwt].tolist()

    document = {
        "_id": f"{idx}_{wavelet_name}_{data_points}",
        "stat_date": stat_date,
        "end_date": end_date,
        "percent_change": percent_change,
        "rows_from_cwt": rows_from_cwt,
        "coefficients": selected_coeffs
    }
    return document

def process_wavelet_collection(timeframe, wavelet_name, data_points, df_dates, df_closes, scales, rows_from_cwt, step):
    collection = db[f'cwt_{timeframe}_{wavelet_name}_{data_points}']
    amount_of_steps = len(df_closes) - data_points
    indices = range(0, amount_of_steps, step)
    
    # Используем пул процессов для параллельных вычислений
    with Pool(processes=cpu_count()) as pool:
        func = partial(
            calculate_cwt_data,
            data_points=data_points,
            wavelet_name=wavelet_name,
            scales=scales,
            closes=df_closes,
            dates=df_dates,
            rows_from_cwt=rows_from_cwt
        )
        results = list(tqdm(pool.imap(func, indices), total=len(indices)))
    
    # Вставка батчами
    batch_size = 1000
    for i in range(0, len(results), batch_size):
        batch = results[i:i + batch_size]
        collection.insert_many(batch, ordered=False)

def main(ohlcv_df, TIMEFRAMES):
    for timeframe in TIMEFRAMES:
        df = ohlcv_df[timeframe]
        dates, closes = prepare_data(df)
        
        for wavelet_name in WAVELETS:
            for data_points in DATA_POINTS:
                print("-"*20, f"{timeframe} {wavelet_name} {data_points}", "-"*20, end="\n"*2)
                
                step = STEP_BETWEEN_IDX.get(timeframe, 1)
                
                process_wavelet_collection(
                    timeframe,
                    wavelet_name,
                    data_points,
                    dates,
                    closes,
                    SCALE,
                    ROWS_FROM_CWT,
                    step
                )

if __name__ == "__main__":
    # Пример загрузки данных
    # ohlcv_df = {
    #     "1h": pd.DataFrame({
    #         "Date": pd.date_range(start="2023-01-01", periods=10000, freq="H"),
    #         "Close": np.random.random(10000)
    #     }),
    #     # Добавьте остальные timeframe
    # }
    TIMEFRAMES = ["1h", "15m", "30m", "2h", "4h", "1d"]
    main(ohlcv_df, TIMEFRAMES)
```