Code covered by the BSD License  

Highlights from
FinMetrics

  • fm(varargin) This source file is subject to version 3 of the GPL license,
  • AssetThis source file is subject to version 3 of the GPL license,
  • AssetUniverseThis source file is subject to version 3 of the GPL license,
  • AxlThis source file is subject to version 3 of the GPL license,
  • CashPositionThis source file is subject to version 3 of the GPL license,
  • CashTransactionThis source file is subject to version 3 of the GPL license,
  • Config This source file is subject to version 3 of the GPL license,
  • ConsoleMenuThis source file is subject to version 3 of the GPL license,
  • ConsoleMenuItemThis source file is subject to version 3 of the GPL license,
  • CurrencyAssetThis source file is subject to version 3 of the GPL license,
  • ExchangeTradedAssetThis source file is subject to version 3 of the GPL license,
  • FinMetricsThis source file is subject to version 3 of the GPL license,
  • PortfolioThis source file is subject to version 3 of the GPL license,
  • PositionThis source file is subject to version 3 of the GPL license,
  • StatisticsThis source file is subject to version 3 of the GPL license,
  • StockPositionThis source file is subject to version 3 of the GPL license,
  • StockTransactionThis source file is subject to version 3 of the GPL license,
  • TextUIThis source file is subject to version 3 of the GPL license,
  • TextUILocaleThis source file is subject to version 3 of the GPL license,
  • TextUIQuestionThis source file is subject to version 3 of the GPL license,
  • TextUIQuestionnaireThis source file is subject to version 3 of the GPL license,
  • TransactionThis source file is subject to version 3 of the GPL license,
  • View all files

FinMetrics

by

 

Open source/open architecture quantitative portfolio management environment.

