File size: 8,417 Bytes
77fd2f6 e48894b 77fd2f6 502f466 b02119b 77fd2f6 502f466 77fd2f6 30c7379 19c8e7e 30c7379 77fd2f6 b02119b 77fd2f6 b02119b 5312195 b02119b 7087a23 b02119b 7087a23 b02119b 7087a23 b02119b 0fd95c5 b02119b a6c0850 b02119b 5312195 b02119b 0fd95c5 b02119b 0fd95c5 b02119b 5312195 b02119b 5312195 77fd2f6 0fd95c5 5312195 0fd95c5 b02119b 0fd95c5 77fd2f6 19c8e7e 77fd2f6 19c8e7e 502f466 77fd2f6 19c8e7e 77fd2f6 30c7379 77fd2f6 | 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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | import streamlit as st
st.set_page_config(page_title="ArbIntel Scanner", layout="wide", page_icon="📈")
import asyncio
import pandas as pd
from datetime import datetime, timezone
import os
from supabase import create_client
import aiohttp
from difflib import SequenceMatcher
from src.strategies.arbitrage import CrossPlatformArbitrage
@st.cache_resource
def init_supabase():
url = st.secrets.get("SUPABASE_URL") or os.environ.get("SUPABASE_URL")
key = st.secrets.get("SUPABASE_KEY") or os.environ.get("SUPABASE_KEY")
if url and key:
return create_client(url, key)
return None
supabase = init_supabase()
# We would import the live clients here, but for the dashboard
# we can instantiate the scanner and populate it directly for testing.
if "capital" not in st.session_state:
st.session_state.capital = 10000.00
if "pnl" not in st.session_state:
st.session_state.pnl = 0.00
if "positions" not in st.session_state:
st.session_state.positions = []
if "trades" not in st.session_state:
st.session_state.trades = []
if "opps" not in st.session_state:
st.session_state.opps = None
st.title("ArbIntel: Prediction Markets Alpha Engine")
st.markdown("### Live Cross-Platform Arbitrage Scanner")
st.write("Detecting price inefficiencies between Polymarket and Kalshi in real-time.")
async def fetch_and_scan():
arb_engine = CrossPlatformArbitrage(min_profit_threshold=0.005)
arb_engine.market_map = {}
kalshi_key = st.secrets.get("KALSHI_API_KEY") or os.environ.get("KALSHI_API_KEY")
poly_markets = {}
kalshi_markets = {}
# REAL Polymarket Gamma API (more reliable)
try:
async with aiohttp.ClientSession() as session:
async with session.get(
"https://gamma-api.polymarket.com/markets",
params={"active": "true", "closed": "false", "limit": 100},
timeout=aiohttp.ClientTimeout(total=10)
) as resp:
data = await resp.json()
for m in data:
if m.get("outcomePrices") and len(m["outcomePrices"]) > 0:
try:
yes_price = float(m["outcomePrices"][0])
if 0.02 < yes_price < 0.98:
poly_markets[m["conditionId"]] = {
"name": m.get("question", ""),
"bid": yes_price - 0.01,
"ask": yes_price + 0.01,
"size": float(m.get("volume", 1000))
}
except ValueError:
pass
except Exception as e:
st.warning(f"Polymarket API: {e}")
# REAL Kalshi REST API (with key)
try:
async with aiohttp.ClientSession() as session:
async with session.get(
"https://api.elections.kalshi.com/trade-api/v2/markets",
params={"status": "open", "limit": 100},
timeout=aiohttp.ClientTimeout(total=10)
) as resp:
data = await resp.json()
for m in data.get("markets", []):
yes_bid = m.get("yes_bid", 0) / 100
yes_ask = m.get("yes_ask", 0) / 100
if yes_bid > 0:
kalshi_markets[m["ticker"]] = {
"name": m.get("title", ""),
"bid": yes_bid,
"ask": yes_ask,
"size": float(m.get("volume", 500))
}
except Exception as e:
st.warning(f"Kalshi API: {e}")
# CROSS-MATCH by name similarity
FEE = 0.02
for pm_id, pm in poly_markets.items():
pm_words = set(pm["name"].lower().split())
for k_ticker, km in kalshi_markets.items():
km_words = set(km["name"].lower().split())
if not pm_words.intersection(km_words):
continue
score = SequenceMatcher(
None,
pm["name"].lower()[:80],
km["name"].lower()[:80]
).ratio()
if score > 0.40:
arb_engine.market_map[pm_id] = pm["name"]
arb_engine.update_state(
"polymarket", pm_id,
pm["bid"], pm["size"], pm["ask"], pm["size"]
)
arb_engine.update_state(
"kalshi", k_ticker,
km["bid"], km["size"], km["ask"], km["size"]
)
opportunities = arb_engine.scan_opportunities()
filtered_opps = []
for opp in opportunities:
if opp.expected_profit_margin > (FEE + 0.005):
opp.event_name = arb_engine.market_map.get(
opp.market_id_pm,
f"{opp.market_id_pm}<->{opp.market_id_kalshi}"
)
filtered_opps.append(opp)
await asyncio.sleep(0.5)
return filtered_opps
if st.button("Run Live Arbitrage Scan", type="primary"):
with st.spinner("Fetching order books from Polymarket and Kalshi APIs..."):
st.session_state.opps = asyncio.run(fetch_and_scan())
if st.session_state.opps is not None:
if len(st.session_state.opps) > 0:
st.success(f"Detected {len(st.session_state.opps)} Arbitrage Opportunities!")
data = []
for o in st.session_state.opps:
data.append({
"Time (UTC)": o.timestamp.strftime("%H:%M:%S"),
"Event Mapped": o.event_name,
"Buy On": o.buy_platform.title(),
"Buy Price": f"${o.buy_price:.3f}",
"Max Size": f"${o.buy_size:.2f}",
"Sell On": o.sell_platform.title(),
"Sell Price": f"${o.sell_price:.3f}",
"Net Edge": f"{o.expected_profit_margin*100:.2f}%"
})
st.dataframe(pd.DataFrame(data), use_container_width=True)
st.markdown("#### Execute Paper Trade")
if st.button("Auto-Execute All"):
for o in st.session_state.opps:
profit = o.expected_profit_margin * o.buy_size
st.session_state.pnl += profit
st.session_state.capital += profit
if o.market_id_pm not in st.session_state.positions:
st.session_state.positions.append(o.market_id_pm)
st.session_state.trades.append(o)
if supabase:
try:
supabase.table("trades").insert({
"event_id": o.event_name,
"buy_platform": o.buy_platform,
"sell_platform": o.sell_platform,
"buy_price": o.buy_price,
"sell_price": o.sell_price,
"size": o.buy_size,
"net_edge": o.expected_profit_margin,
"pnl": profit
}).execute()
except Exception as e:
st.error(f"Failed to write trade to Supabase: {e}")
if supabase:
try:
supabase.table("portfolio").insert({
"capital": st.session_state.capital,
"pnl": st.session_state.pnl,
"active_positions": len(st.session_state.positions),
"market_regime": "Stable"
}).execute()
except Exception as e:
st.error(f"Failed to write portfolio snapshot to Supabase: {e}")
st.session_state.opps = None
st.rerun()
else:
st.info("No active opportunities detected above risk-free threshold (markets efficient).")
st.markdown("---")
st.markdown("### Portfolio & Risk Metrics")
col1, col2, col3, col4 = st.columns(4)
pnl_sign = "+" if st.session_state.pnl >= 0 else "-"
col1.metric("Available Capital", f"${st.session_state.capital:,.2f}", f"{pnl_sign}${abs(st.session_state.pnl):,.2f}")
col2.metric("Active Positions", str(len(st.session_state.positions)))
col3.metric("24h PnL", f"${st.session_state.pnl:,.2f}")
col4.metric("Market Regime", "Stable", "-Vol")
|