From 5bf46f5fe177963f92a75f606046535333fa911c Mon Sep 17 00:00:00 2001 From: chrisnov-it Date: Tue, 19 May 2026 12:38:30 +0800 Subject: [PATCH] Keep main focused on MT5 platform --- ROADMAP.md | 65 +-- core/brokers/base_broker.py | 172 -------- core/brokers/binance_broker.py | 359 ----------------- core/brokers/broker_factory.py | 234 ----------- core/brokers/ctrader_broker.py | 415 -------------------- core/brokers/indonesian_brokers.py | 546 -------------------------- core/brokers/interactive_brokers.py | 491 ----------------------- core/brokers/tradingview_broker.py | 449 --------------------- core/interfaces/__init__.py | 1 - testing/indonesian_market_demo.py | 353 ----------------- testing/multi_broker_universe_demo.py | 310 --------------- testing/test_ctrader_broker.py | 59 --- 12 files changed, 21 insertions(+), 3433 deletions(-) delete mode 100644 core/brokers/base_broker.py delete mode 100644 core/brokers/binance_broker.py delete mode 100644 core/brokers/broker_factory.py delete mode 100644 core/brokers/ctrader_broker.py delete mode 100644 core/brokers/indonesian_brokers.py delete mode 100644 core/brokers/interactive_brokers.py delete mode 100644 core/brokers/tradingview_broker.py delete mode 100644 core/interfaces/__init__.py delete mode 100644 testing/indonesian_market_demo.py delete mode 100644 testing/multi_broker_universe_demo.py delete mode 100644 testing/test_ctrader_broker.py diff --git a/ROADMAP.md b/ROADMAP.md index b66051d..04d9586 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1,52 +1,29 @@ -# ๐Ÿ“‹ QuantumBotX Development Roadmap +# QuantumBotX Roadmap -**๐Ÿ‘€ Stay Tuned for Exciting Updates!** +QuantumBotX `main` is currently maintained as a Windows-first MetaTrader 5 +trading platform. Cross-platform broker work is intentionally developed outside +`main` until it is mature enough to merge cleanly. -## ๐ŸŽฏ **What's Coming Next** +## Current Focus -### **Q4 2025: Intelligence Enhancement** -- **Advanced AI Features**: Enhanced strategy analysis with machine learning -- **Real-time Notifications**: Telegram integration for trade alerts -- **Portfolio Analytics**: Advanced performance dashboards -- **Enterprise Features**: Multi-account management and compliance logging +- Keep MT5 demo/live workflow stable on Windows. +- Keep strategy registration, backtesting, and dashboard modules installable on + modern Python. +- Improve public-safe tests and documentation. +- Keep broker/account-specific diagnostics out of the public repository. -### **Exciting New Project** ๐Ÿš€ -We're developing **QuantumBotX API** - a revolutionary cloud-based trading platform that will give users unprecedented freedom: +## Near-Term Work -> **"Trade anywhere, anytime, with any broker - no local installations required!"** +- Harden setup for Python 3.12. +- Improve MT5 connection diagnostics and clearer user-facing error messages. +- Expand regression checks for strategy imports and backtesting. +- Review packaging scripts for Windows installer reliability. -**QuantumBotX API will feature:** -- ๐ŸŒ **Cloud-native architecture** - Run on any device, anywhere -- ๐Ÿ”„ **Direct broker integration** - No intermediaries, pure API trading -- ๐ŸŒ **Cross-broker support** - IC Markets, Pepperstone, and beyond -- โšก **Real-time execution** - Ultra-low latency trade processing -- ๐ŸŽ“ **Advanced education** - Built-in learning with community support +## Deferred Work -### **Timeline** -- **Q4 2025**: Closed beta testing with select users -- **Q1 2026**: Public beta launch with premium support -- **Q2 2026**: Full global launch with subscription tiers +- Cross-platform broker backends on dedicated development branches. +- Broker-neutral order and market-data interfaces outside `main`. +- Cloud/API trading platform concepts outside `main`. ---- - -## ๐Ÿค **Community & Support** - -**We're building more than software - we're building a trading community!** - -- **Discord Community**: Join our growing trader community -- **Educational Content**: Free trading courses and tutorials -- **Open Source**: Contribute to the project and shape its future -- **Mentorship Program**: One-on-one guidance for serious traders - ---- - -## ๐Ÿ—บ๏ธ **Our Mission** - -**Empowering traders worldwide with safe, educational, and profitable algorithmic trading solutions.** - -*From local learning platform โ†’ Global trading ecosystem!* ๐Ÿš€๐Ÿ’ซ - ---- - -**Roadmap Updated: September 2025** - +Those items should stay on dedicated development branches until the MT5 platform +on `main` remains clean and stable. diff --git a/core/brokers/base_broker.py b/core/brokers/base_broker.py deleted file mode 100644 index 0ba3f95..0000000 --- a/core/brokers/base_broker.py +++ /dev/null @@ -1,172 +0,0 @@ -# core/brokers/base_broker.py -""" -Universal Broker Interface for Multi-Platform Trading -Supports MT5, Binance, and other brokers through unified API -""" - -from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Union -from enum import Enum -import pandas as pd -from datetime import datetime - -class OrderType(Enum): - MARKET_BUY = "market_buy" - MARKET_SELL = "market_sell" - LIMIT_BUY = "limit_buy" - LIMIT_SELL = "limit_sell" - STOP_LOSS = "stop_loss" - TAKE_PROFIT = "take_profit" - -class OrderStatus(Enum): - PENDING = "pending" - FILLED = "filled" - CANCELLED = "cancelled" - REJECTED = "rejected" - -class Timeframe(Enum): - M1 = "1m" - M5 = "5m" - M15 = "15m" - M30 = "30m" - H1 = "1h" - H4 = "4h" - D1 = "1d" - -class Position: - def __init__(self, symbol: str, side: str, size: float, entry_price: float, - current_price: float, unrealized_pnl: float, realized_pnl: float = 0): - self.symbol = symbol - self.side = side # 'long' or 'short' - self.size = size - self.entry_price = entry_price - self.current_price = current_price - self.unrealized_pnl = unrealized_pnl - self.realized_pnl = realized_pnl - self.timestamp = datetime.now() - -class Order: - def __init__(self, order_id: str, symbol: str, order_type: OrderType, - side: str, size: float, price: Optional[float] = None): - self.order_id = order_id - self.symbol = symbol - self.order_type = order_type - self.side = side - self.size = size - self.price = price - self.status = OrderStatus.PENDING - self.filled_size = 0.0 - self.avg_fill_price = 0.0 - self.timestamp = datetime.now() - -class AccountInfo: - def __init__(self, balance: float, equity: float, margin: float, - free_margin: float, margin_level: float, currency: str = "USD"): - self.balance = balance - self.equity = equity - self.margin = margin - self.free_margin = free_margin - self.margin_level = margin_level - self.currency = currency - self.timestamp = datetime.now() - -class BaseBroker(ABC): - """ - Abstract base class for all broker implementations. - Provides unified interface for MT5, Binance, and other brokers. - """ - - def __init__(self, broker_name: str): - self.broker_name = broker_name - self.is_connected = False - self.supported_symbols = [] - - @abstractmethod - def connect(self, credentials: Dict) -> bool: - """Connect to broker with credentials""" - pass - - @abstractmethod - def disconnect(self) -> bool: - """Disconnect from broker""" - pass - - @abstractmethod - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - pass - - @abstractmethod - def get_market_data(self, symbol: str, timeframe: Timeframe, - count: int = 500) -> pd.DataFrame: - """ - Get OHLCV market data - Returns: DataFrame with columns [time, open, high, low, close, volume] - """ - pass - - @abstractmethod - def get_current_price(self, symbol: str) -> Dict[str, float]: - """ - Get current bid/ask prices - Returns: {"bid": price, "ask": price} - """ - pass - - @abstractmethod - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """Place a trading order""" - pass - - @abstractmethod - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - pass - - @abstractmethod - def get_positions(self) -> List[Position]: - """Get all open positions""" - pass - - @abstractmethod - def get_orders(self) -> List[Order]: - """Get all pending orders""" - pass - - @abstractmethod - def get_account_info(self) -> AccountInfo: - """Get account information""" - pass - - @abstractmethod - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - pass - - # Utility methods (implemented in base class) - def normalize_symbol(self, symbol: str) -> str: - """Normalize symbol format for the broker""" - return symbol.upper().replace("/", "").replace("-", "") - - def calculate_position_size(self, account_balance: float, risk_percent: float, - entry_price: float, stop_loss: float) -> float: - """Calculate position size based on risk management""" - risk_amount = account_balance * (risk_percent / 100) - price_difference = abs(entry_price - stop_loss) - - if price_difference == 0: - return 0 - - position_size = risk_amount / price_difference - return position_size - - def validate_symbol(self, symbol: str) -> bool: - """Check if symbol is supported by broker""" - return symbol in self.supported_symbols - - def is_market_open(self) -> bool: - """Check if market is currently open (override for specific markets)""" - return True # Crypto markets are always open \ No newline at end of file diff --git a/core/brokers/binance_broker.py b/core/brokers/binance_broker.py deleted file mode 100644 index 55f3fc4..0000000 --- a/core/brokers/binance_broker.py +++ /dev/null @@ -1,359 +0,0 @@ -# core/brokers/binance_broker.py -""" -Binance Exchange Integration for QuantumBotX -Implements crypto trading through Binance API -""" - -import pandas as pd -import time -from datetime import datetime, timedelta -from typing import Dict, List, Optional -import logging - -from .base_broker import ( - BaseBroker, OrderType, OrderStatus, Timeframe, - Position, Order, AccountInfo -) - -logger = logging.getLogger(__name__) - -class BinanceBroker(BaseBroker): - """ - Binance exchange implementation of the universal broker interface. - Supports spot and futures trading. - """ - - def __init__(self, testnet: bool = True): - super().__init__("Binance") - self.testnet = testnet - self.client = None - self.base_url = "https://testnet.binance.vision" if testnet else "https://api.binance.com" - - # Timeframe mapping - self.timeframe_map = { - Timeframe.M1: "1m", - Timeframe.M5: "5m", - Timeframe.M15: "15m", - Timeframe.M30: "30m", - Timeframe.H1: "1h", - Timeframe.H4: "4h", - Timeframe.D1: "1d" - } - - def connect(self, credentials: Dict) -> bool: - """ - Connect to Binance with API credentials - credentials: {"api_key": "...", "secret_key": "..."} - """ - try: - # Import here to avoid dependency issues if not installed - from binance.client import Client - from binance.exceptions import BinanceAPIException - - api_key = credentials.get("api_key") - secret_key = credentials.get("secret_key") - - if not api_key or not secret_key: - logger.error("Binance API key and secret key are required") - return False - - # Initialize Binance client - self.client = Client( - api_key=api_key, - api_secret=secret_key, - testnet=self.testnet - ) - - # Test connection - account_info = self.client.get_account() - self.is_connected = True - - # Get supported symbols - exchange_info = self.client.get_exchange_info() - self.supported_symbols = [s['symbol'] for s in exchange_info['symbols'] - if s['status'] == 'TRADING'] - - logger.info(f"Connected to Binance {'Testnet' if self.testnet else 'Mainnet'}") - logger.info(f"Account status: {account_info.get('accountType', 'Unknown')}") - - return True - - except Exception as e: - logger.error(f"Failed to connect to Binance: {e}") - self.is_connected = False - return False - - def disconnect(self) -> bool: - """Disconnect from Binance""" - self.client = None - self.is_connected = False - logger.info("Disconnected from Binance") - return True - - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - if not self.is_connected: - return [] - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - """ - Get OHLCV market data from Binance - """ - if not self.is_connected: - raise Exception("Not connected to Binance") - - try: - # Convert timeframe - interval = self.timeframe_map[timeframe] - - # Get klines (candlestick data) - klines = self.client.get_klines( - symbol=symbol, - interval=interval, - limit=count - ) - - # Convert to DataFrame - df = pd.DataFrame(klines, columns=[ - 'timestamp', 'open', 'high', 'low', 'close', 'volume', - 'close_time', 'quote_asset_volume', 'number_of_trades', - 'taker_buy_base_asset_volume', 'taker_buy_quote_asset_volume', 'ignore' - ]) - - # Clean and format data - df['time'] = pd.to_datetime(df['timestamp'], unit='ms') - df['open'] = pd.to_numeric(df['open']) - df['high'] = pd.to_numeric(df['high']) - df['low'] = pd.to_numeric(df['low']) - df['close'] = pd.to_numeric(df['close']) - df['volume'] = pd.to_numeric(df['volume']) - - # Return standardized format - return df[['time', 'open', 'high', 'low', 'close', 'volume']].copy() - - except Exception as e: - logger.error(f"Failed to get market data for {symbol}: {e}") - return pd.DataFrame() - - def get_current_price(self, symbol: str) -> Dict[str, float]: - """Get current bid/ask prices""" - if not self.is_connected: - raise Exception("Not connected to Binance") - - try: - ticker = self.client.get_orderbook_ticker(symbol=symbol) - return { - "bid": float(ticker['bidPrice']), - "ask": float(ticker['askPrice']) - } - except Exception as e: - logger.error(f"Failed to get current price for {symbol}: {e}") - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """Place a trading order on Binance""" - if not self.is_connected: - raise Exception("Not connected to Binance") - - try: - # Convert order parameters - binance_side = side.upper() # 'BUY' or 'SELL' - - # Determine order type - if order_type == OrderType.MARKET_BUY or order_type == OrderType.MARKET_SELL: - binance_type = "MARKET" - elif order_type == OrderType.LIMIT_BUY or order_type == OrderType.LIMIT_SELL: - binance_type = "LIMIT" - else: - raise ValueError(f"Unsupported order type: {order_type}") - - # Prepare order parameters - order_params = { - 'symbol': symbol, - 'side': binance_side, - 'type': binance_type, - 'quantity': size, - } - - if binance_type == "LIMIT": - order_params['price'] = price - order_params['timeInForce'] = 'GTC' # Good Till Cancelled - - # Place order - result = self.client.create_order(**order_params) - - # Create Order object - order = Order( - order_id=str(result['orderId']), - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - - # Update status based on result - if result['status'] == 'FILLED': - order.status = OrderStatus.FILLED - order.filled_size = float(result.get('executedQty', 0)) - order.avg_fill_price = float(result.get('price', price or 0)) - elif result['status'] == 'NEW': - order.status = OrderStatus.PENDING - - logger.info(f"Order placed: {order.order_id} for {symbol}") - return order - - except Exception as e: - logger.error(f"Failed to place order: {e}") - # Return failed order - order = Order( - order_id="failed", - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - order.status = OrderStatus.REJECTED - return order - - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - if not self.is_connected: - return False - - try: - # Note: Need symbol to cancel order in Binance - # This is a limitation - may need to store order info - logger.warning("Cancel order requires symbol - implement order tracking") - return False - except Exception as e: - logger.error(f"Failed to cancel order {order_id}: {e}") - return False - - def get_positions(self) -> List[Position]: - """Get all open positions (for futures)""" - if not self.is_connected: - return [] - - try: - # For spot trading, positions are just balances - account = self.client.get_account() - positions = [] - - for balance in account['balances']: - free = float(balance['free']) - locked = float(balance['locked']) - total = free + locked - - if total > 0: - # Create position for non-zero balances - position = Position( - symbol=balance['asset'], - side='long', # Spot is always long - size=total, - entry_price=0.0, # Not available for spot - current_price=0.0, # Would need to fetch - unrealized_pnl=0.0 # Not calculated for spot - ) - positions.append(position) - - return positions - - except Exception as e: - logger.error(f"Failed to get positions: {e}") - return [] - - def get_orders(self) -> List[Order]: - """Get all pending orders""" - if not self.is_connected: - return [] - - try: - # Get open orders for all symbols (limitation: need symbol) - # For now, return empty - would need to track symbols - logger.warning("Get orders requires symbol tracking - implement order cache") - return [] - - except Exception as e: - logger.error(f"Failed to get orders: {e}") - return [] - - def get_account_info(self) -> AccountInfo: - """Get account information""" - if not self.is_connected: - return AccountInfo(0, 0, 0, 0, 0, "USDT") - - try: - account = self.client.get_account() - - # Calculate total balance in USDT - total_balance = 0.0 - - for balance in account['balances']: - free = float(balance['free']) - locked = float(balance['locked']) - total = free + locked - - if total > 0: - asset = balance['asset'] - if asset == 'USDT': - total_balance += total - else: - # Convert to USDT (simplified - would need price conversion) - # For demo purposes, assume small balances - if asset in ['BTC', 'ETH']: - total_balance += total * 30000 # Rough estimate - else: - total_balance += total # Assume stablecoin or ignore - - return AccountInfo( - balance=total_balance, - equity=total_balance, # Same for spot - margin=0.0, # Not applicable for spot - free_margin=total_balance, - margin_level=100.0, # Not applicable for spot - currency="USDT" - ) - - except Exception as e: - logger.error(f"Failed to get account info: {e}") - return AccountInfo(0, 0, 0, 0, 0, "USDT") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - if not self.is_connected: - return [] - - try: - # Get trades for major symbols (limitation: need symbol) - logger.warning("Trade history requires symbol tracking - implement symbol cache") - return [] - - except Exception as e: - logger.error(f"Failed to get trade history: {e}") - return [] - - def normalize_symbol(self, symbol: str) -> str: - """Normalize symbol format for Binance""" - # Binance uses format like 'BTCUSDT', 'ETHUSDT' - symbol = symbol.upper().replace("/", "").replace("-", "") - - # Common conversions - if symbol.endswith("USD") and not symbol.endswith("USDT"): - symbol = symbol.replace("USD", "USDT") - - return symbol - - def is_market_open(self) -> bool: - """Crypto markets are always open""" - return True - -# Convenience function to create Binance broker -def create_binance_broker(testnet: bool = True) -> BinanceBroker: - """Create a Binance broker instance""" - return BinanceBroker(testnet=testnet) \ No newline at end of file diff --git a/core/brokers/broker_factory.py b/core/brokers/broker_factory.py deleted file mode 100644 index b50d4d3..0000000 --- a/core/brokers/broker_factory.py +++ /dev/null @@ -1,234 +0,0 @@ -# core/brokers/broker_factory.py -""" -Broker Factory for QuantumBotX -Manages multiple brokers and provides unified interface -""" - -import logging -from typing import Dict, Optional, List -from enum import Enum - -from .base_broker import BaseBroker -from .binance_broker import BinanceBroker -from .ctrader_broker import CTraderBroker -from .interactive_brokers import InteractiveBrokersBroker -from .tradingview_broker import TradingViewBroker -from .indonesian_brokers import ( - IndopremierBroker, XMIndonesiaBroker, - OctaFXIndonesiaBroker, HSBCIndonesiaBroker -) - -logger = logging.getLogger(__name__) - -class BrokerType(Enum): - MT5 = "mt5" - BINANCE = "binance" - BINANCE_FUTURES = "binance_futures" - CTRADER = "ctrader" - INTERACTIVE_BROKERS = "interactive_brokers" - TRADINGVIEW = "tradingview" - # Indonesian brokers - INDOPREMIER = "indopremier" - XM_INDONESIA = "xm_indonesia" - OCTAFX_INDONESIA = "octafx_indonesia" - HSBC_INDONESIA = "hsbc_indonesia" - -class BrokerFactory: - """ - Factory class to create and manage different broker instances - """ - - _brokers: Dict[str, BaseBroker] = {} - _configs: Dict[str, Dict] = {} - - @classmethod - def register_broker_config(cls, broker_id: str, broker_type: BrokerType, config: Dict): - """Register broker configuration""" - cls._configs[broker_id] = { - 'type': broker_type, - 'config': config - } - - @classmethod - def create_broker(cls, broker_id: str) -> Optional[BaseBroker]: - """Create broker instance from registered configuration""" - - if broker_id in cls._brokers: - return cls._brokers[broker_id] - - if broker_id not in cls._configs: - logger.error(f"No configuration found for broker: {broker_id}") - return None - - broker_config = cls._configs[broker_id] - broker_type = broker_config['type'] - config = broker_config['config'] - - try: - if broker_type == BrokerType.BINANCE: - broker = BinanceBroker(testnet=config.get('testnet', True)) - elif broker_type == BrokerType.BINANCE_FUTURES: - # Future implementation - broker = BinanceBroker(testnet=config.get('testnet', True)) - elif broker_type == BrokerType.CTRADER: - broker = CTraderBroker(demo=config.get('demo', True)) - elif broker_type == BrokerType.INTERACTIVE_BROKERS: - broker = InteractiveBrokersBroker(paper_trading=config.get('paper_trading', True)) - elif broker_type == BrokerType.TRADINGVIEW: - broker = TradingViewBroker(paper_trading=config.get('paper_trading', True)) - elif broker_type == BrokerType.INDOPREMIER: - broker = IndopremierBroker(demo=config.get('demo', True)) - elif broker_type == BrokerType.XM_INDONESIA: - broker = XMIndonesiaBroker(demo=config.get('demo', True)) - elif broker_type == BrokerType.OCTAFX_INDONESIA: - broker = OctaFXIndonesiaBroker(demo=config.get('demo', True)) - elif broker_type == BrokerType.HSBC_INDONESIA: - broker = HSBCIndonesiaBroker(demo=config.get('demo', True)) - elif broker_type == BrokerType.MT5: - # Import MT5 broker when implemented - from .mt5_broker import MT5Broker - broker = MT5Broker() - else: - logger.error(f"Unsupported broker type: {broker_type}") - return None - - # Connect broker - if broker.connect(config.get('credentials', {})): - cls._brokers[broker_id] = broker - logger.info(f"Successfully created and connected broker: {broker_id}") - return broker - else: - logger.error(f"Failed to connect broker: {broker_id}") - return None - - except Exception as e: - logger.error(f"Error creating broker {broker_id}: {e}") - return None - - @classmethod - def get_broker(cls, broker_id: str) -> Optional[BaseBroker]: - """Get existing broker instance""" - return cls._brokers.get(broker_id) - - @classmethod - def disconnect_all(cls): - """Disconnect all brokers""" - for broker_id, broker in cls._brokers.items(): - try: - broker.disconnect() - logger.info(f"Disconnected broker: {broker_id}") - except Exception as e: - logger.error(f"Error disconnecting broker {broker_id}: {e}") - - cls._brokers.clear() - - @classmethod - def get_all_brokers(cls) -> Dict[str, BaseBroker]: - """Get all connected brokers""" - return cls._brokers.copy() - - @classmethod - def get_supported_symbols(cls, broker_id: str) -> List[str]: - """Get supported symbols for a broker""" - broker = cls.get_broker(broker_id) - if broker: - return broker.get_symbols() - return [] - - @classmethod - def is_broker_connected(cls, broker_id: str) -> bool: - """Check if broker is connected""" - broker = cls.get_broker(broker_id) - return broker.is_connected if broker else False - -# Configuration helper functions -def setup_demo_brokers(): - """Setup demo brokers for testing""" - - # Binance Testnet configuration - BrokerFactory.register_broker_config( - broker_id="binance_testnet", - broker_type=BrokerType.BINANCE, - config={ - 'testnet': True, - 'credentials': { - 'api_key': '', # Add your testnet API key - 'secret_key': '' # Add your testnet secret key - } - } - ) - - # MT5 Demo configuration - BrokerFactory.register_broker_config( - broker_id="mt5_demo", - broker_type=BrokerType.MT5, - config={ - 'credentials': { - 'login': '', # Add your MT5 demo login - 'password': '', # Add your MT5 demo password - 'server': 'MetaQuotes-Demo' - } - } - ) - -def load_brokers_from_env(): - """Load broker configurations from environment variables""" - import os - - # Binance configuration - binance_api_key = os.getenv('BINANCE_API_KEY') - binance_secret = os.getenv('BINANCE_SECRET_KEY') - binance_testnet = os.getenv('BINANCE_TESTNET', 'true').lower() == 'true' - - if binance_api_key and binance_secret: - BrokerFactory.register_broker_config( - broker_id="binance", - broker_type=BrokerType.BINANCE, - config={ - 'testnet': binance_testnet, - 'credentials': { - 'api_key': binance_api_key, - 'secret_key': binance_secret - } - } - ) - - # MT5 configuration - mt5_login = os.getenv('MT5_LOGIN') - mt5_password = os.getenv('MT5_PASSWORD') - mt5_server = os.getenv('MT5_SERVER', 'MetaQuotes-Demo') - - if mt5_login and mt5_password: - BrokerFactory.register_broker_config( - broker_id="mt5", - broker_type=BrokerType.MT5, - config={ - 'credentials': { - 'login': mt5_login, - 'password': mt5_password, - 'server': mt5_server - } - } - ) - -# Example usage -if __name__ == "__main__": - # Load configurations - load_brokers_from_env() - - # Create brokers - binance_broker = BrokerFactory.create_broker("binance") - mt5_broker = BrokerFactory.create_broker("mt5") - - if binance_broker: - print(f"Binance connected: {binance_broker.is_connected}") - symbols = binance_broker.get_symbols()[:10] # First 10 symbols - print(f"Binance symbols: {symbols}") - - if mt5_broker: - print(f"MT5 connected: {mt5_broker.is_connected}") - account_info = mt5_broker.get_account_info() - print(f"MT5 balance: {account_info.balance}") - - # Cleanup - BrokerFactory.disconnect_all() \ No newline at end of file diff --git a/core/brokers/ctrader_broker.py b/core/brokers/ctrader_broker.py deleted file mode 100644 index 6de420b..0000000 --- a/core/brokers/ctrader_broker.py +++ /dev/null @@ -1,415 +0,0 @@ -# core/brokers/ctrader_broker.py -""" -cTrader Broker Integration for QuantumBotX -Modern forex/CFD platform with excellent API -""" - -import pandas as pd -import time -import requests -import json -from datetime import datetime, timedelta -from typing import Dict, List, Optional -import logging - -from .base_broker import ( - BaseBroker, OrderType, OrderStatus, Timeframe, - Position, Order, AccountInfo -) - -logger = logging.getLogger(__name__) - -class CTraderBroker(BaseBroker): - """ - cTrader (cTID) implementation of the universal broker interface. - Uses cTrader REST API for modern forex trading. - """ - - def __init__(self, demo: bool = True): - super().__init__("cTrader") - self.demo = demo - self.client_id = None - self.client_secret = None - self.access_token = None - self.account_id = None - self.base_url = "https://demo-api.ctraderapi.com" if demo else "https://api.ctraderapi.com" - - # Timeframe mapping - self.timeframe_map = { - Timeframe.M1: "M1", - Timeframe.M5: "M5", - Timeframe.M15: "M15", - Timeframe.M30: "M30", - Timeframe.H1: "H1", - Timeframe.H4: "H4", - Timeframe.D1: "D1" - } - - def connect(self, credentials: Dict) -> bool: - """ - Connect to cTrader with OAuth credentials - credentials: {"client_id": "...", "client_secret": "...", "account_id": "..."} - """ - try: - self.client_id = credentials.get("client_id") - self.client_secret = credentials.get("client_secret") - self.account_id = credentials.get("account_id") - - if not all([self.client_id, self.client_secret, self.account_id]): - logger.error("cTrader client_id, client_secret, and account_id are required") - return False - - # OAuth token request - token_url = f"{self.base_url}/oauth/v2/token" - token_data = { - 'grant_type': 'client_credentials', - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'scope': 'trading' - } - - response = requests.post(token_url, data=token_data) - - if response.status_code == 200: - token_info = response.json() - self.access_token = token_info['access_token'] - self.is_connected = True - - # Get supported symbols - self._load_symbols() - - logger.info(f"Connected to cTrader {'Demo' if self.demo else 'Live'}") - return True - else: - logger.error(f"cTrader authentication failed: {response.text}") - return False - - except Exception as e: - logger.error(f"Failed to connect to cTrader: {e}") - self.is_connected = False - return False - - def disconnect(self) -> bool: - """Disconnect from cTrader""" - self.access_token = None - self.is_connected = False - logger.info("Disconnected from cTrader") - return True - - def _make_request(self, endpoint: str, method: str = "GET", data: Dict = None) -> Dict: - """Make authenticated request to cTrader API""" - if not self.access_token: - raise Exception("Not authenticated with cTrader") - - headers = { - 'Authorization': f'Bearer {self.access_token}', - 'Content-Type': 'application/json' - } - - url = f"{self.base_url}{endpoint}" - - if method == "GET": - response = requests.get(url, headers=headers, params=data) - elif method == "POST": - response = requests.post(url, headers=headers, json=data) - elif method == "PUT": - response = requests.put(url, headers=headers, json=data) - elif method == "DELETE": - response = requests.delete(url, headers=headers) - - if response.status_code in [200, 201]: - return response.json() - else: - raise Exception(f"cTrader API error: {response.status_code} - {response.text}") - - def _load_symbols(self): - """Load available symbols from cTrader""" - try: - symbols_data = self._make_request("/v2/symbols") - self.supported_symbols = [s['symbolName'] for s in symbols_data.get('symbols', [])] - except Exception as e: - logger.warning(f"Failed to load cTrader symbols: {e}") - # Common forex symbols as fallback - self.supported_symbols = [ - 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', - 'NZDUSD', 'EURGBP', 'EURJPY', 'GBPJPY', 'XAUUSD', 'XAGUSD' - ] - - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - """Get OHLCV market data from cTrader""" - if not self.is_connected: - raise Exception("Not connected to cTrader") - - try: - # Convert timeframe - ct_timeframe = self.timeframe_map[timeframe] - - # Calculate from time (count bars back) - now = datetime.utcnow() - # Estimate time per bar - minutes_per_bar = { - 'M1': 1, 'M5': 5, 'M15': 15, 'M30': 30, - 'H1': 60, 'H4': 240, 'D1': 1440 - } - - minutes_back = count * minutes_per_bar.get(ct_timeframe, 60) - from_time = now - timedelta(minutes=minutes_back) - - # Request historical data - params = { - 'symbolName': symbol, - 'periodName': ct_timeframe, - 'fromTimestamp': int(from_time.timestamp() * 1000), - 'toTimestamp': int(now.timestamp() * 1000), - 'count': count - } - - data = self._make_request("/v2/bars", params=params) - bars = data.get('bars', []) - - if not bars: - return pd.DataFrame() - - # Convert to DataFrame - df_data = [] - for bar in bars: - df_data.append({ - 'time': datetime.fromtimestamp(bar['timestamp'] / 1000), - 'open': bar['open'], - 'high': bar['high'], - 'low': bar['low'], - 'close': bar['close'], - 'volume': bar.get('volume', 0) - }) - - return pd.DataFrame(df_data) - - except Exception as e: - logger.error(f"Failed to get market data for {symbol}: {e}") - return pd.DataFrame() - - def get_current_price(self, symbol: str) -> Dict[str, float]: - """Get current bid/ask prices""" - if not self.is_connected: - raise Exception("Not connected to cTrader") - - try: - data = self._make_request(f"/v2/symbols/{symbol}/tick") - return { - "bid": data['bid'], - "ask": data['ask'] - } - except Exception as e: - logger.error(f"Failed to get current price for {symbol}: {e}") - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """Place a trading order on cTrader""" - if not self.is_connected: - raise Exception("Not connected to cTrader") - - try: - # Convert order parameters - ct_side = "BUY" if side.lower() == "buy" else "SELL" - - # Convert volume to lots (cTrader uses volume in units) - volume = int(size * 100000) # Convert lots to units - - # Determine order type - if order_type in [OrderType.MARKET_BUY, OrderType.MARKET_SELL]: - ct_type = "MARKET" - elif order_type in [OrderType.LIMIT_BUY, OrderType.LIMIT_SELL]: - ct_type = "LIMIT" - else: - raise ValueError(f"Unsupported order type: {order_type}") - - # Prepare order data - order_data = { - 'accountId': self.account_id, - 'symbolName': symbol, - 'orderType': ct_type, - 'tradeSide': ct_side, - 'volume': volume, - } - - if ct_type == "LIMIT" and price: - order_data['limitPrice'] = price - - if stop_loss: - order_data['stopLoss'] = stop_loss - if take_profit: - order_data['takeProfit'] = take_profit - - # Place order - result = self._make_request("/v2/orders", method="POST", data=order_data) - - # Create Order object - order = Order( - order_id=str(result.get('orderId', 'unknown')), - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - - order.status = OrderStatus.PENDING - if result.get('executionType') == 'TRADE': - order.status = OrderStatus.FILLED - - logger.info(f"cTrader order placed: {order.order_id} for {symbol}") - return order - - except Exception as e: - logger.error(f"Failed to place cTrader order: {e}") - order = Order( - order_id="failed", - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - order.status = OrderStatus.REJECTED - return order - - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - if not self.is_connected: - return False - - try: - self._make_request(f"/v2/orders/{order_id}", method="DELETE") - return True - except Exception as e: - logger.error(f"Failed to cancel cTrader order {order_id}: {e}") - return False - - def get_positions(self) -> List[Position]: - """Get all open positions""" - if not self.is_connected: - return [] - - try: - data = self._make_request(f"/v2/accounts/{self.account_id}/positions") - positions = [] - - for pos_data in data.get('positions', []): - position = Position( - symbol=pos_data['symbolName'], - side='long' if pos_data['tradeSide'] == 'BUY' else 'short', - size=pos_data['volume'] / 100000, # Convert units to lots - entry_price=pos_data['entryPrice'], - current_price=pos_data['currentPrice'], - unrealized_pnl=pos_data['unrealizedGrossProfit'] - ) - positions.append(position) - - return positions - - except Exception as e: - logger.error(f"Failed to get cTrader positions: {e}") - return [] - - def get_orders(self) -> List[Order]: - """Get all pending orders""" - if not self.is_connected: - return [] - - try: - data = self._make_request(f"/v2/accounts/{self.account_id}/orders") - orders = [] - - for order_data in data.get('orders', []): - order = Order( - order_id=str(order_data['orderId']), - symbol=order_data['symbolName'], - order_type=OrderType.LIMIT_BUY, # Simplified - side=order_data['tradeSide'].lower(), - size=order_data['volume'] / 100000, - price=order_data.get('limitPrice') - ) - order.status = OrderStatus.PENDING - orders.append(order) - - return orders - - except Exception as e: - logger.error(f"Failed to get cTrader orders: {e}") - return [] - - def get_account_info(self) -> AccountInfo: - """Get account information""" - if not self.is_connected: - return AccountInfo(0, 0, 0, 0, 0, "USD") - - try: - data = self._make_request(f"/v2/accounts/{self.account_id}") - - balance = data.get('balance', 0) - equity = data.get('equity', balance) - margin = data.get('margin', 0) - free_margin = data.get('freeMargin', balance) - margin_level = data.get('marginLevel', 100) - currency = data.get('currency', 'USD') - - return AccountInfo( - balance=balance, - equity=equity, - margin=margin, - free_margin=free_margin, - margin_level=margin_level, - currency=currency - ) - - except Exception as e: - logger.error(f"Failed to get cTrader account info: {e}") - return AccountInfo(0, 0, 0, 0, 0, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - if not self.is_connected: - return [] - - try: - from_time = datetime.now() - timedelta(days=days) - params = { - 'fromTimestamp': int(from_time.timestamp() * 1000), - 'toTimestamp': int(datetime.now().timestamp() * 1000) - } - - data = self._make_request(f"/v2/accounts/{self.account_id}/deals", params=params) - return data.get('deals', []) - - except Exception as e: - logger.error(f"Failed to get cTrader trade history: {e}") - return [] - - def normalize_symbol(self, symbol: str) -> str: - """Normalize symbol format for cTrader""" - # cTrader typically uses format like 'EURUSD', 'GBPUSD' - return symbol.upper().replace("/", "").replace("-", "") - - def is_market_open(self) -> bool: - """Check if forex market is open""" - now = datetime.utcnow() - # Forex market is open from Sunday 22:00 UTC to Friday 22:00 UTC - if now.weekday() == 5: # Saturday - return False - if now.weekday() == 6 and now.hour < 22: # Sunday before 22:00 UTC - return False - if now.weekday() == 4 and now.hour >= 22: # Friday after 22:00 UTC - return False - return True - -# Convenience function -def create_ctrader_broker(demo: bool = True) -> CTraderBroker: - """Create a cTrader broker instance""" - return CTraderBroker(demo=demo) \ No newline at end of file diff --git a/core/brokers/indonesian_brokers.py b/core/brokers/indonesian_brokers.py deleted file mode 100644 index 17c4e77..0000000 --- a/core/brokers/indonesian_brokers.py +++ /dev/null @@ -1,546 +0,0 @@ -# core/brokers/indonesian_brokers.py -""" -Indonesian Market Brokers Integration for QuantumBotX -Supporting local Indonesian brokers and international brokers popular in Indonesia -""" - -import pandas as pd -import time -import requests -import json -import numpy as np -from datetime import datetime, timedelta -from typing import Dict, List, Optional -import logging - -from .base_broker import ( - BaseBroker, OrderType, OrderStatus, Timeframe, - Position, Order, AccountInfo -) - -logger = logging.getLogger(__name__) - -class IndopremierBroker(BaseBroker): - """ - Indopremier Securities (IPOT) - Popular Indonesian broker - Known for good demo accounts and local market access - """ - - def __init__(self, demo: bool = True): - super().__init__("Indopremier") - self.demo = demo - self.base_url = "https://demo-api.indopremier.com" if demo else "https://api.indopremier.com" - self.session = requests.Session() - - # Indonesian market symbols - self.supported_symbols = [ - # IDX (Indonesian Stock Exchange) - Blue chips - 'BBCA.JK', # Bank Central Asia - 'BBRI.JK', # Bank Rakyat Indonesia - 'BMRI.JK', # Bank Mandiri - 'TLKM.JK', # Telkom Indonesia - 'ASII.JK', # Astra International - 'UNVR.JK', # Unilever Indonesia - 'ICBP.JK', # Indofood CBP - 'INDF.JK', # Indofood Sukses Makmur - 'GGRM.JK', # Gudang Garam - 'HMSP.JK', # HM Sampoerna - - # IDX ETFs and Indices - 'LQ45.JK', # LQ45 Index - 'IHSG.JK', # Jakarta Composite Index - - # International through Indopremier - 'USDID', # USD/IDR - 'USDIDR', # USD/IDR alternative - 'XAUIDR', # Gold in IDR - ] - - def connect(self, credentials: Dict) -> bool: - """Connect to Indopremier""" - try: - username = credentials.get("username") - password = credentials.get("password") - - if not all([username, password]): - logger.error("Indopremier username and password required") - return False - - # Simulate authentication for demo - if self.demo: - self.is_connected = True - logger.info("Connected to Indopremier Demo") - return True - - # Real implementation would use actual API - auth_data = { - 'username': username, - 'password': password - } - - # This would be actual API call - self.is_connected = True - logger.info("Connected to Indopremier Live") - return True - - except Exception as e: - logger.error(f"Failed to connect to Indopremier: {e}") - return False - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - """Get Indonesian market data""" - try: - # For demo, generate realistic Indonesian stock data - dates = pd.date_range(end=datetime.now(), periods=count, freq='1h') - - # Realistic prices for Indonesian stocks - base_prices = { - 'BBCA.JK': 9000, # BCA around 9,000 IDR - 'BBRI.JK': 4500, # BRI around 4,500 IDR - 'BMRI.JK': 8500, # Mandiri around 8,500 IDR - 'TLKM.JK': 3200, # Telkom around 3,200 IDR - 'ASII.JK': 6800, # Astra around 6,800 IDR - 'UNVR.JK': 7200, # Unilever around 7,200 IDR - 'USDID': 15400, # USD/IDR around 15,400 - 'XAUIDR': 1000000, # Gold around 1M IDR per oz - } - - base_price = base_prices.get(symbol, 5000) - - # Indonesian market volatility (generally lower than crypto) - volatility = 0.015 if '.JK' in symbol else 0.008 # 1.5% for stocks, 0.8% for forex - - # Generate price movements - returns = np.random.randn(count) * volatility - prices = base_price * (1 + returns).cumprod() - - df = pd.DataFrame({ - 'time': dates, - 'open': prices, - 'high': prices * (1 + np.random.uniform(0, 0.01, count)), - 'low': prices * (1 - np.random.uniform(0, 0.01, count)), - 'close': prices, - 'volume': np.random.randint(100000, 1000000, count) # Indonesian market volumes - }) - - # Ensure OHLC integrity - df['high'] = df[['high', 'close', 'open']].max(axis=1) - df['low'] = df[['low', 'close', 'open']].min(axis=1) - - # Adjust for Indonesian market hours (09:00-16:00 WIB, Mon-Fri) - # Filter out weekend data for stock symbols - if '.JK' in symbol: - df = df[df['time'].dt.weekday < 5] # Monday=0, Sunday=6 - - return df - - except Exception as e: - logger.error(f"Failed to get Indopremier market data for {symbol}: {e}") - return pd.DataFrame() - def disconnect(self) -> bool: - """Disconnect from Indopremier""" - self.is_connected = False - logger.info("Disconnected from Indopremier") - return True - - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - return self.supported_symbols - - def get_current_price(self, symbol: str) -> Dict[str, float]: - """Get current bid/ask prices""" - try: - # For demo, use last price from market data - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - last_price = df.iloc[-1]['close'] - spread = last_price * 0.001 # 0.1% spread for Indonesian stocks - return { - "bid": last_price - spread/2, - "ask": last_price + spread/2 - } - return {"bid": 0.0, "ask": 0.0} - except Exception as e: - logger.error(f"Failed to get Indopremier current price for {symbol}: {e}") - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """Place order (simulated for demo)""" - try: - order_id = str(int(time.time())) - - # For Indonesian stocks, size is in lots (100 shares) - if '.JK' in symbol: - size = max(1, int(size)) # Minimum 1 lot - - order = Order( - order_id=order_id, - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - - # Simulate immediate execution for demo - order.status = OrderStatus.FILLED - order.filled_size = size - - current_price = self.get_current_price(symbol) - order.avg_fill_price = current_price['ask'] if side.lower() == 'buy' else current_price['bid'] - - logger.info(f"Indopremier demo order: {side} {size} {symbol} at {order.avg_fill_price}") - return order - - except Exception as e: - logger.error(f"Failed to place Indopremier order: {e}") - order = Order( - order_id="failed", - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - order.status = OrderStatus.REJECTED - return order - - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - logger.info(f"Indopremier demo: Order {order_id} cancelled") - return True - - def get_positions(self) -> List[Position]: - """Get all open positions""" - # For demo, return empty list - return [] - - def get_orders(self) -> List[Order]: - """Get all pending orders""" - # For demo, return empty list - return [] - - def get_account_info(self) -> AccountInfo: - """Get account information""" - try: - return AccountInfo( - balance=1000000000, # 1 billion IDR demo balance - equity=1000000000, - margin=0.0, - free_margin=1000000000, - margin_level=100.0, - currency="IDR" - ) - except Exception as e: - logger.error(f"Failed to get Indopremier account info: {e}") - return AccountInfo(0, 0, 0, 0, 0, "IDR") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - # For demo, return empty list - return [] - -class XMIndonesiaBroker(BaseBroker): - """ - XM Indonesia - Popular international broker in Indonesia - Offers forex, commodities, and indices with good demo accounts - """ - - def __init__(self, demo: bool = True): - super().__init__("XM Indonesia") - self.demo = demo - - # XM Indonesia popular symbols - self.supported_symbols = [ - # Major Forex pairs - 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', - 'NZDUSD', 'EURGBP', 'EURJPY', 'GBPJPY', - - # IDR pairs (if available) - 'USDIDR', 'EURIDR', 'GBPIDR', 'JPYIDR', - - # Commodities popular in Indonesia - 'XAUUSD', 'XAGUSD', 'USOIL', 'UKOIL', 'NGAS', - - # Indices - 'US30', 'SPX500', 'NAS100', 'UK100', 'GER30', 'FRA40', - 'AUS200', 'JPN225', 'HK50', - - # Cryptocurrency CFDs - 'BTCUSD', 'ETHUSD', 'LTCUSD', 'XRPUSD' - ] - - def connect(self, credentials: Dict) -> bool: - """Connect to XM Indonesia""" - try: - login = credentials.get("login") - password = credentials.get("password") - server = credentials.get("server", "XM-Demo" if self.demo else "XM-Real") - - if not all([login, password]): - logger.error("XM Indonesia login and password required") - return False - - # XM uses MT4/MT5 platform, so similar to existing MT5 integration - self.is_connected = True - - logger.info(f"Connected to XM Indonesia {'Demo' if self.demo else 'Live'}") - return True - - except Exception as e: - logger.error(f"Failed to connect to XM Indonesia: {e}") - return False - - def disconnect(self) -> bool: - self.is_connected = False - return True - - def get_symbols(self) -> List[str]: - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - # Generate simulated forex data - dates = pd.date_range(end=datetime.now(), periods=count, freq='1h') - base_prices = {'EURUSD': 1.0850, 'USDIDR': 15400, 'XAUUSD': 2020} - base_price = base_prices.get(symbol, 1.0) - - returns = np.random.randn(count) * 0.01 - prices = base_price * (1 + returns).cumprod() - - return pd.DataFrame({ - 'time': dates, 'open': prices, 'high': prices * 1.002, - 'low': prices * 0.998, 'close': prices, 'volume': np.random.randint(1000, 10000, count) - }) - - def get_current_price(self, symbol: str) -> Dict[str, float]: - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - price = df.iloc[-1]['close'] - return {"bid": price - 0.0001, "ask": price + 0.0001} - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, size: float, - price: Optional[float] = None, stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - order = Order(str(int(time.time())), symbol, order_type, side.lower(), size, price) - order.status = OrderStatus.FILLED - return order - - def cancel_order(self, order_id: str) -> bool: - return True - - def get_positions(self) -> List[Position]: - return [] - - def get_orders(self) -> List[Order]: - return [] - - def get_account_info(self) -> AccountInfo: - return AccountInfo(10000, 10000, 0, 10000, 100, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - return [] - -class OctaFXIndonesiaBroker(BaseBroker): - """ - OctaFX Indonesia - Another popular international broker - Known for good spreads and demo accounts - """ - - def __init__(self, demo: bool = True): - super().__init__("OctaFX Indonesia") - self.demo = demo - - self.supported_symbols = [ - # Forex majors and minors - 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', - 'NZDUSD', 'EURGBP', 'EURJPY', 'GBPJPY', 'AUDJPY', 'NZDJPY', - 'EURCHF', 'GBPCHF', 'AUDCHF', 'NZDCHF', 'CADCHF', 'CHFJPY', - - # Exotic pairs including IDR - 'USDIDR', 'USDSGD', 'USDTHB', 'USDMYR', - - # Metals - 'XAUUSD', 'XAGUSD', 'XPDUSD', 'XPTUSD', - - # Energies - 'USOIL', 'UKOIL', 'NGAS', - - # Indices - 'SPX500', 'NAS100', 'US30', 'UK100', 'GER30', 'FRA40', 'ESP35', - 'ITA40', 'AUS200', 'JPN225', 'HK50' - ] - - def connect(self, credentials: Dict) -> bool: - self.is_connected = True - return True - - def disconnect(self) -> bool: - self.is_connected = False - return True - - def get_symbols(self) -> List[str]: - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - dates = pd.date_range(end=datetime.now(), periods=count, freq='1h') - base_price = 1.0850 if 'EUR' in symbol else 15400 if 'IDR' in symbol else 100 - returns = np.random.randn(count) * 0.01 - prices = base_price * (1 + returns).cumprod() - return pd.DataFrame({ - 'time': dates, 'open': prices, 'high': prices * 1.001, - 'low': prices * 0.999, 'close': prices, 'volume': np.random.randint(1000, 5000, count) - }) - - def get_current_price(self, symbol: str) -> Dict[str, float]: - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - price = df.iloc[-1]['close'] - return {"bid": price - 0.0001, "ask": price + 0.0001} - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, size: float, - price: Optional[float] = None, stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - order = Order(str(int(time.time())), symbol, order_type, side.lower(), size, price) - order.status = OrderStatus.FILLED - return order - - def cancel_order(self, order_id: str) -> bool: - return True - - def get_positions(self) -> List[Position]: - return [] - - def get_orders(self) -> List[Order]: - return [] - - def get_account_info(self) -> AccountInfo: - return AccountInfo(10000, 10000, 0, 10000, 100, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - return [] - -class HSBCIndonesiaBroker(BaseBroker): - """ - HSBC Indonesia - International bank with trading platform - Good for forex and international markets - """ - - def __init__(self, demo: bool = True): - super().__init__("HSBC Indonesia") - self.demo = demo - - self.supported_symbols = [ - # Major currencies - 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', - - # Asian currencies (HSBC specialty) - 'USDIDR', 'USDSGD', 'USDHKD', 'USDKRW', 'USDCNY', 'USDTHB', - 'USDMYR', 'USDPHP', 'USDVND', - - # Cross currencies - 'EURIDR', 'GBPIDR', 'AUDIDR', 'JPYIDR', 'SGDIDR', - - # Precious metals - 'XAUUSD', 'XAGUSD' - ] - - def connect(self, credentials: Dict) -> bool: - self.is_connected = True - return True - - def disconnect(self) -> bool: - self.is_connected = False - return True - - def get_symbols(self) -> List[str]: - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - dates = pd.date_range(end=datetime.now(), periods=count, freq='1h') - base_price = 15400 if 'IDR' in symbol else 1.0850 if 'EUR' in symbol else 100 - returns = np.random.randn(count) * 0.008 - prices = base_price * (1 + returns).cumprod() - return pd.DataFrame({ - 'time': dates, 'open': prices, 'high': prices * 1.001, - 'low': prices * 0.999, 'close': prices, 'volume': np.random.randint(500, 2000, count) - }) - - def get_current_price(self, symbol: str) -> Dict[str, float]: - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - price = df.iloc[-1]['close'] - return {"bid": price - 0.0002, "ask": price + 0.0002} - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, size: float, - price: Optional[float] = None, stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - order = Order(str(int(time.time())), symbol, order_type, side.lower(), size, price) - order.status = OrderStatus.FILLED - return order - - def cancel_order(self, order_id: str) -> bool: - return True - - def get_positions(self) -> List[Position]: - return [] - - def get_orders(self) -> List[Order]: - return [] - - def get_account_info(self) -> AccountInfo: - return AccountInfo(10000, 10000, 0, 10000, 100, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - return [] - -# Factory function for Indonesian brokers -def create_indonesian_broker(broker_name: str, demo: bool = True) -> BaseBroker: - """Create Indonesian broker instance""" - brokers = { - 'indopremier': IndopremierBroker, - 'xm_indonesia': XMIndonesiaBroker, - 'octafx_indonesia': OctaFXIndonesiaBroker, - 'hsbc_indonesia': HSBCIndonesiaBroker - } - - broker_class = brokers.get(broker_name.lower()) - if broker_class: - return broker_class(demo=demo) - else: - raise ValueError(f"Unknown Indonesian broker: {broker_name}") - -# Indonesian market information -INDONESIAN_MARKET_INFO = { - 'market_hours': { - 'idx_stocks': 'Monday-Friday 09:00-16:00 WIB (GMT+7)', - 'forex_local': '24/5 (follows global forex)', - 'commodities': '24/5 (follows global commodities)' - }, - 'popular_stocks': { - 'BBCA.JK': 'Bank Central Asia - Largest private bank', - 'BBRI.JK': 'Bank Rakyat Indonesia - State-owned bank', - 'BMRI.JK': 'Bank Mandiri - Largest bank by assets', - 'TLKM.JK': 'Telkom Indonesia - Telecom giant', - 'ASII.JK': 'Astra International - Automotive conglomerate', - 'UNVR.JK': 'Unilever Indonesia - Consumer goods', - 'ICBP.JK': 'Indofood CBP - Food and beverages', - 'GGRM.JK': 'Gudang Garam - Cigarette manufacturer', - 'HMSP.JK': 'HM Sampoerna - Tobacco company' - }, - 'currency_info': { - 'base_currency': 'IDR (Indonesian Rupiah)', - 'typical_usd_idr': '15,000-16,000 IDR per USD', - 'volatility': 'Moderate, influenced by commodity prices' - }, - 'regulatory_info': { - 'regulator': 'OJK (Otoritas Jasa Keuangan)', - 'stock_exchange': 'IDX (Indonesia Stock Exchange)', - 'trading_lot': '100 shares minimum for most stocks' - } -} \ No newline at end of file diff --git a/core/brokers/interactive_brokers.py b/core/brokers/interactive_brokers.py deleted file mode 100644 index 3d24d2d..0000000 --- a/core/brokers/interactive_brokers.py +++ /dev/null @@ -1,491 +0,0 @@ -# core/brokers/interactive_brokers.py -""" -Interactive Brokers Integration for QuantumBotX -Professional-grade multi-asset trading platform -""" - -import pandas as pd -import time -from datetime import datetime, timedelta -from typing import Dict, List, Optional -import logging -import threading - -from .base_broker import ( - BaseBroker, OrderType, OrderStatus, Timeframe, - Position, Order, AccountInfo -) - -logger = logging.getLogger(__name__) - -class InteractiveBrokersBroker(BaseBroker): - """ - Interactive Brokers (IBKR) implementation using TWS API. - Supports stocks, forex, futures, options, and more. - """ - - def __init__(self, paper_trading: bool = True): - super().__init__("Interactive Brokers") - self.paper_trading = paper_trading - self.ib_app = None - self.client_id = 1 # Unique client ID - self.port = 7497 if paper_trading else 7496 # Paper vs Live port - self.host = "127.0.0.1" - self.is_connected_flag = False - - # Data storage - self.positions_data = {} - self.orders_data = {} - self.account_data = {} - self.market_data_cache = {} - - # Timeframe mapping (IB uses specific duration/bar size combinations) - self.timeframe_map = { - Timeframe.M1: ("1 D", "1 min"), # 1 day of 1-minute bars - Timeframe.M5: ("5 D", "5 mins"), # 5 days of 5-minute bars - Timeframe.M15: ("10 D", "15 mins"), # 10 days of 15-minute bars - Timeframe.M30: ("1 M", "30 mins"), # 1 month of 30-minute bars - Timeframe.H1: ("1 M", "1 hour"), # 1 month of 1-hour bars - Timeframe.H4: ("3 M", "4 hours"), # 3 months of 4-hour bars - Timeframe.D1: ("1 Y", "1 day"), # 1 year of daily bars - } - - def connect(self, credentials: Dict) -> bool: - """ - Connect to Interactive Brokers TWS/Gateway - credentials: {"host": "127.0.0.1", "port": 7497, "client_id": 1} - """ - try: - # Import here to avoid dependency issues if not installed - from ibapi.client import EClient - from ibapi.wrapper import EWrapper - from ibapi.contract import Contract - - # Override connection parameters if provided - self.host = credentials.get("host", self.host) - self.port = credentials.get("port", self.port) - self.client_id = credentials.get("client_id", self.client_id) - - # Create IB App class that combines EClient and EWrapper - class IBApp(EWrapper, EClient): - def __init__(self, broker_instance): - EClient.__init__(self, self) - self.broker = broker_instance - self.next_order_id = None - - def nextValidId(self, orderId: int): - """Callback when connection is established""" - self.next_order_id = orderId - self.broker.is_connected_flag = True - logger.info(f"IB connection established. Next order ID: {orderId}") - - def accountSummary(self, reqId: int, account: str, tag: str, value: str, currency: str): - """Account summary callback""" - if account not in self.broker.account_data: - self.broker.account_data[account] = {} - self.broker.account_data[account][tag] = { - 'value': value, - 'currency': currency - } - - def position(self, account: str, contract, position: float, avgCost: float): - """Position callback""" - symbol = contract.symbol - self.broker.positions_data[symbol] = { - 'account': account, - 'symbol': symbol, - 'position': position, - 'avg_cost': avgCost, - 'contract': contract - } - - def openOrder(self, orderId, contract, order, orderState): - """Open order callback""" - self.broker.orders_data[orderId] = { - 'order_id': orderId, - 'contract': contract, - 'order': order, - 'state': orderState - } - - def historicalData(self, reqId, bar): - """Historical data callback""" - if reqId not in self.broker.market_data_cache: - self.broker.market_data_cache[reqId] = [] - - self.broker.market_data_cache[reqId].append({ - 'date': bar.date, - 'open': bar.open, - 'high': bar.high, - 'low': bar.low, - 'close': bar.close, - 'volume': bar.volume - }) - - def error(self, reqId, errorCode, errorString, advancedOrderRejectJson=""): - """Error callback""" - logger.error(f"IB Error {errorCode}: {errorString}") - - # Create and connect IB app - self.ib_app = IBApp(self) - self.ib_app.connect(self.host, self.port, self.client_id) - - # Start message processing in separate thread - def run_loop(): - self.ib_app.run() - - api_thread = threading.Thread(target=run_loop, daemon=True) - api_thread.start() - - # Wait for connection - timeout = 10 # 10 seconds timeout - for _ in range(timeout * 10): # Check every 0.1 seconds - if self.is_connected_flag: - break - time.sleep(0.1) - - if self.is_connected_flag: - self.is_connected = True - - # Request account summary - self.ib_app.reqAccountSummary(1, "All", "$LEDGER") - time.sleep(2) # Wait for data - - # Load supported symbols (simplified list) - self.supported_symbols = [ - # Forex - 'EUR.USD', 'GBP.USD', 'USD.JPY', 'USD.CHF', 'AUD.USD', 'USD.CAD', - # Stocks - 'AAPL', 'GOOGL', 'MSFT', 'TSLA', 'AMZN', 'META', - # Futures - 'ES', 'NQ', 'YM', 'RTY', # Stock index futures - 'GC', 'SI', 'CL', # Commodity futures - ] - - logger.info(f"Connected to Interactive Brokers {'Paper' if self.paper_trading else 'Live'}") - return True - else: - logger.error("Failed to establish IB connection within timeout") - return False - - except ImportError: - logger.error("ibapi package not installed. Install with: pip install ibapi") - return False - except Exception as e: - logger.error(f"Failed to connect to Interactive Brokers: {e}") - self.is_connected = False - return False - - def disconnect(self) -> bool: - """Disconnect from Interactive Brokers""" - if self.ib_app: - self.ib_app.disconnect() - self.is_connected = False - self.is_connected_flag = False - logger.info("Disconnected from Interactive Brokers") - return True - - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - return self.supported_symbols - - def _create_contract(self, symbol: str) -> 'Contract': - """Create IB Contract object for symbol""" - from ibapi.contract import Contract - - contract = Contract() - - # Determine contract type based on symbol format - if '.' in symbol: # Forex (EUR.USD format) - base, quote = symbol.split('.') - contract.symbol = base - contract.secType = "CASH" - contract.currency = quote - contract.exchange = "IDEALPRO" - elif symbol in ['ES', 'NQ', 'YM', 'RTY', 'GC', 'SI', 'CL']: # Futures - contract.symbol = symbol - contract.secType = "FUT" - contract.exchange = "CME" # Simplified - contract.lastTradeDateOrContractMonth = "202412" # Would need dynamic - else: # Stocks - contract.symbol = symbol - contract.secType = "STK" - contract.currency = "USD" - contract.exchange = "SMART" - - return contract - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - """Get OHLCV market data from Interactive Brokers""" - if not self.is_connected: - raise Exception("Not connected to Interactive Brokers") - - try: - contract = self._create_contract(symbol) - duration, bar_size = self.timeframe_map[timeframe] - - # Request historical data - req_id = int(time.time()) # Unique request ID - self.market_data_cache[req_id] = [] - - self.ib_app.reqHistoricalData( - req_id, contract, "", duration, bar_size, "TRADES", 1, 1, False, [] - ) - - # Wait for data - timeout = 10 - for _ in range(timeout * 10): - if req_id in self.market_data_cache and len(self.market_data_cache[req_id]) > 0: - break - time.sleep(0.1) - - # Convert to DataFrame - data = self.market_data_cache.get(req_id, []) - if not data: - return pd.DataFrame() - - df_data = [] - for bar in data: - # Parse IB date format - try: - if len(bar['date']) == 8: # Daily format: 20231201 - date_obj = datetime.strptime(bar['date'], '%Y%m%d') - else: # Intraday format: 20231201 10:30:00 - date_obj = datetime.strptime(bar['date'], '%Y%m%d %H:%M:%S') - except: - date_obj = datetime.now() - - df_data.append({ - 'time': date_obj, - 'open': bar['open'], - 'high': bar['high'], - 'low': bar['low'], - 'close': bar['close'], - 'volume': bar['volume'] - }) - - # Clean up cache - del self.market_data_cache[req_id] - - return pd.DataFrame(df_data) - - except Exception as e: - logger.error(f"Failed to get IB market data for {symbol}: {e}") - return pd.DataFrame() - - def get_current_price(self, symbol: str) -> Dict[str, float]: - """Get current bid/ask prices""" - if not self.is_connected: - raise Exception("Not connected to Interactive Brokers") - - try: - # IB requires market data subscription for real-time prices - # For demo purposes, return last close price as both bid/ask - # In real implementation, would use reqMktData - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - last_price = df.iloc[-1]['close'] - return {"bid": last_price - 0.0001, "ask": last_price + 0.0001} - else: - return {"bid": 0.0, "ask": 0.0} - except Exception as e: - logger.error(f"Failed to get IB current price for {symbol}: {e}") - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """Place a trading order on Interactive Brokers""" - if not self.is_connected: - raise Exception("Not connected to Interactive Brokers") - - try: - from ibapi.order import Order as IBOrder - - contract = self._create_contract(symbol) - - # Create IB order - ib_order = IBOrder() - ib_order.action = "BUY" if side.lower() == "buy" else "SELL" - ib_order.totalQuantity = size - - # Set order type - if order_type in [OrderType.MARKET_BUY, OrderType.MARKET_SELL]: - ib_order.orderType = "MKT" - elif order_type in [OrderType.LIMIT_BUY, OrderType.LIMIT_SELL]: - ib_order.orderType = "LMT" - ib_order.lmtPrice = price - - # Get next order ID - if not self.ib_app.next_order_id: - logger.error("No valid order ID available") - raise Exception("No valid order ID") - - order_id = self.ib_app.next_order_id - self.ib_app.next_order_id += 1 - - # Place order - self.ib_app.placeOrder(order_id, contract, ib_order) - - # Create Order object - order = Order( - order_id=str(order_id), - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - - order.status = OrderStatus.PENDING - logger.info(f"IB order placed: {order_id} for {symbol}") - return order - - except Exception as e: - logger.error(f"Failed to place IB order: {e}") - order = Order( - order_id="failed", - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - order.status = OrderStatus.REJECTED - return order - - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - if not self.is_connected: - return False - - try: - self.ib_app.cancelOrder(int(order_id)) - return True - except Exception as e: - logger.error(f"Failed to cancel IB order {order_id}: {e}") - return False - - def get_positions(self) -> List[Position]: - """Get all open positions""" - if not self.is_connected: - return [] - - try: - # Request positions - self.ib_app.reqPositions() - time.sleep(2) # Wait for data - - positions = [] - for symbol, pos_data in self.positions_data.items(): - if pos_data['position'] != 0: # Only non-zero positions - position = Position( - symbol=symbol, - side='long' if pos_data['position'] > 0 else 'short', - size=abs(pos_data['position']), - entry_price=pos_data['avg_cost'], - current_price=pos_data['avg_cost'], # Would need market price - unrealized_pnl=0.0 # Would need calculation - ) - positions.append(position) - - return positions - - except Exception as e: - logger.error(f"Failed to get IB positions: {e}") - return [] - - def get_orders(self) -> List[Order]: - """Get all pending orders""" - if not self.is_connected: - return [] - - try: - # Request open orders - self.ib_app.reqOpenOrders() - time.sleep(2) # Wait for data - - orders = [] - for order_id, order_data in self.orders_data.items(): - order = Order( - order_id=str(order_id), - symbol=order_data['contract'].symbol, - order_type=OrderType.LIMIT_BUY, # Simplified - side=order_data['order'].action.lower(), - size=order_data['order'].totalQuantity, - price=getattr(order_data['order'], 'lmtPrice', None) - ) - order.status = OrderStatus.PENDING - orders.append(order) - - return orders - - except Exception as e: - logger.error(f"Failed to get IB orders: {e}") - return [] - - def get_account_info(self) -> AccountInfo: - """Get account information""" - if not self.is_connected: - return AccountInfo(0, 0, 0, 0, 0, "USD") - - try: - # Use cached account data - account_data = list(self.account_data.values())[0] if self.account_data else {} - - net_liquidation = float(account_data.get('NetLiquidation', {}).get('value', 0)) - total_cash = float(account_data.get('TotalCashValue', {}).get('value', 0)) - buying_power = float(account_data.get('BuyingPower', {}).get('value', 0)) - - return AccountInfo( - balance=total_cash, - equity=net_liquidation, - margin=0.0, # Would need calculation - free_margin=buying_power, - margin_level=100.0, # Would need calculation - currency="USD" - ) - - except Exception as e: - logger.error(f"Failed to get IB account info: {e}") - return AccountInfo(0, 0, 0, 0, 0, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - if not self.is_connected: - return [] - - try: - # IB trade history would require execution reports - # For now, return empty list - logger.warning("IB trade history not implemented - requires execution report handling") - return [] - - except Exception as e: - logger.error(f"Failed to get IB trade history: {e}") - return [] - - def normalize_symbol(self, symbol: str) -> str: - """Normalize symbol format for Interactive Brokers""" - # Convert common formats to IB format - symbol = symbol.upper() - - # Forex: EURUSD -> EUR.USD - forex_pairs = ['EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD'] - for pair in forex_pairs: - if symbol == pair: - return f"{pair[:3]}.{pair[3:]}" - - return symbol - - def is_market_open(self) -> bool: - """Check if markets are open (simplified)""" - now = datetime.now() - # US market hours: weekdays, roughly 9:30 AM - 4:00 PM ET - return now.weekday() < 5 # Simplified - -# Convenience function -def create_ib_broker(paper_trading: bool = True) -> InteractiveBrokersBroker: - """Create an Interactive Brokers broker instance""" - return InteractiveBrokersBroker(paper_trading=paper_trading) \ No newline at end of file diff --git a/core/brokers/tradingview_broker.py b/core/brokers/tradingview_broker.py deleted file mode 100644 index dca977c..0000000 --- a/core/brokers/tradingview_broker.py +++ /dev/null @@ -1,449 +0,0 @@ -# core/brokers/tradingview_broker.py -""" -TradingView Integration for QuantumBotX -Social trading platform with Pine Script integration -""" - -import pandas as pd -import time -import requests -import json -import websocket -from datetime import datetime, timedelta -from typing import Dict, List, Optional -import logging -import threading - -from .base_broker import ( - BaseBroker, OrderType, OrderStatus, Timeframe, - Position, Order, AccountInfo -) - -logger = logging.getLogger(__name__) - -class TradingViewBroker(BaseBroker): - """ - TradingView integration for QuantumBotX. - - Note: This is a conceptual implementation as TradingView doesn't have - a traditional trading API. In practice, this would work through: - 1. Webhook signals from TradingView alerts - 2. Screen scraping (not recommended) - 3. Third-party integrations - - This implementation shows how it would work architecturally. - """ - - def __init__(self, paper_trading: bool = True): - super().__init__("TradingView") - self.paper_trading = paper_trading - self.session = requests.Session() - self.websocket = None - self.webhook_server = None - - # TradingView doesn't provide direct API access - # This would work through webhook alerts - self.base_url = "https://www.tradingview.com" - - # Simulated data for demo purposes - self.portfolio = {} - self.pending_orders = {} - self.trade_history = [] - self.current_capital = 10000.0 - - # Timeframe mapping - self.timeframe_map = { - Timeframe.M1: "1", - Timeframe.M5: "5", - Timeframe.M15: "15", - Timeframe.M30: "30", - Timeframe.H1: "60", - Timeframe.H4: "240", - Timeframe.D1: "1D" - } - - def connect(self, credentials: Dict) -> bool: - """ - Connect to TradingView (conceptual) - credentials: {"username": "...", "password": "...", "webhook_secret": "..."} - """ - try: - username = credentials.get("username") - password = credentials.get("password") - webhook_secret = credentials.get("webhook_secret") - - if not all([username, webhook_secret]): - logger.error("TradingView username and webhook_secret are required") - return False - - # In real implementation, would set up webhook server - self._setup_webhook_server(webhook_secret) - - self.is_connected = True - - # Popular tradingview symbols - self.supported_symbols = [ - # Forex - 'EURUSD', 'GBPUSD', 'USDJPY', 'USDCHF', 'AUDUSD', 'USDCAD', - 'NZDUSD', 'EURGBP', 'EURJPY', 'GBPJPY', - # Crypto - 'BTCUSD', 'ETHUSD', 'ADAUSD', 'SOLUSD', 'DOGEUSD', - # Stocks - 'AAPL', 'GOOGL', 'MSFT', 'TSLA', 'AMZN', 'META', 'NVDA', - # Commodities - 'XAUUSD', 'XAGUSD', 'USOIL', 'UKOIL', - # Indices - 'SPX', 'DJI', 'NDX', 'RUT' - ] - - logger.info(f"Connected to TradingView {'Paper' if self.paper_trading else 'Live'}") - return True - - except Exception as e: - logger.error(f"Failed to connect to TradingView: {e}") - self.is_connected = False - return False - - def _setup_webhook_server(self, webhook_secret: str): - """Setup webhook server to receive TradingView alerts""" - try: - from flask import Flask, request, jsonify - - webhook_app = Flask(__name__) - - @webhook_app.route('/tradingview-webhook', methods=['POST']) - def handle_webhook(): - try: - # Verify webhook secret - received_secret = request.headers.get('X-Webhook-Secret') - if received_secret != webhook_secret: - return jsonify({'error': 'Invalid webhook secret'}), 401 - - # Parse alert data - alert_data = request.get_json() - self._process_tradingview_alert(alert_data) - - return jsonify({'status': 'success'}), 200 - - except Exception as e: - logger.error(f"Webhook error: {e}") - return jsonify({'error': str(e)}), 500 - - # Run webhook server in background thread - def run_webhook(): - webhook_app.run(host='0.0.0.0', port=5001, debug=False) - - webhook_thread = threading.Thread(target=run_webhook, daemon=True) - webhook_thread.start() - - logger.info("TradingView webhook server started on port 5001") - - except ImportError: - logger.warning("Flask not available for webhook server") - except Exception as e: - logger.error(f"Failed to setup webhook server: {e}") - - def _process_tradingview_alert(self, alert_data: Dict): - """Process incoming TradingView alert""" - try: - # Expected alert format: - # { - # "symbol": "EURUSD", - # "action": "buy" or "sell", - # "price": 1.0850, - # "stop_loss": 1.0800, - # "take_profit": 1.0900, - # "quantity": 1.0, - # "strategy": "My Strategy" - # } - - symbol = alert_data.get('symbol') - action = alert_data.get('action', '').lower() - price = float(alert_data.get('price', 0)) - quantity = float(alert_data.get('quantity', 1.0)) - - if action in ['buy', 'sell'] and symbol and price > 0: - # Execute the trade - order_type = OrderType.MARKET_BUY if action == 'buy' else OrderType.MARKET_SELL - - order = self.place_order( - symbol=symbol, - order_type=order_type, - side=action, - size=quantity, - price=price, - stop_loss=alert_data.get('stop_loss'), - take_profit=alert_data.get('take_profit') - ) - - logger.info(f"TradingView alert processed: {action} {quantity} {symbol} at {price}") - - except Exception as e: - logger.error(f"Failed to process TradingView alert: {e}") - - def disconnect(self) -> bool: - """Disconnect from TradingView""" - self.is_connected = False - logger.info("Disconnected from TradingView") - return True - - def get_symbols(self) -> List[str]: - """Get list of available trading symbols""" - return self.supported_symbols - - def get_market_data(self, symbol: str, timeframe: Timeframe, count: int = 500) -> pd.DataFrame: - """ - Get market data from TradingView - Note: This would require web scraping or third-party API - """ - try: - # For demo purposes, generate simulated data - # In real implementation, would scrape TradingView charts or use third-party API - - logger.warning("TradingView market data: Using simulated data (real implementation would require scraping)") - - # Generate simulated price data - dates = pd.date_range(end=datetime.now(), periods=count, freq='1h') - - # Base prices for different symbols - base_prices = { - 'EURUSD': 1.0850, 'GBPUSD': 1.2650, 'USDJPY': 148.50, - 'BTCUSD': 42000, 'ETHUSD': 2500, 'AAPL': 190.0, - 'XAUUSD': 2020.0, 'SPX': 4500.0 - } - - base_price = base_prices.get(symbol, 100.0) - - # Generate price movements - returns = np.random.randn(count) * 0.01 # 1% volatility - prices = base_price * (1 + returns).cumprod() - - df = pd.DataFrame({ - 'time': dates, - 'open': prices, - 'high': prices * (1 + np.random.uniform(0, 0.005, count)), - 'low': prices * (1 - np.random.uniform(0, 0.005, count)), - 'close': prices, - 'volume': np.random.randint(1000, 10000, count) - }) - - # Ensure OHLC integrity - df['high'] = df[['high', 'close', 'open']].max(axis=1) - df['low'] = df[['low', 'close', 'open']].min(axis=1) - - return df - - except Exception as e: - logger.error(f"Failed to get TradingView market data for {symbol}: {e}") - return pd.DataFrame() - - def get_current_price(self, symbol: str) -> Dict[str, float]: - """Get current bid/ask prices""" - try: - # In real implementation, would scrape TradingView or use websocket - df = self.get_market_data(symbol, Timeframe.M1, 1) - if not df.empty: - last_price = df.iloc[-1]['close'] - spread = last_price * 0.0001 # Typical spread - return { - "bid": last_price - spread/2, - "ask": last_price + spread/2 - } - return {"bid": 0.0, "ask": 0.0} - - except Exception as e: - logger.error(f"Failed to get TradingView current price for {symbol}: {e}") - return {"bid": 0.0, "ask": 0.0} - - def place_order(self, symbol: str, order_type: OrderType, side: str, - size: float, price: Optional[float] = None, - stop_loss: Optional[float] = None, - take_profit: Optional[float] = None) -> Order: - """ - Place order (simulated for TradingView) - In practice, this would trigger through connected broker - """ - try: - order_id = str(int(time.time())) - - # Simulate order execution - if order_type in [OrderType.MARKET_BUY, OrderType.MARKET_SELL]: - current_price = self.get_current_price(symbol) - execution_price = current_price['ask'] if side.lower() == 'buy' else current_price['bid'] - else: - execution_price = price - - # Create order - order = Order( - order_id=order_id, - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=execution_price - ) - - # Simulate immediate execution for market orders - if order_type in [OrderType.MARKET_BUY, OrderType.MARKET_SELL]: - order.status = OrderStatus.FILLED - order.filled_size = size - order.avg_fill_price = execution_price - - # Update portfolio - if symbol not in self.portfolio: - self.portfolio[symbol] = {'long': 0, 'short': 0, 'avg_price': 0} - - if side.lower() == 'buy': - self.portfolio[symbol]['long'] += size - else: - self.portfolio[symbol]['short'] += size - - # Add to trade history - self.trade_history.append({ - 'time': datetime.now(), - 'symbol': symbol, - 'side': side.lower(), - 'size': size, - 'price': execution_price, - 'order_id': order_id - }) - - logger.info(f"TradingView simulated order executed: {side} {size} {symbol} at {execution_price}") - else: - order.status = OrderStatus.PENDING - self.pending_orders[order_id] = order - - return order - - except Exception as e: - logger.error(f"Failed to place TradingView order: {e}") - order = Order( - order_id="failed", - symbol=symbol, - order_type=order_type, - side=side.lower(), - size=size, - price=price - ) - order.status = OrderStatus.REJECTED - return order - - def cancel_order(self, order_id: str) -> bool: - """Cancel an existing order""" - try: - if order_id in self.pending_orders: - del self.pending_orders[order_id] - return True - return False - except Exception as e: - logger.error(f"Failed to cancel TradingView order {order_id}: {e}") - return False - - def get_positions(self) -> List[Position]: - """Get all open positions""" - try: - positions = [] - - for symbol, pos_data in self.portfolio.items(): - long_size = pos_data['long'] - short_size = pos_data['short'] - net_size = long_size - short_size - - if net_size != 0: - current_price_data = self.get_current_price(symbol) - current_price = current_price_data['bid'] if net_size > 0 else current_price_data['ask'] - - position = Position( - symbol=symbol, - side='long' if net_size > 0 else 'short', - size=abs(net_size), - entry_price=pos_data.get('avg_price', current_price), - current_price=current_price, - unrealized_pnl=0.0 # Would calculate based on entry vs current - ) - positions.append(position) - - return positions - - except Exception as e: - logger.error(f"Failed to get TradingView positions: {e}") - return [] - - def get_orders(self) -> List[Order]: - """Get all pending orders""" - return list(self.pending_orders.values()) - - def get_account_info(self) -> AccountInfo: - """Get account information""" - try: - # Simulate account info - return AccountInfo( - balance=self.current_capital, - equity=self.current_capital, # Simplified - margin=0.0, - free_margin=self.current_capital, - margin_level=100.0, - currency="USD" - ) - - except Exception as e: - logger.error(f"Failed to get TradingView account info: {e}") - return AccountInfo(0, 0, 0, 0, 0, "USD") - - def get_trade_history(self, days: int = 30) -> List[Dict]: - """Get trade history""" - try: - cutoff_date = datetime.now() - timedelta(days=days) - recent_trades = [ - trade for trade in self.trade_history - if trade['time'] >= cutoff_date - ] - return recent_trades - - except Exception as e: - logger.error(f"Failed to get TradingView trade history: {e}") - return [] - - def normalize_symbol(self, symbol: str) -> str: - """Normalize symbol format for TradingView""" - # TradingView uses various symbol formats - symbol = symbol.upper() - - # Convert some common formats - if symbol == 'XAUUSD': - return 'GOLD' - elif symbol == 'XAGUSD': - return 'SILVER' - elif symbol.endswith('USDT'): - return symbol.replace('USDT', 'USD') - - return symbol - - def is_market_open(self) -> bool: - """TradingView shows global markets - always something open""" - return True - - def create_pine_script_strategy(self, strategy_code: str) -> str: - """ - Create a Pine Script strategy (conceptual) - Returns strategy ID for webhook alerts - """ - try: - # In real implementation, would create TradingView strategy - # and set up webhook alerts - - strategy_id = f"strategy_{int(time.time())}" - - logger.info(f"Pine Script strategy created (simulated): {strategy_id}") - logger.info("Set up TradingView alerts with webhook URL: http://your-server.com:5001/tradingview-webhook") - - return strategy_id - - except Exception as e: - logger.error(f"Failed to create Pine Script strategy: {e}") - return "" - -# Convenience function -def create_tradingview_broker(paper_trading: bool = True) -> TradingViewBroker: - """Create a TradingView broker instance""" - return TradingViewBroker(paper_trading=paper_trading) \ No newline at end of file diff --git a/core/interfaces/__init__.py b/core/interfaces/__init__.py deleted file mode 100644 index 4bb2b8f..0000000 --- a/core/interfaces/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Init file for core/interfaces \ No newline at end of file diff --git a/testing/indonesian_market_demo.py b/testing/indonesian_market_demo.py deleted file mode 100644 index a0a82c7..0000000 --- a/testing/indonesian_market_demo.py +++ /dev/null @@ -1,353 +0,0 @@ -#!/usr/bin/env python3 -""" -Indonesian Market Trading Demo for QuantumBotX -Showcasing opportunities in Indonesian financial markets -""" - -import sys -import os -import pandas as pd -import numpy as np -from datetime import datetime, timedelta - -# Add the project root to the path -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def demo_indonesian_market_overview(): - """Overview of Indonesian trading opportunities""" - print("๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesian Market Trading Opportunities") - print("=" * 60) - print("Welcome to the Indonesian Financial Markets!") - print("=" * 60) - - market_segments = { - 'IDX Stocks (Jakarta Stock Exchange)': { - 'description': 'Local Indonesian companies', - 'examples': ['BBCA.JK (BCA)', 'BBRI.JK (BRI)', 'TLKM.JK (Telkom)'], - 'trading_hours': '09:00-16:00 WIB (GMT+7)', - 'currency': 'IDR (Indonesian Rupiah)', - 'min_lot': '100 shares', - 'opportunities': ['Banking sector growth', 'Infrastructure development', 'Consumer goods expansion'] - }, - 'USD/IDR Forex': { - 'description': 'Indonesian Rupiah currency trading', - 'examples': ['USDIDR', 'EURIDR', 'JPYIDR'], - 'trading_hours': '24/5 (Global forex hours)', - 'currency': 'IDR pairs', - 'min_lot': 'Varies by broker', - 'opportunities': ['Commodity-driven moves', 'Central bank policy', 'Tourism recovery'] - }, - 'International Markets via Indonesian Brokers': { - 'description': 'Global markets through local brokers', - 'examples': ['XAUUSD', 'US stocks', 'Major forex pairs'], - 'trading_hours': 'Varies by market', - 'currency': 'USD typically', - 'min_lot': 'Standard international', - 'opportunities': ['Global diversification', 'USD income', 'Hedge against IDR'] - } - } - - print("\\n๐Ÿ“Š Indonesian Market Segments:") - for i, (segment, details) in enumerate(market_segments.items(), 1): - print(f"\\n{i}. {segment}") - print(f" ๐Ÿ“ Description: {details['description']}") - print(f" ๐Ÿ“ˆ Examples: {', '.join(details['examples'])}") - print(f" โฐ Hours: {details['trading_hours']}") - print(f" ๐Ÿ’ฐ Currency: {details['currency']}") - print(f" ๐ŸŽฏ Opportunities: {', '.join(details['opportunities'][:2])}") - -def demo_indonesian_brokers(): - """Showcase Indonesian brokers with demo accounts""" - print("\\n๐Ÿข Indonesian Brokers with Demo Accounts") - print("=" * 60) - - brokers = [ - { - 'name': 'Indopremier Securities (IPOT)', - 'type': 'Local Indonesian Broker', - 'specialties': ['IDX Stocks', 'Local bonds', 'Indonesian mutual funds'], - 'demo_account': 'Yes - Full IDX access', - 'advantages': ['Local market expertise', 'IDR-based trading', 'Indonesian customer service'], - 'website': 'https://www.indopremier.com/', - 'best_for': 'Indonesian stock market and local investments' - }, - { - 'name': 'XM Indonesia', - 'type': 'International Broker (Indonesia Office)', - 'specialties': ['Forex', 'CFDs', 'Commodities', 'Crypto CFDs'], - 'demo_account': 'Yes - $10,000 virtual', - 'advantages': ['Global markets', 'MT4/MT5 platform', 'Indonesian support'], - 'website': 'https://www.xm.com/id/', - 'best_for': 'Forex and international markets' - }, - { - 'name': 'OctaFX Indonesia', - 'type': 'International Broker (Popular in Indonesia)', - 'specialties': ['Forex', 'Metals', 'Indices', 'Energies'], - 'demo_account': 'Yes - Unlimited time', - 'advantages': ['Tight spreads', 'Fast execution', 'Indonesian community'], - 'website': 'https://www.octafx.com/id/', - 'best_for': 'Professional forex trading' - }, - { - 'name': 'HSBC Indonesia', - 'type': 'International Bank', - 'specialties': ['Forex', 'Asian currencies', 'Trade finance'], - 'demo_account': 'Available for qualified clients', - 'advantages': ['Banking integration', 'Asian market focus', 'Multi-currency'], - 'website': 'Contact local HSBC branch', - 'best_for': 'Currency hedging and international business' - } - ] - - print("\\n๐ŸŽฏ Recommended Brokers for Indonesian Traders:") - for i, broker in enumerate(brokers, 1): - print(f"\\n{i}. {broker['name']}") - print(f" ๐Ÿข Type: {broker['type']}") - print(f" ๐Ÿ“ˆ Specialties: {', '.join(broker['specialties'][:3])}") - print(f" ๐Ÿงช Demo Account: {broker['demo_account']}") - print(f" โญ Best For: {broker['best_for']}") - print(f" ๐ŸŒ Website: {broker['website']}") - -def demo_idx_stocks_trading(): - """Demo trading Indonesian stocks""" - print("\\n๐Ÿ“ˆ IDX Stock Trading Simulation") - print("=" * 60) - - # Simulate some popular Indonesian stocks - idx_stocks = [ - {'symbol': 'BBCA.JK', 'name': 'Bank Central Asia', 'price': 9150, 'sector': 'Banking'}, - {'symbol': 'BBRI.JK', 'name': 'Bank Rakyat Indonesia', 'price': 4520, 'sector': 'Banking'}, - {'symbol': 'TLKM.JK', 'name': 'Telkom Indonesia', 'price': 3280, 'sector': 'Telecommunications'}, - {'symbol': 'ASII.JK', 'name': 'Astra International', 'price': 6750, 'sector': 'Automotive'}, - {'symbol': 'UNVR.JK', 'name': 'Unilever Indonesia', 'price': 7100, 'sector': 'Consumer Goods'}, - ] - - print("\\n๐Ÿฆ Popular IDX Stocks (Simulated Prices):") - print("Symbol | Company | Price (IDR) | Sector") - print("-" * 70) - - total_portfolio_value = 0 - - for stock in idx_stocks: - # Simulate small price movements - current_price = stock['price'] * (1 + np.random.uniform(-0.02, 0.02)) - change_pct = ((current_price - stock['price']) / stock['price']) * 100 - - # Simulate trading with 1000 IDR capital per stock - shares_affordable = int(100000 / current_price) # 100k IDR investment - position_value = shares_affordable * current_price - total_portfolio_value += position_value - - color = "๐Ÿ“ˆ" if change_pct > 0 else "๐Ÿ“‰" if change_pct < 0 else "โžก๏ธ" - - print(f"{stock['symbol']:10} | {stock['name']:25} | {current_price:8.0f} {color} | {stock['sector']}") - - print(f"\\n๐Ÿ’ผ Simulated Portfolio Value: {total_portfolio_value:,.0f} IDR") - print(f"๐Ÿ’ฐ Equivalent in USD: ${total_portfolio_value/15400:.2f} (assuming 1 USD = 15,400 IDR)") - -def demo_usd_idr_trading(): - """Demo USD/IDR forex trading""" - print("\\n๐Ÿ’ฑ USD/IDR Forex Trading Simulation") - print("=" * 60) - - # Current USD/IDR around 15,400 - base_rate = 15400 - - # Simulate daily USD/IDR movements - days = 30 - dates = pd.date_range(end=datetime.now(), periods=days, freq='D') - - # IDR volatility (typically 0.5-1% daily) - daily_changes = np.random.randn(days) * 0.008 # 0.8% daily volatility - rates = base_rate * (1 + daily_changes).cumprod() - - print(f"\\n๐Ÿ“Š USD/IDR Rate Simulation (Last {days} days):") - print(f"Starting Rate: {base_rate:,.0f} IDR per USD") - print(f"Ending Rate: {rates[-1]:,.0f} IDR per USD") - print(f"Total Change: {((rates[-1] - base_rate) / base_rate) * 100:+.2f}%") - - # Trading simulation - position_size = 10000 # $10,000 USD position - entry_rate = rates[0] - exit_rate = rates[-1] - - if rates[-1] > rates[0]: # USD strengthened - pnl_usd = position_size * ((exit_rate - entry_rate) / entry_rate) - direction = "USD strengthened" - else: # USD weakened - pnl_usd = position_size * ((exit_rate - entry_rate) / entry_rate) - direction = "USD weakened" - - pnl_idr = pnl_usd * exit_rate - - print(f"\\n๐Ÿ’น Trading Simulation:") - print(f"Position: Long ${position_size:,} USD vs IDR") - print(f"Entry Rate: {entry_rate:,.0f} IDR/USD") - print(f"Exit Rate: {exit_rate:,.0f} IDR/USD") - print(f"Market Move: {direction}") - print(f"P&L: ${pnl_usd:+,.2f} USD (or {pnl_idr:+,.0f} IDR)") - -def demo_strategy_performance_indonesia(): - """Test strategies on Indonesian markets""" - print("\\n๐Ÿค– Strategy Performance on Indonesian Markets") - print("=" * 60) - - from core.brokers.indonesian_brokers import IndopremierBroker - - # Create Indonesian broker instance - broker = IndopremierBroker(demo=True) - - # Test symbols - test_symbols = [ - ('BBCA.JK', 'Bank Central Asia'), - ('USDIDR', 'USD/IDR Forex'), - ('XAUIDR', 'Gold in IDR') - ] - - print("\\n๐Ÿ“ˆ Testing QuantumBotX Strategies on Indonesian Markets:") - - for symbol, name in test_symbols: - try: - # Get simulated market data - df = broker.get_market_data(symbol, broker.timeframe_map[broker.Timeframe.H1] if hasattr(broker, 'timeframe_map') else 'H1', 500) - - if not df.empty: - # Calculate basic metrics - volatility = (df['close'].std() / df['close'].mean()) * 100 - price_range = f"{df['close'].min():.0f} - {df['close'].max():.0f}" - - # Assess suitability for different strategies - if volatility < 2: - strategy_rec = "Bollinger Reversion (Low volatility)" - elif volatility > 5: - strategy_rec = "Conservative MA Crossover (High volatility)" - else: - strategy_rec = "QuantumBotX Hybrid (Moderate volatility)" - - print(f"\\n๐Ÿ“Š {symbol} ({name}):") - print(f" Price Range: {price_range}") - print(f" Volatility: {volatility:.1f}%") - print(f" Recommended Strategy: {strategy_rec}") - print(f" Data Points: {len(df)} bars") - else: - print(f"\\nโŒ {symbol}: No data available") - - except Exception as e: - print(f"\\nโŒ {symbol}: Error - {e}") - -def demo_regulatory_compliance(): - """Indonesian regulatory information""" - print("\\nโš–๏ธ Indonesian Regulatory Compliance") - print("=" * 60) - - regulatory_info = { - 'Primary Regulator': { - 'name': 'OJK (Otoritas Jasa Keuangan)', - 'role': 'Financial Services Authority', - 'website': 'https://www.ojk.go.id/', - 'oversight': 'Banks, capital markets, insurance, pension funds' - }, - 'Stock Exchange': { - 'name': 'IDX (Indonesia Stock Exchange)', - 'location': 'Jakarta', - 'website': 'https://www.idx.co.id/', - 'trading_currency': 'Indonesian Rupiah (IDR)' - }, - 'Key Regulations': [ - 'Foreign investment limits in certain sectors', - 'Tax obligations for trading profits', - 'Anti-money laundering (AML) requirements', - 'Know Your Customer (KYC) procedures' - ], - 'Tax Considerations': [ - 'Capital gains tax on stock trading', - 'Forex trading taxation rules', - 'Withholding tax on foreign investments', - 'Professional trader vs investor classification' - ] - } - - print("\\n๐Ÿ›๏ธ Regulatory Framework:") - print(f"Primary Regulator: {regulatory_info['Primary Regulator']['name']}") - print(f"Stock Exchange: {regulatory_info['Stock Exchange']['name']}") - - print("\\nโš ๏ธ Important Considerations:") - for consideration in regulatory_info['Key Regulations'][:3]: - print(f" โ€ข {consideration}") - - print("\\n๐Ÿ’ฐ Tax Implications:") - for tax_item in regulatory_info['Tax Considerations'][:3]: - print(f" โ€ข {tax_item}") - - print("\\n๐Ÿ“ Recommendation:") - print(" โ€ข Consult with Indonesian tax advisor") - print(" โ€ข Understand local broker regulations") - print(" โ€ข Keep detailed trading records") - print(" โ€ข Consider professional trader registration if applicable") - -def main(): - """Main Indonesian market demo""" - print("๐Ÿ‡ฎ๐Ÿ‡ฉ SELAMAT DATANG! Welcome to Indonesian Market Trading!") - print("Your QuantumBotX system now supports Indonesian markets!") - print() - - # Run all demos - demo_indonesian_market_overview() - demo_indonesian_brokers() - demo_idx_stocks_trading() - demo_usd_idr_trading() - demo_strategy_performance_indonesia() - demo_regulatory_compliance() - - print("\\n" + "=" * 60) - print("๐ŸŽฏ NEXT STEPS FOR INDONESIAN TRADING") - print("=" * 60) - - next_steps = [ - { - 'step': '1. Choose Your Indonesian Broker', - 'recommendation': 'Start with XM Indonesia demo (easiest setup)', - 'action': 'Sign up for demo account at xm.com/id/' - }, - { - 'step': '2. Add Indonesian Configuration', - 'recommendation': 'Update .env file with Indonesian broker credentials', - 'action': 'Add XM_INDONESIA_LOGIN and XM_INDONESIA_PASSWORD' - }, - { - 'step': '3. Test IDX Stocks Strategy', - 'recommendation': 'Start with banking stocks (BBCA, BBRI, BMRI)', - 'action': 'Run backtests on Indonesian blue-chip stocks' - }, - { - 'step': '4. Explore USD/IDR Trading', - 'recommendation': 'Great for Indonesian traders to earn USD', - 'action': 'Test forex strategies on USD/IDR pair' - }, - { - 'step': '5. Regulatory Compliance', - 'recommendation': 'Understand Indonesian tax obligations', - 'action': 'Consult with local financial advisor' - } - ] - - for step_info in next_steps: - print(f"\\n{step_info['step']}") - print(f" ๐Ÿ’ก Recommendation: {step_info['recommendation']}") - print(f" ๐ŸŽฏ Action: {step_info['action']}") - - print("\\n๐ŸŽ‰ AMAZING OPPORTUNITY!") - print("=" * 60) - print("You're now building a trading system that covers:") - print("โœ… Global Forex (MT5, cTrader, XM)") - print("โœ… Cryptocurrency (Binance)") - print("โœ… US Stocks (Interactive Brokers)") - print("โœ… Social Trading (TradingView)") - print("โœ… Indonesian Markets (Local brokers)") - print() - print("๐ŸŒ FROM INDONESIA TO THE WORLD!") - print("Your trading system now spans the entire globe! ๐Ÿš€") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/testing/multi_broker_universe_demo.py b/testing/multi_broker_universe_demo.py deleted file mode 100644 index a4e8195..0000000 --- a/testing/multi_broker_universe_demo.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python3 -""" -Multi-Broker Universe Demo for QuantumBotX -Shows how to trade across all major platforms simultaneously -""" - -import sys -import os -import pandas as pd -import numpy as np -from datetime import datetime - -# Add the project root to the path -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -def demo_all_brokers(): - """Demonstrate all broker integrations""" - print("๐ŸŒ QuantumBotX Multi-Broker Universe Demo") - print("=" * 60) - print("Your trading system now supports ALL major platforms!") - print("=" * 60) - - brokers_info = [ - { - 'name': 'MetaTrader 5', - 'type': 'Forex/CFD Platform', - 'assets': ['EURUSD', 'GBPUSD', 'XAUUSD', 'US30', 'AAPL'], - 'advantages': ['Most forex brokers', 'Expert Advisors', 'Built-in indicators'], - 'best_for': 'Forex and traditional CFD trading' - }, - { - 'name': 'Binance', - 'type': 'Crypto Exchange', - 'assets': ['BTCUSDT', 'ETHUSDT', 'ADAUSDT', 'SOLUSDT', 'DOGEUSDT'], - 'advantages': ['24/7 trading', 'High liquidity', 'Low fees'], - 'best_for': 'Cryptocurrency trading and DeFi' - }, - { - 'name': 'cTrader', - 'type': 'Modern Forex Platform', - 'assets': ['EURUSD', 'GBPUSD', 'USDJPY', 'XAUUSD', 'USOIL'], - 'advantages': ['Advanced charting', 'Level II pricing', 'Fast execution'], - 'best_for': 'Professional forex trading' - }, - { - 'name': 'Interactive Brokers', - 'type': 'Multi-Asset Broker', - 'assets': ['AAPL', 'ES', 'EURUSD', 'GC', 'Options'], - 'advantages': ['Global markets', 'Low commissions', 'Advanced tools'], - 'best_for': 'Stocks, futures, and options' - }, - { - 'name': 'TradingView', - 'type': 'Social Trading Platform', - 'assets': ['All markets', 'Pine Script', 'Social signals'], - 'advantages': ['Community strategies', 'Advanced charts', 'Alerts'], - 'best_for': 'Strategy development and social trading' - } - ] - - print("\\n๐Ÿข Broker Overview:") - print("=" * 60) - - for i, broker in enumerate(brokers_info, 1): - print(f"\\n{i}. {broker['name']} ({broker['type']})") - print(f" ๐Ÿ“ˆ Assets: {', '.join(broker['assets'][:3])}{'...' if len(broker['assets']) > 3 else ''}") - print(f" โญ Best For: {broker['best_for']}") - print(f" ๐ŸŽฏ Key Advantages: {', '.join(broker['advantages'][:2])}") - - return brokers_info - -def demo_unified_portfolio(): - """Show how to create a unified portfolio across all brokers""" - print("\\n๐Ÿ’ผ Unified Portfolio Management") - print("=" * 60) - - portfolio_allocation = { - 'MT5 (Forex)': { - 'allocation': '30%', - 'symbols': ['EURUSD', 'GBPUSD', 'USDJPY'], - 'strategy': 'QuantumBotX Hybrid', - 'capital': '$3,000' - }, - 'Binance (Crypto)': { - 'allocation': '25%', - 'symbols': ['BTCUSDT', 'ETHUSDT', 'ADAUSDT'], - 'strategy': 'MA Crossover (Crypto-tuned)', - 'capital': '$2,500' - }, - 'cTrader (Forex Pro)': { - 'allocation': '20%', - 'symbols': ['XAUUSD', 'USOIL'], - 'strategy': 'Bollinger Reversion', - 'capital': '$2,000' - }, - 'Interactive Brokers (Stocks)': { - 'allocation': '20%', - 'symbols': ['AAPL', 'MSFT', 'TSLA'], - 'strategy': 'Quantum Velocity', - 'capital': '$2,000' - }, - 'TradingView (Signals)': { - 'allocation': '5%', - 'symbols': ['Community strategies'], - 'strategy': 'Pine Script alerts', - 'capital': '$500' - } - } - - print("\\n๐Ÿ“Š Portfolio Distribution ($10,000 total):") - print("-" * 60) - - total_expected_return = 0 - - for broker, details in portfolio_allocation.items(): - print(f"\\n{broker}") - print(f" ๐Ÿ’ฐ Capital: {details['capital']} ({details['allocation']})") - print(f" ๐Ÿ“ˆ Assets: {', '.join(details['symbols'][:3])}") - print(f" ๐Ÿค– Strategy: {details['strategy']}") - - # Simulate expected returns - expected_monthly = np.random.uniform(2, 8) # 2-8% monthly return - total_expected_return += expected_monthly * float(details['allocation'].strip('%')) / 100 - print(f" ๐Ÿ“Š Expected Monthly Return: {expected_monthly:.1f}%") - - print(f"\\n๐ŸŽฏ Portfolio Expected Monthly Return: {total_expected_return:.1f}%") - print(f"๐ŸŽฏ Portfolio Expected Annual Return: {total_expected_return * 12:.1f}%") - -def demo_risk_management(): - """Show unified risk management across all brokers""" - print("\\n๐Ÿ›ก๏ธ Unified Risk Management System") - print("=" * 60) - - risk_rules = [ - { - 'rule': 'Maximum Portfolio Risk', - 'value': '15% of total capital', - 'implementation': 'Sum of all open positions across all brokers' - }, - { - 'rule': 'Per-Broker Risk Limit', - 'value': '5% per broker maximum', - 'implementation': 'Individual broker position sizing limits' - }, - { - 'rule': 'Correlation Protection', - 'value': 'Max 3 correlated positions', - 'implementation': 'Cross-broker correlation monitoring' - }, - { - 'rule': 'Volatility Scaling', - 'value': 'Dynamic position sizing', - 'implementation': 'ATR-based sizing per asset class' - }, - { - 'rule': 'Emergency Brake', - 'value': 'Auto-stop at 10% daily loss', - 'implementation': 'Real-time P&L monitoring across all accounts' - } - ] - - print("\\n๐Ÿ”’ Global Risk Rules:") - for i, rule in enumerate(risk_rules, 1): - print(f"\\n{i}. {rule['rule']}: {rule['value']}") - print(f" Implementation: {rule['implementation']}") - -def demo_24_7_opportunities(): - """Show 24/7 trading opportunities""" - print("\\nโฐ 24/7 Global Trading Opportunities") - print("=" * 60) - - trading_schedule = [ - {'time': '00:00-08:00 UTC', 'active': ['Crypto (Binance)', 'Forex (Asian session)'], 'opportunity': 'Crypto volatility + Asian forex'}, - {'time': '08:00-16:00 UTC', 'active': ['All Forex', 'European Stocks', 'Crypto'], 'opportunity': 'European session overlap'}, - {'time': '13:00-17:00 UTC', 'active': ['US Stocks (IB)', 'US/EU Forex overlap', 'Crypto'], 'opportunity': 'Maximum liquidity window'}, - {'time': '17:00-00:00 UTC', 'active': ['Crypto (Binance)', 'Asian prep', 'After-hours'], 'opportunity': 'Crypto focus + overnight gaps'} - ] - - print("\\n๐ŸŒ Global Trading Sessions:") - for session in trading_schedule: - print(f"\\nโฐ {session['time']}") - print(f" ๐ŸŽฏ Active: {', '.join(session['active'])}") - print(f" ๐Ÿ’ก Opportunity: {session['opportunity']}") - - print("\\n๐Ÿ”ฅ Never Miss a Move:") - print(" โ€ข Forex: 24/5 traditional markets") - print(" โ€ข Crypto: 24/7/365 never stops") - print(" โ€ข Stocks: Pre/post market + global exchanges") - print(" โ€ข Commodities: Global futures markets") - -def demo_integration_benefits(): - """Show the benefits of integrated multi-broker system""" - print("\\n๐Ÿš€ Integration Benefits") - print("=" * 60) - - benefits = [ - { - 'category': 'Market Coverage', - 'benefits': [ - 'Trade forex, crypto, stocks, and commodities', - 'Access to global markets 24/7', - 'Never limited by single broker restrictions' - ] - }, - { - 'category': 'Risk Diversification', - 'benefits': [ - 'Spread risk across multiple platforms', - 'Reduce broker-specific risks', - 'Currency and asset class diversification' - ] - }, - { - 'category': 'Strategy Optimization', - 'benefits': [ - 'Different strategies for different markets', - 'Platform-specific advantages utilization', - 'Cross-market arbitrage opportunities' - ] - }, - { - 'category': 'Operational Excellence', - 'benefits': [ - 'Single dashboard for all trading', - 'Unified risk management', - 'Consolidated reporting and analytics' - ] - } - ] - - for benefit_group in benefits: - print(f"\\n๐Ÿ“ˆ {benefit_group['category']}:") - for benefit in benefit_group['benefits']: - print(f" โœ… {benefit}") - -def main(): - """Main demo function""" - print("๐ŸŽ‰ Welcome to the Financial Universe!") - print("Your QuantumBotX system now connects to EVERYTHING!") - print() - - # Demo all components - brokers_info = demo_all_brokers() - demo_unified_portfolio() - demo_risk_management() - demo_24_7_opportunities() - demo_integration_benefits() - - print("\\n" + "=" * 60) - print("๐ŸŽฏ IMPLEMENTATION ROADMAP") - print("=" * 60) - - roadmap = [ - { - 'phase': 'Week 1: Crypto Integration', - 'tasks': ['Set up Binance testnet', 'Test crypto strategies', 'Validate risk management'], - 'impact': 'Add 24/7 trading capability' - }, - { - 'phase': 'Week 2: cTrader Setup', - 'tasks': ['Create cTrader demo account', 'Test modern forex features', 'Compare with MT5'], - 'impact': 'Enhanced forex trading experience' - }, - { - 'phase': 'Week 3: Interactive Brokers', - 'tasks': ['Set up TWS paper trading', 'Test stock strategies', 'Explore futures'], - 'impact': 'Access to US stocks and global markets' - }, - { - 'phase': 'Week 4: TradingView Integration', - 'tasks': ['Set up webhook alerts', 'Create Pine Script strategies', 'Social trading'], - 'impact': 'Community-driven strategy development' - }, - { - 'phase': 'Month 2: Unified Platform', - 'tasks': ['Portfolio manager', 'Cross-broker risk management', 'Performance analytics'], - 'impact': 'Complete multi-broker trading ecosystem' - } - ] - - for i, phase in enumerate(roadmap, 1): - print(f"\\n{i}. {phase['phase']}") - print(f" ๐Ÿ“‹ Tasks: {', '.join(phase['tasks'][:2])}...") - print(f" ๐ŸŽฏ Impact: {phase['impact']}") - - print("\\n" + "=" * 60) - print("๐Ÿ† THE BIG PICTURE") - print("=" * 60) - print("\\n๐ŸŒŸ What You're Building:") - print(" โ€ข Universal Trading Platform - One system, all markets") - print(" โ€ข Risk-Managed Portfolio - Diversified across asset classes") - print(" โ€ข 24/7 Profit Machine - Never miss opportunities") - print(" โ€ข Future-Proof Architecture - Ready for any new broker") - - print("\\n๐Ÿ’ฐ Potential Impact:") - current_profit = 4649.94 - projected_increase = 2.5 # Conservative 2.5x increase - projected_profit = current_profit * projected_increase - - print(f" Current Demo Profit: ${current_profit:,.2f}") - print(f" With Multi-Broker: ${projected_profit:,.2f} (estimated)") - print(f" Improvement Factor: {projected_increase}x") - - print("\\n๐ŸŽ‰ Congratulations!") - print("You've just designed a trading system that rivals") - print("what hedge funds and prop trading firms use!") - print("\\nFrom learning to trade โ†’ Building a financial empire! ๐Ÿš€") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/testing/test_ctrader_broker.py b/testing/test_ctrader_broker.py deleted file mode 100644 index f693c9d..0000000 --- a/testing/test_ctrader_broker.py +++ /dev/null @@ -1,59 +0,0 @@ -# testing/test_ctrader_broker.py -import unittest -from unittest.mock import patch -from datetime import datetime -from core.brokers.ctrader_broker import CTraderBroker - -class TestCTraderBroker(unittest.TestCase): - """ - Test cases for the cTrader broker implementation, focusing on market hours. - """ - - def setUp(self): - """Set up a CTraderBroker instance for testing.""" - self.broker = CTraderBroker(demo=True) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_open_weekday(self, mock_datetime): - """Test that the market is open on a standard weekday.""" - # Wednesday, 12:00 UTC - mock_datetime.utcnow.return_value = datetime(2023, 1, 4, 12, 0, 0) - self.assertTrue(self.broker.is_market_open()) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_closed_saturday(self, mock_datetime): - """Test that the market is closed on Saturday.""" - # Saturday, 12:00 UTC - mock_datetime.utcnow.return_value = datetime(2023, 1, 7, 12, 0, 0) - self.assertFalse(self.broker.is_market_open()) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_opens_sunday_evening(self, mock_datetime): - """Test that the market opens on Sunday evening.""" - # Sunday, 22:01 UTC (market is open) - mock_datetime.utcnow.return_value = datetime(2023, 1, 8, 22, 1, 0) - self.assertTrue(self.broker.is_market_open()) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_closed_sunday_morning(self, mock_datetime): - """Test that the market is closed on Sunday morning.""" - # Sunday, 10:00 UTC (market is closed) - mock_datetime.utcnow.return_value = datetime(2023, 1, 8, 10, 0, 0) - self.assertFalse(self.broker.is_market_open()) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_closes_friday_evening(self, mock_datetime): - """Test that the market closes on Friday evening.""" - # Friday, 22:01 UTC (market is closed) - mock_datetime.utcnow.return_value = datetime(2023, 1, 6, 22, 1, 0) - self.assertFalse(self.broker.is_market_open()) - - @patch('core.brokers.ctrader_broker.datetime') - def test_is_market_open_friday_morning(self, mock_datetime): - """Test that the market is open on Friday morning.""" - # Friday, 10:00 UTC (market is open) - mock_datetime.utcnow.return_value = datetime(2023, 1, 6, 10, 0, 0) - self.assertTrue(self.broker.is_market_open()) - -if __name__ == '__main__': - unittest.main()