📈 Backtest & Trading Flow

Magnificent 7 Portfolio Backtest

Equal-weight portfolio of $10,000 purchased on January 2, 2025 at 9:40 AM ET — import the Node-RED flow and start trading automatically

View Flow JSON

Overview

This Node-RED flow creates and manages a diversified portfolio of the "Magnificent 7" — the seven largest and most influential technology stocks driving the U.S. equity market. The strategy automatically allocates equal portions of a $10,000 portfolio across all seven stocks.

Included Stocks

AAPL - Apple AMZN - Amazon GOOG - Alphabet META - Meta Platforms MSFT - Microsoft NVDA - NVIDIA TSLA - Tesla

Backtest Assumptions

  • Purchase date: January 2, 2025 at 9:40 AM ET (first trading day of 2025)
  • Portfolio size: $10,000 equally split across 7 stocks (~$1,428.57 per position)
  • Tickers: AAPL, AMZN, GOOG, META, MSFT, NVDA, TSLA
  • Buy price: VWAP of the 1-minute bar at 9:40 AM ET on 1/2/2025 (via Alpaca Data API, IEX feed)
  • Current price: Midpoint of latest bid/ask quote (via Alpaca Data API, IEX feed)
  • Historical data: Daily bars with split adjustment from Alpaca's IEX feed
  • Strategy: Buy and hold — no rebalancing, no sells
  • Fractional shares: Enabled (as supported by Alpaca)
  • Commissions: $0 (commission-free via Alpaca)

📊 Performance Analysis

Live backtest results from the Python backtester. Run backtest_mag7.py to update.

Return by Stock

Detailed Breakdown

Symbol Buy Price Current Price Shares Allocation Current Value P&L ($) P&L (%)

Key Features

📊 Strategy Configuration

Set your portfolio size (default $10,000) and the system automatically calculates equal-weight allocations across all 7 Magnificent 7 stocks.

⚡ Automated Execution

One-click trade execution with built-in rate limiting (1 order/second) to comply with API requirements.

💰 Position Liquidation

Easily close all positions in your Magnificent 7 portfolio with a single click. Handles both long and short positions automatically.

📈 Performance Tracking

Calculate your net gain/loss and percentage returns with built-in performance analytics across all 7 positions.

How to Import This Flow

  1. Copy the JSON code from the box below by clicking the "Copy to Clipboard" button.
  2. Open your MachineTrader Node-RED editor.
  3. Click the hamburger menu (☰) in the top-right corner.
  4. Select Import from the dropdown menu.
  5. Paste the JSON code into the import dialog.
  6. Click Import to add the flow to your workspace.
  7. Configure your Alpaca credentials in the configuration node.
  8. Click Deploy to activate the flow.

⚠️ Important: This flow is configured for Alpaca Paper Trading by default. Always test with paper trading before using real funds. You must configure your own Alpaca API credentials before executing trades.

Node-RED Flow JSON

Click the button below to copy the complete flow configuration to your clipboard:

