Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 117 additions & 5 deletions toolbox/utilities/getYahoo.m
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
% 'candle' = candlestick chart
% 'line' = close price only
% 'ma' = close price and moving averages
% 'bollinger' = close price with Bollinger Bands (SMA and ±σ bands)
% Default is 'candle'.
% Example - 'topPanelMode','ma'
% Data Types - char | string
Expand Down Expand Up @@ -229,6 +230,16 @@
% Example - 'msg',false
% Data Types - logical
%
% WindowSize : window length for Bollinger Bands. Positive scalar integer.
% Default is 20.
% Example - 'WindowSize',15
% Data Types - double
%
% NumStd : number of standard deviations for Bollinger Bands. Positive scalar.
% Default is 2.
% Example - 'NumStd',2.5
% Data Types - double
%
% Output:
%
% out : structure array containing the following fields
Expand Down Expand Up @@ -875,10 +886,6 @@ function localPlotYahoo(TT, tickerNow, LastPeriod, intervalThis, ...
% Figure and manual layout
figure('Color','w');

if ~isMATLABReleaseOlderThan("R2025a")
theme(gcf, "light")
end

leftMargin = 0.07;
rightMargin = 0.03;
bottomMargin = 0.08;
Expand All @@ -903,6 +910,111 @@ function localPlotYahoo(TT, tickerNow, LastPeriod, intervalThis, ...

%% Top panel
switch lower(topPanelMode)
case {'bb','bollinger'}
% Bollinger Bands plot in the top panel (independent from 'out')
% Requirements: TT.Close, ax1, x must be available in localPlotYahoo workspace

% Parameters (change to read from options if desired)
bbWindow = 20;
bbNSig = 2;

closePrices = TT.Close;

% Compute Bollinger: use bollinger() if available, otherwise movmean/movstd
if exist('bollinger','file') == 2
try
[bbMid, bbUp, bbLow] = bollinger(closePrices, bbWindow, bbNSig);
catch
bbMid = movmean(closePrices, bbWindow, 'omitnan');
bbSigma = movstd(closePrices, bbWindow, 'omitnan');
bbUp = bbMid + bbNSig .* bbSigma;
bbLow = bbMid - bbNSig .* bbSigma;
end
else
bbMid = movmean(closePrices, bbWindow, 'omitnan');
bbSigma = movstd(closePrices, bbWindow, 'omitnan');
bbUp = bbMid + bbNSig .* bbSigma;
bbLow = bbMid - bbNSig .* bbSigma;
end

% Size safety
bbMid = bbMid(:); bbUp = bbUp(:); bbLow = bbLow(:);
xvec = x(:);
yvec = closePrices(:);

% Remove NaNs consistently across all vectors
valid = ~isnan(xvec) & ~isnan(yvec);
if nnz(valid) < 2
% nothing to plot: exit the case
return;
end
xvec = xvec(valid);
yvec = yvec(valid);
% align bands if there are initial NaN elements
if numel(bbMid) ~= numel(closePrices)
bbMid = interp1(find(~isnan(closePrices)), bbMid(~isnan(closePrices)), 1:numel(closePrices), 'linear', NaN)';
end
bbMid = bbMid(valid);
bbUp = bbUp(valid);
bbLow = bbLow(valid);

% Set axis and title
hold(ax1,'on'); grid(ax1,'on');

% Up/Down colored segments: successive differences
d = [0; diff(yvec)];
upIdx = d >= 0;
dnIdx = d < 0;

% Sequential up/down color plotting (no datetime conversion)
if any(upIdx)
plot(ax1, xvec(upIdx), yvec(upIdx), '-', 'Color', [0 0.6 0], 'LineWidth', 1.4);
end
if any(dnIdx)
plot(ax1, xvec(dnIdx), yvec(dnIdx), '-', 'Color', [0.85 0.15 0.15], 'LineWidth', 1.4);
end

% SMA and bands
plot(ax1, xvec, bbMid, '-','Color',[0 0.4470 0.7410],'LineWidth',1);
plot(ax1, xvec, bbUp, '--','Color',[0.3 0.3 0.3],'LineWidth',0.9);
plot(ax1, xvec, bbLow, '--','Color',[0.3 0.3 0.3],'LineWidth',0.9);

% Fill band using datetime directly (if supported)
try
xpatch = [xvec; flipud(xvec)];
ypatch = [bbUp; flipud(bbLow)];
hPatch = patch('XData', xpatch, 'YData', ypatch, ...
'FaceColor',[0.6 0.6 0.6], 'FaceAlpha', 0.08, ...
'EdgeColor','none', 'Parent', ax1);
uistack(hPatch, 'bottom');
catch
% if MATLAB version does not support patch with datetime, skip fill
end

% Marker on the last point
plot(ax1, xvec(end), yvec(end), 'o', 'MarkerFaceColor',[0 0.4470 0.7410], 'MarkerEdgeColor','k');

% Set limits with a small margin
try
xr = [xvec(1) xvec(end)];
yrLow = min(bbLow(~isnan(bbLow))); yrHigh = max(bbUp(~isnan(bbUp)));
if isempty(yrLow) || isempty(yrHigh) || isnan(yrLow) || isnan(yrHigh)
yrLow = min(yvec); yrHigh = max(yvec);
end
pad = 0.06 * (yrHigh - yrLow);
ylim(ax1, [yrLow - pad, yrHigh + pad]);
xlim(ax1, xr);
catch
% fallback: autoscale
end

% Minimal legend (update if you want to remove duplicate entries)
legend(ax1, {'Price up','Price down','SMA','Upper','Lower'}, 'Location', 'best');

hold(ax1,'off');

% End case

case 'candle'
if removeGaps
Data = [TT.Open TT.High TT.Low TT.Close];
Expand Down Expand Up @@ -1474,4 +1586,4 @@ function showPanelExplanationCommand(topPanelMode, bottomPanelMode, ...
intervalOut = priority{idxAllowed};
end

%FScategory:UTI-FIN
%FScategory:UTI-FIN
Loading