Files
crypto_news_trading/run.py

162 lines
6.2 KiB
Python

import streamlit as st
import logging
from logging.handlers import RotatingFileHandler
import os
import json
from config import (
BINANCE_API_KEY, BINANCE_SECRET, CRYPTOPANIC_API_KEY, NEWS_API_KEY,
TWITTER_BEARER_TOKEN, REDDIT_CLIENT_ID, REDDIT_SECRET, REDDIT_USER_AGENT,
ANTHROPIC_API_KEY, DB_PATH, LOG_DIR, TOP_N_COINS, INITIAL_CAPITAL,
ANALYSIS_INTERVAL_MINUTES, DEFAULT_WEIGHTS, SURGE_VOLUME_MULTIPLIER,
)
from data.db import Database
from data.binance_rest import BinanceRestClient
from data.binance_ws import BinanceWSClient
from data.news_client import NewsClient
from data.social_client import SocialClient
from agents.ai_analyst import AIAgent
from engine.signal import SignalEngine
from engine.surge import SurgeDetector
from engine.portfolio import PortfolioManager
from scheduler.jobs import AnalysisJob, create_scheduler
from dashboard.sidebar import render_sidebar
from dashboard.detail import render_detail
from dashboard.portfolio_view import render_portfolio
os.makedirs(LOG_DIR, exist_ok=True)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
handlers=[
RotatingFileHandler(os.path.join(LOG_DIR, "app.log"), maxBytes=10_000_000, backupCount=5),
logging.StreamHandler(),
],
)
logger = logging.getLogger(__name__)
st.set_page_config(page_title="Crypto Signal Dashboard", layout="wide", page_icon="📊")
if "initialized" not in st.session_state:
st.session_state.initialized = False
st.session_state.selected_coin = None
@st.cache_resource
def init_services():
db = Database(DB_PATH)
db.init()
binance_rest = BinanceRestClient(BINANCE_API_KEY, BINANCE_SECRET)
binance_ws = BinanceWSClient(BINANCE_API_KEY, BINANCE_SECRET)
news_client = NewsClient(CRYPTOPANIC_API_KEY, NEWS_API_KEY)
social_client = SocialClient(REDDIT_CLIENT_ID, REDDIT_SECRET, REDDIT_USER_AGENT, TWITTER_BEARER_TOKEN)
ai_agent = AIAgent(ANTHROPIC_API_KEY)
signal_engine = SignalEngine()
surge_detector = SurgeDetector(SURGE_VOLUME_MULTIPLIER)
portfolio = PortfolioManager(INITIAL_CAPITAL)
saved_weights = db.load_setting("weights")
if saved_weights:
try:
signal_engine.set_weights(json.loads(saved_weights))
except (json.JSONDecodeError, ValueError):
pass
try:
monitored = binance_rest.get_top_coins(TOP_N_COINS)
except Exception:
monitored = ["BTCUSDT", "ETHUSDT", "BNBUSDT", "SOLUSDT", "XRPUSDT"]
logger.warning("Failed to fetch top coins, using defaults")
job = AnalysisJob(
binance=binance_rest, news_client=news_client,
social_client=social_client, ai_agent=ai_agent,
signal_engine=signal_engine, surge_detector=surge_detector,
portfolio=portfolio, db=db, monitored_coins=monitored,
)
scheduler = create_scheduler(job, ANALYSIS_INTERVAL_MINUTES)
scheduler.start()
try:
binance_ws.start(monitored[:20])
except Exception as e:
logger.warning(f"WebSocket start failed: {e}")
job.run_analysis()
return {
"db": db, "binance_rest": binance_rest, "binance_ws": binance_ws,
"news_client": news_client, "social_client": social_client,
"signal_engine": signal_engine, "portfolio": portfolio,
"job": job, "monitored": monitored,
}
def main():
services = init_services()
db = services["db"]
job = services["job"]
portfolio = services["portfolio"]
binance_rest = services["binance_rest"]
binance_ws = services["binance_ws"]
news_client = services["news_client"]
social_client = services["social_client"]
current_prices = binance_ws.get_all_prices()
if not current_prices:
try:
current_prices = binance_rest.get_all_prices()
except Exception:
current_prices = {}
page = render_sidebar(job.latest_results, db)
if page == "Signals":
selected = st.session_state.get("selected_coin")
if selected and selected in job.latest_results:
coin_data = job.latest_results[selected]
try:
ohlcv = binance_rest.get_ohlcv(selected, interval="1h", limit=100)
except Exception:
ohlcv = None
coin_news = news_client.filter_by_coin(news_client._cache, selected)
coin_posts = social_client.filter_posts_by_coin(social_client._cache, selected)
sentiment = social_client.simple_sentiment(coin_posts)
ai_summary = job.ai_summaries.get(selected, "AI analysis not yet available.")
render_detail(selected, coin_data, ohlcv, coin_news, sentiment, ai_summary)
else:
st.title("Crypto Signal Dashboard")
st.info("Select a coin from the sidebar to view detailed analysis.")
if job.latest_results:
_render_overview(job.latest_results)
elif page == "Portfolio":
render_portfolio(portfolio, current_prices, db)
def _render_overview(results: dict):
import pandas as pd
st.subheader("Signal Overview")
buy_coins = [r for r in results.values() if r["signal"] == "BUY"]
sell_coins = [r for r in results.values() if r["signal"] == "SELL"]
col1, col2 = st.columns(2)
with col1:
st.markdown("### :green[BUY Signals]")
if buy_coins:
rows = [{"Coin": c["symbol"].replace("USDT",""), "Score": f"{c['composite']:.0f}",
"Tech": f"{c['technical']:.0f}", "News": f"{c['news']:.0f}"}
for c in sorted(buy_coins, key=lambda x: x["composite"], reverse=True)]
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
else:
st.info("No BUY signals currently")
with col2:
st.markdown("### :red[SELL Signals]")
if sell_coins:
rows = [{"Coin": c["symbol"].replace("USDT",""), "Score": f"{c['composite']:.0f}",
"Tech": f"{c['technical']:.0f}", "News": f"{c['news']:.0f}"}
for c in sorted(sell_coins, key=lambda x: x["composite"])]
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
else:
st.info("No SELL signals currently")
if __name__ == "__main__":
main()