FinMetrics
classdef FinMetrics < handle    
% This source file is subject to version 3 of the GPL license, 
% that is bundled with this package in the file LICENSE, and is 
% available online at http://www.gnu.org/licenses/gpl.txt
%
% This source file can be linked to GPL-incompatible facilities, 
% produced or made available by MathWorks, Inc.

    properties (SetAccess = private, GetAccess = public, Transient = true)
        Project = 'Unnamed';
        Directory = '';
        AssetUniverse;
        Portfolios = {};
        ActivePortfolio;
        ProjectSaved;
        AssetUniverseSaved;
        PortfoliosSaved;        
    end        
    
    properties (SetAccess = private, GetAccess = private)
        assetUniverseFile;
        portfolioFiles = {};
    end
    
    events (ListenAccess = 'public', NotifyAccess = 'private')
        FinMetrics_Project_modified
        FinMetrics_Project_saved
        FinMetrics_AssetUniverse_loaded    
        FinMetrics_AssetUniverse_asset_added
        FinMetrics_AssetUniverse_saved
        FinMetrics_AssetUniverse_modified
        FinMetrics_AssetUniverse_prices_updated
        FinMetrics_Portfolios_saved
        FinMetrics_Portfolios_modified        
    end
           
    methods 
        function Init(FMObj)
            FMObj.addlistener('FinMetrics_Project_modified', @FinMetrics_Project_modified_callback);
            FMObj.addlistener('FinMetrics_Project_saved', @FinMetrics_Project_saved_callback); 
            FMObj.addlistener('FinMetrics_AssetUniverse_modified', @FinMetrics_AssetUniverse_modified_callback);
            FMObj.addlistener('FinMetrics_AssetUniverse_saved', @FinMetrics_AssetUniverse_saved_callback);
            FMObj.addlistener('FinMetrics_Portfolios_modified', @FinMetrics_Portfolios_modified_callback);
            FMObj.addlistener('FinMetrics_Portfolios_saved', @FinMetrics_Portfolios_saved_callback);
            
            function FinMetrics_Project_modified_callback(eventSrc, eventData)
                FMObj.ProjectSaved = false;
            end
            
            function FinMetrics_Project_saved_callback(eventSrc, eventData)
                FMObj.ProjectSaved = true;
            end
            
            function FinMetrics_AssetUniverse_modified_callback(eventSrc, eventData)
                FMObj.AssetUniverseSaved = false;
            end
            
            function FinMetrics_AssetUniverse_saved_callback(eventSrc, eventData)
                FMObj.AssetUniverseSaved = true;
            end
            
            function FinMetrics_Portfolios_modified_callback(eventSrc, eventData)
                FMObj.PortfoliosSaved = false;
                
                if (isempty(FMObj.ActivePortfolio))
                    FMObj.SetActivePortfolio(size(FMObj.Portfolios,1));
                end            
            end
            
            function FinMetrics_Portfolios_saved_callback(eventSrc, eventData)
                FMObj.PortfoliosSaved = true;                
            end
        end
        
        function saveObj = saveobj(FMObj)
            if isa(FMObj.AssetUniverse, 'FM.AssetUniverse')
                % Save Asset Universe
                FMObj.AssetUniverse_Save();
            end
            
            FMObj.SavePortfolios();
            
            saveObj = struct('Project', FMObj.Project, 'assetUniverseFile', FMObj.assetUniverseFile, 'portfolioFiles', { FMObj.portfolioFiles}, 'ActivePortfolio', FMObj.ActivePortfolio);
            return;
        end
        
        function ret = SaveProject(FMObj, projectName, projectFile)
            ret = 0;
                        
            FMObj.Project = projectName;
            
            eval(sprintf('FinMetrics = FMObj;'));
            save(projectFile, 'FinMetrics');
            notify(FMObj, 'FinMetrics_Project_saved');
        end
        
        function CreateAssetUniverse(FMObj, auName, auFile)           
            [auLocation, auFileName, auFileExt, versn] = fileparts(auFile); 
            FMObj.AssetUniverse = FM.AssetUniverse(auName, auLocation);
            FMObj.assetUniverseFile = auFile;            
            
            FMObj.AssetUniverse_Save(auFile);
            notify(FMObj, 'FinMetrics_AssetUniverse_loaded'); 
            notify(FMObj, 'FinMetrics_Project_modified');
        end
        
        function OpenAssetUniverse(FMObj, auFile)
            au=load(auFile);
            FMObj.AssetUniverse = au.(char(fieldnames(au)));
            FMObj.assetUniverseFile = auFile;
                        
            notify(FMObj, 'FinMetrics_AssetUniverse_loaded');
            notify(FMObj, 'FinMetrics_Project_modified');
        end
        
        function CreateNewPortfolio(FMObj, portfolioName, portfolioFile)
            Portfolio = FM.Portfolio(portfolioName);            
            save(portfolioFile, 'Portfolio');  
            notify(FMObj, 'FinMetrics_Portfolios_modified');
        end
        
        function ret = LoadPortfolios(FMObj)
            ret = 0;
            if ~isempty(FMObj.portfolioFiles)
                for i=1:size(FMObj.portfolioFiles,1)
                    if (exist(FMObj.portfolioFiles{i}, 'file'))
                        portfolio = load(FMObj.portfolioFiles{i});
                        portObj = portfolio.(char(fieldnames(portfolio)));

                        if (~strcmp(class(portObj), 'FM.Portfolio'))
                            continue;
                        end
                    else
                        continue;
                    end
                    
                   portObj.SyncAssetsWithUniverse(FMObj.AssetUniverse);
                    
                    if isempty(FMObj.Portfolios)
                        FMObj.Portfolios = { portObj };
                    else
                        FMObj.Portfolios = [ FMObj.Portfolios; { portObj } ];
                    end
                    ret = ret + 1;
                end
            end
            
            if (isempty(FMObj.ActivePortfolio))
                FMObj.SetActivePortfolio(size(FMObj.Portfolios,1));
            end
        end
        
        function ret = ImportPortfolio(FMObj, portfolioFile)
            ret = 0;
            
            portfolio = load(portfolioFile);
            portObj = portfolio.(char(fieldnames(portfolio)));

            if (~strcmp(class(portObj), 'FM.Portfolio'))
                return;
            end
            
            portObj.SyncAssetsWithUniverse(FMObj.AssetUniverse);
            
            if (isempty(FMObj.portfolioFiles) && isempty(FMObj.Portfolios))
                FMObj.portfolioFiles = { portfolioFile };            
                FMObj.Portfolios = { portObj };            
                FMObj.SetActivePortfolio(size(FMObj.Portfolios,1));
            else               
                for i=1:size(FMObj.portfolioFiles,1)
                    if (strcmp(FMObj.portfolioFiles{i}, portfolioFile))
                        ret = 2;
                        return;
                    end
                end
                
                FMObj.portfolioFiles = [ FMObj.portfolioFiles; { portfolioFile } ]; 
                FMObj.Portfolios = [ FMObj.Portfolios; { portObj } ];
            end
            
            notify(FMObj, 'FinMetrics_Project_modified');
            ret = 1;

        end
        
        function SavePortfolios(FMObj)
            if ~isempty(FMObj.portfolioFiles)
                for s=1:size(FMObj.portfolioFiles,1)
                    eval(sprintf('Portfolio = FMObj.Portfolios{%d};', s));
                    save(FMObj.portfolioFiles{s}, 'Portfolio');
                end
                
                notify(FMObj, 'FinMetrics_Portfolios_saved');
            end
        end
        
        function AssetUniverse_AddETAsset(FMObj, symbol, exchange, class, category)
            nETA = FM.ExchangeTradedAsset(symbol, exchange, class, category);
            FMObj.AssetUniverse.AddAsset(nETA); 
            notify(FMObj, 'FinMetrics_AssetUniverse_asset_added');
            notify(FMObj, 'FinMetrics_AssetUniverse_modified');
        end
        
        function AssetUniverse_AddCurrencyAsset(FMObj, currency, class, category)
            nCA = FM.CurrencyAsset(currency, class, category);
            FMObj.AssetUniverse.AddAsset(nCA);
            notify(FMObj, 'FinMetrics_AssetUniverse_asset_added');
            notify(FMObj, 'FinMetrics_AssetUniverse_modified');
        end
        
        function ret = NewScriptTemplate(FMObj, file)
                ret = false;
                
                tmpl = fopen(file, 'w');
                if (tmpl == -1)
                    return;
                end
                
                [pathstr, name, ext, versn] = fileparts(file);
                fprintf(tmpl, '%% Script template file name: %s%s\n', name, ext);
                fprintf(tmpl, '%% Project name: %s\n', FMObj.Project);
                fprintf(tmpl, '%% Script template generated on: %s\n\n', datestr(now));
                
                % List all Assets in AU
                if ~isempty(FMObj.AssetUniverse.Assets)
                    fprintf(tmpl, '%% %d assets are available in Asset Universe\n%%\n', size(FMObj.AssetUniverse.Assets,1));
                    for t=1:size(FMObj.AssetUniverse.Assets,1),                
                        line = sprintf('%s = FinMetrics.AssetUniverse.Assets{%d};', regexprep(FMObj.AssetUniverse.Assets{t}.Ident, '(\W)', ''), t);
                        fprintf(tmpl, '%s\n', line);
                    end
                end

                % List all portfolios associated with project
                if ~isempty(FMObj.Portfolios)
                    fprintf(tmpl, '\n%% %d portfolio[s] associated with the project\n%%\n', size(FMObj.Portfolios,1));
                    for t=1:size(FMObj.Portfolios,1),                
                        fprintf(tmpl, '%% Portfolio name: %s\n', FMObj.Portfolios{t}.Name);                                                
                        fprintf(tmpl, '%% Portfolio file: %s\n', FMObj.portfolioFiles{t});
                        line = sprintf('Portfolio%d = FinMetrics.Portfolios{%d};', t, t);
                        fprintf(tmpl, '%s\n\n', line);
                    end
                end
                
                fprintf(tmpl, '%% Your code should go below this line.');
                fclose(tmpl);
                
        end

        function ret = AssetUniverse_ImportAssetList(FMObj, filename)
            ret = 0;
            assetData = FM.FinMetrics.csvimport(filename);
            
            for i=1:size(assetData,1)
                    expr = ['testAssetSupport = FM.', assetData{i,1}, 'Asset('] ;
                    
                    for s=1:size(assetData(i,4:end),2)                        
                        if (~isempty(assetData{i,s+3}))
                            expr = [ expr, '''', assetData{i,s+3}, ''',' ];                            
                        end
                    end
                    
                    expr = [ expr, '''', assetData{i,2}, ''',','''',assetData{i,3}, ''');' ];
                                        
                    try
                        eval(expr);
                    catch ME1
                        continue;
                    end
                    
                    if (FMObj.AssetUniverse.AddAsset(testAssetSupport) ~= false)
                        notify(FMObj, 'FinMetrics_AssetUniverse_asset_added');
                        notify(FMObj, 'FinMetrics_AssetUniverse_modified');

                        ret = ret + 1;
                    end
                                        
            end
        end
        
        function AssetUniverse_Save(FMObj, auFile)            
            if (nargin == 1)
                auFile = FMObj.assetUniverseFile;
            end
            
            eval(sprintf('AssetUniverse = FMObj.AssetUniverse;'));
            save(auFile, 'AssetUniverse')
            notify(FMObj, 'FinMetrics_AssetUniverse_saved');                        
        end
        
        function AssetUniverse_UpdateAssetPrices(FMObj)
            notify(FMObj.AssetUniverse, 'UpdateAssetPrices');
            notify(FMObj, 'FinMetrics_AssetUniverse_prices_updated');
            notify(FMObj, 'FinMetrics_AssetUniverse_modified');
        end
        
        function ret = Portfolio_CashTransaction(FMObj, portfolio, params)            
            ret = false;
            if (~strcmp(class(portfolio), 'FM.Portfolio'))
               return;
            elseif isfield(params, 'TrID') && isfield(params, 'Asset')
                return;
            elseif isfield(params, 'TrID')
                % Existing transaction
                if ~isfield(params, 'Action') || ( (params.TrID < 1) || (params.TrID > size(portfolio.Transactions, 1)))
                    return;
                else                                        
                    if ~isfield(params, 'Time')
                        params.Time = now();
                    end                        
                    
                    switch upper(params.Action)
                        case 'COMMIT'                            
                            rCode = portfolio.CommitTransaction(params.TrID, params.Time);
                            
                            if (rCode == true)                                
                                notify(FMObj, 'FinMetrics_Portfolios_modified');                                
                                ret = true;
                                return;
                            else
                                ret = false;
                                return;
                            end
                        case 'UNCOMMIT'
                            rCode = portfolio.UnCommitTransaction(params.TrID);
                            
                            if (rCode == true)
                                notify(FMObj, 'FinMetrics_Portfolios_modified');                                
                                ret = true;
                                return;
                            else
                                ret = false;
                                return;
                            end
                        case 'RECOMMIT'
                            if isempty(portfolio.Transactions{params.TrID}.PreviouslyCommittedOn) 
                                ret = false;
                                return;
                            end
                            
                            rCode = portfolio.CommitTransaction(params.TrID, portfolio.Transactions{params.TrID}.PreviouslyCommittedOn);
                            
                            if (rCode == true)                                                                
                                notify(FMObj, 'FinMetrics_Portfolios_modified');
                                ret = true;
                                return;
                            else
                                ret = false;
                                return;
                            end                            
                        otherwise 
                            ret = false;
                            return;
                    end
                end
            else
                % New transaction
                if (~strcmp(class(params.Asset), 'FM.CurrencyAsset'))
                    return;
                elseif ~isfield(params, 'Action') || ~isfield(params, 'Quantity') || ~isfield(params, 'Asset') || (FMObj.AssetUniverse.isAssetIn(params.Asset) == false)
                    return;
                end
                
                if ~isfield(params, 'Commit')
                    params.Commit = false;
                end

                if ~isfield(params, 'Time')
                    params.Time = now();
                end                        

                trans = FM.CashTransaction(struct('Asset', params.Asset, 'Action', params.Action, 'Quantity', params.Quantity));
                trID = portfolio.AddTransaction(trans);
                notify(FMObj, 'FinMetrics_Portfolios_modified');
                
                ret = trID;
                if (params.Commit == true)
                    rCode = portfolio.CommitTransaction(trID, params.Time);
                    if (rCode == true)                        
                        return;
                    else
                        portfolio.DeleteTransaction(trID);
                        ret = false;
                        return;
                    end
                end

            end    
        end
        
        function ret = Portfolio_StockTransaction(FMObj, portfolio, params)            
            % The following fields must be present in the structure if TrID
            % field is not defined:
            % Asset - underlying asset
            % Action - Buy/Sell/Buy to Cover/Sell Short
            % Quantity - number of units of the underlying asset
            % Time - timestamp of the transaction (default now)
            %
            % params structure may also include the following optional
            % parameters when TrID is not set:
            % 
            % Price - purchase or sale price (default - Close price,
            % supported: Open, High, Low, Close or a custom number)
            % Commit - should the transaction be committed (default false,
            % supported true or false)            
            %
            % If TrID is set, these fields should be defined:
            % Action - commit,uncommit or recommit
            ret = false;

            % Check if 'portfolio' variable is of a correct type
            if (~strcmp(class(portfolio), 'FM.Portfolio'))
               return;
            end    
            
            if isfield(params, 'TrID') && isfield(params, 'Asset')
                return;
            elseif isfield(params, 'TrID')
                if ~isfield(params, 'Action') || ( (params.TrID < 1) || (params.TrID > size(portfolio.Transactions, 1)))
                    return;
                else                                        
                    if ~isfield(params, 'Time')
                        params.Time = now();
                    end                        
                    
                    switch upper(params.Action)
                        case 'COMMIT'
                            if ~isfield(params, 'Price')
                                params.Price = 'Close';
                            end
                            
                            if ~isnumeric(params.Price)
                                price = portfolio.Transactions{params.TrID}.Asset.GetPrice(params.Time, params.Price);

                                if (price == -1)
                                    return;
                                end
                            else
                                price = params.Price;
                            end

                            rCode = portfolio.CommitTransaction(params.TrID, params.Time);
                            
                            if (rCode == true)                                
                                portfolio.Transactions{params.TrID}.SetPrice(price);
                                notify(FMObj, 'FinMetrics_Portfolios_modified');
                                
                                ret = true;
                                return;
                            else
                                return;
                            end
                        case 'UNCOMMIT'
                            rCode = portfolio.UnCommitTransaction(params.TrID);
                            
                            if (rCode == true)
                                portfolio.Transactions{params.TrID}.SetPrice('');
                                notify(FMObj, 'FinMetrics_Portfolios_modified');
                                
                                ret = true;
                                return;
                            else
                                return;
                            end
                        case 'RECOMMIT'
                            if isempty(portfolio.Transactions{params.TrID}.PreviouslyCommittedOn) 
                                return;
                            end
                            
                            rCode = portfolio.CommitTransaction(params.TrID, portfolio.Transactions{params.TrID}.PreviouslyCommittedOn);
                            
                            if (rCode == true)                                
                                portfolio.Transactions{params.TrID}.SetPrice(portfolio.Transactions{params.TrID}.PreviousPrice);
                                notify(FMObj, 'FinMetrics_Portfolios_modified');

                                ret = true;
                                return;
                            else
                                return;
                            end                            

                    end
                end
            else                
                if ~isfield(params, 'Action') || ~isfield(params, 'Quantity') || ~isfield(params, 'Asset') || (FMObj.AssetUniverse.isAssetIn(params.Asset) == false)
                    return;
                end

                if (~strcmp(class(params.Asset), 'FM.ExchangeTradedAsset'))
                    return;
                end                                   

                if ~isfield(params, 'Price')
                    params.Price = 'Close';
                end

                if ~isfield(params, 'Commit')
                    params.Commit = false;
                end
                
                trans = FM.StockTransaction(struct('Asset', params.Asset, 'Action', params.Action, 'Quantity', params.Quantity));
                trID = portfolio.AddTransaction(trans);
                notify(FMObj, 'FinMetrics_Portfolios_modified');
                
                ret = trID;

                if (params.Commit == true)
                    if ~isfield(params, 'Time')
                        params.Time = now();
                    end                        

                    if ~isnumeric(params.Price)
                        price = params.Asset.GetPrice(params.Time, params.Price);

                        if (price == -1)
                            return;
                        end
                    else
                        price = params.Price;
                    end

                    rCode = portfolio.CommitTransaction(trID, params.Time);
                    if (rCode == true)
                        portfolio.Transactions{trID}.SetPrice(price);                                                
                        return;
                    else
                        portfolio.DeleteTransaction(trID);
                        ret = false;
                        return;
                    end
                end
            end                                                                                                       
        end
                               
        function ret = SetActivePortfolio(FinMetrics, ap)
            ret=false;
            
            if ((ap < 1) || ap > size(FinMetrics.Portfolios,1))
                return;
            end
            FinMetrics.ActivePortfolio=ap;
            
            ret = true;
        end
        
        function KeyboardMode(FinMetrics)
            keyboard;
        end
    end
    
    methods (Access = private, Static)
        function varargout = csvimport( fileName, varargin )
            % cvsimport function is written by Ashish Sadanandan and covered by BSD License
            % source: http://www.mathworks.com/matlabcentral/fileexchange/23573
            % CSVIMPORT reads the specified CSV file and stores the contents in a cell array or matrix
            %
            % The file can contain any combination of text & numeric values. Output data format will vary
            % depending on the exact composition of the file data.
            %
            % CSVIMPORT( fileName ):         fileName     -  String specifying the CSV file to be read. Set to
            %                                                [] to interactively select the file.
            %
            % CSVIMPORT( fileName, ... ) : Specify a list of options to be applied when importing the CSV file.
            %                              The possible options are:
            %                                delimiter     - String to be used as column delimiter. Default
            %                                                value is , (comma)
            %                                columns       - String or cell array of string listing the columns
            %                                                from which data is to be extracted. If omitted data
            %                                                from all columns in the file is imported.
            %                                outputAsChar  - true / false value indicating whether the data
            %                                                should be output as characters. If set to false the
            %                                                function attempts to convert each column into a
            %                                                numeric array, it outputs the column as characters
            %                                                if conversion of any data element in the column
            %                                                fails. Default value is false.
            %                                uniformOutput - true / false value indicating whether output can be
            %                                                returned without encapsulation in a cell array.
            %                                                This parameter is ignored if the columns / table
            %                                                cannot be converted into a matrix.
            %                                noHeader      - true / false value indicating whether the CSV
            %                                                file's first line contains column headings. Default
            %                                                value is false.
            %                                ignoreWSpace  - true / false value indicating whether to ignore
            %                                                leading and trailing whitespace in the column
            %                                                headers; ignored if noHeader is set to true.
            %                                                Default value is false.
            %
            % The parameters must be specified in the form of param-value pairs, parameter names are not
            % case-sensitive and partial matching is supported.
            %
            % [C1 C2 C3] = CSVIMPORT( fileName, 'columns', {'C1', 'C2', C3'}, ... )
            %   This form returns the data from columns in output variables C1, C2 and C3 respectively, the
            %   column names are case-sensitive and must match a column name in the file exactly. When fetching
            %   data in column mode the number of output columns must match the number of columns to read or it
            %   must be one. In the latter case the data from the columns is returned as a single cell matrix.
            %
            % [C1 C2 C3] = CSVIMPORT( fileName, 'columns', [2, 3, 4], ,'noHeader', true, ... )
            %   This form returns the data from columns in output variables C1, C2 and C3 respectively, the
            %   columns parameter must contain the column indices when the 'noHeader' option is set to true.

            %
            % Notes:  1. Function has not been tested on badly formatted CSV files.
            %         2. Created using R2007b but has been tested on R2006b.
            %
            % Revisions:
            %   04/28/2009: Corrected typo in an error message
            %               Added igonoreWSpace option
            %

            if ( nargin == 0 ) || isempty( fileName )
              [fileName filePath] = uigetfile( '*.csv', 'Select CSV file' );
              if isequal( fileName, 0 )
                return;
              end
              fileName = fullfile( filePath, fileName );
            else
              if ~ischar( fileName )
                error( 'csvimport:FileNameError', 'The first argument to %s must be a valid .csv file', ...
                  mfilename );
              end
            end

            %Setup default values
            p.delimiter       = ',';
            p.columns         = [];
            p.outputAsChar    = false;
            p.uniformOutput   = true;
            p.noHeader        = false;
            p.ignoreWSpace    = false;

            validParams     = {     ...
              'delimiter',          ...
              'columns',            ...
              'outputAsChar',       ...
              'uniformOutput',      ...
              'noHeader',           ...
              'ignoreWSpace'        ...
              };

            %Parse input arguments
            if nargin > 1
              if mod( numel( varargin ), 2 ) ~= 0
                error( 'csvimport:InvalidInput', ['All input parameters after the fileName must be in the ' ...
                  'form of param-value pairs'] );
              end
              params  = lower( varargin(1:2:end) );
              values  = varargin(2:2:end);

              if ~all( cellfun( @ischar, params ) )
                error( 'csvimport:InvalidInput', ['All input parameters after the fileName must be in the ' ...
                  'form of param-value pairs'] );
              end

              lcValidParams   = lower( validParams );
              for ii =  1 : numel( params )
                result        = strmatch( params{ii}, lcValidParams );
                %If unknown param is entered ignore it
                if isempty( result )
                  continue
                end
                %If we have multiple matches make sure we don't have a single unambiguous match before throwing
                %an error
                if numel( result ) > 1
                  exresult    = strmatch( params{ii}, validParams, 'exact' );
                  if ~isempty( exresult )
                    result    = exresult;
                  else
                    %We have multiple possible matches, prompt user to provide an unambiguous match
                    error( 'csvimport:InvalidInput', 'Cannot find unambiguous match for parameter ''%s''', ...
                      varargin{ii*2-1} );
                  end
                end
                result      = validParams{result};
                p.(result)  = values{ii};
              end
            end

            %Check value attributes
            if isempty( p.delimiter ) || ~ischar( p.delimiter )
              error( 'csvimport:InvalidParamType', ['The ''delimiter'' parameter must be a non-empty ' ...
                'character array'] );
            end
            if isempty( p.noHeader ) || ~islogical( p.noHeader ) || ~isscalar( p.noHeader )
              error( 'csvimport:InvalidParamType', ['The ''noHeader'' parameter must be a non-empty ' ...
                'logical scalar'] );
            end
            if ~p.noHeader
              if ~isempty( p.columns )
                if ~ischar( p.columns ) && ~iscellstr( p.columns )
                  error( 'csvimport:InvalidParamType', ['The ''columns'' parameter must be a character array ' ...
                    'or a cell array of strings for CSV files containing column headers on the first line'] );
                end
                if p.ignoreWSpace
                  p.columns = strtrim( p.columns );
                end
              end
            else
              if ~isempty( p.columns ) && ~isnumeric( p.columns )
                error( 'csvimport:InvalidParamType', ['The ''columns'' parameter must be a numeric array ' ...
                  'for CSV files containing column headers on the first line'] );
              end
            end
            if isempty( p.outputAsChar ) || ~islogical( p.outputAsChar ) || ~isscalar( p.outputAsChar )
              error( 'csvimport:InvalidParamType', ['The ''outputAsChar'' parameter must be a non-empty ' ...
                'logical scalar'] );
            end
            if isempty( p.uniformOutput ) || ~islogical( p.uniformOutput ) || ~isscalar( p.uniformOutput )
              error( 'csvimport:InvalidParamType', ['The ''uniformOutput'' parameter must be a non-empty ' ...
                'logical scalar'] );
            end

            %Open file
            [fid msg] = fopen( fileName, 'rt' );
            if fid == -1
              error( 'csvimport:FileReadError', 'Failed to open ''%s'' for reading.\nError Message: %s', ...
                fileName, msg );
            end

            colMode         = ~isempty( p.columns );
            if ischar( p.columns )
              p.columns     = cellstr( p.columns );
            end
            nHeaders        = numel( p.columns );

            if colMode
              if ( nargout > 1 ) && ( nargout ~= nHeaders )
                error( 'csvimport:NumOutputs', ['The number of output arguments must be 1 or equal to the ' ...
                  'number of column names when fetching data for specific columns'] );
              end
            end

            %Read first line and determine number of columns in data
            rowData         = fgetl( fid );
            rowData         = regexp( rowData, p.delimiter, 'split' );
            nCols           = numel( rowData );

            %Check whether all specified columns are present if used in column mode and store their indices
            if colMode
              if ~p.noHeader
                if p.ignoreWSpace
                  rowData     = strtrim( rowData );
                end
                colIdx        = zeros( 1, nHeaders );
                for ii = 1 : nHeaders
                  result      = strmatch( p.columns{ii}, rowData );
                  if isempty( result )
                    fclose( fid );
                    error( 'csvimport:UnknownHeader', ['Cannot locate column header ''%s'' in the file ' ...
                      '''%s''. Column header names are case sensitive.'], p.columns{ii}, fileName );
                  elseif numel( result ) > 1
                    exresult  = strmatch( p.columns{ii}, rowData, 'exact' );
                    if numel( exresult ) == 1
                      result  = exresult;
                    else
                      warning( 'csvimport:MultipleHeaderMatches', ['Column header name ''%s'' matched ' ...
                        'multiple p.columns in the file, only the first match (%d) will be used.'], ...
                        p.columns{ii}, result(1) );
                    end
                  end
                  colIdx(ii)  = result(1);
                end
              else
                colIdx        = p.columns(:);
                if max( colIdx ) > nCols
                  fclose( fid );
                  error( 'csvimport:BadIndex', ['The specified column index ''%d'' exceeds the number of ' ...
                    'columns (%d) in the file'], max( colIdx ), nCols );
                end
              end
            end

            %Calculate number of lines
            pos             = ftell( fid );
            if pos == -1
              msg = ferror( fid );
              fclose( fid );
              error( 'csvimport:FileQueryError', 'FTELL on file ''%s'' failed.\nError Message: %s', ...
                fileName, msg );
            end
            data            = fread( fid );
            nLines          = numel( find( data == sprintf( '\n' ) ) ) + 1;
            %Reposition file position indicator to beginning of second line
            if fseek( fid, pos, 'bof' ) ~= 0
              msg = ferror( fid );
              fclose( fid );
              error( 'csvimport:FileSeekError', 'FSEEK on file ''%s'' failed.\nError Message: %s', ...
                fileName, msg );
            end

            data            = cell( nLines, nCols );
            data(1,:)       = rowData;
            emptyRowsIdx    = [];
            %Get data for remaining rows
            for ii = 2 : nLines
              rowData       = fgetl( fid );
              if isempty( rowData )
                emptyRowsIdx = [emptyRowsIdx(:); ii];
                continue
              end
              rowData       = regexp( rowData, p.delimiter, 'split' );
              nDataElems    = numel( rowData );
              if nDataElems < nCols
                warning( 'csvimport:UnevenColumns', ['Number of data elements on line %d (%d) differs from ' ...
                  'that on the first line (%d). Data in this line will be padded.'], ii, nDataElems, nCols );
                rowData(nDataElems+1:nCols) = {''};
              elseif nDataElems > nCols
                warning( 'csvimport:UnevenColumns', ['Number of data elements on line %d (%d) differs from ' ...
                  'that one the first line (%d). Data in this line will be truncated.'], ii, nDataElems, nCols );
                rowData     = rowData(1:nCols);
              end
              data(ii,:)    = rowData;
            end
            %Close file handle
            fclose( fid );
            data(emptyRowsIdx,:)   = [];

            %Process data for final output
            uniformOutputPossible  = ~p.outputAsChar;
            if p.noHeader
              startRowIdx          = 1;
            else
              startRowIdx          = 2;
            end
            if ~colMode
              if ~p.outputAsChar
                %If we're not outputting the data as characters then try to convert each column to a number
                for ii = 1 : nCols
                  colData     = cellfun( @str2num, data(startRowIdx:end,ii), 'UniformOutput', false );
                  %If any row contains an entry that cannot be converted to a number then return the whole
                  %column as a char array
                  if ~any( cellfun( @isempty, colData ) )
                    if ~p.noHeader
                      data(:,ii)= cat( 1, data(1,ii), colData{:} );
                    else
                      data(:,ii)= colData;
                    end
                  end
                end
              end
              varargout{1}    = data;
            else
              %In column mode get rid of the headers (if present)
              data            = data(startRowIdx:end,colIdx);
              if ~p.outputAsChar
                %If we're not outputting the data as characters then try to convert each column to a number
                for ii = 1 : nHeaders
                  colData     = cellfun( @str2num, data(:,ii), 'UniformOutput', false );
                  %If any row contains an entry that cannot be converted to a number then return the whole
                  %column as a char array
                  if ~any( cellfun( @isempty, colData ) )
                    data(:,ii)= colData;
                  else
                    %If any column cannot be converted to a number then we cannot convert the output to an array
                    %or matrix i.e. uniform output is not possible
                    uniformOutputPossible = false;
                  end
                end
              end
              if nargout == nHeaders
                %Loop through each column and convert to matrix if possible
                for ii = 1 : nHeaders
                  if p.uniformOutput && ~any( cellfun( @ischar, data(:,ii) ) )
                    varargout{ii} = cell2mat( data(:,ii) );
                  else
                    varargout{ii} = data(:,ii);
                  end
                end
              else
                %Convert entire table to matrix if possible
                if p.uniformOutput && uniformOutputPossible
                  data        =  cell2mat( data );
                end
                varargout{1}  = data;
              end
            end
        end
    end
        
    methods (Static)
      function obj = loadobj(obj)
         if isstruct(obj)            
            newObj = FM.FinMetrics();
            newObj.Init();

            newObj.Project = obj.Project;
            newObj.assetUniverseFile = obj.assetUniverseFile;
            newObj.ActivePortfolio = obj.ActivePortfolio;
                        
            if ~isempty(newObj.assetUniverseFile)
                newObj.OpenAssetUniverse(newObj.assetUniverseFile);
            end
            
            newObj.portfolioFiles = obj.portfolioFiles;
            if ~isempty(newObj.portfolioFiles)
                newObj.LoadPortfolios();
            end            
            
            obj = newObj;
         end
      end
    end  
end

Contact us