<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mystique Racing</title>
    <description>The latest articles on DEV Community by Mystique Racing (@mystiqueracing).</description>
    <link>https://dev.to/mystiqueracing</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4159653%2Ffabf2b1b-7648-48ca-acb5-ebd11263d183.png</url>
      <title>DEV Community: Mystique Racing</title>
      <link>https://dev.to/mystiqueracing</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mystiqueracing"/>
    <language>en</language>
    <item>
      <title>HKAT Placement Exam Chinese: The 7 Questions Hong Kong P5-P6 Parents Always Ask</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Wed, 14 Oct 2026 05:28:10 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/hkat-placement-exam-chinese-the-7-questions-hong-kong-p5-p6-parents-always-ask-4mnb</link>
      <guid>https://dev.to/mystiqueracing/hkat-placement-exam-chinese-the-7-questions-hong-kong-p5-p6-parents-always-ask-4mnb</guid>
      <description>&lt;p&gt;每一年九月開學後，小五和小六家長群裡出現頻率最高的三個詞一定是：呈分試、調分、自行收生。而圍繞這三個詞的傳聞，一半以上是錯的。我們在做 &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;LUMI Chinese Institute&lt;/a&gt; 的呈分試對標題庫時，把教育局與考評局公開文件重新讀了一遍，這篇文章把家長最常問的七個問題一次講清楚。&lt;/p&gt;

&lt;h2&gt;
  
  
  1. 呈分試是「一次考試」嗎？——不是，是三次的加權平均
&lt;/h2&gt;

&lt;p&gt;俗稱的「呈分試」正式名稱是校內評估呈報機制：學校把小五下學期、小六上學期、小六下學期三次主要考試的成績，按教育局指引呈報，用於統一派位的組別劃分（Banding）。也就是說：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;它不是一場「一考定生死」的考試，而是&lt;strong&gt;三個學年的橫切面&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;小五下學期的第一次呈分，權重與小六兩次相同——很多家長到小六才開始緊張，已經輸了三分之一；&lt;/li&gt;
&lt;li&gt;三次成績經學校調整後取平均，單次失手有緩衝，但持續下滑會直接壓低組別。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;實務結論：呈分試備考從小五年級就開始，而不是小六衝刺。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 「調分」到底是什麼？——學校之間的橫向校準
&lt;/h2&gt;

&lt;p&gt;呈分試成績呈報後，教育局會按全港統一的統計方法，對不同學校的成績分佈做橫向校準（俗稱調分）：如果某校整體偏嚴，該校學生的分數會被適度上調；反之亦然。&lt;/p&gt;

&lt;p&gt;這意味着兩件事：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;校內排名比分數本身重要。&lt;/strong&gt; 90 分在嚴格學校的價值，可能高於 95 分在寬鬆學校；&lt;/li&gt;
&lt;li&gt;你無法透過「揀學校容易攞高分」來投機，調分機制就是為了消滅這種套利。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. 中文卷在呈分試裡的實際權重——與英文、數學同等
&lt;/h2&gt;

&lt;p&gt;三主科（中、英、數）在呈分中的權重相同，各佔一份。但中文科的&lt;strong&gt;分數離散度通常最大&lt;/strong&gt;：閱讀理解的開放式題、寫作卷的字數與結構評分，都讓中文科成為拉開組別距離的科目。換句話說，數學難滿分，中文易失手——失手的成本是一樣的。&lt;/p&gt;

&lt;h2&gt;
  
  
  4. 中文卷扣分大戶：閱讀開放題＋寫作卷
&lt;/h2&gt;

&lt;p&gt;我們審計過自己的題庫後（見&lt;a href="https://lumi-chinese.com/blog/hong-kong-tsa-chinese-exam-analysis" rel="noopener noreferrer"&gt;上一篇 TSA 實測分析&lt;/a&gt;），發現本地班呈分試題型的三大規律：&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;題型&lt;/th&gt;
&lt;th&gt;扣分原因&lt;/th&gt;
&lt;th&gt;佔失分比重（我們題庫審計估算）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;閱讀開放式題（為什麼/你認為）&lt;/td&gt;
&lt;td&gt;只答事實、不答因果&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;寫作卷&lt;/td&gt;
&lt;td&gt;離題、字數不足、段落無結構&lt;/td&gt;
&lt;td&gt;高&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;語基（標點/重組/擴寫）&lt;/td&gt;
&lt;td&gt;硬性規則失誤&lt;/td&gt;
&lt;td&gt;中，但最易補&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;語基題是「付出＝回報」最確定的部分：規則有限、可刷、可可驗證。寫作與開放題則需要長期訓練因果表達——這正是我們把 AI 批改重點放在「為什麼」類題目的原因。&lt;/p&gt;

&lt;h2&gt;
  
  
  5. 各校自行收生 (自行分配學位) 看呈分嗎？——看，但不止看
&lt;/h2&gt;

&lt;p&gt;自行收生階段（每年一月）學校自行面試與收生，會參考校內成績，但面試表現、課外紀錄、甚至「該校畢業生子女」等因素各校自定。呈分成績此時尚未完成全港校準，學校看到的是校內成績單。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;結論：自行收生是另一場比賽，不要把呈分試當作它的全部。&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. 補習刷題有用嗎？——對語基有效，對寫作可疑
&lt;/h2&gt;

&lt;p&gt;語基題是規則集合，刷題有效。閱讀開放題與寫作靠的是因果表達能力，純刷題容易練成「答題模板」，而呈分試閱卷老師恰恰是模板辨識器。有效的訓練是：每題強迫學生先說「因為」，再說結論——因果鏈斷了，扣分比空着更慘。&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 小五小六各階段該做什麼？
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;小五上/下&lt;/strong&gt;：語基規則全清＋閱讀習慣（每天一篇，答對「為什麼」才收工）；&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;小六上&lt;/strong&gt;：限時寫作訓練（結構優先於修辭），開放題因果表達糾偏；&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;小六下&lt;/strong&gt;：全卷模擬＋錯題歸因，把失分類別統計出來，只補前三類。&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;呈分試的本質是三年的學習習慣被壓縮成三組數字。我們在 LUMI 把這套「語基規則化＋因果表達訓練＋錯題歸因」做成了 AI 導師的內建流程（&lt;a href="https://lumi-chinese.com/pricing" rel="noopener noreferrer"&gt;免費 7 日體驗&lt;/a&gt;），因為它正是機器最擅長、家長最難親自做的那部分：逐題追問「為什麼」。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;本文為教育研究與資訊用途，升學政策細節以教育局官方公佈為準。&lt;/em&gt;&lt;/p&gt;

</description>
      <category>education</category>
      <category>hongkong</category>
      <category>chinese</category>
      <category>edtech</category>
    </item>
    <item>
      <title>We Built an AI English Tutor for Hong Kong's Most Stressful Exam: the School Interview</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Tue, 13 Oct 2026 04:27:28 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/we-built-an-ai-english-tutor-for-hong-kongs-most-stressful-exam-the-school-interview-23l5</link>
      <guid>https://dev.to/mystiqueracing/we-built-an-ai-english-tutor-for-hong-kongs-most-stressful-exam-the-school-interview-23l5</guid>
      <description>&lt;p&gt;Every Hong Kong parent knows the ritual. Somewhere between age 4 and 6, your child sits across a table from a stranger who asks "Tell me about your family" — and the next five years of their education depend on the answer.&lt;/p&gt;

