Development of Mobile Trading Terminal
A mobile trading terminal is not a scaled-down version of a desktop terminal. Different context of use, different interaction patterns, different technical limitations. User on mobile trades in a different situation: checking positions in transport, reacting to alerts, making quick trades. UX should account for this.
Technology Choice
React Native with Expo or clean bare workflow — optimal choice with a web team. Reuse business logic and some UI components with web version.
Flutter (Dart) — excellent performance, beautiful UI out of the box, but different language and ecosystem.
Native (Swift/Kotlin) — maximum performance, but two separate codebases.
For most projects — React Native. With budget and performance requirements — Flutter.
React Native Terminal Architecture
// Navigation structure
const TerminalApp = () => (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Markets" component={MarketsScreen} />
<Tab.Screen name="Terminal" component={TradingScreen} />
<Tab.Screen name="Portfolio" component={PortfolioScreen} />
<Tab.Screen name="Orders" component={OrdersScreen} />
</Tab.Navigator>
</NavigationContainer>
);
// TradingScreen - main terminal screen
const TradingScreen = ({ route }) => {
const { symbol } = route.params;
return (
<SafeAreaView style={styles.container}>
{/* Swipe between tabs */}
<ScrollView
horizontal
pagingEnabled
showsHorizontalScrollIndicator={false}
>
<ChartView symbol={symbol} />
<OrderBookView symbol={symbol} />
<TradeHistoryView symbol={symbol} />
</ScrollView>
{/* Fixed order panel at bottom */}
<OrderFormBottomSheet symbol={symbol} />
</SafeAreaView>
);
};
Charts on Mobile
TradingView Lightweight Charts works in React Native via WebView:
import { WebView } from 'react-native-webview';
const ChartWebView = ({ symbol, candles }: ChartProps) => {
const htmlContent = `
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
</head>
<body style="margin:0; background:#1a1a2e">
<div id="chart"></div>
<script>
const chart = LightweightCharts.createChart(document.getElementById('chart'), {
width: window.innerWidth,
height: window.innerHeight,
layout: { background: { color: '#1a1a2e' }, textColor: '#d1d4dc' },
});
const series = chart.addCandlestickSeries();
series.setData(${JSON.stringify(candles)});
// Get updates from React Native
window.addEventListener('message', (e) => {
const msg = JSON.parse(e.data);
if (msg.type === 'UPDATE_CANDLE') series.update(msg.candle);
});
</script>
</body>
</html>
`;
return (
<WebView
source={{ html: htmlContent }}
style={{ flex: 1 }}
javaScriptEnabled
onMessage={handleChartMessage}
/>
);
};
Order Form: Bottom Sheet
import BottomSheet, { BottomSheetView } from '@gorhom/bottom-sheet';
const OrderFormBottomSheet = ({ symbol }: { symbol: string }) => {
const snapPoints = ['15%', '50%', '90%'];
const { balance, price } = useMarketData(symbol);
return (
<BottomSheet snapPoints={snapPoints} enablePanDownToClose={false}>
<BottomSheetView>
<OrderTypeSelector />
<PriceInput
value={orderPrice}
onChange={setOrderPrice}
currentPrice={price}
/>
<AmountSlider
min={0}
max={balance}
step={0.001}
onChange={(pct) => setAmount(balance * pct)}
/>
<View style={styles.buttons}>
<TouchableOpacity
style={[styles.button, styles.buyButton]}
onPress={() => placeOrder('BUY')}
>
<Text>Buy</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.sellButton]}
onPress={() => placeOrder('SELL')}
>
<Text>Sell</Text>
</TouchableOpacity>
</View>
</BottomSheetView>
</BottomSheet>
);
};
Push Notifications
Trading alerts via Firebase Cloud Messaging (FCM):
// Token registration
import messaging from '@react-native-firebase/messaging';
async function registerPushToken(userId: string) {
const permission = await messaging().requestPermission();
if (permission !== messaging.AuthorizationStatus.AUTHORIZED) return;
const token = await messaging().getToken();
await api.registerPushToken(userId, token, 'mobile');
}
// Handle incoming notification
messaging().onMessage(async (remoteMessage) => {
const { type, symbol, price } = remoteMessage.data;
if (type === 'PRICE_ALERT') {
// Show local notification
await notifee.displayNotification({
title: `${symbol} Alert`,
body: `Price reached ${price}`,
android: { channelId: 'trading-alerts', importance: AndroidImportance.HIGH },
});
}
});
Biometric Authentication
Touch ID / Face ID for quick login and order confirmation:
import ReactNativeBiometrics from 'react-native-biometrics';
const rnBiometrics = new ReactNativeBiometrics();
async function confirmOrderWithBiometrics(order: Order): Promise<boolean> {
const { available } = await rnBiometrics.isSensorAvailable();
if (!available) return await showPinCodeFallback();
const { success } = await rnBiometrics.simplePrompt({
promptMessage: `Confirm ${order.side} ${order.quantity} ${order.symbol}`,
cancelButtonText: 'Cancel',
});
return success;
}
Performance and Optimization
On a mobile device, performance is more critical: limited CPU/GPU, battery. Key optimizations:
- Hermes JavaScript engine — faster than V8 for React Native on Android
- Reanimated 2 for animations — native thread, doesn't block JS
- FlashList instead of FlatList — significantly faster for long lists (order history, trade feed)
- Throttle WebSocket updates — on mobile, 5–10 updates per second is sufficient
- Background mode — iOS/Android limit WebSocket in background. Switch to REST polling when backgrounded







