find me all the errors i have here


import pandas as pd
import numpy as np
import yfinance as yf
import ta
import logging
from datetime import datetime, timedelta
from dataclasses import dataclass
import argparse
from scipy.stats import linregress
import matplotlib.pyplot as plt
import multiprocessing as mp
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import TimeSeriesSplit
import joblib
from itertools import product
import numba
from numba import njit
from numba.typed import List

# Set up logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler("trading_bot.log"),
        logging.StreamHandler()
    ]
)

@dataclass
class StrategyParams:
    basis_type: str
    basis_len: int
    offset_sigma: int     # Only used for ALMA
    offset_alma: float    # Only used for ALMA
    offset_lsma: int      # Only used for LSMA
    stop_loss: float
    take_profit: float
    use_res: bool
    int_res: int
    delay_offset: int
    trade_type: str
    timeframe: str

    def get_param_dict(self):
        """Convert parameters to dictionary for easy storage/comparison"""
        return {
            'basis_type': self.basis_type,
            'basis_len': self.basis_len,
            'offset_sigma': self.offset_sigma if self.basis_type == 'ALMA' else None,
            'offset_alma': self.offset_alma if self.basis_type == 'ALMA' else None,
            'offset_lsma': self.offset_lsma if self.basis_type == 'LSMA' else None,
            'stop_loss': self.stop_loss,
            'take_profit': self.take_profit
        }

@njit
def linregress_numba(x, y):
    """Numba-accelerated linear regression."""
    n = len(x)
    x_mean = np.mean(x)
    y_mean = np.mean(y)
    cov_xy = np.sum((x - x_mean) * (y - y_mean))
    var_x = np.sum((x - x_mean) ** 2)
    slope = cov_xy / var_x
    intercept = y_mean - slope * x_mean
    return slope, intercept

@njit
def lsma_numba(src_values, length, offset=0):
    """Numba-accelerated Least Squares Moving Average with offset."""
    lsma_values = np.full(len(src_values), np.nan)
    for i in range(length - 1, len(src_values)):
        y = src_values[i - length + 1:i + 1]
        x = np.arange(len(y))
        slope, intercept = linregress_numba(x, y)
        pred_point = (length - 1) + offset
        lsma_values[i] = slope * pred_point + intercept
    return lsma_values

def lsma(src, length, offset=0):
    """Least Squares Moving Average with offset using Numba."""
    lsma_values = lsma_numba(src.values, length, offset)
    return pd.Series(lsma_values, index=src.index)

@njit
def alma_numba(src_values, length, sigma=6, offset=0.85):
    """Numba-accelerated Arnaud Legoux Moving Average."""
    m = offset * (length - 1)
    s = length / sigma
    w = np.exp(-0.5 * ((np.arange(length) - m) / s) ** 2)
    w /= np.sum(w)
    alma_series = np.convolve(src_values, w, mode='valid')
    alma_padded = np.concatenate((np.full(length - 1, np.nan), alma_series))
    return alma_padded

def alma(src, length, sigma=6, offset=0.85):
    """Arnaud Legoux Moving Average using Numba."""
    alma_padded = alma_numba(src.values, length, sigma, offset)
    return pd.Series(alma_padded, index=src.index)

def variant_ma(basis_type, src, length, offset_sigma, offset_alma, offset_lsma=0):
    """Calculate various types of moving averages."""
    if basis_type == 'SMA':
        return ta.trend.sma_indicator(src, window=length)
    elif basis_type == 'EMA':
        return ta.trend.ema_indicator(src, window=length)
    elif basis_type == 'TEMA':
        ema1 = ta.trend.ema_indicator(src, window=length)
        ema2 = ta.trend.ema_indicator(ema1, window=length)
        return ta.trend.ema_indicator(ema2, window=length)
    elif basis_type == 'HullMA':
        half_length = int(length / 2)
        sqrt_length = int(np.sqrt(length))
        wma_half = ta.trend.wma_indicator(src, window=half_length)
        wma_full = ta.trend.wma_indicator(src, window=length)
        hull_ma = ta.trend.wma_indicator(2 * wma_half - wma_full, window=sqrt_length)
        return hull_ma
    elif basis_type == 'LSMA':
        return lsma(src, length, offset=offset_lsma)
    elif basis_type == 'ALMA':
        return alma(src, length, sigma=offset_sigma, offset=offset_alma)
    else:
        raise ValueError(f"Unknown moving average type: {basis_type}")

