all workF̶aramarznia
iranbroker newtab

Ninety-six commits, seven and a half weeks, one person. I built the whole thing, then audited it like someone else had.

Scroll: the ledger reconciles.

prj — 004

overview

Iran Broker New Tab is a Chrome extension that replaces the browser’s empty new-tab page with a working dashboard for Persian-speaking traders: four live markets, an economic calendar, a trading journal with real analytics, a focus timer, and an AI assistant called Aira.

I did all of it — the idea, the product decisions, the interface, the visual design, every line of the code, the QA, the documentation, and the Chrome Web Store package. Claude Code was the tool I built it with, the way a designer uses Figma; nobody else made a decision on this product.

Below is what I built, how I built it alone, the bug that nearly invalidated the whole journal, and the single problem that is still keeping it off the store.

role
Founder — idea, design and build
timeline
27 Jun — 18 Aug 2026 · 96 commits
team
Solo, end to end (built with Claude Code)
FounderChrome ExtensionProduct DesignFintech
01

Why I built it

I trade, and I kept six tabs open to do it: prices on one site, the economic calendar on another, a journal in a spreadsheet, a Pomodoro timer on my phone, the forum somewhere else again. None of them talked to each other, and every one of them cost a deliberate act of navigation.

Meanwhile the one page a browser guarantees you will see dozens of times a day — the new tab — was blank. That asymmetry is the whole idea. I was not trying to build a trading platform; I was trying to put the five things I already checked onto the surface I already opened.

The audience made it sharper. Persian-speaking traders get English-first tools with a right-to-left afterthought bolted on, and the data they actually care about — the Tehran Stock Exchange, the free-market dollar rate, the coin price — is exactly the data those tools do not carry. Nobody was going to build this for them, so the product had to be Persian-first in its layout, its calendar, its typography and its data sources, not translated into Persian at the end.

02

What I built

Everything below runs in a single page with no build step: plain HTML, CSS and JavaScript, nine independent scripts, no npm, no bundler, no ES modules. Nothing is a framework component; every widget owns its own `localStorage` key and fails on its own without taking the page down.

That constraint was a decision, not an accident. A new tab page has to paint before the user has finished pressing the keyboard shortcut. Anything that needs a bundle to boot has already lost.

the product

Nine things that had to fit on one page

