following you get my codebase for my trading platform. I m at integrating lightweight charts in frontend and already prepared backend codebase for it.

I want you to take care especially for proper data retrievement in frontend as well as backend. 

Important: I dont want to have any workaround implemented. I only want to use code, which is defentilty is the proper way, for using lightweight charts and how lightweight charts data is handling and expecting regarding its official documentation. if you dont know the proper way, than feel free to ask. dont do any workaround.  

So please review proper data handling and retrievment regarding lightweight charts requirements in the backend code first. for now, the backend just have the one aim, to serve lightweight charts with the needed data. so If anything is not properly implemented for that, or, as well, if anything is unnecessary for working with lightweight charts, than just remove it. im a fan of slim, simplified, customziable and modular coding style. 

after that provide a suitable frontend code with usage of the given technologies. provide full and production ready code. for now, i dont have written any code for frontend, so feel free to provide anything you thinks its appropriate.

**Important:**

I definitely prefer getting everything provided in one single answer.

But: **if you can’t provide all of the above considerations in one single answer because of your limitations, don’t just try it or shortening or summarize** **anything.** In this case split the process of answering up into several answers. For that split process of development into several steps, suitable for your specific limitations of answering and provide everything step-by-step answer for answer. So we could work step by step, while doing one step after another to get the whole picture over several answers. After every step, ask me for confirmation of being ready for the next step, than go on with developing. If you follow this approach, please inform me about your intentions, and how you plan to go with interactions. 

im a huge fan of a slim, simplified, customizable (parameters, variables, configs, …) and modular coding style. 
----

Overall Code Structure:

```bash
trading_platform/
├── backend/
│   ├── app/
│   │   └── config.py
│   │   └── main.py
│   │   └── mt5_connector.py
│   │   └── modely.py
│   │   └── redis_client.py
│   │   └── websocket.py
│   ├── tests/
│   └── lib/
├── frontend/
│   ├── package.json
│   ├── .next/
│   │   └── ...
│   ├── node_modules/
│   │   └── ...
│   ├── public/
│   ├── app/
│   │   ├── app/
│   │   │   └── global.css
│   │   │   └── layout.tsx
│   │   ├── components/
│   │   │   └── Chart.tsx
│   │   │   └── WebsocketManager.tsx
│   │   ├── lib/
│   │   │   └── lightweight-charts.d.ts
│   │   │   └── types.ts
│   ├── tests/
│   └── lib/
├── venv/
└── requirememnts.txt
```

----

Used technology:

Backend:


- Python
- FastAPI
- Redis
- MetaTrader5 Library

Frontend:


- next.js
- lightweight-charts library from Tradingview
- Tailwind CSS
- Typescript


----

Backend Prepared for Lightweight Charts

Relevant Codebase:


- config.py
- main.py
- mt5_connector.py
- modely.py
- redis_client.py
- websocket.py
- ./Tests


----

#### config.py


```python
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    MT5_LOGIN: int
    MT5_PASSWORD: str
    MT5_SERVER: str
    MT5_PATH: str
    REDIS_URL: str
    SYMBOLS: list = ["EURUSD", "GBPUSD", "USDJPY", "US100.cash"]
    TIMEFRAMES: list = ["M1", "M5", "M15", "H1", "H4", "D1"]
    REAL_TIME_UPDATE_FREQUENCY: float = 1.0  # Default to 1 second

    class Config:
        env_file = ".env"

settings = Settings()

```

----

#### main.py


```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .websocket import websocket_endpoint

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Adjust this in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.add_websocket_route("/ws", websocket_endpoint)

@app.get("/")
async def root():
    return {"message": "Trading Platform Backend"}
```

----

#### models.py


```python
from pydantic import BaseModel
from typing import Optional, Any, List

class OHLC(BaseModel):
    time: str
    open: float
    high: float
    low: float
    close: float
    tick_volume: int
    spread: int
    real_volume: int

class Tick(BaseModel):
    time: str
    bid: float
    ask: float
    last: float
    volume: float

class WebSocketMessage(BaseModel):
    action: str
    symbol: Optional[str] = ""
    timeframe: Optional[str] = ""
    data: Optional[Any] = None
```

----

#### mt5_connector.py


