mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 01:08:07 +00:00
Implement internationalization support by adding translation functionality and updating UI components to use translated strings. Add language configuration in .env.example and integrate translations across various strategy and UI components.
This commit is contained in:
+62
-34
@@ -5,6 +5,7 @@ import { getExchangeDisplayName, resolveExchangeId } from "../exchanges/create-a
|
||||
import { buildAdapterFromEnv } from "../exchanges/resolve-from-env";
|
||||
import { BasisArbEngine, type BasisArbSnapshot } from "../strategy/basis-arb-engine";
|
||||
import { formatNumber } from "../utils/format";
|
||||
import { t } from "../i18n";
|
||||
|
||||
interface BasisAppProps {
|
||||
onExit: () => void;
|
||||
@@ -31,7 +32,7 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (exchangeId !== "aster") {
|
||||
setError(new Error("期现套利策略目前仅支持 Aster 交易所。请设置 EXCHANGE=aster 后重试。"));
|
||||
setError(new Error(t("basis.onlyAster")));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -57,8 +58,8 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
if (error) {
|
||||
return (
|
||||
<Box flexDirection="column" padding={1}>
|
||||
<Text color="red">无法启动期现套利策略: {error.message}</Text>
|
||||
<Text color="gray">按 Esc 返回菜单。</Text>
|
||||
<Text color="red">{t("basis.startFailed", { message: error.message })}</Text>
|
||||
<Text color="gray">{t("common.backHint")}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -66,7 +67,7 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
if (!snapshot) {
|
||||
return (
|
||||
<Box padding={1}>
|
||||
<Text>正在初始化期现套利监控…</Text>
|
||||
<Text>{t("basis.initializing")}</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -85,10 +86,15 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
const fundingRatePct = snapshot.fundingRate != null ? `${(snapshot.fundingRate * 100).toFixed(4)}%` : "-";
|
||||
const fundingUpdated = snapshot.fundingLastUpdate ? new Date(snapshot.fundingLastUpdate).toLocaleTimeString() : "-";
|
||||
const nextFundingTime = snapshot.nextFundingTime ? new Date(snapshot.nextFundingTime).toLocaleTimeString() : "-";
|
||||
const fundingIncomePerFunding = snapshot.fundingIncomePerFunding != null ? `${formatNumber(snapshot.fundingIncomePerFunding, 4)} USDT` : "-";
|
||||
const fundingIncomePerDay = snapshot.fundingIncomePerDay != null ? `${formatNumber(snapshot.fundingIncomePerDay, 4)} USDT` : "-";
|
||||
const takerFeesPerRoundTrip = snapshot.takerFeesPerRoundTrip != null ? `${formatNumber(snapshot.takerFeesPerRoundTrip, 4)} USDT` : "-";
|
||||
const fundingCountToBreakeven = snapshot.fundingCountToBreakeven != null ? `${formatNumber(snapshot.fundingCountToBreakeven, 2)} 次` : "-";
|
||||
const fundingIncomePerFunding =
|
||||
snapshot.fundingIncomePerFunding != null ? `${formatNumber(snapshot.fundingIncomePerFunding, 4)} USDT` : "-";
|
||||
const fundingIncomePerDay =
|
||||
snapshot.fundingIncomePerDay != null ? `${formatNumber(snapshot.fundingIncomePerDay, 4)} USDT` : "-";
|
||||
const takerFeesPerRoundTrip =
|
||||
snapshot.takerFeesPerRoundTrip != null ? `${formatNumber(snapshot.takerFeesPerRoundTrip, 4)} USDT` : "-";
|
||||
const fundingCountToBreakeven =
|
||||
snapshot.fundingCountToBreakeven != null ? formatNumber(snapshot.fundingCountToBreakeven, 2) : "-";
|
||||
const feePct = (basisConfig.takerFeeRate * 100).toFixed(4);
|
||||
const feedStatus = snapshot.feedStatus;
|
||||
const lastLogs = snapshot.tradeLog.slice(-5);
|
||||
const spotBalances = (snapshot.spotBalances ?? []).filter((b) => Math.abs(b.free) > 0 || Math.abs(b.locked) > 0);
|
||||
@@ -97,72 +103,94 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
return (
|
||||
<Box flexDirection="column" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text color="cyanBright">Basis Arbitrage Dashboard</Text>
|
||||
<Text color="cyanBright">{t("basis.title")}</Text>
|
||||
<Text>
|
||||
交易所: {exchangeName} | 期货合约: {snapshot.futuresSymbol} | 现货交易对: {snapshot.spotSymbol}
|
||||
{t("basis.headerLine", {
|
||||
exchange: exchangeName,
|
||||
futures: snapshot.futuresSymbol,
|
||||
spot: snapshot.spotSymbol,
|
||||
})}
|
||||
</Text>
|
||||
<Text color="gray">按 Esc 返回策略选择 | 数据状态: 期货({feedStatus.futures ? "OK" : "--"}) 现货({feedStatus.spot ? "OK" : "--"}) 资金费率({feedStatus.funding ? "OK" : "--"})</Text>
|
||||
<Text color="gray">最近更新时间: {lastUpdated}</Text>
|
||||
<Text color="gray">
|
||||
{t("basis.statusLine", {
|
||||
futuresStatus: feedStatus.futures ? "OK" : "--",
|
||||
spotStatus: feedStatus.spot ? "OK" : "--",
|
||||
fundingStatus: feedStatus.funding ? "OK" : "--",
|
||||
})}
|
||||
</Text>
|
||||
<Text color="gray">{t("basis.lastUpdated", { time: lastUpdated })}</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="row" marginBottom={1}>
|
||||
<Box flexDirection="column" marginRight={4}>
|
||||
<Text color="greenBright">期货盘口</Text>
|
||||
<Text>买一: {futuresBid} | 卖一: {futuresAsk}</Text>
|
||||
<Text color="gray">更新时间: {futuresUpdated}</Text>
|
||||
<Text color="greenBright">{t("basis.section.futures")}</Text>
|
||||
<Text>{t("basis.bookLine", { bid: futuresBid, ask: futuresAsk })}</Text>
|
||||
<Text color="gray">{t("basis.updatedAt", { time: futuresUpdated })}</Text>
|
||||
</Box>
|
||||
<Box flexDirection="column">
|
||||
<Text color="greenBright">现货盘口</Text>
|
||||
<Text>买一: {spotBid} | 卖一: {spotAsk}</Text>
|
||||
<Text color="gray">更新时间: {spotUpdated}</Text>
|
||||
<Text color="greenBright">{t("basis.section.spot")}</Text>
|
||||
<Text>{t("basis.bookLine", { bid: spotBid, ask: spotAsk })}</Text>
|
||||
<Text color="gray">{t("basis.updatedAt", { time: spotUpdated })}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text color="yellow">资金费率</Text>
|
||||
<Text>当前资金费率: {fundingRatePct}</Text>
|
||||
<Text color="gray">资金费率更新时间: {fundingUpdated} | 下次结算时间: {nextFundingTime}</Text>
|
||||
<Text>单次资金费率收益(估): {fundingIncomePerFunding} | 日收益(估): {fundingIncomePerDay}</Text>
|
||||
<Text>双边吃单手续费(估): {takerFeesPerRoundTrip} | 回本所需资金费率次数: {fundingCountToBreakeven}</Text>
|
||||
<Text color="yellow">{t("basis.section.funding")}</Text>
|
||||
<Text>{t("basis.fundingRate", { rate: fundingRatePct })}</Text>
|
||||
<Text color="gray">
|
||||
{t("basis.fundingTimes", { updated: fundingUpdated, next: nextFundingTime })}
|
||||
</Text>
|
||||
<Text>{t("basis.fundingIncome", { per: fundingIncomePerFunding, perDay: fundingIncomePerDay })}</Text>
|
||||
<Text>{t("basis.takerFees", { fees: takerFeesPerRoundTrip, count: fundingCountToBreakeven })}</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="row" marginBottom={1}>
|
||||
<Box flexDirection="column" marginRight={4}>
|
||||
<Text color="cyan">现货账户余额(非0)</Text>
|
||||
<Text color="cyan">{t("basis.spotBalanceTitle")}</Text>
|
||||
{spotBalances.length ? (
|
||||
spotBalances.map((b) => (
|
||||
<Text key={`spot-${b.asset}`}>
|
||||
{b.asset}: 可用 {formatNumber(b.free, 8)} | 冻结 {formatNumber(b.locked, 8)}
|
||||
{t("basis.balanceLine", {
|
||||
asset: b.asset,
|
||||
free: formatNumber(b.free, 8),
|
||||
locked: formatNumber(b.locked, 8),
|
||||
})}
|
||||
</Text>
|
||||
))
|
||||
) : (
|
||||
<Text color="gray">无</Text>
|
||||
<Text color="gray">{t("basis.none")}</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box flexDirection="column">
|
||||
<Text color="cyan">合约账户余额(非0)</Text>
|
||||
<Text color="cyan">{t("basis.futuresBalanceTitle")}</Text>
|
||||
{futuresBalances.length ? (
|
||||
futuresBalances.map((b) => (
|
||||
<Text key={`fut-${b.asset}`}>
|
||||
{b.asset}: 钱包 {formatNumber(b.wallet, 8)} | 可用 {formatNumber(b.available, 8)}
|
||||
{t("basis.futuresBalanceLine", {
|
||||
asset: b.asset,
|
||||
wallet: formatNumber(b.wallet, 8),
|
||||
available: formatNumber(b.available, 8),
|
||||
})}
|
||||
</Text>
|
||||
))
|
||||
) : (
|
||||
<Text color="gray">无</Text>
|
||||
<Text color="gray">{t("basis.none")}</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text color={snapshot.opportunity ? "greenBright" : "redBright"}>套利差价(卖期货 / 买现货)</Text>
|
||||
<Text color={snapshot.opportunity ? "green" : undefined}>毛价差: {spread} USDT | {spreadBps} bp</Text>
|
||||
<Text color={snapshot.opportunity ? "greenBright" : "redBright"}>{t("basis.spreadTitle")}</Text>
|
||||
<Text color={snapshot.opportunity ? "green" : undefined}>
|
||||
{t("basis.spreadLine", { spread, bps: spreadBps })}
|
||||
</Text>
|
||||
<Text color={snapshot.opportunity ? "green" : "red"}>
|
||||
扣除 taker 手续费 ({(basisConfig.takerFeeRate * 100).toFixed(4)}% × 双边): {netSpread} USDT | {netSpreadBps} bp
|
||||
{t("basis.netSpreadLine", { feePct, net: netSpread, netBps: netSpreadBps })}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column">
|
||||
<Text color="yellow">最近事件</Text>
|
||||
<Text color="yellow">{t("common.section.recent")}</Text>
|
||||
{lastLogs.length ? (
|
||||
lastLogs.map((entry, index) => {
|
||||
const color = entry.type === "entry" ? "green" : entry.type === "exit" ? "red" : undefined;
|
||||
@@ -173,7 +201,7 @@ export function BasisApp({ onExit }: BasisAppProps) {
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Text color="gray">暂无日志</Text>
|
||||
<Text color="gray">{t("common.noLogs")}</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user