In questo listato var CONFIG = {
    ORDER_SIDE: 'BID',
    SIZE: 1,
    MAX_OPERATIONS: 8,
    LEVELS: { BID: 6, ASK: 6 },
    SECONDS: 5,
    MONITORING_MODE: 'MONITORA_yourlevel'
};

var separator = "---------------------------------------------------\r\n";
var cachedBookState = null;
var lastBidPrice = null, lastBidQuantity = null, lastAskPrice = null, lastAskQuantity = null;

function isValidSideData(sideData) {
    return typeof sideData.Price === 'function' && typeof sideData.Quantity === 'function';
}

function mapSideData(side, sideData, maxLevel) {
    var prices = {}, quantities = {};
    var isValid = isValidSideData(sideData); // Controllo eseguito una sola volta
    for (var level = maxLevel; level >= 0; level--) {
        prices[level] = isValid ? sideData.Price(level) : 0;
        quantities[level] = isValid ? sideData.Quantity(level) : 0;
    }
    return { Prices: prices, Quantities: quantities };
}

function mapBookData(originalBook) {
    return {
        BID: mapSideData('BID', originalBook.Bid, CONFIG.LEVELS.BID),
        ASK: mapSideData('ASK', originalBook.Ask, CONFIG.LEVELS.ASK)
    };
}

function addLevels(logParts, side, book) {
    var maxLevel = CONFIG.LEVELS[side];
    var prices = book[side].Prices;
    var quantities = book[side].Quantities;
    for (var level = 0; level <= maxLevel; level++) {
        logParts.push("Level " + level + ": Price = " + prices[level] + 
                      ", Quantity = " + quantities[level] + "\n");
    }
}

function logFullBook() {
    var book = UserContext.book, logParts = [separator, "BID:\n"];
    addLevels(logParts, 'BID', book);
    logParts.push(separator, "ASK:\n");
    addLevels(logParts, 'ASK', book);
    logParts.push(separator);
    Log(logParts.join(''));
}

var BookUtils = {
    getData: function(book, side, level, type) {
        return (book && book[side] && book[side][type + 's']) ? book[side][type + 's'][level] : null;
    },
    hasBookStateChanged: function(newState) {
        return cachedBookState === null ||
               cachedBookState.book !== newState.book ||
               lastBidPrice !== newState.bidPrice ||
               lastBidQuantity !== newState.bidQuantity ||
               lastAskPrice !== newState.askPrice ||
               lastAskQuantity !== newState.askQuantity;
    },
    getBookState: function(book) {
        var newState = {
            book: book,
            bidPrice: this.getData(book, 'BID', CONFIG.LEVELS.BID, 'Price'),
            bidQuantity: this.getData(book, 'BID', CONFIG.LEVELS.BID, 'Quantity'),
            askPrice: this.getData(book, 'ASK', CONFIG.LEVELS.ASK, 'Price'),
            askQuantity: this.getData(book, 'ASK', CONFIG.LEVELS.ASK, 'Quantity')
        };
        if (this.hasBookStateChanged(newState)) {
            cachedBookState = newState;
            lastBidPrice = newState.bidPrice;
            lastBidQuantity = newState.bidQuantity;
            lastAskPrice = newState.askPrice;
            lastAskQuantity = newState.askQuantity;
        }
        return cachedBookState;
    }
};

var OrderUtils = {
    execOrder: function(side, stock, price, size) {
        return (side === 'BID') ? OrderManager.Buy(stock, price, size) : OrderManager.Sell(stock, price, size);
    },
    updateOrderContext: function(order, side, price) {
        UserContext.order.current = order;
        UserContext.prices[side].oldLevel = price;
        return order;
    },
    processOrModOrder: function(side, stock, price) {
        var currentOrder = UserContext.order.current;
        if (currentOrder) {
            if (currentOrder.Price === price) return currentOrder;
            return this.updateOrderContext(OrderManager.Modify(currentOrder.OrderCode, price), side, price);
        }
        return this.updateOrderContext(this.execOrder(side, stock, price, CONFIG.SIZE), side, price);
    },
    deleteOrder: function(orderCode) {
        return OrderManager.Delete(orderCode);
    }
};

var Helper = {
    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" + separator,
        'message': "{message}\r\n" + separator
    },
    formatMessage: function(template, data) {
        return template.replace(/\{(\w+)\}/g, function(match, key) {
            return data[key] || match;
        });
    },
    formatState: function(bookState) {
        var book = UserContext.book;
        return separator + 
               "Bid Price \tBid Quant.\tAsk Price \tAsk Quant.\r\n" + 
               separator + 
               book.BID.Prices[0] + "\t\t" + book.BID.Quantities[0] + 
               "\t\t" + book.ASK.Prices[0] + "\t\t" + book.ASK.Quantities[0] + "\r\n" + 
               separator + 
               "Numero Operazione: " + UserContext.status.numOperazione + "\r\n" + 
               separator;
    },
    log: function(type, data, includeFormattedState) {
        includeFormattedState = typeof includeFormattedState !== 'undefined' ? includeFormattedState : true;
        var template = this.messages[type];
        if (!template) return;
        var message = separator + this.formatMessage(template, data || {});
        if (includeFormattedState && (type === 'Initialization Complete' || type === 'LevelChange')) {
            message += this.formatState(BookUtils.getBookState(UserContext.book));
        }
        Log(message);
    },

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

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

