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 JSONThis 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.
Live backtest results from the Python backtester. Run backtest_mag7.py to update.
| Symbol | Buy Price | Current Price | Shares | Allocation | Current Value | P&L ($) | P&L (%) |
|---|
Set your portfolio size (default $10,000) and the system automatically calculates equal-weight allocations across all 7 Magnificent 7 stocks.
One-click trade execution with built-in rate limiting (1 order/second) to comply with API requirements.
Easily close all positions in your Magnificent 7 portfolio with a single click. Handles both long and short positions automatically.
Calculate your net gain/loss and percentage returns with built-in performance analytics across all 7 positions.
⚠️ 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.
Click the button below to copy the complete flow configuration to your clipboard:
[
{
"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
}
]
You can easily customize this flow to fit your investment goals:
Edit the "Store strategy definition" function node to change the portfolioSize value from $10,000 to any amount you prefer.
Update the tickers variable to include different stocks. Just use comma-separated ticker symbols (e.g., "AAPL,MSFT,NVDA,TSLA").
Add cron expressions to the inject nodes to automatically execute trades at specific times or intervals.
Import the Magnificent 7 flow into your MachineTrader™ instance and start trading automatically.