Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Charts enhancement #145

Merged
merged 30 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
08853c6
chart styling 1st
maelsar Sep 2, 2023
74de128
TF selector buttons
maelsar Sep 2, 2023
53ebf23
Working Chart - before refactoring
maelsar Sep 6, 2023
f7939a6
update2 before refactor
maelsar Sep 6, 2023
aa41128
Merge branch 'DeXter-on-Radix:main' into redux-charts-enhancement
maelsar Sep 10, 2023
a068194
Synced with upstream/main
maelsar Sep 10, 2023
10a0fae
Refactored crosshair data (for legend) to use redux store
maelsar Sep 10, 2023
9c338f8
Moved helper functions to utils. Moved Crosshair data fetching to slice
maelsar Sep 10, 2023
ea0f07b
fixed eslint warning
maelsar Sep 10, 2023
38ea250
removed pairname from selector bar
maelsar Sep 10, 2023
d741fb1
price selector and cursor prices (legend) applied to charts component
maelsar Sep 11, 2023
ba0a17e
added color formatting to legend values and refactored "Change" logic
maelsar Sep 11, 2023
c8f87f0
Updated volumeSeries type to NOT "any" -- Updated Legend Data "Change…
maelsar Sep 12, 2023
2a19b52
refactored volume bars logic and updated color change
maelsar Sep 12, 2023
95de744
added async thunk to update legend values to latest candle values
maelsar Sep 12, 2023
68ead59
Removed unused variables and cleaned up comments
maelsar Sep 13, 2023
ddae4d8
Merge branch 'DeXter-on-Radix:main' into redux-charts-enhancement
maelsar Sep 13, 2023
8af4225
Merge remote-tracking branch 'upstream/main' into redux-charts-enhanc…
maelsar Sep 13, 2023
cfca325
Merge branch 'redux-charts-enhancement' of https://github.com/maelsar…
maelsar Sep 13, 2023
3469da7
Updated to correct after sync.
maelsar Sep 13, 2023
b141a31
Merge branch 'DeXter-on-Radix:main' into redux-charts-enhancement
maelsar Sep 13, 2023
b637a7b
Merge branch 'DeXter-on-Radix:main' into redux-charts-enhancement
maelsar Sep 15, 2023
2045cef
Removed async thunk
maelsar Sep 15, 2023
0b4077c
Removed the gap between the legend bar and chart
maelsar Sep 15, 2023
8a8160e
Merge branch 'DeXter-on-Radix:main' into redux-charts-enhancement
maelsar Sep 23, 2023
590580d
color and some padding changes
Oct 3, 2023
49a982b
cleaned up import
Oct 3, 2023
ccda908
color adjustments
Oct 4, 2023
ad6e637
fixed prettier
Oct 4, 2023
428e680
Merge branch 'main' into charts-enhancement
nguvictor Oct 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 163 additions & 29 deletions src/app/components/PriceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,56 @@ import {
CANDLE_PERIODS,
OHLCVData,
setCandlePeriod,
handleCrosshairMove,
// fetchCandlesForInitialPeriod,
initializeLegend,
} from "../redux/priceChartSlice";
import { useAppDispatch, useAppSelector } from "../hooks";
import { formatPercentageChange } from "../utils";
import { displayAmount } from "../utils";
import * as tailwindConfig from "../../../tailwind.config";

interface PriceChartProps {
data: OHLCVData[];
}