Every screenshot below is the extension running, captured from its own source.
  1. The new tab: clock, Persian greeting, search bar with engine switcher, and the tool dock
    01

    The page itself

    A Jalali date, a greeting that uses your name and the time of day, the clock, and the one line that matters to a trader before anything else: which session is open right now, and in whose time. The search bar routes to Google or straight into Iran Broker’s own tools, and the dock in the corner opens the journal, the theme, settings and focus mode without leaving the page.

    newtab.js · first contentful paint measured at 296ms, independent of every API

  2. The three-column dashboard with live prices, the economic calendar and the article feed
    02

    Four markets, one column

    Crypto, then forex with gold, oil and the dollar index, then Iran’s free market — dollar, gold, coin — and then the Tehran Stock Exchange. Four tabs over one panel, refreshed every ninety seconds, each row carrying its own sparkline. The Iranian data is the part no international tool ships, and it is the reason the product exists here rather than anywhere else.

    coingecko · yahoo finance · call2/call3.tgju.org with failover · cdn.tsetmc.com

  3. Hub mode: today’s tasks, the Jalali calendar with economic events, and an hourly weather widget
    03

    Hub — the day, not the market

    A second layout for the same page. The price columns give way to a Jalali calendar with the week’s economic events marked on it, an hourly weather widget, today’s task list, and a quick-access grid you fill with your own links. The clock, the search and the identity do not move — the page changes what it is about without changing where anything is.

    js/hub.js · forex factory weekly feed · open-meteo · 1,527 lines

  4. The trading journal entry form, in quick mode, with direction, prices, stop, target and lot size
    04

    The journal — logging a trade

    Two modes: quick, for the fields you always fill, and full, for everything else. Symbol, direction, entry, exit, stop loss, take profit, lot size, commission — and the field most journals leave out, how you felt before you opened the position. Trades import from an MT4 history file and export as CSV that opens correctly in a Persian Excel, which sounds trivial until it is your data stuck in the wrong encoding.

    js/journal.js · MT4 CSV import · localStorage key ib_journal_v1

  5. Journal analytics: win rate, profit factor, average R:R, expectancy, max drawdown and the equity curve
    05

    The journal — what the numbers say

    Nothing here is typed twice. Win rate, profit factor, average R:R, mathematical expectancy, max drawdown, the longest streak, best and worst trade, and the equity curve are all derived from the trades you logged, filtered by any window from this week to all time. This is the screen that has to be arithmetically right, and the one place this project failed hardest before it passed.

    derived stats · equity curve on canvas · R:R distribution histogram

  6. The psychology tab: emotion against performance, and a daily journal heatmap
    06

    The journal — the part I have not seen elsewhere

    Every trade carries the emotion you recorded before entering it, and this tab puts that against the outcome: calm, anxious, excited, confident, each with its own count, win rate and average P&L. Beside it, a calendar heatmap of the days you actually wrote something down. It is the cheapest feature in the product to build and the one most likely to change how somebody trades.

    emotion tagging at entry · daily note heatmap · behaviour patterns

  7. Aira, the Persian AI assistant, answering a question about choosing a forex broker
    07

    Aira — answers in Persian, streamed

    A chat assistant for the questions a new trader actually asks: which broker, what spread, what a prop firm is, how to size a position. The answer streams token by token so you can read it as it arrives, past conversations are listed down the side, and a lock button in the header turns on private mode — the message still goes to the model to get an answer, but nothing is written to local storage.

    js/airo.js · streamed responses · private mode key ib_airo_no_history

  8. Focus mode: a full-screen Pomodoro ring, ambient sound controls and session dots
    08

    Focus mode

    The whole tab becomes a timer: Pomodoro or a trading-session length, an SVG progress ring, ambient sound, session dots tracking the cycle, and keyboard control — space to pause, F for full screen, Escape to leave. It spends the extension’s only Chrome permission, notifications, on exactly one thing: telling you the session ended when the tab is not in front of you.

    js/focus.js · web audio · the one permission in manifest.json

  9. The settings data tab: backup and restore, with an in-panel confirmation before overwriting
    09

    Settings, and getting your data out

    Four themes including a glass mode, four accent colours, and a background picker backed by live Pexels photography across six trading-related categories, cached for a day so a shared key is not burned in an afternoon. The data tab exports every `ib_` key to a JSON file on your machine and restores from one — no network, no server, no account. It rejects foreign keys in both directions and asks for confirmation inside the panel before it overwrites anything.

    backup covers all 14 ib_* keys · no chrome.storage anywhere in the code

03

How I built it alone

Ninety-six commits between 27 June and 18 August 2026, and the shape of that history is the honest part. On day one I built a modular architecture with twenty-one feature modules. Seven weeks later I deleted all twenty-one, because I found they had never been loaded — the page ran on a completely different set of files the whole time. I also built and removed an embedded Telegram sidebar, a widget edit mode with drag-and-resize, and a forex news ribbon. Roughly a fifth of what I wrote never shipped.

Working with Claude Code made me fast, and fast is exactly how you end up with a twenty-one-module architecture nobody loads. So in the last stretch I changed what I was asking for. Instead of more features I asked for an audit, and I gave it one rule: the code is the source of truth, not the documentation, and not what I remembered deciding. Every document in the repo got rewritten from a direct reading of the live code, with that rule printed at the top.

The audit found the dead module tree, four domains in `host_permissions` with no caller left, a permission requested with zero call sites, and twelve of sixteen fetch calls with no timeout — able to hang in a loading state forever, which on an unstable Iranian connection is a normal afternoon. Then I ran seven QA tests against a real browser through Puppeteer, not by reading the source: network conditions emulated over CDP, all eleven storage keys deliberately corrupted, both market-data hosts blocked at the network layer. Four passed, two failed, one passed partially, and the tools are checked into the repo so the results can be reproduced rather than believed.