&lt;p&gt;International school interviews in Hong Kong are brutal, and the English portion is where most local families stumble. Tutoring costs HK$400–800 per hour. The tutor sees your kid once a week for 45 minutes. The rest of the week? Nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://bennettenglish365.com" rel="noopener noreferrer"&gt;Bennett English&lt;/a&gt; is our attempt to fix the economics of this. It's an AI English tutor built specifically for the Hong Kong context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interview practice with an AI interviewer&lt;/strong&gt; — the same "tell me about yourself / your family / your favorite book" questions real interviews use, with follow-up questions that adapt to what the child actually said&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pronunciation feedback&lt;/strong&gt; — the AI listens and points out specific sounds that drifted, instead of just saying "good job"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IB and international school writing tracks&lt;/strong&gt; — not generic ESL, but the genres HK international schools actually grade: show-and-tell, guided reading responses, and eventually the IB-style analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short sessions on purpose&lt;/strong&gt; — 10–15 minutes, because a 5-year-old's attention span is not a productivity problem, it's a design constraint&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;We're a small Hong Kong team. This is not a magic box. The AI interviewer does not replace a great human tutor — a great human tutor reads a child's mood in ways no model can. What it replaces is &lt;em&gt;nothing happening on the six days between tutoring sessions&lt;/em&gt;, and &lt;em&gt;the HK$800/hour for families who can't afford it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;There is a free trial. No credit card, no "cancel anytime" dark patterns — you claim it with an email, it runs out in 7 days, and if it's not for you, that's the end of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://bennettenglish365.com" rel="noopener noreferrer"&gt;Bennett English&lt;/a&gt; — free trial, built in Hong Kong, for Hong Kong families.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(We also build &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;LUMI Chinese&lt;/a&gt; for Chinese learning and &lt;a href="https://lumimath.net" rel="noopener noreferrer"&gt;LumiMath&lt;/a&gt; with ADHD-friendly math practice — same team, same philosophy.)&lt;/em&gt;&lt;/p&gt;

</description>
      <category>education</category>
      <category>ai</category>
      <category>english</category>
      <category>hongkong</category>
    </item>
    <item>
      <title>Why ADHD and Dyscalculic Kids Need Animated Math (And How We Built It)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:22:22 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/why-adhd-and-dyscalculic-kids-need-animated-math-and-how-we-built-it-2lib</link>
      <guid>https://dev.to/mystiqueracing/why-adhd-and-dyscalculic-kids-need-animated-math-and-how-we-built-it-2lib</guid>
      <description>&lt;h1&gt;
  
  
  Why ADHD and Dyscalculic Kids Need Animated Math
&lt;/h1&gt;

&lt;p&gt;If you've watched a child stare at a page of fractions and genuinely not "see" what a half means, you know the problem isn't effort. It's representation. Textbook math asks kids to build a mental movie from static symbols. For learners with dyscalculia or ADHD, that movie never loads — and the lesson collapses into "I'm just bad at math."&lt;/p&gt;

&lt;p&gt;After building LUMI (Chinese learning) and watching how differently kids engage when concepts &lt;em&gt;move&lt;/em&gt; and &lt;em&gt;speak&lt;/em&gt;, we built LumiMath (lumimath.net) around three deliberate mechanisms. No learning-science jargon below — just what's actually in the product and why.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Content-driven animations, not decoration
&lt;/h2&gt;

&lt;p&gt;Every unit has a concept animation built &lt;em&gt;for that specific concept&lt;/em&gt;, in SVG + CSS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clock problems&lt;/strong&gt;: an animated clock where you literally watch the hour hand crawl while the minute hand races — "the hour hand is slow" stops being something to memorize and becomes something you've seen&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probability&lt;/strong&gt;: tree diagrams that grow branch by branch, so P(A and B) is a path you trace, not a formula you squint at&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measurement&lt;/strong&gt;: an on-screen ruler you slide along the shape's edge, then check the computed area against what you measured&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key rule we follow: the animation must be &lt;strong&gt;driven by the content of the question&lt;/strong&gt;. Random spinning shapes are worse than nothing — they compete for the same attention the child is trying to spend on the math. If the diagram doesn't explain &lt;em&gt;this&lt;/em&gt; problem, it doesn't ship.&lt;/p&gt;

&lt;p&gt;This is the classic "number-shape combination" (數形結合) principle from Chinese math pedagogy — pair every abstract symbol with its concrete visual. Dyscalculic learners often can't hold the symbolic version in working memory; the animated concrete version does the holding for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One question at a time: focus mode as an accessibility feature
&lt;/h2&gt;

&lt;p&gt;A standard exam page is an attention massacre for an ADHD kid: 20 questions, all visible, deadlines ticking, distractors everywhere. Our focus mode does one thing ruthlessly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Show exactly one question. The next one doesn't exist yet.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Completion becomes a series of tiny wins instead of a wall. Combined with per-question voice readout (the child doesn't have to grind through text they could misread), a "can't sit still" kid can get through a session that would previously have ended in tears at question 3.&lt;/p&gt;

&lt;p&gt;There's a related design choice worth stealing: &lt;strong&gt;graceful degradation of the daily streak&lt;/strong&gt;. When a kid says they're exhausted, Mr. Lumi (the AI teacher) offers a 5-minute fallback — just 10 vocabulary terms — instead of breaking the streak or guilt-tripping. The stance is explicit in the dialogue: "Being tired is allowed. Quitting is not." Small commitments preserve momentum; shame destroys it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Voice explanations on every mistake
&lt;/h2&gt;

&lt;p&gt;Wrong answers trigger three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Immediate voice explanation of &lt;em&gt;that specific question&lt;/em&gt; — not a canned "try again"&lt;/li&gt;
&lt;li&gt;The error goes into a &lt;strong&gt;weakness log&lt;/strong&gt; that's tracked long-term, per knowledge point&lt;/li&gt;
&lt;li&gt;The same point is re-tested in following days until it "graduates" 🎓&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The parent dashboard angle matters here: ADHD parents are often drowning in school communication that says &lt;em&gt;what&lt;/em&gt; went wrong, never &lt;em&gt;why&lt;/em&gt; or &lt;em&gt;what next&lt;/em&gt;. A weakness tracker that says "your child has missed the 'divide by 2 in triangle area' step three times this month" turns a vague worry into a 10-minute intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why voice matters more than you'd think
&lt;/h2&gt;

&lt;p&gt;Text-first interfaces quietly filter out kids who read below grade level — which over-includes exactly the population (dyslexia comorbidity, ADHD, language learners) that needs math help most. LumiMath reads questions aloud, explains aloud, and takes voice input for vocabulary spelling practice. The same 365-day structure (90 minutes daily, level-up every 8 weeks, F1 through DSE M2 coverage) becomes reachable for kids who would never survive a text-only version of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest version
&lt;/h2&gt;

