mplfinance icon indicating copy to clipboard operation
mplfinance copied to clipboard

Set single candle color

Open FabioMarinelli99 opened this issue 3 years ago • 8 comments

Looking ad this, it seems like in the old API version you could set the color of a single candle, is there a way to do the same with the current version without using the function of the old version?? (i v already seen i could use the same function of the example by doing -> from mplfinance.original_flavor import candlestick_ohl)

FabioMarinelli99 avatar Apr 17 '21 22:04 FabioMarinelli99

Fabio, Coloring candles individually is not directly supported in mplfinance. It would be greatly appreciated if you are interested in contributing this enhancement. The approach that comes to mind would be to modify _construct_candlestick_collections() to accept either a list of marketcolors, or an additional kwarg that would map one or more specific candles (identified by their datetime location in the data) to a specific color for each specifc candle in the mapping.


In the meantime, there are two workarounds that I can think of: One is, as you said, import the old api from mplfinance.original_flavor. The other workaround is as follows (example using this data) ...

  1. Create a second dataframe, the same length, and with the same index, as the first dataframe,
    filled with all nan values except for the specific candles you want to color.
  2. Customize a market colors object for the color you want the specific candles, and place into a custom style.
  3. Plot the first dataframe as usual, but with returnfig=True to gain access to the Axes object.
  4. Call mpf.plot() a second time, with the second dataframe, and the second style, passing in the Axes from the first plot.
df = pd.read_csv('../data/SP500_NOV2019_Hist.csv',index_col=0,parse_dates=True)

# Customize market colors, and place inside style you intend to use:
mc = mpf.make_marketcolors(up='yellow',down='yellow')
s  = mpf.make_mpf_style(base_mpf_style='yahoo',marketcolors=mc)

# Create an all `nan` values dataframe, same size, and same index as the first:
nans = [float('nan')]*len(df)
cdf = pd.DataFrame(dict(Open=nans,High=nans,Low=nans,Close=nans),index=df.index)

# Copy in the specific candles that you want to color:
cdf.loc['2019-11-08'] = df.loc['2019-11-08']
cdf.loc['2019-11-15'] = df.loc['2019-11-15']
cdf.loc['2019-11-19'] = df.loc['2019-11-19']

# Call `mpf.plot()` twice with the two dataframes and two styles:
fig, axlist = mpf.plot(df,type='candle',style='yahoo',returnfig=True)
mpf.plot(cdf,type='candle',style=s,ax=axlist[0])

image

HTH. --Daniel


P.S. I just noticed there is a slight discoloration in the yellow candles, due to the overlap with the red/green candles from the original dataframe. If this is a concern it can be avoided by replacing those candles with nan values in the original dataframe, as follows:

# Copy in the specific candles that you want to color, and replace them with `nan` values in the original dataframe:
cdf.loc['2019-11-08'] = df.loc['2019-11-08'].copy()
cdf.loc['2019-11-15'] = df.loc['2019-11-15'].copy()
cdf.loc['2019-11-19'] = df.loc['2019-11-19'].copy()
df.loc['2019-11-08'] = nans[0]
df.loc['2019-11-15'] = nans[0]
df.loc['2019-11-19'] = nans[0]

DanielGoldfarb avatar Apr 18 '21 02:04 DanielGoldfarb

@DanielGoldfarb Thanks a lot for the complete and exhaustive answer :)

FabioMarinelli99 avatar Apr 18 '21 08:04 FabioMarinelli99

Hey @DanielGoldfarb ! I was facing the same issue and would like to contribute if that is okay :) The approach seems simple enough, I am planning to accept a list of colors (default= None) and change value of colors and edgecolors based on that list (if it is None, then I will keep it as default, else change it to specified).

Let me know if I can take this up and anything else I should know for coding this.

AurumnPegasus avatar Sep 28 '21 23:09 AurumnPegasus

