| | import streamlit as st |
| | import ccxt |
| | import pandas as pd |
| | import numpy as np |
| | import time |
| | from datetime import datetime |
| | import os |
| | from dotenv import load_dotenv |
| | from rich.console import Console |
| |
|
| | |
| | st.set_page_config( |
| | page_title="Gate.io Market Making Dashboard", |
| | page_icon="π", |
| | layout="wide", |
| | initial_sidebar_state="expanded" |
| | ) |
| |
|
| | |
| | st.markdown(""" |
| | <style> |
| | .stApp { |
| | background: linear-gradient(145deg, #1a1a1a, #2d2d2d); |
| | color: #ffffff; |
| | } |
| | .stButton>button { |
| | background: linear-gradient(145deg, #ff6b00, #ff8533); |
| | border: none; |
| | border-radius: 10px; |
| | box-shadow: 5px 5px 10px rgba(0,0,0,0.3), |
| | -5px -5px 10px rgba(255,255,255,0.1); |
| | color: white; |
| | padding: 10px 20px; |
| | transition: all 0.3s ease; |
| | } |
| | .stButton>button:hover { |
| | transform: translateY(-2px); |
| | box-shadow: 6px 6px 12px rgba(0,0,0,0.4), |
| | -6px -6px 12px rgba(255,255,255,0.15); |
| | } |
| | .stSelectbox>div>div>select { |
| | background: linear-gradient(145deg, #2d2d2d, #1a1a1a); |
| | color: white; |
| | border-radius: 10px; |
| | box-shadow: inset 3px 3px 6px rgba(0,0,0,0.3), |
| | inset -3px -3px 6px rgba(255,255,255,0.1); |
| | } |
| | .stSlider>div>div>div { |
| | background: linear-gradient(145deg, #ff6b00, #ff8533); |
| | } |
| | .stMarkdown { |
| | color: #ffffff; |
| | } |
| | .stDataFrame { |
| | background: linear-gradient(145deg, #2d2d2d, #1a1a1a); |
| | border-radius: 15px; |
| | padding: 20px; |
| | box-shadow: 5px 5px 10px rgba(0,0,0,0.3), |
| | -5px -5px 10px rgba(255,255,255,0.1); |
| | } |
| | .error-message { |
| | color: #ff4444; |
| | background: rgba(255,68,68,0.1); |
| | padding: 10px; |
| | border-radius: 10px; |
| | margin: 10px 0; |
| | } |
| | </style> |
| | """, unsafe_allow_html=True) |
| |
|
| | |
| | load_dotenv() |
| |
|
| | |
| | try: |
| | exchange = ccxt.gateio({ |
| | 'apiKey': st.secrets["GATE_API_KEY"], |
| | 'secret': st.secrets["GATE_API_SECRET"], |
| | 'enableRateLimit': True, |
| | 'options': { |
| | 'defaultType': 'swap', |
| | 'defaultSettle': 'usdt', |
| | }, |
| | 'urls': { |
| | 'api': { |
| | 'rest': st.secrets["GATE_API_BASE"] |
| | } |
| | } |
| | }) |
| | |
| | |
| | exchange.load_markets() |
| | st.success("β
Successfully connected to Gate.io API") |
| | except Exception as e: |
| | st.error(f"β Failed to connect to Gate.io API: {str(e)}") |
| | st.stop() |
| |
|
| | st.title("Gate.io Futures Live Data") |
| |
|
| | |
| | col1, col2, col3 = st.columns(3) |
| |
|
| | with col1: |
| | min_spread = st.slider("Minimum Spread %", 0.3, 5.0, 0.5, 0.1) |
| | min_volume = st.slider("Minimum 24h Volume (USDT)", 100000, 1000000, 200000, 10000) |
| | |
| | with col2: |
| | max_positions = st.slider("Max Active Positions", 1, 10, 3, 1) |
| | dca_multiplier = st.slider("DCA Size Multiplier", 1.0, 5.0, 2.0, 0.5) |
| | |
| | with col3: |
| | hedge_ratio = st.slider("Hedge Ratio", 0.5, 2.0, 1.0, 0.1) |
| | leverage = st.slider("Leverage", 1, 10, 3, 1) |
| |
|
| | def fetch_market_data(): |
| | try: |
| | markets = exchange.load_markets() |
| | usdt_pairs = [symbol for symbol in markets.keys() |
| | if "/USDT" in symbol and markets[symbol].get("type") == "swap"] |
| | |
| | data = [] |
| | for symbol in usdt_pairs: |
| | try: |
| | ticker = exchange.fetch_ticker(symbol) |
| | price = ticker['last'] |
| | volume_24h = ticker['quoteVolume'] |
| | |
| | orderbook = exchange.fetch_order_book(symbol) |
| | if orderbook['asks'] and orderbook['bids']: |
| | spread = orderbook['asks'][0][0] - orderbook['bids'][0][0] |
| | spread_pct = (spread / price) * 100 |
| | |
| | |
| | bid_depth = sum(bid[1] for bid in orderbook['bids'][:5]) |
| | ask_depth = sum(ask[1] for ask in orderbook['asks'][:5]) |
| | total_depth = bid_depth + ask_depth |
| | |
| | |
| | ohlcv = exchange.fetch_ohlcv(symbol, timeframe='1h', limit=24) |
| | if len(ohlcv) > 0: |
| | prices = [x[4] for x in ohlcv] |
| | volatility = np.std(prices) / np.mean(prices) * 100 |
| | else: |
| | volatility = 0 |
| | |
| | |
| | spread_score = max(0, 100 - (spread_pct * 20)) |
| | volume_score = min(100, volume_24h / min_volume * 100) |
| | depth_score = min(100, total_depth / 100) |
| | volatility_score = max(0, 100 - (volatility * 10)) |
| | |
| | mm_score = (spread_score + volume_score + depth_score + volatility_score) / 4 |
| | |
| | if volume_24h >= min_volume and spread_pct >= min_spread: |
| | data.append({ |
| | 'Symbol': symbol, |
| | 'Price': price, |
| | 'Spread %': spread_pct, |
| | '24h Volume (USDT)': volume_24h, |
| | 'Total Depth': total_depth, |
| | 'Volatility %': volatility, |
| | 'MM Score': mm_score |
| | }) |
| | except Exception as e: |
| | st.error(f"Error fetching data for {symbol}: {str(e)}") |
| | continue |
| | |
| | if not data: |
| | st.warning("No market data available matching the criteria") |
| | return None |
| | |
| | df = pd.DataFrame(data) |
| | df = df.dropna() |
| | return df |
| | |
| | except Exception as e: |
| | st.error(f"Error fetching market data: {str(e)}") |
| | return None |
| |
|
| | |
| | spread_container = st.empty() |
| | position_container = st.empty() |
| |
|
| | while True: |
| | try: |
| | df = fetch_market_data() |
| | |
| | if df is not None and not df.empty: |
| | |
| | df_sorted = df.sort_values(by='MM Score', ascending=False) |
| | |
| | |
| | with spread_container.container(): |
| | st.markdown("### π Best Market Making Opportunities") |
| | st.dataframe( |
| | df_sorted[['Symbol', 'Price', 'Spread %', '24h Volume (USDT)', 'MM Score']].head(33), |
| | use_container_width=True |
| | ) |
| | |
| | |
| | with position_container.container(): |
| | st.markdown("### π° Active Positions") |
| | st.dataframe( |
| | df_sorted[['Symbol', 'Price', 'Spread %', 'Total Depth', 'Volatility %']].head(max_positions), |
| | use_container_width=True |
| | ) |
| | |
| | time.sleep(3) |
| | |
| | except Exception as e: |
| | st.error(f"Error in main loop: {str(e)}") |
| | time.sleep(3) |
| | continue |