ScottPlot / ScottPlot

Interactive plotting library for .NET
https://ScottPlot.net
MIT License
5.35k stars 864 forks source link

Improve finance demo #4483

Open swharden opened 1 week ago

swharden commented 1 week ago

This issue tracks short term goals to improve financial charting resulting from a discussion today with @quantfreedom and @VladislavPustovarov

Task 1: Scott

Task 2: Vlad

YearOnly - Year
YearAndJuly - Year, July
YearAnd2Months - Year, May, Sep
YearAnd3Months - Year, Apr, Jul, Oct
YearAnd4Months - Year, Mar, May, Jul, Sep, Nov
Months - Year, then each month
MonthAndThirdMonday - months are short names, days are the number
MonthAndEveryMonday - months are short names, days are the number
MonthAndEveryOtherDay - months are short names, days are the number
MonthAndEveryDay - months are short names, days are the number
DayAndEvery12Hours - show the day number for midnight, then show 6, 12, 18
DayAndEvery4Hours
DayAndEvery3Hours
EveryHour
EveryHalfHour
Every15Minutes
Every5Minutes
EveryMinute
Seconds45
Seconds30
Seconds15
Seconds10
Seconds5
Seconds1

Task 3: Scott

This work will be performed after Vlad finishes the date work above

quantfreedom commented 1 week ago

vlad has two accounts and the one he uses for his personal project is

VladislavPustovarov

so you can change the at to match that

swharden commented 1 week ago

@quantfreedom and @VladislavPustovarov - got it, thanks!

swharden commented 1 week ago

I added cursor tracking with highlighted labels and candle snapping in the horizontal axis

I'll keep working in #4484

snap2

quantfreedom commented 1 week ago

tenor holy jesus ... this thing is looking so damn sexy ... this is beyond nuts that all of this is even happening ... insane

swharden commented 1 week ago

@VladislavPustovarov I have an important message about performance.

Performance is indeed very bad when displaying 50,000 candles. However, I think this should never be done. Showing so many distinct candles on a 800px wide chart means each candle is less than 0.02 pixels wide.

I highly recommend detecting when you are trying to display an extremely large number of candles and re-binning the data so that candles are at least 1 pixel wide or wider. I don't think a user will ever want to see second-width candles on a years-wide plot.

For example, TradingView limits how far you can zoom in or out based on the width of candles. This prevents more than a few thousand candles from ever being seen at one time. Users who want to see more time can increase the candle width.

If you implement this strategy in your application, it will be extremely performant. Also, no modification to ScottPlot is required to achieve this high performance.

Let me know if you have any questions about this topic! I am updating the demo app in #4484 to show render time and frame rate at the top in the title area. It's interesting to see how poor performance gets when candles are significantly smaller than 1 pixel wide.

candlePerf2

swharden commented 1 week ago

Resizing Behavior

I'm writing this down to put words to the behavior that needs to change.

Presently, resizing the window "squeezes" the plot so there is no change in axis limits. For financial charting, this is not the desired behavior. resize1

The chart should respond to resize events by "sliding" data off the screen to the left to achieve a fixed "distance per pixel" ratio through resize events resize2

swharden commented 1 week ago

I figured out a way to implement this behavior using a custom axis rule

This completes my list of tasks so I'm going to merge #4484 which will close this issue, but we can still use this issue for conversation and taking notes even after it's closed.

Let's can pick up our discussion again after you make progress on improving the data tick logic

Feel free to reach out in the meantime if you have any questions 👍

resize3

quantfreedom commented 1 week ago

having some issues with the candle width remaining stable as you zoom in and out

as you stay in one place and zoom out and zoom in each time the candle width gets smaller and smaller even though we are just zooming back and forth.

This causes a problem with being able to properly set the display on the x axis based on candle width

Sandbox WinFormsFinance_k2ByYdsu1C

Also when you zoom in and out ... lets say you zoom out and the x axis changes ... it seems like you have to zoom in 3 times for it to change to the next level then you have to zoom out 3 more times for it to change but i am guessing it should always just be one zoom to change from one level to the next

QuantFreedom Av Plotting_aMcmcccmZ3

@VladislavPustovarov can you comment on this so that you can get updates

quantfreedom commented 1 week ago

also the candle width seems to be getting smaller and smaller as you zoom in and out in one place QuantFreedom Av Plotting_LzyZP59XEs

