Database Explorer - Design & Implementation
Overview
The Database Explorer is a read-only web-based interface for browsing and querying raw data from PQAP's SQLite databases. It provides admin users with direct access to underlying data for debugging, analysis, and validation.
Architecture
Components
- DatabaseExplorer (
src/admin/database_explorer.py) - Core backend logic
- Database connection management
- Query execution and validation
- CSV export functionality
-
Security enforcement (read-only mode)
-
Template (
src/admin/templates/database.html) - Web UI for database exploration
- Table browsing interface
- Search/filter controls
- Pagination
-
SQL query editor
-
Integration (
src/admin/api.py) - Routes registered in AdminAPI
- Accessible at
/database
Features
1. Database Selection
Two databases are available: - pqap_dev.db - Trading state (trades, signals, positions, metrics) - market_history.db - Market data (snapshots, orderbooks, price history)
Users can switch between databases using a dropdown selector.
2. Table Browsing
Tables in pqap_dev.db:
- trades - Trade execution history (18,911 rows)
- signals - Signal generation history (47,635 rows)
- strategy_state - Strategy runtime state (7 rows)
- position_snapshots - Position snapshots for equity curves
- daily_metrics - Daily performance summaries
- risk_events - Risk limit breaches
- strategy_configs - Strategy configuration history
- performance_snapshots - Rolling performance metrics
Tables in market_history.db:
- market_snapshots - Market price snapshots (20,550 rows)
- market_resolutions - Market resolution results
- orderbook_snapshots - Orderbook depth snapshots
- price_history - Historical price data
- collection_stats - Data collection statistics
Each table card displays: - Table name - Row count - Clickable to view data
3. Data Viewing
When a table is selected: - Schema Display - Shows CREATE TABLE statement - Column Headers - All columns from the table - Data Grid - Paginated rows (default 100 rows per page) - JSON Detection - JSON columns are highlighted and clickable to view formatted - Pagination Controls - Previous/Next navigation
4. Search & Filter
Search functionality supports two modes:
- Column-specific: column_name:search_value
- Global: Searches across all text columns
Example searches:
- strategy_id:dual_arb_v1 - Find trades for specific strategy
- market_id:0x123 - Find data for specific market
- executed - Global search for "executed" in any column
5. CSV Export
- Export button on every table view
- Downloads filtered data as CSV
- Filename format:
{table_name}_{database}.csv - Respects current search filters
- No row limit (exports all matching rows)
6. Advanced SQL Query
Custom SQL query editor with: - Read-only enforcement - Only SELECT queries allowed - Security validation - Blocks DROP, DELETE, UPDATE, INSERT, ALTER, CREATE - Syntax highlighting - Monospace font for readability - Direct execution - Results displayed inline
Example queries:
SELECT strategy_id, COUNT(*) as trade_count, SUM(CAST(pnl AS REAL)) as total_pnl
FROM trades
WHERE DATE(executed_at) = DATE('2024-01-15')
GROUP BY strategy_id
ORDER BY total_pnl DESC;
Security
Read-Only Mode
- SQLite PRAGMA -
PRAGMA query_only = ONset on all connections - Query Validation - Only SELECT statements allowed
- Keyword Filtering - Dangerous operations blocked
- No Write Access - Physical impossibility of data modification
Admin-Only Access
- Deployed as part of Admin API (port 8080)
- Same authentication as other admin endpoints
- Not exposed to public internet
Audit Trail
All queries are logged via Python logging: - Database accessed - Table queried - Search parameters - Error messages
Usage Examples
Debugging Trade Execution
- Navigate to
/database - Select "Trading Database (pqap_dev.db)"
- Click "trades" table
- Search for
strategy_id:dual_arb_v1 - Review recent trades for that strategy
Analyzing Signal Generation
- Select "signals" table
- Search for
executed:0to find rejected signals - Review
rejected_reasoncolumn - Export to CSV for further analysis
Market Data Analysis
- Switch to "Market History (market_history.db)"
- Select "market_snapshots" table
- Use custom SQL:
sql SELECT question, yes_price, no_price, (yes_price + no_price) as sum FROM market_snapshots WHERE ABS((yes_price + no_price) - 1.0) > 0.05 ORDER BY snapshot_time DESC LIMIT 100; - Identify arbitrage opportunities
Performance Investigation
- Select "daily_metrics" table
- Search for specific date range
- Review realized_pnl, win_rate, sharpe_ratio
- Export for charting in external tools
Technical Implementation
Database Connection
def _get_connection(self, db_name: str) -> sqlite3.Connection:
"""Get database connection."""
db_path = self._get_db_path(db_name)
conn = sqlite3.connect(str(db_path))
conn.row_factory = sqlite3.Row # Dict-like access
conn.execute("PRAGMA query_only = ON") # Read-only mode
return conn
Query Building
Search queries are built dynamically:
query = f"SELECT * FROM {table_name}"
if ":" in search:
col, val = search.split(":", 1)
query += f" WHERE {col} LIKE ?"
params.append(f"%{val}%")
else:
# Search all columns
conditions = [f"{col} LIKE ?" for col in columns]
query += " WHERE " + " OR ".join(conditions)
Template Filters
Custom Jinja2 filters:
- is_json - Detect JSON strings for highlighting
- number_format - Format numbers with thousand separators
Routes
| Route | Method | Description |
|---|---|---|
/database |
GET | Main explorer page |
/database/export |
GET | Export table to CSV |
/database/query |
POST | Execute custom SQL query |
Future Enhancements
Potential Improvements
- Query History - Save recent queries for reuse
- Bookmarks - Save favorite queries
- Visualizations - Inline charts for numeric data
- Cross-Database Joins - Query across both databases
- Export Formats - JSON, Excel support
- Real-time Updates - Auto-refresh for monitoring
- Query Templates - Pre-built queries for common tasks
- Column Filtering - Show/hide specific columns
- Advanced Filters - Date pickers, numeric ranges
- Download Schema - Export CREATE TABLE statements
Performance Optimizations
- Result Caching - Cache frequently accessed queries
- Lazy Loading - Load data as user scrolls
- Index Hints - Suggest indexes for slow queries
- Query Explanation - Show EXPLAIN QUERY PLAN
Monitoring & Observability
The database explorer is monitored through: - Request logs (access patterns) - Error logs (failed queries) - Performance metrics (query execution time) - Usage statistics (most accessed tables)
Best Practices
For Users
- Start with table browsing before writing custom SQL
- Use search filters to narrow results before exporting
- Export large datasets instead of paginating through UI
- Test queries on small date ranges before expanding
- Use column-specific search for better performance
For Developers
- Never disable read-only mode in production
- Validate all user input before query execution
- Log all queries for audit trail
- Test with large datasets to ensure pagination works
- Handle edge cases (empty tables, malformed JSON)
Testing
Manual Testing Checklist
- [ ] Table listing displays correctly
- [ ] Row counts are accurate
- [ ] Schema display works
- [ ] Basic pagination functions
- [ ] Search filters work (column-specific and global)
- [ ] CSV export downloads
- [ ] Custom SQL executes
- [ ] Dangerous SQL is blocked
- [ ] JSON cells are clickable
- [ ] Navigation between databases works
Automated Tests
Located in tests/unit/test_database_explorer.py (to be created):
- Database connection tests
- Query validation tests
- Security enforcement tests
- CSV export tests
- Template rendering tests
Deployment
The Database Explorer is automatically deployed when the Admin API starts:
# In src/admin/api.py
self._database_explorer = DatabaseExplorer(base_path=".")
self._database_explorer.setup_routes(self._app)
No additional configuration required. Access at:
http://localhost:8080/database
Conclusion
The Database Explorer provides essential visibility into PQAP's data layer, enabling: - Debugging - Trace issues through raw data - Analysis - Validate strategy performance - Auditing - Review system behavior - Development - Understand data structures
It maintains security through read-only enforcement while providing powerful querying capabilities.