&lt;p&gt;Animations don't replace practice. Voice doesn't replace a human teacher who notices a bad day. What they do is remove the &lt;em&gt;representation tax&lt;/em&gt; — the extra cost that conventional materials impose on kids whose brains process differently. We'd rather build for those kids first and let the "typical" learners ride along (they do, happily) than build for the median child and bolt on "accessibility" later.&lt;/p&gt;

&lt;p&gt;If you're a parent of an ADHD or dyscalculic learner in Hong Kong, try it free for a day: &lt;a href="https://lumimath.net" rel="noopener noreferrer"&gt;lumimath.net&lt;/a&gt;. English learners, we built the same 365-day system with daily dictation and pronunciation scoring at &lt;a href="https://bennettenglish365.com" rel="noopener noreferrer"&gt;bennettenglish365.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Every feature described above exists in the shipped product today — no roadmap promises.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>education</category>
      <category>ai</category>
      <category>a11y</category>
      <category>product</category>
    </item>
    <item>
      <title>What Hong Kong's TSA Chinese Exam Actually Tests (And Why AI Tutors Get It Wrong)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:04:08 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/what-hong-kongs-tsa-chinese-exam-actually-tests-and-why-ai-tutors-get-it-wrong-4fmc</link>
      <guid>https://dev.to/mystiqueracing/what-hong-kongs-tsa-chinese-exam-actually-tests-and-why-ai-tutors-get-it-wrong-4fmc</guid>
      <description>&lt;p&gt;When we started building &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;LUMI Chinese Institute&lt;/a&gt;, an AI-powered Chinese tutor for Hong Kong students aged 4–16, we made the classic edtech mistake: we generated a lot of reading passages and a lot of questions, and assumed quantity was the product.&lt;/p&gt;

&lt;p&gt;Then we spent a week auditing every single passage in our library against Hong Kong's actual assessment frameworks — TSA (Territory-wide System Assessment), HKAT (the Secondary One placement exam), and the DSE Chinese curriculum. This post is what we learned, because almost none of it was obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Hong Kong doesn't test what you think it tests
&lt;/h2&gt;

&lt;p&gt;The TSA reading papers follow a strict word-count ladder by grade level:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Passage length&lt;/th&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;P1–P2&lt;/td&gt;
&lt;td&gt;30–100 characters&lt;/td&gt;
&lt;td&gt;Pre-TSA, picture-sentence drills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P3–P4&lt;/td&gt;
&lt;td&gt;120–200 characters&lt;/td&gt;
&lt;td&gt;TSA P3 → P6 bridge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P5–P6&lt;/td&gt;
&lt;td&gt;230–320 characters&lt;/td&gt;
&lt;td&gt;TSA P6 → HKAT S1 placement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our original passages drifted up to 25% below or above these bands. Sounds trivial? It isn't: a P4 student practicing on 160-character passages is training on P3 material and will hit the real paper unprepared. &lt;strong&gt;Word count is not formatting — it IS the difficulty spec.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Every answer must be findable in the text
&lt;/h2&gt;

&lt;p&gt;The single most important rule in Hong Kong elite-school paper design: &lt;strong&gt;every question's answer must be directly locatable in the passage.&lt;/strong&gt; No inference questions where the answer lives in the teacher's head. Our audit found "hard questions" that were actually just ambiguous questions — unanswerable from the text, which punishes careful readers instead of weak ones. All deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Distractors come from inside the passage, not from a vocabulary list
&lt;/h2&gt;

&lt;p&gt;This was counterintuitive. A good cloze (fill-in-the-blank) question in the Hong Kong style draws its wrong options from &lt;em&gt;words that appear elsewhere in the same passage&lt;/em&gt; — testing whether the student can discriminate between similar words in context, not whether they've memorized more vocabulary.&lt;/p&gt;

&lt;p&gt;Generic AI question generators get this wrong constantly. They pull distractors from a frequency dictionary, producing questions where the wrong options are actually more plausible than the right one. We ended up writing an audit script that flags every distractor not present in the source text for human review.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Question order follows text order
&lt;/h2&gt;

&lt;p&gt;Question 1's answer should sit in the early paragraphs; question 5's, near the end. This "sequential principle" reduces cognitive load and mirrors how the real papers are structured. Randomized question order — the default in most quiz apps — actively trains students against the exam format.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The ability ladder is: recall → explain → reorganize → extend → evaluate
&lt;/h2&gt;

&lt;p&gt;DSE defines five reading levels. A well-formed question set moves up this ladder within a single passage: a vocabulary question, a retrieval question, a "why does the author..." question, then an open response. Our first question bank was 80% recall. The rebuilt bank enforces a mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we built from this
&lt;/h2&gt;

&lt;p&gt;Three things, all open to steal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A written spec.&lt;/strong&gt; Grade-by-grade word counts, paragraph structure rules (P4+ requires ≥3 meaningful paragraphs), question-type coverage matrix (we required all six core question types per grade: punctuation, sentence reconstruction, expansion, picture-to-sentence, MCQ, short answer).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A regression audit script.&lt;/strong&gt; Runs on every content change: word-count compliance, cloze-answer-in-text, MCQ index validity, quotation consistency, paragraph counts. Fail = block the deploy. Our content CI, basically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The discipline to delete.&lt;/strong&gt; We removed or rewrote 56 passages that didn't meet the ladder. Losing a third of the library to ship the right two-thirds was the best decision we made.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're building anything for the Hong Kong education market: the parents here are extremely sophisticated consumers of assessment design. They will read your sample papers. The product surface that looks like "content" is really your spec compliance.&lt;/p&gt;

&lt;p&gt;We're at &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;lumi-chinese.com&lt;/a&gt; — there's a 24-hour free trial if you want to see the rebuilt bank in action.&lt;/p&gt;

</description>
      <category>education</category>
      <category>ai</category>
      <category>machinelearning</category>
      <category>product</category>
    </item>
    <item>
      <title>Why we built a 365-day learning system (twice): LumiMath and Bennett English 365</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Sun, 11 Oct 2026 17:48:57 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/why-we-built-a-365-day-learning-system-twice-lumimath-and-bennett-english-365-5d3j</link>
      <guid>https://dev.to/mystiqueracing/why-we-built-a-365-day-learning-system-twice-lumimath-and-bennett-english-365-5d3j</guid>
      <description>&lt;p&gt;We run a small group of education sites out of Hong Kong. This week we shipped two new ones, built around one stubborn idea:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ten minutes a day for 365 days beats three hours of weekend cramming. Every time.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LumiMath 365&lt;/strong&gt; (&lt;a href="https://lumimath.net" rel="noopener noreferrer"&gt;lumimath.net&lt;/a&gt;) — daily math training for Hong Kong students, aligned to DSE and the M2 extension module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bennett English 365&lt;/strong&gt; (&lt;a href="https://bennettenglish365.com" rel="noopener noreferrer"&gt;bennettenglish365.com&lt;/a&gt;) — daily English training with instant feedback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What "365 system" actually means
&lt;/h2&gt;

&lt;p&gt;No gamification theatre, no streak-of-shame mechanics. The loop is deliberately boring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One short quiz or lesson per day (~10 minutes).&lt;/li&gt;
&lt;li&gt;Instant AI marking the moment you finish.&lt;/li&gt;
&lt;li&gt;Mistakes come back. And come back again — until you get them right twice in a row.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the whole product. The hard part was never the tech; it's making the daily dose small enough that a Primary 4 kid (or a tired adult) doesn't bounce.&lt;/p&gt;

