import os
import threading
import time
import django
# Set the environment variable to point to your project’s settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'binancebot.settings')

# Initialize Django
django.setup()

from concurrent.futures import ProcessPoolExecutor, as_completed
import ccxt
import pandas as pd
import numpy as np
import talib as ta
from django.utils import timezone
from .models import Signal
import logging
from multiprocessing import Process, Event
from loguru import logger

# Configure logging
logging.basicConfig(level=logging.DEBUG)
#logger.add("trading_bot.log", rotation="1 MB", compression="zip", retention="10 days")

class SignalGenerator:
    def __init__(self, symbol='BTC/USDT', timeframe='1m', ):
        self.symbol = symbol
        self.timeframe = timeframe
        self.exchange = ccxt.binance({
            'options': {'defaultType': 'future'}  # Configure for futures markets
        })
        logging.debug(f"Initialized SignalGenerator with symbol: {symbol}, timeframe: {timeframe}")

    def fetch_market_data(self):
        try:
            latest_bars = self.exchange.fetch_ohlcv(self.symbol, self.timeframe, limit=50) # Fetch more data
            if latest_bars:
                df = pd.DataFrame(latest_bars, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
                df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
                return df
            else:
                logging.error(f"No market data fetched for {self.symbol}.")
                return pd.DataFrame()
        except Exception as e:
            logging.error(f"Error fetching market data for {self.symbol}: {e}")
            return pd.DataFrame()

    def generate_signals(self, df):
        try:
            # Example: simple moving average crossover
            df['EMA10'] = ta.EMA(df['close'], timeperiod=10)
            df['Smoothed_EMA20'] = df['close'].rolling(window=11). mean()
            df['RSI'] = ta.RSI(df['close'], timeperiod=14)
            df['MACD'], df['MACD_signal'], _ = ta.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)
            df['Upper_Band'], df['Middle_Band'], df['Lower_Band'] = ta.BBANDS(df['close'])
            df['Stochastic_K'], df['Stochastic_D'] = ta.STOCH(df['high'], df['low'], df['close'])
            
            logging.debug(f"{self.symbol} indicators: EMA10: {df['EMA10'].tail()}, RSI: {df['RSI'].tail()}, MACD: {df['MACD'].tail()}")

        # Buy Signal
            conditions_for_buy = (
                (df['EMA10'] > df['Smoothed_EMA20']) &
                (df['RSI'] < 35) & 
                (df['MACD'] > df['MACD_signal'])  
            )

        # Sell Signal
            conditions_for_sell = ( 
                (df['EMA10'] < df['Smoothed_EMA20']) &
                (df['RSI'] > 65) &
                (df['MACD'] < df['MACD_signal'])  
            )

            df['Signal'] = np.where(conditions_for_buy, 'BUY', np.where(conditions_for_sell, 'SELL', 'HOLD'))
            return df
        except Exception as e:
            logging.error(f"Error generating signals for {self.symbol}: {e}")
            return df

    def save_signals(self, df):
        try:
            last_signal = df.iloc[-1]
            # Save all generated signals to the database
            Signal.objects.update_or_create(
                timestamp=timezone.make_aware(last_signal['timestamp']),
                symbol=self.symbol,
                defaults={
                    'price': last_signal['close'],
                    'signal_type': last_signal['Signal'],
                }
            )
            logging.info(f"Signal saved for {self.symbol}: {last_signal['Signal']}")
        except Exception as e:
            logging.error(f"Error saving signals for {self.symbol} to database: {e}")

    def run(self):
        #while not stop_event.is_set():
            df = self.fetch_market_data()
            #logging.debug(df.tail(5))  # Check the last few rows
            if not df.empty:
                df = self.generate_signals(df)
                self.save_signals(df)
            else:
                logging.error("No data fetched, cannot proceed with signal generation.")
            #stop_event.wait(1)  # Check the event every second

def worker(symbol):
    signal_generator = SignalGenerator(symbol=symbol)
    while True:
        try:
            signal_generator.run()
            time.sleep(60) # Avoid hammering the API, adjust to your timeframe
        except Exception as e:
            logging.error(f"An error occurred in worker for {symbol}: {e}")
            time.sleep(60) # Backoff time to handle API limitations
    

