LTG Trading & Reporting System
Built the workflow connecting trade intake, validation, price sourcing, portfolio state and public reporting for Laurier Trading Group.
Google Forms · Google Sheets · Apps Script / JavaScript · Yahoo Finance · Stooq · Squarespace
Trade Intake
Capture instrument, direction, entry and portfolio fields in a structured record.
Event Engine
Normalize requests, enforce allocation rules and write lifecycle events.
normalized.actionType = normalizeEventAction_(normalized.actionType);normalized.dataSource = normalizeDataSourceChoice_(normalized.dataSource);normalized.side = normalizeSide_(normalized.side);normalized.portfolio = normalizePortfolio_(normalized.portfolio);normalized.assetType = toUpperSafe_(normalized.assetType || 'OTHER');normalized.entryPrice = toNumber_(normalized.entryPrice);…const validation = validateTradeEventAgainstState_(normalized, stateMap, config);result.validation = validation;if (!validation.ok) { normalized.validationStatus = 'REJECTED'; normalized.validationMessage = validation.message; normalized.statusAfterEvent = ''; appendTradeEvent_(normalized); result.normalizedEvent = normalized; return result;}…const statusAfter = applyEventToStateMap_(normalized, stateMap);normalized.statusAfterEvent = statusAfter;normalized.validationStatus = 'ACCEPTED';normalized.validationMessage = normalized.validationMessage || 'OK';appendTradeEvent_(normalized);Quote Routing
Map instruments to quote sources and fall back to manual marks where needed.
Pricing adapters
Yahoo Finance · Stooq · Twelve Data · MarketData.app · Manual marks
let chosen = null;order.some(function (provider) { if (provider === LTG.DATA_SOURCE.MANUAL) return false; const sourceMap = providerMaps[provider] || {}; for (var i = 0; i < candidates.length; i += 1) { const symbol = toUpperSafe_(candidates[i]); const providerSymbol = mapCandidateForProvider_(provider, symbol); if (!providerSymbol) { attemptNotes.push(provider + ':' + symbol + '=UNSUPPORTED'); continue; } const res = sourceMap[toUpperSafe_(providerSymbol)]; if (!res) { attemptNotes.push(provider + ':' + providerSymbol + '=no-response'); continue; } if (res.status === LTG.PRICE_STATUS.OK && res.price !== null) { … return true; } …Scheduled Refresh
Run scheduled portfolio refreshes and retain execution history.
Portfolio State
Rebuild holdings and calculate trade-level state from accepted events and marks.
function determineNotional_(trade, config) { const explicitNotional = toNumber_(trade.notionalUSD); if (explicitNotional && explicitNotional > 0) return explicitNotional; const alloc = toNumber_(trade.allocPct); const allocUsed = alloc && alloc > 0 ? alloc : getDefaultAllocPct_(config); const capital = getPortfolioCapital_(trade.portfolio, config); return capital * allocUsed;}function computeSingleLegPnLPct_(side, entry, current) { if (entry === null || current === null || entry === 0) return null; if (toUpperSafe_(side) === 'SHORT') { return (entry - current) / entry; } return (current - entry) / entry;}
Public Reporting
Publish trade status and allocation views to the LTG website.