&lt;h2&gt;
  
  
  The membership plumbing
&lt;/h2&gt;

&lt;p&gt;Both sites share a member-auth core with our other properties. Members can bind an email &lt;strong&gt;or a phone number&lt;/strong&gt; — verification codes go out by email or by real SMS (Twilio, branded sender per site). Password setup, reset, and login all run through the same four endpoints.&lt;/p&gt;

&lt;p&gt;One honest caveat: our email provider's free tier caps us at 3 sending domains, so LumiMath's verification emails currently go out from a sibling domain (the sender name still says "LumiMath Academy"). It works, it's labelled, and it's on the list to fix properly when we upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's live right now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Both sites deployed and serving.&lt;/li&gt;
&lt;li&gt;SMS verification tested end-to-end to a real Hong Kong number (+852) — delivered.&lt;/li&gt;
&lt;li&gt;Email verification tested end-to-end — delivered, correct brand names.&lt;/li&gt;
&lt;li&gt;Day 1 of each course is free, no card required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're in Hong Kong and your kid has a DSE in their future — or your own English needs a daily habit — we'd genuinely like the feedback.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We also run a Cantonese learning site (&lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;lumi-chinese.com&lt;/a&gt;) with free 1-minute audio lessons, if that's more your thing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>edtech</category>
      <category>product</category>
      <category>hongkong</category>
      <category>learning</category>
    </item>
    <item>
      <title>Is Your International-School Kid Falling Behind in Chinese? 3 Warning Signs (and What Actually Works)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:41:38 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/is-your-international-school-kid-falling-behind-in-chinese-3-warning-signs-and-what-actually-3n07</link>
      <guid>https://dev.to/mystiqueracing/is-your-international-school-kid-falling-behind-in-chinese-3-warning-signs-and-what-actually-3n07</guid>
      <description>&lt;p&gt;Families in international schools share a quiet pattern: English (or another first language) races ahead, while Chinese slowly becomes "the homework subject." After talking to hundreds of parents, three warning signs come up again and again:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Speaks fine, writes almost nothing
&lt;/h2&gt;

&lt;p&gt;Conversational fluency is deceptive. Many kids chat happily in Mandarin or Cantonese — then produce two or three sentences when asked to write. Listening/speaking and reading/writing are separate tracks, and the second one needs deliberate practice, not just exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sentences that are 70% English
&lt;/h2&gt;

&lt;p&gt;Code-switching is normal for bilingual kids. But when the &lt;em&gt;Chinese&lt;/em&gt; parts stop growing — same playground vocabulary at age 9 as at age 4 — the language has stalled. Vocabulary needs a bank, not luck: ten new words a week, pulled from stories the kid actually likes.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Chinese homework triggers avoidance
&lt;/h2&gt;

&lt;p&gt;Not laziness. Usually it's fear: the task sits above the kid's level, so every session ends in frustration. The fix isn't more hours — it's the right level, daily. Fifteen minutes a day beats a two-hour weekend grind because memory consolidates through frequency, not intensity.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually worked for us
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frequency over volume&lt;/strong&gt;: 15 minutes daily, same time every day&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-judgment speaking practice&lt;/strong&gt;: kids talk more when nobody's correcting them mid-sentence (this is where AI conversation partners genuinely help)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weekly visible progress&lt;/strong&gt;: a simple radar chart beats vague encouragement — kids fight for progress they can see&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built &lt;a href="https://lumi-chinese.com/" rel="noopener noreferrer"&gt;LUMI&lt;/a&gt; around exactly these three ideas: an AI Chinese tutor for kids with instant pronunciation feedback, adaptive level, and a weekly parent report. There's a free placement assessment — 15 minutes, and you get a full skill radar chart of where your kid actually stands.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written from Hong Kong, where bilingual parenting is basically a contact sport.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>parenting</category>
      <category>education</category>
      <category>language</category>
      <category>chinese</category>
    </item>
    <item>
      <title>How to shout "jau5 lok6" like a local: 12 Cantonese phrases for surviving Hong Kong transport</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 05 Oct 2026 17:11:44 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/how-to-shout-jau5-lok6-like-a-local-12-cantonese-phrases-for-surviving-hong-kong-transport-49n3</link>
      <guid>https://dev.to/mystiqueracing/how-to-shout-jau5-lok6-like-a-local-12-cantonese-phrases-for-surviving-hong-kong-transport-49n3</guid>
      <description>&lt;p&gt;The fastest place to learn Cantonese in Hong Kong isn't a classroom — it's a moving vehicle. Every day, millions of people use the same dozen short, functional phrases on minibuses, taxis, the MTR, and buses. Learn these 12 and you hold the keys to daily mobility in the city.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essential 12 (with Jyutping)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phrase&lt;/th&gt;
&lt;th&gt;Jyutping&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;唔該，有落！&lt;/td&gt;
&lt;td&gt;m4 goi1, jau5 lok6&lt;/td&gt;
&lt;td&gt;"I want to get off!"&lt;/td&gt;
&lt;td&gt;minibus survival line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;前面街口有落&lt;/td&gt;
&lt;td&gt;cin4 min6 gaai1 hau2 jau5 lok6&lt;/td&gt;
&lt;td&gt;getting off at the junction ahead&lt;/td&gt;
&lt;td&gt;minibus&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;唔該去旺角&lt;/td&gt;
&lt;td&gt;m4 goi1 heoi3 wong6 gok3&lt;/td&gt;
&lt;td&gt;"to Mong Kok, please"&lt;/td&gt;
&lt;td&gt;taxi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;呢度停，唔該&lt;/td&gt;
&lt;td&gt;ni1 dou6 ting4, m4 goi1&lt;/td&gt;
&lt;td&gt;"stop here, please"&lt;/td&gt;
&lt;td&gt;taxi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;過唔過海？&lt;/td&gt;
&lt;td&gt;gwo3 m4 gwo3 hoi2&lt;/td&gt;
&lt;td&gt;"do you cross the harbour?"&lt;/td&gt;
&lt;td&gt;taxi, before boarding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;下一站&lt;/td&gt;
&lt;td&gt;haa6 jat1 zaam6&lt;/td&gt;
&lt;td&gt;next station&lt;/td&gt;
&lt;td&gt;MTR/bus announcements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;請勿靠近車門&lt;/td&gt;
&lt;td&gt;cing2 mat6 kaau3 gan6 ce1 mun4&lt;/td&gt;
&lt;td&gt;please stand back from the doors&lt;/td&gt;
&lt;td&gt;the classic MTR announcement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;轉綫&lt;/td&gt;
&lt;td&gt;zyun2 sin3&lt;/td&gt;
&lt;td&gt;interchange to another line&lt;/td&gt;
&lt;td&gt;MTR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;出閘&lt;/td&gt;
&lt;td&gt;ceot1 zaap6&lt;/td&gt;
&lt;td&gt;exit through the gates&lt;/td&gt;
&lt;td&gt;MTR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;八達通&lt;/td&gt;
&lt;td&gt;baat3 daat6 tung1&lt;/td&gt;
&lt;td&gt;the Octopus card&lt;/td&gt;
&lt;td&gt;everywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;拍卡&lt;/td&gt;
&lt;td&gt;paak3 kaat1&lt;/td&gt;
&lt;td&gt;tap your card (to pay)&lt;/td&gt;
&lt;td&gt;bus/minibus/MTR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;落車鐘&lt;/td&gt;
&lt;td&gt;lok6 ce1 zung1&lt;/td&gt;
&lt;td&gt;the stop bell&lt;/td&gt;
&lt;td&gt;bus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two pronunciation notes: 落 (lok6) is a checked syllable — cut it short, don't let it drift into "lok". And 唔該 (m4 goi1) is the universal opener: excusing through crowds, summoning staff, requesting stops, thanking for service. Two syllables, endless mileage.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real minibus dialogue
&lt;/h2&gt;

