module Graphics.Rendering.Chart.Plot.Candle(
PlotCandle(..),
Candle(..),
plot_candle_title,
plot_candle_line_style,
plot_candle_tick_length,
plot_candle_width,
plot_candle_centre,
plot_candle_fill,
plot_candle_rise_fill_style,
plot_candle_fall_fill_style,
plot_candle_values,
) where
import Control.Lens hiding (op)
import Data.Monoid
import Graphics.Rendering.Chart.Geometry hiding (close)
import Graphics.Rendering.Chart.Drawing
import Graphics.Rendering.Chart.Plot.Types
import Control.Monad
import Data.Colour (opaque)
import Data.Colour.Names (white, blue)
import Data.Default.Class
data PlotCandle x y = PlotCandle {
_plot_candle_title :: String,
_plot_candle_line_style :: LineStyle,
_plot_candle_fill :: Bool,
_plot_candle_rise_fill_style :: FillStyle,
_plot_candle_fall_fill_style :: FillStyle,
_plot_candle_tick_length :: Double,
_plot_candle_width :: Double,
_plot_candle_centre :: Double,
_plot_candle_values :: [Candle x y]
}
data Candle x y = Candle { candle_x :: x
, candle_low :: y
, candle_open :: y
, candle_mid :: y
, candle_close :: y
, candle_high :: y
} deriving (Show)
instance ToPlot PlotCandle where
toPlot p = Plot {
_plot_render = renderPlotCandle p,
_plot_legend = [(_plot_candle_title p, renderPlotLegendCandle p)],
_plot_all_points = ( map candle_x pts
, concat [ [candle_low c, candle_high c]
| c <- pts ] )
}
where
pts = _plot_candle_values p
renderPlotCandle :: PlotCandle x y -> PointMapFn x y -> BackendProgram ()
renderPlotCandle p pmap =
mapM_ (drawCandle p . candlemap) (_plot_candle_values p)
where
candlemap (Candle x lo op mid cl hi) =
Candle x' lo' op' mid' cl' hi'
where (Point x' mid') = pmap' (x,mid)
(Point _ lo') = pmap' (x,lo)
(Point _ op') = pmap' (x,op)
(Point _ cl') = pmap' (x,cl)
(Point _ hi') = pmap' (x,hi)
pmap' = mapXY pmap
drawCandle :: PlotCandle x y -> Candle Double Double -> BackendProgram ()
drawCandle ps (Candle x lo open mid close hi) = do
let tl = _plot_candle_tick_length ps
let wd = _plot_candle_width ps
let ct = _plot_candle_centre ps
let f = _plot_candle_fill ps
when f $ withFillStyle (if open >= close
then _plot_candle_rise_fill_style ps
else _plot_candle_fall_fill_style ps) $
fillPath $ moveTo' (xwd) open
<> lineTo' (xwd) close
<> lineTo' (x+wd) close
<> lineTo' (x+wd) open
<> lineTo' (xwd) open
withLineStyle (_plot_candle_line_style ps) $ do
strokePath $ moveTo' (xwd) open
<> lineTo' (xwd) close
<> lineTo' (x+wd) close
<> lineTo' (x+wd) open
<> lineTo' (xwd) open
strokePath $ moveTo' x (min lo hi)
<> lineTo' x (min open close)
<> moveTo' x (max open close)
<> lineTo' x (max hi lo)
when (tl > 0) $ strokePath $ moveTo' (xtl) lo
<> lineTo' (x+tl) lo
<> moveTo' (xtl) hi
<> lineTo' (x+tl) hi
when (ct > 0) $ strokePath $ moveTo' (xct) mid
<> lineTo' (x+ct) mid
renderPlotLegendCandle :: PlotCandle x y -> Rect -> BackendProgram ()
renderPlotLegendCandle pc (Rect p1 p2) = do
drawCandle pc2 (Candle (xwid*1/4) lo open mid close hi)
drawCandle pc2 (Candle (xwid*2/3) lo close mid open hi)
where
pc2 = pc { _plot_candle_width = 2 }
xwid = p_x p1 + p_x p2
lo = max (p_y p1) (p_y p2)
mid = (p_y p1 + p_y p2)/2
hi = min (p_y p1) (p_y p2)
open = (lo + mid) / 2
close = (mid + hi) / 2
instance Default (PlotCandle x y) where
def = PlotCandle
{ _plot_candle_title = ""
, _plot_candle_line_style = solidLine 1 $ opaque blue
, _plot_candle_fill = False
, _plot_candle_rise_fill_style = solidFillStyle $ opaque white
, _plot_candle_fall_fill_style = solidFillStyle $ opaque blue
, _plot_candle_tick_length = 2
, _plot_candle_width = 5
, _plot_candle_centre = 0
, _plot_candle_values = []
}
$( makeLenses ''PlotCandle )