BoE Poised For Insurance Hike As Inflation Risks Intensify
/*jshint esversion: 6 */
var socket;
var assigned_server = 3001;
var distributor_server = 3010;
var publisher_channel = '';
var broadcaster_channel = '';
var remote_ip = '';
var session_info = {};
var logging_interval;
var logging_interval_time = 120000;
var socket_emits = 0;
var session_start_time;
var reconnectionDelayGrowFactor = 1.2;
var reconnection_delay = 1000;
var playing = false;
var connected_to_assigned = false;
var socket_close_timeout;
var player_disconnected_annoucement_play_delay = 1500;
var player_disconnected_annoucement_play_timeout = null;
const TOKEN_ENDPOINT = "https://www.livesquawk.com/api/get_token.php";
self.onmessage = function (msg) {
switch(msg.data.action) {
case 'Reactivate': reactivate(); break;
case 'ConnectDistributor': connect_to_distributor(msg); break;
case 'SessionStaticInfo': session_static_info(msg); break;
case 'SessionInfo': update_session_info(msg.data); break;
case 'PauseLogging': clearTimeout(logging_interval); break;
case 'RestartLogging': postMessage({action: 'RequestSessionStaticInfo' }); break;
case 'JoinPublisherChannel': join_publisher_channel(msg.data); break;
case 'JoinPlayerChannel': join_player_channel(msg.data); break;
case 'CheckChannelStatus': check_channel_status(msg.data); break;
case 'PlayerLogResponse': player_log_response(msg.data); break;
case 'CloseSocket': close_socket(); break;
case 'RequestMessageSearch': request_message_search(msg.data); break;
case 'RequestMoreMessages': request_more_messages(msg.data); break;
case 'PlayerDisconnectedAnnoucementTimeoutStart': player_disconnected_annoucement_timeout_start(); break;
case 'PlayerDisconnectedAnnoucementTimeoutClear': player_disconnected_annoucement_timeout_clear(); break;
case 'GetConnectionState': checkSocketConnectionState(); break;
}
};
function player_disconnected_annoucement_timeout_clear() {
clearTimeout(player_disconnected_annoucement_play_timeout);
player_disconnected_annoucement_play_timeout = null;
}
function player_disconnected_annoucement_timeout_start() {
player_disconnected_annoucement_play_timeout = setTimeout(function() { send_play_disconnect_annoucement(); }, player_disconnected_annoucement_play_delay );
}
function send_play_disconnect_annoucement() {
postMessage({action: "PlayDisconnectAnnoucement"});
}
function reactivate() {
playing = false;
connect_to_assigned_server();
}
function player_log_response(data) {
var data_array = {};
data_array.type = 'PlayerLogResponse';
data_array.respond_to_socket_final = data.respond_to_socket_final;
data_array.respond_to_socket = data.respond_to_socket;
data_array.player_socket_id = data.player_socket_id;
data_array.logs = data.logs;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
function request_message_search(data) {
var data_array = {};
data_array.type = 'request_message_search';
data_array.publisher_channel = data.publisher_channel;
data_array.search_term = data.search_term;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
function request_more_messages(data) {
var data_array = {};
data_array.type = 'request_more_messages';
data_array.publisher_channel = data.publisher_channel;
data_array.last_record_id = data.last_record_id;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
function check_channel_status(data) {
var data_array = {};
data_array.type = 'check_channel_status';
data_array.app_name = data.app_name;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1 && connected_to_assigned) { socket.send(msg_string); }
else { setTimeout(function() { check_channel_status(data); },500); }
}
function check_channel_status_response(data) {
postMessage({ action: 'CheckChannelStatusReponse', stream_status: data.stream_status.status, onair: data.stream_status.onair, connected: data.stream_status.connected });
}
async function join_publisher_channel(data) {
if (typeof data.jwt_token === 'string' && data.jwt_token !== '') {
if (!socket || socket.readyState !== WebSocket.OPEN) {
console.warn("[Page worker] JWT join skipped: socket not open");
return;
}
socket.send(JSON.stringify({
type: "join_publisher_channel",
jwt_token: data.jwt_token,
publisher_channel: data.publisher_channel,
username: data.username,
subscriber_group: data.subscriber_group,
display_name: data.display_name,
single_sign_on: data.single_sign_on,
remote_ip: data.remote_ip,
http_referrer: data.http_referrer,
current_page: data.current_page,
php_session_id: data.php_session_id,
from_react: data.from_react,
...(typeof data.api_key === "string" && data.api_key.trim() !== "" ? { api_key: data.api_key } : {}),
}));
console.log("[Page worker] Sent supplied JWT join");
postMessage({ action: 'PublisherChannelJoined' });
return;
}
const joinSocket = socket; if (!joinSocket || joinSocket.readyState !== WebSocket.OPEN) { return; }
const apiKey = typeof data.api_key === "string" ? data.api_key.trim() : "";
const partnerName = partnerFromCurrentPage(data.current_page);
let tokenBody;
if (apiKey !== "") { tokenBody = { api_key: apiKey }; }
else if (partnerName) { tokenBody = { partner_name: partnerName }; }
else { tokenBody = { username: data.username || "", publisher_channel: data.publisher_channel || "", }; }
let jwtToken = null;
try {
const requestStartedAt = Date.now();
const response = await fetch(TOKEN_ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(tokenBody),
credentials: "same-origin",
cache: "no-store",
});
if (!response.ok) {
throw new Error(`token endpoint returned ${response.status}`);
}
const tokenData = await response.json();
if ( !tokenData || typeof tokenData.token !== "string" || tokenData.token === "" ) {
throw new Error("token endpoint returned no token");
}
const expiresIn = Number(tokenData.expires_in);
if (!Number.isFinite(expiresIn) || expiresIn <= 0) {
throw new Error("token endpoint returned an invalid lifetime");
}
const expiresAt = requestStartedAt + (expiresIn * 1000);
if (expiresAt <= Date.now() + 30000) {
throw new Error("token response has insufficient remaining lifetime");
}
jwtToken = tokenData.token;
}
catch (error) {
console.warn("[Page worker] Token request failed", error);
// API-key requests must not silently bypass token authentication.
if (apiKey !== "") { postMessage({ action: "auth_failed" }); return; }
}
// A reconnect during the fetch makes this join attempt obsolete.
if ( socket !== joinSocket || joinSocket.readyState !== WebSocket.OPEN ) { return; }
const payload = {
type: "join_publisher_channel",
publisher_channel: data.publisher_channel,
username: data.username,
subscriber_group: data.subscriber_group,
display_name: data.display_name,
single_sign_on: data.single_sign_on,
remote_ip: data.remote_ip,
http_referrer: data.http_referrer,
current_page: data.current_page,
php_session_id: data.php_session_id,
from_react: data.from_react,
};
if (apiKey !== "") { payload.api_key = apiKey; }
if (jwtToken) { payload.jwt_token = jwtToken; }
console.log( jwtToken ? "[Page worker] Sending fetched JWT with legacy details" : "[Page worker] Sending legacy join without JWT" );
joinSocket.send(JSON.stringify(payload));
postMessage({ action: "PublisherChannelJoined" });
}
function partnerFromCurrentPage(current_page) {
try {
if (!current_page) return '';
const fullUrl = current_page.startsWith('http') ? current_page : `https://${current_page}`;
return new URL(fullUrl).searchParams.get('partner_name') || '';
} catch (e) { return ''; }
}
function session_static_info(msg) {
set_static_session_info(msg.data);
start_log();
}
function connect_to_distributor(msg) {
publisher_channel = msg.data.publisher_channel;
remote_ip = msg.data.remote_ip;
socket = new WebSocket("wss://ws.livesquawk.com/ws"+distributor_server);
socket.onopen = function(event) { request_server_assignment(); };
socket.onmessage = function(event) {
var msg_string = event.data.toString();
var data = JSON.parse(msg_string);
switch(data.type) {
case 'server_assignment_response': server_assignment_response(data); break;
}
};
}
function session_info_interval() {
postMessage({action: 'RequestSessionInfo' });
logging_interval = setTimeout(session_info_interval,logging_interval_time);
}
function server_assignment_response(data) {
assigned_server = data.selected_server;
socket.close();
connect_to_assigned_server();
}
function request_server_assignment() {
var data_array = {};
data_array.type = 'request_server_assignment';
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
function connect_to_assigned_server() {
socket = new WebSocket("wss://ws.livesquawk.com/ws"+assigned_server);
socket.onopen = function(event) {
postMessage({ action: 'VisibleConsole',console_string: "Connected to server "+assigned_server });
console.log("Connected to server "+assigned_server);
postMessage({ action: 'ConnectedToAssigned' });
connected_to_assigned = true;
if (playing) {
postMessage({ action: 'RejoinPlayerChannel' });
}
};
socket.onmessage = function(event) {
var msg_string = event.data.toString();
var data = JSON.parse(msg_string);
if (data.type != 'server_ping') { /* console.log(data.type); */ }
switch(data.type) {
case 'reconnect': postMessage({ action: 'VisibleConsole',console_string: "Reconnected" }); break;
case 'reload': postMessage({ action: 'Reload' }); break;
case 'start_log_response': start_log_response(data); break;
case 'application_refresh_start': application_refresh_start(data); break;
case 'application_refresh_stop': application_refresh_stop(data); break;
case 'player_log_request':
case 'check_channel_status_response': check_channel_status_response(data); break;
case 'PlayerLogRequest':
case 'new_tweet':
case 'new_latest_news':
case 'delete_report':
case 'add_report':
case 'add_ref_doc':
case 'calendar_change':
case 'calendar_insert':
case 'calendar_delete':
case 'new_note':
case 'AppStart':
case 'AppStop':
case 'BroadcasterStarted':
case 'BroadcasterStopped':
case 'added_cme_report':
case 'updated_cme_report':
case 'added_cme_commentary':
case 'updated_cme_commentary':
case 'server_assignment_response':
case 'initial_messages':
case 'message_categories':
case 'more_messages':
case 'matched_messages':
pass_straight_to_post_message(data);
break;
case 'output':
case 'server_ping':
case 'reconnect':
case 'disconnect':
break;
}
};
socket.onerror = function(event) {
postMessage({ action: 'VisibleConsole',console_string: "LS Socket Error" + event.type });
console.log(event);
postMessage({ action: 'LSSocketConnectionError' });
};
socket.onclose = function(event) {
console.log('Socket is closed. Reconnect will be attempted in 1 second.', event.reason);
socket_close_timeout = setTimeout(connect_to_assigned_server, reconnection_delay);
reconnection_delay = reconnection_delay*reconnectionDelayGrowFactor;
connected_to_assigned = false;
};
}
function checkSocketConnectionState() {
postMessage({ action: 'SocketConnectionState', connected: Boolean(socket && socket.readyState === WebSocket.OPEN && connected_to_assigned ), });
}
function close_socket() {
clearTimeout(socket_close_timeout);
socket_close_timeout = null;
socket.onclose = function(event) {};
socket.onmessage = function(event) {};
socket.onerror = function(event) {};
socket.close(1000);
console.log('Socket Closed');
}
function pass_straight_to_post_message(data) {
data.action = data.type;
postMessage(data);
}
function join_player_channel(data) {
var data_array = {};
data_array.type = 'join_player_channel';
data_array.player_channel = data.player_channel;
data_array.remote_ip = data.remote_ip;
data_array.username = data.username;
data_array.app_name = data.app_name;
data_array.onair_status = data.onair_status;
data_array.http_referrer = data.http_referrer;
data_array.current_page = data.current_page;
data_array.php_session_id = data.php_session_id;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
postMessage({action: 'PlayerChannelJoined' });
playing = true;
}
function start_log_response(data) {
session_info.session_id = data.log_record_id;
session_info_interval();
}
function application_refresh_start(data) {
if (data.app_name == session_info.application_name) { postMessage({action: 'ApplicationRefreshStart' }); }
}
function application_refresh_stop(data) {
if (data.app_name == session_info.application_name) { postMessage({action: 'ApplicationRefreshStop' }); }
}
function set_static_session_info(data) {
session_info.browser_codename = data.browser_codename;
session_info.browser_name = data.browser_name;
session_info.user_agent = data.user_agent;
session_info.browser_version = data.browser_version;
session_info.cookies_enabled = data.cookies_enabled;
session_info.platform = data.platform;
session_info.player_version = data.player_version;
session_info.stream = data.stream;
session_info.connection_protocol = data.protocol;
session_info.delay_target = data.delay_target;
session_info.session_email = data.session_email;
session_info.remote_ip = data.remote_ip;
session_info.php_session_id = data.php_session_id;
session_info.application_name = data.application_name;
session_info.http_referrer = data.http_referrer;
}
function start_log() {
var session_start_date = new Date();
session_start_time = session_start_date.getTime();
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var session_start_date_string = session_start_date.getDate() + " " + months[session_start_date.getMonth()] + " " + session_start_date.getFullYear();
var data_array = {};
data_array.type = 'webrtc_start_log';
data_array.remote_ip = session_info.remote_ip;
data_array.username = session_info.session_email;
data_array.php_session_id = session_info.php_session_id;
data_array.webrtc_protocol = '';
data_array.session_start_time = (session_start_time/1000).toFixed(0);
data_array.stream_name = session_info.stream;
data_array.session_start_date = session_start_date_string;
var msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
function update_session_info(data) {
var data_array = {};
var msg_string = '';
var current_date = new Date();
var current_time = current_date.getTime();
session_info.protocol = data.protocol;
session_info.curr_delay = (data.effective_latency / 1000).toFixed(2);
session_info.session_length = current_time - session_start_time;
session_info.ping_time = data.ping_time;
session_info.jitter = data.jitter;
session_info.packet_loss = data.packet_loss;
session_info.effective_latency = data.effective_latency;
session_info.mean_opinion_score = data.mean_opinion_score;
session_info.restarts = data.restarts;
if (socket_emits == 0) {
data_array = {};
data_array.type = 'webrtc_log_data_full';
data_array.connection_protocol = session_info.protocol;
data_array.threshold = 0;
data_array.curr_delay = session_info.curr_delay;
data_array.session_length = session_info.session_length;
data_array.date = current_time;
data_array.browser_codename = session_info.browser_codename;
data_array.browser_name = session_info.browser_name;
data_array.user_agent = session_info.user_agent;
data_array.browser_version = session_info.browser_version;
data_array.cookies_enabled = session_info.cookies_enabled;
data_array.platform = session_info.platform;
data_array.player_version = session_info.player_version;
data_array.stream = session_info.stream;
data_array.restarts = session_info.restarts;
data_array.delay_target = 0;
data_array.op_sys = '';
data_array.flash_version = '';
data_array.remote_ip = session_info.remote_ip;
data_array.http_referrer = session_info.http_referrer;
data_array.php_session_id = session_info.php_session_id;
data_array.user_email = session_info.session_email;
data_array.webrtc_protocol = session_info.protocol;
data_array.ping_time = session_info.ping_time;
data_array.jitter = session_info.jitter;
data_array.packet_loss = session_info.packet_loss;
data_array.effective_latency = session_info.effective_latency;
data_array.mean_opinion_score = session_info.mean_opinion_score;
data_array.session_id = session_info.session_id;
msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
else {
data_array = {};
data_array.type = 'webrtc_log_data';
data_array.session_id = session_info.session_id;
data_array.php_session_id = session_info.php_session_id;
data_array.connection_protocol = session_info.protocol;
data_array.restarts = session_info.restarts;
data_array.threshold = 0;
data_array.curr_delay = session_info.curr_delay;
data_array.session_length = session_info.session_length;
data_array.date = current_time;
data_array.webrtc_protocol = session_info.protocol;
data_array.ping_time = session_info.ping_time;
data_array.jitter = session_info.jitter;
data_array.packet_loss = session_info.packet_loss;
data_array.effective_latency = session_info.effective_latency;
data_array.mean_opinion_score = session_info.mean_opinion_score;
data_array.stream = session_info.stream;
msg_string = JSON.stringify(data_array);
if (socket.readyState == 1) { socket.send(msg_string); }
}
socket_emits++;
if (socket_emits == 100) { socket_emits = 0; }
}
- MPC expected to raise rates by 25bp in 7-2 vote
- Rising inflation fuels fears of second-round effects
- Updated MPR to signal higher inflation, stronger growth
- Bailey likely to frame hike as precautionary
- Rate announcement due at midday, Thursday
- Press conference set for 12:30 GMT
By Harry Daniels
LiveSquawk News
@HarryDaniels71
LONDON – With the cost-of-living crisis worsening for many Britons, it is perhaps no surprise that the Bank of England is expected to tighten monetary policy when it announces its decision at midday on Thursday.
The Bank’s Monetary Policy Committee (MPC) is predicted to raise the Bank Rate by 25 basis points to 4.00%. Economists forecast a 7-2 vote in favour of the move, with Alan Taylor and Swati Dhingra expected to vote to leave rates unchanged.
UBS’s Anna Titareva highlighted the possibility of an 8-1 vote, with Dhingra potentially joining the majority in backing a hike.
“At its September meeting, the BoE left the Bank Rate unchanged but sent a clear hawkish signal, which we interpreted as pointing to policy tightening in the absence of a substantial decline in energy prices.”
Economic data since the MPC last met on 17 September are expected to have pushed policymakers beyond their tolerance threshold. Hawks on the committee have long warned of the risk of second-round inflationary effects stemming from elevated energy costs.
Headline inflation has accelerated beyond even the most hawkish forecasts. Annual CPI rose to 3.5% in September from 3.1% in August and 2.6% earlier in the summer.
Core CPI, which excludes volatile food and energy prices, increased to 2.8% y/y in September, matching expectations but accelerating from 2.6% the previous month.
While underlying inflation pressures have yet to reach particularly alarming levels, the direction of travel is unlikely to provide much comfort to policymakers.
Ruth Gregory, deputy chief UK economist at Capital Economics, noted tentative signs that higher energy costs were beginning to feed through to non-energy inflation.
“Taken together with a likely 13% rise in utility prices in January, we expect these energy cost pass-through effects to lift CPI inflation to a peak of about 4.2% next year,” she said.
Thursday’s decision will be accompanied by the quarterly Monetary Policy Report (MPR), which includes updated economic projections.
Deutsche Bank’s chief UK economist, Sanjay Raja, noted that on almost every measure, the UK economy entered the final quarter of the year on a firmer footing than the MPC had anticipated in July.
“In short, UK GDP is running higher than what the MPC projected back in the July MPR. The labour market looks to be on stronger footing, with unemployment down and HMRC payrolls revised up materially. And finally, inflation is running hotter than expected, as higher energy prices have pushed price momentum well above the Bank’s central expectations.”
Analysts expect the updated projections to show stronger near-term growth and higher inflation, but a weaker economic outlook over the medium term.
As is customary following an MPR-linked decision, Governor Andrew Bailey and colleagues will face questions from reporters 30 minutes after the rate announcement.
Markets currently price around a 30% probability of another quarter-point hike at the year's final policy meeting on 17 December, with the odds rising to roughly 80% by the February MPR.
Central bank watchers broadly expect Bailey to frame Thursday's anticipated move as a precautionary hike rather than the beginning of a prolonged tightening cycle, while avoiding any firm commitment on future policy.
Deutsche Bank's Raja maintained his call for one further insurance hike in the new year, taking Bank Rate to a peak of 4.25%.
“In our view, this is still a risk-management exercise with the MPC taking out some insurance against second-round effects. But the risks are shifting. The bigger risk, in our view, isn't a 2022-style hiking cycle but a more drawn-out hiking cycle with either a slightly higher peak or Bank Rate left unchanged for longer.”
The reluctance among economists to characterise the expected move as the start of a new tightening cycle reflects recent comments from senior Bank officials.
UBS's Titareva pointed to remarks from Chief Economist Huw Pill, who expressed scepticism over market pricing of four rate increases, and Bailey's observation that broader inflation pressures had so far remained subdued. Both comments suggested policymakers were pushing back against aggressive market expectations.
“We think that, at 4.25%, the Bank Rate would be around 75-100bp above the neutral level. Against this backdrop, we expect the MPC to start cutting rates in Q4-27 and proceed at a quarterly pace, eventually bringing Bank Rate back to 3.25% by Q3-28.”