def get_resample_rule(timeframe, int_res):
    """Convert timeframe to proper resample rule using updated pandas offset aliases."""
    timeframe_map = {
        '1m': 'T',
        '2m': 'T',
        '5m': 'T',
        '15m': 'T',
        '30m': 'T',
        '1h': 'h',
        '4h': 'h',
        '1d': 'D',
        '1wk': 'W'
    }

    import re
    match = re.match(r'(\d+)([mhd])', timeframe.lower())
    if match:
        num, unit = match.groups()
        base_unit = timeframe_map.get(timeframe, 'D')
        return f"{int(num) * int_res}{base_unit}"
    return f"{int_res}D"

def apply_strategy(data, params: StrategyParams):
    """Apply the trading strategy to the data."""
    logging.info(f"Calculating {params.basis_type} moving averages with period {params.basis_len}")

    # Only pass offset_lsma when using LSMA
    offset_lsma = params.offset_lsma if params.basis_type == 'LSMA' else 0

    data['Close_MA'] = variant_ma(params.basis_type, data['Close'],
                                  params.basis_len, params.offset_sigma,
                                  params.offset_alma, offset_lsma)
    data['Open_MA'] = variant_ma(params.basis_type, data['Open'],
                                 params.basis_len, params.offset_sigma,
                                 params.offset_alma, offset_lsma)

    if params.use_res:
        resample_rule = get_resample_rule(params.timeframe, params.int_res)
        resampled_data = data.resample(resample_rule).agg({
            'Open': 'first',
            'High': 'max',
            'Low': 'min',
            'Close': 'last',
            'Volume': 'sum',
            'Close_MA': 'last',
            'Open_MA': 'last'
        }).ffill()

        data['Close_MA_Alt'] = resampled_data['Close_MA'].reindex(data.index, method='ffill')
        data['Open_MA_Alt'] = resampled_data['Open_MA'].reindex(data.index, method='ffill')
    else:
        data['Close_MA_Alt'] = data['Close_MA']
        data['Open_MA_Alt'] = data['Open_MA']

    data['Signal'] = 0
    data.loc[data['Close_MA_Alt'] > data['Open_MA_Alt'], 'Signal'] = 1
    data.loc[data['Close_MA_Alt'] < data['Open_MA_Alt'], 'Signal'] = -1

    return data

def get_signal_delay(timeframe):
    """Get appropriate signal delay for the timeframe."""
    delay_map = {
        '1m': timedelta(minutes=1),
        '2m': timedelta(minutes=2),
        '5m': timedelta(minutes=5),
        '15m': timedelta(minutes=15),
        '30m': timedelta(minutes=30),
        '1h': timedelta(hours=1),
        '4h': timedelta(hours=4),
        '1d': timedelta(days=1)
    }
    return delay_map.get(timeframe, timedelta(days=1))

@njit
def backtest_numba(close_prices, signals, params_stop_loss, params_take_profit):
    """Numba-accelerated backtesting logic."""
    position = 0
    entry_price = 0.0
    stop_loss_price = 0.0
    take_profit_price = 0.0
    profit_loss = List()
    trades = List()
    wins = 0
    total_trades = 0

    for i in range(1, len(close_prices)):
        signal = signals[i - 1]
        price = close_prices[i]

        if position == 0:
            if signal == 1 and (params_trade_type in [1, 3]):  # LONG or BOTH
                position = 1
                entry_price = price
                stop_loss_price = entry_price * (1 - params_stop_loss / 100)
                take_profit_price = entry_price * (1 + params_take_profit / 100)
                total_trades += 1
            elif signal == -1 and (params_trade_type in [2, 3]):  # SHORT or BOTH
                position = -1
                entry_price = price
                stop_loss_price = entry_price * (1 + params_stop_loss / 100)
                take_profit_price = entry_price * (1 - params_take_profit / 100)
                total_trades += 1

        elif position == 1:
            if price >= take_profit_price:
                profit_loss.append(take_profit_price - entry_price)
                wins += 1
                position = 0
            elif price <= stop_loss_price:
                profit_loss.append(stop_loss_price - entry_price)
                position = 0

        elif position == -1:
            if price <= take_profit_price:
                profit_loss.append(entry_price - take_profit_price)
                wins += 1
                position = 0
            elif price >= stop_loss_price:
                profit_loss.append(entry_price - stop_loss_price)
                position = 0

    total_profit = np.sum(np.array(list(profit_loss)))
    win_rate = (wins / total_trades * 100) if total_trades > 0 else 0

    return total_profit, win_rate, total_trades

