File size: 4,135 Bytes
dbcee54
 
 
b904011
dbcee54
 
8c65a14
b904011
 
8c65a14
b904011
 
8c65a14
b904011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbcee54
b904011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79e3a17
b904011
 
79e3a17
b904011
dbcee54
b904011
8c65a14
b904011
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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.")