In questo listato var CONFIG = {
ORDER_SIDE: 'BID',
SIZE: 1,
MAX_OPERATIONS: 3,
LEVELS: { BID: 6, ASK: 6 },
SECONDS: 5,
TICK: 1,
MONITORING_MODE: 'MONITORA_yourlevel',
SIDES: ['BID', 'ASK'],
TYPES: ['Price', 'Quantity']
};

var LogManager = (function() {
function MessageFormatter() {
this.separator = "---------------------------------------------------\r\n";
this.messages = {
'Initialization Complete': "Inizializzazione completata:\nBid Price 0: {bidPrice}\nAsk Price 0: {askPrice}\n",
'Order Action': 'Ordine {action}: {orderCode}\n',
'Order Deleted': 'Ordine cancellato: {orderCode}\n',
'LevelChange': "Livello da {lastLevel} all'attuale livello {priceLevel}\n",
'PriceChange': "Prezzo da {previousPrice} all'attuale prezzo {currentPrice}\nOra: {currentTime}\r\n" + this.separator,
'Operation Number': "Numero Operazione: {numOperazione}\r\n",
'message': "{message}\r\n" + this.separator
};
}


MessageFormatter.prototype.format = function(template, data) {
    return template.replace(/{(\w+)}/g, function(match, key) {
        return data[key] !== undefined ? data[key] : match;
    });
};

MessageFormatter.prototype.formatState = function() {
    var book = UserContext.book;
    var status = UserContext.status;
    var prices = UserContext.prices;
    var sep = this.separator;
    return sep +
        "Bid Price \tBid Quant.\tAsk Price \tAsk Quant.\r\n" +
        sep +
        book.BID.Prices[0] + "\t\t" + book.BID.Quantities[0] +
        "\t\t" + prices.ASK.current + "\t\t" + prices.ASK.lastQuantity + "\r\n" +
        sep +
        this.format(this.messages['Operation Number'], { numOperazione: status.numOperazione }) +
        sep;
};

function Logger(formatter) {
    this.formatter = formatter;
}

Logger.prototype.log = function(type, data, includeFormattedState) {
    if (!this.formatter.messages[type]) return;
    var message = this.formatter.separator + this.formatter.format(this.formatter.messages[type], data || {});
    if (includeFormattedState !== false && (type === 'Initialization Complete' || type === 'LevelChange')) {
        message += this.formatter.formatState();
    }
    Log(message);
};

Logger.prototype.createSideMessage = function(side) {
    var book = UserContext.book;
    var maxLevel = CONFIG.LEVELS[side];
    var sideMessage = side + ":\n";
    var pricesLength = book[side].Prices.length;
    for (var level = 0; level < maxLevel && pricesLength > level; level++) {
        sideMessage += "Level " + level + ": Price = " + book[side].Prices[level] + ", Quantity = " + book[side].Quantities[level] + "\n";
    }
    return sideMessage;
};

Logger.prototype.logFullBook = function() {
    var message = [this.createSideMessage('BID'), this.formatter.separator, this.createSideMessage('ASK')];
    Log(message.join(''));
};

Logger.prototype.initializeOrderAndLog = function(OrderUtils, BookUtils) {
    var initialPrice = BookUtils.getData(UserContext.book, CONFIG.ORDER_SIDE, getTargetPriceLevel(CONFIG.ORDER_SIDE, CONFIG), 'Price');
    var initialOrder = OrderUtils.processOrModOrder(CONFIG.ORDER_SIDE, stock, initialPrice);
    this.log('Initialization Complete', {
        bidPrice: UserContext.prices.BID.current,
        askPrice: UserContext.prices.ASK.current
    }, true);
    this.log('Order Action', { action: 'inserito', orderCode: initialOrder.OrderCode });
    this.logFullBook();
    return initialOrder;
};

Logger.prototype.logPriceChange = function(ctx, price) {
    this.log('PriceChange', {
        previousPrice: ctx.lastPrice,
        currentPrice: price,
        currentTime: Now().ToString("HH\:mm\:ss.ff")
    });
    ctx.lastPrice = price;
};

Logger.prototype.logTickCalculation = function(tick) {
    this.log('message', { message: 'Tick calcolato: ' + tick }, false);
};

var messageFormatter = new MessageFormatter();
var logger = new Logger(messageFormatter);

return {
    getLogger: function() {
        return logger;
    }
};
})();