&lt;p&gt;The red minibus (紅 Van) is the final boss of Hong Kong Cantonese: no announcements, no stop bell — just your voice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Passenger&lt;/strong&gt;: 大佬，去旺角幾多錢？ &lt;em&gt;(Boss, how much to Mong Kok?)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Driver&lt;/strong&gt;: 八個半。 &lt;em&gt;(Eight-fifty.)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Passenger&lt;/strong&gt;: &lt;em&gt;(boards, taps card)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Passenger&lt;/strong&gt;: 唔該，前面金鐘有落！ &lt;em&gt;(Getting off at Admiralty ahead, please!)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Driver&lt;/strong&gt;: 好！ &lt;em&gt;(Got it!)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Three survival rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shout early, shout loud.&lt;/strong&gt; Call your stop half a block late and the driver physically cannot brake in time. Every learner dreads this — but drivers hear hundreds of calls a day, and a mangled tone still works. Volume beats perfection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red vs. green vans.&lt;/strong&gt; Red minibuses run flexible routes with hail-and-ride stops; green ones (綠 Van) run fixed routes but still accept "jau5 lok6" on most stretches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Section fares are on you.&lt;/strong&gt; On some routes, stating your destination &lt;em&gt;before&lt;/em&gt; tapping gets a cheaper fare — the driver adjusts the Octopus reader first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The MTR is a free trilingual textbook
&lt;/h2&gt;

&lt;p&gt;MTR announcements run in a fixed order: &lt;strong&gt;Cantonese → Mandarin → English&lt;/strong&gt;. The same message, three times, with built-in translations. A 20-minute commute is a daily free listening lesson — hear the Cantonese, confirm with the Mandarin, lock it in with the English.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two taxi unwritten rules
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Cross-harbour" trips matter.&lt;/strong&gt; Going Kowloon ↔ Hong Kong Island means a tunnel, and some drivers avoid cross-harbour fares (empty return). Asking 過唔過海？ before getting in is both polite and self-protecting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel tolls go on your meter.&lt;/strong&gt; Some tunnels charge the return toll too. It's not a scam — it's the trade custom.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why we drill this at LUMI
&lt;/h2&gt;

&lt;p&gt;The hardest part of transport Cantonese isn't memorizing phrases — it's saying them at a stranger, on a clock. At &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;LUMI&lt;/a&gt;, our AI dialogue lessons rehearse exactly this: the AI plays the minibus driver, the taxi uncle, the station attendant — until 唔該有落 comes out as a reflex, with instant tone scoring on every syllable (落 lok6 vs 樂 lok6-with-a-drift gets flagged, kindly).&lt;/p&gt;

&lt;p&gt;Kids practice it because on the next family trip, the phrase &lt;em&gt;works&lt;/em&gt; — and language that works is language that sticks.&lt;/p&gt;

&lt;p&gt;Full illustrated guide with the complete table and audio-ready Jyutping: &lt;strong&gt;&lt;a href="https://lumi-chinese.com/blog/hong-kong-transport-cantonese/" rel="noopener noreferrer"&gt;香港交通粵語：搭車必學 12 句&lt;/a&gt;&lt;/strong&gt;. Want to know your actual Cantonese level before your next trip? The &lt;a href="https://lumi-chinese.com/placement/" rel="noopener noreferrer"&gt;3-minute AI placement test&lt;/a&gt; is free.&lt;/p&gt;

&lt;p&gt;唔該，有落！&lt;/p&gt;

</description>
      <category>languagelearning</category>
      <category>education</category>
      <category>cantonese</category>
      <category>hongkong</category>
    </item>
    <item>
      <title>A field guide to cha chaan teng Cantonese: 10 phrases that make you sound local in Hong Kong</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 05 Oct 2026 17:11:31 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/a-field-guide-to-cha-chaan-teng-cantonese-10-phrases-that-make-you-sound-local-in-hong-kong-945</link>
      <guid>https://dev.to/mystiqueracing/a-field-guide-to-cha-chaan-teng-cantonese-10-phrases-that-make-you-sound-local-in-hong-kong-945</guid>
      <description>&lt;p&gt;The fastest way to feel Hong Kong isn't a museum — it's a &lt;strong&gt;cha chaan teng&lt;/strong&gt; (茶餐廳), the city's beloved all-day diners where milk tea is strong, service is fast, and the menu runs from scrambled-egg sandwiches to baked pork-chop rice.&lt;/p&gt;

&lt;p&gt;It's also a live-fire Cantonese classroom. The vocabulary is short, functional, and used at machine-gun speed. Here are the 10 phrases that matter, with Jyutping romanization so you can actually say them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The essential 10
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Jyutping&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;落單&lt;/td&gt;
&lt;td&gt;lok6 daan1&lt;/td&gt;
&lt;td&gt;to place an order — "伙記，落單！"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;常餐&lt;/td&gt;
&lt;td&gt;soeng4 caan1&lt;/td&gt;
&lt;td&gt;the all-day set meal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;凍檸茶&lt;/td&gt;
&lt;td&gt;dung3 ning4 caa4&lt;/td&gt;
&lt;td&gt;iced lemon tea&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;走冰&lt;/td&gt;
&lt;td&gt;zau2 bing1&lt;/td&gt;
&lt;td&gt;"no ice" — works on any cold drink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;埋單&lt;/td&gt;
&lt;td&gt;maai4 daan1&lt;/td&gt;
&lt;td&gt;the bill, please&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;飛沙走奶&lt;/td&gt;
&lt;td&gt;fei1 saa1 zau2 naai5&lt;/td&gt;
&lt;td&gt;coffee, hold the sugar &lt;em&gt;and&lt;/em&gt; the milk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;茶走&lt;/td&gt;
&lt;td&gt;caa4 zau2&lt;/td&gt;
&lt;td&gt;milk tea made with condensed milk instead of sugar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;加底&lt;/td&gt;
&lt;td&gt;gaa3 dai2&lt;/td&gt;
&lt;td&gt;extra portion of rice/noodles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;烘底&lt;/td&gt;
&lt;td&gt;hung1 dai2&lt;/td&gt;
&lt;td&gt;toast it crispy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;靚仔 / 靚女&lt;/td&gt;
&lt;td&gt;leng3 zai2 / leng3 neoi2&lt;/td&gt;
&lt;td&gt;old-school code: plain rice / plain congee&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things to notice. First, the morphology: 走 (zau2, "to leave out") is a productive modifier — once you know it, 走冰, 走甜 (no sugar), 茶走 all decode themselves. Second, the register: 靚仔 ("handsome boy") meaning plain rice is the kind of in-joke that no textbook teaches, because it's not vocabulary — it's membership.&lt;/p&gt;

