<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[FÉLAG | Haven]]></title><description><![CDATA[The home port of the FÉLAG trading community. This publication documents the systematic approach to markets: backtests, code architecture, risk management frameworks, tax reporting, and real results. No magic, no predictions. Only craftsmanship, data, and an honest course.

All materials are strictly educational and do not constitute financial advice or a call to action.]]></description><link>https://felag.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6acf5bef2b8eed7aae23fc1b/dad7b7b3-5e1b-42ff-b13e-4aff41cea491.png</url><title>FÉLAG | Haven</title><link>https://felag.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:23:59 GMT</lastBuildDate><atom:link href="https://felag.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A Week Building a FinTech Blog from Scratch]]></title><description><![CDATA[How we built a multilingual trading blog with automated validation, 7 pre-commit checks, and full RU/EN parity in one week.]]></description><link>https://felag.hashnode.dev/a-week-building-a-fintech-blog-from-scratch</link><guid isPermaLink="true">https://felag.hashnode.dev/a-week-building-a-fintech-blog-from-scratch</guid><category><![CDATA[Astro]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Devops]]></category><category><![CDATA[fintech]]></category><dc:creator><![CDATA[Vasiliy]]></dc:creator><pubDate>Fri, 06 Nov 2026 18:59:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6acf5bef2b8eed7aae23fc1b/071d1114-cc59-4259-b885-959a0ac4ef4f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p>When we started building FÉLAG — a multilingual blog about algorithmic trading, cryptocurrency, and automation — we knew we needed a solid foundation. Not just for content, but for the entire engineering workflow.</p>
<p>Here's what we accomplished in the last week, and the technical decisions behind each choice.</p>
<h2>The Goal</h2>
<p>Build a blog that:</p>
<ul>
<li>Supports RU and EN with strict parity</li>
<li>Has automated quality checks before every commit</li>
<li>Delivers seamless UX (smooth transitions, scroll restoration, adaptive layout)</li>
<li>Is optimized for SEO and accessibility</li>
</ul>
<h2>Foundation and CI/CD</h2>
<h3>Automated Validation Pipeline</h3>
<p>We created <code>npm run verify</code> — a script that runs 5 checks before every build:</p>
<ol>
<li><strong>Build project</strong> — catches compilation errors</li>
<li><strong>Check JSON-LD</strong> — prevents raw <code>JSON.stringify</code> in structured data</li>
<li><strong>Check duplicate paths</strong> — avoids 404s from duplicate routes</li>
<li><strong>Check NUL bytes</strong> — prevents Markdown parsing errors</li>
<li><strong>Check internal links</strong> — catches broken links early</li>
</ol>
<p>This script is now part of our pre-commit hooks, blocking any commit that fails validation.</p>
<h3>Multilingual Parity</h3>
<p>One of the biggest challenges was ensuring RU and EN versions stay in sync. We implemented a compile-time guard in <code>i18n/ui.ts</code>:</p>
<pre><code class="language-typescript">type LocaleParity = Record&lt;keyof (typeof ui)['ru'], string&gt;;
const _enParity: LocaleParity = ui.en;
</code></pre>
<p>This TypeScript trick forces the compiler to check that every key in <code>ru</code> exists in <code>en</code>. If someone adds a new translation key to Russian but forgets English, the build fails.</p>
<h2>UX and Performance</h2>
<h3>Scroll Restoration Across Languages</h3>
<p>When users switch languages, they expect to stay at the same position on the page. We implemented this using <code>sessionStorage</code> and <code>requestAnimationFrame</code>:</p>
<pre><code class="language-typescript">// Save scroll position before navigation
sessionStorage.setItem('scrollY', String(window.scrollY));

// Restore after new page renders
requestAnimationFrame(() =&gt; {
  const savedScroll = sessionStorage.getItem('scrollY');
  if (savedScroll) window.scrollTo(0, parseInt(savedScroll));
});
</code></pre>
<h3>Adaptive Grid System</h3>
<p>We built a responsive layout that works from mobile drawer menus to desktop sticky sidebars:</p>
<ul>
<li><strong>Desktop</strong>: Fixed sidebar with categories + 2-column grid</li>
<li><strong>Mobile</strong>: Compact drawer menu + single column</li>
<li><strong>Tablet</strong>: Adaptive breakpoints at 768px and 1024px</li>
</ul>
<h2>Technical Debt Cleanup</h2>
<p>We ran a comprehensive pre-release audit and fixed 14 issues:</p>
<h3>Critical Fixes</h3>
<ul>
<li><strong>FallbackImage import</strong>: Fixed ReferenceError in OG image generation by using static path from <code>/public/</code></li>
<li><strong>Deduplication logic</strong>: Changed <code>.has()</code> to <code>.add()</code> in Set — the deduplication was silently failing</li>
</ul>
<h3>SEO Improvements</h3>
<ul>
<li>Removed query parameters from breadcrumbs (<code>?categories=crew</code> → clean <code>/ru/saga/</code>)</li>
<li>Added rehype plugin for automatic external link handling (class, target, rel, SVG icon)</li>
</ul>
<h3>Accessibility (A11y)</h3>
<ul>
<li>Added <code>aria-hidden="true"</code> to 4 decorative SVG icons</li>
<li>Added <code>aria-label</code> to email input and consent banner buttons</li>
</ul>
<h3>Code Cleanup</h3>
<ul>
<li>Removed 6 dead image files (~189 KB)</li>
<li>Removed dead <code>astro:before-swap</code> event listeners (View Transitions not enabled)</li>
<li>Fixed duplicate <code>addEventListener</code> on search input</li>
</ul>
<h2>Lessons Learned</h2>
<ol>
<li><strong>Automate early</strong>: The <code>npm run verify</code> script saved us hours of manual checking</li>
<li><strong>TypeScript guards are powerful</strong>: The locale parity check caught missing translations before they reached production</li>
<li><strong>Scoped CSS can be tricky</strong>: Astro's scoped styles in components can override global CSS in unexpected ways</li>
<li><strong>Building in public builds trust</strong>: Sharing our process attracts collaborators who value transparency</li>
</ol>
<h2>What's Next</h2>
<p>Now that the foundation is solid, we're moving to content:</p>
<ul>
<li>Trading bot configuration breakdowns</li>
<li>Real trading system analysis</li>
<li>Risk management and mathematical expectation</li>
<li>Mistake decomposition</li>
</ul>
<h2>Resources</h2>
<ul>
<li><a href="https://felag.online/en/saga/behind-the-scenes-architecture/?utm_source=hashnode&amp;utm_medium=blog&amp;utm_campaign=haven_en"><strong>Full article</strong></a></li>
<li><a href="https://t.me/felag_logbook_en"><strong>Telegram channel</strong></a></li>
<li><a href="https://felag.online/en/about/"><strong>Support options</strong></a></li>
<li><a href="https://tradermake.money/trader/Felag"><strong>Trading Journal</strong></a></li>
<li><a href="https://felag.online"><strong>Blog</strong></a></li>
</ul>
<hr />
<p><em>This article is part of our "Building in Public" series. We share our engineering decisions, mistakes, and learnings as we build FÉLAG — a systematic approach to algo-trading.</em></p>
]]></content:encoded></item></channel></rss>