var logger = LogManager.getLogger();

function calculateTick(bidPrices, askPrices) {
var MINIMUM_TICK = 0.005;
if (!bidPrices || !askPrices || bidPrices.length < 2 || askPrices.length < 2) {
logger.logTickCalculation('0');
return 0;
}
var minDiff = Infinity;
var len = Math.min(bidPrices.length, askPrices.length);
for (var i = 0; i < len - 1; i++) {
var bidDiff = Math.abs(bidPrices[i + 1] - bidPrices[i]);
var askDiff = Math.abs(askPrices[i + 1] - askPrices[i]);
if (bidDiff < minDiff && bidDiff >= MINIMUM_TICK) minDiff = bidDiff;
if (askDiff < minDiff && askDiff >= MINIMUM_TICK) minDiff = askDiff;


if (minDiff === MINIMUM_TICK) break;
}
if (minDiff === Infinity) {
    logger.logTickCalculation('0');
    return 0;
}
var tick = Math.round(minDiff * 1000) / 1000;
logger.logTickCalculation(tick.toString());
return tick;
}

function extractSideData(bookSide, levels) {
return {
P: bookSide.Prices.slice(0, levels),
Q: bookSide.Quantities.slice(0, levels),
current: bookSide.Prices[0],
lastQuantity: bookSide.Quantities[0]
};
}

var BookUtils = (function() {
function mapValues(sideData, maxLevel, valueFunc) {
var result = [];
for (var i = 0; i <= maxLevel; i++) {
result.push(valueFunc(sideData, i));
}
return result;
}


function mapBookData(originalBook) {
    function getPrice(sideData, level){
        return sideData.Price(level);
    }

    function getQuantity(sideData, level){
        return sideData.Quantity(level);
    }

    return {
        BID: {
            Prices: mapValues(originalBook.Bid, CONFIG.LEVELS.BID, getPrice),
            Quantities: mapValues(originalBook.Bid, CONFIG.LEVELS.BID, getQuantity)
        },
        ASK: {
            Prices: mapValues(originalBook.Ask, CONFIG.LEVELS.ASK, getPrice),
            Quantities: mapValues(originalBook.Ask, CONFIG.LEVELS.ASK, getQuantity)
        }
    };
}

function arraysEqual(arr1, arr2) {
    if (arr1.length !== arr2.length) return false;
    for(var i = 0; i < arr1.length; i++){
        if(arr1[i] !== arr2[i]) return false;
    }
    return true;
}

var BookDataManager = {
    getData: function(book, side, level, type) {
        if (book[side] && level >= 0 && book[side].Prices.length > level) {
            var typeKey = type === 'Price' ? 'Prices' : 'Quantities';
            return book[side][typeKey][level];
        }
        return undefined;
    }
};

function handleSideContext(bookSide, levels, context) {
    var data = extractSideData(bookSide, levels);
    context.P = data.P;
    context.Q = data.Q;
    context.current = data.current;
    context.lastQuantity = data.lastQuantity;
    return context;
}

 function hasSideChanged(oldSide, newSide) {
    return !arraysEqual(oldSide.P, newSide.Prices) || !arraysEqual(oldSide.Q, newSide.Quantities);
}

var BookStateManager = (function(mapBookDataFunc, arraysEqualFunc, handleSideContextFunc, hasSideChangedFunc) {
    function updateStateIfChanged(book) {
        var newBook = mapBookDataFunc(book);
        var changesDetected = false;

        for (var i = 0; i < CONFIG.SIDES.length; i++) {
            var side = CONFIG.SIDES[i];
            if (hasSideChangedFunc(UserContext.prices[side], newBook[side])) {
                changesDetected = true;
                handleSideContextFunc(newBook[side], CONFIG.LEVELS[side], UserContext.prices[side]);
            }
        }
        UserContext.book = newBook;
        return changesDetected;
    }

    return {
        updateStateIfChanged: updateStateIfChanged,
        getBookState: function() {
            return UserContext.prices;
        }
    };
})(mapBookData, arraysEqual, handleSideContext, hasSideChanged);

return {
    getData: BookDataManager.getData,
    getBookState: BookStateManager.getBookState,
    mapBookData: mapBookData,
    hasChanges: BookStateManager.updateStateIfChanged,
    handleSideContext: handleSideContext
};
})();

