market-forge-env / create_guide_doc.py
kenmandal's picture
Deploy Multi-Agent MarketForge with GPU + trained model support
da87fff verified
Raw
History Blame Contribute Delete
64.8 kB
"""
MarketForge β€” Comprehensive Guide Document Generator
=====================================================
Creates a professional Word document covering:
Part 1: How the environment was built (step-by-step)
Part 2: How business users play in it
Part 3: How developers build and train models
"""
from docx import Document
from docx.shared import Inches, Pt, Cm, RGBColor, Emu
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
import os
doc = Document()
# ── Page Setup ─────────────────────────────────────────────────
for section in doc.sections:
section.top_margin = Cm(2.0)
section.bottom_margin = Cm(2.0)
section.left_margin = Cm(2.5)
section.right_margin = Cm(2.5)
# ── Custom Styles ──────────────────────────────────────────────
style = doc.styles['Normal']
font = style.font
font.name = 'Calibri'
font.size = Pt(11)
font.color.rgb = RGBColor(0x1A, 0x1A, 0x1A)
style.paragraph_format.space_after = Pt(6)
style.paragraph_format.line_spacing = 1.15
# Heading styles
for i, (size, color) in enumerate([(28, 0x0B1D3A), (18, 0x0B1D3A), (14, 0x1565C0), (12, 0x333333)], 1):
hs = doc.styles[f'Heading {i}']
hs.font.name = 'Calibri'
hs.font.size = Pt(size)
hs.font.color.rgb = RGBColor((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF)
hs.font.bold = True
hs.paragraph_format.space_before = Pt(18 if i <= 2 else 12)
hs.paragraph_format.space_after = Pt(8 if i <= 2 else 6)
def add_code_block(doc, code_text, language="python"):
"""Add a formatted code block."""
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(4)
p.paragraph_format.space_after = Pt(4)
p.paragraph_format.left_indent = Cm(0.5)
run = p.add_run(code_text)
run.font.name = 'Consolas'
run.font.size = Pt(9)
run.font.color.rgb = RGBColor(0x1A, 0x1A, 0x1A)
# Add shading
shading = run._element.get_or_add_tcPr if hasattr(run._element, 'get_or_add_tcPr') else None
return p
def add_table(doc, headers, rows, col_widths=None):
"""Add a formatted table."""
table = doc.add_table(rows=1 + len(rows), cols=len(headers))
table.style = 'Light Grid Accent 1'
table.alignment = WD_TABLE_ALIGNMENT.CENTER
# Headers
for i, header in enumerate(headers):
cell = table.rows[0].cells[i]
cell.text = header
for paragraph in cell.paragraphs:
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
for run in paragraph.runs:
run.font.bold = True
run.font.size = Pt(10)
run.font.name = 'Calibri'
# Data rows
for r, row in enumerate(rows):
for c, val in enumerate(row):
cell = table.rows[r + 1].cells[c]
cell.text = str(val)
for paragraph in cell.paragraphs:
for run in paragraph.runs:
run.font.size = Pt(10)
run.font.name = 'Calibri'
if col_widths:
for i, w in enumerate(col_widths):
for row in table.rows:
row.cells[i].width = Inches(w)
doc.add_paragraph() # spacing
return table
def add_note(doc, text, label="NOTE"):
"""Add a callout note."""
p = doc.add_paragraph()
run = p.add_run(f"{label}: ")
run.font.bold = True
run.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
run.font.size = Pt(10)
run = p.add_run(text)
run.font.size = Pt(10)
run.font.italic = True
return p
def add_step(doc, number, title, body):
"""Add a numbered step with title and body."""
p = doc.add_paragraph()
run = p.add_run(f"Step {number}: {title}")
run.font.bold = True
run.font.size = Pt(12)
run.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
doc.add_paragraph(body)
# ══════════════════════════════════════════════════════════════
# COVER PAGE
# ══════════════════════════════════════════════════════════════
doc.add_paragraph()
doc.add_paragraph()
title_p = doc.add_paragraph()
title_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = title_p.add_run("MarketForge")
run.font.size = Pt(42)
run.font.bold = True
run.font.color.rgb = RGBColor(0x0B, 0x1D, 0x3A)
subtitle_p = doc.add_paragraph()
subtitle_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = subtitle_p.add_run("Multi-Agent Market Simulation Environment")
run.font.size = Pt(18)
run.font.color.rgb = RGBColor(0x15, 0x65, 0xC0)
doc.add_paragraph()
desc_p = doc.add_paragraph()
desc_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = desc_p.add_run(
"Complete Guide: Environment Setup, Business User Playbook,\n"
"and Developer Training Guide"
)
run.font.size = Pt(14)
run.font.color.rgb = RGBColor(0x55, 0x55, 0x55)
doc.add_paragraph()
doc.add_paragraph()
meta_p = doc.add_paragraph()
meta_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = meta_p.add_run("Built with OpenEnv v0.2.1 | GRPO Training | Gradio Dashboard")
run.font.size = Pt(11)
run.font.color.rgb = RGBColor(0x88, 0x88, 0x88)
doc.add_page_break()
# ══════════════════════════════════════════════════════════════
# TABLE OF CONTENTS (Manual)
# ══════════════════════════════════════════════════════════════
doc.add_heading("Table of Contents", level=1)
toc_items = [
("Part 1", "How the MarketForge Environment Was Built"),
(" 1.1", "Project Structure and Architecture"),
(" 1.2", "Step 1 β€” Install Dependencies"),
(" 1.3", "Step 2 β€” Define the Data Models (models.py)"),
(" 1.4", "Step 3 β€” Build the Market Engine (market_environment.py)"),
(" 1.5", "Step 4 β€” Create the HTTP Server (server/app.py)"),
(" 1.6", "Step 5 β€” Build the HTTP Client (client.py)"),
(" 1.7", "Step 6 β€” Write the Reward Functions (rewards.py)"),
(" 1.8", "Step 7 β€” Build the Visual Dashboard (app_visual.py)"),
(" 1.9", "Step 8 β€” Create the Training Script (train_market_forge.py)"),
(" 1.10", "Step 9 β€” Dockerize for Deployment"),
(" 1.11", "Step 10 β€” Run and Test Everything"),
("", ""),
("Part 2", "Business User Playbook β€” Trading in MarketForge"),
(" 2.1", "Meet the Trading Personas"),
(" 2.2", "How to Launch the Trading Playground"),
(" 2.3", "Running an Automated Simulation"),
(" 2.4", "Manual Trading β€” Make Your Own Decisions"),
(" 2.5", "Understanding the Dashboard Charts"),
(" 2.6", "How Awards Work β€” Winning the Game"),
(" 2.7", "Reading the Leaderboard"),
(" 2.8", "Trading Strategy Tips for Business Users"),
("", ""),
("Part 3", "Developer Guide β€” Building and Training Models"),
(" 3.1", "Architecture Overview for Developers"),
(" 3.2", "Connecting to the Environment via HTTP API"),
(" 3.3", "Building a Custom Agent (Python)"),
(" 3.4", "Training an LLM Agent with GRPO"),
(" 3.5", "Reward Function Design"),
(" 3.6", "Multi-Turn Rollouts and env_mask"),
(" 3.7", "Deploying to HuggingFace Spaces"),
(" 3.8", "Evaluation and Benchmarking"),
(" 3.9", "Extending the Environment"),
]
for num, title in toc_items:
if not num and not title:
doc.add_paragraph()
continue
p = doc.add_paragraph()
if num.startswith("Part"):
run = p.add_run(f"{num}: {title}")
run.font.bold = True
run.font.size = Pt(12)
else:
run = p.add_run(f" {num} {title}")
run.font.size = Pt(11)
doc.add_page_break()
# ══════════════════════════════════════════════════════════════
# PART 1: HOW THE ENVIRONMENT WAS BUILT
# ══════════════════════════════════════════════════════════════
doc.add_heading("Part 1: How the MarketForge Environment Was Built", level=1)
doc.add_paragraph(
"This section walks you through every step of creating the MarketForge "
"multi-agent trading environment from scratch. Each step includes the exact "
"commands and scripts you need to run manually to build, test, and verify "
"each component."
)
# ── 1.1 Project Structure ──
doc.add_heading("1.1 Project Structure and Architecture", level=2)
doc.add_paragraph(
"MarketForge follows the OpenEnv v0.2.1 architecture: a server-side Environment "
"that manages game state, and a client-side EnvClient that agents use to interact "
"with it over HTTP. Here is the complete file structure:"
)
add_code_block(doc,
"market-forge-openenv/\n"
"|\n"
"|-- models.py # Data contracts (Action, Observation, State)\n"
"|-- client.py # HTTP client for agents to connect\n"
"|-- rewards.py # Reward functions for GRPO training\n"
"|-- app_visual.py # Gradio dashboard (visual playground)\n"
"|-- train_market_forge.py # GRPO training script\n"
"|-- train_market_forge_notebook.py # Notebook-style training + evaluation\n"
"|-- Dockerfile # Docker deployment for HuggingFace Spaces\n"
"|-- requirements.txt # Python dependencies\n"
"|-- README.md # Project documentation\n"
"|\n"
"|-- server/\n"
"| |-- __init__.py\n"
"| |-- app.py # FastAPI HTTP server\n"
"| |-- market_environment.py # Core market engine (the brain)\n"
"|\n"
"|-- .claude/\n"
" |-- launch.json # Dev server configuration\n"
)
doc.add_paragraph(
"The architecture follows a clean separation:"
)
add_table(doc,
["Layer", "File", "Responsibility"],
[
["Data Contracts", "models.py", "MarketAction, MarketObservation, MarketState dataclasses"],
["Market Engine", "server/market_environment.py", "Core game logic, order matching, scoring, awards"],
["HTTP Server", "server/app.py", "FastAPI endpoints: /reset, /step, /state, /health"],
["HTTP Client", "client.py", "MarketForgeEnv class for agents to connect"],
["Rewards", "rewards.py", "5 reward functions for GRPO training"],
["Dashboard", "app_visual.py", "Gradio UI with simulation, manual control, charts"],
["Training", "train_market_forge.py", "GRPO training pipeline with TRL"],
],
col_widths=[1.2, 2.2, 3.0]
)
# ── 1.2 Install Dependencies ──
doc.add_heading("1.2 Step 1 β€” Install Dependencies", level=2)
doc.add_paragraph("Open a terminal and run the following commands:")
add_code_block(doc,
"# Create a project directory\n"
"mkdir -p market-forge-openenv/server\n"
"cd market-forge-openenv\n"
"\n"
"# Install core dependencies\n"
"pip install fastapi 'uvicorn[standard]' gradio matplotlib numpy requests\n"
"\n"
"# Install training dependencies (for developers)\n"
"pip install datasets transformers trl accelerate\n"
"\n"
"# Optional: Install OpenEnv core (if available)\n"
"pip install 'openenv-core[core]>=0.2.1'\n"
)
add_note(doc, "The environment works standalone without openenv-core. "
"Fallback base classes are included in models.py and client.py.")
doc.add_paragraph(
"Verify your installation:"
)
add_code_block(doc,
"python3 -c \"import fastapi, gradio, matplotlib, numpy; print('All dependencies OK')\"\n"
)
# ── 1.3 Define Data Models ──
doc.add_heading("1.3 Step 2 β€” Define the Data Models (models.py)", level=2)
doc.add_paragraph(
"The data models define the contract between agents and the environment. "
"There are exactly three dataclasses:"
)
add_table(doc,
["Dataclass", "Direction", "Purpose"],
[
["MarketAction", "Agent -> Environment", "What the agent wants to do (buy, sell, negotiate, etc.)"],
["MarketObservation", "Environment -> Agent", "What the agent can see (cash, inventory, prices, events)"],
["MarketState", "Server-side only", "Full game state for debugging and visualization"],
],
col_widths=[1.5, 1.8, 3.1]
)
doc.add_paragraph("MarketAction fields β€” the agent's controls:")
add_table(doc,
["Field", "Type", "Description"],
[
["agent_id", "str", "Which agent is acting (e.g., 'trader_1')"],
["action_type", "str", "buy, sell, produce, negotiate, accept_deal, reject_deal, propose_coalition, join_coalition, leave_coalition, pass"],
["commodity", "str", "wheat, iron, timber, or oil"],
["price", "float", "The price per unit"],
["quantity", "int", "How many units"],
["target_agent", "str", "Who to negotiate with"],
["message", "str", "Natural language message for negotiation"],
["coalition_id", "str", "Which coalition or deal to join/accept/reject"],
["compound_good", "str", "bread, tools, or furniture (for produce action)"],
],
col_widths=[1.3, 0.8, 4.3]
)
doc.add_paragraph("MarketObservation fields β€” the agent's dashboard:")
add_table(doc,
["Field", "Type", "What the Agent Sees"],
[
["agent_id, role", "str", "Identity: who am I and what's my role?"],
["cash", "float", "How much money I have"],
["inventory", "dict", "What commodities I hold: {wheat: 10, iron: 5, ...}"],
["reputation", "float", "My trustworthiness score (0.0 to 2.0)"],
["top_of_book", "dict", "Best bid/ask prices per commodity"],
["last_trade_prices", "dict", "Most recent trade price per commodity"],
["price_history", "list", "Historical prices (last 10 rounds)"],
["messages", "list", "Incoming messages from other agents"],
["pending_deals", "list", "Deal offers waiting for my response"],
["coalitions", "list", "Alliances I belong to"],
["event", "str", "Current market event (e.g., 'Drought reduces wheat...')"],
["round_number", "int", "Current round"],
["done", "bool", "Is the game over?"],
["reward", "float", "Reward from my last action"],
["total_wealth", "float", "My total wealth (cash + inventory value)"],
["prompt", "str", "Text description for LLM agents"],
["legal_actions", "list", "Valid action types right now"],
],
col_widths=[1.5, 0.8, 4.1]
)
doc.add_paragraph("To create models.py, save the following script:")
add_code_block(doc,
"# File: models.py\n"
"# Run: python3 -c \"from models import MarketAction, MarketObservation; print('Models OK')\"\n"
"#\n"
"# This file defines three dataclasses that form the API contract.\n"
"# See the full file in the project repository.\n"
)
add_note(doc,
"After creating models.py, verify it by running:\n"
"python3 -c \"from models import MarketAction; a = MarketAction(agent_id='test', action_type='buy', "
"commodity='wheat', price=10, quantity=5); print(a)\""
)
# ── 1.4 Build Market Engine ──
doc.add_heading("1.4 Step 3 β€” Build the Market Engine (server/market_environment.py)", level=2)
doc.add_paragraph(
"This is the core of MarketForge β€” the market engine that processes all "
"agent actions, manages order books, matches trades, and computes rewards. "
"It implements the standard OpenEnv interface with three methods:"
)
add_table(doc,
["Method", "What It Does", "Analogy"],
[
["reset(**kwargs)", "Start a new game. All agents get starting cash and inventory.", "Starting the car engine"],
["step(action)", "Execute one action. Returns observation + reward.", "Pressing gas / brake / steering"],
["state (property)", "View the full game state. For debugging only.", "Looking under the hood"],
],
col_widths=[1.5, 3.5, 1.4]
)
doc.add_paragraph("Key constants built into the engine:")
add_table(doc,
["Constant", "Values"],
[
["COMMODITIES", "wheat, iron, timber, oil"],
["COMPOUND_GOODS", "bread (2 wheat + 1 oil), tools (2 iron + 1 timber), furniture (2 timber + 1 oil)"],
["BASE_PRICES", "wheat=$10, iron=$15, timber=$12, oil=$18"],
["COMPOUND_PRICES", "bread=$35, tools=$50, furniture=$45"],
["AGENTS", "4 producers, 2 consumers, 1 trader, 1 speculator (8 total)"],
["EVENTS", "12 stochastic market events (droughts, embargoes, surpluses, etc.)"],
],
col_widths=[1.5, 4.9]
)
doc.add_paragraph("The 8 agent roles and their starting conditions:")
add_table(doc,
["Agent ID", "Role", "Specialty", "Starting Cash", "Starting Inventory"],
[
["producer_wheat", "Producer", "Wheat", "$500", "40 wheat"],
["producer_iron", "Producer", "Iron", "$500", "40 iron"],
["producer_timber", "Producer", "Timber", "$500", "40 timber"],
["producer_oil", "Producer", "Oil", "$500", "40 oil"],
["consumer_1", "Consumer", "Bread", "$1,000", "None"],
["consumer_2", "Consumer", "Tools", "$1,000", "None"],
["trader_1", "Trader", "General", "$800", "5 of each commodity"],
["speculator_1", "Speculator", "Events", "$1,500", "None"],
],
col_widths=[1.4, 0.9, 0.8, 1.0, 2.3]
)
doc.add_paragraph("What happens inside step():")
doc.add_paragraph(
"1. Agent submits a MarketAction (e.g., buy 5 wheat at $12)\n"
"2. The engine validates the action (enough cash? valid commodity?)\n"
"3. For buy/sell: places order on the order book, attempts to match with existing orders\n"
"4. For negotiate: creates a pending deal and delivers a message to the target\n"
"5. For produce: checks recipe ingredients, consumes them, creates compound good\n"
"6. For coalition actions: creates/joins/leaves alliances\n"
"7. Computes reward based on the outcome\n"
"8. After all agents act in a round: advances the round, generates a new market event, "
"updates prices, and regenerates producer inventory\n"
"9. When round_number reaches max_rounds: game ends, awards and leaderboard are computed"
)
doc.add_paragraph("How trade matching works (Continuous Double Auction):")
add_code_block(doc,
"# Order Book Matching Algorithm:\n"
"# 1. Sort all buy orders (bids) by price: highest first\n"
"# 2. Sort all sell orders (asks) by price: lowest first\n"
"# 3. While best_bid.price >= best_ask.price:\n"
"# trade_price = (best_bid.price + best_ask.price) / 2\n"
"# trade_qty = min(bid.quantity, ask.quantity)\n"
"# buyer pays: trade_price * trade_qty\n"
"# seller receives: trade_price * trade_qty\n"
"# buyer gains inventory, seller loses inventory\n"
"# record trade in trade_history\n"
)
doc.add_paragraph("Verify the engine:")
add_code_block(doc,
"# Test the market engine directly\n"
"cd market-forge-openenv\n"
"python3 -c \"\n"
"from server.market_environment import MarketEnvironment\n"
"from models import MarketAction\n"
"\n"
"env = MarketEnvironment()\n"
"obs = env.reset(max_rounds=10)\n"
"print(f'Game started. Round: {obs.round_number}/{obs.max_rounds}')\n"
"print(f'Event: {obs.event}')\n"
"print(f'Cash: {obs.cash}, Inventory: {obs.inventory}')\n"
"\n"
"# Place a buy order\n"
"action = MarketAction(agent_id='trader_1', action_type='buy',\n"
" commodity='wheat', price=12.0, quantity=5)\n"
"obs = env.step(action)\n"
"print(f'Reward: {obs.reward:.3f}, Cash: {obs.cash}')\n"
"\n"
"# Check awards\n"
"awards = env.compute_awards()\n"
"print(f'Market Champion: {awards[\\\"market_champion\\\"][\\\"agent_id\\\"]}')\n"
"print(f'Master Strategist: {awards[\\\"master_strategist\\\"][\\\"agent_id\\\"]}')\n"
"print('Engine OK!')\n"
"\"\n"
)
# ── 1.5 Create HTTP Server ──
doc.add_heading("1.5 Step 4 β€” Create the HTTP Server (server/app.py)", level=2)
doc.add_paragraph(
"The FastAPI server exposes the environment over HTTP so that agents "
"(local or remote) can connect to it. It provides four endpoints:"
)
add_table(doc,
["Endpoint", "Method", "What It Does", "Request Body"],
[
["/health", "GET", "Check if the server is running", "None"],
["/reset", "POST", "Start a new game", "{max_rounds: 30, seed: 42}"],
["/step", "POST", "Execute one agent action", "{agent_id: 'trader_1', action_type: 'buy', ...}"],
["/state", "GET", "Get full game state (debug)", "None"],
],
col_widths=[0.8, 0.8, 2.0, 2.8]
)
doc.add_paragraph("Start the server manually:")
add_code_block(doc,
"# Option 1: Run with uvicorn\n"
"cd market-forge-openenv\n"
"uvicorn server.app:app --host 0.0.0.0 --port 8000 --reload\n"
"\n"
"# Option 2: Run the module directly\n"
"python3 -m server.app\n"
)
doc.add_paragraph("Test the server with curl:")
add_code_block(doc,
"# Check health\n"
"curl http://localhost:8000/health\n"
"# Expected: {\"status\":\"healthy\",\"env\":\"market_forge\",\"version\":\"0.2.1\"}\n"
"\n"
"# Reset the game\n"
"curl -X POST http://localhost:8000/reset \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"max_rounds\": 20}'\n"
"\n"
"# Place a buy order\n"
"curl -X POST http://localhost:8000/step \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"agent_id\":\"trader_1\",\"action_type\":\"buy\",\"commodity\":\"wheat\",\"price\":12,\"quantity\":5}'\n"
"\n"
"# Get full state\n"
"curl http://localhost:8000/state\n"
)
# ── 1.6 Build HTTP Client ──
doc.add_heading("1.6 Step 5 β€” Build the HTTP Client (client.py)", level=2)
doc.add_paragraph(
"The MarketForgeEnv client is the steering wheel that agents use to "
"interact with the environment. It wraps the HTTP endpoints into a "
"simple Python interface."
)
add_code_block(doc,
"# Using the client:\n"
"from client import MarketForgeEnv\n"
"from models import MarketAction\n"
"\n"
"# Connect to the server\n"
"env = MarketForgeEnv(base_url='http://localhost:8000')\n"
"\n"
"# Start a new game\n"
"result = env.reset(max_rounds=30)\n"
"print(result.observation.prompt) # What the agent sees\n"
"print(result.observation.legal_actions) # What the agent can do\n"
"\n"
"# Take an action\n"
"action = MarketAction(\n"
" agent_id='trader_1',\n"
" action_type='buy',\n"
" commodity='wheat',\n"
" price=12.0,\n"
" quantity=5,\n"
")\n"
"result = env.step(action)\n"
"print(f'Reward: {result.reward}')\n"
"print(f'Done: {result.done}')\n"
"\n"
"# When done=True, check who won\n"
"if result.done:\n"
" awards = result.observation.market_summary.get('awards', {})\n"
" print(f'Champion: {awards[\"market_champion\"][\"agent_id\"]}')\n"
)
# ── 1.7 Reward Functions ──
doc.add_heading("1.7 Step 6 β€” Write the Reward Functions (rewards.py)", level=2)
doc.add_paragraph(
"The reward functions tell the training algorithm what good behavior looks like. "
"MarketForge uses five reward functions, each measuring a different aspect:"
)
add_table(doc,
["Function", "Weight", "What It Measures"],
[
["reward_from_env", "40%", "Direct profit/loss from each trade (environment feedback)"],
["reward_valid_json", "20%", "Did the agent produce a valid JSON action? (format accuracy)"],
["reward_strategic_depth", "20%", "Did the agent use advanced actions like negotiate or propose_coalition?"],
["reward_event_response", "10%", "Did the agent adapt to market events (e.g., buy wheat during drought)?"],
["reward_wealth_growth", "10%", "Did the agent's total wealth increase over the episode?"],
],
col_widths=[1.8, 0.7, 3.9]
)
doc.add_paragraph("Test the reward functions:")
add_code_block(doc,
"python3 -c \"\n"
"from rewards import reward_valid_json, reward_strategic_depth\n"
"\n"
"# Test valid JSON reward\n"
"good = ['{\\\"action_type\\\":\\\"buy\\\",\\\"commodity\\\":\\\"wheat\\\",\\\"price\\\":10,\\\"quantity\\\":5}']\n"
"bad = ['this is not json']\n"
"print(f'Good action reward: {reward_valid_json(good)}')\n"
"print(f'Bad action reward: {reward_valid_json(bad)}')\n"
"\"\n"
)
# ── 1.8 Visual Dashboard ──
doc.add_heading("1.8 Step 7 β€” Build the Visual Dashboard (app_visual.py)", level=2)
doc.add_paragraph(
"The Gradio dashboard provides a visual playground for running simulations, "
"manually controlling agents, and viewing rich charts. It runs on port 7860."
)
doc.add_paragraph("The dashboard has three tabs:")
add_table(doc,
["Tab", "Purpose", "How to Use"],
[
["Simulation", "Run automated simulation with AI agents", "Set rounds (10-100), click 'Run Simulation'"],
["Manual Control", "Control individual agents yourself", "Select agent, action, commodity, price, quantity"],
["Environment Info", "Documentation and reference", "Read about rules, actions, awards, scoring"],
],
col_widths=[1.3, 2.5, 2.6]
)
doc.add_paragraph("The dashboard shows 6 chart panels:")
add_table(doc,
["Panel", "Chart", "What It Shows"],
[
["1", "Commodity Prices", "Price trends for wheat, iron, timber, oil over time"],
["2", "Wealth Race (Award 1)", "Each agent's total wealth per round, champion highlighted"],
["3", "Average Reward", "Mean reward across agents per round with moving average"],
["4", "Market Activity", "Cumulative trades, active coalitions, compound goods produced"],
["5", "Strategic Scores (Award 2)", "Horizontal bar chart of strategic score per agent"],
["6", "Action Accuracy", "% of valid actions per agent (green/yellow/red)"],
],
col_widths=[0.5, 1.8, 4.1]
)
doc.add_paragraph("Launch the dashboard:")
add_code_block(doc,
"cd market-forge-openenv\n"
"python3 app_visual.py\n"
"\n"
"# Then open in your browser:\n"
"# http://localhost:7860\n"
)
# ── 1.9 Training Script ──
doc.add_heading("1.9 Step 8 β€” Create the Training Script (train_market_forge.py)", level=2)
doc.add_paragraph(
"The training script uses HuggingFace TRL's GRPOTrainer to train an LLM agent "
"to make smart trading decisions. It follows the same pattern as the official "
"OpenEnv Wordle GRPO notebook."
)
doc.add_paragraph("Training flow:")
doc.add_paragraph(
"1. Generate 256 diverse market scenarios as training prompts\n"
"2. For each prompt: the LLM generates an action (JSON) -> environment steps -> reward is collected\n"
"3. Multi-turn episodes: 6 actions per game (agent sees feedback and adapts)\n"
"4. Three reward signals combined: environment reward + format accuracy + strategic depth\n"
"5. GRPO optimizer updates the policy using group relative advantage\n"
"6. The env_mask mechanism ensures only the agent's tokens are optimized (not environment feedback)"
)
doc.add_paragraph("Run training:")
add_code_block(doc,
"# Requires GPU (Google Colab T4/A100 recommended)\n"
"pip install trl transformers datasets accelerate\n"
"\n"
"cd market-forge-openenv\n"
"python3 train_market_forge.py\n"
"\n"
"# Training output will be saved to: ./market-forge-agent/\n"
)
# ── 1.10 Dockerize ──
doc.add_heading("1.10 Step 9 β€” Dockerize for Deployment", level=2)
doc.add_paragraph(
"The Dockerfile bundles everything into a single container that runs both "
"the API server (port 8000) and the Gradio dashboard (port 7860)."
)
add_code_block(doc,
"# Build the Docker image\n"
"cd market-forge-openenv\n"
"docker build -t marketforge .\n"
"\n"
"# Run the container\n"
"docker run -p 7860:7860 -p 8000:8000 marketforge\n"
"\n"
"# Access:\n"
"# Dashboard: http://localhost:7860\n"
"# API: http://localhost:8000/health\n"
)
# ── 1.11 Run and Test Everything ──
doc.add_heading("1.11 Step 10 β€” Run and Test Everything", level=2)
doc.add_paragraph("Complete verification script β€” copy and run this to test all components:")
add_code_block(doc,
"cd market-forge-openenv\n"
"python3 << 'EOF'\n"
"import sys\n"
"\n"
"# Test 1: Models\n"
"from models import MarketAction, MarketObservation, MarketState\n"
"a = MarketAction(agent_id='test', action_type='buy', commodity='wheat', price=10, quantity=5)\n"
"assert a.action_type == 'buy'\n"
"print('[PASS] Test 1: Models load correctly')\n"
"\n"
"# Test 2: Environment reset\n"
"from server.market_environment import MarketEnvironment, COMMODITIES, AGENT_ROLES\n"
"env = MarketEnvironment()\n"
"obs = env.reset(max_rounds=10)\n"
"assert obs.round_number == 0\n"
"assert obs.cash > 0\n"
"print('[PASS] Test 2: Environment resets correctly')\n"
"\n"
"# Test 3: Step with buy action\n"
"action = MarketAction(agent_id='trader_1', action_type='buy',\n"
" commodity='wheat', price=12.0, quantity=3)\n"
"obs = env.step(action)\n"
"assert hasattr(obs, 'reward')\n"
"print('[PASS] Test 3: Step executes correctly')\n"
"\n"
"# Test 4: Step with sell action\n"
"action = MarketAction(agent_id='producer_wheat', action_type='sell',\n"
" commodity='wheat', price=9.0, quantity=5)\n"
"obs = env.step(action)\n"
"print('[PASS] Test 4: Sell action works')\n"
"\n"
"# Test 5: Produce compound good\n"
"# Give consumer ingredients first\n"
"env._state.agents['consumer_1']['inventory']['wheat'] = 10\n"
"env._state.agents['consumer_1']['inventory']['oil'] = 5\n"
"action = MarketAction(agent_id='consumer_1', action_type='produce', compound_good='bread')\n"
"obs = env.step(action)\n"
"print('[PASS] Test 5: Production works')\n"
"\n"
"# Test 6: Negotiate\n"
"action = MarketAction(agent_id='consumer_2', action_type='negotiate',\n"
" target_agent='producer_iron', commodity='iron',\n"
" price=13.0, quantity=5, message='Need iron for tools')\n"
"obs = env.step(action)\n"
"print('[PASS] Test 6: Negotiation works')\n"
"\n"
"# Test 7: Coalition\n"
"action = MarketAction(agent_id='speculator_1', action_type='propose_coalition',\n"
" message='Speculator alliance')\n"
"obs = env.step(action)\n"
"print('[PASS] Test 7: Coalition creation works')\n"
"\n"
"# Test 8: Awards\n"
"awards = env.compute_awards()\n"
"assert 'market_champion' in awards\n"
"assert 'master_strategist' in awards\n"
"print('[PASS] Test 8: Awards compute correctly')\n"
"\n"
"# Test 9: Leaderboard\n"
"lb = env.compute_leaderboard()\n"
"assert len(lb) > 0\n"
"assert lb[0]['rank'] == 1\n"
"print('[PASS] Test 9: Leaderboard ranks correctly')\n"
"\n"
"# Test 10: Accuracy\n"
"acc = env.compute_accuracy()\n"
"assert all(0 <= v <= 1 for v in acc.values())\n"
"print('[PASS] Test 10: Accuracy tracking works')\n"
"\n"
"# Test 11: Client class\n"
"from client import MarketForgeEnv\n"
"print('[PASS] Test 11: Client imports correctly')\n"
"\n"
"# Test 12: Rewards\n"
"from rewards import reward_valid_json, reward_strategic_depth\n"
"r = reward_valid_json(['{\"action_type\":\"buy\",\"commodity\":\"wheat\",\"price\":10,\"quantity\":5}'])\n"
"assert r[0] > 0\n"
"print('[PASS] Test 12: Reward functions work')\n"
"\n"
"print()\n"
"print('=' * 50)\n"
"print(' ALL 12 TESTS PASSED - MarketForge is ready!')\n"
"print('=' * 50)\n"
"EOF\n"
)
doc.add_page_break()
# ══════════════════════════════════════════════════════════════
# PART 2: BUSINESS USER PLAYBOOK
# ══════════════════════════════════════════════════════════════
doc.add_heading("Part 2: Business User Playbook β€” Trading in MarketForge", level=1)
doc.add_paragraph(
"This section is written for business users, traders, and non-technical team members "
"who want to use MarketForge as a trading playground. No coding is required β€” everything "
"is done through the visual dashboard."
)
# ── 2.1 Meet the Personas ──
doc.add_heading("2.1 Meet the Trading Personas", level=2)
doc.add_paragraph(
"MarketForge simulates a realistic multi-commodity marketplace with eight "
"participants, each playing a different business role:"
)
add_table(doc,
["Persona", "Role", "Business Objective", "Starting Position"],
[
["Wheat Farmer", "Producer", "Grow and sell wheat at the best price", "$500 cash + 40 wheat"],
["Iron Miner", "Producer", "Mine and sell iron ore", "$500 cash + 40 iron"],
["Timber Mill", "Producer", "Harvest and sell timber", "$500 cash + 40 timber"],
["Oil Driller", "Producer", "Extract and sell crude oil", "$500 cash + 40 oil"],
["Bakery Chain", "Consumer", "Buy wheat + oil to bake bread (profit: $35/unit)", "$1,000 cash"],
["Tool Maker", "Consumer", "Buy iron + timber to make tools (profit: $50/unit)", "$1,000 cash"],
["Commodities Trader", "Trader", "Buy low, sell high across all commodities", "$800 cash + 5 of each"],
["Market Speculator", "Speculator", "Profit from price swings and market events", "$1,500 cash"],
],
col_widths=[1.2, 0.8, 2.8, 1.6]
)
doc.add_paragraph(
"These personas interact through four types of behavior:"
)
add_table(doc,
["Behavior", "What It Means", "Business Example"],
[
["Competition", "Trading on the open market (buy/sell orders)", "Bidding for wheat in an auction"],
["Cooperation", "Combining raw materials into valuable products", "A bakery buying wheat + oil to make bread"],
["Negotiation", "Making deals directly with another player", "Agreeing on a bulk discount with a supplier"],
["Coalition", "Forming alliances for collective bargaining", "Multiple buyers pooling orders for leverage"],
],
col_widths=[1.2, 2.6, 2.6]
)
# ── 2.2 Launch the Playground ──
doc.add_heading("2.2 How to Launch the Trading Playground", level=2)
doc.add_paragraph("To start the trading playground, run one command:")
add_code_block(doc,
"cd market-forge-openenv\n"
"python3 app_visual.py\n"
)
doc.add_paragraph(
"Then open your browser and go to: http://localhost:7860"
)
doc.add_paragraph(
"You will see the MarketForge dashboard with three tabs: Simulation, Manual Control, "
"and Environment Info."
)
# ── 2.3 Running a Simulation ──
doc.add_heading("2.3 Running an Automated Simulation", level=2)
doc.add_paragraph(
"In the Simulation tab, you can watch all 8 trading personas compete against each other. "
"The AI agents make decisions automatically based on their role-specific strategies."
)
doc.add_paragraph("How to run a simulation:")
doc.add_paragraph(
"1. Open the Simulation tab\n"
"2. Set the number of rounds using the slider (default: 30, range: 10-100)\n"
"3. Click the 'Run Simulation' button\n"
"4. Wait for the simulation to complete (usually 5-15 seconds)\n"
"5. Review the results: Awards, Leaderboard, Charts, Activity Log, Event Timeline"
)
doc.add_paragraph(
"After the simulation, you will see:"
)
doc.add_paragraph(
"- Awards: Who won the Market Champion (most wealth) and Master Strategist (best strategy)\n"
"- Leaderboard: All 8 agents ranked by wealth, with their strategic scores and accuracy\n"
"- Charts: 6 panels showing price trends, wealth race, rewards, activity, strategies, accuracy\n"
"- Activity Log: Recent trades, negotiations, and coalition formations\n"
"- Event Timeline: What market events happened each round (droughts, embargoes, surpluses)"
)
# ── 2.4 Manual Trading ──
doc.add_heading("2.4 Manual Trading β€” Make Your Own Decisions", level=2)
doc.add_paragraph(
"In the Manual Control tab, you can step into the shoes of any trading persona and "
"make decisions yourself. This is like playing a trading video game."
)
doc.add_paragraph("Step-by-step guide to manual trading:")
doc.add_paragraph(
"1. First, run a quick reset: go to the Simulation tab, set rounds to 10, click 'Run Simulation' "
"to initialize the market with some activity.\n\n"
"2. Switch to the Manual Control tab.\n\n"
"3. Select your agent from the dropdown (e.g., 'trader_1').\n\n"
"4. Choose an action type:"
)
add_table(doc,
["Action", "When to Use It", "What to Fill In"],
[
["buy", "You want to purchase a commodity", "Commodity, Price, Quantity"],
["sell", "You want to sell a commodity you hold", "Commodity, Price, Quantity"],
["produce", "You have raw materials and want to craft a product", "Commodity = compound good name (bread/tools/furniture)"],
["negotiate", "You want to propose a deal to another agent", "Commodity, Price, Qty, Target Agent, Message"],
["propose_coalition", "You want to form an alliance", "Message (describe your alliance purpose)"],
["pass", "You want to skip this turn", "Nothing"],
],
col_widths=[1.2, 2.3, 2.9]
)
doc.add_paragraph(
"5. Fill in the details (commodity, price, quantity).\n"
"6. Click 'Execute Action'.\n"
"7. Read the result: your reward, new cash balance, inventory, and the current market event."
)
doc.add_paragraph("Example manual trade:")
doc.add_paragraph(
"- Agent: trader_1\n"
"- Action: buy\n"
"- Commodity: wheat\n"
"- Price: 11.00\n"
"- Quantity: 5\n"
"- Click 'Execute Action'\n"
"- Result: Reward: 0.020, Cash: $793.50, Inventory: {wheat: 10, iron: 5, ...}"
)
# ── 2.5 Understanding Charts ──
doc.add_heading("2.5 Understanding the Dashboard Charts", level=2)
add_table(doc,
["Chart", "What to Look For", "Business Insight"],
[
["Commodity Prices", "Trends going up or down", "Which commodities are appreciating? Buy before a rise, sell before a drop."],
["Wealth Race", "Who is leading? Whose wealth is growing fastest?", "The agent with the boldest line is the Market Champion."],
["Average Reward", "Is the trend going up?", "Rising rewards mean agents are making better decisions each round."],
["Market Activity", "Trade volume, coalition count", "High activity = liquid market. Low activity = opportunities for negotiation."],
["Strategic Scores", "Gold bar = Master Strategist winner", "Higher scores mean better overall trading skill (not just wealth)."],
["Action Accuracy", "Green = good, Yellow = okay, Red = poor", "Shows what % of actions were valid. Higher = more reliable agent."],
],
col_widths=[1.3, 2.0, 3.1]
)
# ── 2.6 Awards ──
doc.add_heading("2.6 How Awards Work β€” Winning the Game", level=2)
doc.add_paragraph("MarketForge has two awards that determine who wins the game:")
doc.add_paragraph("")
p = doc.add_paragraph()
run = p.add_run("Award 1: Market Champion (The Wealthiest Player)")
run.font.bold = True
run.font.size = Pt(13)
run.font.color.rgb = RGBColor(0xDA, 0xA5, 0x20)
doc.add_paragraph(
"The agent with the highest total wealth at the end of the game wins. "
"Total wealth is calculated as:\n\n"
" Total Wealth = Cash + Market Value of Inventory + Value of Compound Goods\n\n"
"This award rewards agents that make profitable trades, accumulate valuable "
"inventory, and manage their cash wisely."
)
p = doc.add_paragraph()
run = p.add_run("Award 2: Master Strategist (The Smartest Player)")
run.font.bold = True
run.font.size = Pt(13)
run.font.color.rgb = RGBColor(0x4A, 0x9E, 0xD9)
doc.add_paragraph(
"The agent with the best composite strategic score wins. The score is calculated from "
"four components:\n\n"
" Strategic Score = 35% Trade Efficiency\n"
" + 25% Negotiation Mastery\n"
" + 20% Cooperation Index\n"
" + 20% Event Adaptability\n\n"
"This award values well-rounded strategy over pure wealth. An agent that "
"negotiates well, forms effective partnerships, and adapts to market events "
"can win this even without the most money."
)
# ── 2.7 Leaderboard ──
doc.add_heading("2.7 Reading the Leaderboard", level=2)
doc.add_paragraph(
"After every simulation, the Leaderboard ranks all 8 agents. Here's how to read it:"
)
add_table(doc,
["Column", "What It Means"],
[
["Rank", "Position (1 = best)"],
["Agent", "The agent's name"],
["Role", "Producer, Consumer, Trader, or Speculator"],
["Wealth", "Total value (cash + inventory + compounds) at game end"],
["Strategic", "Composite skill score (0 to 1, higher = better)"],
["Accuracy", "% of valid actions out of total actions taken"],
["Awards", "Which awards this agent won (Champion, Strategist, or none)"],
],
col_widths=[1.0, 5.4]
)
# ── 2.8 Strategy Tips ──
doc.add_heading("2.8 Trading Strategy Tips for Business Users", level=2)
doc.add_paragraph(
"Here are proven strategies that work well in the MarketForge simulation:"
)
strategies = [
("For Producers", "Sell your specialty commodity when prices are high. "
"Watch for demand surges (events that increase demand for your product). "
"Consider forming producer coalitions to stabilize prices."),
("For Consumers", "Buy raw materials when prices dip. Always check if you have "
"enough ingredients to produce compound goods β€” bread ($35), tools ($50), and "
"furniture ($45) are worth much more than raw materials."),
("For Traders", "Look for arbitrage: buy when the asking price is low, sell when "
"the bid price is high. React to events β€” buy commodities before a supply shock "
"drives prices up."),
("For Speculators", "Use event information aggressively. When you see 'Drought reduces "
"wheat supply by 30%', buy wheat immediately β€” the price will rise. "
"Sell before oversupply events."),
("For Everyone", "Negotiate deals for better prices than the open market. "
"Form coalitions for bulk buying power. Keep your reputation high by honoring "
"deals β€” broken contracts hurt your score."),
]
for title, body in strategies:
p = doc.add_paragraph()
run = p.add_run(f"{title}: ")
run.font.bold = True
run = p.add_run(body)
doc.add_page_break()
# ══════════════════════════════════════════════════════════════
# PART 3: DEVELOPER GUIDE
# ══════════════════════════════════════════════════════════════
doc.add_heading("Part 3: Developer Guide β€” Building and Training Models", level=1)
doc.add_paragraph(
"This section is for technical developers who want to build custom agents, "
"train LLM models, and extend the MarketForge environment."
)
# ── 3.1 Architecture Overview ──
doc.add_heading("3.1 Architecture Overview for Developers", level=2)
add_code_block(doc,
" MarketForge Architecture\n"
" ========================\n"
"\n"
" [Your Agent] [MarketForge Server]\n"
" ============ ====================\n"
" |\n"
" LLM / Script / Bot MarketEnvironment\n"
" | |\n"
" | MarketAction (JSON) +------+------+\n"
" +------------> HTTP POST /step -------> | Order Books |\n"
" | | Trade Match |\n"
" | MarketObservation (JSON) | Coalitions |\n"
" <------------ HTTP Response <----------- | Awards |\n"
" | | Scoring |\n"
" v +-------------+\n"
" [Decide Next Action] |\n"
" | [Gradio Dashboard]\n"
" v port 7860\n"
" [GRPO Training]\n"
" (reward -> optimize policy)\n"
)
# ── 3.2 Connecting via HTTP API ──
doc.add_heading("3.2 Connecting to the Environment via HTTP API", level=2)
doc.add_paragraph("Developers can connect to MarketForge from any language using the HTTP API.")
doc.add_paragraph("Python example:")
add_code_block(doc,
"import requests\n"
"import json\n"
"\n"
"BASE_URL = 'http://localhost:8000'\n"
"\n"
"# Reset the game\n"
"resp = requests.post(f'{BASE_URL}/reset', json={'max_rounds': 30})\n"
"obs = resp.json()\n"
"print(f'Game started. Agent: {obs[\"agent_id\"]}, Cash: {obs[\"cash\"]}')\n"
"print(f'Event: {obs[\"event\"]}')\n"
"\n"
"# Game loop\n"
"done = False\n"
"while not done:\n"
" action = {\n"
" 'agent_id': 'trader_1',\n"
" 'action_type': 'buy',\n"
" 'commodity': 'wheat',\n"
" 'price': 12.0,\n"
" 'quantity': 5,\n"
" }\n"
" resp = requests.post(f'{BASE_URL}/step', json=action)\n"
" obs = resp.json()\n"
" done = obs.get('done', False)\n"
" print(f'Round {obs[\"round_number\"]}: reward={obs[\"reward\"]:.3f}, wealth={obs[\"total_wealth\"]:.0f}')\n"
"\n"
"# Check final awards\n"
"awards = obs.get('market_summary', {}).get('awards', {})\n"
"print(f'Champion: {awards[\"market_champion\"][\"agent_id\"]}')\n"
"print(f'Strategist: {awards[\"master_strategist\"][\"agent_id\"]}')\n"
)
doc.add_paragraph("cURL example (works from any terminal):")
add_code_block(doc,
"# Reset\n"
"curl -s -X POST http://localhost:8000/reset \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"max_rounds\": 10}' | python3 -m json.tool\n"
"\n"
"# Buy wheat\n"
"curl -s -X POST http://localhost:8000/step \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"agent_id\":\"trader_1\",\"action_type\":\"buy\",\"commodity\":\"wheat\",\"price\":12,\"quantity\":5}' \\\n"
" | python3 -m json.tool\n"
"\n"
"# Negotiate a deal\n"
"curl -s -X POST http://localhost:8000/step \\\n"
" -H 'Content-Type: application/json' \\\n"
" -d '{\"agent_id\":\"consumer_1\",\"action_type\":\"negotiate\",\"target_agent\":\"producer_wheat\",\\\n"
" \"commodity\":\"wheat\",\"price\":9,\"quantity\":10,\\\n"
" \"message\":\"Need 10 wheat for bread production. Can we do $9/unit?\"}' \\\n"
" | python3 -m json.tool\n"
)
# ── 3.3 Building a Custom Agent ──
doc.add_heading("3.3 Building a Custom Agent (Python)", level=2)
doc.add_paragraph(
"Here is a complete, runnable script for a custom trading agent that connects "
"to the MarketForge server and plays a full game:"
)
add_code_block(doc,
"#!/usr/bin/env python3\n"
"\"\"\"custom_agent.py - A smart trading agent for MarketForge.\"\"\"\n"
"from client import MarketForgeEnv\n"
"from models import MarketAction\n"
"\n"
"class SmartTrader:\n"
" def __init__(self, agent_id='trader_1'):\n"
" self.agent_id = agent_id\n"
" self.buy_history = []\n"
"\n"
" def decide(self, obs):\n"
" \"\"\"Make a trading decision based on observation.\"\"\"\n"
" event = obs.event.lower()\n"
"\n"
" # Strategy 1: React to supply shocks\n"
" for c in ['wheat', 'iron', 'timber', 'oil']:\n"
" if c in event and ('reduce' in event or 'embargo' in event):\n"
" price = obs.top_of_book.get(c, {}).get('best_ask', 15)\n"
" if obs.cash > price * 5:\n"
" return MarketAction(\n"
" agent_id=self.agent_id, action_type='buy',\n"
" commodity=c, price=round(price * 1.05, 1), quantity=5)\n"
"\n"
" # Strategy 2: Sell expensive holdings\n"
" for c, qty in obs.inventory.items():\n"
" if qty > 5:\n"
" price = obs.top_of_book.get(c, {}).get('best_bid', 10)\n"
" if price > 0:\n"
" return MarketAction(\n"
" agent_id=self.agent_id, action_type='sell',\n"
" commodity=c, price=round(price * 1.02, 1),\n"
" quantity=min(qty - 2, 5))\n"
"\n"
" # Strategy 3: Negotiate a deal\n"
" return MarketAction(\n"
" agent_id=self.agent_id, action_type='negotiate',\n"
" target_agent='producer_wheat', commodity='wheat',\n"
" price=9.0, quantity=10,\n"
" message='Looking for bulk wheat. Can you do $9/unit for 10?')\n"
"\n"
"\n"
"# Run the agent\n"
"env = MarketForgeEnv(base_url='http://localhost:8000')\n"
"agent = SmartTrader('trader_1')\n"
"\n"
"result = env.reset(max_rounds=30)\n"
"print(f'Game started: {result.observation.event}')\n"
"\n"
"while not result.done:\n"
" action = agent.decide(result.observation)\n"
" result = env.step(action)\n"
" print(f'R{result.observation.round_number}: '\n"
" f'{action.action_type} {action.commodity} '\n"
" f'-> reward={result.reward:.3f}, '\n"
" f'wealth=${result.observation.total_wealth:.0f}')\n"
"\n"
"awards = result.observation.market_summary.get('awards', {})\n"
"print(f'\\nChampion: {awards[\"market_champion\"][\"agent_id\"]}')\n"
"print(f'Strategist: {awards[\"master_strategist\"][\"agent_id\"]}')\n"
)
doc.add_paragraph("Run this agent:")
add_code_block(doc,
"# First start the server in one terminal:\n"
"uvicorn server.app:app --host 0.0.0.0 --port 8000\n"
"\n"
"# Then run the agent in another terminal:\n"
"python3 custom_agent.py\n"
)
# ── 3.4 Training with GRPO ──
doc.add_heading("3.4 Training an LLM Agent with GRPO", level=2)
doc.add_paragraph(
"GRPO (Group Relative Policy Optimization) trains an LLM to produce good trading "
"actions by comparing multiple generated actions and reinforcing the best ones."
)
doc.add_paragraph("Training prerequisites:")
add_code_block(doc,
"# Install training dependencies (requires GPU)\n"
"pip install trl transformers datasets accelerate\n"
"\n"
"# Recommended: Google Colab with T4 or A100 GPU\n"
"# Model: Qwen/Qwen2.5-0.5B-Instruct (small, fast)\n"
"# For better results: Qwen/Qwen2.5-1.5B-Instruct\n"
)
doc.add_paragraph("How training works (step by step):")
doc.add_paragraph(
"1. Dataset Generation: 256 diverse market scenarios are created by calling env.reset() "
"256 times, each producing a unique market observation (different events, prices, inventories).\n\n"
"2. Prompt Construction: Each scenario is wrapped with a system prompt that teaches the "
"LLM how to format actions as JSON and provides strategic guidelines.\n\n"
"3. Rollout: For each prompt, the LLM generates an action -> the environment processes it -> "
"a reward is computed -> the observation is fed back. This repeats for 6 turns per episode.\n\n"
"4. env_mask: Environment-generated text (observations, rewards) is masked from the training "
"loss. Only the LLM's own action tokens are optimized. This prevents the model from trying "
"to replicate environment output.\n\n"
"5. Reward Collection: Three reward signals are combined β€” environment reward (40%), "
"format accuracy (30%), and strategic depth (30%).\n\n"
"6. Policy Update: GRPO generates multiple action candidates per prompt, computes the "
"relative advantage of each, and updates the model to favor better actions."
)
doc.add_paragraph("Run training:")
add_code_block(doc,
"cd market-forge-openenv\n"
"python3 train_market_forge.py\n"
"\n"
"# Training output:\n"
"# Starting MarketForge agent training with GRPO...\n"
"# Model: Qwen/Qwen2.5-0.5B-Instruct\n"
"# Dataset size: 256\n"
"# Output: market-forge-agent\n"
"# [Step 1/...] loss=... reward=...\n"
"# Training complete! Model saved to ./market-forge-agent\n"
)
doc.add_paragraph("Key training hyperparameters:")
add_table(doc,
["Parameter", "Value", "What It Controls"],
[
["learning_rate", "1e-6", "How fast the model learns (lower = more stable)"],
["num_generations", "2", "How many action candidates to compare per prompt"],
["max_completion_length", "1024", "Maximum tokens per LLM response"],
["gradient_accumulation_steps", "16", "Effective batch size (memory optimization)"],
["max_turns", "6", "Actions per episode (multi-turn)"],
["vllm_gpu_memory_utilization", "0.15", "GPU memory for fast inference"],
],
col_widths=[2.2, 0.8, 3.4]
)
# ── 3.5 Reward Function Design ──
doc.add_heading("3.5 Reward Function Design", level=2)
doc.add_paragraph(
"The reward pipeline uses a mixed-motive payoff function that balances self-interest "
"and collective benefit:"
)
add_code_block(doc,
"# Mixed-motive payoff formula:\n"
"u_i(action) = alpha_i * v_self(action) + (1 - alpha_i) * v_collective(action)\n"
"\n"
"# alpha_i is the self-interest parameter per role:\n"
"# Producer: 0.7 (mostly self-interested)\n"
"# Consumer: 0.5 (balanced)\n"
"# Trader: 0.8 (profit-focused)\n"
"# Speculator: 0.9 (highly self-interested)\n"
)
doc.add_paragraph(
"To create a custom reward function, follow this signature:"
)
add_code_block(doc,
"def my_custom_reward(completions, **kwargs) -> list[float]:\n"
" \"\"\"Custom reward function.\n"
" \n"
" Args:\n"
" completions: List of LLM output texts\n"
" **kwargs: Extra context (env_reward, wealth_change, etc.)\n"
" \n"
" Returns:\n"
" List of float rewards (one per completion)\n"
" \"\"\"\n"
" rewards = []\n"
" for c in completions:\n"
" text = c[0]['content'] if isinstance(c, list) else str(c)\n"
" # Your reward logic here\n"
" rewards.append(0.0)\n"
" return rewards\n"
)
# ── 3.6 env_mask ──
doc.add_heading("3.6 Multi-Turn Rollouts and env_mask", level=2)
doc.add_paragraph(
"The env_mask is a critical mechanism for multi-turn GRPO training. It marks which "
"tokens were generated by the model (mask=1) versus which were generated by the "
"environment (mask=0). Only model tokens are included in the training loss."
)
add_code_block(doc,
"# Token stream during a 2-turn episode:\n"
"#\n"
"# [System Prompt] [Observation 1] <- env tokens (mask=0)\n"
"# [Agent Action 1] <- model tokens (mask=1)\n"
"# [Reward: 0.4. Observation 2] <- env tokens (mask=0)\n"
"# [Agent Action 2] <- model tokens (mask=1)\n"
"# [Reward: 0.6. Game Over.] <- env tokens (mask=0)\n"
"#\n"
"# Only the model's actions (mask=1) are optimized.\n"
"# The environment's feedback provides context but doesn't affect the loss.\n"
)
# ── 3.7 Deploying to HuggingFace Spaces ──
doc.add_heading("3.7 Deploying to HuggingFace Spaces", level=2)
add_code_block(doc,
"# Step 1: Install HuggingFace Hub\n"
"pip install huggingface_hub\n"
"huggingface-cli login\n"
"\n"
"# Step 2: Create and upload to a Space\n"
"python3 -c \"\n"
"from huggingface_hub import HfApi\n"
"api = HfApi()\n"
"\n"
"api.create_repo(\n"
" repo_id='YOUR-USERNAME/market-forge-env',\n"
" repo_type='space',\n"
" space_sdk='docker',\n"
" exist_ok=True,\n"
")\n"
"\n"
"api.upload_folder(\n"
" folder_path='.',\n"
" repo_id='YOUR-USERNAME/market-forge-env',\n"
" repo_type='space',\n"
")\n"
"print('Deployed! Visit: https://huggingface.co/spaces/YOUR-USERNAME/market-forge-env')\n"
"\"\n"
)
# ── 3.8 Evaluation ──
doc.add_heading("3.8 Evaluation and Benchmarking", level=2)
doc.add_paragraph("Run the evaluation script to compare agent baselines:")
add_code_block(doc,
"cd market-forge-openenv\n"
"python3 train_market_forge_notebook.py\n"
"\n"
"# This runs 200 episodes for each baseline:\n"
"# [1/3] Random baseline... (avg: ~0.15)\n"
"# [2/3] Heuristic baseline... (avg: ~1.50)\n"
"# [3/3] Strategic baseline... (avg: ~2.10)\n"
"#\n"
"# Generates training_results.png with 4 charts:\n"
"# - Reward distribution comparison\n"
"# - GRPO training curve\n"
"# - Action distribution (before vs after training)\n"
"# - Cumulative reward over episodes\n"
)
doc.add_paragraph("Key metrics to track during evaluation:")
add_table(doc,
["Metric", "What It Measures", "Target"],
[
["Format Accuracy", "% of responses that are valid JSON with correct action_type", ">95%"],
["Environment Reward", "Average reward per step from the environment", ">0.3"],
["Strategic Score", "Composite: trade efficiency + negotiation + cooperation + adaptability", ">0.6"],
["Wealth Growth", "Final wealth vs starting wealth", ">20%"],
["Action Diversity", "Number of unique action types used", ">=4"],
],
col_widths=[1.5, 3.0, 0.9]
)
# ── 3.9 Extending the Environment ──
doc.add_heading("3.9 Extending the Environment", level=2)
doc.add_paragraph("Here are ways to extend MarketForge for your specific use case:")
extensions = [
("Add new commodities", "Add to COMMODITIES list and BASE_PRICES dict in market_environment.py"),
("Add new compound goods", "Add recipe to COMPOUND_GOODS and price to COMPOUND_PRICES"),
("Add new agent roles", "Add to AGENT_ROLES dict with role, specialty, cash, inventory, alpha"),
("Add new market events", "Add to EVENTS list with text, effects dict, and severity"),
("Change game duration", "Pass max_rounds parameter to reset() (default: 50)"),
("Add new action types", "1. Add handler method in MarketEnvironment\n"
"2. Add to action_handlers dict in step()\n"
"3. Add to MarketAction dataclass\n"
"4. Update legal_actions in _make_observation()"),
("Custom reward function", "Create function with signature: def my_reward(completions, **kwargs) -> list[float]\n"
"Add to reward_funcs list in GRPOTrainer constructor"),
("Custom scoring formula", "Modify _compute_strategic_score() in market_environment.py\n"
"Adjust the 4 component weights (must sum to 1.0)"),
]
for title, body in extensions:
p = doc.add_paragraph()
run = p.add_run(f"{title}: ")
run.font.bold = True
run = p.add_run(body)
# ══════════════════════════════════════════════════════════════
# APPENDIX
# ══════════════════════════════════════════════════════════════
doc.add_page_break()
doc.add_heading("Appendix: Quick Reference", level=1)
doc.add_heading("All Action Types", level=2)
add_table(doc,
["Action", "Pillar", "Required Fields", "Reward Range"],
[
["buy", "Competition", "commodity, price, quantity", "+0.02 to +0.40"],
["sell", "Competition", "commodity, price, quantity", "+0.02 to +0.40"],
["produce", "Cooperation", "compound_good", "+0.50 to +0.80"],
["negotiate", "Negotiation", "target_agent, commodity, price, quantity, message", "+0.05"],
["accept_deal", "Negotiation", "coalition_id (as deal_id)", "+0.40 to +0.60"],
["reject_deal", "Negotiation", "coalition_id (as deal_id)", "0.00"],
["propose_coalition", "Coalition", "message", "+0.05"],
["join_coalition", "Coalition", "coalition_id", "+0.10 to +0.30"],
["leave_coalition", "Coalition", "coalition_id", "-0.08"],
["pass", "None", "none", "-0.01"],
],
col_widths=[1.4, 1.0, 2.5, 1.5]
)
doc.add_heading("All Commodities and Recipes", level=2)
add_table(doc,
["Item", "Type", "Base Price", "Recipe / Source"],
[
["Wheat", "Raw", "$10", "Produced by producer_wheat (regenerates each round)"],
["Iron", "Raw", "$15", "Produced by producer_iron"],
["Timber", "Raw", "$12", "Produced by producer_timber"],
["Oil", "Raw", "$18", "Produced by producer_oil"],
["Bread", "Compound", "$35", "2 Wheat + 1 Oil (profit margin: ~47%)"],
["Tools", "Compound", "$50", "2 Iron + 1 Timber (profit margin: ~52%)"],
["Furniture", "Compound", "$45", "2 Timber + 1 Oil (profit margin: ~53%)"],
],
col_widths=[1.0, 1.0, 0.8, 3.6]
)
doc.add_heading("All Market Events", level=2)
add_table(doc,
["Event", "Affected Commodity", "Effect"],
[
["Drought reduces wheat supply by 30%", "Wheat", "Supply -30% (price rises)"],
["Iron mine collapse", "Iron", "Supply -50% (price rises sharply)"],
["Timber surplus", "Timber", "Supply +40% (price drops)"],
["Oil embargo", "Oil", "Supply -60% (price rises sharply)"],
["Trade festival", "All", "Transaction costs waived"],
["Furniture demand surge +50%", "Timber, Oil", "Demand increases (prices rise)"],
["New trade route opens", "All", "Supply +10% (prices drop slightly)"],
["Calm markets", "None", "No effect"],
["Speculator rumor: oil to skyrocket", "None", "Information only (may cause speculation)"],
["Government subsidy for tools", "Iron, Timber", "Supply +10% (prices drop)"],
["Storm damages warehouses", "Wheat, Timber", "Supply -20% (prices rise)"],
["International trade deal signed", "All", "Supply -5% (demand up, prices stable)"],
],
col_widths=[2.5, 1.5, 2.4]
)
doc.add_heading("Startup Commands Cheat Sheet", level=2)
add_table(doc,
["Task", "Command"],
[
["Install dependencies", "pip install fastapi uvicorn gradio matplotlib numpy requests"],
["Start API server", "uvicorn server.app:app --host 0.0.0.0 --port 8000"],
["Start dashboard", "python3 app_visual.py"],
["Run all tests", "python3 (paste verification script from Section 1.11)"],
["Run evaluation", "python3 train_market_forge_notebook.py"],
["Train with GRPO", "python3 train_market_forge.py"],
["Build Docker", "docker build -t marketforge ."],
["Run Docker", "docker run -p 7860:7860 -p 8000:8000 marketforge"],
["Deploy to HF Spaces", "See Section 3.7"],
],
col_widths=[1.5, 4.9]
)
# ══════════════════════════════════════════════════════════════
# SAVE
# ══════════════════════════════════════════════════════════════
output_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"MarketForge_Complete_Guide.docx")
doc.save(output_path)
print(f"\nWord document saved to: {output_path}")
print(f"Total sections: 3 Parts + Appendix")