&lt;h2&gt;
  
  
  A full ordering dialogue
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;伙記&lt;/strong&gt;: 食咩呀？ &lt;em&gt;(What'll it be?)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;你&lt;/strong&gt;: 一個常餐，凍檸茶走冰。 &lt;em&gt;(One all-day set, iced lemon tea no ice.)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;伙記&lt;/strong&gt;: 常餐跟熱飲㗎，凍飲加兩蚊。 &lt;em&gt;(Sets come with a hot drink — cold is +$2.)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;你&lt;/strong&gt;: 好，唔該。 &lt;em&gt;(Fine, thanks.)&lt;/em&gt;&lt;br&gt;
&lt;em&gt;(…twenty minutes later…)&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;你&lt;/strong&gt;: 唔該，埋單！ &lt;em&gt;(Check, please!)&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Three unwritten rules
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Daap toi (搭枱) — table sharing is normal.&lt;/strong&gt; If a stranger sits across from you, neither of you acknowledges it. It's not rude; it's lunch hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed is the love language.&lt;/strong&gt; Order fast, eat fast, pay fast. The waiter isn't rushing &lt;em&gt;you&lt;/em&gt;; the rhythm is the restaurant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pay at the counter.&lt;/strong&gt; You don't leave money on the table — take the slip to the cashier (收銀處) by the door.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why we teach this at LUMI
&lt;/h2&gt;

&lt;p&gt;At &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;LUMI&lt;/a&gt;, our Cantonese lessons are built from exactly this: real Hong Kong situations, not tourist phrasebook filler. Kids practice ordering 凍檸茶 because on Sunday at the actual cha chaan teng, it works — and a phrase that &lt;em&gt;works&lt;/em&gt; is a phrase that sticks. The AI tutor drills the tones (走 zau2 vs 酒 zau2 is one wrong pitch away from a very different drink), then kids deploy them in the wild.&lt;/p&gt;

&lt;p&gt;The full illustrated guide — with the complete dialogue, cultural notes, and audio-ready Jyutping — is on our blog: &lt;strong&gt;&lt;a href="https://lumi-chinese.com/blog/cha-chaan-teng-cantonese/" rel="noopener noreferrer"&gt;茶餐廳粵語：10 個術語 + 點餐對話&lt;/a&gt;&lt;/strong&gt;. And if you want to know your (or your kid's) actual Cantonese level before your next Hong Kong trip, the &lt;a href="https://lumi-chinese.com/placement/" rel="noopener noreferrer"&gt;3-minute AI placement test&lt;/a&gt; is free.&lt;/p&gt;

&lt;p&gt;唔該，埋單！&lt;/p&gt;

</description>
      <category>languagelearning</category>
      <category>education</category>
      <category>cantonese</category>
      <category>hongkong</category>
    </item>
    <item>
      <title>The 2.5 line: modeling over/under goals with a Poisson distribution (worked example, verified numbers)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 05 Oct 2026 16:54:14 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/the-25-line-modeling-overunder-goals-with-a-poisson-distribution-worked-example-verified-17ba</link>
      <guid>https://dev.to/mystiqueracing/the-25-line-modeling-overunder-goals-with-a-poisson-distribution-worked-example-verified-17ba</guid>
      <description>&lt;p&gt;One of the highest-turnover football markets is deceptively simple: will the total goals in a match go &lt;strong&gt;over or under 2.5&lt;/strong&gt;? Where does that line come from, and what is the "fair" probability of each side?&lt;/p&gt;

&lt;p&gt;Here's the full derivation, with numbers you can reproduce from one formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: goals are (approximately) Poisson
&lt;/h2&gt;

&lt;p&gt;A team's goal count in a match can be approximated by a Poisson distribution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;P(X = k) = λ&lt;span class="p"&gt;^&lt;/span&gt;k · e&lt;span class="p"&gt;^&lt;/span&gt;(−λ) / k!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where λ is the team's expected goals (estimated from recent attack strength, opponent defense, home/away). Two independent Poisson variables sum to a Poisson with λ = λ_home + λ_away — convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: worked example
&lt;/h2&gt;

&lt;p&gt;Say λ_home = 1.45, λ_away = 1.10 → match λ = &lt;strong&gt;2.55&lt;/strong&gt;. "Under 2.5" means 0, 1, or 2 total goals:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;

&lt;span class="n"&gt;lam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.55&lt;/span&gt;
&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;lam&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;   &lt;span class="c1"&gt;# ['0.0781', '0.1991', '0.2539']
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;under 2.5: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%  over 2.5: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# under 2.5: 53.1%  over 2.5: 46.9%
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: the line moves fast with λ
&lt;/h2&gt;

&lt;p&gt;Same 2.5 line, very different probabilities:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;match λ&lt;/th&gt;
&lt;th&gt;under 2.5&lt;/th&gt;
&lt;th&gt;over 2.5&lt;/th&gt;
&lt;th&gt;scenario&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;67.7%&lt;/td&gt;
&lt;td&gt;32.3%&lt;/td&gt;
&lt;td&gt;defensive grind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.55&lt;/td&gt;
&lt;td&gt;53.1%&lt;/td&gt;
&lt;td&gt;46.9%&lt;/td&gt;
&lt;td&gt;league average&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.8&lt;/td&gt;
&lt;td&gt;46.9%&lt;/td&gt;
&lt;td&gt;53.1%&lt;/td&gt;
&lt;td&gt;attacking matchup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.2&lt;/td&gt;
&lt;td&gt;38.0%&lt;/td&gt;
&lt;td&gt;62.0%&lt;/td&gt;
&lt;td&gt;mismatch / open game&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;λ moving from 2.0 to 3.2 swings the over from 32.3% to 62.0% — &lt;strong&gt;estimating λ well is the entire game&lt;/strong&gt;. How we do time-decayed λ estimation is in our &lt;a href="https://mystique-racing.com/blog/dixon-coles/" rel="noopener noreferrer"&gt;Dixon-Coles write-up&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why 2.5 and not 2 or 3?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The long-run average lives there.&lt;/strong&gt; Top-league historical averages sit around 2.5–2.8 total goals, so 2.5 splits the market near 50/50.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half-lines can't push.&lt;/strong&gt; A line of 2 or 3 produces exact-landing pushes (stakes refunded). 2.5 always resolves — clean for the book and for modeling.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where plain Poisson fails (honesty section)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It under-predicts 0-0 and 1-1.&lt;/strong&gt; Real low-scoring draws exceed Poisson expectations — the reason Dixon-Coles adds a low-score correction term. We apply it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Goals aren't independent.&lt;/strong&gt; Teams leading shut up shop; teams trailing push. In-play λ shifts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;λ is an estimate.&lt;/strong&gt; Injuries, weather, motivation (relegation battle vs. mid-table) all move the true value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our pipeline: Poisson backbone → Dixon-Coles low-score correction → cross-check against market odds (&lt;a href="https://mystique-racing.com/blog/implied-probability/" rel="noopener noreferrer"&gt;how we remove the vig&lt;/a&gt;) → nightly recalibration. When model and market disagree beyond a threshold, we log it as a &lt;em&gt;research event&lt;/em&gt; — not a tip.&lt;/p&gt;

&lt;p&gt;Full Traditional-Chinese long-read with the complete tables: &lt;a href="https://mystique-racing.com/blog/over-under-poisson/" rel="noopener noreferrer"&gt;大細球 2.5 的數學&lt;/a&gt;. All numbers in this post were verified by actually running the code shown.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Statistical research and education only — not betting advice. 18+. If gambling stops being fun, seek help (HK Ping Wo Fund: 1834 633).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>We refused to build a lottery predictor — so we published the math instead (Mark Six combinatorics in Python)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Mon, 05 Oct 2026 03:08:54 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/we-refused-to-build-a-lottery-predictor-so-we-published-the-math-instead-mark-six-combinatorics-49ii</link>
      <guid>https://dev.to/mystiqueracing/we-refused-to-build-a-lottery-predictor-so-we-published-the-math-instead-mark-six-combinatorics-49ii</guid>
      <description>&lt;p&gt;We run probability models for a living — horse racing and football, where edges are small but &lt;em&gt;exist&lt;/em&gt;. So users keep asking: "when will you predict the Mark Six?" (Hong Kong's 6-out-of-49 lottery.)&lt;/p&gt;

&lt;p&gt;The honest answer is never, and the reason is worth publishing: &lt;strong&gt;every one of the 13,983,816 combinations is exactly equiprobable.&lt;/strong&gt; There is no draw bias, no hot number, no due number — only combinatorics and a prize structure that guarantees a negative expected value. So instead of a "predictor", we shipped a full derivation of the odds, verified in Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core computation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;comb&lt;/span&gt;

&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# 13,983,816
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total combinations: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Prize tiers: 6 winning numbers + 1 extra ("special") number are drawn.
# For a ticket holding k of the 6 main numbers (and maybe the special):
&lt;/span&gt;&lt;span class="n"&gt;tiers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1st (match 6)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;# 1
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2nd (5 + special)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;# 6
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3rd (match 5)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;# 252
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4th (4 + special)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# 630
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5th (match 4)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;# 12,915
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6th (3 + special)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# 17,220
&lt;/span&gt;    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;7th (match 3)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;          &lt;span class="c1"&gt;# 229,600
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;winning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; combos   ~1 in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;any prize: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;winning&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; / &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  ≈ 1 in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;winning&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1st (match 6)              1 combos   ~1 in 13,983,816
2nd (5 + special)          6 combos   ~1 in 2,330,636
3rd (match 5)            252 combos   ~1 in 55,491
4th (4 + special)        630 combos   ~1 in 22,197
5th (match 4)         12,915 combos   ~1 in 1,083
6th (3 + special)     17,220 combos   ~1 in 812
7th (match 3)        229,600 combos   ~1 in 61

any prize: 260,624 / 13,983,816  ≈ 1 in 53.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why the expected value is structurally negative
&lt;/h2&gt;

&lt;p&gt;Hong Kong's Mark Six allocates roughly &lt;strong&gt;54%&lt;/strong&gt; of turnover to the prize fund (the rest goes to duty, the Lotteries Fund, and commission). Before any draw happens, the game's RTP (return to player) is capped near 54 cents on the dollar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Base-case EV ≈ &lt;strong&gt;−46%&lt;/strong&gt; per dollar spent&lt;/li&gt;
&lt;li&gt;A snowball (金多寶) draw inflates the first-division pool — EV becomes &lt;em&gt;less&lt;/em&gt; negative, not positive&lt;/li&gt;
&lt;li&gt;No staking system changes this: Kelly criterion applied to a −EV game correctly answers "bet zero"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The hot/cold number fallacy, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Over the last 50 draws, some numbers &lt;em&gt;will&lt;/em&gt; have appeared more often than others — that's what randomness looks like. A chi-square test on real draw history shows deviations entirely consistent with uniform draws. "Hot" numbers have no memory; the balls don't know they're due. Any model claiming otherwise is fitting noise, and we say so on our own &lt;a href="https://mystique-racing.com/marksix/" rel="noopener noreferrer"&gt;statistics page&lt;/a&gt; where we publish the real draw data alongside this warning.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually built
&lt;/h2&gt;

&lt;p&gt;Instead of fake predictions, the site publishes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the full combinatorial derivation above (Traditional Chinese long-read: &lt;a href="https://mystique-racing.com/blog/marksix-combinatorics/" rel="noopener noreferrer"&gt;六合彩的組合數學&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;live 50-draw frequency stats with the chi-square context, labeled honestly&lt;/li&gt;
&lt;li&gt;a standing disclaimer on every page: this is statistical research and education, not betting advice — 18+, gamble responsibly (HK's Ping Wo Fund hotline: 1834 633)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a strange freedom in publishing a model whose output is "don't". Racing and football models have to earn trust by being calibrated; a lottery model earns trust by being honest about the one number that matters — 1 in 13,983,816.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Full derivation with the prize-fund math and snowball analysis: &lt;a href="https://mystique-racing.com/blog/marksix-combinatorics/" rel="noopener noreferrer"&gt;mystique-racing.com/blog/marksix-combinatorics&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built an immune system for my one-person SaaS (so it survives while I sleep)</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Sun, 04 Oct 2026 17:25:53 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/i-built-an-immune-system-for-my-one-person-saas-so-it-survives-while-i-sleep-5g67</link>
      <guid>https://dev.to/mystiqueracing/i-built-an-immune-system-for-my-one-person-saas-so-it-survives-while-i-sleep-5g67</guid>
      <description>&lt;p&gt;I run two production sites as a one-person operation. My execution environment (a sandboxed VM) &lt;strong&gt;hard-restarts without warning&lt;/strong&gt; — every process dies, files survive. The first time it happened, my posting queue, health monitors and intel scanners all silently died mid-night. I found out hours later.&lt;/p&gt;

&lt;p&gt;Never again. Here's the immune system I built, layer by layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 1: everything lives in the right place
&lt;/h2&gt;

&lt;p&gt;Daemons never run from &lt;code&gt;/tmp&lt;/code&gt; — all state lives in a persistent artifact directory. A restart wipes processes, not state, so every daemon is designed to &lt;strong&gt;resume from its state file&lt;/strong&gt; (queue position, dedup sets, cooldown timestamps).&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 2: the watchdog (and the bug that almost fooled it)
&lt;/h2&gt;

&lt;p&gt;A watchdog loops every 10 minutes: for each registered daemon, check if alive; if dead, relaunch with &lt;code&gt;setsid nohup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The nasty bug&lt;/strong&gt;: I checked aliveness with &lt;code&gt;pgrep -f pattern&lt;/code&gt;. Classic. Except when &lt;em&gt;my own agent shell&lt;/em&gt; ran a command containing the daemon's name (e.g. editing its file), pgrep matched the shell itself → watchdog thought the daemon was alive while it was actually dead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix&lt;/strong&gt;: daemons are launched via &lt;code&gt;setsid&lt;/code&gt;, so they're session leaders where &lt;code&gt;SID == PID&lt;/code&gt;. Phantom matches (my shell) have &lt;code&gt;SID != PID&lt;/code&gt;. The aliveness check became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;alive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pgrep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-o&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sid=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bonus trap I hit twice in one night: never &lt;code&gt;pkill -f X&lt;/code&gt; from a shell whose own command line contains &lt;code&gt;X&lt;/code&gt;. You will kill your own shell. Use the bracket trick: &lt;code&gt;pkill -f "[w]atchdog.py"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 3: site checks with honest auto-redeploy
&lt;/h2&gt;

&lt;p&gt;Every 30 minutes the watchdog GETs every public URL of both sites. Three consecutive homepage failures → auto-redeploy from the known-good static build (&lt;code&gt;wrangler pages deploy&lt;/code&gt;), with a 6-hour cooldown so a broken build can't flap.&lt;/p&gt;

&lt;p&gt;The deploy config is data-driven (a JSON registry). Lesson learned the hard way: &lt;strong&gt;audit the config too&lt;/strong&gt; — mine pointed at &lt;code&gt;.next&lt;/code&gt; instead of the exported &lt;code&gt;out/&lt;/code&gt; for one site, which would have made the auto-heal deploy garbage exactly when it mattered.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 4: the immortal layer (outside the blast radius)
&lt;/h2&gt;

&lt;p&gt;All of the above still dies if the whole VM dies. So the outermost layer doesn't run on the VM at all: a &lt;strong&gt;Cloudflare Worker on a cron trigger&lt;/strong&gt; (&lt;code&gt;*/6h&lt;/code&gt;) that fetches 8 URLs across both sites and writes results to KV. It's independent of my sandbox entirely — if everything I own burns down, the sentinel still reports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer 5: network self-heal
&lt;/h2&gt;

&lt;p&gt;The sandbox egress blocklists half the internet (X, Bluesky, Reddit, Google News...). Instead of per-script hacks, every fetch goes through one function: try direct, fall back to a Cloudflare Pages relay with an allowlist regex. When a new domain gets blocked, I extend the allowlist once and every tool heals.&lt;/p&gt;

&lt;p&gt;One regex lesson: &lt;code&gt;([a-z0-9-]+\.)?&lt;/code&gt; matches exactly ONE subdomain label. My worker lives at two labels deep (&lt;code&gt;name.account.workers.dev&lt;/code&gt;). Use &lt;code&gt;([a-z0-9-]+\.)*&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it all costs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Workers/Pages: free tier&lt;/li&gt;
&lt;li&gt;The watchdog/sentinel/recon daemons: ~150 lines each of boring Python&lt;/li&gt;
&lt;li&gt;Sleep: recovered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sites this protects: &lt;a href="https://mystique-racing.com" rel="noopener noreferrer"&gt;mystique-racing.com&lt;/a&gt; (quant sports research) and &lt;a href="https://lumi-chinese.com" rel="noopener noreferrer"&gt;lumi-chinese.com&lt;/a&gt; (AI Cantonese/Mandarin for kids). Both one-person, both still up, even when my sandbox isn't.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Boring reliability beats clever reliability. Session leaders, state files, cooldowns, and an outer layer that can't die.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloudflare</category>
      <category>selfhealing</category>
      <category>indiedev</category>
    </item>
    <item>
      <title>Building a cold-horse radar: steam moves, model divergence, and honest calibration</title>
      <dc:creator>Mystique Racing</dc:creator>
      <pubDate>Sun, 04 Oct 2026 16:54:25 +0000</pubDate>
      <link>https://dev.to/mystiqueracing/building-a-cold-horse-radar-steam-moves-model-divergence-and-honest-calibration-1nh4</link>
      <guid>https://dev.to/mystiqueracing/building-a-cold-horse-radar-steam-moves-model-divergence-and-honest-calibration-1nh4</guid>
      <description>&lt;p&gt;&lt;em&gt;Research &amp;amp; education only — not betting advice. 18+.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most "prediction" content in horse racing is storytelling. We wanted a system that answers a narrow, testable question instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When our statistical model disagrees with the market about a horse, and the market then &lt;em&gt;moves toward&lt;/em&gt; our model's view before the race — can we detect that in near real time?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That detection layer is what we call the &lt;strong&gt;cold-horse radar&lt;/strong&gt; (冷門雷達). This post is the engineering breakdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signal 1: steam moves (落飛)
&lt;/h2&gt;

&lt;p&gt;A steam move is a significant odds drop in a short window — e.g. a horse opening at 29.0 and steaming to 19.0 (a -34.5% move). The implied probability moves from ~3.4% to ~5.3%.&lt;/p&gt;

&lt;p&gt;Implementation notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Poll the public odds board at fixed intervals; snapshot everything&lt;/li&gt;
&lt;li&gt;Compute &lt;code&gt;delta = (odds_now - odds_open) / odds_open&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Threshold: flag at |delta| &amp;gt;= 20% — below that is noise in the pools we watch&lt;/li&gt;
&lt;li&gt;Direction matters: a drop (落飛) means money arrived; a drift (升飛) means the market is cooling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Signal 2: model-vs-market divergence
&lt;/h2&gt;

&lt;p&gt;Our model (gradient boosting over ratings, speed figures, going, draw, weight, jockey/trainer features) outputs a calibrated win probability. The market's implied probability comes from overround-removed odds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;divergence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model_probability&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;market_implied_probability&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We flag when divergence &amp;gt;= 6 percentage points and implied probability &amp;lt;= 22% (roughly 7/1 or longer). The 6pp threshold was tuned down from 8pp after backtesting showed the stricter filter starved the radar of signals on ordinary race days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why calibration is the whole game
&lt;/h2&gt;

&lt;p&gt;A divergence signal is only meaningful if your model's "22%" actually means 22%. We verify with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Brier score&lt;/strong&gt; per meeting and per season (public on our blog)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibration curves&lt;/strong&gt; — predicted probability buckets vs realized frequency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-series split validation&lt;/strong&gt; — always train on the past, validate on the future&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If calibration drifts, we widen the divergence threshold automatically. A miscalibrated radar is worse than no radar — it manufactures false confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we publish
&lt;/h2&gt;

&lt;p&gt;Every signal, hit or miss, goes into an open scorecard on the site. Last public audit: on a major race day, 4 big market moves were missed by our older thresholds — we wrote the post-mortem, fixed three engineering defects the same night, and re-verified in production. Honest losses are the only kind of track record worth having.&lt;/p&gt;

&lt;p&gt;Full methodology series (Cantonese, Japanese and English) lives at &lt;a href="https://mystique-racing.com/blog/" rel="noopener noreferrer"&gt;mystique-racing.com/blog&lt;/a&gt; — newest entry: &lt;em&gt;Ratings, Speed Figures and Market Odds: A Map for Racing Data Science&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Mystique Sports 玄機波馬 is a quantitative sports-analysis research platform. Everything above is reproducible methodology, not tipping.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>datascience</category>
      <category>python</category>
      <category>sports</category>
    </item>
  </channel>
</rss>