function getTargetPriceLevel(side, config) {
return config.MONITORING_MODE === 'MONITORA_1Level' ? 0 : config.LEVELS[side] - 1;
}

// Funzione generica per gestire lo stato degli ordini
function updateOrder(action, order, side, price) {
switch (action) {
case 'delete':
UserContext.order.current = null;
UserContext.prices[side].oldLevel = undefined;
break;
case 'update':
if (!order || order.Price !== price) {
UserContext.order.current = order;
UserContext.prices[side].oldLevel = price;
}
break;
}
return UserContext.order.current;
}

var OrderContextManager = (function(logger) {


function cancelCurrentOrder(status, OrderUtils) {
    var currentOrder = UserContext.order.current;
    if (!currentOrder) return false;
    OrderUtils.deleteOrder(currentOrder.OrderCode);
    // Usa updateOrder per gestire lo stato
    updateOrder('delete', null, CONFIG.ORDER_SIDE, null);
    logger.log('Order Deleted', { orderCode: currentOrder.OrderCode });
    status.run = false;
    return true;
}

function processOrder(side, stock, price, OrderUtils) {
  // Usa updateOrder per gestire lo stato
  var newOrder = UserContext.order.current
      ? OrderManager.Modify(UserContext.order.current.OrderCode, price)
      : OrderManager.Buy(stock, price, CONFIG.SIZE);

  return updateOrder('update', newOrder, side, price);
}

return {
    cancelCurrentOrder: cancelCurrentOrder,
    processOrder: processOrder
};
})(logger);

var OrderUtils = (function() {
function execOrder(side, stock, price, size) {
return (side === 'BID') ? OrderManager.Buy(stock, price, size) : OrderManager.Sell(stock, price, size);
}


function processOrModOrder(side, stock, price) {
    var newOrder = UserContext.order.current
        ? OrderManager.Modify(UserContext.order.current.OrderCode, price)
        : execOrder(side, stock, price, CONFIG.SIZE);
    return updateOrder('update', newOrder, side, price);
}

function deleteOrder(orderCode) {
    return OrderManager.Delete(orderCode);
}

return {
    execOrder: execOrder,
    processOrModOrder: processOrModOrder,
    deleteOrder: deleteOrder
};
})();

var PriceHelper = {
priceDirection: function(targetPrice, oldPrice) {
return targetPrice > oldPrice ? 1 : targetPrice < oldPrice ? -1 : 0;
}
};

function initializeSideContext(bookSide, levels) {
return BookUtils.handleSideContext(bookSide, levels, {
current: undefined,
oldLevel: undefined,
lastQuantity: undefined,
P: undefined,
Q: undefined
});
}

function initializeUserContext(book, sideLevels) {
return {
book: book,
order: { current: null },
status: { run: true, secondCount: 0, numOperazione: 0 },
prices: {
BID: initializeSideContext(book.BID, sideLevels.BID),
ASK: initializeSideContext(book.ASK, sideLevels.ASK)
},
targetPriceLevels: sideLevels
};
}

function setupTradeContext(OrderUtils, BookUtils, calculateTick, logger, manageOrderCancellation, getTargetPriceLevelFunc, ps, config) {
var originalBook = BookUtils.mapBookData(Book(ps));
var sideLevels = {
BID: getTargetPriceLevelFunc('BID', config),
ASK: getTargetPriceLevelFunc('ASK', config)
};
UserContext = initializeUserContext(originalBook, sideLevels);
calculateTick(originalBook.BID.Prices, originalBook.ASK.Prices);
logger.initializeOrderAndLog(OrderUtils, BookUtils);
handleCycle(UserContext.status, manageOrderCancellation, BookUtils);
}

function handleCycle(status, manageOrderCancellation, BookUtils) {
if (++status.secondCount >= CONFIG.SECONDS) {
manageOrderCancellation();
if (status.run) {
UserContext.book = BookUtils.mapBookData(Book(ps));
updatePrice(CONFIG.ORDER_SIDE);
}
status.secondCount = 0;
}
}