@AurumnPegasus Shivansh, Thank you for your offer to contribute! Definitely okay! Here are my initial thoughts on the enhancement; let me know what you think:

  • As mentioned above my thinking is that the user would pass in (via a new kwarg) a list of marketcolor objects (that is, a list of the outputs from mpf.make_marketcolors).
    • If you think it is also important to accept just colors, then we could also allow a simple list of single colors, which internally, before being passed to _construct_mpf_collections(), would get converted to a list of marketcolor objects but with the wicks, bodies, and edges all the same color. (The idea is that once you get inside _construct_mpf_collections() then the code deals only with marketcolor objects).
  • We can also allow None values in this list. A None value would mean to color the candle according to the specified style. Thus the non-None values represent colors that "override" the plot's style.
  • I am inclined to think that we should check and require that the list of marketcolors be the same length as the dataframe. (But I am open to suggestions of you have another way to handle this).
  • It would be nice if we can make this work not just for candles, but for OHLC bars as well. (However, Renko and PnF are not practical because the user does not know the number of bricks or boxes ahead of time, and thus can't know how many colors to pass in).

Let me know what you think of the above, or if you want me to review and code in your fork. All the best. --Daniel

DanielGoldfarb avatar Sep 30 '21 02:09 DanielGoldfarb

Hey @DanielGoldfarb, Update, its almost done!

Approach:

  • Instead of working on _construct_candlestick_collections as you had suggested, I thought it would be better to make _updown_colors accept a new parameter (list of colors and Nones) since other functions (like Hollow Candlestick) are using it as well.
  • Previously, _updown_colors was accepting uc and dc as rgba colours, and I am constructing the code in the same way, where if the passed list has 'None', then it displays uc or dc, but if it has a given colour then it displays the candle in that colour.

Remaining:

  • As mentioned above my thinking is that the user would pass in (via a new kwarg) a list of marketcolor objects (that is, a list of the outputs from mpf.make_marketcolors).
  • As far as I have understood, marketcolor object is essentially a dictionary which contains required parameters. I do not understand the need of creating a list of objects as you have mentioned here. Do you want the user to create a seperate marketcolor object for each candle that they want to customise?

I need to just refactor my code and remove a fair few comments 😛 . If everything works out well I should be done by today!

AurumnPegasus avatar Oct 01 '21 08:10 AurumnPegasus

As far as I have understood, marketcolor object is essentially a dictionary which contains required parameters. I do not understand the need of creating a list of objects as you have mentioned here. Do you want the user to create a seperate marketcolor object for each candle that they want to customise?

Shivansh,

Yes, marketcolor object is just a dictionary, however mplfinance users should always treat it like any other object, by not directly setting or manipulating that dict, rather always calling mpf.make_marketcolors() to create one. (That way, should we ever decide to write our own markcolors class, we are free to do so).

My concerns with how to implement are as follows ... and it seems you have already given this more thought that I have so, for now, I will trust your judgement and simply express my concerns:

My first concern (obviously) is to keep the code as easily maintainable as we practically can. The other concern is this block of code here where users have the ability to specify up and down colors differently for the body, edge, and wick of the candles. So I am thinking that users specifying per-candle colors should also have that option. However they should also have the option to pass in simply a single color for each candle and the code would then use that color for all of body, edge and wick.

The basic idea of this enhancement is that the user specified colors essentially override what _updown_colors() otherwise does. At this point I can see that potentially being implemented either inside (as part of processing) or outside (as "post" processing) of _updown_colors() ... so I would just say go with your gut and see which works (keeping the above concerns in mind). I am also open to hearing arguments as to why only colors should be allowed, thus not giving the user control over the wick and edge of the candle. I am inlined not to take that control away from the user, as it is a basic part of mplfinance styles.

Also, as I think about it, I find myself on the fence about the following: Should we force users to pass in a list of all colors and Nones, or list of all marketcolor dicts and Nones, but not allow mixing of plain colors and marketcolor dicts in the same list? Or should we allow such mixing? Obviously allowing mixing will make the mplfinance code a little bit more complex, but if we do as I first suggested: somewhere early in the code convert the entire list to markcolor objects, then this added complexity will be contained.

Again, thank you so much for contributing. All the best. I am looking forward to reviewing and testing the code. Let me know if you have any questions. --Daniel

DanielGoldfarb avatar Oct 01 '21 12:10 DanielGoldfarb

can i use a column of my csv with black, white and red to paint my candles with the color ?

brcake avatar Jun 25 '22 06:06 brcake

@brcake Take a look at the market color overrides tutorial. Presently you can store the market colors as a column in your dataframe, however that column will be ignored except that you can also "manually" take the values from that column and pass them to the marketcolor_overrides kwarg.

This is an interesting idea. Perhaps we can enhance mpfinance to accept a column name as the value marketcolor_overrides kwarg, and it will then grab it automatically from your dataframe. However for now you will have to follow the intstructions in the above mentioned tutorial. All the best. --Daniel

DanielGoldfarb avatar Jun 26 '22 02:06 DanielGoldfarb