Here's what we're building: TradingView fires an alert → a free Google Apps Script webhook catches it → your trade lands in a Google Sheets journal automatically. Zero cost. Zero third-party dependencies. Takes about 15 minutes to set up.
I run a USDJPY momentum strategy with TradingView alerts. Before this setup, I was copy-pasting alert messages into a spreadsheet like a caveman. Never again.
What You Need
- A TradingView account (free plan works for 1 alert; Essential+ for multiple)
- A Google account (for Sheets + Apps Script)
- 15 minutes
Step 1: Create Your Trading Journal Spreadsheet
Open Google Sheets and create a new spreadsheet. Name it something like "Trading Journal 2026."
Set up these column headers in Row 1:
| A | B | C | D | E | F | G | H |
|---|---|---|---|---|---|---|---|
| Timestamp | Ticker | Action | Price | Timeframe | Strategy | Alert Message | Notes |
Step 2: Deploy the Google Apps Script Webhook
This is where the magic happens. Google Apps Script gives you a free webhook endpoint that writes directly to your spreadsheet.
1. In your spreadsheet, go to Extensions → Apps Script
2. Delete any existing code inCode.gs
3. Paste this:
function doPost(e) {
try {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = JSON.parse(e.postData.contents);
sheet.appendRow([
new Date(), // Timestamp
data.ticker || '', // Ticker symbol
data.action || '', // BUY / SELL / CLOSE
data.price || '', // Current price
data.timeframe || '', // Chart timeframe
data.strategy || '', // Strategy name
data.message || '', // Full alert message
'' // Notes (manual)
]);
return ContentService
.createTextOutput(JSON.stringify({ status: 'ok' }))
.setMimeType(ContentService.MimeType.JSON);
} catch (error) {
return ContentService
.createTextOutput(JSON.stringify({ status: 'error', message: error.toString() }))
.setMimeType(ContentService.MimeType.JSON);
}
}
// Required for webhook verification
function doGet(e) {
return ContentService
.createTextOutput('Webhook is active')
.setMimeType(ContentService.MimeType.TEXT);
}
4. Click Deploy → New deployment
5. Select type: Web app 6. Set "Execute as" to Me 7. Set "Who has access" to Anyone 8. Click Deploy and authorize when prompted 9. Copy the Web app URL — this is your webhook endpointThe URL looks like: https://script.google.com/macros/s/AKfycb.../exec
Security Note
Setting access to "Anyone" means anyone with the URL can POST data to your sheet. The URL itself is a long random string, so it's effectively a secret key. Don't share it publicly. If you ever need to revoke access, just delete the deployment.
Step 3: Configure TradingView Alerts with Webhook
Now connect TradingView to your webhook.
1. Open any chart on TradingView
2. Click the Alert button (clock icon) or pressAlt+A
3. Set your alert condition (price cross, indicator signal, etc.)
4. In the Notifications tab, check Webhook URL
5. Paste your Google Apps Script URL
6. In the Message field, paste this JSON template:
{
"ticker": "{{ticker}}",
"action": "{{strategy.order.action}}",
"price": "{{close}}",
"timeframe": "{{interval}}",
"strategy": "Momentum Crossover",
"message": "{{ticker}} {{strategy.order.action}} at {{close}} on {{interval}} chart"
}
7. Click Create
Understanding the Placeholders
TradingView replaces these placeholders with real values when the alert fires:
| Placeholder | What It Returns | Example |
|---|---|---|
{{ticker}} | Symbol name | USDJPY, BTCUSDT |
{{close}} | Current price | 149.850 |
{{interval}} | Chart timeframe | 60 (minutes), D (daily) |
{{strategy.order.action}} | Trade direction | buy, sell |
{{time}} | Alert trigger time | 2026-03-19T05:00:00Z |
{{volume}} | Current volume | 1523400 |
{{exchange}} | Exchange name | BINANCE, OANDA |
{{strategy.order.action}} gives you the actual buy/sell signal. For plain indicators, use a static value like "action": "SIGNAL" and fill in the direction manually.
Step 4: Test the Pipeline
Before trusting this with real alerts, test it manually.
Option A: Test from TradingView
Set a price alert just above/below the current price so it triggers within seconds. Watch your Google Sheet — a new row should appear within 5-10 seconds.
Option B: Test with curl
curl -L -X POST "YOUR_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d '{"ticker":"BTCUSDT","action":"BUY","price":"84250.00","timeframe":"60","strategy":"Test Signal","message":"Test alert from curl"}'
If you see {"status":"ok"} and a new row appears in your sheet — you're live.
Common Issues
"Webhook URL is not valid" in TradingView: Make sure you copied the full Apps Script URL including the/exec at the end. Also verify your TradingView plan supports webhooks (Essential+).
No data appearing in sheet: Check the Apps Script execution log. Go to Apps Script → Executions (left sidebar). Look for errors. The most common: JSON parsing failure because the alert message isn't valid JSON.
Data in wrong columns: The appendRow array order must match your column headers. Adjust the order in the script if needed.
Step 5: Advanced — Add P&L Tracking
Once the basic pipeline works, enhance your journal with calculated fields.
In column I, add a header "Entry Price." In column J, add "Exit Price." In column K, add "P&L."
For the P&L formula in K2:
=IF(AND(I2<>"",J2<>""), IF(G2="BUY", J2-I2, I2-J2), "")
Auto-Link Entries and Exits
Here's a more advanced Apps Script that automatically pairs BUY/SELL signals for the same ticker:
Like what you're reading? Try it yourself — this link supports ChartedTrader at no cost to you.
Try TradingView Free →
function doPost(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = JSON.parse(e.postData.contents);
var lastRow = sheet.getLastRow();
if (data.action === 'sell' || data.action === 'SELL' || data.action === 'CLOSE') {
for (var i = lastRow; i >= 2; i--) {
var rowTicker = sheet.getRange(i, 2).getValue();
var rowAction = sheet.getRange(i, 3).getValue();
var exitPrice = sheet.getRange(i, 10).getValue();
if (rowTicker === data.ticker &&
(rowAction === 'BUY' || rowAction === 'buy') &&
exitPrice === '') {
sheet.getRange(i, 10).setValue(parseFloat(data.price));
break;
}
}
}
sheet.appendRow([
new Date(),
data.ticker || '',
data.action || '',
data.price || '',
data.timeframe || '',
data.strategy || '',
data.message || '',
''
]);
return ContentService
.createTextOutput(JSON.stringify({ status: 'ok' }))
.setMimeType(ContentService.MimeType.JSON);
}
This automatically fills the exit price on your original entry row when a SELL signal fires for the same ticker. Combined with the P&L formula, you get automatic trade tracking.
Step 6: Pine Script Integration — Log Strategy Signals
If you're running a Pine Script strategy, you can send richer data with alert() calls:
//@version=6
strategy("My Momentum Strategy", overlay=true)
fast = ta.sma(close, 10)
slow = ta.sma(close, 50)
if ta.crossover(fast, slow)
strategy.entry("Long", strategy.long)
if ta.crossunder(fast, slow)
strategy.close("Long")
When you create the alert, select "Order fills only" or "Alert() function calls only" depending on whether you want to log every signal or just actual fills.
For the alert message with a strategy:
{
"ticker": "{{ticker}}",
"action": "{{strategy.order.action}}",
"price": "{{strategy.order.price}}",
"timeframe": "{{interval}}",
"strategy": "{{strategy.order.id}}",
"message": "{{strategy.order.comment}}"
}
{{strategy.order.price}} gives you the actual fill price (accounting for slippage in backtests), not just the closing price. Much more accurate for journal tracking.
Why Not Use Zapier or Make?
I tried Zapier first. It works — but here's why I switched:
| Feature | Google Apps Script | Zapier | Make |
|---|---|---|---|
| Cost | Free forever | $19.99/mo (Starter) | $9/mo (Core) |
| Webhook limit | Unlimited | 750 tasks/mo on Starter | 1,000 ops/mo on Core |
| Latency | 2-5 seconds | 5-15 seconds | 5-10 seconds |
| Customization | Full JavaScript | Visual builder | Visual builder |
| Maintenance | Set and forget | Auth tokens expire | Auth tokens expire |
The only scenario where Zapier wins: if you need complex multi-step workflows (alert → log + send Telegram + update Notion + email). For that, sure, pay for Zapier. For a simple journal? Apps Script all day.
Scaling: Multiple Strategies, One Sheet
If you run multiple strategies (like I do — USDJPY momentum on the daily chart, plus crypto swing trades on 4H), use the "strategy" field to differentiate:
{
"ticker": "{{ticker}}",
"action": "{{strategy.order.action}}",
"price": "{{close}}",
"timeframe": "{{interval}}",
"strategy": "USDJPY Momentum",
"message": "{{ticker}} {{strategy.order.action}} at {{close}}"
}
Then in Google Sheets, use Filter Views or a pivot table to analyze each strategy separately. You can also modify the Apps Script to write to different sheet tabs based on the strategy name:
var sheetName = data.strategy || 'Default';
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
if (!sheet) {
sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(sheetName);
sheet.appendRow(['Timestamp', 'Ticker', 'Action', 'Price', 'Timeframe', 'Strategy', 'Message', 'Notes']);
}
Each strategy gets its own tab automatically. Clean.
What This Setup Looks Like in Practice
After running this for my USDJPY momentum strategy, here's what a typical week looks like in the sheet:
| Timestamp | Ticker | Action | Price | Timeframe | Strategy |
|---|---|---|---|---|---|
| 2026-03-17 08:00 | USDJPY | buy | 149.230 | D | Momentum |
| 2026-03-17 14:30 | BTCUSDT | buy | 83450.00 | 240 | Swing |
| 2026-03-18 02:15 | BTCUSDT | sell | 84120.00 | 240 | Swing |
FAQ
Can I use this with TradingView's free plan?
No. Webhooks require the Essential plan ($12.95/month) or higher. You can set one alert on the free plan, but without the webhook URL option. If you're serious about automated journaling, the Essential plan is the minimum.
Will this work with OKX or other exchange alerts?
This setup catches any TradingView alert with a webhook — it doesn't matter what exchange your chart data comes from. If you're trading on OKX and want to go further (actually execute trades from alerts, not just log them), check out my OKX Signal Bot setup guide.
How many alerts can I log per day?
Google Apps Script has a daily quota of about 20,000 URL fetches for webhook receives via doPost. Unless you're running hundreds of 1-minute alerts, you'll never hit it.
Can I add charts or screenshots to the journal?
Not automatically from TradingView webhooks — they only send text data. But you can add a Google Sheets column for screenshot URLs and paste TradingView chart links manually.
What if Google Apps Script goes down?
It's Google infrastructure — uptime is essentially 99.9%+. I've been running this for months without a single missed alert. If you want redundancy, set up a second webhook endpoint in the same TradingView alert.
Next Steps
Once your journal is running:
1. Review weekly — Look for patterns. Which strategy wins? Which timeframe? Which session (Asian/London/NY)?
2. Add conditional formatting — Color-code winning trades green, losing trades red 3. Build a dashboard — Use Google Sheets charts to visualize your equity curve, win rate, and average R:R 4. Connect to your broker — If you're using TradingView connected to OKX, you can log both the signal AND the execution in the same journalThe best trading journal is the one you actually use. Automating the boring part (data entry) means you spend your time on the useful part (analysis).
Try TradingView — webhooks require the Essential plan or higher.