function updatePrice(side) {
var userCtx = UserContext;
var book = userCtx.book;
var status = userCtx.status;
var targetPriceLevel = getTargetPriceLevel(side, CONFIG);
var targetPrice = book[side].Prices[targetPriceLevel];
var oldPrice = userCtx.prices[side].oldLevel;


if (targetPrice !== oldPrice && OrderContextManager.processOrder(side, ps, targetPrice, OrderUtils)) {
    logger.log('LevelChange', {
        lastLevel: getTargetPriceLevel(side, CONFIG) + PriceHelper.priceDirection(targetPrice, oldPrice),
        priceLevel: targetPriceLevel
    }, true);
    status.secondCount = 0;
    status.numOperazione++;
}
}

function manageOrderCancellation() {
if (UserContext.status.numOperazione >= CONFIG.MAX_OPERATIONS) {
OrderContextManager.cancelCurrentOrder(UserContext.status, OrderUtils);
}
}

function eventPrice(ctx, price) {
if (ctx.lastPrice === price) return;
logger.logPriceChange(ctx, price);
}

function eventTime(ctx, manageOrderCancellation, BookUtils) {
handleCycle(ctx.status, manageOrderCancellation, BookUtils)
}

function handleEvent(context, type, eventPrice, eventTime, manageOrderCancellation, BookUtils) {
if (type === TSEventType.Price) {
var price = Val(ps, "LAST");
eventPrice(context, price);
} else if (type === TSEventType.Time) {
eventTime(context, manageOrderCancellation, BookUtils);
}
}

function marketEventFactory(ps, bookProvider, bookMapper, eventHandler, userContext, systemContext, handleEvent, BookUtils) {
return function() {
if (userContext.status.run) {
BookUtils.hasChanges(Book(ps));
eventHandler(userContext, systemContext.EventType, eventPrice, eventTime, manageOrderCancellation, BookUtils);
}
};
}

if (SystemContext.IsFirst) {
setupTradeContext(OrderUtils, BookUtils, calculateTick, logger, manageOrderCancellation, getTargetPriceLevel, ps, CONFIG);
}

var marketEventInstance = marketEventFactory(ps, Book, BookUtils.mapBookData, handleEvent, UserContext, SystemContext, handleEvent, BookUtils);

marketEventInstance();   ci sono duplicazioni nella gestione degli stati legati agli ordini
Ci sono diverse funzioni che ripetono un pattern simile per aggiornare lo stato dell'ordine. Ad esempio:

function updateOrder(action, order, side, price) {
switch (action) {
case 'delete':
UserContext.order.current = null;
UserContext.prices[side].oldLevel = undefined;
break;
case 'update':
if (!order || order.Price !== price) {
UserContext.order.current = order;
UserContext.prices[side].oldLevel = price;
}
break;
}
return UserContext.order.current;
}
Questa funzione è chiamata sia in:

OrderContextManager.cancelCurrentOrder
OrderContextManager.processOrder
OrderUtils.processOrModOrder
Ogni volta, la logica per aggiornare lo stato dell'ordine si sovrappone. Ad esempio:

In OrderContextManager.cancelCurrentOrder:

updateOrder('delete', null, CONFIG.ORDER_SIDE, null);
In OrderUtils.processOrModOrder:

return updateOrder('update', newOrder, side, price);
Questa ripetizione è ridondante e potrebbe essere centralizzata ulteriormente per evitare di passare manualmente i parametri su ogni chiamata.

Soluzione proposta: un' astrazione ulteriore per standardizzare la gestione dello stato dell'ordine:

function manageOrderState(action, params = {}) {
let { order, side, price } = params;


if (action === 'delete') {
    UserContext.order.current = null;
    UserContext.prices[side].oldLevel = undefined;
} else if (action === 'update') {
    if (!order || order.Price !== price) {
        UserContext.order.current = order;
        UserContext.prices[side].oldLevel = price;
    }
}
return UserContext.order.current;
}
In questo modo, le funzioni chiamanti possono semplicemente delegare la gestione dello stato senza preoccuparsi di passare parametri espliciti ogni volta:

OrderContextManager.cancelCurrentOrder = function(status, OrderUtils) {
if (!UserContext.order.current) return false;


OrderUtils.deleteOrder(UserContext.order.current.OrderCode);
manageOrderState('delete', { side: CONFIG.ORDER_SIDE });
logger.log('Order Deleted', { orderCode: UserContext.order.current.OrderCode });
status.run = false;
return true;
};  Riscrivi il codice completo corretto in ES5