Trade History
Every forex trade in StableFX follows a defined lifecycle from request-for-quote (RFQ) to final settlement. This page covers the ForexTradeRecord type, the ForexTradeStatus lifecycle, and how to fetch and display trade history.
Trade Lifecycle
StableFX trades progress through the following statuses:
RFQ ──> QUOTED ──> MATCHED ──> SETTLED
| | |
| | +──> FAILED
| +──> FAILED
+──> FAILED
| Status | Description |
|---|---|
RFQ | Request-for-quote submitted. The system is sourcing prices from liquidity pools. |
QUOTED | A quote has been received. The price is locked for a short window. |
MATCHED | The trade has been matched with a counterparty. Settlement is in progress. |
SETTLED | Trade completed successfully. Funds have been distributed. |
FAILED | Trade failed at any stage. Funds in the clearing pool are returned. |
ForexTradeStatus Type
import type { ForexTradeStatus } from '@one_deploy/sdk';
type ForexTradeStatus = 'RFQ' | 'QUOTED' | 'MATCHED' | 'SETTLED' | 'FAILED';
ForexTradeRecord Type
import type { ForexTradeRecord } from '@one_deploy/sdk';
interface ForexTradeRecord {
/** Unique trade identifier. */
id: string;
/** The investment this trade belongs to. */
investmentId: string;
/** Currency pair traded, e.g. "EUR/USD". */
currencyPair: string;
/** Trade direction. */
side: 'buy' | 'sell';
/** Trade amount in base currency units. */
amount: number;
/** Quoted price at time of trade. */
quotePrice: number;
/** Execution price (may differ from quotePrice due to slippage). */
executionPrice: number | null;
/** Current trade status. */
status: ForexTradeStatus;
/** Realized profit or loss (in USDC), set after settlement. */
pnl: number | null;
/** Fee charged for this trade (in USDC). */
fee: number;
/** Slippage between quote and execution price (decimal). */
slippage: number | null;
/** ISO-8601 timestamp when the RFQ was submitted. */
createdAt: string;
/** ISO-8601 timestamp when the trade was last updated. */
updatedAt: string;
/** ISO-8601 timestamp when the trade was settled, if applicable. */
settledAt: string | null;
/** On-chain transaction hash, if applicable. */
txHash: string | null;
/** Reason for failure, if status is FAILED. */
failureReason: string | null;
}
Fetching Trade History
Trade records are accessed through the useForexInvestments hook's investment data, or directly through the useForexTrading hook.
Using useForexTrading
import { useForexTrading } from '@one_deploy/sdk';
function useTradeHistory() {
const { getTradeHistory } = useForexTrading();
const fetchTrades = async (investmentId: string) => {
const trades = await getTradeHistory({
investmentId,
limit: 50,
offset: 0,
});
return trades;
};
return { fetchTrades };
}
Full Trade History Screen
import React, { useEffect, useState } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator } from 'react-native';
import { useForexTrading } from '@one_deploy/sdk';
import type { ForexTradeRecord, ForexTradeStatus } from '@one_deploy/sdk';
function TradeHistoryScreen({ investmentId }: { investmentId: string }) {
const { getTradeHistory } = useForexTrading();
const [trades, setTrades] = useState<ForexTradeRecord[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
getTradeHistory({ investmentId, limit: 100 })
.then(setTrades)
.finally(() => setLoading(false));
}, [investmentId, getTradeHistory]);
if (loading) return <ActivityIndicator size="large" style={{ marginTop: 40 }} />;
return (
<FlatList
data={trades}
keyExtractor={(t) => t.id}
ListHeaderComponent={
<Text style={styles.header}>
Trade History ({trades.length} trades)
</Text>
}
renderItem={({ item }) => <TradeRow trade={item} />}
ListEmptyComponent={
<Text style={styles.empty}>No trades found.</Text>
}
/>
);
}
function TradeRow({ trade }: { trade: ForexTradeRecord }) {
const statusColor = getStatusColor(trade.status);
return (
<View style={styles.row}>
<View style={styles.rowLeft}>
<View style={styles.rowHeader}>
<Text style={styles.pair}>{trade.currencyPair}</Text>
<Text style={[styles.side, trade.side === 'buy' ? styles.buy : styles.sell]}>
{trade.side.toUpperCase()}
</Text>
</View>
<Text style={styles.amount}>
{trade.amount.toLocaleString()} units @ {trade.quotePrice}
</Text>
{trade.executionPrice && (
<Text style={styles.exec}>
Executed @ {trade.executionPrice}
{trade.slippage !== null &&
` (slippage: ${(trade.slippage * 100).toFixed(3)}%)`}
</Text>
)}
<Text style={styles.date}>
{new Date(trade.createdAt).toLocaleString()}
</Text>
</View>
<View style={styles.rowRight}>
<Text style={[styles.status, { color: statusColor }]}>
{trade.status}
</Text>
{trade.pnl !== null && (
<Text
style={[
styles.pnl,
{ color: trade.pnl >= 0 ? '#44cc88' : '#cc4444' },
]}
>
{trade.pnl >= 0 ? '+' : ''}${trade.pnl.toFixed(2)}
</Text>
)}
{trade.status === 'FAILED' && trade.failureReason && (
<Text style={styles.failReason}>{trade.failureReason}</Text>
)}
</View>
</View>
);
}
function getStatusColor(status: ForexTradeStatus): string {
switch (status) {
case 'RFQ': return '#ffaa44';
case 'QUOTED': return '#44aaff';
case 'MATCHED': return '#aa88ff';
case 'SETTLED': return '#44cc88';
case 'FAILED': return '#cc4444';
default: return '#888888';
}
}
const styles = StyleSheet.create({
header: { fontSize: 18, fontWeight: '700', color: '#fff', padding: 16 },
empty: { color: '#666', textAlign: 'center', padding: 40 },
row: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 14,
borderBottomWidth: 1,
borderColor: '#1a1a2e',
},
rowLeft: { flex: 1 },
rowRight: { alignItems: 'flex-end', justifyContent: 'center' },
rowHeader: { flexDirection: 'row', alignItems: 'center', gap: 8 },
pair: { fontSize: 15, fontWeight: '700', color: '#fff' },
side: { fontSize: 11, fontWeight: '700', paddingHorizontal: 6, paddingVertical: 2, borderRadius: 4 },
buy: { backgroundColor: '#1a3a2a', color: '#44cc88' },
sell: { backgroundColor: '#3a1a1a', color: '#cc4444' },
amount: { fontSize: 13, color: '#aaa', marginTop: 4 },
exec: { fontSize: 12, color: '#888', marginTop: 2 },
date: { fontSize: 11, color: '#666', marginTop: 4 },
status: { fontSize: 12, fontWeight: '700' },
pnl: { fontSize: 14, fontWeight: '600', fontFamily: 'monospace', marginTop: 4 },
failReason: { fontSize: 11, color: '#cc4444', marginTop: 2, maxWidth: 120 },
});
Tracking Trade Status
You can poll for status updates on in-flight trades:
import { useForexTrading } from '@one_deploy/sdk';
import type { ForexTradeRecord } from '@one_deploy/sdk';
function useTradeStatusPolling(tradeId: string, intervalMs: number = 3000) {
const { getTradeById } = useForexTrading();
const [trade, setTrade] = useState<ForexTradeRecord | null>(null);
useEffect(() => {
let active = true;
const poll = async () => {
const updated = await getTradeById(tradeId);
if (!active) return;
setTrade(updated);
// Stop polling when trade reaches a terminal status
if (updated.status === 'SETTLED' || updated.status === 'FAILED') {
return;
}
setTimeout(poll, intervalMs);
};
poll();
return () => { active = false; };
}, [tradeId, intervalMs, getTradeById]);
return trade;
}
// Usage
function TradeStatusTracker({ tradeId }: { tradeId: string }) {
const trade = useTradeStatusPolling(tradeId);
if (!trade) return <Text>Loading...</Text>;
return (
<View style={{ padding: 16 }}>
<Text style={{ color: '#fff', fontSize: 16 }}>
Trade {trade.id.slice(0, 8)}...
</Text>
<Text style={{ color: getStatusColor(trade.status), marginTop: 8 }}>
Status: {trade.status}
</Text>
{trade.status === 'SETTLED' && (
<Text style={{ color: '#44cc88', marginTop: 4 }}>
Settled at {new Date(trade.settledAt!).toLocaleString()}
</Text>
)}
</View>
);
}
Trade Record Summary
| Field | Available At | Description |
|---|---|---|
id, currencyPair, side, amount | RFQ | Set when the trade is created |
quotePrice | QUOTED | Set when a quote is received |
executionPrice, slippage | MATCHED | Set when the trade is matched |
pnl, settledAt, txHash | SETTLED | Set upon successful settlement |
failureReason | FAILED | Set when the trade fails |
Next Steps
- Console View -- watch trades flow through the system in real time.
- Creating Investments -- create the investments that generate trades.
- Components: OneForexTradeHistory -- pre-built trade history component.