
function consumption_gui
% Help: This application estimates a stationary density of Surplus Consumption
% Ratio via simulation. It is a part of consumption-based asset pricing model 
% proposed by Campbell and Cochrane in 1999. Set the parameters and click
% the Start button to see the result. Questions & comments should be addressed 
% to Nik Tuzov: ntuzov@purdue.edu

                                           %## Constant parameters used in GUI ## 
                                           
    handles.scale_coef = 0.65;    % Size of GUI window will be handles.scale_coef * screen size
    handles.T_def = 1000;         % default path length
    handles.T_min = 10;           % minimal path length
    handles.T_max = 20000;        % maximal path length
    handles.N_def = 5000;         % default number of simulated paths
    handles.N_min = 10;           % minimal number of paths
    handles.N_max = 30000;        % maximal number of paths
    handles.BdWidth_def = 0.005;  % default bandwidth for kernel density estimator
    handles.BdWidth_min = 0.001;  % minmimal bandwidth
    handles.BdWidth_max = 0.01;   % maximal bandwidth
    handles.SlStep_min = 0.005;   % slider parameters
    handles.SlStep_max = 0.1;     % slider parameters

                                     %##  Create the main window and other components ##
                                           
    handles.fgMainWind = figure('Visible', 'off');
    set(handles.fgMainWind, 'NumberTitle', 'off');
    set(handles.fgMainWind, 'Name', 'Stationary Distribution of Surplus Consumption Ratio');
    ScreenSize = get(0,'ScreenSize');  % scaled to screen size
    set(handles.fgMainWind, 'Position', [1, 1, ScreenSize(3) * handles.scale_coef, ScreenSize(4) * handles.scale_coef]);
    % Single path plot handle:
    handles.axSinglePath = axes('Parent', handles.fgMainWind, 'Position', [.1 .57 .6 .35], 'Fontunits', 'normalized', ...
                                'Fontsize', 0.05);
    % Density plot handle:
    handles.axDensity = axes('Parent', handles.fgMainWind, 'Position', [.1 .1 .6 .35], 'Fontunits', 'normalized', ...
                             'Fontsize', 0.05);
    % Panel for the controls:
    handles.pnSimParam = uipanel('Parent', handles.fgMainWind,'Title',' Simulation Parameters',...
    'Position',[0.72 0.1 0.27 0.83], 'TitlePosition', 'CenterTop', 'FontUnits', 'normalized',...
    'fontsize', 0.04);
    % Push buttons' handles:
    handles.pbStart = uicontrol('Style','pushbutton', 'String','Start',...
                          'Units', 'Normalized', 'Position', [0.83, 0.41, 0.05, 0.04]);
    handles.pbReset = uicontrol('Style','pushbutton', 'String','Reset',...
                          'Units', 'Normalized', 'Position', [0.83, 0.34, 0.05, 0.04], ...
                          'TooltipString', 'Restore default parameters' );
    handles.pbHelp = uicontrol('Style','pushbutton', 'String','Help',...
                          'Units', 'Normalized', 'Position', [0.83, 0.27, 0.05, 0.04]);
    handles.pbQuit = uicontrol('Style','pushbutton', 'String','Quit',...
                          'Units', 'Normalized', 'Position', [0.83, 0.20, 0.05, 0.04]);                  
    % Static Text labels:
    handles.stNumPaths = uicontrol('Style','text','String','Number of paths',...
                        'Units', 'Normalized', 'Position', [0.75, 0.8, 0.18, 0.04]);
    handles.stPathLgth = uicontrol('Style','text','String','Path length',...
                        'Units', 'Normalized', 'Position', [0.80, 0.7, 0.12, 0.04]);
    handles.stBdWidth = uicontrol('Style','text','String','Bandwidth for density ',...
                        'Units', 'Normalized', 'Position', [0.74, 0.6, 0.18, 0.04]);
                
    % Editable texts and their sliders:
    handles.edNumPaths = uicontrol('Style','edit', 'String', '1000', 'Units', 'Normalized', 'Position', [0.91 0.8 0.07 0.04]);
    handles.slNumPaths = uicontrol('Style','slider', 'Max', handles.N_max, 'Min', handles.N_min,'Value', handles.N_def, ...
                                   'SliderStep', [handles.SlStep_min handles.SlStep_max],...
                                   'Units', 'Normalized', 'Position',[0.91 0.76 0.07 0.04]);
    handles.edPathLgth = uicontrol('Style','edit', 'String', '1000', 'Units', 'Normalized', 'Position', [0.91 0.7 0.07 0.04]);
    handles.slPathLgth = uicontrol('Style','slider', 'Max', handles.T_max, 'Min', handles.T_min,'Value', handles.T_def, ...
                                   'SliderStep', [handles.SlStep_min handles.SlStep_max],...
                                   'Units', 'Normalized', 'Position',[0.91 0.66 0.07 0.04]);
    handles.edBdWidth =  uicontrol('Style','edit', 'String', '0.003', 'Units', 'Normalized', 'Position', [0.91 0.6 0.07 0.04], ...
                                   'TooltipString', 'Bandwidth for kernel density estimator');
    handles.slBdWidth = uicontrol('Style','slider', 'Max', handles.BdWidth_max, 'Min', handles.BdWidth_min, ... 
                                  'Value', handles.BdWidth_def, 'SliderStep', [handles.SlStep_min handles.SlStep_max],...
                                  'Units', 'Normalized', 'Position',[0.91 0.56 0.07 0.04]);
    % set FontUnits:
    set([handles.stNumPaths, handles.stPathLgth, handles.stBdWidth, handles.pbStart, handles.pbReset, handles.pbHelp, handles.pbQuit, ...
        handles.edNumPaths, handles.edPathLgth, handles.edBdWidth], 'FontUnits','normalized', 'FontSize', 0.6);
    
    % Set all callbacks:
    set(handles.pbStart, 'Callback',{@pbStart_Callback, handles});
    set(handles.pbReset, 'Callback', {@pbReset_Callback, handles});
    set(handles.pbHelp, 'Callback', {@pbHelp_Callback, handles});
    set(handles.pbQuit, 'Callback', {@pbQuit_Callback, handles});                  
    set(handles.edNumPaths, 'Callback', {@edNumPaths_Callback, handles});                     
    set(handles.slNumPaths, 'Callback', {@slNumPaths_Callback, handles});                     
    set(handles.edPathLgth, 'Callback', {@edPathLgth_Callback, handles});
    set(handles.slPathLgth, 'Callback', {@slPathLgth_Callback, handles});
    set(handles.edBdWidth, 'Callback', {@edBdWidth_Callback, handles});
    set(handles.slBdWidth, 'Callback', {@slBdWidth_Callback, handles});                                
    set(handles.fgMainWind, 'CloseRequestFcn', {@fgMainWind_CloseRequestFcn, handles});            
    % 
    movegui(handles.fgMainWind,'center');
    set(handles.fgMainWind, 'Visible', 'on');

