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

Add Geom.candlestick #1215

Merged
merged 19 commits into from
Feb 19, 2023
Merged

Add Geom.candlestick #1215

merged 19 commits into from
Feb 19, 2023

Conversation

iblislin
Copy link
Contributor

@iblislin iblislin commented Oct 24, 2018

Contributor checklist:

  • I've updated the documentation to reflect these changes
  • I've added and/or updated the unit tests
  • I've run the regression tests
  • I've squash'ed or fixup'ed junk commits with git-rebase
  • I've built the docs and confirmed these changes don't cause new errors

This PR

  • add Geom.candlestick()

Example

using Gadfly, MarketData
ta = ohlc[1:40]
plot(
    x     = timestamp(ta),
    open  = values(ta.Open),
    high  = values(ta.High),
    low   = values(ta.Low),
    close = values(ta.Close),
    Geom.candlestick,
    Scale.color_discrete_manual("green", "red"))

k

TODO

  • inverse color, if close price < open price Just let user set the desired colour via Scale

@codecov-io
Copy link

codecov-io commented Oct 24, 2018

Codecov Report

Merging #1215 into master will increase coverage by 0.44%.
The diff coverage is 90%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1215      +/-   ##
==========================================
+ Coverage   90.31%   90.75%   +0.44%     
==========================================
  Files          39       39              
  Lines        4242     4512     +270     
