JuliaPlots / StatsPlots.jl

Statistical plotting recipes for Plots.jl
Other
437 stars 88 forks source link

Add support to TimeSeries #37

Closed juliohm closed 7 years ago

juliohm commented 7 years ago

Does the package support TimeSeries? The code below produces an error.

using TimeSeries
using StatPlots

dates  = collect(Date(1999,1,1):Date(2000,12,31))
series = TimeArray(dates, rand(length(dates)))

plot(series)
mkborregaard commented 7 years ago

No, but it should be straightforward to make a Type recipe that replaces a TimeArray with an Array{DataTime} object (which is accepted by Plots). You can see how to do this here: https://juliaplots.github.io/recipes/

juliohm commented 7 years ago

Thank you @mkborregaard, I am gonna read the docs for Plots recipes.

milktrader commented 7 years ago

@juliohm if you get a working recipe example I'd love to add it to the TimeSeries docs.

mkborregaard commented 7 years ago

@milktrader You might even consider adding it to the package itself? The idea of recipes is to be able to add plotting capability in the lightest way possible, so a Type recipe requires no dependency on Plots.jl – only on RecipesBase, which is a single macro. I personally think it is an elegant solution to the issue of adding generalized plotting capability to packages in a package ecosystem with multiple plotting packages.

juliohm commented 7 years ago

@mkborregaard thank you for sharing this idea, it is super useful. So basically, we can add plotting capabalities as recipes in any package without depending on the plot packages/backends. I will read RecipesBase.jl to implement visualization in my packages.

pkofod commented 7 years ago

Yes that is exactly the idea behind the package. I think a simple plot of a time series should be a recipe in the time series package(s).

milktrader commented 7 years ago

I'm on board with this. Plotting is integral to time series analysis but the brittle nature of plotting packages has prevented depending on them. If a simple recipe that depends on a lean package will work I'm all for it.

mkborregaard commented 7 years ago

Right, catch us on gitter (or here) if you need any help with the recipes :-)

juliohm commented 7 years ago

@milktrader please let us know when we can close this issue. Thanks.

milktrader commented 7 years ago

I can open an issue, reference this one and then you can close it.

milktrader commented 7 years ago

@juliohm I've added a list of TODO, please feel free to contribute to any of those items, preferably starting with the recipe code.

mkborregaard commented 7 years ago

It could be as simple as

using RecipesBase
@recipe f{T<:TimeArray}(ta::T) = ta.timestamp, ta.values

which seems to work:

using TimeSeries, MarketData, Plots
plot(MarketData.ohlc)

But there are possibilities of setting more default values etc. ping @milktrader @juliohm

mkborregaard commented 7 years ago

You could set sensible values for legend labels with something like:

using RecipesBase, TimeSeries
@recipe function f{T<:TimeArray}(ta::T)
       labels --> reshape(ta.colnames,1,length(ta.colnames))
       seriestype --> :path
       ta.timestamp, ta.values
end

using Plots, MarketData
gr() # or whichever plotting package the user may like that has a Plots 'backend'
plot(MarketData.ohlc)

defaultplot

Note that I didn't choose the best example - as Plots already has a specialized plotting type for OHLC. Of course since it isn't a special type in MarketData there is no way to dispatch on it automatically. But you could, in this case, convert it manually:

mo = MarketData.ohlc
moplot = OHLC.([(mo.values[i,:]...) for i in 1:size(mo.values,1)])
plot(moplot)

ohlcplot

mkborregaard commented 7 years ago

@juliohm are you still meaning to contribute this recipe to TimeArrays? @milktrader do you think ohlc is such an important use case that it warrants a special plotting recipe in MarketData (note that the code bit I posted currently does not have dates on the x axis)? Or is that first plot reasonable as the default for all TimeArrays?

juliohm commented 7 years ago

Hi @mkborregaard, I don't have time to contribute to it now, if you want to do it, please go ahead :)

Since @milktrader opened an issue in the TimeSeries repo, I am closing this one in StatPlots.

Thanks for pinging.

milktrader commented 7 years ago

OHLC is important in financial time series. The most common depiction is a candlestick with the body representing the open/close and up/down wicks representing high/low. The body will be red/green depending on whether open or close was higher.

milktrader commented 7 years ago

Ohlc charts years ago followed this pattern. Another cool depiction is the Heikin Ashi candlesticks

mkborregaard commented 7 years ago

There is a depiction for this already in Plots, as shown above (@tbreloff was a financial analyst, I think). Here is a zoom: ohlc I think it is quite cool. The way to get this to plot automagically is to define these objects as an OHLC type in MarketData, which can then get a type recipe that simply converts it to the OHLC type in Plots. I guess this only makes sense if you want to specialize other functions on OHLC, though. If it is a common enough usecase the user recipe could perhaps check for the names Open, Low, High and Close and do the conversion to an OHLC.

tbreloff commented 7 years ago

Might also be able to pass in a list of 4-tuples with seriestype = :ohlc? (At least... that worked sometime in the past...)

On Mon, Feb 13, 2017 at 5:38 PM Michael Krabbe Borregaard < notifications@github.com> wrote:

There is a depiction for this already in Plots, as shown above (@tbreloff https://github.com/tbreloff was a financial analyst, I think). Here is a zoom: [image: ohlc] https://cloud.githubusercontent.com/assets/8429802/22906357/6ee91d2a-f244-11e6-9954-c9fba834acc2.png I think it is quite cool. The way to get this to plot automagically is to define these objects as an OHLC type in MarketData, which can then get a type recipe that simply converts it to the OHLC type in Plots. I guess this only makes sense if you want to specialize other functions on OHLC, though. If it is a common enough usecase the user recipe could perhaps check for the names Open, Low, High and Close and do the conversion to an OHLC.

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/JuliaPlots/StatPlots.jl/issues/37#issuecomment-279546511, or mute the thread https://github.com/notifications/unsubscribe-auth/AA492lC9S5F9PZnBmHZgzoed7957QsDQks5rcNtRgaJpZM4LtHV7 .

mkborregaard commented 7 years ago

That works - that is (almost) what I did here: https://github.com/JuliaPlots/StatPlots.jl/issues/37#issuecomment-277936721 I was just wondering whether the creation of the 4-tuple could be hid under the hood.

mkborregaard commented 7 years ago

@milktrader you could define a recipe for candlestick using segments and boxes similar to how boxplots are built in StatPlots https://github.com/JuliaPlots/StatPlots.jl/blob/master/src/boxplot.jl. It could be called with a singleton type used only for dispatch. Here is a really fast hack to show the principle (I have cheated here by just calling boxplot)

type OHLCbox end
@recipe function f{T<:TimeArray}(ta::T, ::Type{OHLCbox})
              seriestype --> :boxplot
              size(ta.values,2) == 4 || error("ohlc series have 4 columns")
              vcat([ta.timestamp for i in 1:4]...), vcat([ta.values[:,i] for i in 1:4]...)
       end

using StatPlots, MarketData
mo = MarketData.ohlc[1:100]
plot(mo, OHLCbox)

ohlcbox

mkborregaard commented 7 years ago

@milktrader if there are candlesticks for ohlc, is there also a special format for OHLCV?

mkborregaard commented 7 years ago

x-ref https://github.com/JuliaStats/TimeSeries.jl/pull/306/files

milktrader commented 7 years ago

No format (maybe something esoteric I haven't seen) for OHLCV