function PriceChartCanvas(props: PriceChartProps) {
const chartContainerRef = useRef<HTMLDivElement>(null);
const dispatch = useAppDispatch();
const { data } = props;
const theme = tailwindConfig.daisyui.themes[0].dark;

useEffect(() => {
const chartContainer = chartContainerRef.current;

// dispatch(fetchCandlesForInitialPeriod());
if (data && data.length > 0) {
dispatch(initializeLegend());
}

if (chartContainer) {
const handleResize = () => {
chart.applyOptions({ width: chartContainer.clientWidth });
};

const chart = createChart(chartContainer, {
width: chartContainer.clientWidth,
height: 450,

// TODO: timescale is not visible
height: 500,
//MODIFY THEME COLOR HERE
layout: {
background: {
color: theme["base-100"],
}, //base-100
textColor: theme["primary-content"],
},
//MODIFY THEME COLOR HERE
grid: {
vertLines: { color: theme["secondary-content"] },
horzLines: { color: theme["secondary-content"] },
},
timeScale: {
//MODIFY THEME COLOR HERE
borderColor: theme["primary-content"],
timeVisible: true,
},
});
Expand All @@ -37,30 +63,53 @@ function PriceChartCanvas(props: PriceChartProps) {
// OHLC
const ohlcSeries = chart.addCandlestickSeries({});
ohlcSeries.setData(clonedData);

ohlcSeries.applyOptions({
wickUpColor: theme["success"], //success
upColor: theme["success"], //success
wickDownColor: theme["error"], //error
downColor: theme["error"], //error
});

chart.priceScale("right").applyOptions({
//MODIFY THEME COLOR HERE
borderColor: theme["primary-content"], //primary-content
scaleMargins: {
top: 0,
bottom: 0.2,
top: 0.1,
bottom: 0.3,
},
});

// Volume
// Volume Initialization
const volumeSeries = chart.addHistogramSeries({
priceFormat: {
type: "volume",
},
priceScaleId: "volume",
color: "#eaeff5",
});

volumeSeries.setData(clonedData);
// VOLUME BARS
// MODIFY THEME COLOR HERE
volumeSeries.setData(
data.map((datum) => ({
...datum,
color:
datum.close - datum.open <= 0 ? theme["error"] : theme["success"], //error : success
}))
);

// volumeSeries.setData(clonedData);
chart.priceScale("volume").applyOptions({
scaleMargins: {
top: 0.8,
bottom: 0,
bottom: 0.01,
},
});

//Crosshair Data for legend
dispatch(handleCrosshairMove(chart, data, volumeSeries));

//Prevent Chart from clipping
const chartDiv = chartContainer.querySelector(".tv-lightweight-charts");
if (chartDiv) {
(chartDiv as HTMLElement).style.overflow = "visible";
Expand All @@ -70,37 +119,122 @@ function PriceChartCanvas(props: PriceChartProps) {

return () => {
window.removeEventListener("resize", handleResize);
// clearInterval(intervalId);
chart.remove();
};
}
}, [data]);

return <div ref={chartContainerRef} />;
}, [data, dispatch]);
//Temporary brute force approach to trim the top of the chart to remove the gap
return <div ref={chartContainerRef} className="relative mt-[-1.7rem]"></div>;
}

export function PriceChart() {
const state = useAppSelector((state) => state.priceChart);
const dispatch = useAppDispatch();
const candlePeriod = useAppSelector((state) => state.priceChart.candlePeriod);
const candlePrice = useAppSelector(
(state) => state.priceChart.legendCandlePrice
);
const change = useAppSelector((state) => state.priceChart.legendChange);
const percChange = useAppSelector(
(state) => state.priceChart.legendPercChange
);
const currentVolume = useAppSelector(
(state) => state.priceChart.legendCurrentVolume
);
const isNegativeOrZero = useAppSelector(
(state) => state.priceChart.isNegativeOrZero
);
const noDigits = 4;
const decimalSeparator = ".";
const thousandSeparator = ",";
const fixedDecimals = 3;

return (
<div>
<label htmlFor="candle-period-selector">Candle Period:</label>
<select
className="select select-ghost"
id="candle-period-selector"
value={state.candlePeriod}
onChange={(e) => {
dispatch(setCandlePeriod(e.target.value));
}}
>
{CANDLE_PERIODS.map((period) => (
<option key={period} value={period}>
{period}
</option>
))}
</select>

<div className="">
<div className="flex">
{CANDLE_PERIODS.map((period) => (
<button
key={period}
className={`btn btn-sm ${
candlePeriod === period ? "text-accent" : ""
}`}
onClick={() => dispatch(setCandlePeriod(period))}
>
{period}
</button>
))}
</div>
<div className="flex justify-between text-sm">
<div className="ml-4">
Open:{" "}
<span>
{displayAmount(
candlePrice?.open || 0,
noDigits,
decimalSeparator,
thousandSeparator,
fixedDecimals
)}
</span>
</div>
<div>
High:{" "}
<span>
{displayAmount(
candlePrice?.high || 0,
noDigits,
decimalSeparator,
thousandSeparator,
fixedDecimals
)}
</span>
</div>
<div>
Low:{" "}
<span>
{displayAmount(
candlePrice?.low || 0,
noDigits,
decimalSeparator,
thousandSeparator,
fixedDecimals
)}
</span>
</div>
<div>
Close:{" "}
<span>
{displayAmount(
candlePrice?.close || 0,
noDigits,
decimalSeparator,
thousandSeparator,
fixedDecimals
)}
</span>
</div>
<div>
Volume:{" "}
<span>
{displayAmount(
currentVolume,
noDigits,
decimalSeparator,
thousandSeparator,
fixedDecimals
)}
</span>
</div>
<div className="mr-4">
Change:{" "}
<span className={isNegativeOrZero ? "text-error" : "text-success"}>
{change}
{formatPercentageChange(percChange)}
</span>
</div>
</div>
</div>
<PriceChartCanvas data={state.ohlcv} />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Home() {
<div className="col-span-12 lg:col-span-5 xl:col-span-3 order-2 lg:order-1 text-center lg:border-r-4 border-base-300">
<OrderInput />
</div>
<div className="col-span-12 min-h-[200px] lg:col-span-7 xl:col-span-6 order-1 lg:order-2 text-center xs:border-b-4 lg:border-b-0 border-base-300">
<div className="col-span-12 p-2 lg:col-span-7 xl:col-span-6 order-1 lg:order-2 text-center xs:border-b-4 lg:border-b-0 border-base-300">
<PriceChart />
</div>
</div>
Expand Down
Loading