gate_dash2 / app.py
luguog's picture
Update app.py
b904011 verified
import streamlit as st
import ccxt
import numpy as np
import pandas as pd
import time
from datetime import datetime
from dotenv import load_dotenv
import os
load_dotenv()
GATE_API_KEY = os.getenv("GATE_API_KEY")
GATE_API_SECRET = os.getenv("GATE_API_SECRET")
exchange = ccxt.gateio({
'apiKey': GATE_API_KEY,
'secret': GATE_API_SECRET,
'enableRateLimit': True,
'options': {'defaultType': 'swap'}
})
st.set_page_config(page_title="Dynamic MM Hedger", layout="wide")
# === UI state ===
if "enabled" not in st.session_state:
st.session_state.enabled = False
if "pnl_log" not in st.session_state:
st.session_state.pnl_log = []
if "ghost_orders" not in st.session_state:
st.session_state.ghost_orders = {}
# === Controls ===
st.sidebar.title("Controls")
if st.sidebar.button("Start Bot"):
st.session_state.enabled = True
if st.sidebar.button("Stop Bot"):
st.session_state.enabled = False
st.sidebar.markdown(f"### Status: {'🟢 RUNNING' if st.session_state.enabled else '🔴 STOPPED'}")
ORDER_USDT = st.sidebar.slider("Order Size (USDT)", 5, 50, 10)
GRID_MULTIPLIER = st.sidebar.slider("Grid ATR Multiplier", 1.0, 5.0, 2.0, 0.5)
PROFIT_TARGET = st.sidebar.slider("Profit Target %", 0.3, 3.0, 0.7) / 100
# === Utility ===
def get_top_pairs(limit=3):
data = []
for s in exchange.load_markets():
if "/USDT" in s:
try:
ticker = exchange.fetch_ticker(s)
orderbook = exchange.fetch_order_book(s)
if not orderbook['asks'] or not orderbook['bids']: continue
spread_pct = (orderbook['asks'][0][0] - orderbook['bids'][0][0]) / ticker['last'] * 100
ohlcv = exchange.fetch_ohlcv(s, '1h', limit=24)
vols = [x[4] for x in ohlcv]
close = [x[4] for x in ohlcv]
score = spread_pct + np.mean(vols) / 100000 - np.std(close)
data.append((s, score))
except: continue
return sorted(data, key=lambda x: x[1], reverse=True)[:limit]
def get_atr(pair):
candles = exchange.fetch_ohlcv(pair, '1m', limit=100)
highs = np.array([c[2] for c in candles])
lows = np.array([c[3] for c in candles])
return np.mean(np.abs(highs - lows))
def get_bal(asset):
return float(exchange.fetch_balance()['total'].get(asset, 0))
def place(side, pair, price, size):
try:
return exchange.create_limit_order(pair, side, size, price)
except Exception as e:
st.warning(f"Order error: {e}")
# === Trading Logic ===
def run_grid(pair):
price = float(exchange.fetch_ticker(pair)['last'])
atr = get_atr(pair)
spacing = GRID_MULTIPLIER * atr
size = ORDER_USDT / price
if pair not in st.session_state.ghost_orders:
st.session_state.ghost_orders[pair] = {'long': {}, 'short': {}}
for side in ['long', 'short']:
ledger = st.session_state.ghost_orders[pair][side]
# update ghost grid
if side == "long":
level = round(price - spacing, 4)
else:
level = round(price + spacing, 4)
ledger[level] = ledger.get(level, 0) + size
# check for profit-taking
wavg = sum([p*q for p, q in ledger.items()]) / sum(ledger.values())
if (side == "long" and price >= wavg * (1 + PROFIT_TARGET)) or \
(side == "short" and price <= wavg * (1 - PROFIT_TARGET)):
order_side = "sell" if side == "long" else "buy"
place(order_side, pair, price, sum(ledger.values()))
pnl = (price - wavg) * sum(ledger.values()) if side == "long" else (wavg - price) * sum(ledger.values())
st.session_state.pnl_log.append({"Time": datetime.now(), "Pair": pair, "Side": side, "PnL": pnl})
ledger.clear()
# === Execution Loop ===
if st.session_state.enabled:
pairs = [x[0] for x in get_top_pairs()]
st.write("Top Pairs:", pairs)
for p in pairs:
run_grid(p)
df = pd.DataFrame(st.session_state.pnl_log)
st.line_chart(df.set_index("Time")["PnL"].cumsum() if not df.empty else pd.Series())
time.sleep(3)
else:
st.write("Bot is stopped.")