mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-03 19:11:02 +08:00
feat(exchange): add Bybit Futures support (#1100)
* feat(exchange): add Bybit Futures support - Add Bybit Go SDK dependency (github.com/bybit-exchange/bybit.go.api) - Create trader/bybit_trader.go implementing Trader interface for USDT perpetual futures - Update config/database.go to include Bybit in default exchanges - Update manager/trader_manager.go to handle Bybit API key configuration - Update trader/auto_trader.go to add BybitAPIKey/BybitSecretKey fields and bybit case - Add Bybit icon to frontend ExchangeIcons.tsx Bybit uses standard API Key/Secret Key authentication (similar to Binance). Only USDT perpetual futures (category=linear) are supported. Co-Authored-By: tinkle-community <tinklefund@gmail.com> * test(bybit): add comprehensive unit tests for Bybit trader - Add BybitTraderTestSuite following existing test patterns - Interface compliance test (Trader interface) - Symbol format validation tests - FormatQuantity tests with 3-decimal precision - API response parsing tests (success, error, permission denied) - Position side conversion tests (Buy->long, Sell->short) - Cache duration verification test - Mock server integration tests for API endpoints All 12 Bybit tests pass. Co-Authored-By: tinkle-community <tinklefund@gmail.com> * fix(frontend): add Bybit support to exchange config forms 修復前端對 Bybit 交易所的支持: - 添加 Bybit 到 API Key/Secret Key 輸入欄位顯示邏輯 - 添加 Bybit 的表單驗證邏輯 - 修復 ExchangeConfigModal.tsx 和 AITradersPage.tsx Co-Authored-By: tinkle-community <tinklefund@gmail.com> --------- Co-authored-by: the-dev-z <the-dev-z@users.noreply.github.com> Co-authored-by: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
committed by
GitHub
parent
0002f36dc8
commit
ded86d831f
@@ -2095,8 +2095,9 @@ function ExchangeConfigModal({
|
||||
|
||||
{selectedExchange && (
|
||||
<>
|
||||
{/* Binance 和其他 CEX 交易所的字段 */}
|
||||
{/* Binance/Bybit 和其他 CEX 交易所的字段 */}
|
||||
{(selectedExchange.id === 'binance' ||
|
||||
selectedExchange.id === 'bybit' ||
|
||||
selectedExchange.type === 'cex') &&
|
||||
selectedExchange.id !== 'hyperliquid' &&
|
||||
selectedExchange.id !== 'aster' && (
|
||||
@@ -2584,10 +2585,13 @@ function ExchangeConfigModal({
|
||||
(!asterUser.trim() ||
|
||||
!asterSigner.trim() ||
|
||||
!asterPrivateKey.trim())) ||
|
||||
(selectedExchange.id === 'bybit' &&
|
||||
(!apiKey.trim() || !secretKey.trim())) ||
|
||||
(selectedExchange.type === 'cex' &&
|
||||
selectedExchange.id !== 'hyperliquid' &&
|
||||
selectedExchange.id !== 'aster' &&
|
||||
selectedExchange.id !== 'binance' &&
|
||||
selectedExchange.id !== 'bybit' &&
|
||||
selectedExchange.id !== 'okx' &&
|
||||
(!apiKey.trim() || !secretKey.trim()))
|
||||
}
|
||||
|
||||
@@ -47,6 +47,39 @@ const HyperliquidIcon: React.FC<IconProps> = ({
|
||||
</svg>
|
||||
)
|
||||
|
||||
// Bybit SVG 图标组件
|
||||
const BybitIcon: React.FC<IconProps> = ({
|
||||
width = 24,
|
||||
height = 24,
|
||||
className,
|
||||
}) => (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 200 200"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M50.5 53.3H75.5L100 77.8V122.2L75.5 146.7H50.5V53.3Z"
|
||||
fill="#F7A600"
|
||||
/>
|
||||
<path
|
||||
d="M149.5 53.3H124.5L100 77.8V122.2L124.5 146.7H149.5V53.3Z"
|
||||
fill="#F7A600"
|
||||
/>
|
||||
<path
|
||||
d="M75.5 53.3H124.5V77.8H75.5V53.3Z"
|
||||
fill="#F7A600"
|
||||
/>
|
||||
<path
|
||||
d="M75.5 122.2H124.5V146.7H75.5V122.2Z"
|
||||
fill="#F7A600"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
// Aster SVG 图标组件
|
||||
const AsterIcon: React.FC<IconProps> = ({
|
||||
width = 24,
|
||||
@@ -133,11 +166,13 @@ export const getExchangeIcon = (
|
||||
// 支持完整ID或类型名
|
||||
const type = exchangeType.toLowerCase().includes('binance')
|
||||
? 'binance'
|
||||
: exchangeType.toLowerCase().includes('hyperliquid')
|
||||
? 'hyperliquid'
|
||||
: exchangeType.toLowerCase().includes('aster')
|
||||
? 'aster'
|
||||
: exchangeType.toLowerCase()
|
||||
: exchangeType.toLowerCase().includes('bybit')
|
||||
? 'bybit'
|
||||
: exchangeType.toLowerCase().includes('hyperliquid')
|
||||
? 'hyperliquid'
|
||||
: exchangeType.toLowerCase().includes('aster')
|
||||
? 'aster'
|
||||
: exchangeType.toLowerCase()
|
||||
|
||||
const iconProps = {
|
||||
width: props.width || 24,
|
||||
@@ -147,13 +182,15 @@ export const getExchangeIcon = (
|
||||
|
||||
switch (type) {
|
||||
case 'binance':
|
||||
case 'cex':
|
||||
return <BinanceIcon {...iconProps} />
|
||||
case 'bybit':
|
||||
return <BybitIcon {...iconProps} />
|
||||
case 'hyperliquid':
|
||||
case 'dex':
|
||||
return <HyperliquidIcon {...iconProps} />
|
||||
case 'aster':
|
||||
return <AsterIcon {...iconProps} />
|
||||
case 'cex':
|
||||
default:
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -404,8 +404,9 @@ export function ExchangeConfigModal({
|
||||
|
||||
{selectedExchange && (
|
||||
<>
|
||||
{/* Binance 和其他 CEX 交易所的字段 */}
|
||||
{/* Binance/Bybit 和其他 CEX 交易所的字段 */}
|
||||
{(selectedExchange.id === 'binance' ||
|
||||
selectedExchange.id === 'bybit' ||
|
||||
selectedExchange.type === 'cex') &&
|
||||
selectedExchange.id !== 'hyperliquid' &&
|
||||
selectedExchange.id !== 'aster' && (
|
||||
@@ -1012,11 +1013,14 @@ export function ExchangeConfigModal({
|
||||
!asterPrivateKey.trim())) ||
|
||||
(selectedExchange.id === 'lighter' &&
|
||||
(!lighterWalletAddr.trim() || !lighterPrivateKey.trim())) ||
|
||||
(selectedExchange.id === 'bybit' &&
|
||||
(!apiKey.trim() || !secretKey.trim())) ||
|
||||
(selectedExchange.type === 'cex' &&
|
||||
selectedExchange.id !== 'hyperliquid' &&
|
||||
selectedExchange.id !== 'aster' &&
|
||||
selectedExchange.id !== 'lighter' &&
|
||||
selectedExchange.id !== 'binance' &&
|
||||
selectedExchange.id !== 'bybit' &&
|
||||
selectedExchange.id !== 'okx' &&
|
||||
(!apiKey.trim() || !secretKey.trim()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user