function setupTradeContext() {
    var originalBook = mapBookData(Book(ps));  
    var sideLevels = {
        BID: (CONFIG.MONITORING_MODE === 'MONITORA_1Level' ? 0 : CONFIG.LEVELS.BID - 1),
        ASK: (CONFIG.MONITORING_MODE === 'MONITORA_1Level' ? 0 : CONFIG.LEVELS.ASK - 1)
    };
    var pricesBID = originalBook.BID.Prices;
    var pricesASK = originalBook.ASK.Prices;
    
    UserContext = {
        book: originalBook,
        order: { current: null },
        status: { run: true, secondCount: 0, numOperazione: 0 },
        prices: {
            BID: {
                current: pricesBID[0],
                oldLevel: pricesBID[sideLevels.BID]
            },
            ASK: {
                current: pricesASK[0],
                oldLevel: pricesASK[sideLevels.ASK]
            }
        },
        targetPriceLevels: sideLevels
    };

    var side = CONFIG.ORDER_SIDE;  // Prendiamo il lato configurato (BID o ASK)
    var price = BookUtils.getData(originalBook, side, sideLevels[side], 'Price');  // Prezzo target
    var initialOrder = OrderUtils.execOrder(side, stock, price, CONFIG.SIZE);  // Eseguiamo l'ordine

    OrderUtils.updateOrderContext(initialOrder, side, price);

    Helper.log('Initialization Complete', {
        bidPrice: UserContext.prices.BID.current,
        askPrice: UserContext.prices.ASK.current
    }, true); 
    Helper.log('Order Action', { action: 'inserito', orderCode: initialOrder.OrderCode }, false);
    logFullBook();

    manageCycle();  
}

function manageCycle() {
    var status = UserContext.status;
    if (checkOps() || status.secondCount < CONFIG.SECONDS) {
        return;
    }
    var side = CONFIG.ORDER_SIDE;
    var originalBook = Book(ps);
    var mappedBook = mapBookData(originalBook);
    UserContext.book = mappedBook;

    priceUpdate(side, mappedBook);
    status.secondCount = 0;
}

function priceUpdate(side, book) {
    var status = UserContext.status;
    var pricesSide = UserContext.prices[side];
    var targetPriceLevel = UserContext.targetPriceLevels[side];
    var targetPrice = book[side].Prices[targetPriceLevel];
    var oldPrice = pricesSide.oldLevel;
    var maxLevel = CONFIG.LEVELS[side];
    
    if (targetPrice === oldPrice) return;
    
    var newOrder = OrderUtils.processOrModOrder(side, ps, targetPrice);
    if (!newOrder) return;
    
    pricesSide.current = targetPrice;
    var direction = Helper.priceDirection(targetPrice, oldPrice);
    var levelDifference = maxLevel + direction;
        Helper.log('LevelChange', { lastLevel: levelDifference, priceLevel: targetPriceLevel }, true);
    status.secondCount = 0;
    status.numOperazione++;
}

function checkOps() {
    var status = UserContext.status;
    if (status.numOperazione < CONFIG.MAX_OPERATIONS) {
        return false;
    }
    var currentOrder = UserContext.order.current;
    if (currentOrder) {
        OrderUtils.deleteOrder(currentOrder.OrderCode);
        Helper.log('Order Deleted', { orderCode: currentOrder.OrderCode });
        status.run = false;
        return true;
    }
    return false;
}

function handleEvent(context, eventType) {
    switch(eventType) {
        case TSEventType.Price:
            var currentPrice = Val(ps, "LAST");
            if (context.lastPrice !== currentPrice) {
                var currentTime = Now().ToString("HH:mm:ss.ff");
                Helper.log('PriceChange', { 
                    previousPrice: context.lastPrice, 
                    currentPrice: currentPrice, 
                    currentTime: currentTime 
                });
                context.lastPrice = currentPrice;
            }
            break;
        case TSEventType.Time:
            context.status.secondCount++;
            manageCycle();
            break;
    }
}

function marketEvent() {
    if (!UserContext.status.run) return;
    var originalBook = Book(ps);
    var mappedBook = mapBookData(originalBook);
    UserContext.book = mappedBook;

    handleEvent(UserContext, SystemContext.EventType);
}

if (SystemContext.IsFirst) {
    setupTradeContext();
}

marketEvent();  vorrei che dopo la funzione function addLevels( venisse calcolato lo step che intercorre tra i vari livelli con il nome di "Tick" e venisse effettuato un log dello stesso. Come calcolare il tick? Sicuramente nel caso di book liquido e' sufficiente calcolare la differenza algebrica tra primo e secondo livello. In caso di book illiquido sarebbe invece necessario calcolare la differenza algebrica prevalente, cioe' la differenza che in una distribuzione normale e' la differenza piu' diffusa tra tutte le differenze tra i vari livelli. Quale potrebbe essere una soluzione matematica a questo problema di determinare il tick ?