Initial commit — Trading AI Secure project complet
Architecture Docker (8 services), FastAPI, TimescaleDB, Redis, Streamlit. Stratégies : scalping, intraday, swing. MLEngine + RegimeDetector (HMM). BacktestEngine + WalkForwardAnalyzer + Optuna optimizer. Routes API complètes dont /optimize async. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
719
docs/IG_INTEGRATION.md
Normal file
719
docs/IG_INTEGRATION.md
Normal file
@@ -0,0 +1,719 @@
|
||||
# 🔌 Guide d'Intégration IG Markets - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Vue d'ensemble IG Markets](#vue-densemble-ig-markets)
|
||||
2. [Configuration Compte](#configuration-compte)
|
||||
3. [API REST](#api-rest)
|
||||
4. [Streaming Lightstreamer](#streaming-lightstreamer)
|
||||
5. [Gestion des Ordres](#gestion-des-ordres)
|
||||
6. [Risk Management IG](#risk-management-ig)
|
||||
7. [Migration Progressive](#migration-progressive)
|
||||
8. [Implémentation](#implémentation)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble IG Markets
|
||||
|
||||
### Pourquoi IG Markets ?
|
||||
|
||||
- ✅ **API complète** : REST + Streaming temps réel
|
||||
- ✅ **Compte démo gratuit** : Test sans risque
|
||||
- ✅ **Large gamme d'instruments** : Forex, Indices, Actions, Crypto
|
||||
- ✅ **CFD et DMA** : Flexibilité trading
|
||||
- ✅ **Guaranteed stops** : Protection slippage
|
||||
- ✅ **Documentation** : API bien documentée
|
||||
|
||||
### Architecture IG
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ IG MARKETS API │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ REST API STREAMING API │
|
||||
│ ├─ Authentication ├─ Lightstreamer │
|
||||
│ ├─ Account Info ├─ Prix temps réel │
|
||||
│ ├─ Market Data ├─ Positions updates │
|
||||
│ ├─ Orders (CRUD) └─ Account updates │
|
||||
│ ├─ Positions │
|
||||
│ └─ Historical Data │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Configuration Compte
|
||||
|
||||
### Étape 1 : Créer Compte Démo
|
||||
|
||||
1. **S'inscrire** : https://www.ig.com/uk/demo-account
|
||||
2. **Activer compte démo** : Capital virtuel £10,000
|
||||
3. **Accéder API** : Dashboard → API → Generate API Key
|
||||
|
||||
### Étape 2 : Obtenir Credentials
|
||||
|
||||
```yaml
|
||||
# config/ig_config.yaml (EXEMPLE - NE PAS COMMITER)
|
||||
ig_credentials:
|
||||
# Démo
|
||||
demo:
|
||||
api_key: "YOUR_DEMO_API_KEY"
|
||||
username: "YOUR_DEMO_USERNAME"
|
||||
password: "YOUR_DEMO_PASSWORD"
|
||||
account_id: "YOUR_DEMO_ACCOUNT_ID"
|
||||
api_url: "https://demo-api.ig.com/gateway/deal"
|
||||
|
||||
# Live (À ACTIVER APRÈS VALIDATION)
|
||||
live:
|
||||
api_key: "YOUR_LIVE_API_KEY"
|
||||
username: "YOUR_LIVE_USERNAME"
|
||||
password: "YOUR_LIVE_PASSWORD"
|
||||
account_id: "YOUR_LIVE_ACCOUNT_ID"
|
||||
api_url: "https://api.ig.com/gateway/deal"
|
||||
|
||||
# Lightstreamer
|
||||
lightstreamer:
|
||||
demo_url: "https://demo-apd.marketdatasys.com"
|
||||
live_url: "https://apd.marketdatasys.com"
|
||||
```
|
||||
|
||||
### Étape 3 : Sécuriser Credentials
|
||||
|
||||
```python
|
||||
# src/core/config_manager.py
|
||||
|
||||
import os
|
||||
from cryptography.fernet import Fernet
|
||||
import yaml
|
||||
|
||||
class SecureConfigManager:
|
||||
"""
|
||||
Gestion sécurisée des credentials IG
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Générer clé encryption (à stocker en variable d'environnement)
|
||||
self.key = os.getenv('ENCRYPTION_KEY') or Fernet.generate_key()
|
||||
self.cipher = Fernet(self.key)
|
||||
|
||||
def load_ig_credentials(self, environment='demo'):
|
||||
"""
|
||||
Charge credentials IG de manière sécurisée
|
||||
"""
|
||||
with open('config/ig_config.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
credentials = config['ig_credentials'][environment]
|
||||
|
||||
# Décrypter password si encrypté
|
||||
if credentials['password'].startswith('encrypted:'):
|
||||
encrypted_password = credentials['password'].replace('encrypted:', '')
|
||||
credentials['password'] = self.cipher.decrypt(
|
||||
encrypted_password.encode()
|
||||
).decode()
|
||||
|
||||
return credentials
|
||||
|
||||
def encrypt_password(self, password: str) -> str:
|
||||
"""Encrypte password"""
|
||||
return 'encrypted:' + self.cipher.encrypt(password.encode()).decode()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 API REST
|
||||
|
||||
### Authentification
|
||||
|
||||
```python
|
||||
# src/data/ig_connector.py
|
||||
|
||||
import requests
|
||||
from typing import Dict, Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IGRestAPI:
|
||||
"""
|
||||
Connecteur IG REST API
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: Dict):
|
||||
self.api_key = credentials['api_key']
|
||||
self.username = credentials['username']
|
||||
self.password = credentials['password']
|
||||
self.account_id = credentials['account_id']
|
||||
self.base_url = credentials['api_url']
|
||||
|
||||
self.session = requests.Session()
|
||||
self.access_token = None
|
||||
self.cst_token = None
|
||||
self.security_token = None
|
||||
|
||||
def authenticate(self) -> bool:
|
||||
"""
|
||||
Authentification IG API
|
||||
|
||||
Returns:
|
||||
True si succès, False sinon
|
||||
"""
|
||||
url = f"{self.base_url}/session"
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-IG-API-KEY': self.api_key,
|
||||
'Version': '2'
|
||||
}
|
||||
|
||||
payload = {
|
||||
'identifier': self.username,
|
||||
'password': self.password
|
||||
}
|
||||
|
||||
try:
|
||||
response = self.session.post(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
# Extraire tokens
|
||||
self.cst_token = response.headers.get('CST')
|
||||
self.security_token = response.headers.get('X-SECURITY-TOKEN')
|
||||
|
||||
# Sélectionner compte
|
||||
self._select_account(self.account_id)
|
||||
|
||||
logger.info("IG API authentication successful")
|
||||
return True
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"IG API authentication failed: {e}")
|
||||
return False
|
||||
|
||||
def _select_account(self, account_id: str):
|
||||
"""Sélectionne compte de trading"""
|
||||
url = f"{self.base_url}/session"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
headers['_method'] = 'PUT'
|
||||
|
||||
payload = {
|
||||
'accountId': account_id,
|
||||
'defaultAccount': True
|
||||
}
|
||||
|
||||
response = self.session.put(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
def _get_headers(self) -> Dict:
|
||||
"""Headers pour requêtes authentifiées"""
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-IG-API-KEY': self.api_key,
|
||||
'CST': self.cst_token,
|
||||
'X-SECURITY-TOKEN': self.security_token
|
||||
}
|
||||
|
||||
def get_account_info(self) -> Dict:
|
||||
"""Récupère informations compte"""
|
||||
url = f"{self.base_url}/accounts"
|
||||
|
||||
response = self.session.get(url, headers=self._get_headers())
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_market_details(self, epic: str) -> Dict:
|
||||
"""
|
||||
Récupère détails d'un marché
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché IG (ex: 'CS.D.EURUSD.MINI.IP')
|
||||
"""
|
||||
url = f"{self.base_url}/markets/{epic}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '3'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_positions(self) -> Dict:
|
||||
"""Récupère positions ouvertes"""
|
||||
url = f"{self.base_url}/positions"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '2'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def place_order(
|
||||
self,
|
||||
epic: str,
|
||||
direction: str,
|
||||
size: float,
|
||||
stop_loss: Optional[float] = None,
|
||||
take_profit: Optional[float] = None,
|
||||
guaranteed_stop: bool = True
|
||||
) -> Dict:
|
||||
"""
|
||||
Place ordre de trading
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
direction: 'BUY' ou 'SELL'
|
||||
size: Taille position
|
||||
stop_loss: Niveau stop-loss
|
||||
take_profit: Niveau take-profit
|
||||
guaranteed_stop: Utiliser guaranteed stop (recommandé)
|
||||
"""
|
||||
url = f"{self.base_url}/positions/otc"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '2'
|
||||
|
||||
payload = {
|
||||
'epic': epic,
|
||||
'direction': direction,
|
||||
'size': size,
|
||||
'orderType': 'MARKET',
|
||||
'timeInForce': 'FILL_OR_KILL',
|
||||
'guaranteedStop': guaranteed_stop,
|
||||
'currencyCode': 'GBP'
|
||||
}
|
||||
|
||||
# Ajouter stop-loss
|
||||
if stop_loss:
|
||||
payload['stopLevel'] = stop_loss
|
||||
payload['stopDistance'] = None # Calculé automatiquement
|
||||
|
||||
# Ajouter take-profit
|
||||
if take_profit:
|
||||
payload['limitLevel'] = take_profit
|
||||
payload['limitDistance'] = None
|
||||
|
||||
try:
|
||||
response = self.session.post(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
deal_reference = result.get('dealReference')
|
||||
|
||||
# Confirmer ordre
|
||||
return self._confirm_order(deal_reference)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Order placement failed: {e}")
|
||||
raise
|
||||
|
||||
def _confirm_order(self, deal_reference: str) -> Dict:
|
||||
"""Confirme exécution ordre"""
|
||||
url = f"{self.base_url}/confirms/{deal_reference}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def close_position(self, deal_id: str) -> Dict:
|
||||
"""Ferme position"""
|
||||
# Récupérer détails position
|
||||
position = self._get_position_details(deal_id)
|
||||
|
||||
url = f"{self.base_url}/positions/otc"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
headers['_method'] = 'DELETE'
|
||||
|
||||
payload = {
|
||||
'dealId': deal_id,
|
||||
'direction': 'SELL' if position['direction'] == 'BUY' else 'BUY',
|
||||
'size': position['size'],
|
||||
'orderType': 'MARKET'
|
||||
}
|
||||
|
||||
response = self.session.delete(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_historical_prices(
|
||||
self,
|
||||
epic: str,
|
||||
resolution: str = 'MINUTE',
|
||||
num_points: int = 100
|
||||
) -> Dict:
|
||||
"""
|
||||
Récupère prix historiques
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
resolution: 'SECOND', 'MINUTE', 'MINUTE_5', 'HOUR', 'DAY'
|
||||
num_points: Nombre de points (max 1000)
|
||||
"""
|
||||
url = f"{self.base_url}/prices/{epic}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '3'
|
||||
|
||||
params = {
|
||||
'resolution': resolution,
|
||||
'max': min(num_points, 1000)
|
||||
}
|
||||
|
||||
response = self.session.get(url, headers=headers, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 Streaming Lightstreamer
|
||||
|
||||
### Configuration
|
||||
|
||||
```python
|
||||
# src/data/ig_streaming.py
|
||||
|
||||
from lightstreamer.client import LightstreamerClient, Subscription
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IGStreamingAPI:
|
||||
"""
|
||||
Connecteur IG Streaming (Lightstreamer)
|
||||
|
||||
Permet de recevoir:
|
||||
- Prix en temps réel
|
||||
- Updates positions
|
||||
- Updates compte
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: Dict, cst_token: str, security_token: str):
|
||||
self.lightstreamer_url = credentials.get('lightstreamer_url')
|
||||
self.cst_token = cst_token
|
||||
self.security_token = security_token
|
||||
self.account_id = credentials['account_id']
|
||||
|
||||
self.client = None
|
||||
self.subscriptions = {}
|
||||
|
||||
def connect(self):
|
||||
"""Connexion Lightstreamer"""
|
||||
self.client = LightstreamerClient(self.lightstreamer_url, "DEFAULT")
|
||||
|
||||
# Configuration connexion
|
||||
self.client.connectionDetails.setUser(self.account_id)
|
||||
self.client.connectionDetails.setPassword(f"CST-{self.cst_token}|XST-{self.security_token}")
|
||||
|
||||
# Listeners
|
||||
self.client.addListener(ConnectionListener())
|
||||
|
||||
# Connecter
|
||||
self.client.connect()
|
||||
|
||||
logger.info("Lightstreamer connection established")
|
||||
|
||||
def subscribe_market(self, epic: str, callback):
|
||||
"""
|
||||
Subscribe à prix temps réel d'un marché
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
callback: Fonction appelée à chaque update
|
||||
"""
|
||||
# Items à subscribe
|
||||
items = [f"MARKET:{epic}"]
|
||||
|
||||
# Fields à recevoir
|
||||
fields = [
|
||||
"BID", "OFFER", "HIGH", "LOW",
|
||||
"MID_OPEN", "CHANGE", "CHANGE_PCT",
|
||||
"UPDATE_TIME", "MARKET_STATE"
|
||||
]
|
||||
|
||||
# Créer subscription
|
||||
subscription = Subscription(
|
||||
mode="MERGE",
|
||||
items=items,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
# Ajouter listener
|
||||
subscription.addListener(MarketDataListener(callback))
|
||||
|
||||
# Subscribe
|
||||
self.client.subscribe(subscription)
|
||||
self.subscriptions[epic] = subscription
|
||||
|
||||
logger.info(f"Subscribed to market: {epic}")
|
||||
|
||||
def subscribe_account(self, callback):
|
||||
"""Subscribe à updates compte"""
|
||||
items = [f"ACCOUNT:{self.account_id}"]
|
||||
|
||||
fields = [
|
||||
"AVAILABLE_CASH", "PNL", "DEPOSIT",
|
||||
"USED_MARGIN", "AVAILABLE_TO_DEAL", "EQUITY"
|
||||
]
|
||||
|
||||
subscription = Subscription(
|
||||
mode="MERGE",
|
||||
items=items,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
subscription.addListener(AccountListener(callback))
|
||||
|
||||
self.client.subscribe(subscription)
|
||||
self.subscriptions['account'] = subscription
|
||||
|
||||
def unsubscribe(self, key: str):
|
||||
"""Unsubscribe d'un stream"""
|
||||
if key in self.subscriptions:
|
||||
self.client.unsubscribe(self.subscriptions[key])
|
||||
del self.subscriptions[key]
|
||||
|
||||
def disconnect(self):
|
||||
"""Déconnexion Lightstreamer"""
|
||||
if self.client:
|
||||
self.client.disconnect()
|
||||
logger.info("Lightstreamer disconnected")
|
||||
|
||||
class MarketDataListener:
|
||||
"""Listener pour données marché"""
|
||||
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def onItemUpdate(self, update):
|
||||
"""Appelé à chaque update prix"""
|
||||
data = {
|
||||
'bid': update.getValue('BID'),
|
||||
'offer': update.getValue('OFFER'),
|
||||
'high': update.getValue('HIGH'),
|
||||
'low': update.getValue('LOW'),
|
||||
'change': update.getValue('CHANGE'),
|
||||
'change_pct': update.getValue('CHANGE_PCT'),
|
||||
'update_time': update.getValue('UPDATE_TIME'),
|
||||
'market_state': update.getValue('MARKET_STATE')
|
||||
}
|
||||
|
||||
self.callback(data)
|
||||
|
||||
class AccountListener:
|
||||
"""Listener pour updates compte"""
|
||||
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def onItemUpdate(self, update):
|
||||
"""Appelé à chaque update compte"""
|
||||
data = {
|
||||
'available_cash': update.getValue('AVAILABLE_CASH'),
|
||||
'pnl': update.getValue('PNL'),
|
||||
'deposit': update.getValue('DEPOSIT'),
|
||||
'used_margin': update.getValue('USED_MARGIN'),
|
||||
'equity': update.getValue('EQUITY')
|
||||
}
|
||||
|
||||
self.callback(data)
|
||||
|
||||
class ConnectionListener:
|
||||
"""Listener pour état connexion"""
|
||||
|
||||
def onStatusChange(self, status):
|
||||
logger.info(f"Lightstreamer status: {status}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Gestion des Ordres
|
||||
|
||||
### Wrapper Unifié
|
||||
|
||||
```python
|
||||
# src/core/order_manager.py
|
||||
|
||||
class IGOrderManager:
|
||||
"""
|
||||
Gestionnaire d'ordres IG avec validation
|
||||
"""
|
||||
|
||||
def __init__(self, ig_api: IGRestAPI, risk_manager: RiskManager):
|
||||
self.ig_api = ig_api
|
||||
self.risk_manager = risk_manager
|
||||
|
||||
def execute_signal(self, signal: Signal) -> Optional[str]:
|
||||
"""
|
||||
Exécute signal de trading
|
||||
|
||||
Returns:
|
||||
Deal ID si succès, None sinon
|
||||
"""
|
||||
# 1. Valider avec risk manager
|
||||
is_valid, error = self.risk_manager.validate_trade(
|
||||
symbol=signal.symbol,
|
||||
quantity=signal.quantity,
|
||||
price=signal.entry_price,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
strategy=signal.strategy
|
||||
)
|
||||
|
||||
if not is_valid:
|
||||
logger.warning(f"Trade rejected: {error}")
|
||||
return None
|
||||
|
||||
# 2. Convertir symbol en EPIC IG
|
||||
epic = self._symbol_to_epic(signal.symbol)
|
||||
|
||||
# 3. Placer ordre
|
||||
try:
|
||||
result = self.ig_api.place_order(
|
||||
epic=epic,
|
||||
direction='BUY' if signal.direction == 'LONG' else 'SELL',
|
||||
size=signal.quantity,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
guaranteed_stop=True # Toujours utiliser guaranteed stop
|
||||
)
|
||||
|
||||
deal_id = result.get('dealId')
|
||||
|
||||
# 4. Enregistrer position dans risk manager
|
||||
if deal_id:
|
||||
self.risk_manager.add_position(Position(
|
||||
symbol=signal.symbol,
|
||||
quantity=signal.quantity,
|
||||
entry_price=signal.entry_price,
|
||||
current_price=signal.entry_price,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
strategy=signal.strategy,
|
||||
entry_time=datetime.now(),
|
||||
unrealized_pnl=0.0,
|
||||
risk_amount=abs(signal.entry_price - signal.stop_loss) * signal.quantity
|
||||
))
|
||||
|
||||
return deal_id
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Order execution failed: {e}")
|
||||
return None
|
||||
|
||||
def _symbol_to_epic(self, symbol: str) -> str:
|
||||
"""
|
||||
Convertit symbol standard en EPIC IG
|
||||
|
||||
Exemples:
|
||||
- EURUSD → CS.D.EURUSD.MINI.IP
|
||||
- GBPUSD → CS.D.GBPUSD.MINI.IP
|
||||
- US500 → IX.D.SPTRD.IFE.IP
|
||||
"""
|
||||
# Mapping symbol → EPIC
|
||||
EPIC_MAP = {
|
||||
'EURUSD': 'CS.D.EURUSD.MINI.IP',
|
||||
'GBPUSD': 'CS.D.GBPUSD.MINI.IP',
|
||||
'USDJPY': 'CS.D.USDJPY.MINI.IP',
|
||||
'US500': 'IX.D.SPTRD.IFE.IP',
|
||||
'US30': 'IX.D.DOW.IFE.IP',
|
||||
'GER40': 'IX.D.DAX.IFE.IP',
|
||||
}
|
||||
|
||||
return EPIC_MAP.get(symbol, symbol)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Risk Management IG
|
||||
|
||||
### Spécificités IG
|
||||
|
||||
```python
|
||||
class IGRiskCalculator:
|
||||
"""
|
||||
Calculs risk spécifiques IG
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def calculate_margin_required(
|
||||
epic: str,
|
||||
size: float,
|
||||
market_details: Dict
|
||||
) -> float:
|
||||
"""
|
||||
Calcule margin requis pour position
|
||||
|
||||
IG utilise margin factor variable selon instrument
|
||||
"""
|
||||
margin_factor = market_details['dealingRules']['minDealSize']['value']
|
||||
current_price = market_details['snapshot']['offer']
|
||||
|
||||
margin_required = size * current_price * margin_factor
|
||||
|
||||
return margin_required
|
||||
|
||||
@staticmethod
|
||||
def calculate_guaranteed_stop_premium(
|
||||
stop_distance: float,
|
||||
market_details: Dict
|
||||
) -> float:
|
||||
"""
|
||||
Calcule coût du guaranteed stop
|
||||
|
||||
IG facture premium pour guaranteed stops
|
||||
"""
|
||||
premium_factor = market_details['dealingRules']['marketOrderPreference']
|
||||
|
||||
# Premium généralement 0.3-0.5% du stop distance
|
||||
premium = stop_distance * 0.003 # 0.3%
|
||||
|
||||
return premium
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Migration Progressive
|
||||
|
||||
### Plan de Migration
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ MIGRATION VERS IG MARKETS │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ PHASE 1: Développement (Semaines 1-8) │
|
||||
│ └─ Sources gratuites (Yahoo, Alpha Vantage) │
|
||||
│ │
|
||||
│ PHASE 2: Tests IG Démo (Semaines 9-10) │
|
||||
│ ├─ Connexion API IG démo │
|
||||
│ ├─ Paper trading 30 jours │
|
||||
│ └─ Validation performance │
|
||||
│ │
|
||||
│ PHASE 3: Live Progressif (Semaine 11+) │
|
||||
│ ├─ Capital initial minimal (£500) │
|
||||
│ ├─ 1 stratégie uniquement │
|
||||
│ ├─ Monitoring 24/7 │
|
||||
│ └─ Scale progressif si succès │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Documentation IG Markets complète !**
|
||||
Reference in New Issue
Block a user