def backtest(data, params: StrategyParams):
    """Perform backtesting of the strategy using Numba."""
    global params_trade_type
    if params.trade_type == 'LONG':
        params_trade_type = 1
    elif params.trade_type == 'SHORT':
        params_trade_type = 2
    else:
        params_trade_type = 3  # BOTH

    close_prices = data['Close'].values
    signals = data['Signal'].values.astype(np.int8)

    total_profit, win_rate, total_trades = backtest_numba(
        close_prices, signals, params.stop_loss, params.take_profit
    )

    return total_profit, win_rate, total_trades

def fetch_data(ticker, start, end, timeframe):
    """Fetch data from Yahoo Finance with proper timeframe handling."""
    valid_intervals = ['1m', '2m', '5m', '15m', '30m', '60m', '90m',
                       '1h', '1d', '5d', '1wk', '1mo', '3mo']

    if timeframe not in valid_intervals:
        raise ValueError(f"Invalid interval: {timeframe}. Must be one of {valid_intervals}")

    if timeframe in ['1m', '2m', '5m', '15m', '30m', '60m', '90m']:
        end_date = pd.Timestamp(end)
        start_date = pd.Timestamp(start)

        if (end_date - start_date).days > 7:
            logging.warning("Yahoo Finance limits intraday data to 7 days. Adjusting start date.")
            start = (end_date - pd.Timedelta(days=7)).strftime('%Y-%m-%d')

    try:
        data = yf.download(ticker, start=start, end=end, interval=timeframe)
        if data.empty:
            raise ValueError(f"No data found for {ticker}")
        return data
    except Exception as e:
        logging.error(f"Error fetching data for {ticker}: {e}")
        return None

def evaluate_params(args):
    """Evaluate a single parameter combination."""
    params, data, timeframe = args
    basis_type, basis_len, offset_sigma, offset_alma, offset_lsma, stop_loss, take_profit = params

    strategy_params = StrategyParams(
        basis_type=basis_type,
        basis_len=basis_len,
        offset_sigma=offset_sigma if basis_type == 'ALMA' else 6,
        offset_alma=offset_alma if basis_type == 'ALMA' else 0.85,
        offset_lsma=offset_lsma if basis_type == 'LSMA' else 0,
        stop_loss=stop_loss,
        take_profit=take_profit,
        use_res=True,
        int_res=2,
        delay_offset=0,
        trade_type='BOTH',
        timeframe=timeframe
    )

    try:
        strategy_data = apply_strategy(data.copy(), strategy_params)
        total_profit, _, _ = backtest(strategy_data, strategy_params)
        return (params, total_profit)
    except Exception as e:
        logging.error(f"Error evaluating params {params}: {e}")
        return (params, -np.inf)

def optimize_with_brute_force(data, ticker, timeframe):
    """Run brute-force parameter optimization with conditional parameters."""
    from itertools import product

    # Define base parameter grids
    basis_types = ['SMA', 'EMA', 'TEMA', 'HullMA', 'LSMA', 'ALMA']
    basis_lens = range(10, 51, 10)  # 10, 20, 30, 40, 50
    stop_losses = [round(x * 0.5, 1) for x in range(1, 11)]  # 0.5 to 5.0
    take_profits = [round(x * 0.5, 1) for x in range(1, 11)]  # 0.5 to 5.0

    # Create parameter combinations for each MA type separately
    parameter_combinations = []

    # Basic MAs (SMA, EMA, TEMA, HullMA)
    basic_mas = ['SMA', 'EMA', 'TEMA', 'HullMA']
    for ma_type in basic_mas:
        params = product(
            [ma_type],
            basis_lens,
            [6],  # dummy offset_sigma
            [0.85],  # dummy offset_alma
            [0],  # dummy offset_lsma
            stop_losses,
            take_profits
        )
        parameter_combinations.extend(params)

    # ALMA specific parameters
    offset_sigmas = range(1, 6)      # 1 to 5
    offset_almas = [round(x * 0.1, 1) for x in range(1, 11)]  # 0.1 to 1.0
    alma_params = product(
        ['ALMA'],
        basis_lens,
        offset_sigmas,
        offset_almas,
        [0],  # dummy offset_lsma
        stop_losses,
        take_profits
    )
    parameter_combinations.extend(alma_params)

    # LSMA specific parameters
    offset_lsmas = range(-5, 6)      # -5 to 5
    lsma_params = product(
        ['LSMA'],
        basis_lens,
        [6],  # dummy offset_sigma
        [0.85],  # dummy offset_alma
        offset_lsmas,
        stop_losses,
        take_profits
    )
    parameter_combinations.extend(lsma_params)

    logging.info(f"Total parameter combinations to evaluate: {len(parameter_combinations)}")

    # Prepare arguments for the worker function
    eval_args = [(params, data, timeframe) for params in parameter_combinations]

    # Utilize multiprocessing Pool
    n_cores = max(1, mp.cpu_count() - 1)
    logging.info(f"Using {n_cores} cores for parallel processing")

    with mp.Pool(n_cores) as pool:
        results = pool.map(evaluate_params, eval_args)

    # Find the best parameters
    best_params, best_profit = max(results, key=lambda x: x[1])

    logging.info(f"Best Profit: {best_profit} with parameters: {best_params}")

    return best_params, best_profit

