Code covered by the BSD License  

Highlights from
GARCH Tool

image thumbnail

GARCH Tool

by

 

User Interface for fitting and evaluating a generic GARCH model using the Econometrics Toolbox.

GARCHTool
function GARCHTool
% Function to create a user interface that may be used to fit and evaluate
% a generic GARCH model (AR, MA, ARMA, GARCH) to a given set of loaded
% data.
%
% The data may be loaded from,
% - an Excel file that contains a time series, with dates down the first
%   column and data down the second column.  The first row of the
%   spreadsheet is assumed to be column headers.
% - a MATLAB Workspace variable.  The variable must have a field called
%   dates that is a vector of date numbers such as those generated by the
%   datenum function, and a field called data which must be a numeric
%   vector the same length as the dates.  Any other fields in the structure
%   are ignored.
%
% Once data is loaded the UI allows the user to,
% - process the raw input data by selecting (sub-)ranges, convert to
%   returns, and differencing the data upto twice.
% - view the Auto Correlation and Partial Auto Correlation of the selected
%   data.
% - View the Box-Jenkins Stationarity test for 0-12 lags
% - Specify and fit an ARCH/GARCH model.
%
% Once a model is specified and fit to the data the UI allows the user to,
% - view the fitted model parameters
% - view the Ljung-Box Q test on the standardized innovations and
%   the standardized innovations squared
% - view the autocorrelation of the standardized innovations and
%   the standardized innovations squared
% - plot predictions out into the future
% - plot simulations out into the future
%
% At any stage the raw data, processed data, the model, the prediction and
% the simulation data may be exported to the MATLAB Workspace.  (If a model
% has not yet been fitted and/or a forecast not generated then those fields
% will be empty in the exported data set.)
%
% Those unfamiliar with GARCH modeling should refer to some of the examples
% within the Econometrics Toolbox documentation.
%
% Requirements: The Econometrics Toolbox.

% Author: Phil Goddard (phil@goddardconsulting.ca)
% Date: Q4, 2011

% The code uses the econometrics toolbox
if license('test','econometrics_toolbox')
    localInitUI;
else
    error('The %s requires an Econometrics Toolbox license to run.',mfilename);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to initialize the user interface and create a structure that may
% be used to update all the uicontrols (enable, position, strings, etc);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localInitUI

% Create a figure window that is centred on the screen, 90% of it's height
% and 80% of its width
wFactor = 0.9;
hFactor = 0.8;
screenSize = get(0,'screensize');
figSize = [(screenSize(3)*(1-wFactor))/2 (screenSize(4)*(1-hFactor))/2 ...
    screenSize(3)*wFactor screenSize(4)*hFactor];

% Create the figure window
hf = figure(...,
    'Units','pixels',...
    'Position', figSize,...
    'Toolbar','none',...
    'Menubar','none',...
    'HandleVisibility','callback',...
    'IntegerHandle','off',...
    'NumberTitle','off',...
    'Name', mfilename,...
    'Tag',mfilename,...
    'Visible','off');

% Create and initialize all of the uicontrols
ad = localCreateAllWidgets(hf);

% Assign default strings for various controls
ad.InputFileName = 'Use button at right to select a data file...';
ad.ViewIdx = 1; % Which axes to be showing
ad.DateVec = {'Read from file'};
ad.FittedModelDirty = true;
ad.ForecastDirty = true;
ad = localGetInitialPopupValues(ad);

% save the app data
guidata(hf,ad);

% Update the UI (at this point nothing should really change)
localUpdateUI(hf);

% call the resize function to correctly position the panels/controls
localResizeUI(hf);

% Finally make the figure visible and set a resize function
set(hf,...
    'Visible','on',...
    'ResizeFcn',@localResizeUI);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to create all of the widgets on the UI.
% Their position is set near the end of the function.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localCreateAllWidgets(hf)

% The figure color is needed multiple times so get it here
figColor = get(hf,'Color');

% Create the File Menu
hfm = uimenu('Parent',hf,...
    'Label','File',...
    'Tag','FileMenu');
hfl = uimenu('Parent',hfm,...
    'Label','Load...',...
    'Tag','FileLoadMenu');
uimenu('Parent',hfl,...
    'Label','From File',...
    'Callback',@localInputFileBrowsed,...
    'Tag','FileLoadFromFileMenu');
uimenu('Parent',hfl,...
    'Label','From Workspace',...
    'Callback',@localLoadFromWorkspace,...
    'Tag','FileLoadFromWorkspaceMenu');
hfe = uimenu('Parent',hfm,...
    'Label','Export...',...
    'Enable','off',...
    'Tag','FileExportMenu');
uimenu('Parent',hfe,...
    'Label','To Workspace',...
    'Callback',@localExportToWorkspace,...
    'Tag','FileExportToWorkspaceMenu');
% Craete the Help menu
hhm = uimenu('Parent',hf,...
    'Label','Help',...
    'Tag','HelpMenu');
uimenu('Parent',hhm,...
    'Label',sprintf('%s Help',mfilename),...
    'Callback',@(src,evt)feval(@doc,mfilename),...
    'Tag','FileAppHelpMenu');
uimenu('Parent',hhm,...
    'Label','Econometrics Toolbox Help',...
    'Callback',@(src,evt)feval(@doc,'econ'),...
    'Tag','FileeconHelpMenu');
uimenu('Parent',hhm,...
    'Separator','on',...
    'Label','About',...
    'Callback',@localCreateAboutBox,...
    'Tag','HelpAboutMenu');

% Create the data panel and its contained widgets
hpd = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Data',...
    'BackgroundColor',figColor,...
    'Tag','DataPanel');
uicontrol('Parent',hpd,...
    'Style','text',...
    'Units','pixels',...
    'String','Source:',...
    'BackgroundColor',figColor,...
    'Tag','DataSourceText');
uicontrol('Parent',hpd,...
    'Style','edit',...
    'Units','pixels',...
    'String','Specify a file to load by using the button at the right...',...
    'BackgroundColor',[1 1 1],...
    'HorizontalAlignment','left',...
    'Enable','Inactive',...
    'Tag','DataFileNameEdit');
uicontrol('Parent',hpd,...
    'Style','pushbutton',...
    'Units','pixels',...
    'String','...',...
    'Callback',@localInputFileBrowsed,...
    'BackgroundColor',figColor,...
    'Tag','DataPushbutton');

% Create the View panel and its contained widgets
hpv = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Visualization',...
    'BackgroundColor',figColor,...
    'Tag','ViewPanel');
uicontrol('Parent',hpv,...
    'Style','text',...
    'Units','pixels',...
    'String','View:',...
    'BackgroundColor',figColor,...
    'Tag','VizViewText');
ad.viewStr = {'GARCH Model Equations',...
    'Original (Raw) Time Series',...
    'Preprocessed Time Series',...
    'Aug. Dickey-Fuller Tests',...
    'Auto Correlation',...
    'Partial Auto Correlation',...
    'Fitted Model Parameters',...
    'Auto Corr (Innovations)',...
    'Auto Corr (Innovations^2)',...
    'Forecast'};
uicontrol('Parent',hpv,...
    'Style','popupmenu',...
    'Units','pixels',...
    'String',ad.viewStr,...
    'Enable','off',...
    'Callback',@localViewChanged,...
    'BackgroundColor',[1 1 1],...
    'Tag','VizViewPopupmenu');

% Create the preprocessing panel and its contained widgets
hpp = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Preprocessing',...
    'BackgroundColor',figColor,...
    'Tag','PreprocessingPanel');
textString = {'Start Date:','End Date:',...
    'Transform:','First Difference:','Second Difference:'};
textTag = {'PPSDateText','PPEDateText','PPTText','PPFDText','PPSDText'};
for idx = 1:length(textString)
    uicontrol('Parent',hpp,...
        'Style','text',...
        'Units','pixels',...
        'String',textString{idx},...
        'BackgroundColor',figColor,...
        'Tag',textTag{idx});