Backtest Magnificent 7 Portfolio.json
[
    {
        "id": "5e7dc1eff97fb27b",
        "type": "tab",
        "label": "Backtest Magnificent 7 Portfolio",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "24864b0dbc331ccc",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Backtest: Buy $10k equal-weight Mag 7 on 1/2/2025 at 9:40 AM ET. Click Inject nodes in order.",
        "info": "",
        "x": 370,
        "y": 40,
        "wires": []
    },
    {
        "id": "dd3b799630bbb818",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Step 1: Store strategy definition",
        "info": "",
        "x": 170,
        "y": 100,
        "wires": []
    },
    {
        "id": "92fd8ee48062aa86",
        "type": "inject",
        "z": "5e7dc1eff97fb27b",
        "name": "Run once",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 120,
        "y": 140,
        "wires": [
            [
                "66663fc8804d14bc"
            ]
        ]
    },
    {
        "id": "66663fc8804d14bc",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "Store strategy definition",
        "func": "flow.set(\"tickers\", \"AAPL,AMZN,GOOG,META,MSFT,NVDA,TSLA\")\nflow.set(\"portfolioSize\", 10000)\nflow.set(\"number\", 7)\nflow.set(\"purchaseDate\", \"2025-01-02\")\nflow.set(\"purchaseTime\", \"09:40\")\nflow.set(\"backtestResults\", [])\nnode.warn(\"Strategy defined: $10,000 equal-weight Mag 7, purchase date 1/2/2025 9:40 AM ET\")\nreturn msg;\n",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 330,
        "y": 140,
        "wires": [
            []
        ]
    },
    {
        "id": "cef57551db140e73",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Step 2: Fetch historical buy prices (1-min bar VWAP at 9:40 AM on 1/2/2025)",
        "info": "",
        "x": 300,
        "y": 200,
        "wires": []
    },
    {
        "id": "85783e9e775af97b",
        "type": "inject",
        "z": "5e7dc1eff97fb27b",
        "name": "Run once",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 120,
        "y": 240,
        "wires": [
            [
                "01b75afb593539ef"
            ]
        ]
    },
    {
        "id": "01b75afb593539ef",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "get symbols",
        "func": "msg.payload = flow.get(\"tickers\")\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 270,
        "y": 240,
        "wires": [
            [
                "c5283c3bc2ea8c74"
            ]
        ]
    },
    {
        "id": "c5283c3bc2ea8c74",
        "type": "split",
        "z": "5e7dc1eff97fb27b",
        "name": "",
        "splt": ",",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "property": "payload",
        "x": 410,
        "y": 240,
        "wires": [
            [
                "6a97a394a3dc1705"
            ]
        ]
    },
    {
        "id": "6a97a394a3dc1705",
        "type": "delay",
        "z": "5e7dc1eff97fb27b",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 550,
        "y": 240,
        "wires": [
            [
                "e421cdc66673fdb8"
            ]
        ]
    },
    {
        "id": "e421cdc66673fdb8",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "build historical bar request",
        "func": "// Build Alpaca Data API request for 1-min bar on 1/2/2025\n// to get the VWAP at 9:40 AM ET\nmsg.symbol = msg.payload;\n\n// Alpaca expects RFC3339 timestamps in UTC\n// 9:40 AM ET = 14:40 UTC (ET is UTC-5 in January)\nvar params = [\n    \"timeframe=1Min\",\n    \"start=\" + encodeURIComponent(\"2025-01-02T14:40:00Z\"),\n    \"end=\" + encodeURIComponent(\"2025-01-02T14:41:00Z\"),\n    \"feed=iex\",\n    \"adjustment=split\",\n    \"limit=1\"\n].join(\"&\");\n\nmsg.url = \"https://data.alpaca.markets/v2/stocks/\" + msg.symbol + \"/bars?\" + params;\nmsg.method = \"GET\";\nmsg.apiKeyId = global.get(\"apiKeyLive\") || \"YOUR_KEY_ID\";\nmsg.apiSecretKey = global.get(\"apiSecretLive\") || \"YOUR_SECRET_KEY\";\n\nnode.warn(\"Fetching 1-min bar for \" + msg.symbol + \" on 1/2/2025 at 9:40 AM ET\");\n//node.warn(msg.url);\n//node.warn(\"API Key starts with: \" + String(msg.apiKeyId).substring(0, 6) + \"...\");\n//node.warn(\"API Secret starts with: \" + String(msg.apiSecretKey).substring(0, 6) + \"...\");\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 760,
        "y": 240,
        "wires": [
            [
                "903980747b16c1b2"
            ]
        ]
    },
    {
        "id": "903980747b16c1b2",
        "type": "http request",
        "z": "5e7dc1eff97fb27b",
        "name": "Alpaca Historical Bars",
        "method": "use",
        "ret": "obj",
        "paytoqs": "ignore",
        "url": "",
        "tls": "",
        "persist": false,
        "proxy": "",
        "insecureHTTPParser": false,
        "authType": "",
        "senderr": false,
        "headers": [
            {
                "keyType": "other",
                "keyValue": "APCA-API-KEY-ID",
                "valueType": "msg",
                "valueValue": "apiKeyId"
            },
            {
                "keyType": "other",
                "keyValue": "APCA-API-SECRET-KEY",
                "valueType": "msg",
                "valueValue": "apiSecretKey"
            },
            {
                "keyType": "other",
                "keyValue": "accept",
                "valueType": "other",
                "valueValue": "application/json"
            }
        ],
        "x": 1000,
        "y": 240,
        "wires": [
            [
                "5472e5a8dd181a50"
            ]
        ]
    },
    {
        "id": "5472e5a8dd181a50",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "extract VWAP buy price & store",
        "func": "// Extract the VWAP from the 1-min bar response\nlet bars = msg.payload.bars\nlet symbol = msg.symbol\n\nif (!bars || bars.length === 0) {\n    node.warn(\"No bar data for \" + symbol + \". Check API keys and date.\")\n    return null;\n}\n\nlet bar = bars[0]\nlet vwap = bar.vw  // VWAP of the 1-min bar\nlet open = bar.o\nlet high = bar.h\nlet low = bar.l\nlet close = bar.c\nlet volume = bar.v\n\n// Calculate shares purchased\nlet portfolioSize = flow.get(\"portfolioSize\")\nlet numStocks = flow.get(\"number\")\nlet allocation = portfolioSize / numStocks\nlet shares = allocation / vwap\n\n// Store the buy price for this symbol\nlet buyPrices = flow.get(\"buyPrices\") || {}\nbuyPrices[symbol] = {\n    vwap: vwap,\n    open: open,\n    high: high,\n    low: low,\n    close: close,\n    volume: volume,\n    shares: shares,\n    allocation: allocation,\n    date: \"2025-01-02T09:40:00\"\n}\nflow.set(\"buyPrices\", buyPrices)\n\nnode.warn(symbol + \" | VWAP: $\" + vwap.toFixed(2) + \" | Shares: \" + shares.toFixed(4) + \" | Allocation: $\" + allocation.toFixed(2))\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 230,
        "y": 300,
        "wires": [
            []
        ]
    },
    {
        "id": "4d9da73b1d98352c",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Step 3: Fetch current prices (latest quotes) and calculate backtest P&L",
        "info": "",
        "x": 290,
        "y": 380,
        "wires": []
    },
    {
        "id": "bd23d95a7ff94877",
        "type": "inject",
        "z": "5e7dc1eff97fb27b",
        "name": "Run once",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 120,
        "y": 420,
        "wires": [
            [
                "bb66a6f6a1fa1042"
            ]
        ]
    },
    {
        "id": "bb66a6f6a1fa1042",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "get symbols",
        "func": "msg.payload = flow.get(\"tickers\")\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 270,
        "y": 420,
        "wires": [
            [
                "3fe30253dbbb7d89"
            ]
        ]
    },
    {
        "id": "3fe30253dbbb7d89",
        "type": "split",
        "z": "5e7dc1eff97fb27b",
        "name": "",
        "splt": ",",
        "spltType": "str",
        "arraySplt": 1,
        "arraySpltType": "len",
        "stream": false,
        "addname": "",
        "property": "payload",
        "x": 410,
        "y": 420,
        "wires": [
            [
                "dc194922f92e9d2d"
            ]
        ]
    },
    {
        "id": "dc194922f92e9d2d",
        "type": "delay",
        "z": "5e7dc1eff97fb27b",
        "name": "",
        "pauseType": "rate",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 550,
        "y": 420,
        "wires": [
            [
                "f73dfb7de2cbc4da"
            ]
        ]
    },
    {
        "id": "f73dfb7de2cbc4da",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "set msg.symbol",
        "func": "msg.symbol = msg.payload\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 700,
        "y": 420,
        "wires": [
            [
                "e9a2bc7994aad24b"
            ]
        ]
    },
    {
        "id": "e9a2bc7994aad24b",
        "type": "alpaca-data-last-quote",
        "z": "5e7dc1eff97fb27b",
        "conf": "1dfe729b4b3149f6",
        "symbol": "",
        "name": "",
        "x": 900,
        "y": 420,
        "wires": [
            [
                "5b358d7f17aa999d"
            ]
        ]
    },
    {
        "id": "5b358d7f17aa999d",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "calculate P&L per stock",
        "func": "let symbol = msg.symbol\nlet buyPrices = flow.get(\"buyPrices\") || {}\n\nif (!buyPrices[symbol]) {\n    node.warn(\"No buy price for \" + symbol + \". Run Step 2 first.\")\n    return null;\n}\n\n// Get current price from latest quote (midpoint of bid/ask)\nlet ask = Number(msg.payload.ask_price) || 0\nlet bid = Number(msg.payload.bid_price) || 0\nlet currentPrice = 0\n\nif (ask > 0 && bid > 0) {\n    currentPrice = (ask + bid) / 2\n} else if (ask > 0) {\n    currentPrice = ask\n} else {\n    currentPrice = bid\n}\n\nlet buyData = buyPrices[symbol]\nlet buyPrice = buyData.vwap\nlet shares = buyData.shares\nlet allocation = buyData.allocation\n\n// Calculate P&L\nlet currentValue = shares * currentPrice\nlet gainLoss = currentValue - allocation\nlet gainLossPct = (gainLoss / allocation) * 100\n\n// Store result\nlet results = flow.get(\"backtestResults\") || []\nresults.push({\n    symbol: symbol,\n    buyPrice: Number(buyPrice.toFixed(2)),\n    currentPrice: Number(currentPrice.toFixed(2)),\n    shares: Number(shares.toFixed(4)),\n    allocation: Number(allocation.toFixed(2)),\n    currentValue: Number(currentValue.toFixed(2)),\n    gainLoss: Number(gainLoss.toFixed(2)),\n    gainLossPct: Number(gainLossPct.toFixed(2))\n})\nflow.set(\"backtestResults\", results)\n\nnode.warn(symbol + \" | Buy: $\" + buyPrice.toFixed(2) + \" | Now: $\" + currentPrice.toFixed(2) + \" | P&L: $\" + gainLoss.toFixed(2) + \" (\" + gainLossPct.toFixed(2) + \"%)\")\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 230,
        "y": 480,
        "wires": [
            []
        ]
    },
    {
        "id": "1b939c0fa139f4d0",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Step 4: Display full backtest summary",
        "info": "",
        "x": 200,
        "y": 560,
        "wires": []
    },
    {
        "id": "4a6155a4072675ea",
        "type": "inject",
        "z": "5e7dc1eff97fb27b",
        "name": "Run once",
        "props": [],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 120,
        "y": 600,
        "wires": [
            [
                "fd74b6965ba19c82"
            ]
        ]
    },
    {
        "id": "fd74b6965ba19c82",
        "type": "function",
        "z": "5e7dc1eff97fb27b",
        "name": "display backtest summary",
        "func": "let results = flow.get(\"backtestResults\") || []\nlet portfolioSize = flow.get(\"portfolioSize\")\n\nif (results.length === 0) {\n    node.warn(\"No backtest results. Run Steps 1-3 first.\")\n    return null;\n}\n\n// Sort by symbol\nresults.sort((a, b) => a.symbol.localeCompare(b.symbol))\n\n// Calculate totals\nlet totalAllocation = 0\nlet totalCurrentValue = 0\nlet totalGainLoss = 0\n\nnode.warn(\"========================================\")\nnode.warn(\"  MAGNIFICENT 7 BACKTEST RESULTS\")\nnode.warn(\"  Purchase Date: January 2, 2025 9:40 AM ET\")\nnode.warn(\"  Portfolio Size: $\" + portfolioSize.toLocaleString())\nnode.warn(\"========================================\")\nnode.warn(\"\")\n\nresults.forEach(r => {\n    let sign = r.gainLoss >= 0 ? \"+\" : \"\"\n    node.warn(r.symbol + \" | Buy: $\" + r.buyPrice.toFixed(2) + \" | Now: $\" + r.currentPrice.toFixed(2) + \" | Shares: \" + r.shares.toFixed(4) + \" | Value: $\" + r.currentValue.toFixed(2) + \" | P&L: \" + sign + \"$\" + r.gainLoss.toFixed(2) + \" (\" + sign + r.gainLossPct.toFixed(2) + \"%)\")\n    totalAllocation += r.allocation\n    totalCurrentValue += r.currentValue\n    totalGainLoss += r.gainLoss\n})\n\nlet totalPct = (totalGainLoss / totalAllocation) * 100\nlet sign = totalGainLoss >= 0 ? \"+\" : \"\"\n\nnode.warn(\"\")\nnode.warn(\"========================================\")\nnode.warn(\"  TOTAL: $\" + totalAllocation.toFixed(2) + \" → $\" + totalCurrentValue.toFixed(2))\nnode.warn(\"  P&L: \" + sign + \"$\" + totalGainLoss.toFixed(2) + \" (\" + sign + totalPct.toFixed(2) + \"%)\")\nnode.warn(\"========================================\")\n\n// Store in global for dashboard\nglobal.set(\"backtestMag7Results\", results)\nglobal.set(\"backtestMag7Total\", {\n    allocation: Number(totalAllocation.toFixed(2)),\n    currentValue: Number(totalCurrentValue.toFixed(2)),\n    gainLoss: Number(totalGainLoss.toFixed(2)),\n    gainLossPct: Number(totalPct.toFixed(2))\n})\n\nmsg.payload = {\n    results: results,\n    total: {\n        allocation: Number(totalAllocation.toFixed(2)),\n        currentValue: Number(totalCurrentValue.toFixed(2)),\n        gainLoss: Number(totalGainLoss.toFixed(2)),\n        gainLossPct: Number(totalPct.toFixed(2))\n    }\n}\n\nreturn msg;\n",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 340,
        "y": 600,
        "wires": [
            [
                "76bffd9581ec08aa"
            ]
        ]
    },
    {
        "id": "76bffd9581ec08aa",
        "type": "debug",
        "z": "5e7dc1eff97fb27b",
        "name": "Backtest Results",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "payload",
        "targetType": "msg",
        "statusVal": "",
        "statusType": "auto",
        "x": 570,
        "y": 600,
        "wires": []
    },
    {
        "id": "0fba61e8958b3bb4",
        "type": "comment",
        "z": "5e7dc1eff97fb27b",
        "name": "Note: Set global.apiKeyLive and global.apiSecretLive for historical bar requests (Step 2). Step 3 uses the Alpaca config node.",
        "info": "",
        "x": 470,
        "y": 680,
        "wires": []
    },
    {
        "id": "1dfe729b4b3149f6",
        "type": "alpaca-account",
        "name": "Paper",
        "keyId": "USE-OAUTH-OR-REPLACE",
        "paper": true
    }
]

Customization Options

You can easily customize this flow to fit your investment goals:

🎯 Change Portfolio Size

Edit the "Store strategy definition" function node to change the portfolioSize value from $10,000 to any amount you prefer.

📝 Modify Stock Selection

Update the tickers variable to include different stocks. Just use comma-separated ticker symbols (e.g., "AAPL,MSFT,NVDA,TSLA").

⏰ Schedule Automated Execution

Add cron expressions to the inject nodes to automatically execute trades at specific times or intervals.

Ready to Automate Your Trading?

Import the Magnificent 7 flow into your MachineTrader™ instance and start trading automatically.