def calculate_portfolio_metrics(all_results, initial_capital=10000):
    """Calculate portfolio metrics including percentage returns."""
    total_profit = sum(r['total_profit'] for r in all_results)
    percentage_return = (total_profit / initial_capital) * 100

    return {
        'total_profit': total_profit,
        'percentage_return': percentage_return,
        'avg_win_rate': np.mean([r['win_rate'] for r in all_results]),
        'total_trades': sum(r['total_trades'] for r in all_results)
    }

def plot_backtest(data, params: StrategyParams, ticker, total_profit, win_rate):
    """Plot backtest results."""
    plt.figure(figsize=(15, 8))

    # Plot price and moving averages
    plt.plot(data.index, data['Close'], label='Close Price', alpha=0.7)
    plt.plot(data.index, data['Close_MA'], label=f'Close {params.basis_type}', alpha=0.7)
    plt.plot(data.index, data['Open_MA'], label=f'Open {params.basis_type}', alpha=0.7)

    # Plot signals
    buy_signals = data[data['Signal'] == 1]
    sell_signals = data[data['Signal'] == -1]

    plt.scatter(buy_signals.index, buy_signals['Close'],
                marker='^', color='green', label='Buy Signal', alpha=0.7)
    plt.scatter(sell_signals.index, sell_signals['Close'],
                marker='v', color='red', label='Sell Signal', alpha=0.7)

    plt.title(f'Backtest Results for {ticker}\n'
              f'Strategy: {params.basis_type}, Return: {total_profit:.2f}%, '
              f'Win Rate: {win_rate:.2f}%')
    plt.xlabel('Date')
    plt.ylabel('Price')
    plt.legend()
    plt.grid(True)
    plt.tight_layout()
    plt.show()

def print_summary(all_results, initial_capital=10000):
    """Print comprehensive summary with percentage returns."""
    metrics = calculate_portfolio_metrics(all_results, initial_capital)

    logging.info("\n" + "=" * 80)
    logging.info("BACKTEST SUMMARY FOR ALL TICKERS")
    logging.info("=" * 80)

    # Print individual performance
    logging.info("\nIndividual Performance:")
    logging.info(f"{'Ticker':<10} {'Return %':<12} {'Win Rate':<12} {'Trades':<8} {'Params':<40}")
    logging.info("-" * 80)

    for result in sorted(all_results, key=lambda x: x['total_profit'], reverse=True):
        return_pct = (result['total_profit'] / (initial_capital / len(all_results))) * 100
        params = result['params'].get_param_dict()
        param_str = f"{params['basis_type']}({params['basis_len']})"

        # Add specific parameters based on MA type
        if params['basis_type'] == 'ALMA':
            param_str += f" σ={params['offset_sigma']} α={params['offset_alma']:.2f}"
        elif params['basis_type'] == 'LSMA':
            param_str += f" offset={params['offset_lsma']}"

        logging.info(f"{result['ticker']:<10} "
                     f"{return_pct:>8.2f}%   "
                     f"{result['win_rate']:>8.2f}%   "
                     f"{result['total_trades']:>6}   "
                     f"{param_str:<40}")

    # Print portfolio summary
    logging.info("\nPortfolio Summary:")
    logging.info("-" * 80)
    logging.info(f"Total Portfolio Return: {metrics['percentage_return']:.2f}%")
    logging.info(f"Average Win Rate: {metrics['avg_win_rate']:.2f}%")
    logging.info(f"Total Number of Trades: {metrics['total_trades']}")

    # Print best parameters
    best_result = max(all_results, key=lambda x: x['total_profit'])
    logging.info("\nBest Strategy Parameters:")
    logging.info("-" * 80)
    best_params = best_result['params'].get_param_dict()
    for param, value in best_params.items():
        if value is not None:  # Only print relevant parameters
            logging.info(f"{param}: {value}")

