Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,20 +4,17 @@ import plotly.graph_objs as go
|
|
| 4 |
import pandas as pd
|
| 5 |
import ccxt
|
| 6 |
from datetime import datetime
|
|
|
|
| 7 |
|
| 8 |
-
#
|
| 9 |
exchange = ccxt.mexc()
|
| 10 |
-
|
| 11 |
-
# Fetch available markets and timeframes
|
| 12 |
markets = exchange.load_markets()
|
| 13 |
symbols = sorted([symbol for symbol in markets if "/USDT" in symbol])
|
| 14 |
timeframes = list(exchange.timeframes.keys())
|
| 15 |
|
| 16 |
-
# Default selections
|
| 17 |
DEFAULT_SYMBOL = "BTC/USDT"
|
| 18 |
DEFAULT_TIMEFRAME = "1h"
|
| 19 |
|
| 20 |
-
# Fetch OHLCV data
|
| 21 |
def fetch_ohlcv(symbol, timeframe):
|
| 22 |
try:
|
| 23 |
ohlcv = exchange.fetch_ohlcv(symbol, timeframe)
|
|
@@ -25,94 +22,87 @@ def fetch_ohlcv(symbol, timeframe):
|
|
| 25 |
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
|
| 26 |
return df
|
| 27 |
except Exception as e:
|
| 28 |
-
print(f"β Error
|
| 29 |
return pd.DataFrame()
|
| 30 |
|
| 31 |
-
# Add indicators
|
| 32 |
def add_indicators(df):
|
| 33 |
df = df.copy()
|
| 34 |
df['ema12'] = df['close'].ewm(span=12).mean()
|
| 35 |
df['ema26'] = df['close'].ewm(span=26).mean()
|
| 36 |
df['macd'] = df['ema12'] - df['ema26']
|
| 37 |
df['macd_signal'] = df['macd'].ewm(span=9).mean()
|
| 38 |
-
|
| 39 |
delta = df['close'].diff()
|
| 40 |
gain = delta.clip(lower=0)
|
| 41 |
-
loss = -
|
| 42 |
avg_gain = gain.rolling(14).mean()
|
| 43 |
avg_loss = loss.rolling(14).mean()
|
| 44 |
rs = avg_gain / avg_loss
|
| 45 |
df['rsi'] = 100 - (100 / (1 + rs))
|
| 46 |
return df
|
| 47 |
|
| 48 |
-
# Add signals
|
| 49 |
def add_signals(df):
|
| 50 |
df['buy'] = (df['macd'] > df['macd_signal']) & (df['macd'].shift(1) <= df['macd_signal'].shift(1)) & (df['rsi'] < 50)
|
| 51 |
df['sell'] = (df['macd'] < df['macd_signal']) & (df['macd'].shift(1) >= df['macd_signal'].shift(1)) & (df['rsi'] > 50)
|
| 52 |
return df
|
| 53 |
|
| 54 |
-
# Build the chart
|
| 55 |
def create_chart(df):
|
| 56 |
fig = make_subplots(rows=3, cols=1, shared_xaxes=True,
|
| 57 |
-
vertical_spacing=0.02,
|
| 58 |
-
row_heights=[0.6, 0.2, 0.2])
|
| 59 |
|
| 60 |
-
fig.add_trace(go.Candlestick(
|
| 61 |
-
|
| 62 |
-
low=df['low'], close=df['close'],
|
| 63 |
-
name='Candlesticks'), row=1, col=1)
|
| 64 |
|
| 65 |
-
fig.add_trace(go.Scatter(
|
| 66 |
-
|
| 67 |
-
line=dict(color='orange'), name='RSI'), row=2, col=1)
|
| 68 |
|
| 69 |
-
fig.add_trace(go.Scatter(
|
| 70 |
-
|
| 71 |
|
| 72 |
-
fig.add_trace(go.Scatter(
|
| 73 |
-
|
| 74 |
|
|
|
|
| 75 |
buys = df[df['buy']]
|
| 76 |
sells = df[df['sell']]
|
| 77 |
-
fig.add_trace(go.Scatter(x=buys['timestamp'], y=buys['close'], mode='markers',
|
| 78 |
-
marker=dict(color='green', size=10, symbol='triangle-up')), row=1, col=1)
|
| 79 |
-
fig.add_trace(go.Scatter(x=sells['timestamp'], y=sells['close'], mode='markers',
|
| 80 |
-
marker=dict(color='red', size=10, symbol='triangle-down')), row=1, col=1)
|
| 81 |
|
| 82 |
-
fig.update_layout(height=800, showlegend=True)
|
| 83 |
return fig
|
| 84 |
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
# Dash app
|
| 88 |
app = dash.Dash(__name__)
|
| 89 |
-
app.title = "MEXC
|
| 90 |
|
| 91 |
app.layout = html.Div([
|
| 92 |
-
html.H2("π MEXC
|
| 93 |
|
| 94 |
html.Div([
|
| 95 |
html.Label("Select Pair:"),
|
| 96 |
dcc.Dropdown(id="pair-dropdown", options=[{"label": s, "value": s} for s in symbols],
|
| 97 |
-
value=DEFAULT_SYMBOL)
|
| 98 |
-
], style={
|
| 99 |
|
| 100 |
html.Div([
|
| 101 |
-
html.Label("
|
| 102 |
html.Div([
|
| 103 |
html.Button(tf, id={'type': 'tf-button', 'index': tf}, n_clicks=0,
|
| 104 |
-
style={"margin": "2px"}) for tf in timeframes
|
| 105 |
-
], id=
|
| 106 |
-
], style={
|
| 107 |
|
| 108 |
-
dcc.Store(id=
|
| 109 |
dcc.Graph(id="chart", config={"displayModeBar": True}),
|
|
|
|
|
|
|
| 110 |
])
|
| 111 |
|
| 112 |
-
# Callbacks
|
| 113 |
@app.callback(
|
| 114 |
Output("selected-timeframe", "data"),
|
| 115 |
-
Input({'type': 'tf-button', 'index': dash.dependencies.ALL}, 'n_clicks')
|
| 116 |
)
|
| 117 |
def update_timeframe(n_clicks_list):
|
| 118 |
ctx = dash.callback_context
|
|
@@ -123,17 +113,24 @@ def update_timeframe(n_clicks_list):
|
|
| 123 |
return timeframe
|
| 124 |
|
| 125 |
@app.callback(
|
| 126 |
-
Output("chart", "figure"),
|
| 127 |
-
Input("pair-dropdown", "value"),
|
| 128 |
-
Input("selected-timeframe", "data")
|
| 129 |
)
|
| 130 |
def update_chart(symbol, timeframe):
|
| 131 |
df = fetch_ohlcv(symbol, timeframe)
|
| 132 |
if df.empty:
|
| 133 |
-
return go.Figure()
|
| 134 |
df = add_indicators(df)
|
| 135 |
df = add_signals(df)
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
if __name__ == "__main__":
|
| 139 |
app.run(host="0.0.0.0", port=7860)
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
import ccxt
|
| 6 |
from datetime import datetime
|
| 7 |
+
from plotly.subplots import make_subplots
|
| 8 |
|
| 9 |
+
# Init MEXC
|
| 10 |
exchange = ccxt.mexc()
|
|
|
|
|
|
|
| 11 |
markets = exchange.load_markets()
|
| 12 |
symbols = sorted([symbol for symbol in markets if "/USDT" in symbol])
|
| 13 |
timeframes = list(exchange.timeframes.keys())
|
| 14 |
|
|
|
|
| 15 |
DEFAULT_SYMBOL = "BTC/USDT"
|
| 16 |
DEFAULT_TIMEFRAME = "1h"
|
| 17 |
|
|
|
|
| 18 |
def fetch_ohlcv(symbol, timeframe):
|
| 19 |
try:
|
| 20 |
ohlcv = exchange.fetch_ohlcv(symbol, timeframe)
|
|
|
|
| 22 |
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
|
| 23 |
return df
|
| 24 |
except Exception as e:
|
| 25 |
+
print(f"β Error: {e}")
|
| 26 |
return pd.DataFrame()
|
| 27 |
|
|
|
|
| 28 |
def add_indicators(df):
|
| 29 |
df = df.copy()
|
| 30 |
df['ema12'] = df['close'].ewm(span=12).mean()
|
| 31 |
df['ema26'] = df['close'].ewm(span=26).mean()
|
| 32 |
df['macd'] = df['ema12'] - df['ema26']
|
| 33 |
df['macd_signal'] = df['macd'].ewm(span=9).mean()
|
| 34 |
+
|
| 35 |
delta = df['close'].diff()
|
| 36 |
gain = delta.clip(lower=0)
|
| 37 |
+
loss = -delta.clip(upper=0)
|
| 38 |
avg_gain = gain.rolling(14).mean()
|
| 39 |
avg_loss = loss.rolling(14).mean()
|
| 40 |
rs = avg_gain / avg_loss
|
| 41 |
df['rsi'] = 100 - (100 / (1 + rs))
|
| 42 |
return df
|
| 43 |
|
|
|
|
| 44 |
def add_signals(df):
|
| 45 |
df['buy'] = (df['macd'] > df['macd_signal']) & (df['macd'].shift(1) <= df['macd_signal'].shift(1)) & (df['rsi'] < 50)
|
| 46 |
df['sell'] = (df['macd'] < df['macd_signal']) & (df['macd'].shift(1) >= df['macd_signal'].shift(1)) & (df['rsi'] > 50)
|
| 47 |
return df
|
| 48 |
|
|
|
|
| 49 |
def create_chart(df):
|
| 50 |
fig = make_subplots(rows=3, cols=1, shared_xaxes=True,
|
| 51 |
+
vertical_spacing=0.02, row_heights=[0.6, 0.2, 0.2])
|
|
|
|
| 52 |
|
| 53 |
+
fig.add_trace(go.Candlestick(x=df['timestamp'], open=df['open'], high=df['high'],
|
| 54 |
+
low=df['low'], close=df['close'], name='Candles'), row=1, col=1)
|
|
|
|
|
|
|
| 55 |
|
| 56 |
+
fig.add_trace(go.Scatter(x=df['timestamp'], y=df['rsi'], name='RSI',
|
| 57 |
+
line=dict(color='orange')), row=2, col=1)
|
|
|
|
| 58 |
|
| 59 |
+
fig.add_trace(go.Scatter(x=df['timestamp'], y=df['macd'], name='MACD',
|
| 60 |
+
line=dict(color='blue')), row=3, col=1)
|
| 61 |
|
| 62 |
+
fig.add_trace(go.Scatter(x=df['timestamp'], y=df['macd_signal'], name='MACD Signal',
|
| 63 |
+
line=dict(color='red')), row=3, col=1)
|
| 64 |
|
| 65 |
+
# Signal markers
|
| 66 |
buys = df[df['buy']]
|
| 67 |
sells = df[df['sell']]
|
| 68 |
+
fig.add_trace(go.Scatter(x=buys['timestamp'], y=buys['close'], mode='markers',
|
| 69 |
+
name='Buy', marker=dict(color='green', size=10, symbol='triangle-up')), row=1, col=1)
|
| 70 |
+
fig.add_trace(go.Scatter(x=sells['timestamp'], y=sells['close'], mode='markers',
|
| 71 |
+
name='Sell', marker=dict(color='red', size=10, symbol='triangle-down')), row=1, col=1)
|
| 72 |
|
| 73 |
+
fig.update_layout(height=800, margin=dict(t=30, b=20), showlegend=True)
|
| 74 |
return fig
|
| 75 |
|
| 76 |
+
# Dash App
|
|
|
|
|
|
|
| 77 |
app = dash.Dash(__name__)
|
| 78 |
+
app.title = "MEXC AI Signals"
|
| 79 |
|
| 80 |
app.layout = html.Div([
|
| 81 |
+
html.H2("π MEXC Crypto Signal Chart", style={'textAlign': 'center'}),
|
| 82 |
|
| 83 |
html.Div([
|
| 84 |
html.Label("Select Pair:"),
|
| 85 |
dcc.Dropdown(id="pair-dropdown", options=[{"label": s, "value": s} for s in symbols],
|
| 86 |
+
value=DEFAULT_SYMBOL, style={"width": "100%"})
|
| 87 |
+
], style={"width": "30%", "display": "inline-block"}),
|
| 88 |
|
| 89 |
html.Div([
|
| 90 |
+
html.Label("Timeframes:"),
|
| 91 |
html.Div([
|
| 92 |
html.Button(tf, id={'type': 'tf-button', 'index': tf}, n_clicks=0,
|
| 93 |
+
style={"margin": "2px", "width": "50px"}) for tf in timeframes
|
| 94 |
+
], id="tf-button-row")
|
| 95 |
+
], style={"width": "68%", "display": "inline-block"}),
|
| 96 |
|
| 97 |
+
dcc.Store(id="selected-timeframe", data=DEFAULT_TIMEFRAME),
|
| 98 |
dcc.Graph(id="chart", config={"displayModeBar": True}),
|
| 99 |
+
|
| 100 |
+
html.Div(id="signal-box", style={"marginTop": "10px", "textAlign": "center", "fontSize": "16px"})
|
| 101 |
])
|
| 102 |
|
|
|
|
| 103 |
@app.callback(
|
| 104 |
Output("selected-timeframe", "data"),
|
| 105 |
+
Input({'type': 'tf-button', 'index': dash.dependencies.ALL}, 'n_clicks')
|
| 106 |
)
|
| 107 |
def update_timeframe(n_clicks_list):
|
| 108 |
ctx = dash.callback_context
|
|
|
|
| 113 |
return timeframe
|
| 114 |
|
| 115 |
@app.callback(
|
| 116 |
+
[Output("chart", "figure"), Output("signal-box", "children")],
|
| 117 |
+
[Input("pair-dropdown", "value"), Input("selected-timeframe", "data")]
|
|
|
|
| 118 |
)
|
| 119 |
def update_chart(symbol, timeframe):
|
| 120 |
df = fetch_ohlcv(symbol, timeframe)
|
| 121 |
if df.empty:
|
| 122 |
+
return go.Figure(), "β No data available"
|
| 123 |
df = add_indicators(df)
|
| 124 |
df = add_signals(df)
|
| 125 |
+
|
| 126 |
+
latest_signal = "π No recent signal"
|
| 127 |
+
if df['buy'].iloc[-1]:
|
| 128 |
+
latest_signal = f"π’ Latest Signal: BUY at {df['close'].iloc[-1]:.2f} on {df['timestamp'].iloc[-1]}"
|
| 129 |
+
elif df['sell'].iloc[-1]:
|
| 130 |
+
latest_signal = f"π΄ Latest Signal: SELL at {df['close'].iloc[-1]:.2f} on {df['timestamp'].iloc[-1]}"
|
| 131 |
+
|
| 132 |
+
fig = create_chart(df)
|
| 133 |
+
return fig, latest_signal
|
| 134 |
|
| 135 |
if __name__ == "__main__":
|
| 136 |
app.run(host="0.0.0.0", port=7860)
|