end
popupStr = {'22/22/2222','22/22/2222',...
    {'None',...
    'Natural Logarithm',...
    'Convert to Returns (Cont.)',...
    'Convert to Returns (Disc.)'},...
    cellstr(num2str((0:24)')),...
    cellstr(num2str((0:24)'))};
ad.popupTag = {'PPSDatePopup','PPEDatePopup','PPTPopup','PPFDPopup','PPSDPopup'};
for idx = 1:length(popupStr)
    uicontrol('Parent',hpp,...
        'Style','popupmenu',...
        'Units','pixels',...
        'String',popupStr{idx},...
        'Callback',@localPreprocessingChanged,...
        'Enable','off',...
        'BackgroundColor',[1 1 1],...
        'Tag',ad.popupTag{idx});
end
% specify functions for the transforms (and their inverse) here too
ad.TransFcns = {@(x)x,...
    @(x)log(x),...
    @(x)log(x(2:end)./x(1:end-1)),...
    @(x)diff(x)./x(1:end-1)};
% d0 is assumed to be scalar - the last element of the measured data
% data is assume to be a multicolumn martrix of forecast/simulation data
ad.TransFcnsInv = {@(d0,data)[repmat(d0,1,size(data,2));data],...
    @(d0,data)[repmat(d0,1,size(data,2));exp(data)],...
    @(d0,data)d0*cumprod(exp([zeros(1,size(data,2));data])),...
    @(d0,data)d0*cumprod([ones(1,size(data,2));(1+data)])};

% Create the Process Model panel and its contained widgets
hppm = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Process Model (ARMA)',...
    'BackgroundColor',figColor,...
    'Tag','ProcessModelPanel');
uicontrol('Parent',hppm,...
    'Style','checkbox',...
    'Units','pixels',...
    'String','Force C = 0',...
    'Callback',@localModelInfoChanged,...
    'Enable','off',...
    'BackgroundColor',figColor,...
    'Tag','PMForceCCheckbox');
textString = {'Distribution:','AR Terms:','MA Terms:'};
textTag = {'PMDText','PMARText','PMMAText'};
for idx = 1:length(textString)
    uicontrol('Parent',hppm,...
        'Style','text',...
        'Units','pixels',...
        'String',textString{idx},...
        'BackgroundColor',figColor,...
        'Tag',textTag{idx});
end
popupStr = {...
    {'Gaussian','T'},...
    cellstr(num2str((0:10)')),...
    cellstr(num2str((0:10)'))};
popupTag = {'PMDPopup','PMARPopup','PMMAPopup'};
for idx = 1:length(textString)
    uicontrol('Parent',hppm,...
        'Style','popupmenu',...
        'Units','pixels',...
        'String',popupStr{idx},...
        'Callback',@localModelInfoChanged,...
        'Enable','off',...
        'BackgroundColor',[1 1 1],...
        'Tag',popupTag{idx});
end

% Create the Innovations Model panel and its contained widgets
hpim = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Innovations Model (GARCH)',...
    'BackgroundColor',figColor,...
    'Tag','InnovationsModelPanel');
textString = {'P Terms (GARCH):','Q Terms (ARCH):'};
textTag = {'IMPText','IMQText'};
for idx = 1:length(textString)
    uicontrol('Parent',hpim,...
        'Style','text',...
        'Units','pixels',...
        'String',textString{idx},...
        'BackgroundColor',figColor,...
        'Tag',textTag{idx});
end
popupStr = {...
    cellstr(num2str((0:10)')),...
    cellstr(num2str((0:10)'))};
popupTag = {'IMPPopup','IMQPopup'};
for idx = 1:length(textString)
    uicontrol('Parent',hpim,...
        'Style','popupmenu',...
        'Units','pixels',...
        'String',popupStr{idx},...
        'Callback',@localModelInfoChanged,...
        'Enable','off',...
        'BackgroundColor',[1 1 1],...
        'Tag',popupTag{idx});
end

% Create the Forecasting panel and its contained widgets
hpf = uipanel('Parent',hf,...
    'Units','pixels',...
    'Title','Forecasting',...
    'BackgroundColor',figColor,...
    'Tag','ForecastingPanel');
textString = {'Out of Sample Points:','Simulation Paths:'};
textTag = {'FSampleText','FPathsText'};
for idx = 1:length(textString)
    uicontrol('Parent',hpf,...
        'Style','text',...
        'Units','pixels',...
        'String',textString{idx},...
        'BackgroundColor',figColor,...
        'Tag',textTag{idx});
end
popupStr = {...
    {'1';'2';'5';'10';'20';'30';'60';'90'},...
    {'0';'10';'100';'1000';'10000';'50000'}};
popupTag = {'FSamplePopup','FPathsPopup'};
for idx = 1:length(textString)
    uicontrol('Parent',hpf,...
        'Style','popupmenu',...
        'Units','pixels',...
        'String',popupStr{idx},...
        'Callback',@localForecastInfoChanged,...
        'Enable','off',...
        'BackgroundColor',[1 1 1],...
        'Tag',popupTag{idx});
end

% Create the Regenerate Model button
uicontrol('Parent',hf,...
    'Style','pushbutton',...
    'Units','pixels',...
    'String','<html><center>Regenerate<br/>Model</center></html>',...
    'BackgroundColor',[1 1 1],...
    'Callback',@localRegenerateModel,...
    'Enable','off',...
    'Tag','RegenerateModelPushbutton');
% Create the Regenerate Forecast button
uicontrol('Parent',hf,...
    'Style','pushbutton',...
    'Units','pixels',...
    'String','<html><center>Regenerate<br/>Forecast</center></html>',...
    'BackgroundColor',[1 1 1],...
    'Callback',@localRegenerateForecast,...
    'Enable','off',...
    'Tag','RegenerateForecastPushbutton');

% determine default sizes for the controls (based on their strings)
localSetUIControlSize(findall(hf,'Type','uicontrol'));

% create the various axes required (and that can be visualized from
% the Visualization panel).
ad.axesTags = {'EquationsAxis','RawDataAxis','PreprocessedDataAxis',...
    'ADFTestAxis','AutoCorrAxis','ParCorrAxis',....
    'FittedModelAxis','FittedAutoCorrAxis',...
    'FittedAutoCorr2Axis','ForecastAxis'};
for idx = 1:length(ad.axesTags)
    axes('Parent',hf,...
        'Units','pixels',...
        'Tag',ad.axesTags{idx},...
        'Visible','off');
end

% Create the table that will be seen on the ADFTestAxis
uitable('Parent',hf,...
    'Units','pixels',...
    'ColumnName',[{'Lags'};cellstr(num2str((0:12)'))],...
    'RowName',[],...
    'ColumnEditable',false(1,14),...
    'RowStriping','on',...
    'FontSize',12,...
    'FontWeight','bold',...
    'Tag','ADFTestTable',...
    'Visible','off');

% Create the table that will be seen on the FittedModelAxis
uitable('Parent',hf,...
    'Units','pixels',...
    'ColumnName',{'Parameter','Value','Standard Error','T Statistic'},...
    'RowName',[],...
    'ColumnEditable',false(1,4),...
    'RowStriping','on',...
    'FontSize',12,...
    'FontWeight','bold',...
    'Tag','FittedModelTable',...
    'Visible','off');

% Create a handles structure
ad.handles = guihandles(hf);

% Now set the sizes of the panels
% This couldn't be done earlier as it required the controls to be sized
% first
localInitializeUIPanels(ad.handles);

% The equations axis can have some special treatment as it doesn't change
% at any point
set(ad.handles.EquationsAxis,...
    'XTick',[],'YTick',[],...
    'Color',figColor,...
    'XColor',figColor,...
    'YColor',figColor);
title(ad.handles.EquationsAxis,'ARMA/GARCH Model Equations');
% get the equation to be displayed (as a string)
eqnString = localGetEquationString;
% Create a text object to display the string
ad.handles.EquationsText = text(...
    'Parent',ad.handles.EquationsAxis,...
    'Interpreter','latex',...
    'HorizontalAlignment','center',...
    'Position',[0.5 0.5],...
    'String',eqnString,...
    'Visible','on',...
    'Tag','EquationsText',...
    'FontWeight','bold',...
    'FontSize',20);

% The ADF Test axis needs some setup
set(ad.handles.ADFTestAxis,...
    'XTick',[],'YTick',[],...
    'Color',figColor,...
    'XColor',figColor,...
    'YColor',figColor);
title(ad.handles.ADFTestAxis,'Augmented Dickey-Fuller Test Results');
% The fitted model axis needs some setup
set(ad.handles.FittedModelAxis,...
    'XTick',[],'YTick',[],...
    'Color',figColor,...
    'XColor',figColor,...
    'YColor',figColor);
title(ad.handles.FittedModelAxis,'Parameters of the Fitted Model');
% Create a text object to display the string
ad.handles.FittedModelText = text(...
    'Parent',ad.handles.FittedModelAxis,...
    'HorizontalAlignment','center',...
    'Position',[0.5 0.9],...
    'String','',...
    'Visible','off',...
    'Tag','FittedModelText',...
    'FontWeight','bold',...
    'FontSize',12);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to set (an initial) desired size for all uicontrols
% Note: the x and y position aren't important for this function, only the
% width and height of the control.
% This function adds space around the end of the control so that it's
% display looks OK
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localSetUIControlSize(uicontrolHandles)
% Put a different amount of space around a control depending on its type

for idx = 1:length(uicontrolHandles)
    thisControl = uicontrolHandles(idx);
    position = get(thisControl,'Position');
    extent = get(thisControl,'Extent');
    switch get(thisControl,'Style')
        case {'text','edit'}
            set(thisControl,'Position',[position(1:2) extent(3) extent(4)]);
        case 'checkbox'
            % The extend does not allow for the check box itself, only the
            % string, so the control needs to be a little larger to fit
            % both.  Make it larger by the height of the control
            set(thisControl,'Position',[position(1:2) sum(extent(3:4)) extent(4)]);
        case 'popupmenu'
            % These need more work as the longest string needs to be used
            % to determine the largest extent.
            % So loop over all strings, putting them into a temporary text
            % control and getting the extent, and from it the maximum
            % width.
            strings =  get(thisControl,'String');
            hTemp = uicontrol(...
                'Parent',get(thisControl,'Parent'),...
                'Style','text');
            maxWidth = -Inf;
            if iscell(strings)
                % Generally a multi-element pulldown menu
                for jdx = 1:length(strings)
                    set(hTemp,'String',strings{jdx});
                    extent = get(hTemp,'Extent');
                    maxWidth = max(maxWidth,extent(3));
                end
            else
                % generally a single line char array
                for jdx = 1:size(strings,1)
                    set(hTemp,'String',strings(jdx,:));
                    extent = get(hTemp,'Extent');
                    maxWidth = max(maxWidth,extent(3));
                end
            end
            % Delete the temporary control
            delete(hTemp);
            % The width allows for the "selection array" which is about,
            % but slightly smaller that the height, so just use height
            set(thisControl,...
                'Position',...
                [position(1:2) maxWidth+extent(4) extent(4)]);
        case 'pushbutton'
            % Need to check if it has a multiline string in it (which is
            % achieved my using <html> and the tag <br/>
            fullStr = get(thisControl,'String');
            % find any <br/> tags
            [subStrings,loc] = regexp(fullStr,'<br/>','split');
            if isempty(loc)
                % There are no <br/> tags
                set(thisControl,...
                    'Position',...
                    [position(1:2) extent(3)+2*extent(4) 2*extent(4)]);
            else
                % Create a temp text control
                hTemp = uicontrol(...
                    'Parent',get(thisControl,'Parent'),...
                    'Style','text');
                maxWidth = -Inf;
                height = 0;
                % Loop over the substrings removing any additional html
                % tags and putting the rsulting string on a temporary
                % uicontrols and getting their extent.
                for jdx = 1:length(subStrings)
                    % get location of additional tags
                    loc = regexp(subStrings{jdx},'[<>]');
                    % then remove the tags
                    for kdx = round(length(loc)/2):-1:1
                        subStrings{jdx}(loc(2*kdx-1):loc(2*kdx))='';
                    end
                    % Now use the resulting (subsub)string to get the extent
                    set(hTemp,'String',subStrings{jdx});
                    extent = get(hTemp,'Extent');
                    maxWidth = max(maxWidth,extent(3));
                    height = height + extent(4);
                end
                % Delete the temporary control
                delete(hTemp);
                % Set the control width and height (and put in some
                % margins)
                set(thisControl,...
                    'Position',...
                    [position(1:2) maxWidth+extent(4) height+extent(4)]);
            end
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to initialize all uipanels, including all of the controls inside
% each panel.
% This should be called after the UIControls sizes have been set.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localInitializeUIPanels(handles)

% Get margins
[margins,spacing] = localGetMargins;

% get the figure size
fPos = get(handles.(mfilename),'Position');
width = fPos(3);
height = fPos(4);

% Process the Data Panel -- only its height is important here as its width
% and position will be set in the resize function.
editPos = get(handles.DataFileNameEdit,'Position');
panelPos = [margins(1), ...
    height-margins(4)-2*spacing-editPos(4), ...
    width-sum(margins(1:2)), ...
    3*spacing+editPos(4)];
set(handles.DataPanel,'Position',panelPos);

% To size the other panels requires knowing their width which isn't an easy
% calculation as they must be the width of the largest width of several
% controls that are positioned next to each other horizontally.
% Create the visualization panel and its contained widgets
panelWidth = nan(5,1);
% max width for Visualization Panel
vpPos = get(handles.VizViewText,'Position') + ...
    get(handles.VizViewPopupmenu,'Position');
panelWidth(1) = vpPos(3);
% max width for Preprocessing Panel
textTag = {'PPSDateText','PPEDateText','PPTText','PPFDText','PPSDText'};
popupTag = {'PPSDatePopup','PPEDatePopup','PPTPopup','PPFDPopup','PPSDPopup'};
ppPos = nan(length(textTag),4);
for idx = 1:size(ppPos,1)
    ppPos(idx,:) = get(handles.(textTag{idx}),'Position') + ...
        get(handles.(popupTag{idx}),'Position');
end
panelWidth(2) = max(ppPos(:,3));
% max width for Process Model Panel
textTag = {'PMDText','PMARText','PMMAText'};
popupTag = {'PMDPopup','PMARPopup','PMMAPopup'};
pmPos = nan(length(textTag)+1,4);
pmPos(1,:) = get(handles.PMForceCCheckbox,'Position');
for idx = 2:size(pmPos,1)
    pmPos(idx,:) = get(handles.(textTag{idx-1}),'Position') + ...
        get(handles.(popupTag{idx-1}),'Position');
end
panelWidth(3) = max(pmPos(:,3));
% max width for Innovations Model Panel
textTag = {'IMPText','IMQText'};
popupTag = {'IMPPopup','IMQPopup'};
imPos = nan(length(textTag),4);
for idx = 1:size(imPos,1)
    imPos(idx,:) = get(handles.(textTag{idx}),'Position') + ...
        get(handles.(popupTag{idx}),'Position');
end
panelWidth(4) = max(imPos(:,3));
% max width for Forecasting Panel
textTag = {'FSampleText','FPathsText'};
popupTag = {'FSamplePopup','FPathsPopup'};
fpPos = nan(length(textTag),4);
for idx = 1:size(fpPos,1)
    fpPos(idx,:) = get(handles.(textTag{idx}),'Position') + ...
        get(handles.(popupTag{idx}),'Position');
end
panelWidth(5) = max(fpPos(:,3));

% Hence the required width of the panels (except the Data Panel) is
panelWidth = max(panelWidth) + 2*spacing;

% Position the Visualization Panel
% The controls on the panel can also be positioned as they don't change
% when the figure is resized
vvtPos = get(handles.VizViewText,'Position');
vvpuPos = get(handles.VizViewPopupmenu,'Position');
panelPos = [margins(1), ...
    panelPos(2)-2*spacing-2*spacing-vvpuPos(4), ...
    panelWidth, ...
    2*spacing+vvpuPos(4)+spacing];
set(handles.ViewPanel,'Position',panelPos);
set(handles.VizViewText,...
    'Position',[spacing spacing vvtPos(3:4)]);
set(handles.VizViewPopupmenu,...
    'Position',[panelWidth-spacing-vvpuPos(3) spacing vvpuPos(3:4)]);

% Position the Preprocessing Panel
% The controls on the panel can also be positioned as they don't change
% when the figure is resized
panelPos = [margins(1), ...
    panelPos(2)-2*spacing-6*spacing-5*editPos(4), ...
    panelWidth, ...
    7*spacing+5*editPos(4)];
set(handles.PreprocessingPanel,'Position',panelPos);
% Position the text and popupmenus
textTag = {'PPSDateText','PPEDateText','PPTText','PPFDText','PPSDText'};
popupTag = {'PPSDatePopup','PPEDatePopup','PPTPopup','PPFDPopup','PPSDPopup'};
heightOffset = 0;
for idx = length(textTag):-1:1
    tPosNow = get(handles.(textTag{idx}),'Position');
    pPosNow = get(handles.(popupTag{idx}),'Position');
    tNewPos = [spacing spacing+heightOffset tPosNow(3:4)];
    pNewPos = [panelPos(3)-spacing-pPosNow(3),...
        spacing+heightOffset pPosNow(3:4)];
    set(handles.(textTag{idx}),'Position',tNewPos);
    set(handles.(popupTag{idx}),'Position',pNewPos);
    heightOffset = spacing+heightOffset+tPosNow(4);
end

% Position the Process Model Panel
% The controls on the panel can also be positioned as they don't change
% when the figure is resized
panelPos = [margins(1), ...
    panelPos(2)-2*spacing-4*spacing-3*editPos(4), ...
    panelWidth, ...
    6*spacing+4*editPos(4)];
set(handles.ProcessModelPanel,'Position',panelPos);
% Position the text and popupmenus
textTag = {'PMDText','PMARText','PMMAText'};
popupTag = {'PMDPopup','PMARPopup','PMMAPopup'};
heightOffset = 0;
for idx = length(textTag):-1:1
    tPosNow = get(handles.(textTag{idx}),'Position');
    pPosNow = get(handles.(popupTag{idx}),'Position');
    tNewPos = [spacing spacing+heightOffset tPosNow(3:4)];
    pNewPos = [panelPos(3)-spacing-pPosNow(3),...
        spacing+heightOffset pPosNow(3:4)];
    set(handles.(textTag{idx}),'Position',tNewPos);
    set(handles.(popupTag{idx}),'Position',pNewPos);
    heightOffset = spacing+heightOffset+tPosNow(4);
end
% Finally do the check box
ppPosNow = get(handles.PMForceCCheckbox,'Position');
ppNewPos = [spacing spacing+heightOffset ppPosNow(3:4)];
set(handles.PMForceCCheckbox,'Position',ppNewPos);

% Position the Innovations Model Panel
% The controls on the panel can also be positioned as they don't change
% when the figure is resized
panelPos = [margins(1), ...
    panelPos(2)-2*spacing-4*spacing-3*editPos(4), ...
    panelWidth, ...
    4*spacing+2*editPos(4)];
set(handles.InnovationsModelPanel,'Position',panelPos);
% Position the text and popupmenus
textTag = {'IMPText','IMQText'};
popupTag = {'IMPPopup','IMQPopup'};
heightOffset = 0;
for idx = length(textTag):-1:1
    tPosNow = get(handles.(textTag{idx}),'Position');
    pPosNow = get(handles.(popupTag{idx}),'Position');
    tNewPos = [spacing spacing+heightOffset tPosNow(3:4)];
    pNewPos = [panelPos(3)-spacing-pPosNow(3),...
        spacing+heightOffset pPosNow(3:4)];
    set(handles.(textTag{idx}),'Position',tNewPos);
    set(handles.(popupTag{idx}),'Position',pNewPos);
    heightOffset = spacing+heightOffset+tPosNow(4);
end

% Position the Forecasting Model Panel
% The controls on the panel can also be positioned as they don't change
% when the figure is resized
panelPos = [margins(1), ...
    panelPos(2)-2*spacing-2*spacing-editPos(4), ...
    panelWidth, ...
    4*spacing+2*editPos(4)];
set(handles.ForecastingPanel,'Position',panelPos);
textTag = {'FSampleText','FPathsText'};
popupTag = {'FSamplePopup','FPathsPopup'};
heightOffset = 0;
for idx = length(textTag):-1:1
    tPosNow = get(handles.(textTag{idx}),'Position');
    pPosNow = get(handles.(popupTag{idx}),'Position');
    tNewPos = [spacing spacing+heightOffset tPosNow(3:4)];
    pNewPos = [panelPos(3)-spacing-pPosNow(3),...
        spacing+heightOffset pPosNow(3:4)];
    set(handles.(textTag{idx}),'Position',tNewPos);
    set(handles.(popupTag{idx}),'Position',pNewPos);
    heightOffset = spacing+heightOffset+tPosNow(4);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to move required uicontrols when figure is resized
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localResizeUI(src,evt) %#ok

% get the app data
ad = guidata(src);

% Get margins
[margins,spacing] = localGetMargins;

% get the figure size
fPos = get(ad.handles.(mfilename),'Position');
width = fPos(3);
height = fPos(4);

% all the widgets are anchored to the top left of the figure
% Start at the top and work down.
% Do the Data Panel - repositioning the controls in the panel could be done
% in a resizefcn for the panel, but since there are only 3 of them we'll do
% it here for convenient
oldPos = get(ad.handles.DataPanel,'Position');
newPos = [margins(1),...
    height-margins(4)-oldPos(4),...
    width-sum(margins([1 2])),...
    oldPos(4)];
set(ad.handles.DataPanel,'Position',newPos);
% need to know the y-position of the Data Panel later so store it here
yPosDataPanel = newPos(2);

% Now do its widgets
oldTextPos = get(ad.handles.DataSourceText,'Position');
newTextPos = [spacing spacing oldTextPos(3:4)];
set(ad.handles.DataSourceText,'Position', newTextPos);
oldEditPos = get(ad.handles.DataFileNameEdit,'Position');
newEditPos = [sum(newTextPos([1 3])),...
    spacing,...
    newPos(3)-sum(newTextPos([1 3]))-spacing-oldEditPos(4),...
    oldEditPos(4)];
set(ad.handles.DataFileNameEdit,'Position',newEditPos);
%oldButtonPos = get(ad.handles.DataPushbutton,'Position');
newButtonPos = [sum(newEditPos([1 3])),spacing,oldEditPos([4 4])];
set(ad.handles.DataPushbutton,'Position',newButtonPos);

% Loop over the other panels
panelTags = {'ViewPanel','PreprocessingPanel',...
    'ProcessModelPanel','InnovationsModelPanel','ForecastingPanel'};
for idx = 1:length(panelTags)
    oldPos = get(ad.handles.(panelTags{idx}),'Position');
    newPos = [margins(1) newPos(2)-spacing-oldPos(4) oldPos(3:4)];
    set(ad.handles.(panelTags{idx}),'Position',newPos);
end
% Need to know the panel width later so stor it here
panelWidth = newPos(3);

% Now do the Regenerate pushbuttons, which need to be centred under the
% Forecasting Panel
% Need to know the width of both buttons
rgmOldPos = get(ad.handles.RegenerateModelPushbutton,'Position');
rgfOldPos = get(ad.handles.RegenerateForecastPushbutton,'Position');
% TODO: The following assumes the buttons are narrower than the panel,
% which is true in this case but not generically
pbSpace = (panelWidth-(rgmOldPos(3)+rgfOldPos(3)))/3;
% Position the Regenerate Model pushbutton
newPos = [margins(1)+pbSpace,...
    newPos(2)-spacing-rgmOldPos(4) rgmOldPos(3:4)];
set(ad.handles.RegenerateModelPushbutton,'Position',newPos);
% Position the Regenerate Forecast pushbutton
newPos = [margins(1)+2*pbSpace+rgmOldPos(3),...
    newPos(2) rgmOldPos(3:4)];
set(ad.handles.RegenerateForecastPushbutton,'Position',newPos);

% Now reposition all the axes
hAllAxes = findall(ad.handles.(mfilename),'Type','axes');
set(hAllAxes,'OuterPosition',...
    [margins(1)+panelWidth,...
    margins(3),...
    max(1,width-panelWidth-spacing),...
    max(1,yPosDataPanel-margins(3))]);

% Reposition the table that's on the ADFTestAxis
% Make the 6 of the 14 columns span the viewable table
axisPos = get(ad.handles.ADFTestAxis,'Position');
set(ad.handles.ADFTestTable,...
    'Position',[axisPos(1:3) 0.95*axisPos(4)],...
    'ColumnWidth',{axisPos(3)/6});

% Reposition the table that's on the FittedModelAxis
% Make the (4) columns span the entire table
axisPos = get(ad.handles.FittedModelAxis,'Position');
set(ad.handles.FittedModelTable,...
    'Position',[axisPos(1:3) 0.8*axisPos(4)],...
    'ColumnWidth',{axisPos(3)/4});

% Flush the graphics buffer
drawnow

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to specify standard margins and spacing for the controls
% TODO: This is inefficient as it creates and destroys a figure more than
% once.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [margins,spacing] = localGetMargins

% % Create a temporary figure and text control to get its position and use
% % that for the margins
% hfTemp = figure(...
%     'Units','pixels',...
%     'Visible','off',...
%     'HandleVisibility','callback',...
%     'IntegerHandle','off');
% htemp = uicontrol(...
%     'Parent', hfTemp,...
%     'Style','text',...
%     'String','Remove this control');
% htempPos = get(htemp,'Position');
% delete(hfTemp);
% % margins will be [left right bottom top]
% margins = [htempPos(1) htempPos(1) htempPos(2) htempPos(2)];
%
% % define spacing between controls as the same as the bottom margin
% spacing = margins(1)/2;
margins = [20 20 20 20];
spacing = 12;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to create a string containing the ARMA/GARCH Equations
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function eqnFullString = localGetEquationString
% Create the equations
eqnARMA = sprintf('$$%s%s%s$$',...
    'y_{t} = C + ',...
    '\sum_{i=1}^{R}\phi_{i}y_{t-i} + \epsilon_{t} + ',...
    '\sum_{j=1}^{M}\theta_{j}\epsilon_{t-j}');
eqnGARCH = sprintf('$$%s%s%s$$',...
    '\sigma_{t}^{2} = K + ',...
    '\sum_{i=1}^{P}G_{i}\sigma_{t-i}^{2} + ',...
    '\sum_{j=1}^{Q}A_{j}\epsilon_{t-j}^{2}');
eqnConst1 = sprintf('$$%s$$',...
    '\sum_{i=1}^{P}G_{i} + \sum_{j=1}^{Q}A_{j} < 1');
eqnConst2 = '$$K > 0$$';
eqnConst3 = '$$G_i \ge 0$$';
eqnConst4 = '$$A_j \ge 0$$';
eqnFullString = sprintf('%s\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s',...
    eqnARMA,eqnGARCH,...
    ' subject to the constraints ',...
    eqnConst1,eqnConst2,eqnConst3,eqnConst4);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to get initial values for popupmenus
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localGetInitialPopupValues(ad)

ad.StartDateIdx = 1;  % Index into DateVec that is currenlt showing
% The date vector is a litle harder to handle
if isfield(ad,'DateVec') && ~isempty(ad.DateVec)
    % Index into DateVec that is currently showing
    ad.EndDateIdx = length(ad.DateVec);
else
    ad.EndDateIdx = 1;
end
ad.PPTPopupIdx = 1; % Index of the tranformation
ad.PPFDPopupIdx = 1; % Index of the first difference term
ad.PPSDPopupIdx = 1; % Index of the second difference term
ad.modelinfo.PMForceCCheckboxIdx = 1; % Value of the force C = 0 term
ad.modelinfo.PMDPopupIdx = 1; % Index of the selected distribution
ad.modelinfo.PMARPopupIdx = 1; % Index of the AR term
ad.modelinfo.PMMAPopupIdx = 1; % Index of the MA term
ad.modelinfo.IMPPopupIdx = 1; % Index of the P term
ad.modelinfo.IMQPopupIdx = 1; % Index of the Q term
ad.forecast.FSamplePopupIdx = 1; % Index of the forecast samples
ad.forecast.FPathsPopupIdx = 1; % Index of the forecast paths
% Reset the forecast info based on the above Idx being reset
str = get(ad.handles.FSamplePopup,'String');
ad.forecast.horizon = str2double(str{ad.forecast.FSamplePopupIdx});
% Store the requested simulation paths
str = get(ad.handles.FPathsPopup,'String');
ad.forecast.nPaths = str2double(str{ad.forecast.FPathsPopupIdx});

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to update the UI
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localUpdateUI(src)
% get the app data
ad = guidata(src);

% Set the strings into the appropriate uicontrols
set(ad.handles.DataFileNameEdit,'String',ad.InputFileName);
% Set the popupmenus
% visualization panel
set(ad.handles.VizViewPopupmenu,...
    'Value',ad.ViewIdx);
% preprocessing panel
set(ad.handles.PPSDatePopup,...
    'String',ad.DateVec,...
    'Value',ad.StartDateIdx);
set(ad.handles.PPEDatePopup,...
    'String',ad.DateVec,...
    'Value',ad.EndDateIdx);
set(ad.handles.PPTPopup,'Value',ad.PPTPopupIdx);
set(ad.handles.PPFDPopup,'Value',ad.PPFDPopupIdx);
set(ad.handles.PPSDPopup,'Value',ad.PPSDPopupIdx);
% Process model panel
set(ad.handles.PMForceCCheckbox,'Value',ad.modelinfo.PMForceCCheckboxIdx);
set(ad.handles.PMDPopup,'Value',ad.modelinfo.PMDPopupIdx);
set(ad.handles.PMARPopup,'Value',ad.modelinfo.PMARPopupIdx);
set(ad.handles.PMMAPopup,'Value',ad.modelinfo.PMMAPopupIdx);
% Innovations panel
set(ad.handles.IMPPopup,'Value',ad.modelinfo.IMPPopupIdx);
set(ad.handles.IMQPopup,'Value',ad.modelinfo.IMQPopupIdx);
% The ADFTest table
if isfield(ad,'adftest') && ...
        ~isempty(ad.adftest)
    set(ad.handles.ADFTestTable,'Data',ad.adftest);
end
% The Fitted Model Table and Text
if isfield(ad.modelinfo,'fittedmodel') && ...
        ~isempty(ad.modelinfo.fittedmodel)
    % Firstly correctly size the data to be displayed
    tableData = ...
        [ad.modelinfo.fittedmodel.colData;...
        cell(2,4);
        ad.modelinfo.fittedmodel.lbqTableInfo];
    % Now populate the table
    set(ad.handles.FittedModelText,...
        'String',ad.modelinfo.fittedmodel.headerStr);
    set(ad.handles.FittedModelTable,...
        'Data',tableData,...
        'ColumnName',ad.modelinfo.fittedmodel.colHeaders);
end

% Set the view axis and its children's visibilities
for idx = 1:length(ad.axesTags)
    if (idx == ad.ViewIdx)
        set(ad.handles.(ad.axesTags{idx}),'Visible','on');
        set(get(ad.handles.(ad.axesTags{idx}),'Children'),'Visible','on');
    else
        set(ad.handles.(ad.axesTags{idx}),'Visible','off');
        set(get(ad.handles.(ad.axesTags{idx}),'Children'),'Visible','off');
    end
end
% Handle the ADFTest Table
set(ad.handles.ADFTestTable,'Visible',...
    get(ad.handles.ADFTestAxis,'Visible'));

% Handle the Fitted Model Table
set(ad.handles.FittedModelTable,'Visible',...
    get(ad.handles.FittedModelAxis,'Visible'));

% flush the graphics buffer
drawnow

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function for when the input file browser is used
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localInputFileBrowsed(src,evt) %#ok

%Get the app data
ad = guidata(src);
% open a file browser in the directory currently showing (if there is one)
pathstr = fileparts(ad.InputFileName);
FilterSpec = '*.xls;*.xlsx;*.xlsm';
if ~isempty(pathstr)
    FilterSpec = sprintf('%s%s%s',pathstr,filesep,FilterSpec);
end
% Create the selector dialog
DialogTitle = 'Select an input data file...';
[FileName,PathName] = uigetfile(FilterSpec,DialogTitle);

% Process the selection
if FileName == 0
    % Cancel was selected so just stop now
    return
end

% Save the file name
ad.InputFileName = sprintf('%s%s',PathName,FileName);

% Let the user know something is going on by using waitbar and
% changing the cursor
% Change the pointer so it looks like something is happending
currentPointer = get(ad.handles.(mfilename),'Pointer');
set(ad.handles.(mfilename),'Pointer','watch');
drawnow
% Make the waitbar non-closable
hwb = waitbar(1/3,'Please wait while the file is loaded.',...
    'Pointer','watch',...
    'CloseRequestFcn',@(src,evt)error(''));

% Check that the file is of the correct format and reset the UI if it is
try
    % Read the file
    ad = localReadInputFile(ad);
    
    % Update the waitbar
    waitbar(2/3,hwb,'Data in file is being validated.');
    
    % Preprocess the data
    [ad.preDates,ad.preData] = ...
        localPreprocessInputs(ad.DatenumVec,ad.DataVec,...
        ad.StartDateIdx,ad.EndDateIdx,...
        ad.TransFcns{ad.PPTPopupIdx},ad.PPFDPopupIdx-1,ad.PPSDPopupIdx-1);
    
    % Reinitialize the local UI structure with the new data
    ad = localResetUI(ad);
    
    %save the updated app data
    guidata(src,ad);
    
    % Update the waitbar
    waitbar(1,hwb,'Updating the User Interface.');
    
    % update the UI
    localUpdateUI(src);
    
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
catch ME
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    % throw an error
    errordlg(sprintf('%s %s\n\n%s\n%s\n\n%s\n',...
        'There was an error reading the file',...
        FileName,...
        'The specific error was:',...
        ME.message,...
        'Check the file and try again.'),'Error','modal');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function for when the input file browser is used
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localLoadFromWorkspace(src,evt) %#ok

%Get the app data
ad = guidata(src);

% Get the name of the variable in the MATLAB Workspace
allVars = evalin('base','whos');
allNames = {allVars.name};
% IF there are any variables then ask the use to select one
if ~isempty(allNames)
    [nameIdx,ok] = listdlg(...
        'PromptString','Select a Workspace Variable:',...
        'SelectionMode','single',...
        'ListString',allNames);
else
    % There are no variables to select so tell the user and return
    wStr = sprintf('%s\n\n%s',...
        'There are no variables in the Workspace.',...
        'Create an appropriate variable and try again.');
    uiwait(warndlg(wStr,sprintf('%s Warning',mfilename),'modal'));
    return
end

% Do nothing if cancel was pressed
if ~ok
    return;
else
    % load the variable 
    varName = allNames{nameIdx};
    varData = evalin('base',varName);
end

% Specify a file name even though the data came from a variable
ad.InputFileName = sprintf('%s %s',...
    'Read from Workspace variable',varName);

% Let the user know something is going on by using waitbar and
% changing the cursor
% Change the pointer so it looks like something is happending
currentPointer = get(ad.handles.(mfilename),'Pointer');
set(ad.handles.(mfilename),'Pointer','watch');
drawnow
% Make the waitbar non-closable
hwb = waitbar(1/2,'Please wait while the data is loaded.',...
    'Pointer','watch',...
    'CloseRequestFcn',@(src,evt)error(''));

% Check that the data is of the correct format and reset the UI if it is
try    
    % validate the data
    ad = localValidateDataFromWorkspace(ad,varName,varData);
    
    % Reinitialize the local UI structure with the new data
    ad = localResetUI(ad);
    
    % save the updated app data
    guidata(src,ad);
    
    % Update the waitbar
    waitbar(1,hwb,'Updating the User Interface.');
    
    % update the UI
    localUpdateUI(src);
    
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
catch ME
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    % throw an error
    errordlg(sprintf('%s %s\n\n%s\n%s\n\n%s\n',...
        'There is a problem with the data in the workspace variable ',...
        varName,...
        'The specific error was:',...
        ME.message,...
        'Check the variable and try again.'),'Error','modal');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to reset the UI after new data has been read
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localResetUI(ad)

% Set the view to show the raw data plot
ad.ViewIdx = find(strcmp(ad.viewStr,'Original (Raw) Time Series'));

% Get the initial values for the preprocessing panel
ad = localGetInitialPopupValues(ad);

% Make the menus, popupmenus, checkboxes and pushbuttons enabled
hm = findall(ad.handles.(mfilename),'Type','uimenu');
hpu = findall(ad.handles.(mfilename),'Type','uicontrol','Style','popupmenu');
hcb = findall(ad.handles.(mfilename),'Type','uicontrol','Style','checkbox');
hpb = findall(ad.handles.(mfilename),'Type','uicontrol','Style','pushbutton');
set([hm(:);hpu(:);hcb(:);hpb(:)],'Enable','on');

% Update the axes
ad = localUpdatePreProcessAxes(ad);

% Indicate that the model is now dirty
ad.FittedModelDirty = true;
ad.ForecastDirty = true;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to read the specified input file
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localReadInputFile(ad)

% read the file
[~,~,ad.rawData] = xlsread(ad.InputFileName);
% update the app data
ad.DateVec = ad.rawData(2:end,1);
% Validate the data that was read
[ad.DatenumVec,ad.DataVec,ad.DateVec] = ...
    localValidateInputSpreadsheet(ad.rawData(1,:),...
    ad.rawData(2:end,1),ad.rawData(2:end,2:end));
% Initially show all data
ad.StartDateIdx = 1;
ad.EndDateIdx = length(ad.DateVec);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to validate a variable read from the workspace
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localValidateDataFromWorkspace(ad, varName, varData)

% It must be a structure
if isempty(varData) || ~isstruct(varData)
    error('The variable must be a structure.');
end
% It must contain fields called 'data' and 'dates' and they must be numeric
% vector of data
requiredFields = {'data','dates'};
for idx = 1:length(requiredFields)
    if ~isfield(varData,requiredFields{idx});
        error('The variable must have a field called %s.',...
            requiredFields{idx});
    elseif isempty(requiredFields{idx}) ...
            || ~isnumeric(varData.(requiredFields{idx})) ...
            || numel(varData.(requiredFields{idx})) ...
                            ~= length(varData.(requiredFields{idx}))
        error('The field %s.%s must be a numeric vector.',...
            varName, requiredFields{idx});
    end
end
% The dates and data must be the same length
if length(varData.data) ~= length(varData.dates)
    error('The dates and data vectors must be the same length.');
end

% check if the dates seem sequential
if ~all(diff(varData.dates) > 0) && ~all(diff(varData.dates) < 0)
    wStr = sprintf('%s\n\n%s %s',...
        'The dates appear to be non sequential or duplicated.',...
        mfilename,...
        'will still work, however the results may be unexpected.');
   uiwait(warndlg(wStr,sprintf('%s Warning',mfilename),'modal'));
end

% Everything seems Ok so populate the appdata
ad.DatenumVec =  varData.dates;
ad.DateVec = datestr(ad.DatenumVec);
ad.DataVec = varData.data;
ad.preDates = ad.DatenumVec;  % use all of them to start
ad.preData = ad.DataVec;  % use all of it to start
ad.StartDateIdx = 1;
ad.EndDateIdx = length(ad.preDates);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to check the format of the spreadsheet
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dates,data,ListOfDates] = ...
    localValidateInputSpreadsheet(ListOfTickers,ListOfDates,Data)

% The first row must be strings
if ~all(cellfun(@ischar,ListOfTickers))
    error('%s%s\n%s',...
        'The first row of the input spreadsheet must contain only ',...
        'names (i.e. words, not numbers).');
end
% The dates must be able to be converted to datenums
try
    % Firstly remove any elements that correspond to NaN in the dates
    nanIdx = cellfun(@(x)isnumeric(x)&&isnan(x),ListOfDates);
    if any(nanIdx)
        ListOfDates(nanIdx)=[];
        Data(nanIdx)=[];
    end
    % Tru to convert the dates
    dates = datenum(ListOfDates,'mm/dd/yyyy');
    % Sometimes that's the wrong format.
    % The following is not bullet proof but provides some instances where
    % the date format is likely _NOT_ 'mm/dd/yyyy'
    diffDates = diff(dates);
    if ~all(diffDates>0) ...
            || ~all(diffDates<0) ...
            || max(diffDates) > 180 ...
            || min(diffDates) < -180
        % the date format may be wrong so try another format
        dates = datenum(ListOfDates,'dd/mm/yyyy');
    end
catch ME
    error('%s%s\n%s',...
        'The first column of the input spreadsheet must contain only ',...
        'dates.');
end
% The data must be numeric
try
    data = cell2mat(Data);
catch ME
    error('%s%s\n%s',...
        'The data in the input spreadsheet must contain only ',...
        'numeric returns (or be empty).');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to convert the raw data according to the pre-process
% specifications dialogs
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dates,data] = ...
    localPreprocessInputs(dates,data,sdidx,edidx,transFcn,fDiff,sDiff)

%TODO: this function needs error checking
% select the subregion for the data
data = data(sdidx:edidx);
dates = dates(sdidx:edidx);

% perform the transformation
data = feval(transFcn,data);

% Perform the differences
dVal = [fDiff,sDiff];
for idx = 1:length(dVal)
    if length(data) < (dVal(idx)+1)
        error('%s %d%s',...
            'There are not enough points in the dataset to take the',...
            dVal(idx),'-th difference');
    else
        if dVal(idx) > 0
            % create a filter to do the difference
            num = [1 zeros(1,dVal(idx)-1) -1];
            data = filter(num,1,data);
            % The first few points aren't "good" so drop them
            data(1:dVal(idx)) = [];
        end
    end
end

% the dates may need to be reduced due the transformation, i.e. if
% converted to returns then there will be one fewer point.
dates = dates((end-length(data)+1):end);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to change which View axis is currently visible
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localViewChanged(src,evt) %#ok

% get the app data
ad = guidata(src);

% get the index of the View to be displayed
newViewIdx = get(src,'Value');

% Process differently depending on which View has been selected
switch ad.viewStr{newViewIdx}
    case {'Fitted Model Parameters',...
            'Auto Corr (Innovations)',...
            'Auto Corr (Innovations^2)'}
        % If the view changed to the Fitted Model Axis or one of the
        % autocorrelations related to the fitted model then update the
        % fitted model if the displayed model is currently dirty
        % Save the new view
        ad.ViewIdx = newViewIdx;
        % save the changed app data
        guidata(src,ad);
        if ad.FittedModelDirty
            % The model needs regenerating
            localRegenerateModel(src);
            % The fitted model is no longer dirty
            ad.FittedModelDirty = false;
        end
        % update the ui accordingly
        localUpdateUI(src);
    case 'Forecast'
        % If the view changed to the Forecast Axis then update the
        % forecast if it is currently dirty
        % Save the new view
        ad.ViewIdx = newViewIdx;
        % save the changed app data
        guidata(src,ad);
        if ad.ForecastDirty
            % The model needs regenerating
            localRegenerateForecast(src);
            % The fitted model is no longer dirty
            ad.ForecastDirty = false;
        end
        % update the ui accordingly
        localUpdateUI(src);
    otherwise
        % Only do something if the selection has actually changed
        if newViewIdx ~= ad.ViewIdx
            % Save the new view
            ad.ViewIdx = newViewIdx;
            % save the changed app data
            guidata(src,ad);
            % update the ui accordingly
            localUpdateUI(src);
        end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to change the data when the preprocessing popupmenus change
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localPreprocessingChanged(src,evt) %#ok

% get the app data
ad = guidata(src);

% Get the current state of the preprocessing panel
ad.StartDateIdx = get(ad.handles.PPSDatePopup,'Value');
ad.EndDateIdx = get(ad.handles.PPEDatePopup,'Value');
ad.PPTPopupIdx = get(ad.handles.PPTPopup,'Value');
ad.PPFDPopupIdx = get(ad.handles.PPFDPopup,'Value');
ad.PPSDPopupIdx = get(ad.handles.PPSDPopup,'Value');

% Preprocess the data
[ad.preDates,ad.preData] = ...
    localPreprocessInputs(ad.DatenumVec,ad.DataVec,...
    ad.StartDateIdx,ad.EndDateIdx,...
    ad.TransFcns{ad.PPTPopupIdx},ad.PPFDPopupIdx-1,ad.PPSDPopupIdx-1);

% Update the axes
ad = localUpdatePreProcessAxes(ad);

% Indicate that the model is now out of data
ad.FittedModelDirty = true;
ad.ForecastDirty = true;

% Save the updated app data
guidata(src,ad);

% update the ui accordingly
localUpdateUI(src);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to update all the axes that can be plotted to
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localUpdatePreProcessAxes(ad)

%ad.axesTags = {'EquationsAxis','RawDataAxis','PreprocessedDataAxis',...
%    'AutoCorrAxis','ParCorrAxis','FittedModelAxis','ForecastAxis'};

% The Raw Data Axis and the PreprocessedData axis are similar so can
% be done in an appropriately initialized loop.
lineTags = {'RawDataLine','PreprocessedLine'};
axisTags = {'RawDataAxis','PreprocessedDataAxis'};
titleStr = {'Raw Data','Preprocessed Data'};
xData = {ad.DatenumVec,ad.preDates};
yData = {ad.DataVec,ad.preData};

for idx = 1:length(lineTags)
    % Update the raw data axes
    if ~isfield(ad.handles,lineTags{idx})
        % create a line on the axis for future use
        ad.handles.(lineTags{idx}) = ...
            line(...
            'Parent',ad.handles.(axisTags{idx}),...
            'Color',[0 0 1],...
            'Tag',lineTags{idx},...
            'Visible','off');
    end
    set(ad.handles.(lineTags{idx}),...
        'XData',xData{idx},'YData',yData{idx});
    axis(ad.handles.(axisTags{idx}),'tight');
    title(ad.handles.(axisTags{idx}),titleStr{idx});
    xlabel(ad.handles.(axisTags{idx}),'Dates');
    ylabel(ad.handles.(axisTags{idx}),'Data');
    datetick(ad.handles.(axisTags{idx}),'x');
    grid(ad.handles.(axisTags{idx}),'on');
end

% The autocorr and parcorr axes are a little harder to handle as they
% need to be copied from a temporary figure (since there is no mechanism
% to pass the autocorr and parcorr functions a specific axis handle and get
% them to plot to that axis.
% nPoints is the number of lags to include in the plot, and is based on the
% lags that have been specified (to account for seasonality)
nLags = max(20,3*((ad.PPFDPopupIdx-1)+(ad.PPSDPopupIdx-1)));
ad.handles.AutoCorrAxis = ...
    localUpdateAutoParCorrAxis(...
    ad.handles.AutoCorrAxis,@autocorr,ad.preData,nLags);
ad.handles.ParCorrAxis = ...
    localUpdateAutoParCorrAxis(...
    ad.handles.ParCorrAxis,@parcorr,ad.preData,nLags);

% Finally do the Augmented Dickey Fuller Tests
ad.adftest = localUpdateADFTestTable(ad.preData);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to update either the autocorr or the partial auto correlation
% axes.
% The whichFcn input should be a either @autocorr or @parcorr.
% The update is achieved by creating the required plot on a temporary
% invisible figure and then copying it to GARCHTool figure.  As part of
% doing this the axis handle needs to be updated.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function haNew = localUpdateAutoParCorrAxis(haOld,whichFcn,data,nLags)

% Create an invisible temporary figure the same size at the viewer
hf = get(haOld,'Parent');
hfTemp = figure(...
    'Units',get(hf,'Units'),...
    'Position',get(hf,'Position'),...
    'Visible','off');
% perform the autocorr which will plot to the invisible figure
try
    feval(whichFcn,data,nLags);
    % set the position of the axis to the same as for the viewer
    set(gca,...
        'Units',get(haOld,'Units'),...
        'OuterPosition',get(haOld,'OuterPosition'),...
        'Visible','off');
    % copy the autocorr into the viewer
    haNew = copyobj(gca,hf);
    set(haNew,'Tag',get(haOld,'Tag'));
    set(get(haNew,'Children'),'Visible','off');
    % delete the temporary figure
    delete(hfTemp);
    % delete the old axis
    delete(haOld);
catch ME
    % clean-up handles
    if exist('hfTemp','var') && ~isempty(hfTemp) && ishandle(hfTemp)
        delete(hfTemp);
    end
    if exist('haNew','var') && ~isempty(haNew) && ishandle(haNew)
        delete(haNew);
    end
    if exist('haOld','var') && ~isempty(haOld) && ishandle(haOld)
        haNew = haOld;
    else
        eStr = sprintf('%s%s%s\n\n%s\n',...
            'A fatal error has occured and ',...
            mfilename,...
            ' is being closed.',...
            'The specific error was:',...
            ME.message);
        errordlg(eStr,'Fatal Error','modal');
        if exist('hf','var') && ~isempty(hf) && ishandle(hf)
            delete(hf);
        end
    end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to perform the required augmented Dickey-Fuller tests.
% The results must be returned in a cell array that has 14 columns
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function adfResults = localUpdateADFTestTable(data)

% The result will have 17 rows
adfResults = cell(17,14);

% There are 3 models to try
mTypes = {'AR','AR With Drift','Trend Stationary'};
mNames = {'AR','ARD','TS'};
statNames = {'Decision Variable';'P-Value';'Test Statistic';'Critical Value'};

% The adftest can throw some warnings so turn them off
wState = cell(2);
wState{1} = warning('query','econ:adftest:LeftTailStatTooSmall');
wState{2} = warning('query','econ:adftest:InvalidStatistic');
warning('off','econ:adftest:LeftTailStatTooSmall');
warning('off','econ:adftest:InvalidStatistic');

% loop over the 3 models
for idx = 1:length(mNames)
    % The result to be displayed needs to be changed if a
    % econ:adftest:InvalidStatistic warning is thrown so reset the
    % warning message so that lastwarn is empty
    lastwarn('');
    % Create an index specifying the location in the table to write the
    % results for the current test
    jdx = 5*(idx-1)+idx;
    % Put the headers into the table
    adfResults{jdx,1} = mTypes{idx};
    adfResults(jdx+1:jdx+length(statNames),1) = statNames;
    % Run the test
    try
        [h,pValue,stat,cValue] = adftest(data,'model',mNames{idx},'Lags',0:12);
        % check the state of the warning
        [~,wID] = lastwarn;
        switch wID
            case 'econ:adftest:InvalidStatistic'
                % The test is invalid so report the fact
                adfResults(jdx+(1:4),2:end) = {'Invalid'};
            otherwise
                % The test is fine so populate the table
                adfResults(jdx+1,2:end) = num2cell(double(h));
                adfResults(jdx+2,2:end) = num2cell(pValue);
                adfResults(jdx+3,2:end) = num2cell(stat);
                adfResults(jdx+4,2:end) = num2cell(cValue);
        end
    catch ME
        % fill this part of the table with an error message
        adfResults(jdx+1,2:end) = {'ADFTest cannot be calculated'};
        adfResults(jdx+2,2:end) = {'The error was:'};
        adfResults(jdx+3,2:end) = {ME.message};
    end
end
% turn the warnings back to their original state
for idx = 1:length(wState)
    warning(wState{idx});
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to update the forecast axis
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ad = localUpdateForecastAxis(ha,ad)

% Delete all children of the axis (so that we can start fresh)
delete(get(ha,'Children'));

% Define some data
dates = ad.DatenumVec;
data = ad.DataVec;
sdidx = ad.StartDateIdx;
edidx = ad.EndDateIdx;
transFcn = ad.TransFcns{ad.PPTPopupIdx};
transFcnInv = ad.TransFcnsInv{ad.PPTPopupIdx};
fDiff = ad.PPFDPopupIdx-1;
sDiff = ad.PPSDPopupIdx-1;

% Need to get the data that would result from the transformation and
% first difference
[fdDates,fdData] = ...
    localPreprocessInputs(dates,data,sdidx,edidx,transFcn,fDiff,0);
% Get the forecast based on the (non-random) forecast data
% Reverse the second difference
[~,forecastTemp] = ...
    localReverseDiff(fdDates,fdData,sDiff,ad.forecast.usingPredData);

% Need to get the data that would result from just the transformation
[tDates,tData] = ...
    localPreprocessInputs(dates,data,sdidx,edidx,transFcn,0,0);
% Reverse the first difference
[forecastDates,forecastData] = ...
    localReverseDiff(tDates,tData,fDiff,forecastTemp);
% Now undo the transformation
ad.forecast.forecastData = feval(transFcnInv,data(edidx),forecastData);
ad.forecast.forecastDates = [dates(edidx);forecastDates];

% Get the forecast based on the (random) simulation data
% Reverse the second difference
if ad.forecast.nPaths > 0
    [~,simulationTemp] = ...
        localReverseDiff(fdDates,fdData,sDiff,ad.forecast.ySim);
    % Reverse the first difference
    [simulationDates,simulationData] = ...
        localReverseDiff(tDates,tData,fDiff,simulationTemp);
    % Now undo the transformation
    ad.forecast.simulationData = feval(transFcnInv,data(edidx),simulationData);
    ad.forecast.simulationDates = [dates(edidx);simulationDates];
end

% Plot the raw data
% Get the data to plot
dataToPlot = data(sdidx:edidx);
datesToPlot = dates(sdidx:edidx);
% Determine how many in-sample points to plot based on the requested number
% of out of sample points
%nInSample = 10*ceil(ad.forecast.horizon/10);
%nInSample = round(length(dates/4));
%nInSample = min(length(dataToPlot),10*ceil(ad.forecast.horizon));
%nInSample = length(dataToPlot);
nInSample = max(round(length(dataToPlot)/4),10*ceil(ad.forecast.horizon));

% ensure too many points haven't been selected
nInSample = min(length(dataToPlot),nInSample);

line('Parent',ha,...
    'XData',datesToPlot((end-nInSample+1):end),...
    'YData',dataToPlot((end-nInSample+1):end),...
    'Color',[0 0 1],...
    'Visible','off');
% Plot the simulation
if ad.forecast.nPaths > 0
    line(ad.forecast.simulationDates,ad.forecast.simulationData,...
        'Parent',ha,...
        'Visible','off');
end
% Plot the forecast (Do this last so it's on top)
line('Parent',ha,...
    'XData',ad.forecast.forecastDates,...
    'YData',ad.forecast.forecastData,...
    'Color',[0 0 1],...
    'LineWidth',2,...
    'Visible','off');

% Format the axis
axis(ha,'tight');
title(ha,'Forecast and Simulation');
xlabel(ha,'Dates');
ylabel(ha,'Data');
datetick(ha,'x');
grid(ha,'on');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to "un"-difference the a time series
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [dates,forecast] = localReverseDiff(dates,data,nDiff,forecast)

% reverse the preprocessing differencing
if length(data) < (nDiff+1)
    error('%s %d%s',...
        'There are not enough points in the dataset to undo the',...
        nDiff,'-th difference');
else
    if nDiff > 0
        % Form the columns of data required to "undo" the differencing
        unDiff = [repmat(data((end-nDiff+1):end),1,size(forecast,2));forecast];
        % Do the "un"-differencing
        for jdx = (nDiff+1):size(unDiff,1)
            unDiff(jdx,:) = unDiff(jdx,:) + unDiff(jdx-nDiff,:);
        end
        % The first few points aren't part of the forecast so drop them
        forecast = unDiff((nDiff+1):end,:);
    end
end

% Guestimate dates for the forecast period
dates = cumsum([dates(end);diff(dates((end-size(forecast,1)):end))]);
dates(1) = [];

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function to process a change in the model info popup menus
% and check boxes
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localModelInfoChanged(src,evt) %#ok

% get the app data
ad = guidata(src);

% The app data property that changes is related to the tag of the uicontrol
% that has changed
ad.modelinfo.(sprintf('%sIdx',get(src,'Tag'))) = get(src,'Value');
% The model is now dirty
ad.FittedModelDirty = true;
ad.ForecastDirty = true;

% save the change
guidata(src,ad);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function to process a change in the forecast info popup menus
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localForecastInfoChanged(src,evt) %#ok

% get the app data
ad = guidata(src);

% The app data property that changes is related to the tag of the uicontrol
% that has changed
ad.forecast.(sprintf('%sIdx',get(src,'Tag'))) = get(src,'Value');

% Store the requested horizon
str = get(ad.handles.FSamplePopup,'String');
ad.forecast.horizon = str2double(str{ad.forecast.FSamplePopupIdx});
% Store the requested simulation paths
str = get(ad.handles.FPathsPopup,'String');
ad.forecast.nPaths = str2double(str{ad.forecast.FPathsPopupIdx});

% The forecast is now dirty
ad.ForecastDirty = true;

% save the change
guidata(src,ad);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function to generate the ARMA/GARCH model parameters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localRegenerateModel(src,evt) %#ok

% get the app data
ad = guidata(src);

% Try to do the optimization but if something goes wrong then
% create an error dialog

% Let the user know something is going on by using a non-closeable waitbar
% and changing the cursor
currentPointer = get(ad.handles.(mfilename),'Pointer');
set(ad.handles.(mfilename),'Pointer','watch');
% This can take a while so put up a non-closable waitbar
hwb = waitbar(1/2,'Please wait while the model parameters are estimated.',...
    'Pointer','watch',...
    'CloseRequestFcn',@(src,evt)error(''));
drawnow

try
    % If there's no data then throw an appropriate error
    if ~isfield(ad,'preData') || isempty(ad.preData)
        error('There is no data loaded.');
    end
    
    % Create a GARCH specification set
    spec = garchset(...
        'R',ad.modelinfo.PMARPopupIdx-1,...
        'M',ad.modelinfo.PMMAPopupIdx-1,...
        'P',ad.modelinfo.IMPPopupIdx-1,...
        'Q',ad.modelinfo.IMQPopupIdx-1,...
        'Display','off');
    % The C value needs special processing
    if ad.modelinfo.PMForceCCheckboxIdx
        spec = garchset(spec,'C',nan,'FixC',true);
    end
    % and so does the distribution
    if ad.modelinfo.PMDPopupIdx > 1
        spec = garchset(spec,'Distribution','T');
    end
    
    % Fit the GARCH (ARMA) model
    [ad.modelinfo.fittedmodel.coeff,...
        ad.modelinfo.fittedmodel.errors,...
        ~,...
        ad.modelinfo.fittedmodel.eFit,...
        ad.modelinfo.fittedmodel.sFit] = garchfit(spec,ad.preData);
    % Create the string to update the Fitted Model Table
    [ad.modelinfo.fittedmodel.headerStr,...
        ad.modelinfo.fittedmodel.colHeaders,...
        ad.modelinfo.fittedmodel.colData] = ...
        LocalGarchdispForTable(...
        ad.modelinfo.fittedmodel.coeff,...
        ad.modelinfo.fittedmodel.errors);
    % Perform the Ljung-Box Q test on the standardized innovations and
    % the standardized innovations squared
    sResiduals = ad.modelinfo.fittedmodel.eFit./ad.modelinfo.fittedmodel.sFit;
    [lbq(1).h,lbq(1).p,lbq(1).stat,lbq(1).CV] = lbqtest(sResiduals);
    [lbq(2).h,lbq(2).p,lbq(2).stat,lbq(2).CV] = lbqtest(sResiduals.^2);
    ad.modelinfo.fittedmodel.lbq = lbq;
    % Create a cell array of lbq test results to display in the
    % Fitted Model Table
    ad.modelinfo.fittedmodel.lbqTableInfo = ...
        localCreateLBQTableInfo(lbq);
    
    % Update the autocorr of the innovations and innovations squared.
    % nPoints is the number of lags to include in the plot, and is based on the
    % lags that have been specified (to account for seasonality)
    nLags = max(20,3*((ad.PPFDPopupIdx-1)+(ad.PPSDPopupIdx-1)));
    ad.handles.FittedAutoCorrAxis = ...
        localUpdateAutoParCorrAxis(...
        ad.handles.FittedAutoCorrAxis,@autocorr,sResiduals,nLags);
    title(ad.handles.FittedAutoCorrAxis,...
        'Auto Correlation of the Standardized Innovations');
    ad.handles.FittedAutoCorr2Axis = ...
        localUpdateAutoParCorrAxis(...
        ad.handles.FittedAutoCorr2Axis,@autocorr,sResiduals.^2,nLags);
    title(ad.handles.FittedAutoCorr2Axis,...
        'Auto Correlation of the Standardized Innovations Squared');
    
    % If it was the Regenerate Model pushbutton that got us here then
    % change the view
    if strcmp(get(src,'Type'),'uicontrol') ...
            && strcmp(get(src,'Style'),'pushbutton')
        ad.ViewIdx = find(strcmp(ad.viewStr,'Fitted Model Parameters'));
    end
    
    % save the app data
    guidata(src,ad);
    
    % update the UI
    localUpdateUI(src);
    
    % The model is now clean
    ad.FittedModelDirty = false;
    
    % save the app data
    guidata(src,ad);
    
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    
catch ME
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    % Throw up an error dialog and stop
    errordlg(ME.message,sprintf('%s Error',mfilename),'modal');
    return
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Callback function to generate the forecast and simulation
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localRegenerateForecast(src,evt) %#ok

% get the app data
ad = guidata(src);

% Try to do the forecast but if something goes wrong then
% create an error dialog

% Let the user know something is going on by using a non-closeable waitbar
% and changing the cursor
currentPointer = get(ad.handles.(mfilename),'Pointer');
set(ad.handles.(mfilename),'Pointer','watch');
% This can take a while so put up a non-closable waitbar
hwb = waitbar(1/2,'Please wait while the forecast is recalculated.',...
    'Pointer','watch',...
    'CloseRequestFcn',@(src,evt)error(''));
drawnow

try
    % Error if there is no model to forecast or it is potentially out of
    % date
    if (~isfield(ad,'modelinfo') ||...
            isempty(ad.modelinfo) ||...
            ad.FittedModelDirty)
        error('%s\n%s',...
            'A model has not yet been created or is potentially out of date.',...
            '(Re)generate a model then try again.');
    end
    
    % Get the requested horizon
    str = get(ad.handles.FSamplePopup,'String');
    ad.forecast.horizon = str2double(str{ad.forecast.FSamplePopupIdx});
    % Get the requested simulation paths
    str = get(ad.handles.FPathsPopup,'String');
    ad.forecast.nPaths = str2double(str{ad.forecast.FPathsPopupIdx});
    
    % Perform a prediction
    [~,ad.forecast.usingPredData] = garchpred(...
        ad.modelinfo.fittedmodel.coeff,...
        ad.preData,ad.forecast.horizon);
    % Perform a simulation
    if ad.forecast.nPaths > 0
        [~,~,ad.forecast.ySim] = garchsim(...
            ad.modelinfo.fittedmodel.coeff,...
            ad.forecast.horizon,...
            ad.forecast.nPaths,...
            [],[],[], ...
            ad.modelinfo.fittedmodel.eFit,...
            ad.modelinfo.fittedmodel.sFit,...
            ad.preData);
    end
    
    % Update the forecast axis
    ad = localUpdateForecastAxis(ad.handles.ForecastAxis,ad);
    
    % Change the view
    ad.ViewIdx = find(strcmp(ad.viewStr,'Forecast'));
    
    % save the app data
    guidata(src,ad);
    
    % update the UI
    localUpdateUI(src);
    
    % The forecast is now clean
    ad.ForecastDirty = false;
    
    % save the app data
    guidata(src,ad);
    
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    
catch ME
    % destroy the waitbar
    if exist('hwb','var') && ishandle(hwb)
        delete(hwb);
    end
    % reset the pointer
    set(ad.handles.(mfilename),'Pointer',currentPointer);
    drawnow
    % Throw up an error dialog and stop
    errordlg(ME.message,sprintf('%s Error',mfilename),'modal');
    return
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to create a cell array for displaying the LBQ test info on the
% Fitted Model Table
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function lbqTestCell = localCreateLBQTableInfo(lbqTestStruct)

lbqTestCell = cell(5,4);
lbqTestCell(1,1:3) = {'LBQ Test Stats','Innovations','Innovations^2'};
lbqTestCell(2:end,1) = ...
    {'Decision Variable';'P-Value';'Test Statistic';'Critical Value'};
for idx = 1:length(lbqTestStruct)
    lbqTestCell(2:end,idx+1) = ...
        {double(lbqTestStruct(idx).h);
        lbqTestStruct(idx).p;
        lbqTestStruct(idx).stat;
        lbqTestStruct(idx).CV};
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to create cell arrays suitable for displaying the fitted model
% data on the Fitted Model Table
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [headerStr,colHeaders,colData] = ...
    LocalGarchdispForTable(EstSpec,EstSE)
%
% This is a copy of the standard garchdisp function from the Econometrics
% Toolbox, but rather than printing to the Command Window the display is
% placed into a cell array suitable for displaying in a uitable.
%
if nargin < 2
    
    error('econ:garchdisp:UnspecifiedInput',...
        'Coefficient and standard error \nspecification structures required.')
    
end

if ~isstruct(EstSpec)
    
    error('econ:garchdisp:NonStructureSpec',...
        'Coefficient estimates must be \ngiven in a specification structure.')
    
end

if ~isstruct(EstSE)
    
    error('econ:garchdisp:NonStructureErrors',...
        'Standard errors must be \ngiven in a specification structure.')
    
end

% Extract model orders:

R = garchget(EstSpec,'R'); % Conditional mean AR order
M = garchget(EstSpec,'M'); % Conditional mean MA order
P = garchget(EstSpec,'P'); % Conditional variance order for lagged variances
Q = garchget(EstSpec,'Q'); % Conditional variance order for lagged residuals
nX = length(garchget(EstSpec,'Regress')); % Number of regressors

% Set flags:

if isnan(garchget(EstSpec,'C'))
    isConstantInMean = false; % Mean equation constant is not included
else
    isConstantInMean = true; % Mean equation includes a constant
end

varianceModel = garchget(EstSpec,'VarianceModel');
[varianceModel,InMeanString] = strtok(varianceModel,'-');
isVarianceInMean = ~isempty(InMeanString);

distribution = lower(garchget(EstSpec,'Distribution'));
distribution = distribution(~isspace(distribution));
distribution(1) = upper(distribution(1)); % Capitalize the first letter
isDistributionT = strcmpi(distribution,'T'); % Flag t distribution

% Extract parameter estimates:

C       = garchget(EstSpec,'C'); % Conditional mean constant
AR      = garchget(EstSpec,'AR');% Conditional mean AR coefficients
MA      = garchget(EstSpec,'MA'); % Conditional mean MA coefficients
regress = garchget(EstSpec,'Regress'); % Regression coefficients
InMean  = garchget(EstSpec,'InMean'); % Variance-in-mean coefficient

K        = garchget(EstSpec,'K'); % Conditional variance constant
GARCH    = garchget(EstSpec,'GARCH'); % Conditional variance coefficients for lagged variances
ARCH     = garchget(EstSpec,'ARCH'); % Conditional variance coefficients for lagged residuals
Leverage = garchget(EstSpec,'Leverage'); % Conditional variance Leverage coefficients

DoF = garchget(EstSpec,'DoF');

parameters = [repmat(C,isConstantInMean,1);AR(:);MA(:);regress(:);...
    repmat(InMean,isVarianceInMean,1);K;GARCH(:);ARCH(:);Leverage(:);...
    repmat(DoF,isDistributionT,1)];

% Extract equality constraint information for coefficients

FixC        = garchget(EstSpec,'FixC');
FixAR       = garchget(EstSpec,'FixAR');
FixMA       = garchget(EstSpec,'FixMA');
FixRegress  = garchget(EstSpec,'FixRegress');
FixInMean   = garchget(EstSpec,'FixInMean');
FixK        = garchget(EstSpec,'FixK');
FixGARCH    = garchget(EstSpec,'FixGARCH');
FixARCH     = garchget(EstSpec,'FixARCH');
FixLeverage = garchget(EstSpec,'FixLeverage');
FixDoF      = garchget(EstSpec,'FixDoF');

if isempty(FixC)       , FixC        = zeros(isConstantInMean,1); end
if isempty(FixAR)      , FixAR       = zeros(R,1);  end
if isempty(FixMA)      , FixMA       = zeros(M,1);  end
if isempty(FixRegress) , FixRegress  = zeros(nX,1); end
if isempty(FixInMean)  , FixInMean   = zeros(isVarianceInMean,1); end

if isempty(FixK)       , FixK        = 0;                         end
if isempty(FixGARCH)   , FixGARCH    = zeros(P,1);                end
if isempty(FixARCH)    , FixARCH     = zeros(Q,1);                end
if isempty(FixLeverage), FixLeverage = zeros(length(Leverage),1); end
if isempty(FixDoF)     , FixDoF      = zeros(isDistributionT,1);  end

Fix = logical([FixC;FixAR(:);FixMA(:);FixRegress(:);FixInMean;...
    FixK;FixGARCH(:);FixARCH(:);FixLeverage(:);FixDoF]);

% Extract standard errors of the parameter estimates:

eC       =  garchget(EstSE,'C');
eAR      =  garchget(EstSE,'AR');
eMA      =  garchget(EstSE,'MA');
eRegress =  garchget(EstSE,'Regress');
eInMean  =  garchget(EstSE,'InMean');

eK        =  garchget(EstSE,'K');
eGARCH    =  garchget(EstSE,'GARCH');
eARCH     =  garchget(EstSE,'ARCH');
eLeverage =  garchget(EstSE,'Leverage');
eDoF      =  garchget(EstSE,'DoF');

errors = [repmat(eC,isConstantInMean,1);eAR(:);eMA(:);eRegress(:);...
    repmat(eInMean,isVarianceInMean,1);eK;eGARCH(:);eARCH(:);eLeverage(:);...
    repmat(eDoF,isDistributionT,1)];

% The presence of NaNs in the EstSE structure will propagate into the
% errors vector above, indicating that an error occurred in the calculation
% of the parameter estimation covariance matrix. The following flag
% indicates the presence or absence of such an error condition:

errorFlag = any(isnan(errors)); % If true, an error occurred

% Compute the total number of parameters estimated in the composite
% conditional mean and variance models, excluding any held fixed as user-
% specified equality constraints:

nTotalParameters = isConstantInMean + R + M + nX + isVarianceInMean + 1 + P + Q + isDistributionT;

if any(strcmpi(varianceModel,{'EGARCH' 'GJR'}))
    nTotalParameters = nTotalParameters + Q; % Allow for leverage terms
end

nEstimatedParameters = nTotalParameters - sum(Fix); % Subtract equality constraints

% Display summary information:
headerStr = cell(3,1);
headerStr{1} = deblank(fliplr(deblank(fliplr(garchget(EstSpec,'Comment')))));
headerStr{2} = sprintf('Conditional Probability Distribution: %s',distribution);
headerStr{3} = sprintf('Number of Model Parameters Estimated: %d' ,nEstimatedParameters);
colHeaders = {'Parameter','Value','Standard Error','T Statistic'};

% Format annotation strings and display the information. Standard errors
% and t-statistics held fixed by equality constraints during estimation
% have zero estimation error, and are designated 'Fixed'.

n = [ones(isConstantInMean,1) 1:R 1:M 1:nX ones(isVarianceInMean,1) 1 1:P 1:Q]' ;

if any(strcmpi(varianceModel,{'EGARCH' 'GJR'}))
    n = [n;(1:Q)'];
end

n = [n;ones(isDistributionT,1)];

strings = [repmat({'C'}, isConstantInMean, 1);
    repmat({'AR(#)'}, R , 1);
    repmat({'MA(#)'}, M , 1);
    repmat({'Regress(#)'}, nX, 1);
    repmat({'InMean'}, isVarianceInMean, 1);
    'K';
    repmat({'GARCH(#)'}, P, 1);
    repmat({'ARCH(#)'}, Q, 1);
    repmat({'Leverage(#)'}, length(Leverage), 1);
    repmat({'DoF'}, isDistributionT, 1)];

colData = cell(nTotalParameters,4);
for row = 1:nTotalParameters
    %   If no error occurred in the parameter estimation covariance matrix,
    %   then indicate that a particular parameter was held fixed throughout the
    %   optimization by printing 'Fixed'. Otherwise, allow a NaN to appear in
    %   the printout to indicate the presence of an error condition.
    
    if Fix(row) && ~errorFlag
        colData(row,:) = {strrep(strings{row,:},'#',int2str(n(row))),...
            sprintf('%.4f',parameters(row)),'Fixed','Fixed'};
    else
        Tval = parameters(row) / errors(row); % Compute t-statistic for this parameter
        colData(row,:) = {strrep(strings{row,:},'#',int2str(n(row))),...
            sprintf('%.4f',parameters(row)),...
            sprintf('%.6f',errors(row)),...
            sprintf('%.4f',Tval)};
    end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to create a simple About Box
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localCreateAboutBox(src,evt) %#ok

mStr = sprintf('%s %s, %s\n\n%s\n\n%s\n%s',...
    mfilename,...
    'was written using MATLAB R2010a by Phil Goddard',...
    'the Principal of Goddard Consulting.',...
    'Please send comments to phil@goddardconsulting.ca.',...
    'Version 1.2');
msgbox(mStr,...
    sprintf('%s About Box',mfilename),...
    'help',...
    'modal');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Function to export data to the MATLAB Workspace
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function localExportToWorkspace(src,evt)%#ok

% get the app data
ad = guidata(src);

try 
    % Get the name of the variable to write to
    requery = true;
    varName = {sprintf('%sStruct',mfilename)};
    while requery
        % Keep prompting until the user enters a valid variable name or
        % presses the cancel button
        varName = inputdlg(...
            'Enter the name of the variable to create:',...
            'Get Export Variable Name',...
            1,varName);
        if isempty(varName)
            % Cancel was pressed so do nothing
            return
        elseif ~isvarname(varName{1})
            % This is not a valid variable name so tell the user and
            % then re-query them for a variable name
            eStr = sprintf('"%s"%s\n\n%s',...
                varName{1},...
                ' is not a valid variable name.',...
                'Please specify a different name.');
            uiwait(errordlg(eStr,...
                sprintf('%s Error',mfilename),...
                'modal'));
        else
            % Check if a variable with this name already exists
            allNames = evalin('base','whos');
            if ismember(varName,{allNames.name})
                % name already exists so prompt for overwriting
                qStr = sprintf('"%s"%s\n\n%s',...
                    varName{1},...
                    ' already exists.',...
                    'Do you wish to overwrite it?');
                overWrite = questdlg(...
                    qStr,...
                    sprintf('%s Question',mfilename),...
                    'Yes','No','No');
                if strcmp(overWrite,'Yes')
                    % name is OK so create/overwrite it
                    requery = false;
                % else
                  % They don't want to overwrite so prompt again  
                end
            else
                % name is OK so create it
                requery = false;
            end
        end
    end
    
    % Get all the info to be output
    % The data and dates info will always exist but check anyway
    requiredFields = {'DatenumVec','DataVec','preDates','preData'};
    outputName = {'dates','data','fittedDates','fittedData'};
    for idx = 1:length(requiredFields)
        if isfield(ad,requiredFields{idx})
            output.(outputName{idx}) = ad.(requiredFields{idx});
        else
            output.(outputName{idx}) = [];
        end
    end
    % modelinfo will only be available if a model has been fitted
    if isfield(ad,'modelinfo')
        if isfield(ad.modelinfo,'fittedmodel')
            requiredFields = {'coeff','errors','eFit','sFit'};
            outputName = {'coeff','errors','eFit','sFit'};
            for idx = 1:length(requiredFields)
                if isfield(ad,requiredFields{idx})
                    output.fittedmodel.(outputName{idx}) = ...
                        ad.modelinfo.fittedmodel.(requiredFields{idx});
                else
                    output.fittedmodel.(outputName{idx}) = [];
                end
            end
        else
            output.modelinfo.fittedmodel = [];
        end
    else
        output.modelinfo = [];
    end
        % forecast will only be available if a forecast was calculated
    if isfield(ad,'forecast')
        requiredFields = {'usingPredData','ySim',...
            'simulationDates','simulationData',...
            'forecastDates','forecastData'};
        outputName = {'modelPrediction','modelSimulation',...
            'simulationDates','simulationData',...
            'forecastDates','forecastData'};
        for idx = 1:length(requiredFields)
            if isfield(ad.forecast,requiredFields{idx})
                output.forecast.(outputName{idx}) = ...
                    ad.forecast.(requiredFields{idx});
            else
                output.forecast.(outputName{idx}) = [];
            end
        end
    else
        output.forecast = [];
    end
    

    
    % Write the variable
    assignin('base',varName{1},output);
    
catch ME
    eStr = sprintf('%s\n\n%s%s',...
        'There was a problem exporting the data and model information.',...
        'The specific error was: ',...
        ME.message);
    errordlg(eStr,sprintf('%s Error',mfilename),'modal');
end

Contact us