def main():
    parser = argparse.ArgumentParser(description="Trading Bot with Multi-Timeframe Support")
    parser.add_argument('-t', '--tickers', nargs='+', required=True,
                        help='List of tickers to analyze')
    parser.add_argument('-s', '--start', required=True,
                        help='Start date (YYYY-MM-DD)')
    parser.add_argument('-e', '--end', required=True,
                        help='End date (YYYY-MM-DD)')
    parser.add_argument('-tf', '--timeframe',
                        choices=['1m', '2m', '5m', '15m', '30m', '1h', '4h', '1d'],
                        default='1d',
                        help='Trading timeframe')
    parser.add_argument('-o', '--optimize', action='store_true',
                        help='Run parameter optimization')
    parser.add_argument('--no-plot', action='store_true',
                        help='Disable plotting')
    parser.add_argument('--initial-capital', type=float, default=10000,
                        help='Initial capital for percentage calculations')

    args = parser.parse_args()
    all_results = []

    # Process all tickers
    for ticker in args.tickers:
        logging.info(f"\nProcessing {ticker}")
        logging.info("=" * 50)

        try:
            data = fetch_data(ticker, args.start, args.end, args.timeframe)
            if data is None or data.empty:
                continue

            if args.optimize:
                best_params_tuple, best_profit = optimize_with_brute_force(data, ticker, args.timeframe)

                # Unpack best_params_tuple
                basis_type, basis_len, offset_sigma, offset_alma, offset_lsma, stop_loss, take_profit = best_params_tuple

                strategy_params = StrategyParams(
                    basis_type=basis_type,
                    basis_len=basis_len,
                    offset_sigma=offset_sigma if basis_type == 'ALMA' else 6,
                    offset_alma=offset_alma if basis_type == 'ALMA' else 0.85,
                    offset_lsma=offset_lsma if basis_type == 'LSMA' else 0,
                    stop_loss=stop_loss,
                    take_profit=take_profit,
                    use_res=True,
                    int_res=2,
                    delay_offset=0,
                    trade_type='BOTH',
                    timeframe=args.timeframe
                )
            else:
                strategy_params = StrategyParams(
                    basis_type='EMA',
                    basis_len=20,
                    offset_sigma=6,
                    offset_alma=0.85,
                    offset_lsma=0,
                    stop_loss=2.0,
                    take_profit=4.0,
                    use_res=True,
                    int_res=2,
                    delay_offset=0,
                    trade_type='BOTH',
                    timeframe=args.timeframe
                )

            # Apply strategy and run backtest
            strategy_data = apply_strategy(data.copy(), strategy_params)
            total_profit, win_rate, total_trades = backtest(strategy_data, strategy_params)

            # Calculate percentage return based on allocated capital
            allocated_capital = args.initial_capital / len(args.tickers)
            percentage_return = (total_profit / allocated_capital) * 100

            # Store results
            result = {
                'ticker': ticker,
                'timeframe': args.timeframe,
                'total_profit': percentage_return,  # Store as percentage
                'win_rate': win_rate,
                'total_trades': total_trades,
                'params': strategy_params,
                'trades': []  # Trades are not stored in this example
            }
            all_results.append(result)

            if not args.no_plot:
                plot_backtest(strategy_data, strategy_params, ticker, percentage_return, win_rate)

        except Exception as e:
            logging.error(f"Error processing {ticker}: {str(e)}")
            continue

    # Print final summary with percentage returns
    if all_results:
        print_summary(all_results, args.initial_capital)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logging.info("\nExecution interrupted by user")
    except Exception as e:
        logging.error(f"Unexpected error: {str(e)}")
    finally:
        logging.info("\nExecution completed")