{
  "name": "Crypto Arbitrage",
  "nodes": [
    {
      "parameters": {
        "content": "## Crypto Arbitrage Scanner v2.0\n**11 Exchanges** | **100+ Pairs** | **3 Quote Assets**\n\nExchanges: Binance, KuCoin, Gate.io, OKX, Bybit, MEXC, Bitget, HTX, Crypto.com, BingX, Poloniex\nQuote assets: USDT, USDC, USD\n\nThreshold: 1.5% after fees | Volume: $100k/24h\nFees: per-exchange taker (0.1–0.2%) | Slippage: 0.1%/side\n\nStage 1: Price scan every 30s → Net profit calc → Volume filter\nStage 2: Order book depth check (native HTTP nodes) → VWAP at $500\nStrategy: Pre-positioned buckets (simultaneous buy+sell, no transfer delay)",
        "height": 240,
        "width": 440,
        "color": 4
      },
      "id": "811f35cc-b078-4022-8db4-a8b9592c9eb2",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [23792, 4976]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict", "version": 2 },
          "combinator": "and",
          "conditions": [{ "id": "profitable", "operator": { "type": "boolean", "operation": "equals" }, "leftValue": "={{ $json.isProfitable }}", "rightValue": true }]
        },
        "options": {}
      },
      "id": "2173751a-0a9b-4b77-856a-017e42380c68",
      "name": "Check Profitable",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [27072, 6128]
    },
    {
      "parameters": {
        "jsCode": "// CRYPTO ARBITRAGE SCANNER v2.0 — Production Grade\n// Per-exchange fees | Slippage | Volume filter | Net profit\n// Simultaneous trade simulation (Pre-positioned bucket method)\n\nconst THRESHOLD = 1.5;\nconst MIN_VOLUME_USD = 100000;\nconst SLIPPAGE = 0.001;\nconst WITHDRAW_FEE = 0.50;\nconst TRADE_SIZE = 500;\n\nconst FEES = {\n  binance: 0.001, okx: 0.001,\n  kucoin: 0.001, bybit: 0.001,\n  bitget: 0.001, gateio: 0.002,\n  mexc: 0.002, htx: 0.002,\n  cryptocom: 0.002, bingx: 0.002,\n  poloniex: 0.002\n};\n\nconst BASE_ASSETS = [\n  'BTC','ETH','SOL','XRP','BNB','ADA','DOGE','MATIC','DOT','AVAX',\n  'LINK','UNI','ATOM','LTC','BCH','FIL','ETC','XLM','ALGO','VET',\n  'NEAR','ICP','HBAR','SAND','MANA','AXS','THETA','EOS','AAVE','MKR',\n  'COMP','SNX','CRV','GRT','1INCH','YFI','SUSHI','BAL','REN','KNC',\n  'ZRX','BAT','ENJ','CHZ','HOT','IOTX','ONE','ANKR','CKB','STMX',\n  'WRX','CELR','BAND','MTL','REEF','TKO','POLS','DODO','ALICE','TLM',\n  'LINA','HARD','BURGER','SFP','XVS','BAKE','CAKE','ALPACA','ALPHA',\n  'RUNE','SXP','WIN','BTTC','BTT','JASMY','ACH','SLP',\n  'GALA','IMX','APE','GMT','STG','OP','ARB','RDNT','PEPE','WLD',\n  'TIA','INJ','PYTH','JUP','STRK','MANTA','PORTAL','PIXEL','MYRO',\n  'BOME','SLERF','WIF','FLOKI','BONK','NEIRO','DOGS','HMSTR','CATI',\n  'MAJOR','BLUM','DYM','SEI','ZETA','TRB','ORBS','BLUR','CFX','SSV',\n  'SUI','TON','APT','NOT','EGLD','FLOW','MINA','ROSE','KSM','ZIL',\n  'DYDX','OCEAN','MAGIC','ENS','STX','ONDO','TAO','POL','ZK','ZRO',\n  'VIRTUAL','TRUMP','MEW','POPCAT','COW','ETHFI','EIGEN','IO','BB',\n  'SAGA','ALT','LAYER','LISTA','TRX','SHIB','FET','RNDR','LDO','PENDLE'\n];\nconst QUOTE_ASSETS = ['USDT','USDC','USD'];\n\nfunction detectAndParse(raw) {\n  const results = {};\n  try {\n    const d = (typeof raw === 'string') ? JSON.parse(raw) : raw;\n    if (!d) return results;\n    if (d.data && d.data.ticker && Array.isArray(d.data.ticker)) {\n      const ex = {};\n      for (const x of d.data.ticker) {\n        const sym = (x.symbol || '').replace('-', '');\n        const p = parseFloat(x.last); const v = parseFloat(x.volValue);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.kucoin = ex;\n    } else if (d.data && Array.isArray(d.data) && d.data.length > 0 && d.data[0].instId !== undefined) {\n      const ex = {};\n      for (const x of d.data) {\n        const sym = (x.instId || '').replace(/-/g, '');\n        const p = parseFloat(x.last); const v = parseFloat(x.volCcy24h);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.okx = ex;\n    } else if (d.result && d.result.list && Array.isArray(d.result.list)) {\n      const ex = {};\n      for (const x of d.result.list) {\n        const p = parseFloat(x.lastPrice); const v = parseFloat(x.turnover24h);\n        if (p > 0 && x.symbol) ex[x.symbol] = { price: p, volume24h: v || 0 };\n      }\n      results.bybit = ex;\n    } else if (d.data && Array.isArray(d.data) && d.data.length > 0 && d.data[0].lastPr !== undefined) {\n      const ex = {};\n      for (const x of d.data) {\n        const sym = (x.symbol || '').replace(/_?SPBL$/, '').replace(/_?UMCBL$/, '');\n        const p = parseFloat(x.lastPr); const v = parseFloat(x.quoteVolume || x.usdtVol || '0');\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.bitget = ex;\n    } else if (d.status === 'ok' && Array.isArray(d.data) && d.data.length > 0 && d.data[0].close !== undefined) {\n      const ex = {};\n      for (const x of d.data) {\n        const sym = (x.symbol || '').toUpperCase();\n        const p = parseFloat(x.close); const v = parseFloat(x.vol);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.htx = ex;\n    } else if (d.result && d.result.data && Array.isArray(d.result.data) && d.result.data.length > 0 && d.result.data[0].i !== undefined) {\n      const ex = {};\n      for (const x of d.result.data) {\n        const sym = (x.i || '').replace('_', '');\n        const p = parseFloat(x.a); const v = parseFloat(x.vv);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.cryptocom = ex;\n    } else if (d.data && Array.isArray(d.data) && d.data.length > 0 && d.data[0].lastPrice !== undefined && d.data[0].symbol && d.data[0].symbol.includes('-')) {\n      const ex = {};\n      for (const x of d.data) {\n        const sym = (x.symbol || '').replace('-', '');\n        const p = parseFloat(x.lastPrice); const v = parseFloat(x.quoteVolume);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.bingx = ex;\n    } else if (Array.isArray(d) && d.length > 0 && d[0].currency_pair !== undefined) {\n      const ex = {};\n      for (const x of d) {\n        const sym = (x.currency_pair || '').replace('_', '');\n        const p = parseFloat(x.last); const v = parseFloat(x.quote_volume);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.gateio = ex;\n    } else if (Array.isArray(d) && d.length > 0 && d[0].close !== undefined && d[0].symbol && d[0].symbol.includes('_')) {\n      const ex = {};\n      for (const x of d) {\n        const sym = (x.symbol || '').replace('_', '');\n        const p = parseFloat(x.close); const v = parseFloat(x.amount);\n        if (p > 0) ex[sym] = { price: p, volume24h: v || 0 };\n      }\n      results.poloniex = ex;\n    } else if (Array.isArray(d) && d.length > 0 && d[0].weightedAvgPrice !== undefined && d[0].lastPrice !== undefined) {\n      const ex = {};\n      for (const x of d) {\n        const p = parseFloat(x.lastPrice); const v = parseFloat(x.quoteVolume);\n        if (p > 0 && x.symbol) ex[x.symbol] = { price: p, volume24h: v || 0 };\n      }\n      results.binance = ex;\n    } else if (Array.isArray(d) && d.length > 0 && d[0].lastPrice !== undefined && d[0].quoteVolume !== undefined) {\n      const ex = {};\n      for (const x of d) {\n        const p = parseFloat(x.lastPrice); const v = parseFloat(x.quoteVolume);\n        if (p > 0 && x.symbol) ex[x.symbol] = { price: p, volume24h: v || 0 };\n      }\n      results.mexc = ex;\n    }\n  } catch (_) {}\n  return results;\n}\n\nfunction calcNetProfit(buyEx, buyPrice, sellEx, sellPrice, tradeSize) {\n  const buyFee = FEES[buyEx] || 0.002;\n  const sellFee = FEES[sellEx] || 0.002;\n  const effectiveBuy = buyPrice * (1 + buyFee + SLIPPAGE);\n  const effectiveSell = sellPrice * (1 - sellFee - SLIPPAGE);\n  const spreadRaw = ((sellPrice - buyPrice) / buyPrice) * 100;\n  const spreadNet = ((effectiveSell - effectiveBuy) / effectiveBuy) * 100;\n  const qty = tradeSize / effectiveBuy;\n  const revenue = qty * effectiveSell;\n  const buyFeeCost = tradeSize * buyFee;\n  const sellFeeCost = revenue * sellFee;\n  const slippageCost = (tradeSize + revenue) * SLIPPAGE;\n  const netProfitUSD = revenue - tradeSize - buyFeeCost - sellFeeCost - slippageCost - WITHDRAW_FEE;\n  return { spreadRaw, spreadNet, netProfitUSD, effectiveBuy, effectiveSell, qty, buyFeeCost, sellFeeCost, slippageCost, roi: (netProfitUSD / tradeSize) * 100, isProfitable: spreadNet >= THRESHOLD && netProfitUSD > 0 };\n}\n\nfunction formatPair(base, quote, exchange) {\n  switch (exchange) {\n    case 'kucoin': return base + '-' + quote;\n    case 'gateio': return base + '_' + quote;\n    case 'okx': return base + '-' + quote;\n    case 'cryptocom': return base + '_' + quote;\n    case 'bingx': return base + '-' + quote;\n    case 'poloniex': return base + '_' + quote;\n    case 'htx': return (base + quote).toLowerCase();\n    default: return base + quote;\n  }\n}\n\nconst priceMap = {};\nfor (const item of $input.all()) {\n  const parsed = detectAndParse(item.json);\n  for (const [exName, exData] of Object.entries(parsed)) {\n    for (const base of BASE_ASSETS) {\n      for (const quote of QUOTE_ASSETS) {\n        const variants = [base + quote, base + '-' + quote, base + '_' + quote];\n        for (const sym of variants) {\n          if (exData[sym]) {\n            const pair = base + '/' + quote;\n            if (!priceMap[pair]) priceMap[pair] = {};\n            if (!priceMap[pair][exName]) priceMap[pair][exName] = exData[sym];\n            break;\n          }\n        }\n      }\n    }\n  }\n}\n\nconst opportunities = [];\nfor (const [pair, exchanges] of Object.entries(priceMap)) {\n  const exList = Object.entries(exchanges);\n  if (exList.length < 2) continue;\n  const [base, quote] = pair.split('/');\n  const liquidEx = exList.filter(function(e) { return e[1].volume24h >= MIN_VOLUME_USD; });\n  if (liquidEx.length < 2) continue;\n  let minPrice = Infinity, maxPrice = -Infinity, buyExchange = '', sellExchange = '';\n  for (const e of liquidEx) {\n    if (e[1].price < minPrice) { minPrice = e[1].price; buyExchange = e[0]; }\n    if (e[1].price > maxPrice) { maxPrice = e[1].price; sellExchange = e[0]; }\n  }\n  if (buyExchange === sellExchange) continue;\n  const calc = calcNetProfit(buyExchange, minPrice, sellExchange, maxPrice, TRADE_SIZE);\n  if (!calc.isProfitable) continue;\n  const minVol = Math.min((exchanges[buyExchange] && exchanges[buyExchange].volume24h) || 0, (exchanges[sellExchange] && exchanges[sellExchange].volume24h) || 0);\n  opportunities.push({\n    pair, baseAsset: base, quoteAsset: quote,\n    spread: parseFloat(calc.spreadRaw.toFixed(4)), spreadNet: parseFloat(calc.spreadNet.toFixed(4)), profit: parseFloat(calc.spreadNet.toFixed(4)),\n    netProfitUSD: parseFloat(calc.netProfitUSD.toFixed(4)), roi: parseFloat(calc.roi.toFixed(4)),\n    buyExchange, sellExchange, buyPrice: minPrice, sellPrice: maxPrice,\n    effectiveBuy: parseFloat(calc.effectiveBuy.toFixed(8)), effectiveSell: parseFloat(calc.effectiveSell.toFixed(8)),\n    buyFeeUSD: parseFloat(calc.buyFeeCost.toFixed(4)), sellFeeUSD: parseFloat(calc.sellFeeCost.toFixed(4)),\n    slippageUSD: parseFloat(calc.slippageCost.toFixed(4)), withdrawFeeUSD: WITHDRAW_FEE,\n    volume24h: parseFloat(minVol.toFixed(0)), isProfitable: true, marketCount: exList.length,\n    buyPair: formatPair(base, quote, buyExchange), sellPair: formatPair(base, quote, sellExchange),\n    exchanges: Object.fromEntries(exList.map(function(e) { return [e[0], e[1].price]; })),\n    simultaneousTrade: { strategy: 'Pre-positioned bucket (simultaneous execution)', tradeSize: TRADE_SIZE, buyQty: parseFloat(calc.qty.toFixed(6)), buyExchange, buyPrice: minPrice, sellExchange, sellPrice: maxPrice, netProfitUSD: parseFloat(calc.netProfitUSD.toFixed(4)), roi: calc.roi.toFixed(3) + '%', note: 'No transfer needed - rebalance wallets when spread closes' },\n    exampleTrade: 'Buy ' + base + ' on ' + buyExchange + ' @ ' + minPrice + ' ' + quote + ' -> Sell on ' + sellExchange + ' @ ' + maxPrice + ' ' + quote + ' | Net: $' + calc.netProfitUSD.toFixed(2) + ' per $' + TRADE_SIZE\n  });\n}\n\nopportunities.sort(function(a, b) { return b.netProfitUSD - a.netProfitUSD; });\nif (opportunities.length === 0) return [{ json: { isProfitable: false, message: 'No profitable opportunities found' } }];\nreturn opportunities.slice(0, 10).map(function(op) { return { json: op }; });"
      },
      "id": "e2b3798d-407e-453c-96e3-528da0b6d318",
      "name": "Calculate Arbitrage",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [26880, 6208]
    },
    {
      "parameters": {},
      "id": "6697b473-1555-4fc4-8ca7-8b55c6af8868",
      "name": "Merge 4",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [25440, 5440]
    },
    {
      "parameters": {},
      "id": "ea1d20cd-1ba7-4cc7-8d0f-76048bde50a4",
      "name": "Merge 3",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [25232, 5280]
    },
    {
      "parameters": {},
      "id": "31ba84b8-09f9-4b88-834f-2e8f6133e6f7",
      "name": "Merge 2",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [25040, 5136]
    },
    {
      "parameters": {},
      "id": "2a2ef360-1e6d-4c6d-9474-4f3d2acb0c1e",
      "name": "Merge 1",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [24832, 4992]
    },
    {
      "parameters": { "url": "https://api.bybit.com/v5/market/tickers?category=spot", "options": { "timeout": 5000 } },
      "id": "8a7746e1-214c-47a4-9c43-22876f1d115d",
      "name": "Bybit Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 5696]
    },
    {
      "parameters": { "url": "https://www.okx.com/api/v5/market/tickers?instType=SPOT", "options": { "timeout": 5000 } },
      "id": "934a54d2-abaa-4aaa-94a5-ccbf53ce2f49",
      "name": "OKX Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 5488]
    },
    {
      "parameters": { "url": "https://api.gateio.ws/api/v4/spot/tickers", "options": { "timeout": 5000 } },
      "id": "708d53a8-c25c-4088-83c1-bf253acbac55",
      "name": "Gateio Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 5280]
    },
    {
      "parameters": { "url": "https://api.kucoin.com/api/v1/market/allTickers", "options": { "timeout": 5000 } },
      "id": "bcf98741-b08a-40fc-8edb-6b6f92eb335d",
      "name": "KuCoin Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 5088]
    },
    {
      "parameters": { "url": "https://api.binance.com/api/v3/ticker/24hr", "options": { "timeout": 5000 } },
      "id": "f0cdcfd1-8846-4e8b-9bff-f4dbf901e3bd",
      "name": "Binance Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 4880]
    },
    {
      "parameters": { "rule": { "interval": [{ "field": "seconds" }] } },
      "id": "58286b8f-88eb-480c-85d5-fbd602762164",
      "name": "Every 30 Seconds",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [23776, 5648]
    },
    {
      "parameters": { "url": "https://api.mexc.com/api/v3/ticker/24hr", "options": { "timeout": 5000 } },
      "id": "bbdc2ff7-a296-4712-8131-9bd6b70f2a72",
      "name": "MEXC Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 5904]
    },
    {
      "parameters": { "url": "https://api.bitget.com/api/v2/spot/market/tickers", "options": { "timeout": 5000 } },
      "id": "cf9995d1-65ea-4a33-8937-e36336222d2c",
      "name": "Bitget Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 6112]
    },
    {
      "parameters": { "url": "https://api.huobi.pro/market/tickers", "options": { "timeout": 5000 } },
      "id": "3dd956ee-d378-44b1-8d98-23904329ded5",
      "name": "HTX Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 6320]
    },
    {
      "parameters": { "url": "https://api.crypto.com/v2/public/get-ticker", "options": { "timeout": 5000 } },
      "id": "e02bd29b-c7d4-4d47-bc34-e27cddede5fe",
      "name": "Crypto.com Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 6528]
    },
    {
      "parameters": {},
      "id": "6870ce0e-961b-4242-aedb-85b059e3cfe1",
      "name": "Merge 5",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [25648, 5600]
    },
    {
      "parameters": {},
      "id": "bb74631e-8734-4191-ad3f-b52780323e6b",
      "name": "Merge 6",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [25856, 5744]
    },
    {
      "parameters": {},
      "id": "20d4c0a7-da84-4d40-b2a8-f2fa1497c9f6",
      "name": "Merge 7",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [26064, 5888]
    },
    {
      "parameters": {},
      "id": "e72ee30a-7632-4a93-aec2-2b17b3858518",
      "name": "Merge 8",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [26272, 6032]
    },
    {
      "parameters": { "url": "https://open-api.bingx.com/openApi/spot/v1/ticker/24hr", "options": { "timeout": 5000 } },
      "id": "4722a274-7627-494e-962d-bab3d730c905",
      "name": "BingX Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 6736]
    },
    {
      "parameters": { "url": "https://api.poloniex.com/markets/ticker24h", "options": { "timeout": 5000 } },
      "id": "98124c14-2e2d-4609-8463-c7234bd349df",
      "name": "Poloniex Price",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [24576, 6944]
    },
    {
      "parameters": {},
      "id": "879f562c-7a75-447b-a434-57bc5c35d79c",
      "name": "Merge 9",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [26480, 6176]
    },
    {
      "parameters": {},
      "id": "1b07fc46-ace6-41b4-866f-b6c5f083e97e",
      "name": "Merge 10",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [26688, 6320]
    },
    {
      "parameters": {
        "jsCode": "// BUILD ORDER BOOK URLS — Stage 2 prep\n// Generates exchange-specific order book API URLs for buy and sell sides\n\nfunction orderBookUrl(exchange, pair) {\n  var urls = {\n    binance:   'https://api.binance.com/api/v3/depth?symbol=' + pair + '&limit=20',\n    kucoin:    'https://api.kucoin.com/api/v1/market/orderbook/level2_20?symbol=' + pair,\n    gateio:    'https://api.gateio.ws/api/v4/spot/order_book?currency_pair=' + pair + '&limit=20',\n    okx:       'https://www.okx.com/api/v5/market/books?instId=' + pair + '&sz=20',\n    bybit:     'https://api.bybit.com/v5/market/orderbook?category=spot&symbol=' + pair + '&limit=20',\n    mexc:      'https://api.mexc.com/api/v3/depth?symbol=' + pair + '&limit=20',\n    bitget:    'https://api.bitget.com/api/v2/spot/market/orderbook?symbol=' + pair + '&limit=20',\n    htx:       'https://api.huobi.pro/market/depth?symbol=' + pair + '&type=step0&depth=20',\n    cryptocom: 'https://api.crypto.com/v2/public/get-book?instrument_name=' + pair + '&depth=20',\n    bingx:     'https://open-api.bingx.com/openApi/spot/v1/market/depth?symbol=' + pair + '&depth=20',\n    poloniex:  'https://api.poloniex.com/markets/' + pair + '/orderBook?scale=0&limit=20'\n  };\n  return urls[exchange] || '';\n}\n\nreturn $input.all().map(function(item) {\n  var o = item.json;\n  return { json: Object.assign({}, o, {\n    buyBookUrl: orderBookUrl(o.buyExchange, o.buyPair),\n    sellBookUrl: orderBookUrl(o.sellExchange, o.sellPair)\n  }) };\n});"
      },
      "id": "c1d2e3f4-aaaa-bbbb-cccc-111111111111",
      "name": "Build OB URLs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [27280, 6128]
    },
    {
      "parameters": {
        "url": "={{ $json.buyBookUrl }}",
        "options": { "timeout": 5000 }
      },
      "id": "d2e3f4a1-bbbb-cccc-dddd-222222222222",
      "name": "Fetch Buy OB",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "continueOnFail": true,
      "position": [27280, 6400]
    },
    {
      "parameters": {
        "mode": "combine",
        "combineBy": "combineByPosition",
        "options": {}
      },
      "id": "e3f4a1b2-cccc-dddd-eeee-333333333333",
      "name": "Merge Buy OB",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3,
      "position": [27488, 6240]
    },
    {
      "parameters": {
        "url": "={{ $json.sellBookUrl }}",
        "options": { "timeout": 5000 }
      },
      "id": "f4a1b2c3-dddd-eeee-ffff-444444444444",
      "name": "Fetch Sell OB",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "continueOnFail": true,
      "position": [27696, 6240]
    },
    {
      "parameters": {
        "jsCode": "// VERIFY DEPTH — Calculate VWAP from real order books\n// Reads buy OB from Merge Buy OB node, sell OB from current input\n\nvar MIN_SPREAD = 1.0;\nvar TRADE_SIZE = 500;\n\nfunction parseBook(exchange, data) {\n  try {\n    if (!data) return { asks: [], bids: [] };\n    if (exchange === 'okx') {\n      var book = data.data && data.data[0];\n      return {\n        asks: (book && book.asks || []).map(function(a) { return [parseFloat(a[0]), parseFloat(a[1])]; }),\n        bids: (book && book.bids || []).map(function(b) { return [parseFloat(b[0]), parseFloat(b[1])]; })\n      };\n    } else if (exchange === 'bybit') {\n      var book = data.result;\n      return {\n        asks: (book && book.a || []).map(function(a) { return [parseFloat(a[0]), parseFloat(a[1])]; }),\n        bids: (book && book.b || []).map(function(b) { return [parseFloat(b[0]), parseFloat(b[1])]; })\n      };\n    } else if (exchange === 'htx') {\n      var tick = data.tick;\n      return {\n        asks: (tick && tick.asks || []).map(function(a) { return [parseFloat(a[0]), parseFloat(a[1])]; }),\n        bids: (tick && tick.bids || []).map(function(b) { return [parseFloat(b[0]), parseFloat(b[1])]; })\n      };\n    } else if (exchange === 'cryptocom') {\n      var book = data.result && data.result.data;\n      return {\n        asks: (book && book.asks || []).map(function(a) { return [parseFloat(a.price), parseFloat(a.quantity)]; }),\n        bids: (book && book.bids || []).map(function(b) { return [parseFloat(b.price), parseFloat(b.quantity)]; })\n      };\n    } else if (exchange === 'kucoin') {\n      var d = data.data;\n      return {\n        asks: (d && d.asks || []).map(function(a) { return [parseFloat(a[0]), parseFloat(a[1])]; }),\n        bids: (d && d.bids || []).map(function(b) { return [parseFloat(b[0]), parseFloat(b[1])]; })\n      };\n    } else {\n      return {\n        asks: (data.asks || []).map(function(a) { return [parseFloat(a[0]), parseFloat(a[1])]; }),\n        bids: (data.bids || []).map(function(b) { return [parseFloat(b[0]), parseFloat(b[1])]; })\n      };\n    }\n  } catch (_) { return { asks: [], bids: [] }; }\n}\n\nfunction calcVWAP(orders, tradeUSDT) {\n  var remaining = tradeUSDT, totalCost = 0, totalQty = 0;\n  for (var i = 0; i < orders.length; i++) {\n    var o = orders[i];\n    if (!o[0] || !o[1] || isNaN(o[0]) || isNaN(o[1])) continue;\n    var available = o[1] * o[0];\n    var fill = Math.min(available, remaining);\n    totalCost += fill; totalQty += fill / o[0]; remaining -= fill;\n    if (remaining <= 0) break;\n  }\n  if (remaining > 0 || totalQty === 0) return null;\n  return totalCost / totalQty;\n}\n\nvar sellItems = $input.all();\nvar mergedItems;\ntry { mergedItems = $('Merge Buy OB').all(); } catch(_) { mergedItems = []; }\n\nvar output = [];\nfor (var i = 0; i < sellItems.length; i++) {\n  var sellOB = sellItems[i].json;\n  var merged = (mergedItems[i]) ? mergedItems[i].json : null;\n\n  if (!merged) {\n    output.push({ json: Object.assign({}, sellOB, { liquidityOk: true, effectiveSpread: sellOB.spreadNet || 0, depthBuyVWAP: null, depthSellVWAP: null, walletNote: 'Depth check skipped' }) });\n    continue;\n  }\n\n  var buyExchange = merged.buyExchange;\n  var sellExchange = merged.sellExchange;\n  var buyBook = parseBook(buyExchange, merged);\n  var sellBook = parseBook(sellExchange, sellOB);\n\n  var liquidityOk = false, effectiveSpread = 0;\n  var depthBuyVWAP = null, depthSellVWAP = null;\n  var walletNote = 'Verify deposit/withdrawal status before trading';\n\n  if (buyBook.asks.length > 0 && sellBook.bids.length > 0) {\n    depthBuyVWAP = calcVWAP(buyBook.asks, TRADE_SIZE);\n    depthSellVWAP = calcVWAP(sellBook.bids, TRADE_SIZE);\n    if (depthBuyVWAP && depthSellVWAP) {\n      effectiveSpread = ((depthSellVWAP - depthBuyVWAP) / depthBuyVWAP) * 100;\n      liquidityOk = effectiveSpread >= MIN_SPREAD;\n    }\n  }\n\n  output.push({ json: {\n    pair: merged.pair, baseAsset: merged.baseAsset, quoteAsset: merged.quoteAsset,\n    spread: merged.spread, spreadNet: merged.spreadNet, profit: merged.profit,\n    netProfitUSD: merged.netProfitUSD, roi: merged.roi,\n    buyExchange: merged.buyExchange, sellExchange: merged.sellExchange,\n    buyPrice: merged.buyPrice, sellPrice: merged.sellPrice,\n    effectiveBuy: merged.effectiveBuy, effectiveSell: merged.effectiveSell,\n    buyFeeUSD: merged.buyFeeUSD, sellFeeUSD: merged.sellFeeUSD,\n    slippageUSD: merged.slippageUSD, withdrawFeeUSD: merged.withdrawFeeUSD,\n    volume24h: merged.volume24h, isProfitable: merged.isProfitable,\n    marketCount: merged.marketCount, buyPair: merged.buyPair, sellPair: merged.sellPair,\n    exchanges: merged.exchanges, simultaneousTrade: merged.simultaneousTrade,\n    exampleTrade: merged.exampleTrade,\n    liquidityOk: liquidityOk,\n    effectiveSpread: parseFloat(effectiveSpread.toFixed(4)),\n    depthBuyVWAP: depthBuyVWAP ? parseFloat(depthBuyVWAP.toFixed(8)) : null,\n    depthSellVWAP: depthSellVWAP ? parseFloat(depthSellVWAP.toFixed(8)) : null,\n    walletNote: walletNote\n  } });\n}\n\nreturn output;"
      },
      "id": "a5b6c7d8-eeee-ffff-0000-555555555555",
      "name": "Verify Depth",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [27904, 6240]
    },
    {
      "parameters": {
        "conditions": {
          "options": { "leftValue": "", "caseSensitive": true, "typeValidation": "strict", "version": 2 },
          "combinator": "and",
          "conditions": [{ "id": "liquidity-ok", "operator": { "type": "boolean", "operation": "equals" }, "leftValue": "={{ $json.liquidityOk }}", "rightValue": true }]
        },
        "options": {}
      },
      "id": "b2c3d4e5-5555-6666-7777-888888888888",
      "name": "Filter Viable Trades",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [28112, 6240]
    },
    {
      "parameters": {
        "jsCode": "// FORMAT ALERT v1.0.1 — Enhanced with liquidity + simultaneous execution plan\nconst PAIR_COOLDOWN_MS = 10 * 60 * 1000;\nconst VERSION = 'v1.0.1';\n\nconst state = $getWorkflowStaticData('global');\nif (!state.pairLastSent) state.pairLastSent = {};\nif (!state.dailyLog) state.dailyLog = [];\n\nconst now = Date.now();\nconst today = new Date().toISOString().slice(0, 10);\nif (state.lastLogDate !== today) { state.dailyLog = []; state.lastLogDate = today; }\n\nconst allItems = $input.all();\nif (allItems.length === 0) return [];\n\nconst newItems = allItems.filter(function(item) {\n  const last = state.pairLastSent[item.json.pair] || 0;\n  return (now - last) >= PAIR_COOLDOWN_MS;\n});\n\nif (newItems.length === 0) return [];\n\nfor (const item of newItems) {\n  state.pairLastSent[item.json.pair] = now;\n  state.dailyLog.push({\n    ts: now, pair: item.json.pair,\n    spreadPct: item.json.spread, spreadNet: item.json.spreadNet,\n    netProfitUSD: item.json.netProfitUSD,\n    buyEx: item.json.buyExchange, sellEx: item.json.sellExchange,\n    buyPrice: item.json.buyPrice, sellPrice: item.json.sellPrice,\n    quoteAsset: item.json.quoteAsset, liquidityOk: item.json.liquidityOk\n  });\n}\n\nconst count = newItems.length;\nlet message = '🔥 *ARBITRAGE ALERT ' + VERSION + '* — ' + count + ' opportunit' + (count === 1 ? 'y' : 'ies') + ' >1.5% spread\\n\\n';\n\nfor (let i = 0; i < newItems.length; i++) {\n  const o = newItems[i].json;\n  const liqIcon = o.liquidityOk ? '✅' : '⚠️';\n  const effSpread = (o.effectiveSpread != null) ? o.effectiveSpread.toFixed(3) : o.spreadNet.toFixed(3);\n  message += (i + 1) + '. *' + o.pair + '*  |  Ticker: *' + o.spread.toFixed(3) + '%*  |  Net: *' + o.spreadNet.toFixed(3) + '%*\\n';\n  message += liqIcon + ' Depth-verified: *' + effSpread + '%*  |  Vol 24h: $' + (o.volume24h / 1000).toFixed(0) + 'k\\n';\n  message += 'Buy *' + o.buyExchange.toUpperCase() + '* @ ' + o.buyPrice + ' -> Sell *' + o.sellExchange.toUpperCase() + '* @ ' + o.sellPrice + ' ' + o.quoteAsset + '\\n';\n  message += '💰 Net profit: *$' + o.netProfitUSD.toFixed(2) + '* per $500  (ROI: ' + o.roi.toFixed(3) + '%)\\n';\n  message += '📋 Fees: buy $' + o.buyFeeUSD.toFixed(2) + ' + sell $' + o.sellFeeUSD.toFixed(2) + ' + slip $' + o.slippageUSD.toFixed(2) + ' + rebal $' + o.withdrawFeeUSD.toFixed(2) + '\\n';\n  message += '⚡ *Simultaneous:* Buy ' + o.simultaneousTrade.buyQty + ' ' + o.baseAsset + ' on ' + o.buyExchange.toUpperCase() + ' & sell on ' + o.sellExchange.toUpperCase() + ' at same time\\n';\n  message += '⚠️ ' + (o.walletNote || 'Verify wallet deposit/withdrawal status before trading') + '\\n';\n  if (i < newItems.length - 1) message += '\\n';\n}\n\nreturn [{ json: { message: message, count: count } }];"
      },
      "id": "5e27ec1d-209c-44ff-920e-0770776732af",
      "name": "Format Alert",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [28320, 6032]
    },
    {
      "parameters": {
        "chatId": "-1003712551578",
        "text": "={{ $json.message }}",
        "additionalFields": { "appendAttribution": false, "parse_mode": "Markdown" }
      },
      "id": "45f84413-1de9-4a46-b08e-c35a9806b4f2",
      "name": "Send Telegram Alert",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [28496, 5920],
      "webhookId": "1ae8a5dc-9d02-40c2-b5e2-aff07b23270b",
      "credentials": { "telegramApi": { "id": "TVZOdtZlMvXILyoC", "name": "Crypto Arbitrage" } }
    },
    {
      "parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 8 * * *" }] } },
      "id": "1c6fba80-6bae-4745-b4b0-b87752abd253",
      "name": "Daily 8AM Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [23376, 6480]
    },
    {
      "parameters": {
        "jsCode": "const state = $getWorkflowStaticData('global');\nconst log = state.dailyLog || [];\nstate.dailyLog = [];\nconst VERSION = 'v1.0.1';\n\nif (log.length === 0) {\n  return [{ json: { message: '📊 *DAILY DIGEST ' + VERSION + '* — No arbitrage opportunities above 1.5% detected today.' } }];\n}\n\nconst INVESTMENT = 10000;\nconst byPair = {};\nfor (const e of log) { if (!byPair[e.pair]) byPair[e.pair] = []; byPair[e.pair].push(e); }\n\nconst best = log.reduce(function(a, b) { return (a.spreadNet || a.spreadPct || 0) > (b.spreadNet || b.spreadPct || 0) ? a : b; });\nconst bestSpread = best.spreadNet || best.spreadPct || 0;\nconst avgSpread = log.reduce(function(s, e) { return s + (e.spreadNet || e.spreadPct || 0); }, 0) / log.length;\nconst avgProfitUSD = log.reduce(function(s, e) { return s + (e.netProfitUSD || 0); }, 0) / log.length;\n\nconst pairSummary = Object.entries(byPair)\n  .map(function(entry) {\n    const entries = entry[1];\n    return { pair: entry[0], count: entries.length,\n      maxSpread: Math.max.apply(null, entries.map(function(e) { return e.spreadNet || e.spreadPct || 0; })),\n      avgSpread: entries.reduce(function(s, e) { return s + (e.spreadNet || e.spreadPct || 0); }, 0) / entries.length };\n  })\n  .sort(function(a, b) { return b.maxSpread - a.maxSpread; }).slice(0, 5);\n\nconst yesterday = new Date(Date.now() - 86400000).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });\nconst uniquePairs = Object.keys(byPair).length;\nconst verified = log.filter(function(e) { return e.liquidityOk === true; }).length;\nconst rejected = log.filter(function(e) { return e.liquidityOk === false; }).length;\n\nconst topLines = pairSummary.map(function(p, i) {\n  return (i + 1) + '. *' + p.pair + '* — max ' + p.maxSpread.toFixed(2) + '%, avg ' + p.avgSpread.toFixed(2) + '% (' + p.count + ' alerts)';\n}).join('\\n');\n\nconst simBest = INVESTMENT * (bestSpread / 100);\nconst simAvg = INVESTMENT * (avgSpread / 100);\n\nconst message =\n  '📊 *DAILY ARBITRAGE DIGEST ' + VERSION + '* — ' + yesterday + '\\n\\n' +\n  'Opportunities detected: *' + log.length + '*  |  Unique pairs: *' + uniquePairs + '*\\n' +\n  '🔍 Quality filters: ' + verified + ' passed depth-check, ' + rejected + ' rejected (thin books)\\n\\n' +\n  '🏆 *Best trade:*\\n' + best.pair + ' | Spread: ' + bestSpread.toFixed(2) + '%  |  Net profit: $' + (best.netProfitUSD || 0).toFixed(2) + '\\n' +\n  'Buy ' + best.buyEx + ' @ ' + Number(best.buyPrice).toLocaleString() + ' -> Sell ' + best.sellEx + ' @ ' + Number(best.sellPrice).toLocaleString() + ' ' + best.quoteAsset + '\\n\\n' +\n  '📈 *Top pairs by spread:*\\n' + topLines + '\\n\\n' +\n  '💰 *$' + INVESTMENT.toLocaleString() + ' simulation:*\\n' +\n  'Best (' + best.pair + ' ' + bestSpread.toFixed(2) + '%) -> Profit: *$' + simBest.toFixed(0) + '* -> Total: $' + (INVESTMENT + simBest).toFixed(0) + '\\n' +\n  'Avg (' + avgSpread.toFixed(2) + '%) -> Avg profit: *$' + simAvg.toFixed(0) + '*  |  Avg net: $' + avgProfitUSD.toFixed(2) + '/trade';\n\nreturn [{ json: { message: message } }];"
      },
      "id": "62ef22fd-4a0f-4ff2-ab01-a4faf193087a",
      "name": "Build Daily Digest",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [23616, 6480]
    },
    {
      "parameters": {
        "chatId": "-1003712551578",
        "text": "={{ $json.message }}",
        "additionalFields": { "appendAttribution": false, "parse_mode": "Markdown" }
      },
      "id": "f79998dd-2ae8-4ed5-aa1b-da269f6c23f8",
      "name": "Send Daily Digest",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [23840, 6480],
      "webhookId": "3d90dc84-a8dd-42a9-8927-403ddf96f292",
      "credentials": { "telegramApi": { "id": "TVZOdtZlMvXILyoC", "name": "Crypto Arbitrage" } }
    }
  ],
  "pinData": {},
  "connections": {
    "Daily 8AM Trigger": { "main": [[{ "node": "Build Daily Digest", "type": "main", "index": 0 }]] },
    "Build Daily Digest": { "main": [[{ "node": "Send Daily Digest", "type": "main", "index": 0 }]] },
    "Calculate Arbitrage": { "main": [[{ "node": "Check Profitable", "type": "main", "index": 0 }]] },
    "Check Profitable": { "main": [[{ "node": "Build OB URLs", "type": "main", "index": 0 }]] },
    "Build OB URLs": {
      "main": [[
        { "node": "Fetch Buy OB", "type": "main", "index": 0 },
        { "node": "Merge Buy OB", "type": "main", "index": 0 }
      ]]
    },
    "Fetch Buy OB": { "main": [[{ "node": "Merge Buy OB", "type": "main", "index": 1 }]] },
    "Merge Buy OB": { "main": [[{ "node": "Fetch Sell OB", "type": "main", "index": 0 }]] },
    "Fetch Sell OB": { "main": [[{ "node": "Verify Depth", "type": "main", "index": 0 }]] },
    "Verify Depth": { "main": [[{ "node": "Filter Viable Trades", "type": "main", "index": 0 }]] },
    "Filter Viable Trades": { "main": [[{ "node": "Format Alert", "type": "main", "index": 0 }]] },
    "Format Alert": { "main": [[{ "node": "Send Telegram Alert", "type": "main", "index": 0 }]] },
    "Merge 8": { "main": [[{ "node": "Merge 9", "type": "main", "index": 0 }]] },
    "Merge 9": { "main": [[{ "node": "Merge 10", "type": "main", "index": 0 }]] },
    "Merge 10": { "main": [[{ "node": "Calculate Arbitrage", "type": "main", "index": 0 }]] },
    "BingX Price": { "main": [[{ "node": "Merge 9", "type": "main", "index": 1 }]] },
    "Poloniex Price": { "main": [[{ "node": "Merge 10", "type": "main", "index": 1 }]] },
    "Merge 7": { "main": [[{ "node": "Merge 8", "type": "main", "index": 0 }]] },
    "Merge 6": { "main": [[{ "node": "Merge 7", "type": "main", "index": 0 }]] },
    "Merge 5": { "main": [[{ "node": "Merge 6", "type": "main", "index": 0 }]] },
    "Merge 4": { "main": [[{ "node": "Merge 5", "type": "main", "index": 0 }]] },
    "Merge 3": { "main": [[{ "node": "Merge 4", "type": "main", "index": 0 }]] },
    "Merge 2": { "main": [[{ "node": "Merge 3", "type": "main", "index": 0 }]] },
    "Merge 1": { "main": [[{ "node": "Merge 2", "type": "main", "index": 0 }]] },
    "Bybit Price": { "main": [[{ "node": "Merge 4", "type": "main", "index": 1 }]] },
    "OKX Price": { "main": [[{ "node": "Merge 3", "type": "main", "index": 1 }]] },
    "Gateio Price": { "main": [[{ "node": "Merge 2", "type": "main", "index": 1 }]] },
    "KuCoin Price": { "main": [[{ "node": "Merge 1", "type": "main", "index": 1 }]] },
    "Binance Price": { "main": [[{ "node": "Merge 1", "type": "main", "index": 0 }]] },
    "MEXC Price": { "main": [[{ "node": "Merge 5", "type": "main", "index": 1 }]] },
    "Bitget Price": { "main": [[{ "node": "Merge 6", "type": "main", "index": 1 }]] },
    "HTX Price": { "main": [[{ "node": "Merge 7", "type": "main", "index": 1 }]] },
    "Crypto.com Price": { "main": [[{ "node": "Merge 8", "type": "main", "index": 1 }]] },
    "Every 30 Seconds": {
      "main": [[
        { "node": "Binance Price", "type": "main", "index": 0 },
        { "node": "KuCoin Price", "type": "main", "index": 0 },
        { "node": "Gateio Price", "type": "main", "index": 0 },
        { "node": "OKX Price", "type": "main", "index": 0 },
        { "node": "Bybit Price", "type": "main", "index": 0 },
        { "node": "MEXC Price", "type": "main", "index": 0 },
        { "node": "Bitget Price", "type": "main", "index": 0 },
        { "node": "HTX Price", "type": "main", "index": 0 },
        { "node": "Crypto.com Price", "type": "main", "index": 0 },
        { "node": "BingX Price", "type": "main", "index": 0 },
        { "node": "Poloniex Price", "type": "main", "index": 0 }
      ]]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "versionId": "a3a7d7cd-c390-4a0a-bb9f-f522e58a5593",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "e09d1f0f8b9f78e80ac70b6dc8726dd263b0b6ffc4300b0eee3b58f6da316f29"
  },
  "id": "ubyoshY7PN0Spkrp",
  "tags": [{ "updatedAt": "2026-03-17T19:44:52.030Z", "createdAt": "2026-03-17T19:44:52.030Z", "id": "lD7oCDT7Xh6mgzKv", "name": "Trading" }]
}
