0bps

Strategy vault / Arbitrage

Arbitrage Chart

JavaScript strategy by ChaoZhang.

JavaScriptArbitrageSMA
Author
ChaoZhang
Complexity
Intermediate · 162 lines
Parameters
4

The original spec has no written description. Read the source below.

Parameters

NameDefaultWhat it controls
SYMBOL1trading pair1
SYMBOL2trading pair2
BASESYBMOLReference Trading Pair
PERIODtimeframe

Source JavaScript

// thischartAt/InJavaScriptIn the language it is an object, when usingChartBefore the function, we need to declare an object variable to configure the chartchart
let PERIODARR = [PERIOD_M1, PERIOD_M3, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H2, PERIOD_H4, PERIOD_H6, PERIOD_H12, PERIOD_D1, PERIOD_D3, PERIOD_W1];

/**
 * Calculate the simple moving average (SMA)
 * @param {Array<number>} data - Input data array
 * @param {number} period - Period of moving average
 * @returns {Array<number>} - An array containing SMA values
 */
function calculateSMA(data, period) {
    let sma = [];
    let sum = 0;

    for (let i = 0; i < data.length; i++) {
        sum += data[i];

        if (i >= period - 1) {
            sma.push(sum / period);
            sum -= data[i - period + 1];
        } else {
            sma.push(null); // If there are not enough data points to calculateSMA,Returnnull
        }
    }

    // Fillnullvalue
    let firstNonNull = sma.find(value => value !== null);
    for (let i = 0; i < sma.length; i++) {
        if (sma[i] === null) {
            sma[i] = firstNonNull;
        }
    }

    return sma;
}

/**
 * Calculate Standard Deviation (Standard Deviation)
 * @param {Array<number>} data - Input data array
 * @param {number} period - Period for standard deviation calculation
 * @returns {Array<number>} - An array containing standard deviation values
 */
function calculateStdev(data, period) {
    let stdev = [];

    for (let i = 0; i < data.length; i++) {
        if (i >= period - 1) {
            let subset = data.slice(i - period + 1, i + 1);
            let mean = subset.reduce((a, b) => a + b, 0) / period;
            let variance = subset.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / period;
            stdev.push(Math.sqrt(variance));
        } else {
            stdev.push(null); // If there are not enough data points to calculate standard deviation, returnnull
        }
    }

    // Fillnullvalue
    let firstNonNull = stdev.find(value => value !== null);
    for (let i = 0; i < stdev.length; i++) {
        if (stdev[i] === null) {
            stdev[i] = firstNonNull;
        }
    }

    return stdev;
}

function main() {
    exchange.SetContractType("swap");
    // exchange.SetMaxBarLen(200);
    let chart = {
        // This field marks whether the chart is a general chart; those interested can modify itfalseRun and see
        __isStock: false, // Zoom tool
        tooltip: {
            xDateFormat: "%Y-%m-%d %H:%M:%S, %A",
            shared: true,
            crosshairs: true, // Enable Crosshair
            formatter: function () {
                let s = "<b>" + Highcharts.dateFormat("%Y-%m-%d %H:%M:%S, %A", this.x) + "</b>";

                this.points.forEach(function (point) {
                    s += "<br/>" + point.series.name + ": " + point.y.toFixed(2);
                });

                return s;
            }
        }, // Title
        title: { text: "Spread analysis chart
        rangeSelector: {
            buttons: [
                { type: "hour", count: 1, text: "1h" },
                { type: "hour", count: 3, text: "3h" },
                {
                    type: "hour",
                    count: 8,
                    text: "8h"
                },
                { type: "all", text: "All" }
            ],
            selected: 0,
            inputEnabled: false
        }, // The horizontal axis of the coordinate axis is: x-axis. The current setting type is: time
        xAxis: {
            type: "datetime",
            crosshair: {
                color: "#cccccc", // Crosshair Color
                dashStyle: "shortdash", // Dashed line style
                width: 1, // Line width
                zIndex: 5 // Ensure the line displays above other elements
            }
        }, // The vertical axis of the coordinate axis is: y-axis. The default value is adjusted according to the data size.
        yAxis: {
            // Title
            title: { text: "Spread
            opposite: false
        }, // Data series, this property stores the various data series (lines, candlestick charts, labels, etc.)....)
        series: [
            // indexed as0,dataThe array stores the data of this index series
            //Specify Color Asblue
            { name: "spread", id: "spread", data: [], color: "blue" }, // Index is 1, dashStyle:'shortdash' is set, meaning: set to dashed line.
            { name: "basis", id: "basis", data: [], color: "orange" },
            {
                name: "upper",
                id: "upper",
                data: [],
                color: "red"
            },
            { name: "lower", id: "lower", data: [], color: "green" }
        ]
    };
    let symbol1Trades = exchange.GetRecords(SYMBOL1 , PERIODARR[PERIOD]);
    let symbol2Trades = exchange.GetRecords(SYMBOL2 , PERIODARR[PERIOD]);
    let baseTrades = exchange.GetRecords(BASESYBMOL , PERIODARR[PERIOD]);
    Log(symbol1Trades.length, symbol2Trades.length, baseTrades.length);
    Log(symbol1Trades[0], symbol2Trades[0], baseTrades[0]);
    //Take the smallest length as the number of cycles
    let len = Math.min(symbol1Trades.length, symbol2Trades.length, baseTrades.length);
    let spreadRecords = [];
    //Loop traversal
    for (let i = 0; i < len; i++) {
        //Get the price of each trading pair
        let symbol1Price = symbol1Trades[i].Close;
        let symbol2Price = symbol2Trades[i].Close;
        let basePrice = baseTrades[i].Close;
        //Calculate the price difference
        let diff1 = symbol1Price / basePrice;
        let diff2 = symbol2Price / basePrice;
        let spread = diff1 / diff2;
        spreadRecords.push(spread);
        let basis = calculateSMA(spreadRecords, 20);
        let calculateStdevs = calculateStdev(spreadRecords, 20);
        let dev = 2 * calculateStdevs[i];
        let upper = basis[i] + dev;
        let lower = basis[i] - dev;
        //Add spread data to the chart
        chart.series[0].data.push([symbol1Trades[i].Time, spread]);
        chart.series[1].data.push([symbol1Trades[i].Time, basis[i]]);
        chart.series[2].data.push([symbol1Trades[i].Time, upper]);
        chart.series[3].data.push([symbol1Trades[i].Time, lower]);
    }
    return chart;
}

Published by ChaoZhang on the FMZ strategy square · last modified 2024-07-11. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a spread arbitrage engine with WebSocket order routing and leg-risk controls from strategies/Arbitrage-Chart.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open