"""
html = f"""
{game['name']} - Market Watch
{nav()}
๐ {game['name']}
{game['status'].upper()}
Started {game['start_date']} ยท ${game['starting_cash']:,.0f} starting cash ยท {len(game['players'])} players
Leaderboard
Rank
Player
Portfolio
Return
P&L
Positions
Trades
{rank_rows if rank_rows else '
No players yet
'}
Join This Game
"""
self.send_html(html)
def serve_player(self, game_id, username):
game = game_engine.get_game(game_id)
p = game_engine.get_portfolio(game_id, username)
if not game or not p:
return self.send_error(404)
trades = game_engine.get_trades(game_id, username)
snapshots = game_engine.get_snapshots(game_id, username)
pnl_class = "positive" if p["total_pnl"] >= 0 else "negative"
is_ai = username == "case"
badge = 'AI Player' if is_ai else 'Human'
# Positions table
pos_rows = ""
for ticker, pos in sorted(p["positions"].items()):
pc = "positive" if pos["unrealized_pnl"] >= 0 else "negative"
pos_rows += f"""
{ticker}
{pos['shares']}
${pos['avg_cost']:.2f}
${pos['current_price']:.2f}
${pos['market_value']:,.2f}
${pos['unrealized_pnl']:+,.2f}
${pos.get('trailing_stop',0):.2f}
"""
if not pos_rows:
pos_rows = '
No positions
'
# Trade log
trade_rows = ""
for t in reversed(trades[-30:]):
action_class = "positive" if t["action"] == "BUY" else "negative"
pnl_cell = ""
if t["action"] == "SELL":
rpnl = t.get("realized_pnl", 0)
rpnl_class = "positive" if rpnl >= 0 else "negative"
pnl_cell = f'${rpnl:+,.2f}'
trade_rows += f"""
{t['action']}
{t['ticker']}
{t['shares']}
${t['price']:.2f}
{pnl_cell}
{t.get('reason','')[:40]}
{t['timestamp'][:16]}
"""
chart_labels = json.dumps([s["date"] for s in snapshots])
chart_values = json.dumps([s["total_value"] for s in snapshots])
starting = game.get("starting_cash", 100000)
# Trade form (only for humans)
trade_form = "" if is_ai else f"""
๐ Place Trade
"""
html = f"""
{username} - {game['name']}
{nav()}
{p['cash']/max(p['total_value'],1)*100:.1f}% available
Return
{p['pnl_pct']:+.2f}%
${p['total_pnl']:+,.2f}
Positions
{p['num_positions']}
{len(trades)} total trades
Performance
{trade_form}
Positions
Ticker
Shares
Avg Cost
Price
Value
P&L
Stop
{pos_rows}
Trade Log
Action
Ticker
Shares
Price
P&L
Reason
Time
{trade_rows if trade_rows else '
No trades yet
'}
"""
self.send_html(html)
def serve_scans(self):
rows = ""
if os.path.exists(SCANS_DIR):
for sf in sorted(os.listdir(SCANS_DIR), reverse=True)[:30]:
if not sf.endswith(".json"): continue
data = {}
with open(os.path.join(SCANS_DIR, sf)) as f:
data = json.load(f)
n = data.get("candidates_found", len(data.get("candidates", [])))
top = ", ".join(c.get("ticker","?") for c in data.get("candidates", [])[:8])
rows += f'
{sf.replace(".json","")}
{data.get("total_scanned",0)}
{n}
{top or "โ"}
'
html = f"""
Scans - Market Watch
{nav('scans')}
๐ก GARP Scan History
Date
Scanned
Candidates
Top Picks
{rows if rows else '
No scans yet
'}
"""
self.send_html(html)
def redirect(self, url):
self.send_response(303)
self.send_header("Location", url)
self.end_headers()
def send_html(self, content):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(content.encode())
def send_json(self, data):
self.send_response(200)
self.send_header("Content-type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(json.dumps(data, default=str).encode())
def log_message(self, format, *args):
pass
def parse_form(body):
"""Parse URL-encoded form data."""
result = {}
for pair in body.split("&"):
if "=" in pair:
k, v = pair.split("=", 1)
from urllib.parse import unquote_plus
result[unquote_plus(k)] = unquote_plus(v)
return result
def main():
game_engine.ensure_default_game()
print(f"๐ Market Watch Portal starting on localhost:{PORT}")
server = ThreadedHTTPServer(("0.0.0.0", PORT), MarketWatchHandler)
try:
server.serve_forever()
except KeyboardInterrupt:
print("\nPortal stopped")
if __name__ == "__main__":
main()