示例#1
0
class ModelView(wx.Panel):
    title = 'Profile'
    default_size = (600, 400)

    def __init__(self, *args, **kw):
        wx.Panel.__init__(self, *args, **kw)

        # Fig
        self.fig = Figure(
            figsize=(1, 1),
            dpi=75,
            facecolor='white',
            edgecolor='white',
        )
        # Canvas
        self.canvas = FigureCanvas(self, -1, self.fig)
        self.fig.set_canvas(self.canvas)

        # Axes
        self.axes = self.fig.add_axes(Subplot(self.fig, 111))
        self.axes.set_autoscale_on(False)
        self.theta_axes = self.axes.twinx()
        self.theta_axes.set_autoscale_on(False)

        # Show toolbar or not?
        self.toolbar = NavigationToolbar2WxAgg(self.canvas)
        self.toolbar.Show(True)

        # Create a figure manager to manage things
        self.figmgr = FigureManager(self.canvas, 1, self)

        # Panel layout
        self.profile_selector_label = wx.StaticText(self, label="Sample")
        self.profile_selector = wx.Choice(self)
        self.profile_selector.Hide()
        self.profile_selector_label.Hide()
        self.Bind(wx.EVT_CHOICE, self.OnProfileSelect)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.canvas,
                       1,
                       border=2,
                       flag=wx.LEFT | wx.TOP | wx.GROW)
        self.tbsizer = wx.BoxSizer(wx.HORIZONTAL)
        self.tbsizer.Add(self.toolbar, 0, wx.ALIGN_CENTER_VERTICAL)
        self.tbsizer.AddSpacer(20)
        self.tbsizer.Add(self.profile_selector_label, 0,
                         wx.ALIGN_CENTER_VERTICAL)
        self.tbsizer.AddSpacer(5)
        self.tbsizer.Add(self.profile_selector, 0, wx.ALIGN_CENTER_VERTICAL)
        self.sizer.Add(self.tbsizer)

        self.SetSizer(self.sizer)
        self.sizer.Fit(self)

        # Status bar
        frame = self.GetTopLevelParent()
        self.statusbar = frame.GetStatusBar()
        if self.statusbar is None:
            self.statusbar = frame.CreateStatusBar()
        status_update = lambda msg: self.statusbar.SetStatusText(msg)

        # Set the profile interactor
        self.profile = ProfileInteractor(self.axes,
                                         self.theta_axes,
                                         status_update=status_update)

        # Add context menu and keyboard support to canvas
        self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnContextMenu)
        #self.canvas.Bind(wx.EVT_LEFT_DOWN, lambda evt: self.canvas.SetFocus())

        self.model = None
        self._need_interactors = self._need_redraw = False
        self.Bind(wx.EVT_SHOW, self.OnShow)

    def OnContextMenu(self, event):
        """
        Forward the context menu invocation to profile, if profile exists.
        """
        sx, sy = event.GetX(), event.GetY()
        #transform = self.axes.transData
        #data_x,data_y = pixel_to_data(transform, sx, self.fig.bbox.height-sy)

        popup = wx.Menu()
        item = popup.Append(wx.ID_ANY, '&Grid on/off', 'Toggle grid lines')
        wx.EVT_MENU(self, item.GetId(), lambda _:
                    (self.axes.grid(), self.fig.canvas.draw_idle()))
        item = popup.Append(wx.ID_ANY, '&Rescale', 'Show entire profile')
        wx.EVT_MENU(
            self, item.GetId(), lambda _:
            (self.profile.reset_limits(), self.profile.draw_idle()))
        self.PopupMenu(popup, (sx, sy))
        return False

    def OnProfileSelect(self, event):
        self._set_profile(*self.profiles[event.GetInt()])

    # ==== Model view interface ===
    def OnShow(self, event):
        if not event.Show:
            return
        #print "showing profile"
        if self._need_redraw:
            self.redraw(reset_interactors=False, reset_limits=True)
        #event.Skip()

    def get_state(self):
        return self.model

    def set_state(self, state):
        self.set_model(state)

    def set_model(self, model):
        # print ">>>>>>> refl1d profile set model"
        self.model = model
        self.redraw(reset_interactors=True, reset_limits=True)

    def update_model(self, model):
        # print ">>>>>>> refl1d profile update model"
        if self.model == model:
            self.redraw(reset_interactors=False, reset_limits=True)

    def update_parameters(self, model):
        # print ">>>>>>> refl1d profile update parameters"
        if self.model == model:
            self.redraw(reset_interactors=False, reset_limits=False)

    def redraw(self, reset_interactors=False, reset_limits=False):
        if reset_interactors:
            self._need_interactors = True

        if not self.IsShown():
            self._need_redraw = True
            return

        if self._need_interactors:
            self._create_interactors()
            self._set_profile(*self.profiles[0])
            self._need_interactors = False

        self.profile.redraw(reset_limits=reset_limits)

    # =============================================
    def _create_interactors(self):
        self.profiles = []

        def add_profiles(name, exp, idx):
            if isinstance(exp, MixedExperiment):
                for i, p in enumerate(exp.parts):
                    self.profiles.append((name + chr(ord("a") + i), p, idx))
            else:
                self.profiles.append((name, exp, idx))

        if isinstance(self.model, MultiFitProblem):
            for i, p in enumerate(self.model.models):
                if hasattr(p.fitness, "reflectivity"):
                    name = p.fitness.name
                    if not name: name = "M%d" % (i + 1)
                    add_profiles(name, p.fitness, i)
        else:
            add_profiles("", self.model.fitness, -1)

        self.profile_selector.Clear()
        if len(self.profiles) > 1:
            self.profile_selector.AppendItems([k for k, _, _ in self.profiles])
            self.profile_selector_label.Show()
            self.profile_selector.Show()
            self.profile_selector.SetSelection(0)
        else:
            self.profile_selector_label.Hide()
            self.profile_selector.Hide()

    def _set_profile(self, name, experiment, idx):
        # Turn the model into a user interface
        # It is the responsibility of the party that is indicating
        # that a redraw is necessary to clear the precalculated
        # parts of the view; otherwise the theory function calculator
        # is going to be triggered twice.  This happens inside profile
        # before the profile is calculated.  Note that the profile
        # panel will receive its own signal, which will cause the
        # profile interactor to draw itself again.  We hope this isn't
        # too much of a problem.
        def signal_update():
            """Notify other views that the model has changed"""
            signal.update_parameters(model=self.model)

        def force_recalc():
            self.model.model_update()

        if isinstance(self.model, MultiFitProblem):
            self.model.set_active_model(idx)
        self.profile.set_experiment(experiment,
                                    force_recalc=force_recalc,
                                    signal_update=signal_update)

    def onPrinterSetup(self, event=None):
        self.canvas.Printer_Setup(event=event)

    def onPrinterPreview(self, event=None):
        self.canvas.Printer_Preview(event=event)

    def onPrint(self, event=None):
        self.canvas.Printer_Print(event=event)

    def OnSaveFigureMenu(self, evt):
        """
        Save the current figure as an image file
        """
        dlg = wx.FileDialog(
            self,
            message="Save Figure As ...",
            defaultDir=os.getcwd(),
            defaultFile="",
            wildcard=
            "PNG files (*.png)|*.png|BMP files (*.bmp)|*.bmp|All files (*.*)|*.*",
            style=wx.SAVE)
        _val = dlg.ShowModal()
        if _val == wx.ID_CANCEL: return  #Do nothing
        if _val == wx.ID_OK:
            outfile = dlg.GetPath()

        dlg.Destroy()

        # Save
        self.fig.savefig(outfile)

    def GetToolBar(self):
        """
        backend_wx call this function. KEEP it
        """
        return None

    def OnPanelFrameClose(self, evt):
        """
        On Close this Frame
        """
        self.Destroy()
        evt.Skip()

    def OnCopyFigureMenu(self, evt):
        """
        Copy the current figure
        """
        CopyImage(self.canvas)

    def CanShowContextMenu(self):
        return True

    def quit_on_error(self):
        numpy.seterr(all='raise')
        ProfileInteractor._debug = True
        BaseInteractor._debug = True
