Console View
The console view provides a real-time window into the StableFX trading engine. It streams log entries showing trade execution, pool rebalancing, agent activity, and system events. This page covers the useForexSimulation hook, the forexSimulationEngine service, and the types used for log streaming.
Core Types
ForexLogType
import type { ForexLogType } from '@one_deploy/sdk';
type ForexLogType =
| 'TRADE_SUBMITTED'
| 'TRADE_QUOTED'
| 'TRADE_MATCHED'
| 'TRADE_SETTLED'
| 'TRADE_FAILED'
| 'POOL_REBALANCE'
| 'POOL_DEPOSIT'
| 'POOL_WITHDRAWAL'
| 'AGENT_ACTION'
| 'SYSTEM_INFO'
| 'SYSTEM_WARNING'
| 'SYSTEM_ERROR';
ForexLogEntry
import type { ForexLogEntry } from '@one_deploy/sdk';
interface ForexLogEntry {
/** Unique log entry identifier. */
id: string;
/** Log category. */
type: ForexLogType;
/** Human-readable log message. */
message: string;
/** ISO-8601 timestamp. */
timestamp: string;
/** Structured data associated with this log entry. */
metadata: Record<string, unknown>;
/** The agent that produced this log, if applicable. */
agentId?: string;
/** Related trade ID, if this log is trade-related. */
tradeId?: string;
/** Related pool ID, if this log is pool-related. */
poolId?: string;
/** Severity level for display. */
severity: 'info' | 'warning' | 'error' | 'success';
}
ForexAgent
import type { ForexAgent } from '@one_deploy/sdk';
interface ForexAgent {
/** Unique agent identifier. */
id: string;
/** Human-readable agent name. */
name: string;
/** Agent role in the trading system. */
role: 'market_maker' | 'hedger' | 'arbitrageur' | 'risk_manager';
/** Current agent status. */
status: 'active' | 'idle' | 'error';
/** Currency pairs this agent is assigned to. */
assignedPairs: string[];
/** Number of trades executed by this agent. */
tradeCount: number;
/** ISO-8601 timestamp of last activity. */
lastActiveAt: string;
}
useForexSimulation Hook
The useForexSimulation hook connects to the forexSimulationEngine and provides a reactive stream of log entries and agent state.
Hook Signature
import { useForexSimulation } from '@one_deploy/sdk';
function useForexSimulation(): UseForexSimulationResult;
UseForexSimulationResult
interface UseForexSimulationResult {
/** Live stream of log entries, newest first. */
logs: ForexLogEntry[];
/** All active agents in the simulation. */
agents: ForexAgent[];
/** Whether the simulation is currently running. */
isRunning: boolean;
/** Whether the initial connection is being established. */
isConnecting: boolean;
/** Error object if the connection or simulation failed. */
error: Error | null;
/** Start the simulation log stream. */
start: () => void;
/** Stop the simulation log stream. */
stop: () => void;
/** Clear all accumulated log entries. */
clearLogs: () => void;
/** Filter logs by type. */
filterByType: (types: ForexLogType[]) => ForexLogEntry[];
/** Filter logs by severity. */
filterBySeverity: (severity: ForexLogEntry['severity']) => ForexLogEntry[];
}
forexSimulationEngine Service
For non-React contexts or when you need lower-level control, use the forexSimulationEngine service directly.
import { forexSimulationEngine } from '@one_deploy/sdk';
// Subscribe to log entries
const unsubscribe = forexSimulationEngine.onLog((entry: ForexLogEntry) => {
console.log(`[${entry.type}] ${entry.message}`);
});
// Start the simulation
forexSimulationEngine.start();
// Get current agents
const agents = forexSimulationEngine.getAgents();
// Stop and clean up
forexSimulationEngine.stop();
unsubscribe();
Service API
interface ForexSimulationEngine {
/** Start the simulation log stream. */
start(): void;
/** Stop the simulation log stream. */
stop(): void;
/** Whether the simulation is currently running. */
isRunning(): boolean;
/** Subscribe to new log entries. Returns an unsubscribe function. */
onLog(callback: (entry: ForexLogEntry) => void): () => void;
/** Get all currently active agents. */
getAgents(): ForexAgent[];
/** Subscribe to agent state changes. Returns an unsubscribe function. */
onAgentUpdate(callback: (agent: ForexAgent) => void): () => void;
/** Get all accumulated log entries. */
getLogs(): ForexLogEntry[];
/** Clear all accumulated log entries. */
clearLogs(): void;
}
Building a Console View
Basic Log Stream
import React from 'react';
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
import { useForexSimulation } from '@one_deploy/sdk';
import type { ForexLogEntry } from '@one_deploy/sdk';
function ForexConsole() {
const { logs, isRunning, isConnecting, start, stop, clearLogs } =
useForexSimulation();
return (
<View style={styles.container}>
<View style={styles.toolbar}>
<Text style={styles.title}>StableFX Console</Text>
<View style={styles.controls}>
<TouchableOpacity
onPress={isRunning ? stop : start}
style={[styles.btn, isRunning ? styles.btnStop : styles.btnStart]}
>
<Text style={styles.btnText}>
{isConnecting ? 'Connecting...' : isRunning ? 'Stop' : 'Start'}
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={clearLogs} style={styles.btn}>
<Text style={styles.btnText}>Clear</Text>
</TouchableOpacity>
</View>
</View>
<FlatList
data={logs}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <LogRow entry={item} />}
inverted
style={styles.logList}
/>
</View>
);
}
function LogRow({ entry }: { entry: ForexLogEntry }) {
const color = getSeverityColor(entry.severity);
const time = new Date(entry.timestamp).toLocaleTimeString();
return (
<View style={styles.logRow}>
<Text style={[styles.logTime, { color }]}>{time}</Text>
<Text style={[styles.logType, { color }]}>[{entry.type}]</Text>
<Text style={styles.logMessage} numberOfLines={2}>
{entry.message}
</Text>
</View>
);
}
function getSeverityColor(severity: ForexLogEntry['severity']): string {
switch (severity) {
case 'info': return '#4488ff';
case 'warning': return '#ffaa44';
case 'error': return '#cc4444';
case 'success': return '#44cc88';
default: return '#888888';
}
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0a0a14' },
toolbar: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 12,
borderBottomWidth: 1,
borderColor: '#1a1a2e',
},
title: { fontSize: 16, fontWeight: '700', color: '#fff' },
controls: { flexDirection: 'row', gap: 8 },
btn: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 6,
backgroundColor: '#2a2a4e',
},
btnStart: { backgroundColor: '#1a3a2a' },
btnStop: { backgroundColor: '#3a1a1a' },
btnText: { color: '#fff', fontSize: 13, fontWeight: '600' },
logList: { flex: 1, paddingHorizontal: 12 },
logRow: {
flexDirection: 'row',
alignItems: 'flex-start',
paddingVertical: 4,
gap: 8,
},
logTime: { fontSize: 11, fontFamily: 'monospace', width: 75 },
logType: { fontSize: 11, fontFamily: 'monospace', width: 130 },
logMessage: { fontSize: 12, color: '#ccc', flex: 1 },
});
Console with Filtering and Agent Panel
import React, { useState } from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet } from 'react-native';
import { useForexSimulation } from '@one_deploy/sdk';
import type { ForexLogType, ForexLogEntry, ForexAgent } from '@one_deploy/sdk';
const LOG_FILTERS: { label: string; types: ForexLogType[] }[] = [
{ label: 'All', types: [] },
{ label: 'Trades', types: ['TRADE_SUBMITTED', 'TRADE_QUOTED', 'TRADE_MATCHED', 'TRADE_SETTLED', 'TRADE_FAILED'] },
{ label: 'Pools', types: ['POOL_REBALANCE', 'POOL_DEPOSIT', 'POOL_WITHDRAWAL'] },
{ label: 'Agents', types: ['AGENT_ACTION'] },
{ label: 'System', types: ['SYSTEM_INFO', 'SYSTEM_WARNING', 'SYSTEM_ERROR'] },
];
function AdvancedConsole() {
const { logs, agents, isRunning, start, stop, clearLogs, filterByType } =
useForexSimulation();
const [activeFilter, setActiveFilter] = useState(0);
const filteredLogs =
activeFilter === 0
? logs
: filterByType(LOG_FILTERS[activeFilter].types);
return (
<View style={styles.container}>
{/* Agent Status Bar */}
<View style={styles.agentBar}>
{agents.map((agent) => (
<AgentBadge key={agent.id} agent={agent} />
))}
</View>
{/* Filter Tabs */}
<View style={styles.filterRow}>
{LOG_FILTERS.map((filter, idx) => (
<TouchableOpacity
key={filter.label}
onPress={() => setActiveFilter(idx)}
style={[
styles.filterTab,
activeFilter === idx && styles.filterTabActive,
]}
>
<Text
style={[
styles.filterText,
activeFilter === idx && styles.filterTextActive,
]}
>
{filter.label}
</Text>
</TouchableOpacity>
))}
</View>
{/* Log Stream */}
<FlatList
data={filteredLogs}
keyExtractor={(item) => item.id}
renderItem={({ item }) => <LogRow entry={item} />}
inverted
style={{ flex: 1 }}
/>
{/* Controls */}
<View style={styles.footer}>
<Text style={styles.footerText}>
{filteredLogs.length} entries | {isRunning ? 'Live' : 'Paused'}
</Text>
<View style={styles.footerControls}>
<TouchableOpacity onPress={clearLogs} style={styles.footerBtn}>
<Text style={styles.footerBtnText}>Clear</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={isRunning ? stop : start}
style={[styles.footerBtn, isRunning ? styles.stopBtn : styles.startBtn]}
>
<Text style={styles.footerBtnText}>
{isRunning ? 'Pause' : 'Resume'}
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
}
function AgentBadge({ agent }: { agent: ForexAgent }) {
const statusColor =
agent.status === 'active' ? '#44cc88' :
agent.status === 'idle' ? '#ffaa44' : '#cc4444';
return (
<View style={styles.agentBadge}>
<View style={[styles.agentDot, { backgroundColor: statusColor }]} />
<Text style={styles.agentName}>{agent.name}</Text>
<Text style={styles.agentRole}>{agent.role}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#0a0a14' },
agentBar: {
flexDirection: 'row',
padding: 10,
gap: 10,
borderBottomWidth: 1,
borderColor: '#1a1a2e',
flexWrap: 'wrap',
},
agentBadge: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
backgroundColor: '#1a1a2e',
paddingHorizontal: 10,
paddingVertical: 4,
borderRadius: 12,
},
agentDot: { width: 8, height: 8, borderRadius: 4 },
agentName: { color: '#fff', fontSize: 12, fontWeight: '600' },
agentRole: { color: '#888', fontSize: 10 },
filterRow: {
flexDirection: 'row',
padding: 8,
gap: 6,
borderBottomWidth: 1,
borderColor: '#1a1a2e',
},
filterTab: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 14 },
filterTabActive: { backgroundColor: '#2a2a5e' },
filterText: { color: '#888', fontSize: 12 },
filterTextActive: { color: '#4488ff', fontWeight: '600' },
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 10,
borderTopWidth: 1,
borderColor: '#1a1a2e',
},
footerText: { color: '#666', fontSize: 12 },
footerControls: { flexDirection: 'row', gap: 8 },
footerBtn: { paddingHorizontal: 14, paddingVertical: 6, borderRadius: 6, backgroundColor: '#2a2a4e' },
footerBtnText: { color: '#fff', fontSize: 12, fontWeight: '600' },
startBtn: { backgroundColor: '#1a3a2a' },
stopBtn: { backgroundColor: '#3a1a1a' },
});
Simulation Engine -- Node.js Usage
The forexSimulationEngine can also be used outside of React, for example in a Node.js script:
import {
forexSimulationEngine,
setForexAccessToken,
setForexEngineUrl,
} from '@one_deploy/sdk';
// Configure
setForexEngineUrl('https://engine.one23.io');
setForexAccessToken(process.env.FOREX_ACCESS_TOKEN!);
// Listen
forexSimulationEngine.onLog((entry) => {
const time = new Date(entry.timestamp).toISOString();
console.log(`${time} [${entry.severity.toUpperCase()}] ${entry.type}: ${entry.message}`);
});
forexSimulationEngine.onAgentUpdate((agent) => {
console.log(`Agent ${agent.name} (${agent.role}): ${agent.status}`);
});
// Run
forexSimulationEngine.start();
// Stop after 60 seconds
setTimeout(() => {
forexSimulationEngine.stop();
const logs = forexSimulationEngine.getLogs();
console.log(`Collected ${logs.length} log entries.`);
}, 60_000);
Next Steps
- Components: OneForexConsoleView -- drop-in console view component.
- Trade History -- detailed trade records and status tracking.
- Pool Metrics -- see the metrics behind pool operations.