The installation may currently fail. We recommend copying the code below and creating the extension manually in Eidos.
By: Mayne
Empty block extension
import React, { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Slider } from "@/components/ui/slider"
import { Badge } from "@/components/ui/badge"
import { TrendingUp, TrendingDown, Plus, Minus, RefreshCw } from "lucide-react"
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Area, AreaChart } from "recharts"
export default function GridTradingTool() {
const [currentPrice, setCurrentPrice] = useState(50000)
const [gridLevels, setGridLevels] = useState(10)
const [priceRange, setPriceRange] = useState({ min: 45000, max: 55000 })
const [grids, setGrids] = useState([])
const [trades, setTrades] = useState([])
const [totalProfit, setTotalProfit] = useState(0)
const [isRunning, setIsRunning] = useState(false)
// 生成网格价格
const generateGrids = () => {
const gridSize = (priceRange.max - priceRange.min) / gridLevels
const newGrids = []
for (let i = 0; i <= gridLevels; i++) {
const price = priceRange.min + (gridSize * i)
newGrids.push({
id: i,
price: parseFloat(price.toFixed(2)),
status: currentPrice >= price ? 'bought' : 'sell',
executed: false
})
}
setGrids(newGrids)
}
// 模拟价格更新
const simulatePriceUpdate = () => {
const change = (Math.random() - 0.5) * 1000
const newPrice = Math.max(priceRange.min, Math.min(priceRange.max, currentPrice + change))
setCurrentPrice(parseFloat(newPrice.toFixed(2)))
}
// 执行网格交易
const executeGridTrade = () => {
if (!isRunning) return
const activeGrid = grids.find(grid =>
Math.abs(grid.price - currentPrice) < (priceRange.max - priceRange.min) / gridLevels / 2
)
if (activeGrid && !activeGrid.executed) {
const newTrades = [...trades]
const trade = {
id: Date.now(),
price: currentPrice,
type: activeGrid.status,
timestamp: new Date().toLocaleTimeString(),
profit: activeGrid.status === 'sell' ? 100 : 0
}
newTrades.unshift(trade)
setTrades(newTrades.slice(0, 20))
if (trade.profit > 0) {
setTotalProfit(prev => prev + trade.profit)
}
setGrids(prev => prev.map(grid =>
grid.id === activeGrid.id ? { ...grid, executed: true } : grid
))
}
}
// 生成图表数据
const generateChartData = () => {
const data = []
for (let i = 0; i < 24; i++) {
const time = `${i}:00`
const price = currentPrice + (Math.random() - 0.5) * 2000
data.push({
time,
price: parseFloat(price.toFixed(2)),
gridHigh: priceRange.max,
gridLow: priceRange.min
})
}
return data
}
useEffect(() => {
generateGrids()
}, [gridLevels, priceRange])
useEffect(() => {
executeGridTrade()
}, [currentPrice])
useEffect(() => {
let interval
if (isRunning) {
interval = setInterval(() => {
simulatePriceUpdate()
}, 2000)
}
return () => clearInterval(interval)
}, [isRunning, currentPrice])
return (
<div className="p-6 max-w-7xl mx-auto space-y-6">
<Card>
<CardHeader>
<CardTitle>网格交易工具</CardTitle>
<CardDescription>自动化网格交易策略管理</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="space-y-4">
<div className="text-center p-4 bg-gray-50 rounded-lg">
<div className="text-2xl font-bold text-blue-600">
¥{currentPrice.toLocaleString()}
</div>
<div className="text-sm text-gray-500">当前价格</div>
</div>
<div className="flex justify-between items-center">
<Badge variant={totalProfit >= 0 ? "default" : "destructive"}>
总利润: ¥{totalProfit.toLocaleString()}
</Badge>
<Badge variant="outline">
网格数: {gridLevels}
</Badge>
</div>
</div>
<div className="space-y-4">
<div>
<Label>网格数量: {gridLevels}</Label>
<Slider
value={[gridLevels]}
onValueChange={(value) => setGridLevels(value[0])}
min={5}
max={20}
step={1}
className="mt-2"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<Label>最低价格</Label>
<Input
type="number"
value={priceRange.min}
onChange={(e) => setPriceRange(prev => ({ ...prev, min: parseFloat(e.target.value) }))}
className="mt-1"
/>
</div>
<div>
<Label>最高价格</Label>
<Input
type="number"
value={priceRange.max}
onChange={(e) => setPriceRange(prev => ({ ...prev, max: parseFloat(e.target.value) }))}
className="mt-1"
/>
</div>
</div>
</div>
<div className="space-y-4">
<Button
onClick={() => setIsRunning(!isRunning)}
className="w-full"
variant={isRunning ? "destructive" : "default"}
>
{isRunning ? "停止交易" : "开始交易"}
</Button>
<Button
onClick={generateGrids}
variant="outline"
className="w-full"
>
<RefreshCw className="w-4 h-4 mr-2" />
重新生成网格
</Button>
<Button
onClick={simulatePriceUpdate}
variant="outline"
className="w-full"
>
手动更新价格
</Button>
</div>
</div>
</CardContent>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>价格图表</CardTitle>
<CardDescription>24小时价格走势与网格区间</CardDescription>
</CardHeader>
<CardContent>
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={generateChartData()}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="time" />
<YAxis />
<Tooltip />
<Area
type="monotone"
dataKey="gridHigh"
stroke="#ff7300"
fill="#ff7300"
fillOpacity={0.1}
/>
<Area
type="monotone"
dataKey="gridLow"
stroke="#00ff00"
fill="#00ff00"
fillOpacity={0.1}
/>
<Line
type="monotone"
dataKey="price"
stroke="#8884d8"
strokeWidth={2}
dot={false}
/>
</AreaChart>
</ResponsiveContainer>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>网格设置</CardTitle>
<CardDescription>当前网格价格分布</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2 max-h-80 overflow-y-auto">
{grids.map((grid) => (
<div
key={grid.id}
className={`flex justify-between items-center p-3 rounded-lg border ${
Math.abs(grid.price - currentPrice) < (priceRange.max - priceRange.min) / gridLevels / 2
? "border-blue-500 bg-blue-50"
: "border-gray-200"
}`}
>
<span className="font-medium">网格 {grid.id + 1}</span>
<div className="flex items-center space-x-2">
<span className="text-sm">¥{grid.price.toLocaleString()}</span>
<Badge
variant={grid.status === 'buy' ? "default" : "secondary"}
className="text-xs"
>
{grid.status === 'buy' ? '买入' : '卖出'}
</Badge>
{grid.executed && (
<Badge variant="outline" className="text-xs">
已执行
</Badge>
)}
</div>
</div>
))}
</div>
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle>交易记录</CardTitle>
<CardDescription>最近的交易活动</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2 max-h-60 overflow-y-auto">
{trades.length === 0 ? (
<div className="text-center text-gray-500 py-8">
暂无交易记录
</div>
) : (
trades.map((trade) => (
<div
key={trade.id}
className="flex justify-between items-center p-3 bg-gray-50 rounded-lg"
>
<div className="flex items-center space-x-3">
{trade.type === 'buy' ? (
<TrendingDown className="w-4 h-4 text-red-500" />
) : (
<TrendingUp className="w-4 h-4 text-green-500" />
)}
<div>
<div className="font-medium">
{trade.type === 'buy' ? '买入' : '卖出'}
</div>
<div className="text-sm text-gray-500">
{trade.timestamp}
</div>
</div>
</div>
<div className="text-right">
<div className="font-medium">¥{trade.price.toLocaleString()}</div>
{trade.profit > 0 && (
<div className="text-sm text-green-600">
+¥{trade.profit}
</div>
)}
</div>
</div>
))
)}
</div>
</CardContent>
</Card>
</div>
)
}