def main():
    
    try:
        exchange = ccxt.binance({'options': {'defaultType': 'future'}})
        markets = exchange.load_markets()

# Filter for USDT pairs
        usdt_pairs = [symbol for symbol in markets if symbol.endswith('/USDT')]
        # Fetch 24-hour price change statistics
        #tickers = exchange.fetch_tickers()

        # Collect the price change percentage for each USDT pair
        #symbols = [symbol for symbol, data in tickers.items() if symbol.endswith('/USDT')]
        # Sort symbols by absolute price change percentage
        #sorted_usdt_pairs = sorted(price_changes, key=lambda x: abs(x[1]), reverse=True)
        
        # Extract sorted symbols
        #sorted_usdt_pairs = [(symbol, change) for symbol, change in sorted_symbols]
    
        #for symbol in sorted_usdt_pairs:
            #p = Process(target=worker, args=(symbol, stop_event))
            #processes.append(p)
            #p.start()

        # Wait for the processes to complete
        #for p in processes:
            #p.join()
# Iterate over each USDT pair
        with ProcessPoolExecutor(max_workers=3) as executor:
            #executor.map(worker, usdt_pairs)
         #Make the executor ignore SIGINT in workers and let the main process handle it.
        
            futures = {executor.submit(worker, symbol) for symbol in usdt_pairs}
        #try:
            for future in as_completed(futures):
                symbol = futures[future]
                try:
                    # Make sure each task completes when testing
                    future.result(timeout=360)  # 6 minutes max per execution, customize as needed
                    #future.result()
                except Exception as e:
                    logging.error(f"Error processing symbol {symbol}: {e}")
        #for symbol in usdt_pairs:
            #try:
                #logging.info(f"Processing symbol: {symbol}")
                #signal_generator = SignalGenerator(symbol=symbol)
                #signal_generator.run()
            #except Exception as e:
                #logging.error(f"Error processing symbol {symbol}: {e}")
    except KeyboardInterrupt:
        logging.info("KeyboardInterrupt caught. Setting stop event and terminating all processes.")
        #stop_event.set()
        #for p in processes:
            #p.join()
    except Exception as e:
        logging.error("All processes have been terminated gracefully.")
        


if __name__ == "__main__":
    main()

This was my previous crypto bot for generating signals. Can you change the logic to fetch more data and use below strategy:
Strategy Overview
Exponential Moving Average (EMA):
Use two EMAs for trend identification (e.g., a 20-period EMA for short-term trends and a 50-period EMA for longer-term trends).
Signal: A bullish trend is indicated when the 20 EMA crosses above the 50 EMA, and a bearish trend when the 20 EMA crosses below the 50 EMA.
Ichimoku Cloud:
The Ichimoku Cloud provides an overview of support and resistance, as well as trend direction and momentum.
Signal:
A bullish signal occurs when the price breaks above the cloud, and a bearish signal when it breaks below.
Look at the Senkou Span A and Senkou Span B lines to identify the cloud’s leading edge and potential support/resistance areas.
Average True Range (ATR):
Use ATR to measure market volatility and set appropriate stop-loss and take-profit levels.
Signal:
Higher ATR values indicate more volatile conditions.
Set stop-loss levels to 1.5 times the ATR to account for natural price swings.
Relative Strength Index (RSI):
RSI helps determine potential overbought or oversold conditions.
Signal:
Consider RSI above 70 as overbought (potential sell signal) and below 30 as oversold (potential buy signal).
Look for divergence between RSI and price for potential reversal signals.
Volume Profile:
Volume Profile shows the traded volume at each price level, which helps identify significant support and resistance levels.
Signal:
Identify areas of high trade activity as support and resistance levels.
A breakout from these levels on high volume can signal a strong move in the direction of the breakout.
Comprehensive Trading Plan
Identify Trend:
Use the EMA crossover to identify prevailing trend directions.
Confirm with the Ichimoku Cloud: Is the price above or below the cloud?
Entry Signals:
Bullish Entry:
20 EMA crosses above 50 EMA.
Price breaks out above the Ichimoku Cloud and there’s confirmation with RSI (not overbought).
Volume Profile confirms breakout above a high-volume node.
Bearish Entry:
20 EMA crosses below 50 EMA.
Price breaks below the Ichimoku Cloud and confirms with RSI (not oversold).
Volume Profile confirms breakdown below a high-volume node.
Exit Signals:
Use ATR for setting stop-loss and take-profit levels.
Exit if RSI enters extreme overbought/sold areas and price action shows a reversal signal.
Consider exiting when the price moves against the trend and breaks the opposite border of the Ichimoku Cloud.
Position Sizing:
Determine position size based on risk level and ATR values to maintain consistent risk management.
Risk Management:
Keep a risk-reward ratio of at least 1:2.
Never risk more than 1-2% of trading capital on a single trade.
Here are some suggestions for improvement and additional considerations:

Indicator Overlap: You're using multiple indicators to confirm signals. This is good, but ensure that your signals are not too closely aligned, which could lead to false positives. For example, if the EMA crossover and Ichimoku Cloud both indicate a bullish signal, you might want to wait for confirmation from the RSI or volume profile before entering a long position.
ATR Usage: You're using ATR for stop-loss levels, which is a good practice. However, consider using it for take-profit levels as well. This can help you capture more profits while managing risk effectively.
Risk-Reward Ratio: You've set a risk-reward ratio of at least 1:2, which is a good rule of thumb. However, it's important to adjust this based on the specific market conditions and the potential move of the price. In volatile markets, you might want to reduce your risk-reward ratio, and in less volatile markets, you might increase it.
Position Sizing: Your position sizing strategy is based on risk management, which is crucial. However, consider using a more dynamic approach, such as the Kelly Criterion, which calculates the optimal position size based on the expected win rate, win/loss ratio, and risk tolerance.
Diversification: While your strategy is focused on a single instrument, consider diversifying your portfolio to reduce risk. This could involve trading multiple instruments or asset classes, or using a strategy that involves different time frames.
Market Analysis: Your strategy is heavily reliant on technical indicators. While this is a good starting point, consider incorporating fundamental analysis to supplement your technical signals. This could involve analyzing news, earnings reports, or other economic indicators that could affect the price of the asset you're trading.
Backtesting and Optimization: Regularly backtest your strategy to ensure that it's still effective and to identify any potential issues. Consider optimizing your strategy by adjusting parameters or adding new indicators to improve its performance.
Stop-Loss and Take-Profit Management: Your strategy sets stop-loss levels based on ATR, which is a good practice. However, consider using a more dynamic approach, such as trailing stops, which can help you capture more profits while limiting losses.
Time Frame Considerations: Your strategy seems to be designed for a specific time frame (e.g., daily or weekly). However, different time frames can provide different signals. Consider using a multi-time frame approach to confirm signals and reduce the risk of false entries.
Leverage: Be cautious with leverage. While it can increase potential profits, it can also lead to significant losses if the market moves against you. Always ensure that you understand the risks involved when using leverage.
Remember to design the bot to fetch more data and process historical data and generate signals. Here’s how you might approach it:

1. Historical Data Analysis:
- Ensure the bot is capable of analyzing historical data to establish the initial state of the indicators when it starts running.
- When the bot is initialized, it should load historical price data and calculate the EMA values (both 20 and 50) for each historical time point up to the current moment. This way, it can determine if a crossover has already occurred.

2. Checking for Existing Signals:
- If the 20 EMA crossed above the 50 EMA before the bot started and you want the bot to recognize this signal, then the bot needs to ascertain the current trend.
- The bot should not only look at real-time data but first run through the last few hours or days of historical data to check the existing state of all indicators, including the EMAs. Based on this analysis, it can identify whether there’s an existing bullish or bearish trend when it starts.

3. Real-Time Monitoring:
- Once the initial state is established, the bot can proceed with real-time monitoring to detect new signals based on subsequent data.
- It might be programmed to set a “signal detected” flag if it identifies that the 20 EMA is already above the 50 EMA upon initializing, which would suggest maintaining or initiating bullish positions according to the strategy’s rules.

4. Strategy Implementation:
- Determine if the strategy dictates that you should only act on crossovers that happen when the bot is actively running, or if it should also respond to previous crossovers that are still valid according to other strategy conditions (e.g., confirmed by Ichimoku Cloud and other indicators).

5. Flexibility and Customization:
- The bot’s logic should accommodate such customization, allowing you to specify whether to consider pre-existing signals at startup. This is crucial for adapting to situations like starting the bot late in an ongoing trend.