Feature/okx trading (#1177)

* feat: add OKX exchange trading support
- Add OKX trader client with full trading API integration
- Support API Key, Secret Key, and Passphrase authentication
- Add OKX icon and frontend configuration modal
- Update exchange store and types for OKX fields
* fix: add passphrase column migration and fix exchange type mapping
* fix: show OKX input fields in exchange config modal
* fix: ensure all supported exchanges exist for user when listing
* fix: simplify exchange type check condition for OKX
* debug: add visible debug info for exchange id
* fix: remove debug info from exchange config modal
* fix: add OKX to exchange type condition in AITradersPage
* feat: complete OKX trading support and fix exchange config issues
- Add LIGHTER exchange UI support in AITradersPage
- Add passphrase field to UpdateExchangeConfigRequest type
- Fix OKX HTTP client to bypass proxy (disable system proxy)
- Auto-fetch initial balance from exchange when not set
- Support multiple balance field names for different exchanges
- Add detailed error messages when trader fails to load
- Add lighter_api_key_private_key field to exchange store
This commit is contained in:
tinkle-community
2025-12-06 18:04:59 +08:00
committed by GitHub
parent 5d1d0b6842
commit 1e5ece947c
11 changed files with 1489 additions and 68 deletions

View File

@@ -23,6 +23,7 @@ interface ExchangeConfigModalProps {
exchangeId: string,
apiKey: string,
secretKey?: string,
passphrase?: string, // OKX专用
testnet?: boolean,
hyperliquidWalletAddr?: string,
asterUser?: string,
@@ -222,13 +223,17 @@ export function ExchangeConfigModal({
// 根据交易所类型验证不同字段
if (selectedExchange?.id === 'binance') {
if (!apiKey.trim() || !secretKey.trim()) return
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet)
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), '', testnet)
} else if (selectedExchange?.id === 'okx') {
if (!apiKey.trim() || !secretKey.trim() || !passphrase.trim()) return
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), passphrase.trim(), testnet)
} else if (selectedExchange?.id === 'hyperliquid') {
if (!apiKey.trim() || !hyperliquidWalletAddr.trim()) return // 验证私钥和钱包地址
await onSave(
selectedExchangeId,
apiKey.trim(),
'',
'',
testnet,
hyperliquidWalletAddr.trim()
)
@@ -239,6 +244,7 @@ export function ExchangeConfigModal({
selectedExchangeId,
'',
'',
'',
testnet,
undefined,
asterUser.trim(),
@@ -251,6 +257,7 @@ export function ExchangeConfigModal({
selectedExchangeId,
lighterPrivateKey.trim(),
'',
'',
testnet,
lighterWalletAddr.trim(),
undefined,
@@ -260,13 +267,10 @@ export function ExchangeConfigModal({
lighterPrivateKey.trim(),
lighterApiKeyPrivateKey.trim()
)
} else if (selectedExchange?.id === 'okx') {
if (!apiKey.trim() || !secretKey.trim() || !passphrase.trim()) return
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet)
} else {
// 默认情况其他CEX交易所
if (!apiKey.trim() || !secretKey.trim()) return
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), testnet)
await onSave(selectedExchangeId, apiKey.trim(), secretKey.trim(), '', testnet)
}
}
@@ -404,12 +408,10 @@ export function ExchangeConfigModal({
{selectedExchange && (
<>
{/* Binance/Bybit 和其他 CEX 交易所的字段 */}
{/* Binance/Bybit/OKX 的输入字段 */}
{(selectedExchange.id === 'binance' ||
selectedExchange.id === 'bybit' ||
selectedExchange.type === 'cex') &&
selectedExchange.id !== 'hyperliquid' &&
selectedExchange.id !== 'aster' && (
selectedExchange.id === 'okx') && (
<>
{/* 币安用户配置提示 (D1 方案) */}
{selectedExchange.id === 'binance' && (