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.

LTG input-sheet trade records

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.

Completed time-driven portfolio refresh executions

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;}
LTG holdings with portfolio, side, weight, entry and current marks

Public Reporting

Publish trade status and allocation views to the LTG website.