end   % consumption_gui

                              %###  Callbacks  ###  
                                
function pbStart_Callback(source, eventdata, handles)       

    % Clear the plots:
    plot(handles.axSinglePath, 0, 0);
    plot(handles.axDensity , 0, 0);
    % Change the Start button:
    set(source, 'String', 'Wait...', 'FontWeight', 'bold', 'Fontangle', 'italic');
    drawnow;
    
    N = get(handles.slNumPaths, 'Value');
    T = get(handles.slPathLgth, 'Value');
    BdWidth = get(handles.slBdWidth, 'Value');
    
    %## Monte-Carlo simulation of N paths of length T ##
    s = zeros(1, T);   % a single path of log-Surplus Consumption Ratio
    SCR = zeros(1, N); % stationary values of Surplus Consumption Ratio
    %    Parameters taken from the Table1 of the paper:         
    phi = 0.87 ^ (1/12);
    sigma = (1.5/100) / sqrt(12);       
    gamma = 2;    
    S_bar = sigma * sqrt(gamma /(1 - phi));         
    s_bar = log(S_bar);    
    S_max = 0.094;
    s_max = log(S_max);     

    for i = 1 : 1 : N     
      s(1) = s_bar;   % start the process at its mean value
      for time = 2 : 1 : T
         vt1 = sigma * randn; % i.i.d random shock      
         % calculating lambda - heteroskedastic multiple:
         if (s(time - 1) <= s_max)
             lambda = (1 / S_bar) * sqrt( max(0, 1 - 2*(s(time - 1) - s_bar)) ) - 1;
         else
             lambda = 0;
         end;           
         % calculate next value of s:
         s(time) = (1 - phi) * s_bar + phi * s(time - 1) + lambda * vt1;
      end;   
      SCR(i) = exp(s(T)); % stationary value of SCR
    end;  
                  % Plotting the result:       
    time = 1 : 1 : T;  
    plot(handles.axSinglePath, time, s),
    xlabel(handles.axSinglePath, ' Time '),
    ylabel(handles.axSinglePath, ' s = Ln(S) '),
    title(handles.axSinglePath, 'Log of Surplus Consumption Ratio, sample path'), 
    grid(handles.axSinglePath, 'on');

    [f, x] = ksdensity(SCR, 'width', BdWidth); 
    plot(handles.axDensity , x, f / max(f), 'LineWidth', 3),
    title(handles.axDensity, 'Stationary density of Surplus Consumption Ratio, S')      
    xlabel(handles.axDensity, ' Surplus Consumption Ratio, S')
    set(handles.axDensity, 'YLim', [0 1.1])
    grid(handles.axDensity, 'on'); 
    
    % Change the Start button:
    set(source, 'String', 'Start', 'Fontweight', 'normal', 'fontangle', 'normal');
    
