function [hAxes, hTimer] = IBMatlab_AlgoRSI(varargin)
% IBMatlab_AlgoRSI - simple Price-RSI plot and trading algo
%
% Inputs:     list of IBMatlab parameters e.g. 'Symbol', 'Exchange',
%             'Currency', 'BarSize', 'Quantity', 'AccountName', etc.
% Processing: Display interconnected plots of price vs. RSI
% Output:     hAxes  - handles to plot axes
%             hTimer - handle to the update timer object
% Example:    IBMatlab_AlgoRSI('Symbol','EUR', 'SecType','cash', 
%                              'Exchange','idealpro', 'Quantity',100);

   % Ensure that we have all mandatory input params
   IBMatlab_params = varargin;
   symbolIdx = find(strcmpi(IBMatlab_params,'symbol')) + 1;
   if isempty(symbolIdx)
       error('IBMatlab:NoSymbol','Must specify the Symbol');
   end
   symbol = upper(IBMatlab_params{symbolIdx});
   tagStr = ['RSI_' symbol];

   % Prepare the GUI window
   hAxes = prepareGUI();
   lastAction = '';

   % Prepare a timer to periodically check market conditions
   period = 60;  % [seconds]
   timerTag = ['timer-' tagStr];
   hTimer = timer('ExecutionMode','fixedRate', ...
                  'Period',period, ...
                  'Name',timerTag, ...
                  'Tag',timerTag, ...
                  'TimerFcn',@timerFcn);
   start(hTimer);

   % Prepare GUI window
   function hAxes = prepareGUI()
       % Create a new figure window for this symbol
       titleStr = ['Price / RSI for ' symbol];
       figure('NumberTitle','off', 'Name',titleStr, ...
              'Tag',tagStr, 'CloseRequestFcn',@closeFig);
       zoom on;

       % Prepare the prices plot axes
       hAxes(1) = axes('Units','norm', 'Pos',[.1,.50,.87,.45]);

       % Prepare the RSI plot axes
       hAxes(2) = axes('Units','norm', 'Pos',[.1,.25,.87,.20]);

       % Prepare the open position plot axes
       hAxes(3) = axes('Units','norm', 'Pos',[.1,.05,.87,.15]);

       % Link the X axes of the plots
       linkaxes(hAxes,'x');
   end

   % Callback function upon closing the figure window
   function closeFig(hFig, eventData) %#ok<*INUSD>
       try stop(hTimer); delete(hTimer); catch, end
       delete(hFig);
   end

   % Get the latest historical/intraday data from IB
   function data = getData()
       % Get the historical/intraday data bars from IB
       % Note: IBMatlab_params are persisted in the parent function
       bars = IBMatlab('Action','history', 'BarSize','5 mins', ...
                       'DurationValue',1, 'DurationUnits','W', ...
                       'WhatToShow','MIDPOINT', IBMatlab_params{:});

       % Ensure that enough data is available, otherwise bail out
       if length(bars.close) <= 15
           error('IBMatlab:NoData','Not enough data points!');
       end

       % Determine the RSI values for the retrieved data
       % rsindex() requires a column vector, so transpose the data
       RSI_vector = rsindex(bars.close');

       % Create timeseries objects for plotting
       data.times = datestr(datenum(bars.dateTime,'yyyymmdd  HH:MM:SS'));
       data.ts_close = timeseries(bars.close, data.times);
       data.ts_rsi   = timeseries(RSI_vector, data.times);
   end

   % Trade based on the retrieved IB data
   function data = tradeData(data)
       % Initialize the data points and trade action
       RSI_vector = data.ts_rsi.Data;
       openPos = zeros(1,length(RSI_vector));
       action = '';

       % Get the requested trading quantity
       quantityIdx = find(strcmpi(IBMatlab_params,'quantity'))+1;
       if isempty(quantityIdx)
           quantity = 1;
       else
           quantity = IBMatlab_params{quantityIdx};
       end

       % Calculate the trading indicator signals
       for idx = 2 : length(RSI_vector)
           currentRSI = RSI_vector(idx);
           lastRSI    = RSI_vector(idx-1);

           % If position is open
           if openPos(idx-1) ~= 0
               % If RSI has crossed 50
               if (currentRSI-50)*openPos(idx-1) > 0
                   % Close the open position
                   if openPos(idx-1) > 0
                       action = 'Sell';
                   else
                       action = 'Buy';
                   end
                   openPos(idx) = 0;
               else
                   % Else, do nothing for now
                   openPos(idx) = openPos(idx-1);
               end

           % Else, if RSI>70 going down (overbought reversal?)
           elseif currentRSI < lastRSI && currentRSI > 70
               % Place a SELL order
               action = 'Sell';
               openPos(idx) = -quantity;

           % Else, if RSI<30 going up (oversold reversal?)
           elseif currentRSI > lastRSI && currentRSI < 30
               % Place a BUY order
               action = 'Buy';
               openPos(idx) = quantity;
           end
       end

       % Trade if an action was triggered for latest data bar
       if ~isempty(action) && ~strcmp(action, lastAction)
           fprintf('%s %sing %d %s\n', datestr(now), action, quantity, symbol);
           IBMatlab('Action',action, 'Type','MKT', ...
                    'Quantity',quantity, IBMatlab_params{:});
       end
       lastAction = action;

       % Add the trading decisions to returned data (for plotting)
       data.ts_pos = timeseries(openPos, data.times);
   end

   % Plot the updated data
   function plotData(data)
       % hAxes is persisted in the parent function
       plotTS(hAxes(1), data.ts_close, 'Closing price', true);
       plotTS(hAxes(2), data.ts_rsi,   'RSI', true);
       plotTS(hAxes(3), data.ts_pos,   'Open pos', false);
   end

   % Timer callback function
   function timerFcn(hTimer,eventData)
       try
           data = getData();
           data = tradeData(data);
           plotData(data);
       catch err
           disp(err.message);
           return;
       end
   end
end  % IBMatlab_AlgoRSI

% Utility function to plot a time-series on an axes
function plotTS(hAxes, data, label, emptyXTicksFlag)
    axes(hAxes);
    plot(data);
    box off;
    grid on;
    title('');
    ylabel(label);
    if emptyXTicksFlag
        set(hAxes, 'XTick',[]);
    end
end