示例#2
0
class PlotFrame(wx.Frame):
    help_msg = """  Menus for  
     Save           export figure (png,eps,bmp) to file 
     Copy           copy bitmap of figure to the system clipboard
     Print Setup    setup size of figure for printing
     Print Preview  preview printer page
     Print          send figure to a system printer
     Exit           end application
    
     where 'figure' means an image of the matplotlib canvas
 
  In addition, "Ctrl-C" is bound to copy-figure-to-clipboard
"""

    start_msg = """        Use Menus to test printing
        or Ctrl-C to copy plot image to clipboard  """

    about_msg = """        printing_in_wx version 0.1  12-Nov-2004
        Matt Newville <*****@*****.**>"""

    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Test Printing with WX Backend")
        self.fig = Figure((5.0, 3.0), 100)
        self.canvas = FigCanvas(self, -1, self.fig)
        self.axes = self.fig.add_axes([0.15, 0.15, 0.75, 0.75])

        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
        sizer.Add(wx.StaticText(self, -1, self.start_msg), 0,
                  wx.ALIGN_LEFT | wx.TOP)

        self.canvas.Bind(wx.EVT_KEY_DOWN, self.onKeyEvent)

        self.SetSizer(sizer)
        self.Fit()
        self.Build_Menus()
        self.Plot_Data()

    def Build_Menus(self):
        """ build menus """
        MENU_EXIT = wx.NewId()
        MENU_SAVE = wx.NewId()
        MENU_PRINT = wx.NewId()
        MENU_PSETUP = wx.NewId()
        MENU_PREVIEW = wx.NewId()
        MENU_CLIPB = wx.NewId()
        MENU_HELP = wx.NewId()

        menuBar = wx.MenuBar()

        f0 = wx.Menu()
        f0.Append(MENU_SAVE, "&Export", "Save Image of Plot")
        f0.AppendSeparator()
        f0.Append(MENU_PSETUP, "Page Setup...", "Printer Setup")
        f0.Append(MENU_PREVIEW, "Print Preview...", "Print Preview")
        f0.Append(MENU_PRINT, "&Print", "Print Plot")
        f0.AppendSeparator()
        f0.Append(MENU_EXIT, "E&xit", "Exit")
        menuBar.Append(f0, "&File")

        f1 = wx.Menu()
        f1.Append(MENU_HELP, "Quick Reference", "Quick Reference")

        menuBar.Append(f1, "&Help")

        self.SetMenuBar(menuBar)

        self.Bind(wx.EVT_MENU, self.onPrint, id=MENU_PRINT)
        self.Bind(wx.EVT_MENU, self.onPrinterSetup, id=MENU_PSETUP)
        self.Bind(wx.EVT_MENU, self.onPrinterPreview, id=MENU_PREVIEW)
        self.Bind(wx.EVT_MENU, self.onClipboard, id=MENU_CLIPB)
        self.Bind(wx.EVT_MENU, self.onExport, id=MENU_SAVE)
        self.Bind(wx.EVT_MENU, self.onExit, id=MENU_EXIT)
        self.Bind(wx.EVT_MENU, self.onHelp, id=MENU_HELP)

    # the printer / clipboard methods are implemented
    # in backend_wx, and so are very simple to use.
    def onPrinterSetup(self, event=None):
        self.canvas.Printer_Setup(event=event)

    def onPrinterPreview(self, event=None):
        self.canvas.Printer_Preview(event=event)

    def onPrint(self, event=None):
        self.canvas.Printer_Print(event=event)

    def onClipboard(self, event=None):
        self.canvas.Copy_to_Clipboard(event=event)

    def onKeyEvent(self, event=None):
        """ capture , act upon keystroke events"""
        if event == None: return
        key = event.KeyCode()
        if (key < wx.WXK_SPACE or key > 255): return

        if (event.ControlDown() and chr(key) == 'C'):  # Ctrl-C
            self.onClipboard(event=event)

    def onHelp(self, event=None):
        dlg = wx.MessageDialog(self, self.help_msg, "Quick Reference",
                               wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onExport(self, event=None):
        """ save figure image to file"""
        file_choices = "PNG (*.png)|*.png|" \
                       "PS (*.ps)|*.ps|" \
                       "EPS (*.eps)|*.eps|" \
                       "BMP (*.bmp)|*.bmp"

        thisdir = os.getcwd()

        dlg = wx.FileDialog(self,
                            message='Save Plot Figure as...',
                            defaultDir=thisdir,
                            defaultFile='plot.png',
                            wildcard=file_choices,
                            style=wx.SAVE)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.canvas.print_figure(path, dpi=300)
            if (path.find(thisdir) == 0):
                path = path[len(thisdir) + 1:]
            print 'Saved plot to %s' % path

    def onExit(self, event=None):
        self.Destroy()

    def Plot_Data(self):
        t = numpy.arange(0.0, 5.0, 0.01)
        s = numpy.sin(2.0 * numpy.pi * t)
        c = numpy.cos(0.4 * numpy.pi * t)
        self.axes.plot(t, s)
        self.axes.plot(t, c)
示例#3
0
class PlotFrame(wx.Frame):
    help_msg="""  Menus for
     Import...import project settings file
     Export...export figure (png, jpg) to file
     Print Setup...setup size of figure for printing
     Print Preview...preview printer page
     Print...send figure to a system printer
     Exit...end application

     Basic-2D...View basic 2D representation of data
     Advanced-2D...View 2D representation (with span selection control)
     Advanced-3D...View 3D representation of data

     View-Grid...Toggle grid
     View-Legend...Toggle legend
     View-Fill...Toggle fill representation of data (only for 2D)

     where 'figure' means an image of the matplotlib canvas

     In addition, "Ctrl-C" is bound to copy-figure-to-clipboard    
    """

    about_msg="""
        Option_price_visualisation v0.1  29-Aug-2013

        Nathan Floor, [email protected]
    """

    def __init__(self):
        wx.Frame.__init__(self, None, -1, "Visualise Option Prices and Greeks", size=(300, 500))
        self.Bind(wx.EVT_KEY_DOWN, self.onKeyEvent)

        # intialise variables when start program
        self.viewLegend = False
        self.viewGrid = True
        self.viewFill = False
        
        self.reInitialiseData()
        
        # build interface
        self.Build_Menus()
        self.Build_Panel()
        self.statusbar = self.CreateStatusBar()
        self.Plot_Data()
        self.SetSize(size=(900, 550))

    def reInitialiseData(self):
        self.fileReader = csvReader.Reader()
        self.current_view = 0
        self.time = numpy.arange(0, 31, 1)
        self.indmin = 0
        self.indmax = 31
        self.strike_price = 0

        # initialise data arrays
        self.option_price = []
        self.stock_price = []
        self.delta = []
        self.gamma = []
        self.vega = []
        self.theta = []
        self.rho = []
        self.risidual = []
        
        # initialise secondary data sets
        self.option_price_fill = []
        self.time_span_fill = []
        self.delta_fill = []
        self.gamma_fill = []
        self.vega_fill = []
        self.theta_fill = []
        self.rho_fill = []
        
        # initialise bump values
        self.stock_bump = 0
        self.time_bump = 0
        self.rate_bump = 0
        self.volitile_bump = 0

    # on span-selection of graph
    def onselect(self, xmin, xmax):
        # initialise data sets
        option_price = []
        delta = []
        gamma = []
        theta = []
        rho = []
        vega = []
        maxYLimlist = []
        minYLimlist = []

        # identify the indices of new data set based on selection
        self.indmin = int(xmin)
        #self.indmax = numpy.searchsorted(t, (xmin, xmax))
        self.indmax = min(len(self.time)-1, int(xmax)+1)

        self.time_span_fill = self.time[self.indmin:self.indmax]
        if len(self.option_price) > 0:
            option_price = numpy.array(map(float, self.option_price[self.stock_bump]))
            self.option_price_fill = option_price[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.option_price_fill))
            minYLimlist.append(min(self.option_price_fill))
            if not self.viewFill:
                self.line1.set_data(self.time_span_fill, self.option_price_fill)
        if len(self.delta) > 0:
            delta = numpy.array(map(float, self.delta[self.stock_bump]))
            self.delta_fill = delta[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.delta_fill))
            minYLimlist.append(min(self.delta_fill))
            if not self.viewFill:
                self.line2.set_data(self.time_span_fill, self.delta_fill)
        if len(self.gamma) > 0:
            gamma = numpy.array(map(float, self.gamma[self.stock_bump]))
            self.gamma_fill = gamma[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.gamma_fill))
            minYLimlist.append(min(self.gamma_fill))
            if not self.viewFill:
                self.line3.set_data(self.time_span_fill, self.gamma_fill)
        if len(self.theta) > 0:
            theta = numpy.array(map(float, self.theta[self.time_bump]))
            self.theta_fill = theta[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.theta_fill))
            minYLimlist.append(min(self.theta_fill))
            if not self.viewFill:
                self.line4.set_data(self.time_span_fill, self.theta_fill)
        if len(self.rho) > 0:
            rho = numpy.array(map(float, self.rho[self.rate_bump]))
            self.rho_fill = rho[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.rho_fill))
            minYLimlist.append(min(self.rho_fill))
            if not self.viewFill:
                self.line5.set_data(self.time_span_fill, self.rho_fill)
        if len(self.vega) > 0:
            vega = numpy.array(map(float, self.vega[self.volitile_bump]))
            self.vega_fill = vega[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.vega_fill))
            minYLimlist.append(min(self.vega_fill))
            if not self.viewFill:
                self.line6.set_data(self.time_span_fill, self.vega_fill)
        if len(self.risidual) > 0:
            risidual = numpy.array(map(float, self.risidual[self.stock_bump]))
            self.risidual_fill = risidual[self.indmin:self.indmax]
            #  append to lists for axes limits
            maxYLimlist.append(max(self.risidual_fill))
            minYLimlist.append(min(self.risidual_fill))
            if not self.viewFill:
                self.line7.set_data(self.time_span_fill, self.risidual_fill)

        if len(maxYLimlist) > 0:
            maxYLim = max(maxYLimlist)
        else:
            maxYLim = 1
        if len(minYLimlist) > 0:
            minYLim = min(minYLimlist)
        else:
            minYLim = 0

        self.axes2.set_xlim(self.time_span_fill[0]-1, self.time_span_fill[-1]+1)
        self.axes2.set_ylim(minYLim, maxYLim)
        if not self.viewFill:
            self.canvas.draw()
        else:
            self.Plot_Data()

    def Build_Panel(self):
        self.panel = wx.Panel(self)

        # Create Figure and canvas objects
        self.fig = Figure((6.0, 4.0))
        self.canvas = FigCanvas(self.panel, -1, self.fig)
        self.axes = self.fig.add_subplot(111)

        # setup slider-widgets for controlling GUI
        self.sliderPanel = wx.Panel(self.panel)
        self.stockSlider_label = wx.StaticText(self.sliderPanel, -1, "Stock Price: ")
        self.stockSlider = wx.Slider(self.sliderPanel, value=5, minValue=1, maxValue=9, 
            pos=(20, 20), size=(100,-1), style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS)
        # self.stockSlider.SetTickFreq(9, 1)
        self.rateSlider_label = wx.StaticText(self.sliderPanel, -1, "Interest Rate: ")
        self.rateSlider = wx.Slider(self.sliderPanel, value=5, minValue=1, maxValue=9, 
            pos=(20, 20), size=(100,-1), style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS)
        self.rateSlider.SetTickFreq(1, 1)
        self.timeStepSlider_label = wx.StaticText(self.sliderPanel, -1, "Time Step: ")
        self.timeStepSlider = wx.Slider(self.sliderPanel, value=5, minValue=1, maxValue=9, 
            pos=(20, 20), size=(100,-1), style=wx.SL_HORIZONTAL|wx.SL_AUTOTICKS)
        self.timeStepSlider.SetTickFreq(1, 1)

        self.Bind(wx.EVT_SLIDER, self.onStockSlider, self.stockSlider)
        self.Bind(wx.EVT_SLIDER, self.onRateSlider, self.rateSlider)
        self.Bind(wx.EVT_SLIDER, self.ontimeStepSlider, self.timeStepSlider)        

        # setup options-widgets for controlling graphs
        self.callRadio = wx.RadioButton(self.panel, label="Call options", pos=(10, 10))
        self.putRadio = wx.RadioButton(self.panel, label="Put options", pos=(10, 30))
        self.callRadio.SetValue(True)        
        self.spaceKeeper = wx.StaticText(self.panel, -1, '')
        self.optionPriceCheck = wx.CheckBox(self.panel, label="view Option Price", pos=(20, 20))
        self.deltaCheck = wx.CheckBox(self.panel, label="show Delta", pos=(20, 20))
        self.gammaCheck = wx.CheckBox(self.panel, label="show Gamma", pos=(20, 20))
        self.rhoCheck = wx.CheckBox(self.panel, label="show Rho", pos=(20, 20))
        self.thetaCheck = wx.CheckBox(self.panel, label="show Theta", pos=(20, 20))
        self.fillCheck = wx.CheckBox(self.panel, label="show fill feature", pos=(20, 20))
        self.risidualCheck = wx.CheckBox(self.panel, label="Show Residual", pos=(20, 20))
        self.differenceCheck = wx.CheckBox(self.panel, label="Show Difference", pos=(20, 20))
        self.effectCheck = wx.CheckBox(self.panel, label="Show Greek's effect on option price", pos=(20, 20))
        self.effectCheck.SetValue(True)

        self.Bind(wx.EVT_RADIOBUTTON, self.onCallRadio, self.callRadio)
        self.Bind(wx.EVT_RADIOBUTTON, self.onPutRadio, self.putRadio)
        self.Bind(wx.EVT_CHECKBOX, self.onOptionPrice, self.optionPriceCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onDelta, self.deltaCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onGamma, self.gammaCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onRho, self.rhoCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onTheta, self.thetaCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onRisidual, self.risidualCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onDifferenceCheck, self.differenceCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onShowEffects, self.effectCheck)
        self.Bind(wx.EVT_CHECKBOX, self.onShowFillEffect, self.fillCheck)

        # Create the navigation toolbar, tied to the canvas
        self.toolbar = NavigationToolbar(self.canvas)

        ####################
        # Layout with sizers
        ####################
        flags = wx.ALIGN_LEFT | wx.ALL | wx.ALIGN_CENTER_VERTICAL
        self.sizer = wx.BoxSizer(wx.VERTICAL)

        self.hboxMainBlock = wx.BoxSizer(wx.HORIZONTAL)
        self.vboxOptions = wx.BoxSizer(wx.VERTICAL)
        self.hboxSliders = wx.BoxSizer(wx.HORIZONTAL)
        self.flexiGridSizer = wx.FlexGridSizer(4, 2, 3, 10)

        # adds border around sliders to group related widgets
        self.vboxOptions.AddSpacer(10)
        self.sliderStaticBox = wx.StaticBox(self.sliderPanel, -1, 'Bump Sliders')
        self.sliderBorder = wx.StaticBoxSizer(self.sliderStaticBox, orient=wx.VERTICAL)
        self.flexiGridSizer.AddMany([(self.stockSlider_label), (self.stockSlider, 1, wx.ALL), 
            (self.rateSlider_label), (self.rateSlider, 1, wx.EXPAND), (self.timeStepSlider_label), (self.timeStepSlider, 1, wx.EXPAND)])
        self.sliderBorder.Add(self.flexiGridSizer, 1, wx.ALL, 5)
        self.sliderPanel.SetSizer(self.sliderBorder)
        self.vboxOptions.Add(self.sliderPanel, 0, flag=wx.ALIGN_LEFT|wx.ALL)

        # add border for type of option price
        self.optionsBorder = wx.StaticBoxSizer(wx.StaticBox(self.panel, -1, 'Option Price Type'), orient=wx.VERTICAL)
        self.flexiOptions = wx.FlexGridSizer(3, 1, 3, 10)
        self.flexiOptions.AddMany([(self.callRadio, 1, wx.EXPAND), 
            (self.putRadio, 1, wx.EXPAND), (self.optionPriceCheck, 1, wx.EXPAND)])            
        self.optionsBorder.Add(self.flexiOptions, 1, wx.ALL, 5)
        self.vboxOptions.Add(self.optionsBorder, 0, flag=wx.ALIGN_LEFT|wx.ALL|wx.GROW)
        
        # add border for greeks
        self.greekOptionsBorder = wx.StaticBoxSizer(wx.StaticBox(self.panel, -1, 'Greek effect Options'), orient=wx.VERTICAL)
        self.flexiOptions2 = wx.FlexGridSizer(6, 1, 3, 10)
        self.flexiOptions2.AddMany([(self.deltaCheck, 1, wx.EXPAND), (self.gammaCheck, 1, wx.EXPAND), 
            (self.rhoCheck, 1, wx.EXPAND), (self.thetaCheck, 1, wx.EXPAND), (self.risidualCheck, 1, wx.EXPAND)])
        self.greekOptionsBorder.Add(self.flexiOptions2, 1, wx.ALL, 5)
        self.vboxOptions.Add(self.greekOptionsBorder, 1, flag=wx.ALIGN_LEFT|wx.ALL|wx.GROW)
        #self.vboxOptions.AddSpacer(5)
        
        # add border for other checkable options
        self.otherOptionsBorder = wx.StaticBoxSizer(wx.StaticBox(self.panel, -1, 'Extra Options'), orient=wx.VERTICAL)
        self.flexiOptions3 = wx.FlexGridSizer(3, 1, 3, 10)
        self.flexiOptions3.AddMany([(self.fillCheck, 1, wx.EXPAND), (self.differenceCheck, 1, wx.EXPAND), 
            (self.effectCheck, 1, wx.EXPAND)])
        self.otherOptionsBorder.Add(self.flexiOptions3, 1, wx.ALL, 5)
        self.vboxOptions.Add(self.otherOptionsBorder, 0, flag=wx.ALIGN_LEFT|wx.ALL|wx.GROW)
        self.vboxOptions.AddSpacer(5)

        self.hboxMainBlock.Add(self.vboxOptions, 0, flag=flags)
        self.hboxMainBlock.Add(self.canvas, 1, flag=wx.ALIGN_RIGHT|wx.ALL|wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
        self.sizer.Add(self.hboxMainBlock, 1, wx.ALL|wx.EXPAND)

        self.sizer.Add(self.toolbar, 0, wx.ALL|wx.ALIGN_RIGHT)
        self.sizer.AddSpacer(1)

        self.canvas.Bind(wx.EVT_KEY_DOWN, self.onKeyEvent)
        self.Bind(wx.EVT_CLOSE, self.onExit)

        self.panel.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.Center()

    def Build_Menus(self):
        """ build menus """
        MENU_EXIT = wx.NewId()
        MENU_OPEN = wx.NewId()
        MENU_SAVE = wx.NewId()
        MENU_PRINT = wx.NewId()
        MENU_PSETUP = wx.NewId()
        MENU_PREVIEW =wx.NewId()
        MENU_CLIPB =wx.NewId()
        MENU_VIEW_GRID = wx.NewId()
        MENU_HELP =wx.NewId()
        MENU_BASIC = wx.NewId()
        MENU_ADVANCE = wx.NewId()
        MENU_LEGEND = wx.NewId()
        MENU_3D = wx.NewId()
        MENU_ABOUT = wx.NewId()

        menuBar = wx.MenuBar()

        f0 = wx.Menu()
        importItem = wx.MenuItem(f0, MENU_OPEN, "&Import\tCtrl+I")
        f0.AppendItem(importItem)  
        f0.Append(MENU_SAVE,   "&Export",   "Save Image of Plot")
        f0.AppendSeparator()
        printMenu = wx.Menu()
        printMenu.Append(MENU_PSETUP, "Page Setup...",    "Printer Setup")
        printMenu.Append(MENU_PREVIEW,"Print Preview...", "Print Preview")
        printItem = wx.MenuItem(printMenu, MENU_PRINT,  "Print\tCtrl+P")
        printMenu.AppendItem(printItem)
        f0.AppendMenu(-1, '&Print', printMenu)
        f0.AppendSeparator()
        exitItem = wx.MenuItem(f0, MENU_EXIT, 'E&xit\tCtrl+Q')
        f0.AppendItem(exitItem)
        menuBar.Append(f0,     "&File")

        f1 = wx.Menu()
        f1.Append(MENU_BASIC, '&Basic', "Basic View(2D)")
        f1.Append(MENU_ADVANCE, '&Advanced-2D', "Advanced View(2D)")
        f1.Append(MENU_3D, 'A&dvanced-3D', "Advanced View(3D)")

        optionsMenu = wx.Menu()
        viewGridItem = wx.MenuItem(optionsMenu, MENU_VIEW_GRID, 'View &Grid\tCtrl+G')
        optionsMenu.AppendItem(viewGridItem)
        viewLegendItem = wx.MenuItem(optionsMenu, MENU_LEGEND, 'View &Legend\tCtrl+L')
        optionsMenu.AppendItem(viewLegendItem)
        f1.AppendMenu(-1, "&Options", optionsMenu)

        menuBar.Append(f1, "&View")

        f2 = wx.Menu()
        f2.Append(MENU_HELP, "Quick &Reference",  "Quick Reference")
        f2.Append(MENU_ABOUT, "&About",  "About this interface")
        menuBar.Append(f2, "&Help")

        self.SetMenuBar(menuBar)

        self.Bind(wx.EVT_MENU, self.onImport,       id=MENU_OPEN)
        self.Bind(wx.EVT_MENU, self.onPrint,        id=MENU_PRINT)
        self.Bind(wx.EVT_MENU, self.onPrinterSetup, id=MENU_PSETUP)
        self.Bind(wx.EVT_MENU, self.onPrinterPreview, id=MENU_PREVIEW)
        self.Bind(wx.EVT_MENU, self.onClipboard,    id=MENU_CLIPB)
        self.Bind(wx.EVT_MENU, self.onExport,       id=MENU_SAVE)
        self.Bind(wx.EVT_MENU, self.onExit ,        id=MENU_EXIT)
        self.Bind(wx.EVT_MENU, self.onViewGrid,     id=MENU_VIEW_GRID)
        self.Bind(wx.EVT_MENU, self.onViewLegend,     id=MENU_LEGEND)
        self.Bind(wx.EVT_MENU, self.onHelp,         id=MENU_HELP)
        self.Bind(wx.EVT_MENU, self.onAbout,         id=MENU_ABOUT)
        self.Bind(wx.EVT_MENU, self.onBasicView,    id=MENU_BASIC)
        self.Bind(wx.EVT_MENU, self.onAdvancedView, id=MENU_ADVANCE)
        self.Bind(wx.EVT_MENU, self.onAdvanced3DView, id=MENU_3D)

    """ Menu event methods """
    def onViewLegend(self, event=None):
        if self.viewLegend:
            self.viewLegend = False
            self.Plot_Data()
        else:
            self.viewLegend = True
            # use proxy artist
            plot_op = Line2D([], [], linewidth=3, color="black") 
            plot_delta = Line2D([], [], linewidth=3, color="royalblue") 
            plot_gamma = Line2D([], [], linewidth=3, color="cyan") 
            plot_theta = Line2D([], [], linewidth=3, color="green") 
            plot_rho = Line2D([], [], linewidth=3, color="darkorange") 
            plot_risidual = Line2D([], [], linewidth=3, color="purple")

            # Shink current axis by 15%
            box = self.axes.get_position()
            self.axes.set_position([box.x0, box.y0, box.width * 0.88, box.height])
            # Put a legend to the right of the current axis
            self.axes.legend([plot_op, plot_delta, plot_gamma, plot_theta, plot_rho, plot_risidual], ['Option Price', 'Delta', 'Gamma', 'Theta', 'Rho', 'Residual'],
                loc='center left', bbox_to_anchor=(1, 0.5), prop={'size':8})

            if self.current_view == 1:
                box = self.axes2.get_position()
                self.axes2.set_position([box.x0, box.y0, box.width * 0.88, box.height])
                # Put a legend to the right of the current axis
                self.axes2.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size':8})
            self.canvas.draw()

    def onBasicView(self, event=None):
        self.current_view = 0
        # show sliders panel
        self.sliderPanel.Enable()
        self.toolbar.Enable()
        self.fillCheck.Enable()
        self.panel.Layout()
        self.Plot_Data()

    def onAdvancedView(self, event=None):
        self.current_view = 1
        # show sliders panel
        self.sliderPanel.Enable()
        self.toolbar.Enable()
        self.fillCheck.Enable()
        self.panel.Layout()
        self.Plot_Data()

    def onAdvanced3DView(self, event=None):
        self.current_view = 2
        # hide slider panel since will not be used
        self.sliderPanel.Disable()
        self.toolbar.Disable()
        self.fillCheck.Disable()
        self.panel.Layout()   
        self.Plot_Data()

    def onPrinterSetup(self,event=None):
        self.canvas.Printer_Setup(event=event)

    def onPrinterPreview(self,event=None):
        self.canvas.Printer_Preview(event=event)

    def onPrint(self,event=None):
        self.canvas.Printer_Print(event=event)

    def onClipboard(self,event=None):
        self.canvas.Copy_to_Clipboard(event=event)

    def onKeyEvent(self,event=None):
        """ capture and act upon keystroke events """
        if event == None: return
        key = event.GetKeyCode()
        if (key < wx.WXK_SPACE or  key > 255):  return

        if (event.ControlDown() and chr(key)=='C'): # Ctrl-C
            self.onClipboard(event=event)
        if (event.ControlDown() and chr(key)=='P'): # Ctrl-P
            self.onPrinterPreview(event=event)

    def onHelp(self, event=None):
        dlg = wx.MessageDialog(self, self.help_msg, "Quick Reference", wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onAbout(self, event=None):
        dlg = wx.MessageDialog(self, self.about_msg, "About", wx.OK | wx.ICON_INFORMATION)
        dlg.ShowModal()
        dlg.Destroy()

    def onViewGrid(self, event=None):
        if self.viewGrid:
            self.viewGrid = False
        else:
            self.viewGrid = True
        for a in self.fig.axes:
            a.grid(self.viewGrid)
        self.canvas.draw()

    def onExport(self,event=None):
        """ save figure image to file"""
        file_choices = "PNG (*.png)|*.png|" \
                       "JPEG (*.jpg)|*.jpg|" \
                       "BMP (*.bmp)|*.bmp"

        thisdir  = os.getcwd()

        dlg = wx.FileDialog(self, message='Save Plot Figure as...',
                            defaultDir = thisdir, defaultFile='plot.png',
                            wildcard=file_choices, style=wx.SAVE)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            self.canvas.print_figure(path,dpi=300)
            if (path.find(thisdir) ==  0):
                path = path[len(thisdir)+1:]
            print('Saved plot to %s' % path)

    def onImport(self, event=None):
        """ Import csv file of option prices and greeks """
        file_choices = "SETTINGS (*.settings)|*.settings"
        thisdir  = ''.join(os.getcwd()+'/data')

        # import output file
        dlg = wx.FileDialog(self, message='Import option prices and greeks (Outputs)',
                            defaultDir = thisdir, defaultFile='data.settings',
                            wildcard=file_choices, style=wx.OPEN)

        if dlg.ShowModal() == wx.ID_OK:
            path = dlg.GetPath()
            projectDir = path.rsplit('/', 1)[0]
            #projectDir = path.rsplit('\\', 1)[0]
            self.reInitialiseData()
            
            # this also involves reading in all the data
            self.number_bumps = self.fileReader.loadSettingsFile(path, projectDir, self.statusbar)
            print('Opened settings file at %s' % path)
        else:
            dlg = wx.MessageDialog(self, "Failed to import the correct settings file.", "Complication", wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            return

        # populate data
        self.strike_price = self.fileReader.getStrikePrice()
        self.stock_price = self.fileReader.getStockPrice()
        self.option_price = self.fileReader.getOptionPrice(self.callRadio.GetValue(), self.optionPriceCheck.IsChecked())
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), self.deltaCheck.IsChecked())
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), self.gammaCheck.IsChecked())
        # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), self.vegaCheck.IsChecked())
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), self.thetaCheck.IsChecked())
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), self.rhoCheck.IsChecked())

        self.onBasicView()

    def onExit(self,event=None):
        dlg = wx.MessageDialog(None, 'Are you sure to exit?', 'Confirm', wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION)
        ret = dlg.ShowModal()
        if ret == wx.ID_YES:
            self.Destroy()

    """ GUI event methods """
    def onShowFillEffect(self, event=None):
        if self.fillCheck.IsChecked():
            if self.differenceCheck.IsChecked():
                self.differenceCheck.SetValue(False)
                # reload and replot data
                self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
                    self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
                self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
                    self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
                # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
                #    self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
                self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
                    self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
                self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
                    self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
                self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
                    self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
            
            self.viewFill = True
        else:
            self.viewFill = False
        self.Plot_Data()

    def onCallRadio(self, event=None):
        self.option_price = self.fileReader.getOptionPrice(self.callRadio.GetValue(), 
            self.optionPriceCheck.IsChecked())
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
            self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
            self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
        #     self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
            self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
            self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
            self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onPutRadio(self, event=None):
        self.option_price = self.fileReader.getOptionPrice(self.callRadio.GetValue(), 
            self.optionPriceCheck.IsChecked())
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
            self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
            self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
        #     self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
            self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
            self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
            self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data() 

    def onOptionPrice(self, event=None):
        self.option_price = self.fileReader.getOptionPrice(self.callRadio.GetValue(), 
                self.optionPriceCheck.IsChecked())
        self.Plot_Data()

    def onDelta(self, event=None):
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
            self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onGamma(self, event=None):
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
            self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())

        self.Plot_Data()

    def onRho(self, event=None):
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
            self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onTheta(self, event=None):
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
            self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onVega(self, event=None):
        self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
            self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onRisidual(self, event=None):
        self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
            self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())

        # print(self.risidual)
        self.Plot_Data()

    def onShowEffects(self, event=None):
        if not self.effectCheck.IsChecked():
            warning_msg = """
                This setting will plot the value of the greeks, but not terms of the option price. This means that the greek graphs will no-longer be comparable. You will still be able to plot the greeks against each other though.

                Do you want to continue?
            """
            dlg = wx.MessageDialog(self, warning_msg, "Take Note", wx.YES_NO | wx.ICON_INFORMATION)
            ret = dlg.ShowModal()
            if ret == wx.ID_NO:
                dlg.Destroy()
                self.effectCheck.SetValue(True)
                return
            dlg.Destroy()
            self.differenceCheck.Disable()
            self.fillCheck.Disable()
            self.optionPriceCheck.SetValue(False)
            self.onOptionPrice()
            self.optionPriceCheck.Disable()
        else:
            self.differenceCheck.Enable()
            self.optionPriceCheck.Enable()
            if self.current_view != 2:
                self.fillCheck.Enable()

        # reload and replot data
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
            self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
            self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
        #     self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
            self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
            self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
            self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())

        self.Plot_Data()

    def onDifferenceCheck(self, event=None):
        if self.fillCheck.IsChecked():    
            self.fillCheck.SetValue(False)
            self.viewFill = False

        # reload and replot data
        self.delta = self.fileReader.getDeltaValues(self.callRadio.GetValue(), 
            self.deltaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.gamma = self.fileReader.getGammaValues(self.callRadio.GetValue(), 
            self.gammaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        # self.vega = self.fileReader.getVegaValues(self.callRadio.GetValue(), 
        #     self.vegaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.theta = self.fileReader.getThetaValues(self.callRadio.GetValue(), 
            self.thetaCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.rho = self.fileReader.getRhoValues(self.callRadio.GetValue(), 
            self.rhoCheck.IsChecked(), self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.risidual = self.fileReader.getRisidualValues(self.callRadio.GetValue(), self.risidualCheck.IsChecked(),
            self.differenceCheck.IsChecked(), self.effectCheck.IsChecked())
        self.Plot_Data()

    def onStockSlider(self, event=None):
        self.statusbar.SetStatusText("Stock price bump: "+str(self.stockSlider.GetValue()))
        self.stock_bump = self.stockSlider.GetValue()-1
        self.Plot_Data()

    def onRateSlider(self, event=None):
        self.statusbar.SetStatusText("Interest Rate bump: "+str(self.rateSlider.GetValue()))
        self.rate_bump = self.rateSlider.GetValue()-1
        self.Plot_Data()

    def onVolatilSlider(self, event=None):
        self.statusbar.SetStatusText("Volatility bump: "+str(self.volatilSlider.GetValue()))
        self.volitile_bump = self.volatilSlider.GetValue()-1
        self.Plot_Data()

    def ontimeStepSlider(self, event=None):
        self.statusbar.SetStatusText("Time step bump: "+str(self.timeStepSlider.GetValue()))
        self.time_bump = self.timeStepSlider.GetValue()-1
        self.Plot_Data()
        self.Plot_Data()

    """ Graph plotting methods """
    def clearPlots(self):
        """ Clear all graphs and plots for next draw """
        for a in self.fig.axes:
            self.fig.delaxes(a)

    def Plotter_2D_general(self, axes):
        # plot option price graph
        temp_option_price = []
        if len(self.option_price) > 0:
            temp_option_price = numpy.array(map(float, self.option_price[self.stock_bump]))
        if len(self.option_price) > 0:
            axes.plot(self.time, self.option_price[self.stock_bump], color='black')
        
        # stagger plot effects of greeks 
        temp_delta = []
        temp_gamma = []
        temp_theta = []
        temp_rho = []
        temp_risidual = []
        if len(self.delta) > 0:
            temp_delta = numpy.array(self.delta[self.stock_bump])
        if len(self.gamma) > 0:
            temp_gamma = numpy.array(self.gamma[self.stock_bump])
        if len(self.rho) > 0:
            temp_rho = numpy.array(self.rho[self.rate_bump])
        if len(self.theta) > 0:
            temp_theta = numpy.array(self.theta[self.time_bump])
        if len(self.risidual) > 0:
            temp_risidual = numpy.array(self.risidual[self.stock_bump])

        if not self.differenceCheck.IsChecked() and len(temp_option_price) > 0:
            for t in self.time:
                greeks_below = []
                greeks_above = []  # above/below option price
                if t < 30:
                    # sort arrays
                    if len(temp_delta) > 0:
                        if temp_delta[t+1] > temp_option_price[t+1]:
                            greeks_above.append([temp_delta[t:t+2], 'delta'])
                        else:
                            greeks_below.append([temp_delta[t:t+2], 'delta'])
                    if len(temp_gamma) > 0:
                        if temp_gamma[t+1] > temp_option_price[t+1]:
                            greeks_above.append([temp_gamma[t:t+2], 'gamma'])
                        else:
                            greeks_below.append([temp_gamma[t:t+2], 'gamma'])
                    if len(temp_theta) > 0:
                        if temp_theta[t+1] > temp_option_price[t+1]:
                            greeks_above.append([temp_theta[t:t+2], 'theta'])
                        else:
                            greeks_below.append([temp_theta[t:t+2], 'theta'])
                    if len(temp_rho) > 0:
                        if temp_rho[t+1] > temp_option_price[t+1]:
                            greeks_above.append([temp_rho[t:t+2], 'rho'])
                        else:
                            greeks_below.append([temp_rho[t:t+2], 'rho'])
                    if len(temp_risidual) > 0:
                        if temp_risidual[t+1] > temp_option_price[t+1]:
                            greeks_above.append([temp_risidual[t:t+2], 'risidual'])
                        else:
                            greeks_below.append([temp_risidual[t:t+2], 'risidual'])

                    temp_time = numpy.arange(t, t+2, 1)    
                    greeks_above = sorted(greeks_above, key=lambda tup: tup[0][1])
                    greeks_below = sorted(greeks_below, key=lambda tup: tup[0][1], reverse=True)

                    # PLOT stagger greek effects
                    temp_time = numpy.arange(t, t+2, 1)
                    temp1 = numpy.array(temp_option_price[t:t+2])

                    # print(t, greeks_below, greeks_above)
                    for g in greeks_above:
                        # print(t)
                        temp2 = numpy.array([temp_option_price[t], g[0][1]])
                        if self.viewFill and len(self.option_price) > 0:
                            if g[1] == 'delta':
                                axes.fill_between(temp_time, temp2, temp1, color='blue')
                            if g[1] == 'gamma':
                                axes.fill_between(temp_time, temp2, temp1, color='cyan')
                            if g[1] == 'theta':
                                axes.fill_between(temp_time, temp2, temp1, color='green')
                            if g[1] == 'rho':
                                axes.fill_between(temp_time, temp2, temp1, color='darkorange')
                            if g[1] == 'risidual':
                                axes.fill_between(temp_time, temp2, temp1, color='purple')
                        if g[1] == 'delta':
                            axes.plot(temp_time, temp2, label="Delta", color='blue')
                        if g[1] == 'gamma':
                            axes.plot(temp_time, temp2, label="Gamma", color='cyan')
                        if g[1] == 'theta':
                            axes.plot(temp_time, temp2, label="Theta", color='green')
                        if g[1] == 'rho':
                            axes.plot(temp_time, temp2, label="Rho", color='darkorange')
                        if g[1] == 'risidual':
                            axes.plot(temp_time, temp2, label="risidual", color='purple')
                        temp1 = temp2

                    temp1 = numpy.array(temp_option_price[t:t+2])
                    for g in greeks_below:
                        temp2 = numpy.array([temp_option_price[t], g[0][1]])
                        if self.viewFill and len(self.option_price) > 0:
                            if g[1] == 'delta':
                                axes.fill_between(temp_time, temp2, temp1, color='blue')
                            if g[1] == 'gamma':
                                axes.fill_between(temp_time, temp2, temp1, color='cyan')
                            if g[1] == 'theta':
                                axes.fill_between(temp_time, temp2, temp1, color='green')
                            if g[1] == 'rho':
                                axes.fill_between(temp_time, temp2, temp1, color='darkorange')
                            if g[1] == 'risidual':
                                axes.fill_between(temp_time, temp2, temp1, color='purple')
                        if g[1] == 'delta':
                            axes.plot(temp_time, temp2, label="Delta", color='blue')
                        if g[1] == 'gamma':
                            axes.plot(temp_time, temp2, label="Gamma", color='cyan')
                        if g[1] == 'theta':
                            axes.plot(temp_time, temp2, label="Theta", color='green')
                        if g[1] == 'rho':
                            axes.plot(temp_time, temp2, label="Rho", color='darkorange')
                        if g[1] == 'risidual':
                            axes.plot(temp_time, temp2, label="risidual", color='purple')
                        temp1 = temp2
        else:
            # plot difference between greeks and option price
            if len(self.delta) > 0:
                axes.plot(self.delta[self.stock_bump], color='blue')
            if len(self.gamma) > 0:
                axes.plot(self.gamma[self.stock_bump], color='cyan')
            # if len(self.vega) > 0:
            #    axes.plot(self.vega[self.volitile_bump], label="Vega", color='yellow')
            if len(self.theta) > 0:
                axes.plot(self.time, self.theta[self.time_bump], color='green')
            if len(self.rho) > 0:
                axes.plot(self.time, self.rho[self.rate_bump], color='darkorange')
            if len(self.risidual) > 0:
                axes.plot(self.time, self.risidual[self.stock_bump], color='purple')

        if self.viewLegend:
            # use proxy artist
            plot_op = Line2D([], [], linewidth=3, color="black") 
            plot_delta = Line2D([], [], linewidth=3, color="royalblue") 
            plot_gamma = Line2D([], [], linewidth=3, color="cyan") 
            plot_theta = Line2D([], [], linewidth=3, color="green") 
            plot_rho = Line2D([], [], linewidth=3, color="darkorange") 
            plot_risidual = Line2D([], [], linewidth=3, color="purple")

            # Shink current axis by 15%
            box = axes.get_position()
            axes.set_position([box.x0, box.y0, box.width * 0.88, box.height])
            # Put a legend to the right of the current axis
            axes.legend([plot_op, plot_delta, plot_gamma, plot_theta, plot_rho, plot_risidual], ['Option Price', 'Delta', 'Gamma', 'Theta', 'Rho', 'Residual'],
                loc='center left', bbox_to_anchor=(1, 0.5), prop={'size':8})

    def Plot_Data(self):
        if self.current_view == 1:
            self.Plot_Data_advanced()
        elif self.current_view == 2:
            self.Plot_Data_3D()
        elif self.current_view == 0:            
            """ Basic 2D graph plotter """
            self.clearPlots()
            
            self.axes = self.fig.add_subplot(111) # can use add_axes, but then nav-toolbar would not work
            self.axes.set_xlim(0, 30)
            self.axes.grid(self.viewGrid)

            self.Plotter_2D_general(self.axes)

            # set caption and axes labels
            self.axes.set_xlabel('Time (Daily)')
            if self.effectCheck.IsChecked():
                self.axes.set_ylabel('Price (Rands)')
                if hasattr(self, 'title'):
                    self.title.set_text('Option Prices and breakdown of the effects of Greeks')
                else:
                    self.title = self.fig.suptitle('Option Prices and breakdown of the effects of Greeks')
            else:
                self.axes.set_ylabel('Greek Value')
                if hasattr(self, 'title'):
                    self.title.set_text('Raw Greek values not in terms of option price')
                else:
                    self.title = self.fig.suptitle('Greek values not in terms of option price')

            self.canvas.draw()

    def Plot_Data_advanced(self):
        """ Advanced 2D plotter """
        self.clearPlots()
        self.axes2 = self.fig.add_subplot(212)
        self.axes = self.fig.add_subplot(211, sharex=self.axes2)
        self.axes.set_xlim(0, 30)
        self.axes.grid(self.viewGrid)        
        self.axes2.grid(self.viewGrid)

        self.Plotter_2D_general(self.axes)

        # plot strike price and stock price curve
        if self.strike_price > 0:
            self.axes2.plot([0, 30], [self.strike_price, self.strike_price], label="Strike Price", color='red')

        # plot stock price curve
        temp_stock_price = numpy.array(self.stock_price)
        if len(temp_stock_price) > 0:
            self.axes2.plot(self.time, temp_stock_price, label="Stock Price", color='blue')

        # set limits for x and y axes
        # self.axes2.set_xlim(self.time_span_fill[0], self.time_span_fill[-1])
        # self.axes2.set_ylim(min(p, key=float), max(p, key=float))

        # set caption and axes labels
        self.axes2.set_xlabel('Time (Daily)')
        self.axes2.set_ylabel('Price (Rands)')
        if self.effectCheck.IsChecked():
            self.axes.set_ylabel('Price (Rands)')
            if hasattr(self, 'title'):
                self.title.set_text('Option Prices and breakdown of the effects of Greeks')
            else:
                self.title = self.fig.suptitle('Option Prices and breakdown of the effects of Greeks')
        else:
            self.axes.set_ylabel('Greek Value')
            if hasattr(self, 'title'):
                self.title.set_text('Raw Greek values not in terms of option price')
            else:
                self.title = self.fig.suptitle('Greek values not in terms of option price')

        # set useblit True on gtkagg for enhanced performance
        # self.span = SpanSelector(self.axes, self.onselect, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='red'))

        if self.viewLegend:
            # Shink current axis by 15%
            box = self.axes2.get_position()
            self.axes2.set_position([box.x0, box.y0, box.width * 0.88, box.height])
            # Put a legend to the right of the current axis
            self.axes2.legend(loc='center left', bbox_to_anchor=(1, 0.5), prop={'size':8})

        self.canvas.draw()

    def Plot_Data_3D(self):
        """ Advanced 3D plotter """
        # plot graphs
        self.clearPlots()
            
        self.axes = self.fig.add_subplot(111, projection='3d') # can use add_axes, but then nav-toolbar would not work
        self.axes.grid(self.viewGrid)

        b = numpy.arange(0, 9, 1)
        X2D, Y2D = numpy.meshgrid(self.time, b)
        Z2D = None
        
        # plot option price surface
        if len(self.option_price) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.option_price]            
            surf = self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, cmap=cm.afmhot, zorder=0.5)  #cm.coolwarm, cm.winter, cm.autumn
            cbar = self.fig.colorbar(surf, shrink=0.5, aspect=5)
            cbar.set_label('Option Price', rotation=90)

            ### TODO - mayavi ###
            # X2D, Y2D = numpy.mgrid[self.time, b]
            # print(X2D)
            # s = mlab.surf(Z2D)
            # mlab.show()

        # plot greek surfaces
        if len(self.delta) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.delta]
            self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, color='blue')

        if len(self.gamma) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.gamma]
            self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, color='cyan')
                
        if len(self.theta) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.theta]
            self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, color='green')
                
        if len(self.rho) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.rho]
            self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, color='orange')
                
        # if len(self.vega) > 0:
        #     Z2D = [[float(string) for string in inner] for inner in self.vega]
        #     self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
        #         antialiased=False, alpha=0.75, color='aqua')

        if len(self.risidual) > 0:
            Z2D = [[float(string) for string in inner] for inner in self.risidual]
            self.axes.plot_surface(X2D, Y2D, Z2D, rstride=1, cstride=1,
                antialiased=False, alpha=0.75, color='rosybrown')
        
        if Z2D != None:
            # cset1 = self.axes.contourf(X2D, Y2D, Z2D, zdir='z', offset=300, cmap=cm.afmhot)
            self.axes.contourf(X2D, Y2D, Z2D, zdir='x', offset=0, cmap=cm.coolwarm)
            self.axes.contour(X2D, Y2D, Z2D, zdir='y', offset=-0.3, cmap=cm.winter)
            # cset = self.axes.contour(X2D, Y2D, Z2D, zdir='y', offset=10, cmap=cm.afmhot)
            # cbar = self.fig.colorbar(cset1, shrink=0.7, aspect=3)
            # cbar = self.fig.colorbar(cset2, shrink=0.7, aspect=3)
            # cbar = self.fig.colorbar(cset3, shrink=0.5, aspect=5)
            # cbar.set_label('Option Proce', rotation=90)

        # set captions and axis labels
        self.axes.set_xlabel('Time (Days)')
        self.axes.set_xlim(0, 35)
        self.axes.set_ylabel('Bump Size')
        #~ self.axes.set_ylim(-3, 8)
        # self.axes.set_zlabel('Price (Rands)')
        # ~ self.axes.set_zlim(-100, 100)

        if self.effectCheck.IsChecked():
            if self.differenceCheck.IsChecked():
                self.axes.set_zlabel('Greek Value')
            else:
                self.axes.set_zlabel('Price (Rands)')
            if hasattr(self, 'title'):
                self.title.set_text('Option Prices and breakdown of the effects of Greeks')
            else:
                self.title = self.fig.suptitle('Option Prices and breakdown of the effects of Greeks')
        else:
            self.axes.set_zlabel('Greek Value')
            if hasattr(self, 'title'):
                self.title.set_text('Raw Greek values not in terms of option price')
            else:
                self.title = self.fig.suptitle('Greek values not in terms of option price')

        self.canvas.draw()