Spaces:
Runtime error
Runtime error
Commit ·
2f55d16
1
Parent(s): c9d8d74
add application
Browse files- dashboard.py +0 -131
- trading_dashboard.py +0 -115
dashboard.py
CHANGED
|
@@ -1,131 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import plotly.graph_objects as go
|
| 4 |
-
import requests
|
| 5 |
-
from datetime import datetime, timedelta
|
| 6 |
-
import time
|
| 7 |
-
|
| 8 |
-
# Configuration
|
| 9 |
-
API_URL = "https://thibautmodrin-fast-api.hf.space"
|
| 10 |
-
|
| 11 |
-
# Fonction pour récupérer la liste des stratégies
|
| 12 |
-
def get_available_strategies():
|
| 13 |
-
try:
|
| 14 |
-
response = requests.get(f"{API_URL}/strategies")
|
| 15 |
-
if response.status_code == 200:
|
| 16 |
-
data = response.json()
|
| 17 |
-
if not data["strategies"]:
|
| 18 |
-
return ["SIMPLE_MA"]
|
| 19 |
-
return data["strategies"]
|
| 20 |
-
else:
|
| 21 |
-
st.error("Erreur lors de la récupération des stratégies")
|
| 22 |
-
return ["SIMPLE_MA"]
|
| 23 |
-
except Exception as e:
|
| 24 |
-
st.error(f"Erreur de connexion: {str(e)}")
|
| 25 |
-
return ["SIMPLE_MA"]
|
| 26 |
-
|
| 27 |
-
# Fonction pour récupérer les données
|
| 28 |
-
def get_strategy_data(strategy_id):
|
| 29 |
-
url = f"{API_URL}/metrics/{strategy_id}"
|
| 30 |
-
try:
|
| 31 |
-
response = requests.get(url)
|
| 32 |
-
if response.status_code == 200:
|
| 33 |
-
data = response.json()
|
| 34 |
-
return pd.DataFrame(data)
|
| 35 |
-
return None
|
| 36 |
-
except Exception as e:
|
| 37 |
-
st.error(f"Erreur de connexion: {str(e)}")
|
| 38 |
-
return None
|
| 39 |
-
|
| 40 |
-
# Interface principale
|
| 41 |
-
st.title("📈 Trading Strategy Dashboard")
|
| 42 |
-
|
| 43 |
-
# Rafraîchissement des stratégies
|
| 44 |
-
if st.button("🔄 Rafraîchir la liste des stratégies"):
|
| 45 |
-
st.experimental_rerun()
|
| 46 |
-
|
| 47 |
-
# Sélection de la stratégie avec les données dynamiques
|
| 48 |
-
strategies = get_available_strategies()
|
| 49 |
-
strategy_id = st.selectbox(
|
| 50 |
-
"Sélectionner une stratégie",
|
| 51 |
-
options=strategies,
|
| 52 |
-
help="Sélectionnez la stratégie à afficher. La liste est mise à jour automatiquement."
|
| 53 |
-
)
|
| 54 |
-
|
| 55 |
-
# Bouton de rafraîchissement avec état
|
| 56 |
-
if st.button("🔄 Rafraîchir les données"):
|
| 57 |
-
with st.spinner("Chargement des données..."):
|
| 58 |
-
data = get_strategy_data(strategy_id)
|
| 59 |
-
if data is not None and not data.empty:
|
| 60 |
-
# Conversion des timestamps
|
| 61 |
-
data['timestamp'] = pd.to_datetime(data['timestamp'])
|
| 62 |
-
|
| 63 |
-
# Métriques principales
|
| 64 |
-
col1, col2, col3, col4 = st.columns(4)
|
| 65 |
-
latest = data.iloc[0]
|
| 66 |
-
|
| 67 |
-
with col1:
|
| 68 |
-
st.metric("Equity", f"${latest['equity']:.2f}")
|
| 69 |
-
with col2:
|
| 70 |
-
st.metric("Drawdown", f"{latest['drawdown']:.2f}%")
|
| 71 |
-
with col3:
|
| 72 |
-
st.metric("Total Trades", latest['total_trades'])
|
| 73 |
-
with col4:
|
| 74 |
-
st.metric("Symbole", latest.get('symbol', 'N/A'))
|
| 75 |
-
|
| 76 |
-
# Graphique de l'equity
|
| 77 |
-
st.subheader("Évolution de l'Equity")
|
| 78 |
-
fig_equity = go.Figure()
|
| 79 |
-
fig_equity.add_trace(go.Scatter(
|
| 80 |
-
x=data['timestamp'],
|
| 81 |
-
y=data['equity'],
|
| 82 |
-
name='Equity',
|
| 83 |
-
line=dict(color='#2E8B57', width=2)
|
| 84 |
-
))
|
| 85 |
-
fig_equity.update_layout(
|
| 86 |
-
xaxis_title="Date",
|
| 87 |
-
yaxis_title="Equity ($)",
|
| 88 |
-
hovermode='x unified',
|
| 89 |
-
title=f"Stratégie: {strategy_id} - Symbole: {latest.get('symbol', 'N/A')}"
|
| 90 |
-
)
|
| 91 |
-
st.plotly_chart(fig_equity, use_container_width=True)
|
| 92 |
-
|
| 93 |
-
# Tableau des dernières données avec réorganisation des colonnes
|
| 94 |
-
st.subheader("Dernières mises à jour")
|
| 95 |
-
# Réorganiser les colonnes du DataFrame
|
| 96 |
-
columns_order = ['timestamp', 'symbol', 'equity', 'drawdown', 'total_trades', 'current_price']
|
| 97 |
-
# S'assurer que toutes les colonnes existent
|
| 98 |
-
for col in columns_order:
|
| 99 |
-
if col not in data.columns:
|
| 100 |
-
data[col] = 'N/A'
|
| 101 |
-
# Afficher le DataFrame réorganisé
|
| 102 |
-
display_data = data[columns_order].copy()
|
| 103 |
-
# Renommer les colonnes pour l'affichage
|
| 104 |
-
display_data.columns = ['Date', 'Symbole', 'Equity', 'Drawdown (%)', 'Trades', 'Prix']
|
| 105 |
-
st.dataframe(display_data.head())
|
| 106 |
-
else:
|
| 107 |
-
st.warning("Aucune donnée disponible pour cette stratégie")
|
| 108 |
-
|
| 109 |
-
# Ajouter dans la sidebar un bouton de suppression avec confirmation
|
| 110 |
-
st.sidebar.markdown("### Danger Zone")
|
| 111 |
-
with st.sidebar.expander("🗑️ Supprimer les données", expanded=False):
|
| 112 |
-
st.warning(f"⚠️ Vous êtes sur le point de supprimer toutes les données de la stratégie : {strategy_id}")
|
| 113 |
-
|
| 114 |
-
# Ajout d'une case à cocher pour confirmation
|
| 115 |
-
confirmation = st.checkbox("Je comprends que cette action est irréversible")
|
| 116 |
-
|
| 117 |
-
# Bouton de suppression activé uniquement si la case est cochée
|
| 118 |
-
if st.button("Supprimer les données", disabled=not confirmation):
|
| 119 |
-
try:
|
| 120 |
-
response = requests.delete(f"{API_URL}/metrics/{strategy_id}")
|
| 121 |
-
if response.status_code == 200:
|
| 122 |
-
result = response.json()
|
| 123 |
-
st.success(f"✅ {result['deleted_count']} entrées supprimées pour la stratégie {strategy_id}")
|
| 124 |
-
# Attendre 2 secondes pour que l'utilisateur puisse voir le message
|
| 125 |
-
time.sleep(2)
|
| 126 |
-
# Rafraîchir la page
|
| 127 |
-
st.experimental_rerun()
|
| 128 |
-
else:
|
| 129 |
-
st.error(f"❌ Erreur lors de la suppression: {response.status_code}")
|
| 130 |
-
except Exception as e:
|
| 131 |
-
st.error(f"❌ Erreur de connexion: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
trading_dashboard.py
DELETED
|
@@ -1,115 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import requests
|
| 3 |
-
from datetime import datetime
|
| 4 |
-
import pandas as pd
|
| 5 |
-
|
| 6 |
-
# Configuration
|
| 7 |
-
API_URL = "https://thibautmodrin-fast-api.hf.space"
|
| 8 |
-
|
| 9 |
-
# Configuration de la page
|
| 10 |
-
st.set_page_config(
|
| 11 |
-
page_title="Trading Control Panel",
|
| 12 |
-
page_icon="🎮",
|
| 13 |
-
layout="wide"
|
| 14 |
-
)
|
| 15 |
-
|
| 16 |
-
# Fonction pour envoyer un ordre
|
| 17 |
-
def send_order(symbol, side, quantity, price, order_type, strategy_name):
|
| 18 |
-
try:
|
| 19 |
-
# Préparer les données de l'ordre
|
| 20 |
-
order_data = {
|
| 21 |
-
"symbol": symbol,
|
| 22 |
-
"side": side,
|
| 23 |
-
"quantity": float(quantity),
|
| 24 |
-
"order_type": order_type,
|
| 25 |
-
"strategy_name": strategy_name
|
| 26 |
-
}
|
| 27 |
-
|
| 28 |
-
# Ajouter le prix seulement pour les ordres LIMIT
|
| 29 |
-
if order_type == "LIMIT" and price:
|
| 30 |
-
order_data["price"] = float(price)
|
| 31 |
-
else:
|
| 32 |
-
order_data["price"] = 0.0 # Prix par défaut pour les ordres MARKET
|
| 33 |
-
|
| 34 |
-
response = requests.post(f"{API_URL}/trading/order", json=order_data)
|
| 35 |
-
if response.status_code == 200:
|
| 36 |
-
st.success(f"✅ Ordre {side} envoyé avec succès pour {symbol}")
|
| 37 |
-
return response.json()
|
| 38 |
-
else:
|
| 39 |
-
st.error(f"❌ Erreur lors de l'envoi de l'ordre: {response.text}")
|
| 40 |
-
return None
|
| 41 |
-
except Exception as e:
|
| 42 |
-
st.error(f"❌ Erreur: {str(e)}")
|
| 43 |
-
return None
|
| 44 |
-
|
| 45 |
-
# Interface principale
|
| 46 |
-
st.title("🎮 Trading Control Panel")
|
| 47 |
-
|
| 48 |
-
# Création de deux colonnes principales
|
| 49 |
-
col1, col2 = st.columns([2, 1])
|
| 50 |
-
|
| 51 |
-
with col1:
|
| 52 |
-
st.subheader("📊 Nouvel Ordre")
|
| 53 |
-
|
| 54 |
-
# Récupération des symboles et stratégies
|
| 55 |
-
symbols = requests.get(f"{API_URL}/trading/symbols").json()["symbols"]
|
| 56 |
-
strategies = requests.get(f"{API_URL}/trading/strategies").json()["strategies"]
|
| 57 |
-
|
| 58 |
-
# Formulaire d'ordre
|
| 59 |
-
with st.form("order_form"):
|
| 60 |
-
symbol = st.selectbox("Symbole", symbols)
|
| 61 |
-
strategy = st.selectbox("Stratégie", strategies)
|
| 62 |
-
|
| 63 |
-
col_a, col_b = st.columns(2)
|
| 64 |
-
with col_a:
|
| 65 |
-
side = st.radio("Type d'ordre", ["BUY", "SELL"])
|
| 66 |
-
quantity = st.number_input("Quantité", min_value=0.0, step=0.1)
|
| 67 |
-
|
| 68 |
-
with col_b:
|
| 69 |
-
order_type = st.radio("Type de prix", ["MARKET", "LIMIT"])
|
| 70 |
-
# Afficher le champ prix uniquement pour les ordres LIMIT
|
| 71 |
-
price = st.number_input("Prix (pour Limit)", min_value=0.0, step=0.1, disabled=order_type=="MARKET") if order_type == "LIMIT" else 0.0
|
| 72 |
-
|
| 73 |
-
submit = st.form_submit_button("Envoyer l'ordre")
|
| 74 |
-
|
| 75 |
-
if submit:
|
| 76 |
-
if quantity <= 0:
|
| 77 |
-
st.error("❌ La quantité doit être supérieure à 0")
|
| 78 |
-
elif order_type == "LIMIT" and price <= 0:
|
| 79 |
-
st.error("❌ Le prix limite doit être supérieur à 0")
|
| 80 |
-
else:
|
| 81 |
-
result = send_order(symbol, side, quantity, price, order_type, strategy)
|
| 82 |
-
if result:
|
| 83 |
-
st.json(result)
|
| 84 |
-
|
| 85 |
-
with col2:
|
| 86 |
-
st.subheader("⚡ Actions Rapides")
|
| 87 |
-
|
| 88 |
-
# Boutons d'action rapide
|
| 89 |
-
if st.button("🔴 Fermer toutes les positions"):
|
| 90 |
-
# Logique pour fermer toutes les positions
|
| 91 |
-
st.warning("Fonctionnalité à implémenter")
|
| 92 |
-
|
| 93 |
-
if st.button("⚫ Annuler tous les ordres"):
|
| 94 |
-
# Logique pour annuler tous les ordres
|
| 95 |
-
st.warning("Fonctionnalité à implémenter")
|
| 96 |
-
|
| 97 |
-
# Historique des ordres (à implémenter)
|
| 98 |
-
st.subheader("📜 Historique des Ordres")
|
| 99 |
-
st.info("L'historique des ordres sera affiché ici")
|
| 100 |
-
|
| 101 |
-
# Positions actuelles (à implémenter)
|
| 102 |
-
st.subheader("📌 Positions Actuelles")
|
| 103 |
-
st.info("Les positions actuelles seront affichées ici")
|
| 104 |
-
|
| 105 |
-
# Sidebar avec les statistiques
|
| 106 |
-
st.sidebar.title("📊 Statistiques")
|
| 107 |
-
st.sidebar.metric("Balance", "$10,000", "↑ $500")
|
| 108 |
-
st.sidebar.metric("Positions Ouvertes", "3")
|
| 109 |
-
st.sidebar.metric("Ordres en Attente", "2")
|
| 110 |
-
|
| 111 |
-
# Configuration dans la sidebar
|
| 112 |
-
st.sidebar.title("⚙️ Configuration")
|
| 113 |
-
with st.sidebar.expander("Paramètres API"):
|
| 114 |
-
st.text_input("API URL", value=API_URL)
|
| 115 |
-
st.text_input("API Key", type="password")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|