Eidos

Installation Notice

The installation may currently fail. We recommend copying the code below and creating the extension manually in Eidos.

New Block - 0142a054

By: Mayne

Install Latest (v0.0.1)

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>
  )
}

Information

Author
Mayne
Type
block
Latest Version
0.0.1
Last Updated
11/07/2025
Published
11/07/2025

Version History

  • v0.0.1 11/07/2025