“;y(M(t,”application/vnd.ms-excel”)||”data:application/vnd.ms-excel;base64,”+T.btoa(unescape(encodeURIComponent(t))),this.getFilename()+”.xls”)})}function R(t){let e=””,o=this.getDataRows(),a=this.options.exporting.csv,n=C(a.decimalPoint,”,”!==a.itemDelimiter&&t?1.1.toLocaleString()[1]:”.”),i=C(a.itemDelimiter,”,”===n?”;”:”,”),r=a.lineDelimiter;return o.forEach((t,a)=>{let s=””,l=t.length;for(;l–;)”string”==typeof(s=t[l])&&(s=`”${s}”`),”number”==typeof s&&”.”!==n&&(s=s.toString().replace(“.”,n)),t[l]=s;t.length=o.length?o[0].length:0,e+=t.join(i),a1?o:e.name,topLevelColumnTitle:e.name}:e.name+(a>1?” (“+o+”)”:””):e.options.title&&e.options.title.text||(e.dateTime?u:p):p},m=function(t,e,o){let a={},n={};return e.forEach(function(e){let i=(t.keyToAxis&&t.keyToAxis[e]||e)+”Axis”,r=A(o)?t.chart[i][o]:t[i];a[e]=r&&r.categories||[],n[e]=r&&r.dateTime}),{categoryMap:a,dateTimeValueAxisMap:n}},f=function(t,e){let o=t.pointArrayMap||[“y”];return t.data.some(t=>void 0!==t.y&&t.name)&&e&&!e.categories&&”name”!==t.exportKey?[“x”,…o]:o},x=[],b,y,w,T=0,v,S;for(v in this.series.forEach(function(e){let o=e.options.keys,l=e.xAxis,d=o||f(e,l),p=d.length,u=!e.requireSorting&&{},b=r.indexOf(l),y=m(e,d),v,D;if(!1!==e.options.includeInDataExport&&!e.options.isInternal&&!1!==e.visible){for(L(x,function(t){return t[0]===b})||x.push([b,T]),D=0;D
{if(!t.tagName||”#text”===t.tagName)return t.textContent||””;let o=t.attributes,a=`<${t.tagName}`;return o&&Object.keys(o).forEach(t=>{let e=o[t];a+=` ${t}=”${e}”`}),a+=”>”,a+=t.textContent||””,(t.children||[]).forEach(t=>{a+=e(t)}),a+=`${t.tagName}>`};return e(this.getTableAST(t))}function B(t){let e=0,o=[],a=this.options,n=t?1.1.toLocaleString()[1]:”.”,i=C(a.exporting.useMultiLevelHeaders,!0),r=this.getDataRows(i),s=i?r.shift():null,l=r.shift(),h=function(t,e){let o=t.length;if(e.length!==o)return!1;for(;o–;)if(t[o]!==e[o])return!1;return!0},c=function(t,e,o,a){let i=C(a,””),r=”highcharts-text”+(e?” “+e:””);return”number”==typeof i?(i=i.toString(),”,”===n&&(i=i.replace(“.”,n)),r=”highcharts-number”):a||(r=”highcharts-empty”),{tagName:t,attributes:o=S({class:r},o),textContent:i}};!1!==a.exporting.tableCaption&&o.push({tagName:”caption”,attributes:{class:”highcharts-table-caption”},textContent:C(a.exporting.tableCaption,a.title.text?a.title.text:”Chart”)});for(let t=0,o=r.length;te&&(e=r[t].length);o.push(function(t,e,o){let n=[],r=0,s=o||e&&e.length,l,d=0,p;if(i&&t&&e&&!h(t,e)){let o=[];for(;r1&&t.attributes&&(t.attributes.valign=”top”,t.attributes.rowspan=p),o.push(t)}n.push({tagName:”tr”,children:o})}if(e){let t=[];for(r=0,s=e.length;rt.children[e].textContent,a=(t,e)=>(a,n)=>{let i,r;return i=o(e?a:n,t),r=o(e?n:a,t),””===i||””===r||isNaN(i)||isNaN(r)?i.toString().localeCompare(r):i-r};if(e&&t.options.exporting&&t.options.exporting.allowTableSorting){let o=e.querySelector(“thead tr”);o&&o.childNodes.forEach(o=>{let n=o.closest(“table”);o.addEventListener(“click”,function(){let i=[…e.querySelectorAll(“tr:not(thead tr)”)],r=[…o.parentNode.children];i.sort(a(r.indexOf(o),t.ascendingOrderInTable=!t.ascendingOrderInTable)).forEach(t=>{n.appendChild(t)}),r.forEach(t=>{[“highcharts-sort-ascending”,”highcharts-sort-descending”].forEach(e=>{t.classList.contains(e)&&t.classList.remove(e)})}),o.classList.add(t.ascendingOrderInTable?”highcharts-sort-ascending”:”highcharts-sort-descending”)})})}}function P(){this.options&&this.options.exporting&&this.options.exporting.showTable&&!this.options.chart.forExport&&this.viewData()}function K(){this.dataTableDiv?.remove()}let W=s();W.dataURLtoBlob=W.dataURLtoBlob||u.dataURLtoBlob,W.downloadURL=W.downloadURL||u.downloadURL,({compose:function(t,e){let o=t.prototype;if(!o.getCSV){let a=x().exporting;v(t,”afterViewData”,j),v(t,”render”,P),v(t,”destroy”,K),o.downloadCSV=H,o.downloadXLS=O,o.getCSV=R,o.getDataRows=N,o.getTable=V,o.getTableAST=B,o.hideData=F,o.toggleDataTable=I,o.viewData=U,a&&(S(a.menuItemDefinitions,{downloadCSV:{textKey:”downloadCSV”,onclick:function(){this.downloadCSV()}},downloadXLS:{textKey:”downloadXLS”,onclick:function(){this.downloadXLS()}},viewData:{textKey:”viewData”,onclick:function(){k.call(this,this.toggleDataTable)}}}),a.buttons&&a.buttons.contextButton.menuItems&&a.buttons.contextButton.menuItems.push(“separator”,”downloadCSV”,”downloadXLS”,”viewData”)),b(f);let{arearange:n,gantt:i,map:r,mapbubble:s,treemap:l,xrange:h}=e.types;n&&(n.prototype.keyToAxis={low:”y”,high:”y”}),i&&(i.prototype.exportKey=”name”,i.prototype.keyToAxis={start:”x”,end:”x”}),r&&(r.prototype.exportKey=”name”),s&&(s.prototype.exportKey=”name”),l&&(l.prototype.exportKey=”name”),h&&(h.prototype.keyToAxis={x2:”x”})}}}).compose(W.Chart,W.Series);let $=s();return i.default})());
// Internal-FR – Source Code
!/**
* Highcharts JS v12.1.2 (2024-12-21)
* @module highcharts/modules/series-label
* @requires highcharts
*
* (c) 2009-2024 Torstein Honsi
*
* License: www.highcharts.com/license
*/function(t,e){“object”==typeof exports&&”object”==typeof module?module.exports=e(t._Highcharts,t._Highcharts.Templating):”function”==typeof define&&define.amd?define(“highcharts/modules/series-label”,[“highcharts/highcharts”],function(t){return e(t,t.Templating)}):”object”==typeof exports?exports[“highcharts/modules/series-label”]=e(t._Highcharts,t._Highcharts.Templating):t.Highcharts=e(t.Highcharts,t.Highcharts.Templating)}(“undefined”==typeof window?this:window,(t,e)=>(()=>{“use strict”;var o={984:t=>{t.exports=e},944:e=>{e.exports=t}},r={};function a(t){var e=r[t];if(void 0!==e)return e.exports;var i=r[t]={exports:{}};return o[t](i,i.exports,a),i.exports}a.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return a.d(e,{a:e}),e},a.d=(t,e)=>{for(var o in e)a.o(e,o)&&!a.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},a.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var i={};a.d(i,{default:()=>B});var h=a(944),n=a.n(h),l=a(984),s=a.n(l);let c={enabled:!0,connectorAllowed:!1,connectorNeighbourDistance:24,format:void 0,formatter:void 0,minFontSize:null,maxFontSize:null,onArea:null,style:{fontSize:”0.8em”,fontWeight:”bold”},useHTML:!1,boxesToAvoid:[]};function p(t,e,o,r,a,i){let h=(i-e)*(o-t)-(r-e)*(a-t);return h>0||!(h<0)}function d(t,e,o,r,a,i,h,n){return p(t,e,a,i,h,n)!==p(o,r,a,i,h,n)&&p(t,e,o,r,a,i)!==p(t,e,o,r,h,n)}let{animObject:f}=n(),{format:u}=s(),{setOptions:x}=n(),{composed:g}=n(),{boxIntersectLine:b,intersectRect:m}={boxIntersectLine:function(t,e,o,r,a,i,h,n){return d(t,e,t+o,e,a,i,h,n)||d(t+o,e,t+o,e+r,a,i,h,n)||d(t,e+r,t+o,e+r,a,i,h,n)||d(t,e,t,e+r,a,i,h,n)},intersectRect:function(t,e){return!(e.left>t.right||e.rightt.bottom||e.bottom=e-16&&m[X-1].chartX<=e+r.width+16){if(b(e,o,r.width,r.height,m[X-1].chartX,m[X-1].chartY,m[X].chartX,m[X].chartY))return!1;t===s&&!u&&a&&(u=b(e-16,o-16,r.width+32,r.height+32,m[X-1].chartX,m[X-1].chartY,m[X].chartX,m[X].chartY))}(l||u)&&(t!==s||n)&&(c=Math.min(c,(x=e+r.width/2-m[X].chartX)*x+(g=o+r.height/2-m[X].chartY)*g))}if(!n&&l&&t===s&&(a&&!u||c(t||0)+(e||0),0),e.labelSeriesMaxSum=Math.max(e.labelSeriesMaxSum||0,r.sum||0)),”load”===t.type&&(o=Math.max(o,f(r.options.animation).duration)),h&&(void 0!==h[0].plotX?i.animate({x:h[0].plotX+h[1],y:h[0].plotY+h[2]}):i.attr({opacity:0})))}),e.seriesLabelTimer=S(function(){e.series&&e.labelSeries&&function(t){t.boxesToAvoid=[];let e=t.labelSeries||[],o=t.boxesToAvoid;t.series.forEach(t=>(t.points||[]).forEach(e=>(e.dataLabels||[]).forEach(e=>{let{width:r,height:a}=e.getBBox(),i=(e.translateX||0)+(t.xAxis?t.xAxis.pos:t.chart.plotLeft),h=(e.translateY||0)+(t.yAxis?t.yAxis.pos:t.chart.plotTop);o.push({left:i,top:h,right:i+r,bottom:h+a})}))),e.forEach(function(t){let e=t.options.label||{};t.interpolatedPoints=function(t){let e,o,r,a,i;if(!t.xAxis&&!t.yAxis)return;let h=t.points,n=[],l=t.graph||t.area,s=l&&l.element,c=t.chart.inverted,p=t.xAxis,d=t.yAxis,f=c?d.pos:p.pos,u=c?p.pos:d.pos,x=c?p.len:d.len,g=c?d.len:p.len,b=Y((t.options.label||{}).onArea,!!t.area),m=d.getThreshold(t.options.threshold),y={},X=c?”chartCenterX”:”chartCenterY”;function w(t){let e=Math.round((t.plotX||0)/8)+”,”+Math.round((t.plotY||0)/8);y[e]||(y[e]=1,n.push(t))}if(t.getPointSpline&&s&&s.getPointAtLength&&!b&&h.length<(t.chart.plotSizeX||0)/16){let t=l.toD&&l.attr(“d”);for(l.toD&&l.attr({d:l.toD}),r=s.getTotalLength(),e=0;e16&&o<999)for(i=1,a=Math.ceil(o/16);ir&&t<=a-o.width&&e>=s&&e<=s+p-o.height}function E(){L&&(e.labelBySeries=L.destroy())}if(x&&!n&&(T=[e.xAxis.toPixels(b[0]),e.xAxis.toPixels(b[b.length-1])],P=Math.min.apply(Math,T),B=Math.max.apply(Math,T)),e.visible&&!e.boosted&&d){if(!L){let a=e.name;if(“string”==typeof o.format?a=u(o.format,e,t):o.formatter&&(a=o.formatter.call(e)),e.labelBySeries=L=t.renderer.label(a,0,0,”connector”,0,0,o.useHTML).addClass(“highcharts-series-label highcharts-series-label-“+e.index+” “+(e.options.className||””)+” “+r),!t.renderer.styledMode){let r=”string”==typeof e.color?e.color:”#666666″;L.css(X({color:x?t.renderer.getContrast(r):r},o.style||{})),L.attr({opacity:t.renderer.forExport?1:0,stroke:e.color,”stroke-width”:1})}i&&h&&L.css({fontSize:i+(e.sum||0)/(e.chart.labelSeriesMaxSum||0)*(h-i)+”px”}),L.attr({padding:0,zIndex:3}).add()}for((m=L.getBBox()).width=Math.round(m.width),A=d.length-1;A>0;A-=1)x?C(y=(d[A].chartCenterX??d[A].chartX)-m.width/2,w=(d[A].chartCenterY??d[A].chartY)-m.height/2,m)&&(S=v(e,y,w,m)):(C(y=d[A].chartX+3,w=d[A].chartY-m.height-3,m)&&(S=v(e,y,w,m,!0)),S&&g.push(S),C(y=d[A].chartX+3,w=d[A].chartY+3,m)&&(S=v(e,y,w,m,!0)),S&&g.push(S),C(y=d[A].chartX-m.width-3,w=d[A].chartY+3,m)&&(S=v(e,y,w,m,!0)),S&&g.push(S),C(y=d[A].chartX-m.width-3,w=d[A].chartY-m.height-3,m)&&(S=v(e,y,w,m,!0))),S&&g.push(S);if(o.connectorAllowed&&!g.length&&!x)for(y=l+c-m.width;y>=l;y-=16)for(w=s;we.weight-t.weight),S=g[0],(t.boxesToAvoid||[]).push({left:S.x,right:S.x+m.width,top:S.y,bottom:S.y+m.height});let o=Math.sqrt(Math.pow(Math.abs(S.x-(L.x||0)),2)+Math.pow(Math.abs(S.y-(L.y||0)),2));if(o&&e.labelBySeries){let r,i={opacity:t.renderer.forExport?1:0,x:S.x,y:S.y},h={opacity:1};o<=10&&(h={x:i.x,y:i.y},i={}),a&&(r=f(e.options.animation),r.duration*=.2),e.labelBySeries.attr(X(i,{anchorX:S.connectorPoint&&(S.connectorPoint.plotX||0)+l,anchorY:S.connectorPoint&&(S.connectorPoint.plotY||0)+s})).animate(h,r),e.options.kdNow=!0,e.buildKDTree();let n=e.searchPoint({chartX:S.x,chartY:S.y},!0);n&&(L.closest=[n,S.x-(n.plotX||0),S.y-(n.plotY||0)])}}else E()}else E()}),w(t,”afterDrawSeriesLabels”)}(e)},e.renderer.forExport||!o?0:o)}}function T(t,e,o,r,a){let i=a&&a.anchorX,h=a&&a.anchorY,n,l,s=o/2;return M(i)&&M(h)&&(n=[[“M”,i,h]],(l=e-h)<0&&(l=-r-l),le+r?n.push([“L”,t+s,e+r]):ht+o&&n.push([“L”,t+o,e+r/2])),n||[]}let P=n();({compose:function(t,e){A(g,”SeriesLabel”)&&(y(t,”load”,L),y(t,”redraw”,L),e.prototype.symbols.connector=T,x({plotOptions:{series:{label:c}}}))}}).compose(P.Chart,P.SVGRenderer);let B=n();return i.default})());
const highchartsRecessionBands = [
{ color: ‘#EFEFEF’, from: Date.UTC(1923,4,1), to: Date.UTC(1924,5,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1926,9,1), to: Date.UTC(1927,10,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1929,7,1), to: Date.UTC(1933,2,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1937,4,1), to: Date.UTC(1938,5,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1945,1,1), to: Date.UTC(1945,9,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1948,10,1), to: Date.UTC(1949,9,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1953,6,1), to: Date.UTC(1954,4,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1957,7,1), to: Date.UTC(1958,3,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1960,3,1), to: Date.UTC(1961,1,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1969,11,1), to: Date.UTC(1970,10,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1973,10,1), to: Date.UTC(1975,2,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1980,0,1), to: Date.UTC(1980,6,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1981,6,1), to: Date.UTC(1982,10,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(1990,6,1), to: Date.UTC(1991,2,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(2001,2,1), to: Date.UTC(2001,10,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(2007,11,1), to: Date.UTC(2009,5,1) },
{ color: ‘#EFEFEF’, from: Date.UTC(2020,2,1), to: Date.UTC(2021,3,1) }
];
const monthNames = [ “January”,
“February”, “March”, “April”, “May”,
“June”, “July”, “August”, “September”,
“October”, “November”, “December”
];
let resizeTimeout;
let chartList = [];
const FRBAHighcharts = (function FRBAHighcharts() {
let title = “”;
let subTitle = “”;
let legendAlignPosition = “top”;
let legendYPosition = 0;
let sourceLine = “Source: Federal Reserve Bank of Atlanta”;
let categories = null;
let stacking = “normal”;
let dataSeries = [];
let xAxisType = “datetime”;
let xAxisTitle = “”;
let reverse = false;
let yAxisLabel = “”;
let xAxisMin = null;
let yAxisMin = null;
let enableYAxisLabel = true;
let enableTooltip = true;
let tooltipHeader = “”;
let formattedTooltip = ”;
let isShowLegend = true;
let additionalTooltipDataFormat = null;
let chartRecessionBands = null;
let enableLastDataLabel = false;
let tickInterval = null;
let polar = false;
let mobileMarginBottom;
function initializeHighcharts(type = “line”, marginBottom = 110) {
categories = null;
chartRecessionBands = highchartsRecessionBands;
dataSeries = [];
enableLastDataLabel = false;
formattedTooltip = ”;
mobileMarginBottom = marginBottom;
reverse = false;
subTitle=””;
tickInterval = null;
tooltipHeader=””;
xAxisTitle=””;
yAxisLabel=””;
Highcharts.setOptions({
chart: {
marginBottom: marginBottom,
style: { overflow: ‘visible’ },
styledMode: true,
type: type
},
accessibility: {
enabled: true,
screenReaderSection: {
beforeChartFormat: ‘{chartTitle}’
}
},
credits: { enabled: false},
exporting: {
scale: 1, allowHTML: true,
chartOptions: {
legend: { enabled: true },
rangeSelector: { enabled: false }
},
enableImages: true,
buttons: {
contextButton: {
text: ‘Export’,
menuItems: [{
text: ‘Print this chart’, onclick: function () {
this.print();
thisTitle = this.options.title.text; newTitle = thisTitle.replace(“
“,”: “); newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘print’, ‘label’: newTitle + ‘ | ‘ + document.title });
}
}, {
separator: true,
}, {
text: ‘Save as PNG’, onclick: function () {
this.exportChart();
let thisTitle = this.options.title.text;
let newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘png’, ‘label’: newTitle + ‘ | ‘ + document.title });
}
}, {
text: ‘Save as JPEG’, onclick: function () {
this.exportChart({ type: ‘image/jpeg’ });
let thisTitle = this.options.title.text;
let newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘jpeg’, ‘label’: newTitle + ‘ | ‘ + document.title });
}
}, {
text: ‘Save as SVG’, onclick: function () {
this.exportChart({ type: ‘image/svg+xml’ });
let thisTitle = this.options.title.text;
let newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘svg’, ‘label’: newTitle + ‘ | ‘ + document.title });
}
}, {
text: ‘Save as PDF’, onclick: function () {
this.exportChart({ type: ‘application/pdf’ });
let thisTitle = this.options.title.text;
let newTitle = thisTitle.replace(“
“,”: “); newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘pdf’, ‘label’: newTitle + ‘ | ‘ + document.title });
}
}]
}
}
},
lang: {
thousandsSep: ‘,’
},
plotOptions: {
area: {
marker: { enabled: false }
},
scatter: { marker: { enabled: true } },
spline: { marker: { enabled: false } },
series: {
events: {
hide: function () {
thisTitle = this.chart.title.textStr;
newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘hideSeries’, ‘label’: newTitle + ‘ (series: ‘ + this.name + ‘) | ‘ + document.title });
},
show: function () {
thisTitle = this.chart.title.textStr;
newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘showSeries’, ‘label’: newTitle + ‘ (series: ‘ + this.name + ‘) | ‘ + document.title });
}
},
label: { enabled: false},
marker: {
//enabled: true
},
states: {
inactive: { opacity: 1 }
}
}
},
xAxis: {
labels: {
y: 20,
lineWidth: 2, tickmarkPlacement: ‘on’,
labels: { y: 40},
}
},
yAxis: {
lineWidth: 0, tickmarkPlacement: ‘on’,
plotLines: [{ value: 0, width: 2, zIndex: 1 }]
}
});
}
function setMobileMarginBottom(margin) {
mobileMarginBottom = margin;
}
// sets the title of the highchart
function setTitle(text) {
title = text;
}
// sets the subtitle
function setSubTitle(text) {
subTitle = text;
}
function setSourceLine(source) {
sourceLine = source;
}
function setXAxisType(type) {
xAxisType = type;
}
function setXAxisTitle(title) {
xAxisTitle = title;
}
function setYAxisLabel(label) {
yAxisLabel = label;
}
function setEnableYAxisLabel(value) {
enableYAxisLabel = value;
}
function setPointStart(start) {
xAxisMin = convertDateToUTC(start);
}
function setXAxisMin(value) {
xAxisMin = value;
}
function setYAxisMin(value) {
yAxisMin = value;
}
function setReversed(value) {
reverse = value;
}
function setCategories(cats) {
categories = cats;
}
// add a series to the data chart with the series name as the first argucment and an array as the second arguement
function addSeries(seriesName, seriesData, visible = true) {
let seriesObject = {};
let description = title + “: ” + seriesName;
seriesObject = {name: seriesName, data: seriesData, visible: visible, accessibility: {description: description}};
dataSeries.push(seriesObject);
}
// used when you have a mix of series such as line, column
function addSeriesType(seriesName, seriesData, type, classColor=””, showInLegend = true, zIndex = 99 ) {
let seriesObject = {};
let description = title + “: ” + seriesName;
seriesObject = {name: seriesName, data: seriesData, accessibility: {description: description}, type: type, className: classColor, showInLegend: showInLegend, zIndex: zIndex };
dataSeries.push(seriesObject);
}
function hideSeries(chart, index) {
chart.series[index].hide();
let thisTitle = chart.title.textStr;
let newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘hideSeries’, ‘label’: newTitle + ‘ (series: ‘ + this.name + ‘) | ‘ + document.title });
}
function showSeries(chart, index) {
chart.series[index].show();
let thisTitle = chart.title.textStr;
let newTitle = thisTitle.replace(“
“,”: “);
newTitle = thisTitle.replace(“
“,”: “);
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ‘event’: ‘Highcharts’, ‘category’: ‘Highcharts’, ‘action’: ‘showSeries’, ‘label’: newTitle + ‘ (series: ‘ + this.name + ‘) | ‘ + document.title });
}
function setTooltipHeader(value) {
tooltipHeader = value;
}
// set tooltip for hover or click on a line series
function setTooltip(value) {
formattedTooltip = value;
}
// hide or show legend of the chart
function showLegend(enabled) {
isShowLegend = enabled;
}
function setLegendPosition(align, y) {
legendAlignPosition = align;
legendYPosition = y;
}
function setAdditionalTooltipData(data) {
additionalTooltipDataFormat = data;
}
// used for stacking columns
function setStacking(value) {
stacking = value;
}
function showLastDataLabel(enable) {
enableLastDataLabel = enable;
}
function hideRecessionBands(hide) {
if(hide) {
chartRecessionBands = null;
} else {
chartRecessionBands = recessionBands;
}
}
function setTickInterval(interval) {
tickInterval = interval;
}
// draws the chart and returns the chart reference
function drawChart(container) {
let formatString = formattedTooltip;
let tooltipHeaderFormat = tooltipHeader;
let additionalTooltipData = additionalTooltipDataFormat;
let chart = new Highcharts.Chart({
chart: {
polar: polar,
renderTo: container
},
title: {
align: ‘left’,
text: title,
useHTML: true,
width: $(`#${container}`)[0].offsetWidth * 0.8
},
subtitle: {
text: subTitle,
width: $(`#${container}`)[0].offsetWidth * 0.8
},
legend: {
enabled: isShowLegend, borderRadius: 5, borderWidth: 1, layout: ‘horizontal’, symbolWidth: 20,
useHTML: false, itemMarginTop: 2, itemMarginBottom: 2, verticalAlign: legendAlignPosition, y: legendYPosition
},
yAxis: {
labels: {
enabled: enableYAxisLabel
},
min: yAxisMin,
title: {
text: yAxisLabel
}
},
xAxis: {
min: xAxisMin,
categories: categories,
labels: {
y: undefined
},
plotBands: chartRecessionBands,
reversed: reverse,
tickInterval: tickInterval,
title: { text: xAxisTitle },
type: xAxisType
},
plotOptions: {
area: {
enableMouseTracking: enableTooltip,
stacking: stacking
},
arearange: {
lineWidth: 0,
marker: { enabled: false }
},
bar: {
dataLabels: {
enabled: true,
x: 50,
align: ‘right’
}
},
column: {
stacking: stacking
},
line: {
enableMouseTracking: enableTooltip,
dataLabels: {
enabled: enableLastDataLabel,
formatter: function() {
if (this.point.x == this.series.data[this.series.data.length-1].x && this.point.y == this.series.data[this.series.data.length-1].y) {
return ” + Highcharts.numberFormat(this.y, 1) + ‘%’;
}
}
},
label: {
connectorAllowed: false
},
marker: {
enabled: false
}
}
},
responsive: {
rules: [{
condition: {
maxWidth: 750
},
chartOptions: {
chart: {
marginBottom: mobileMarginBottom,
},
legend: {
itemWidth: 100,
verticalAlign: “top”,
x: -15,
y: 0
}
}
}]
},
series: dataSeries,
tooltip: {
enabled: enableTooltip,
formatter: function () {
let date = new Date(this.x);
var s = “”;
if(tooltipHeaderFormat.length > 0) {
s = eval(tooltipHeaderFormat);
}
if(formatString.length > 0) {
this.points.forEach(function (point, i) {
s += eval(formatString);
});
}
return s;
},
shared: true
}
});
const tabPanel = $(‘.tab-panel.active’);
let element = document.getElementById(container);
let chartWidth = element.offsetWidth;
// when a chart is inside a tab, the div width is the size of the tab header and not the full time
if(tabPanel.length > 0) {
const padding = parseFloat(window.getComputedStyle(tabPanel[0]).getPropertyValue(‘padding’)) * 2 + 3;
chartWidth = tabPanel[0].offsetWidth – padding;
$(‘.highcharts-title’).css(“width”, chartWidth * .8);
}
element.style.overflow = ‘visible’;
chart.renderer.html(”).add();
let lastPoint;
for(let i = 0; i < chart.series.length; i++) {
if(chart.series[i].data.length > 2 && typeof chart.series[i].data[chart.series[i].data.length – 2].y != “undefined” && chart.series[i].data[chart.series[i].data.length – 2].y=== null) {
lastPoint = chart.series[i].points[chart.series[i].points.length – 1];
lastPoint.update({marker: { enabled: true }}, false);
chart.series[i].chart.redraw();
}
}
chartList.push(chart);
$(‘.frba-highcharts-footer’).css(“width”, chartWidth);
$(‘.source-line’).css(“width”, chartWidth *.9);
return chart;
}
function destroyChart(chart) {
console.log(“in destroy chart”);
if(chart !== undefined) {
console.log(“chartList length b4”);
console.log(chartList.length);
for(let i = chartList.length-1; i >= 0; i–) {
if(chart.renderTo.id == chartList[i].renderTo.id) {
chartList.splice(i, 1);
console.log(“chartList length after”);
console.log(chartList.length);
chart.destroy();
}
}
}
}
// set the max data that can be shown on the xaxis of a chart with a start and end date.
function setExtremes(start, end, chart) {
let max = convertDateToUTC(end, 0);
const d = new Date(max);
d.setUTCMonth(d.getUTCMonth() + 1);
chart.xAxis[0].min = convertDateToUTC(start);
chart.xAxis[0].max = d.getTime();
chart.xAxis[0].setExtremes(chart.xAxis[0].min, chart.xAxis[0].max);
}
function addPlotBands(chart, data) {
chart.xAxis[0].addPlotBand(data);
}
function setPolar(value) {
polar = value;
}
function setEnableTooltip(value) {
enableTooltip = value;
}
return {
initializeHighcharts,
setTitle,
setSubTitle,
setSourceLine,
setXAxisType,
setXAxisTitle,
setXAxisMin,
setYAxisMin,
setReversed,
setYAxisLabel,
setEnableYAxisLabel,
setCategories,
setStacking,
setPointStart,
addSeries,
addSeriesType,
hideSeries,
showSeries,
setTooltipHeader,
setTooltip,
setEnableTooltip,
showLegend,
setLegendPosition,
drawChart,
destroyChart,
setExtremes,
setAdditionalTooltipData,
addPlotBands,
hideRecessionBands,
showLastDataLabel,
setTickInterval,
setPolar,
setMobileMarginBottom
}
}());
function createSlider(
sliderSelector,
dataArray,
chartInstance,
defaultStart = dataArray.length – 1,
getDateLabel = (date) => `${date[0].substring(0, 3)} ${date[2]}`
) {
let start = defaultStart === dataArray.length-1 ? 0 : dataArray.length – defaultStart – 1;
let max = dataArray.length – 1;
$(`${sliderSelector}`)[0].setAttribute(“default-start”, defaultStart);
$(sliderSelector).slider({
range: true,
min: 0,
max: max,
values: [start, max],
create: function () {
$(this)
.find(“.ui-slider-handle”)
.each(function (index) {
let pos = index === 0 ? start : max;
let date = getDateFromArray(dataArray, pos);
let label = getDateLabel(date);
$(this).append(`${label}`);
});
},
slide: function (event, ui) {
$(this)
.find(“.ui-slider-handle”)
.each(function (index) {
let date = getDateFromArray(dataArray, ui.values[index]);
$(this).find(“.handle-label”).text(getDateLabel(date));
});
},
stop: function (event, ui) {
const dates = ui.values.map(value => getDateFromArray(dataArray, value));
const labels = dates.map(date => getDateLabel(date));
$(this).find(“.ui-slider-handle .handle-label”).each((index, label) => {
$(label).html(labels[index]);
});
FRBAHighcharts.setExtremes(dates[0], dates[1], chartInstance);
}
});
}
window.addEventListener(‘load’, function() {
const resetButtonElements = document.querySelectorAll(‘.dataPickerReset’);
resetButtonElements.forEach(element => {
// resets slider to beginning and end position
element.addEventListener(‘click’, function(event) {
let id = this.getAttribute(“slider-id”);
let $slider = $(`#${id}`);
let defaultStart = $slider[0].getAttribute(“default-start”);
var maxValue = $slider.slider(‘option’, ‘max’);
let values = [maxValue-defaultStart, maxValue];
$slider.slider({values:values});
// invoke stop to redraw the chart
$slider.slider(“option”, “stop”).call($slider, null, {handle: $slider.find(“.ui-slider-handle”), values: values});
});
});
});
/* Some dates are in 3/1/2025 format while others are in March 1, 2025 Format */
function getDateFromArray(dataArray, uiValue) {
let date = [];
if(dataArray[uiValue].Date == undefined) {
// handle 1 dimentional array
if (!dataArray.some(element => Array.isArray(element))) {
date = dataArray[uiValue].split(“https://www.atlantafed.org/”);
} else {
// handle 2 dimentional array
date = dataArray[uiValue][0].split(“https://www.atlantafed.org/”);
}
date[0] = monthNames[date[0]-1];
} else {
date = dataArray[uiValue].Date.split(” “);
}
return date;
}
// dates come in as either [“Month name”, “dd”, “yyyy”] or m/dd/yyyy
// the monthIndex is used for the max due to 3/31 needs to be 4/1 to show the first tick(far right) on the x axis
function convertDateToUTC(date, monthIndex = 1) {
let utc;
if(!date.isArray && date.includes(“https://www.atlantafed.org/”)) {
let dateArray = date.split(“https://www.atlantafed.org/”);
utc = Date.UTC(parseInt(dateArray[2]), parseInt(dateArray[0]) – monthIndex, parseInt(dateArray[1]));
} else {
utc = Date.UTC(parseInt(date[2]), monthNames.indexOf(date[0]), parseInt(date[1]));
}
return utc;
}
window.addEventListener(‘resize’, function() {
// Clear the previous timeout
clearTimeout(resizeTimeout);
// Set a new timeout to execute the function when the user stops resizing
resizeTimeout = setTimeout(function() {
// This will be executed when the user stops resizing
for(let i = 0; i < chartList.length; i++) {
const chart = chartList[i];
const tabPanel = $(‘.tab-panel.active’);
let chartWidth = chart.chartWidth;
if(tabPanel.length > 0) {
const padding = parseFloat(window.getComputedStyle(tabPanel[0]).getPropertyValue(‘padding’)) * 2 + 3;
chartWidth = tabPanel[0].offsetWidth – padding;
$(‘.highcharts-title’).css(“width”, chartWidth * .8);
}
chart.title.textWidth = chartWidth *.8;
chart.subtitle.textWidth = chartWidth *.8;
$(‘.frba-highcharts-footer’).css(“width”, chartWidth);
$(‘.source-line’).css(“width”, chartWidth *.9);
chart.redraw();
}
}, 250); // 250 milliseconds delay before executing the resize
});
let mptRateMoves = null;
let mptTargetRange = null;
let mptDistributions = null;
window.addEventListener(‘load’, function() {
const mptChart = document.getElementById(‘chart-mpt-rate-moves’);
const checkboxes = document.querySelectorAll(‘.mpt-series’);
const dateInputs = document.querySelectorAll(‘.date-picker’);
if(mptChart) {
RateMovesHeadings.reverse();
let [date1, date2] = buildDateMenus();
populateContractDropDownMenu();
mptRateMoves = createRateMoves(date1, date2);
mptTargetRange = createTargetRangeProbabilities(date1, date2);
mptDistributions = createDistributionOfFutureAverage(date1, date2);
}
// put charts back in original state
$(“#reset”).click(function() {
const date1 = dates[dates.length – 2];
const date2 = dates[dates.length – 1];
$(“#contract-date”).val($(‘#contract-date option:first’).val());
$(“#datepicker-from-input”).val(date1);
$(“#datepicker-to-input”).val(date2);
handleChartChange(date1, date2);
});
// a new contract is selected, change the charts accordingly
$(“#contract-date”).on(‘change’, function() {
const date1 = $(“#datepicker-from-input”).val();
const date2 = $(“#datepicker-to-input”).val();
handleChartChange(date1, date2);
});
checkboxes.forEach((item) => {
item.addEventListener(‘click’, function (e) {
// get the series index from the checkbox name
const index = $(‘#’ + this.name).attr(“date-index”) – 1;
if(this.checked) {
FRBAHighcharts.showSeries(mptRateMoves, index);
FRBAHighcharts.showSeries(mptRateMoves, index+2);
FRBAHighcharts.showSeries(mptTargetRange, index);
FRBAHighcharts.showSeries(mptDistributions, index);
} else {
FRBAHighcharts.hideSeries(mptRateMoves, index);
FRBAHighcharts.hideSeries(mptRateMoves, index+2);
FRBAHighcharts.hideSeries(mptTargetRange, index);
FRBAHighcharts.hideSeries(mptDistributions, index);
}
});
});
// Date’s can’t fall on the weekend. If it’s a saturday, pick the previous day, if it’s a sunday, pick the next day.
// As long as the dates fall within the min/max
dateInputs.forEach((item) => {
item.addEventListener(‘input’, function() {
const selectedDate = new Date(this.value);
const day = selectedDate.getUTCDay();
if (day === 6) { // Saturday
if (this.value > this.min) {
this.value = new Date(selectedDate.setDate(selectedDate.getDate() – 1)).toISOString().split(‘T’)[0];
} else {
this.value = new Date(selectedDate.setDate(selectedDate.getDate() + 2)).toISOString().split(‘T’)[0];
}
} else if (day === 0) { // Sunday
if (this.value < this.max) {
this.value = new Date(selectedDate.setDate(selectedDate.getDate() + 1)).toISOString().split(‘T’)[0];
} else {
this.value = new Date(selectedDate.setDate(selectedDate.getDate() – 2)).toISOString().split(‘T’)[0];
}
}
const date1 = $(“#datepicker-from-input”).val();
const date2 = $(“#datepicker-to-input”).val();
mptRateMoves = createRateMoves(date1, date2);
mptTargetRange = createTargetRangeProbabilities(date1, date2);
mptDistributions = createDistributionOfFutureAverage(date1, date2);
});
});
});
function buildDateMenus() {
/* reverse the “dates” array so the values are in chronological order */
dates.reverse();
const date1 = dates[dates.length – 2];
const date2 = dates[dates.length – 1];
const min = dates[0];
const max = dates[dates.length – 1];
const minArray = min.split(“-“);
const maxArray = max.split(“-“);
$(“#datepicker-from-input”).val(date1);
$(“#datepicker-to-input”).val(date2);
// set the min and max calendar dates
$(“#datepicker-from-input”).attr(“min”, min);
$(“#datepicker-from-input”).attr(“max”, max);
$(“#datepicker-to-input”).attr(“min”, min);
$(“#datepicker-to-input”).attr(“max”, max);
let message = “The available dates range from: ” +
minArray[1] + “https://www.atlantafed.org/” + minArray[2] + “https://www.atlantafed.org/” + minArray[0] + ” to ” +
maxArray[1] + “https://www.atlantafed.org/” + maxArray[2] + “https://www.atlantafed.org/” + maxArray[0] + ‘. ‘ +
“Dates selected on the weekend will be changed to the closest available date.”;
$(“#description-dates”).html(message);
return [date1, date2];
}
/* build the contract select menus*/
function populateContractDropDownMenu() {
for(let i = 1; i < 5; i++) {
$(“#contract-date”).append(
$(“”, { value: “contract” + i }).text(eval(`contract${i}_startDate`)[0])
);
}
}
function handleChartChange(date1, date2) {
FRBAHighcharts.destroyChart(mptRateMoves);
FRBAHighcharts.destroyChart(mptTargetRange);
FRBAHighcharts.destroyChart(mptDistributions);
mptRateMoves = createRateMoves(date1, date2);
mptTargetRange = createTargetRangeProbabilities(date1, date2);
mptDistributions = createDistributionOfFutureAverage(date1, date2);
}
function createRateMoves(date1, date2) {
let rateMovesHeadingsForChartCategories = [];
/* build the rate moves select menu; select every third item and extract the date part of the column’s name, which is formatted like YYYY-MM-DD_value */
for (let i = 0; i < RateMovesHeadings.length; i = i + 3) {
rateMovesHeadingsForChartCategories.push(RateMovesHeadings[i].split(“_”)[0]);
}
let beginDateMidPoints = [];
let endDateMidPoints = [];
let beginDateRange = [];
let endDateRange = [];
for (let thisRate = 1; thisRate <= rateMovesHeadingsForChartCategories.length; thisRate++) {
beginDateMidPoints.push(getValueForDate(getRateMovesMidpoints(thisRate), date1));
endDateMidPoints.push(getValueForDate(getRateMovesMidpoints(thisRate), date2));
beginDateRange.push(getLowHighForDate(getRateMovesRanges(thisRate), date1));
endDateRange.push(getLowHighForDate(getRateMovesRanges(thisRate), date2));
}
const tooltip = “‘
● ‘ + point.series.name + ‘: ‘ + Highcharts.numberFormat(point.y, 1) + ‘%'”;
FRBAHighcharts.initializeHighcharts();
FRBAHighcharts.setTitle(“The Expected Three-Month Average SOFR Path”);
FRBAHighcharts.setSubTitle(“Current target range: ” + RateMovesBasisPoints + ” basis points”);
FRBAHighcharts.setYAxisLabel(“Basis Points”);
FRBAHighcharts.hideRecessionBands(true);
FRBAHighcharts.setCategories(rateMovesHeadingsForChartCategories);
FRBAHighcharts.addSeries(date1, beginDateMidPoints);
FRBAHighcharts.addSeries(date2, endDateMidPoints);
FRBAHighcharts.addSeriesType(“date 1:range”, beginDateRange, “arearange”, “highcharts-area-range-1”, false);
FRBAHighcharts.addSeriesType(“date 2:range”, endDateRange, “arearange”, “highcharts-area-range-2″, false);
// getComputedStyle(point.series.group.element).fill + ‘;\”>● ‘ + point.series.name
FRBAHighcharts.setTooltipHeader(“this.points[0].series.chart.xAxis[0].categories[this.x] + ‘
● expected: ‘ + Highcharts.numberFormat(this.points[0].point.y, 2) + ‘
● range: ‘ + Highcharts.numberFormat(this.points[2].point.low, 2) + ‘ to ‘ + Highcharts.numberFormat(this.points[2].point.high, 2)”);
FRBAHighcharts.setMobileMarginBottom(150);
let chart = FRBAHighcharts.drawChart(“chart-mpt-rate-moves”);
for(let i = 0; i < chart.series.length; i++) {
if(chart.series[i].type === “line”) {
chart.series[i].update({marker: {enabled: true}}, false);
}
}
chart.redraw();
return chart;
}
function createTargetRangeProbabilities(date1, date2) {
let dateOneProbabilities = [];
let dateTwoProbabilities = [];
let availableBuckets = new Map();
const contractDate = $(“#contract-date option:selected”).text();
const contractArray = contractDate.split(“-“);
// The buckets along the x axis are based on all the current contract dates, the ProbBucket
// is a complete list of all posible buckets
for (let i = 0; i < buckets.length; i++) {
for (let j = 0; j < ProbBucket.length; j++) {
if (buckets[i] == ProbBucket[j] &&
ProbContract[j] == contractDate &&
ProbDate[j] == date1) {
dateOneProbabilities.push(Prob[j]);
availableBuckets.set(buckets[i], 1);
} else if (buckets[i] == ProbBucket[j] &&
ProbContract[j] == contractDate &&
ProbDate[j] == date2) {
dateTwoProbabilities.push(Prob[j]);
availableBuckets.set(buckets[i], 1);
}
}
}
let b = [];
for (let i = 0; i < buckets.length; i++) {
if (availableBuckets.get(buckets[i]) == 1) {
b.push(buckets[i]);
}
}
// If there are a lot of categories, they will appear vertically so there needs to be more
// room to display them
let marginSize = 110;
if(b.length > 15) {
marginSize = 180;
}
FRBAHighcharts.initializeHighcharts(“column”, marginSize);
FRBAHighcharts.setTitle(“Target Range Probabilities for ” + monthNames[contractArray[1]-1] + ” ” + contractArray[0]);
FRBAHighcharts.setYAxisLabel(“Probability”);
FRBAHighcharts.hideRecessionBands(true);
FRBAHighcharts.setCategories(b);
FRBAHighcharts.setStacking(null);
FRBAHighcharts.addSeries(date1, dateOneProbabilities);
FRBAHighcharts.addSeries(date2, dateTwoProbabilities);
FRBAHighcharts.setTooltip(“‘
Date: ‘ + point.series.name + ‘, Probability: ‘ + Highcharts.numberFormat(point.y, 2)”);
FRBAHighcharts.setMobileMarginBottom(170);
let chart = FRBAHighcharts.drawChart(“chart-mpt-target-range-probabilites”);
return chart;
}
function createDistributionOfFutureAverage(date1, date2) {
const contract = $(“#contract-date”).val();
const contractDate = $(“#contract-date option:selected”).text();
const dateOneProbabilities = eval(`${contract}_point${dates.indexOf(date1) + 1}`);
const dateTwoProbabilities = eval(`${contract}_point${dates.indexOf(date2) + 1}`);
// getComputedStyle(point.series.group.element).fill + ‘;\”>● ‘ + point.series.name
/*
FRBAHighcharts.setTooltipHeader(“this.points[0].series.chart.xAxis[0].categories[this.x] + ‘
● expected: ‘ + Highcharts.numberFormat(this.points[0].point.y, 2) + ‘
● range: ‘ + Highcharts.numberFormat(this.points[2].point.low, 2) + ‘ to ‘ + Highcharts.numberFormat(this.points[2].point.high, 2)”);
*/
FRBAHighcharts.initializeHighcharts(“spline”);
FRBAHighcharts.setTitle(“The Distribution of Future Three-Month Average SOFR”);
FRBAHighcharts.setSubTitle(“Starting: ” + contractDate);
FRBAHighcharts.hideRecessionBands(true);
FRBAHighcharts.setCategories([]);
FRBAHighcharts.setTickInterval(20);
FRBAHighcharts.setXAxisTitle(“Basis Points”);
FRBAHighcharts.setTooltip(“‘
●’ + point.series.name + ‘, ‘ + Highcharts.numberFormat(point.y, 1)”);
FRBAHighcharts.addSeries(date1, dateOneProbabilities);
FRBAHighcharts.addSeries(date2, dateTwoProbabilities);
FRBAHighcharts.setMobileMarginBottom(130);
let chart = FRBAHighcharts.drawChart(“chart-mpt-distribution-of-future-average”);
return chart;
}
function getRateMovesMidpoints(n) {
return eval(`RateMovesMidpoint${n}`);
}
function getRateMovesRanges(n) {
return eval(`RateMovesRange${n}`);
}
function getValueForDate(data, date) {
const matchingItem = data.find(item => item[0] === date);
return matchingItem ? matchingItem[1] : null;
}
function getLowHighForDate(data, date) {
const matchingItem = data.filter(item => item.x === date);
let range = [];
range.push(parseFloat(matchingItem[0].low));
range.push(parseFloat(matchingItem[0].high));
return range;
}
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src=”https://www.clarity.ms/tag/”+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, “clarity”, “script”, “jgl6rwv6yx”);
Skip to Content
This tool estimates the market-implied probabilities of various ranges for the three-month average fed funds rate.
Close
The Market Probability Tracker estimates probability distributions implied
by the prices of options from the Chicago Mercantile Exchange that
reference the three-month compounded average Secured Overnight Financing
Rate (SOFR). SOFR, published by the Federal Reserve Bank of New York,
broadly measures the cost of overnight (one-day) loans collateralized with
Treasury securities in the repurchase agreement, or repo, market. Because
the New York Fed’s Open Market Trading Desk implements monetary policy
through repo market transactions, we can use the estimated distributions
to make inferences about the market’s assessment of future target ranges
that the Federal Open Market Committee sets.
Updates are made daily using the most recently available data, typically
from the previous day. In this tracker, we show results from the four
nearest-expiring quarterly contracts. To observe changes in the market’s
assessment, users can view and compare estimates across the prior six
weeks for the market’s expected three-month average SOFR path and its
25th- to 75th-percentile region; the probabilities associated with future
target ranges over the quarterly intervals specified by individual
contracts; and the full distribution estimated by our model.
Note that CME Group market data are used—with permission from CME—as a
source of information for this report. CME Group has no other connection
to the Federal Reserve Bank of Atlanta’s products and services and does
not sponsor, endorse, recommend, or promote the report, its contents, or
the authors or any of their respective products or services. CME Group has
no obligation or liability in connection with the report, its contents, or
the authors or any of their respective products or services. CME group
does not guarantee the accuracy and/or the completeness of any market data
in connection with the report, its contents, or the authors or any of
their respective products or services and shall not have any liability for
any errors, omissions, or interruptions therein. There are no third-party
beneficiaries of any agreements or arrangements between CME Group and the
authors.
An overview of our approach can be found in our
Notes from the Vault. For a more detailed look at the model,
please refer to this paper and technical note on its implementation. To download source code and historical data: MPT Source Code, MPT Historical Data.
Distributions
Frequently Asked Questions
References and Resources
Use the options below to change the observation date(s) and contract you’d like to view.
What is the Secured Overnight Financing Rate (SOFR)?
The Secured Overnight Financing Rate, or SOFR, is the Alternative Reference
Rates Committee’s replacement for the US dollar London Interbank Offer Rate,
or USD LIBOR, as the market’s key benchmark rate on US dollar-denominated
securities. It represents the broad cost of overnight (one-day) loans, called
overnight repurchase agreements, or simply “repo,” that are collateralized
with US Treasury securities.
How is SOFR calculated?
The Federal Reserve Bank of New York calculates SOFR each day using
transaction-level data from the Bank of New York Mellon on overnight,
specific-counterparty tri-party general collateral repo transactions secured
by Treasury securities, along with GCF Repo transaction data obtained from the
US Department of Treasury’s Office of Financial Research. For more complete
information about SOFR’s administration, along with detail about the
transactions included in the calculation, please visit the
New York Fed’s website.
What are SOFR futures and options on SOFR futures?
SOFR futures are derivative contracts that deliver the equivalent overnight
repo-based financing across specific future quarterly windows. The rate
associated with a futures contract—the three-month compounded average SOFR—is
based on the market’s expectation of the cost of Treasury repo financing each
day throughout the contract’s indicated reference quarter.
Options on SOFR futures are derivative contracts that give the holder the
right, but not the obligation, to buy or sell SOFR futures expiring on (or
before) a specific date for a specific price, called the strike price. The
price of an option, called the option premium, is based on where the market
expects the cost of overnight Treasury repo financing will be relative to the
option’s strike price once the option expires. Thus, option premiums contain
information about the probability that SOFR, over the three months indicated
in the contract’s reference quarter, will be either above or below the strike
price specified in the option contract at expiration.
Both SOFR futures and options on SOFR futures are traded on the Chicago
Mercantile Exchange. For more detail about the contract specifications, please
visit the
CME’s website.
What impact do FOMC decisions have on SOFR?
Federal Open Market Committee decisions affect SOFR through the New York Fed’s
Open Market Trading Desk’s (the “Desk’s”) implementation of monetary policy.
Operationally, the Desk is authorized to conduct repurchase transactions with
its primary dealers and other eligible financial institutions to apply
downward pressure on the federal funds rate and to reverse repurchase
agreements to apply upward pressure on the federal funds rate. The Desk also
has the authority to conduct unscheduled repurchase transactions should
conditions warrant them. Together, these actions can help maintain the federal
funds rate within the FOMC’s target range and support smooth market
functioning. The rates at which repo and reverse repo transactions are
conducted are reflected by SOFR.
For more information about the Desk’s monetary policy implementation, please
go to the
New York Fed’s website.