```python
import MetaTrader5 as mt5
import asyncio
import numpy as np
from .config import settings
import pandas as pd
from asyncio import Lock
from datetime import datetime, timedelta
from .redis_client import get_cached_data, set_cached_data
import time

# Assuming these functions are defined elsewhere in your code
def get_cached_data(key):
    # Placeholder for actual cache retrieval logic
    pass

def set_cached_data(key, data):
    # Placeholder for actual cache storage logic
    pass

class MT5Connector:
    def __init__(self):
        self.initialized = False
        self.lock = Lock()

    async def initialize(self):
        async with self.lock:
            if not self.initialized:
                if not mt5.initialize(
                    login=settings.MT5_LOGIN,
                    password=settings.MT5_PASSWORD,
                    server=settings.MT5_SERVER,
                    path=settings.MT5_PATH
                ):
                    print("MT5 initialization failed")
                    mt5.shutdown()
                    return False
                self.initialized = True
        return True

    def get_mt5_server_time(self, symbol=None):
        if symbol is None:
            symbols = mt5.symbols_get()
            if not symbols:
                raise Exception("No symbols available in MT5")
            symbol = symbols[0].name
        
        tick = mt5.symbol_info_tick(symbol)
        if tick is None:
            raise Exception(f"Failed to get tick data for {symbol}")
        
        server_time = tick.time
        return datetime.fromtimestamp(server_time)

    async def get_historical_data(self, symbol, timeframe, count=1000):
        if not await self.initialize():
            raise Exception("Failed to initialize MT5 connection")
        
        print(f"INFO: Fetching historical data for {symbol} ({timeframe}) from MT5")
        
        tf = getattr(mt5, f"TIMEFRAME_{timeframe}")
        mt5_time = self.get_mt5_server_time(symbol)
        
        rates = mt5.copy_rates_from(symbol, tf, mt5_time, count)
        
        if rates is not None and len(rates) > 0:
            df = pd.DataFrame(rates)
            df['time'] = pd.to_datetime(df['time'], unit='s')
            
            result = df.to_dict('records')
            for item in result:
                item['time'] = int(item['time'].timestamp())
            
            print(f"INFO: Retrieved {len(result)} candles for {symbol} ({timeframe})")
            print(f"First candle: {result[0]}")
            print(f"Last candle: {result[-1]}")
            
            return result
        else:
            print(f"WARNING: No data retrieved for {symbol} ({timeframe})")
            raise Exception(f"No historical data available for {symbol} on {timeframe} timeframe")


    async def stream_ohlc_data(self, symbol, timeframe):
        if not await self.initialize():
            raise Exception("Failed to initialize MT5 connection")
        
        tf = getattr(mt5, f"TIMEFRAME_{timeframe}")
        last_candle_time = None
        
        while True:
            mt5_time = self.get_mt5_server_time(symbol)
            # Wait until the start of the next minute
            await asyncio.sleep(60 - mt5_time.second)
            
            # Fetch the last 3 candles
            rates = mt5.copy_rates_from(symbol, tf, mt5_time, 3)
            
            if rates is not None and len(rates) >= 2:
                df = pd.DataFrame(rates)
                df['time'] = pd.to_datetime(df['time'], unit='s')
                print(f"\nReceived OHLC data for {symbol} at {mt5_time}:")
                print(df[['time', 'open', 'high', 'low', 'close']].to_string(index=False))

                last_complete_candle = rates[-2]  # The second-to-last candle
                current_candle_time = int(last_complete_candle['time'])
                
                if last_candle_time is None or current_candle_time > last_candle_time:
                    last_candle_time = current_candle_time
                    yield self._sanitize_data({
                        'time': current_candle_time,
                        'open': round(last_complete_candle['open'], 5),
                        'high': round(last_complete_candle['high'], 5),
                        'low': round(last_complete_candle['low'], 5),
                        'close': round(last_complete_candle['close'], 5),
                        'volume': last_complete_candle['tick_volume']
                    })
            else:
                print(f"Failed to fetch data for {symbol} at {mt5_time}")

    def _sanitize_data(self, data):
        """Convert numpy integers to Python integers."""
        if isinstance(data, np.integer):
            return int(data)
        elif isinstance(data, dict):
            return {key: self._sanitize_data(value) for key, value in data.items()}
        elif isinstance(data, list):
            return [self._sanitize_data(item) for item in data]
        return data

mt5_connector = MT5Connector()

```

----

#### redis_client.py


```python
import redis
from .config import settings
import json

redis_client = redis.Redis.from_url(settings.REDIS_URL)

def get_cached_data(key):
    data = redis_client.get(key)
    return json.loads(data) if data else None

def set_cached_data(key, value, expiration=3600):
    redis_client.setex(key, expiration, json.dumps(value))

# We'll implement more Redis functionality as needed for the hybrid approach
```

----

#### websocket.py


```python
import asyncio
from fastapi import WebSocket, WebSocketDisconnect
from .models import WebSocketMessage
from .mt5_connector import mt5_connector
import json
import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    logger.info("WebSocket connection accepted")
    
    try:
        while True:
            data = await websocket.receive_text()
            message = WebSocketMessage.parse_raw(data)
            
            if message.action == "get_data":
                historical_data = await mt5_connector.get_historical_data(message.symbol, message.timeframe)
                await websocket.send_text(json.dumps({
                    "action": "historical_data",
                    "symbol": message.symbol,
                    "timeframe": message.timeframe,
                    "data": historical_data
                }))
                
                # Start streaming OHLC data
                await stream_ohlc_data(websocket, message.symbol, message.timeframe)
    except WebSocketDisconnect:
        logger.info("WebSocket disconnected")
    except Exception as e:
        logger.error(f"WebSocket error: {str(e)}")

async def stream_ohlc_data(websocket: WebSocket, symbol: str, timeframe: str):
    async for candle in mt5_connector.stream_ohlc_data(symbol, timeframe):
        await websocket.send_text(json.dumps({
            "action": "ohlc_update",
            "symbol": symbol,
            "timeframe": timeframe,
            "data": candle
        }))

```

----

#### requirements.txt


```python
annotated-types==0.7.0
anyio==4.6.2.post1
asyncio==3.4.3
click==8.1.7
colorama==0.4.6
fastapi==0.115.2
h11==0.14.0
idna==3.10
MetaTrader5==5.0.4424
numpy==1.26.4
pandas==2.2.3
pydantic==2.9.2
pydantic-settings==2.5.2
pydantic_core==2.23.4
python-dateutil==2.9.0.post0
python-dotenv==1.0.1
pytz==2024.2
redis==5.1.1
six==1.16.0
sniffio==1.3.1
starlette==0.39.2
typing_extensions==4.12.2
tzdata==2024.2
uvicorn==0.31.1
websockets==13.1

```