the build

Seven and a half weeks, and what did not survive

96 commits, one author. Roughly a fifth of the work was later deleted — on purpose.
  1. 27 JunInitial commit, then a three-column dashboard, a WebGL shader background and a modular architecture of 21 feature modules — all on day one.
  2. 28–29 JunFocus mode built, then rebuilt from scratch the same day. Settings modal, community sidebar, a Telegram sidebar added and reverted within hours. The journal arrives.
  3. 30 Jun – 12 JulHub mode, the first-run tour, the articles sidebar, the four price tabs, Aira. Widget edit mode is built — and then removed.
  4. 15 Jul – 11 AugPolish and performance: shader GPU cost, lazy hub data, Jalali calendar, redesigned weather. A forex news ribbon ships and is reverted.
  5. 15–18 AugThe audit. Dead module tree deleted, QA suite run, store package written, docs resynced against the code, P&L bug fixed, backup/restore added.
  • 96commits
  • 21modules built on day one, deleted in week seven
  • 3features shipped and then removed
  • 1author
the find

The number that nearly invalidated the journal

symbolBTCdirectionlongentry61,200exit63,000lot0.10
what the journal showed+$0.00

One function assumed every symbol was a forex pair, so a $1,800 Bitcoin move became eighteen million “pips”. That single figure poisoned every statistic on the analytics tab — best trade, total P&L, profit factor, the equity curve. I found it while seeding fake trades for an unrelated right-to-left screenshot test, not while looking for it.

the audit

Seven tests, run in a real browser

Four passed, two failed, one partially. The failures are why it was worth running.
  1. 1Visual regression at three widthspass
  2. 2RTL reviewfail
  3. 3API outage simulationpartial
  4. 4Timeout / AbortController auditfail
  5. 5localStorage integritypass
  6. 6Clean installpass
  7. 7First-paint timingpass
  • 21dead files — the module tree I wrote on day one
  • 4domains with no caller left in the code
  • 1permission requested and never used
  • 4.8 MBof bundled images, replaced by a live picker
04

Outcome

The extension is finished, documented, packaged for the Chrome Web Store, and deliberately unpublished. Everything a store reviewer asks for exists — privacy policy, permission justifications, data-disclosure form, MV3 compliance checklist, store copy, five screenshots — and it is still sitting there, because of one thing I have not fixed.

  • 96 commits, 0 → 100idea, design, code, QA, docsevery decision and every line mine, built with Claude Code over seven and a half weeks
  • 1 permission, 13 domainsthe access surfaceonly notifications, spent entirely on the focus-session alert; every other origin blocked by CSP
  • not shippedthe decision I stand behinda hardcoded paid API key blocks release; publishing anyway would put an extractable key inside every install
what i'd do differently

Building alone with an AI that never gets tired removes the two things that used to slow me into good decisions: the cost of writing code, and somebody asking why. I shipped a twenty-one-module architecture on day one because it was cheap to write, and I did not notice for seven weeks that the browser never loaded it. Nothing forced me to look. What fixed it was not discipline, it was changing the instruction — asking for an audit against the code instead of asking for the next feature, and writing “the code is the source of truth” at the top of every document so that future me could not quietly believe the old story. I now schedule that audit rather than waiting for a suspicion, and I treat speed as the thing that needs a counterweight, not as the achievement.

still openP0 · REQ-1

Why it is not in the Chrome Web Store

Aira’s API key is hardcoded into code that runs in the user’s browser, where anyone can read it out of devtools — and it is a metered, paid key. Fixing it properly means putting a backend proxy in front of it, which is a different project from this one. So the package sits finished and unpublished. That is the honest state of it, and I would rather write that here than ship an extractable key inside every install.