end   %pbStart_Callback
    
function pbReset_Callback(source, eventdata, handles)    
    set(handles.edNumPaths, 'String', num2str(handles.N_def));
    set(handles.edPathLgth, 'String', num2str(handles.T_def));
    set(handles.edBdWidth, 'String', num2str(handles.BdWidth_def));
    set(handles.slNumPaths, 'Value', handles.N_def);
    set(handles.slPathLgth, 'Value', handles.T_def);
    set(handles.slBdWidth, 'Value', handles.BdWidth_def);
    plot(handles.axSinglePath, 0, 0);
    plot(handles.axDensity , 0, 0);   
end

function pbQuit_Callback(source, eventdata, handles)
    result = questdlg('Are you sure you want to quit?', 'Quit Application',...
                      'Yes', 'No', 'No');
    if strcmp(result, 'Yes')
        delete(handles.fgMainWind);
    end
end

function pbHelp_Callback(source, eventdata, handles)
    msg = 'This application estimates a stationary density of Surplus Consumption';
    msg = strcat(msg, ' Ratio via simulation. It is a part of consumption-based asset pricing model');
    msg = strcat(msg, ' proposed by Campbell and Cochrane in 1999. Set the three parameters and click');
    msg = strcat(msg, ' Start to see the result. Questions & comments should be addressed');
    msg = strcat(msg, ' to Nik Tuzov: ntuzov@purdue.edu');    
    helpdlg(msg, 'Help');  
end
    
% Callbacks for sliders and their textboxes.
% Note that all inputs are verified. If an inappropriate value
% (character, negative number, etc) is entered, the old value 
% is used instead.
function slNumPaths_Callback(source, eventdata, handles)        
    NewVal = round(get(source, 'Value'));  
    set(source, 'Value', NewVal);
    set(handles.edNumPaths, 'String', NewVal);        
end  

function edNumPaths_Callback(source, eventdata, handles)        
    NewVal = round(str2double(get(source, 'String')));                
    % Check the range
    if isempty(NewVal) || isnan(NewVal) || (NewVal < handles.N_min) || (NewVal > handles.N_max)
        OldVal = get(handles.slNumPaths,'Value'); % use old value from slider
        set(source, 'String', OldVal); 
    else % range is fine        
        set(handles.slNumPaths, 'Value', NewVal);
        set(source, 'String', NewVal); 
    end
end

function slPathLgth_Callback(source, eventdata, handles)        
    NewVal = round(get(source, 'Value'));  
    set(source, 'Value', NewVal);
    set(handles.edPathLgth, 'String', NewVal);        
end  
function edPathLgth_Callback(source, eventdata, handles)        
    NewVal = round(str2double(get(source, 'String')));                
    % Check the range
    if isempty(NewVal) || isnan(NewVal) || (NewVal < handles.T_min) || (NewVal > handles.T_max)
        OldVal = get(handles.slPathLgth,'Value'); % use old value from slider
        set(source, 'String', OldVal); 
    else % range is fine        
        set(handles.slPathLgth, 'Value', NewVal);
        set(source, 'String', NewVal); 
    end
end

function slBdWidth_Callback(source, eventdata, handles)
    NewVal = get(source, 'Value');         
    set(handles.edBdWidth, 'String', NewVal);        
end  
function edBdWidth_Callback(source, eventdata, handles)        
    NewVal = str2double(get(source, 'String'));                
    % Check the range
    if isempty(NewVal) || isnan(NewVal) || (NewVal < handles.BdWidth_min) || (NewVal > handles.BdWidth_max)
        OldVal = get(handles.slBdWidth,'Value'); % use old value from slider
        set(source, 'String', OldVal); 
    else % range is fine        
        set(handles.slBdWidth, 'Value', NewVal);            
    end
end

% Close function for the main window:
function fgMainWind_CloseRequestFcn(source, eventdata, handles)        
    result = questdlg('Are you sure you want to quit?', 'Quit Application',...
                      'Yes', 'No', 'No');
    if strcmp(result, 'Yes')
        delete(source);
    end
end   
    
    %### End of callbacks ###
    