public virtual void Render(RenderPack rp)
    {
        if (DateTimes.Length == 0)
            return;

        // allow drawing outside the data area
        rp.CanvasState.DisableClipping();

        // get the best tick generator given the field of view
        int minIndexInView = (int)(Math.Max(0, Axes.XAxis.Range.Min));
        int maxIndexInView = (int)(Math.Min(DateTimes.Length - 1, Axes.XAxis.Range.Max));
        if (maxIndexInView <= minIndexInView) return;
        TimeSpan timeSpanInView = DateTimes[maxIndexInView] - DateTimes[minIndexInView];
        IFinancialTickGenerator tickGenerator = GetBestTickGenerator(timeSpanInView, rp.DataRect.Width);
        List<(int, string)> ticks = tickGenerator.GetTicks(DateTimes, minIndexInView, maxIndexInView);

        widthOfCandleInPixels = rp.DataRect.Width / Axes.XAxis.Range.Span;
        // render each tick label
        using SKPaint paint = new();
        foreach ((int x, string label) in ticks)
        {
            Pixel px = new(Axes.XAxis.GetPixel(x, rp.DataRect), rp.DataRect.Bottom);
            LabelStyle.Render(rp.Canvas, px, paint, label);
        }
    }

    private IFinancialTickGenerator GetBestTickGenerator(TimeSpan timeSpan, float widthInPixels)
    {
        // adjust the scale so small plots show fewer ticks
        double scaledViewDays = timeSpan.TotalDays * 600 / widthInPixels;
        var maxWidth = LabelStyle.Measure(labelFormat).Size.Width;

        Trace.WriteLine(widthOfCandleInPixels);
        Trace.WriteLine(maxWidth);
        Trace.WriteLine(maxWidth / widthOfCandleInPixels);
        Trace.WriteLine("");

        var numCandles = maxWidth / widthOfCandleInPixels;

        if (widthOfCandleInPixels > 85)
        {
            return new EachCandleValue();
        }
        else if (widthOfCandleInPixels > 40)
        {
            return new EveryOtherCandle();
        }
        else if (widthOfCandleInPixels > 20)
        {
            return new EveryFourthCandle();
        }
        else if (scaledViewDays < 180)
        {
            return new MonthsAndMondays();
        }
        else if (scaledViewDays < 360 * 2)
        {
            return new Months();
        }
        else
        {
            return new Years();
        }
    }

also there is a thing that the zoom doesn't update the x axis unless you click on the chart or interact with it in some way QuantFreedom Av Plotting_G1itruCpBt

tiger2014 commented 1 week ago

@swharden

Thank you for providing such high-quality charting solutions for the C# community.

As a financial hobbyist, I often need to plot candlestick charts with volume and add indicators like EMA, RSI, and others. Let me outline the common requirements for financial charting:

  1. The typical financial candlestick format includes: time, open, high, low, close, and volume.
  2. Since financial charts have non-continuous time intervals, it would be ideal to handle non-continuous time automatically without requiring manual adjustments. If the data includes volume, the chart should automatically generate volume bars without extra steps. For example, a single line like Plot.Add.Candlestick(candles) could generate a candlestick chart with volume.

image

  1. Convenient support for adding subplots, as indicators like RSI are often displayed in subplots.

image

  1. Most indicators are lines, so a simple way to plot lines would be highly valuable.
swharden commented 1 week ago

Hi @tiger2014, thank you for your feedback! I can see we have a growing collection of developers who are interested in seeing ScottPlot improve support for financial charts, and I'm excited to keep working to make it better!

Part of my effort is directed at adding these new features, and part of my effort is also supporting others as they contribute new features, and also to provide some direction about which features to work on in which order. Right now we are focused on adding better support for dates and times (the content of the discussion above), and I probably will wait until that contribution is submitted before putting more of my direct effort into improving financial charting, but it is a goal of mine for sure.

Based on your feedback, in my next iteration of the financial demo I'll add volume bars and also separate plots like you show to show SMA, RSI, and other similar indicators. Thanks for this suggestion!

swharden commented 1 week ago

Based on your feedback, in my next iteration of the financial demo I'll add volume bars and also separate plots like you show to show SMA, RSI, and other similar indicators. Thanks for this suggestion!

I modified the "to-do" list at the top of this issue to add my work after Vlad's. I'll re-open this issue and use it to track the progress Vlad is making toward improved support for date axis labels.

swharden commented 6 days ago

as you stay in one place and zoom out and zoom in each time the candle width gets smaller and smaller even though we are just zooming back and forth.

When you say the candles "get smaller", you mean "each candle occupies fewer pixels horizontally on the screen as you zoom out", right? Assuming this is the case, I think this is the intended behavior. It's what TradingView does when you zoom in and out.

This causes a problem with being able to properly set the display on the x axis based on candle width

There are few different ways "choosing the best horizontal tick labels" could be accomplished, but my recommendation is to not have them at all related to candle width. I would assume that a high quality chart (with nice date ticks) should be able to be made even if no candles are displayed, or if a scatter plot is displayed. Decoupling these two concepts may make the problem simpler?

"Choosing the best horizontal ticks" is probably only dependent on (1) how wide the chart is (in pixels) and (2) how much time the visible area of the chart spans.

If this task becomes too difficult to implement, let me know and I'll try to put some time into doing the "task 2" list above. If I hit a wall too maybe we can come up with a different solution.

also the candle width seems to be getting smaller and smaller as you zoom in and out in one place

Let's set this issue aside for now because a future refinement will be to make zooming automatically "snap" axis limits to edges that are between candles so zooming will not result in situations where candles run off the screen appearing cut in half

also there is a thing that the zoom doesn't update the x axis unless you click on the chart or interact with it in some way

After changing axis limits, call formsPlot1.Refresh() to force a redraw


Summarizing what comes next, focus hard on implementing those different tick views outlined above in task 2, and if you get stuck or think the design is not sufficient then let me know and I can start working on that myself 👍

swharden commented 6 days ago

also the candle width seems to be getting smaller and smaller as you zoom in and out in one place

Fixed in #4516 / #4520