Я пытался создать постоянно движущуюся вертикальную линию на графике, подобную той, что показана на фото. Несмотря на то что я попробовал уже более сотни различных методов, мне не удалось найти решение.
import React, { useEffect, useRef, useState } from 'react';
import { createChart } from 'lightweight-charts';
const App = () => {
const chartContainerRef = useRef(null);
const chart = useRef(null);
const lineSeries = useRef(null);
const [selectedCurrency, setSelectedCurrency] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [demoBalance, setDemoBalance] = useState(100000);
const [tradeDetails, setTradeDetails] = useState({
amount: '',
interval: '5',
});
const [orderHistory, setOrderHistory] = useState([]);
const [notifications, setNotifications] = useState([]);
const [activeMenu, setActiveMenu] = useState(null);
const [currentPrice, setCurrentPrice] = useState(null);
const [timeInterval, setTimeInterval] = useState(5);
const [activeTradesTab, setActiveTradesTab] = useState('Active');
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
const verticalLine = useRef(null);
const currencies = [
'Asia Composite Index', 'Commodity Composite Index', 'Crypto Composite Index',
'Europe Composite Index', 'AUD/USD OTC', 'USD/JPY OTC', 'Quickler',
'EUR/USD OTC', 'GBP/USD OTC', 'AUD/CAD OTC'
];
const { lastMessage } = useWebSocket('ws://docker181472-options.mircloud.host/price', {
onOpen: () => console.log('WebSocket connection established.'),
onClose: () => console.log('WebSocket connection closed.'),
onError: (error) => console.log(`WebSocket error: ${error.message}`),
shouldReconnect: (closeEvent) => true,
});
useEffect(() => {
chart.current = createChart(chartContainerRef.current, {
width: chartContainerRef.current.clientWidth,
height: chartContainerRef.current.clientHeight,
layout: {
background: { color: "#301157" },
textColor: "#C3BCDB",
},
grid: {
vertLines: { color: "#56377D" },
horzLines: { color: "#56377D" },
},
timeScale: {
timeVisible: true,
secondsVisible: true,
},
});
lineSeries.current = chart.current.addLineSeries({
color: '#EEA751',
lineWidth: 2,
priceLineVisible: false,
crossHairMarkerVisible: false,
priceLineColor: '#EEA751',
});
const currentLocale = window.navigator.languages[0];
const myPriceFormatter = Intl.NumberFormat(currentLocale, {
style: "currency",
currency: "USD",
}).format;
const handleResize = () => {
chart.current.resize(chartContainerRef.current.clientWidth, chartContainerRef.current.clientHeight);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chart.current.remove();
};
}, []);
useEffect(() => {
if (lastMessage !== null) {
const data = JSON.parse(lastMessage.data);
const point = { time: data.time / 1000, value: data.price };
if (lineSeries.current) {
lineSeries.current.update(point);
setCurrentPrice(data.price);
}
}
}, [lastMessage]);
const handleMenuItemClick = (menu) => {
setActiveMenu(menu === activeMenu ? null : menu);
};
const filteredCurrencies = currencies.filter(currency =>
currency.toLowerCase().includes(searchQuery.toLowerCase())
);
const handleTradeInputChange = (e) => {
const { name, value } = e.target;
setTradeDetails(prevDetails => ({
...prevDetails,
[name]: value,
}));
if (name === 'interval') {
setTimeInterval(parseInt(value, 10));
}
};
const handleOpenTrade = (direction) => {
const { amount, interval } = tradeDetails;
if (amount && interval && currentPrice !== null) {
const parsedAmount = parseFloat(amount);
const parsedInterval = parseInt(interval, 10) * 1000;
if (!isNaN(parsedAmount)) {
if (parsedAmount > demoBalance) {
setNotifications(prevNotifications => [
...prevNotifications,
'Insufficient balance. Please deposit funds.'
]);
return;
}
const newBalance = demoBalance - parsedAmount;
setDemoBalance(newBalance);
const trade = {
startTime: Date.now(),
interval: parsedInterval,
amount: parsedAmount,
direction,
date: new Date().toLocaleString(),
id: Date.now(),
status: 'pending',
startPrice: currentPrice,
};
setOrderHistory(prevHistory => [...prevHistory, trade]);
setNotifications(prevNotifications => [
...prevNotifications,
`Trade opened: ${direction} ${amount} units`
]);
// Adding vertical lines for trade start and end using markers
const startMarker = {
time: Math.floor(Date.now() / 1000),
position: 'aboveBar',
color: direction === 'Buy' ? 'green' : 'red',
shape: 'arrowUp',
text: 'Start'
};
const endMarker = {
time: Math.floor((Date.now() + parsedInterval) / 1000),
position: 'belowBar',
color: direction === 'Buy' ? 'green' : 'red',
shape: 'arrowDown',
text: 'End'
};
lineSeries.current.setMarkers([startMarker, endMarker]);
setTimeout(() => {
const endPrice = currentPrice;
const priceDifference = endPrice - trade.startPrice;
const profit = direction === 'Buy' ? parsedAmount * priceDifference : -parsedAmount * priceDifference;
const totalProfit = profit;
const newBalanceAfterTrade = newBalance + totalProfit;
setDemoBalance(newBalanceAfterTrade);
setOrderHistory(prevHistory => prevHistory.map(t =>
t.id === trade.id ? { ...t, profit, endPrice, status: 'completed' } : t
));
setNotifications(prevNotifications => [
...prevNotifications,
`Trade completed: ${direction} ${amount} units. Profit: ${profit > 0 ? '+' : ''}$${profit.toFixed(2)}`
]);
}, parsedInterval);
} else {
setNotifications(prevNotifications => [
...prevNotifications,
'Please enter valid data.'
]);
}
} else {
setNotifications(prevNotifications => [
...prevNotifications,
'Please fill in all fields to open a trade.'
]);
}
};
const handleQuickAmount = (amount) => {
setTradeDetails(prevDetails => ({
...prevDetails,
amount: amount.toString(),
}));
};
const handleAmountChange = (delta) => {
setTradeDetails(prevDetails => ({
...prevDetails,
amount: Math.max(0, parseFloat(prevDetails.amount || 0) + delta).toString(),
}));
};
const calculatePotentialPayout = () => {
const amount = parseFloat(tradeDetails.amount || 0);
return (amount + (amount * 0.95)).toFixed(2);
};
return (
<div className="main-container">
<div className="chart-container" ref={chartContainerRef} />
</div>
);
};
export default App;