==========================================
+ Hits         3831     4095     +264     
- Misses        411      417       +6
Impacted Files Coverage Δ
src/aesthetics.jl 81.3% <ø> (ø) ⬆️
src/geom/boxplot.jl 97.22% <90%> (-2.78%) ⬇️
src/statistics.jl 97.19% <0%> (+0.27%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3a2e115...4c2bbe1. Read the comment docs.

@iblislin
Copy link
Contributor Author

I tried to plot a dataframe with 20 rows,
got the following plot.
The number of candlesticks is correct, but the spacing between them is weird.
Any ideas?

https://gist.github.com/iblis17/7fc5a709eb201f8bbc6a7b6faa4479c4

@bjarthur
Copy link
Member

the x-axis is a date, and it's skipping the weekends

@bjarthur
Copy link
Member

needs a test, and maybe sth for the gallery (say TSLA after musk was charged by SEC?). otherwise, lgtm. @Mattriks @tlnagy thoughts?

@iblislin
Copy link
Contributor Author

iblislin commented Oct 24, 2018

the x-axis is a date, and it's skipping the weekends

is it able to remove the weekends in the render function?

@iblislin
Copy link
Contributor Author

needs a test, and maybe sth for the gallery (say TSLA after musk was charged by SEC?).

will do, but I need to figure out my TODO (inverse color) first.

BoxplotGeometry(Gadfly.Stat.boxplot(method=method), suppress_outliers, tag)
function BoxplotGeometry(; method=:tukey, suppress_outliers=false, suppress_fences=false,
candlestick=false, tag=empty_tag)
stat = candlestick ? Gadfly.Stat.identity() : Gadfly.Stat.boxplot(method=method)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the use of candlestick as a Bool here. For a better example of the preferred style, see the Gadfly polygon.jl file, and Geom.ellipse . Note there is no ellipse::Bool within struct PolygonGeometry.

@iblislin
Copy link
Contributor Author

What kind of API design is suitable for this case:
I want that user can configure a pair of colors for (1) open > close and (2) open < close.
(because I found that the inverting color automatically is difficault in this case, that is,
inverting the RGB of red != RGB of green. But in stock market, green and red are common for drawing candlestick.)

@Mattriks
Copy link
Member

Here is the Gadfly way. The idea is to write an apply_statistic function for a CandlestickStatistic. You'll find examples in Gadfly's statistics.jl file, where the apply_statistic function adds a color_scale. The end of theapply_statistic function will look something like in the code below. To test, run the uncommented lines directly in julia, and look at the aes object. You'll see a color scale has been added, which (in this example) contains red and blue at the appropriate values of hlgroup. Magic!

# scales and aes are function arguments, which I simulate here
# You can use aes.open and aes.close, rather than the hinges which I use here 

scales = Dict(:color=>Scale.color_discrete_manual("red","blue"))
aes = Gadfly.Aesthetics()
aes.upper_hinge = rand(10)  
aes.lower_hinge = rand(10) 

# The apply_statistic function:
# function apply_statistic(stat::CandlestickStatistic, scales, coord, aes)

    hlgroup = aes.upper_hinge .> aes.lower_hinge
    color_scale = get(scales, :color, Scale.color_discrete())
    Scale.apply_scale(color_scale, [aes], Gadfly.Data(color = hlgroup))
# end    

aes.color

@Mattriks
Copy link
Member

Also test this example (replace the appropriate lines above):

 scales = Dict{Symbol, Gadfly.ScaleElement}() 
 
 color_scale = get(scales, :color, Scale.color_discrete_manual("red","green"))

Does it make sense?

@iblislin
Copy link
Contributor Author

cool, I ran them in my REPL. looks fine. And I just need time to understand what happened.

@iblislin iblislin closed this Mar 28, 2020
@iblislin iblislin reopened this Mar 28, 2020
@iblislin iblislin closed this Mar 28, 2020
@iblislin iblislin reopened this Mar 28, 2020
@iblislin iblislin changed the title WIP: add Geom.candlestick Add Geom.candlestick Mar 28, 2020
@iblislin
Copy link
Contributor Author

iblislin commented Mar 28, 2020

This PR is ready for review.

I think the Travis CI failures are unrelated to this PR.

@iblislin
Copy link
Contributor Author

iblislin commented Mar 30, 2020

I added an example png.

@tlnagy
Copy link
Member

tlnagy commented Mar 31, 2020

@bjarthur and @Mattriks, this looks good to go to me.

Comment on lines 108 to 121
```@example
using Gadfly, MarketData
set_default_plot_size(21cm, 8cm)
ta = ohlc[1:50]
plot(
x = timestamp(ta),
open = values(ta.Open),
high = values(ta.High),
low = values(ta.Low),
close = values(ta.Close),
Geom.candlestick,
Scale.color_discrete_manual("green", "red")
)
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doc example is identical to the tests. nice things about tests are that they can double as documentation, but are most useful in that capacity only if they present different examples than in the actual docs. how about using one of the large historical data sets from MarketData here instead? i like having real data, instead of synthetic fake data, in docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I will make the example more complete.
I ran into an issue: there are some unwanted gaps on weekend days, if I set the x-axis as a series of Date. Is there a way to remove that gaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the example in doc.

@codecov-commenter
Copy link

codecov-commenter commented Aug 8, 2020

Codecov Report

Merging #1215 into master will increase coverage by 0.02%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1215      +/-   ##
==========================================
+ Coverage   89.32%   89.35%   +0.02%     
==========================================
  Files          39       39              
  Lines        4395     4404       +9     
==========================================
+ Hits         3926     3935       +9     
  Misses        469      469              
Impacted Files Coverage Δ
src/aesthetics.jl 77.77% <ø> (ø)
src/geom/boxplot.jl 100.00% <100.00%> (ø)
src/statistics.jl 96.47% <100.00%> (+0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d507b20...4366e97. Read the comment docs.

@iblislin
Copy link
Contributor Author

iblislin commented Aug 9, 2020

Good to go?

@iblislin
Copy link
Contributor Author

new example:
new

Project.toml Show resolved Hide resolved
@bjarthur
Copy link
Member

this looks good to me. @Mattriks ?

@Mattriks
Copy link
Member

Hopefully I'll get around to looking at this soon (this week).

@iblislin
Copy link
Contributor Author

iblislin commented Nov 3, 2020

any updates?

@codecov-io
Copy link

codecov-io commented Dec 27, 2020

Codecov Report

Merging #1215 (c741f56) into master (904078a) will increase coverage by 0.02%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1215      +/-   ##
==========================================
+ Coverage   89.62%   89.64%   +0.02%     
==========================================
  Files          39       39              
  Lines        4596     4606      +10     
==========================================
+ Hits         4119     4129      +10     
  Misses        477      477              
Impacted Files Coverage Δ
src/aesthetics.jl 78.94% <ø> (ø)
src/geom/boxplot.jl 100.00% <100.00%> (ø)
src/statistics.jl 96.48% <100.00%> (+0.01%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 904078a...c741f56. Read the comment docs.

@iblislin
Copy link
Contributor Author

I resolved the conflict.
Good to go?

@btbvoy
Copy link

btbvoy commented Feb 13, 2023

@iblislin @Mattriks @bjarthur @tlnagy could you please share any reasons this PR left abandoned and whether there is any chance to make it happen? It looks like it was ready to be merged for quite some time, but due to a long period of inactivity there are some other conflicts. Thanks in advance

@bjarthur
Copy link
Member

this still LGTM. @Mattriks fine by you?

to resolve the conflict, i think the dep on MarketData simply needs to be added to test/Project.toml, and the main Project.toml needs to be left unchanged.

@bjarthur bjarthur merged commit 8934178 into GiovineItalia:master Feb 19, 2023
@bjarthur
Copy link
Member

accidentally merged while resolving conflict, but it looks good so i think it's okay. can always fix later if there's a problem.

i couldn't get the docs to build locally though, and the dev version online doesn't seem to have updated. unrelated to this PR though i think. will look into it later. the method signature of render_discrete_key seems to need to be expanded to AbstractString:

$ julia make.jl 
[ Info: SetupBuildDirectory: setting up build directory.
[ Info: Doctest: running doctests.
[ Info: ExpandTemplates: expanding markdown templates.
ERROR: LoadError: MethodError: no method matching render_discrete_key(::Vector{InlineStrings.String7}, ::Context, ::Measures.AbsoluteLength, ::Theme; sizes=Measures.AbsoluteLength[1.0583333333333331mm, 1.6462962962962961mm, 2.234259259259259mm, 2.822222222222222mm], colors=ColorTypes.Colorant[])
Closest candidates are:
  render_discrete_key(::Vector{String}, ::Context, ::Measure, ::Theme; colors, aes_color_label, shapes, sizes) at ~/.julia/dev/Gadfly/src/guide/keys.jl:72
Stacktrace:
  [1] render(guide::Gadfly.Guide.SizeKey, theme::Theme, aes::Gadfly.Aesthetics)
    @ Gadfly.Guide ~/.julia/dev/Gadfly/src/guide/keys.jl:247
  [2] render(guide::Gadfly.Guide.SizeKey, theme::Theme, aes::Gadfly.Aesthetics, dynamic::Bool)
    @ Gadfly.Guide ~/.julia/dev/Gadfly/src/guide.jl:143
  [3] render_prepared(plot::Plot, coord::Gadfly.Coord.Cartesian, plot_aes::Gadfly.Aesthetics, layer_aess::Vector{Gadfly.Aesthetics}, layer_stats::Vector{Vector{Gadfly.StatisticElement}}, layer_subplot_aess::Vector{Vector{Gadfly.Aesthetics}}, layer_subplot_datas::Vector{Vector{Gadfly.Data}}, scales::Dict{Symbol, Gadfly.ScaleElement}, guides::Vector{Gadfly.GuideElement}; table_only::Bool, dynamic::Bool)
    @ Gadfly ~/.julia/dev/Gadfly/src/Gadfly.jl:813
  [4] render_prepared(plot::Plot, coord::Gadfly.Coord.Cartesian, plot_aes::Gadfly.Aesthetics, layer_aess::Vector{Gadfly.Aesthetics}, layer_stats::Vector{Vector{Gadfly.StatisticElement}}, layer_subplot_aess::Vector{Vector{Gadfly.Aesthetics}}, layer_subplot_datas::Vector{Vector{Gadfly.Data}}, scales::Dict{Symbol, Gadfly.ScaleElement}, guides::Vector{Gadfly.GuideElement})
    @ Gadfly ~/.julia/dev/Gadfly/src/Gadfly.jl:782
  [5] render(plot::Plot)
    @ Gadfly ~/.julia/dev/Gadfly/src/Gadfly.jl:740
  [6] draw
    @ ~/.julia/dev/Gadfly/src/Gadfly.jl:843 [inlined]
  [7] show(io::IOBuffer, m::MIME{Symbol("text/html")}, p::Plot)
    @ Gadfly ~/.julia/dev/Gadfly/src/Gadfly.jl:933
  [8] __binrepr(m::MIME{Symbol("text/html")}, x::Plot, context::Nothing)
    @ Base.Multimedia ./multimedia.jl:159
  [9] _textrepr
    @ ./multimedia.jl:151 [inlined]
 [10] #stringmime#8
    @ /Applications/Julia-1.8.app/Contents/Resources/julia/share/julia/stdlib/v1.8/Base64/src/Base64.jl:43 [inlined]
 [11] stringmime(m::MIME{Symbol("text/html")}, x::Plot)
    @ Base64 /Applications/Julia-1.8.app/Contents/Resources/julia/share/julia/stdlib/v1.8/Base64/src/Base64.jl:43
 [12] display_dict(x::Plot)
    @ Documenter.Utilities ~/.julia/packages/Documenter/bFHi4/src/Utilities/Utilities.jl:627
 [13] #invokelatest#2
    @ ./essentials.jl:729 [inlined]
 [14] invokelatest
    @ ./essentials.jl:726 [inlined]
 [15] runner(#unused#::Type{Documenter.Expanders.ExampleBlocks}, x::Markdown.Code, page::Documenter.Documents.Page, doc::Documenter.Documents.Document)
    @ Documenter.Expanders ~/.julia/packages/Documenter/bFHi4/src/Expanders.jl:582
 [16] dispatch(::Type{Documenter.Expanders.ExpanderPipeline}, ::Markdown.Code, ::Vararg{Any})
    @ Documenter.Utilities.Selectors ~/.julia/packages/Documenter/bFHi4/src/Utilities/Selectors.jl:170
 [17] expand(doc::Documenter.Documents.Document)
    @ Documenter.Expanders ~/.julia/packages/Documenter/bFHi4/src/Expanders.jl:42
 [18] runner(#unused#::Type{Documenter.Builder.ExpandTemplates}, doc::Documenter.Documents.Document)
    @ Documenter.Builder ~/.julia/packages/Documenter/bFHi4/src/Builder.jl:227
 [19] dispatch(#unused#::Type{Documenter.Builder.DocumentPipeline}, x::Documenter.Documents.Document)
    @ Documenter.Utilities.Selectors ~/.julia/packages/Documenter/bFHi4/src/Utilities/Selectors.jl:170
 [20] #2
    @ ~/.julia/packages/Documenter/bFHi4/src/Documenter.jl:249 [inlined]
 [21] cd(f::Documenter.var"#2#3"{Documenter.Documents.Document}, dir::String)
    @ Base.Filesystem ./file.jl:112
 [22] makedocs(; debug::Bool, format::Documenter.Writers.HTMLWriter.HTML, kwargs::Base.Pairs{Symbol, Any, NTuple{4, Symbol}, NamedTuple{(:modules, :clean, :sitename, :pages), Tuple{Vector{Module}, Bool, String, Vector{Any}}}})
    @ Documenter ~/.julia/packages/Documenter/bFHi4/src/Documenter.jl:248
 [23] top-level scope
    @ ~/.julia/dev/Gadfly/docs/make.jl:3
in expression starting at /Users/arthurb/.julia/dev/Gadfly/docs/make.jl:3

@Mattriks
Copy link
Member

@bjarthur I can add the change to render_discrete_key in my PR #1606.

@bjarthur
Copy link
Member

thanks @iblislin . sorry this took so long

@bjarthur
Copy link
Member

bjarthur commented Feb 20, 2023

thanks @Mattriks . i think i got it though with #1607. changes were needed for Compose too. let me know what you think and i'll merge.

@iblislin
Copy link
Contributor Author

🎉

@iblislin iblislin deleted the ib/candlestick branch February 22, 2023 01:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants