<?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: finaltype</title>
    <description>The latest articles on DEV Community by finaltype (@finaltype).</description>
    <link>https://dev.to/finaltype</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%2F4089379%2Fc8248cd8-86bb-41c0-a181-9f13f26132f8.jpg</url>
      <title>DEV Community: finaltype</title>
      <link>https://dev.to/finaltype</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/finaltype"/>
    <language>en</language>
    <item>
      <title>"[261022] 조기 진입 실거래 첫날, 핵-액티브 구조는 재원 문제로 보류"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Thu, 22 Oct 2026 13:04:23 +0000</pubDate>
      <link>https://dev.to/finaltype/261022-jogi-jinib-silgeorae-ceosnal-haeg-aegtibeu-gujoneun-jaeweon-munjero-boryu-29a2</link>
      <guid>https://dev.to/finaltype/261022-jogi-jinib-silgeorae-ceosnal-haeg-aegtibeu-gujoneun-jaeweon-munjero-boryu-29a2</guid>
      <description>&lt;p&gt;&lt;em&gt;랭킹을 미리 얼려 첫 라운드를 당기는 조기 진입이 실거래에 들어갔고, 어제 제안받은 두 층 구조는 투입 재원이 없어 보류됐습니다.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  조기 진입이 실거래에 들어갔다
&lt;/h2&gt;

&lt;p&gt;랭킹을 미리 얼려두고 첫 매매 라운드 체결 시각도 앞당기는 구조를, 밤사이 설계·구현·AI 점검을 거쳐 오늘 아침 배포했습니다.&lt;/p&gt;

&lt;p&gt;함께 몇 가지 관측 전용 섀도(장중 추가 모델 평가, 분봉 단위 보조 신호)도 같이 올라갔습니다. 아직 실제 주문에는 영향을 주지 않는 관측용입니다.&lt;/p&gt;

&lt;p&gt;오전 첫 라운드부터 저녁까지 지켜본 결과, 동결·체결·후속 라운드가 전부 예상 범위 안에서 돌았습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  어제 나온 두 층 구조, 코드는 끝났지만 돈은 없다
&lt;/h2&gt;

&lt;p&gt;어제 자문에서 나온 "지수 추종 핵 + 소규모 액티브" 구조의 실행 코드를 오늘 완성해 병합했습니다. 다만 아직 비활성 상태로만 올려뒀습니다.&lt;/p&gt;

&lt;p&gt;지수 추종 ETF 매수·분기 리밸런싱 계획기, 계좌 그룹 단위로 보유·현금을 합산하는 로직, 실행 진입점까지 여러 갈래로 나눠 작업했습니다.&lt;/p&gt;

&lt;p&gt;투입 규모와 재원을 두고 자문에 물었더니 "신규 입금으로 일정 비율을 사들인 뒤 연말 판정에 따라 증액"하는 안이 돌아왔습니다.&lt;/p&gt;

&lt;p&gt;오후에 제가 "신규 입금도, 보유 종목 매도도 없다"고 전제를 정정하면서 이 안은 폐기됐고, 핵-액티브 구조는 당분간 보류 상태로 남았습니다. 코드는 이미 올라가 있으니 재원이 정해지면 바로 활성화할 수 있습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  검증 가설 여섯 개, 전부 불합격
&lt;/h2&gt;

&lt;p&gt;어제 언급한 "결과를 보고 나서 기준을 끼워 맞추는 일을 막기 위한" 사전 가설 검증을, 오늘 실제로 돌렸습니다. 여섯 개 가설 모두 통계적으로 의미 있는 신호를 보여주지 못했습니다.&lt;/p&gt;

&lt;p&gt;결과를 다른 각도에서 다시 점검해보니, 그중 하나는 애초에 구현에 버그가 섞여 있었습니다. 버그를 고쳐 다시 돌려봐도 결론은 바뀌지 않았고, 검증 방식 자체의 설계도 효과를 잡아내기엔 느슨했다는 지적을 받았습니다.&lt;/p&gt;

&lt;p&gt;그래서 가설 하나하나를 따로 검증하는 방식 대신, 더 긴 기간과 더 엄격한 기준을 미리 정해두는 방식으로 바꿔 새로 등록했습니다. 판정은 내년 초에 나옵니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  회전율 상한, AI 자문 수치가 틀렸다는 걸 제가 잡아냈다
&lt;/h2&gt;

&lt;p&gt;조기 진입 구조에서는 매매 회전율 상한을 계산하는 방식이 기존 설계와 안 맞는 부분이 있었습니다. 라운드 단위로 걸던 제한을 하루 단위로 통합하자는 자문이 아침에 나왔습니다.&lt;/p&gt;

&lt;p&gt;제가 "거부당한 매도 거래 중 손실로 이어진 비율이 크다는데, 그럼 그냥 상한을 없애야 하지 않냐"고 반론을 제기했습니다. 자문에게 다시 점검을 맡겼더니, 처음 보고했던 수치의 귀속이 실제로 틀려 있었습니다. 손실로 이어졌다고 지목된 거래 일부는 회전율 상한 때문이 아니라 다른 제한(종목당 한도) 때문에 거부된 거래였습니다.&lt;/p&gt;

&lt;p&gt;귀속을 다시 정리한 뒤에는 "상한을 완전히 없애지는 말고, 하루 단위로 통합하면서 상한 자체는 다소 올리자"는 수정안으로 좁혀졌습니다. 이 안을 승인해 바로 반영했고, 다음 주 초부터 적용됩니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  왜 중요한가
&lt;/h2&gt;

&lt;p&gt;오늘 눈에 띄는 공통점은 "코드가 준비됐다"와 "그 코드를 돌릴 조건이 갖춰졌다"를 계속 구분한 것입니다.&lt;/p&gt;

&lt;p&gt;핵-액티브 구조도 코드는 끝났지만 재원이 없어 멈췄습니다. 가설 검증도 결과가 안 나왔다고 억지로 통과시키지 않고, 그대로 재설계로 넘어갔습니다.&lt;/p&gt;

&lt;p&gt;회전율 상한 사건은 AI 자문도 틀릴 수 있다는 걸 보여준 사례입니다. 숫자를 그냥 받아들이지 않고 반론을 제기했기 때문에, 틀린 귀속을 바로잡을 수 있었습니다.&lt;/p&gt;

&lt;p&gt;조기 진입은 설계부터 배포까지 하룻밤 사이 돌았지만, 실제로 중요한 건 오늘 첫 실거래 라운드가 예상대로 돌았는지를 끝까지 지켜본 일이었습니다.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[Oct 22] First Live Day of Early Entry, and a Two-Tier Structure Stalled on Funding"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Thu, 22 Oct 2026 13:03:22 +0000</pubDate>
      <link>https://dev.to/finaltype/oct-22-first-live-day-of-early-entry-and-a-two-tier-structure-stalled-on-funding-2iio</link>
      <guid>https://dev.to/finaltype/oct-22-first-live-day-of-early-entry-and-a-two-tier-structure-stalled-on-funding-2iio</guid>
      <description>&lt;p&gt;&lt;em&gt;Early entry — freezing the ranking ahead of time and pulling the first trading round forward — went live, while yesterday's proposed two-tier structure stalled for lack of capital to fund it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Early entry went live
&lt;/h2&gt;

&lt;p&gt;A structure that freezes the ranking ahead of time and pulls the first trading round's execution time earlier was designed, built, and reviewed by an AI advisor overnight, then deployed this morning.&lt;/p&gt;

&lt;p&gt;A few observation-only shadow runs went up alongside it too — an extra intraday model evaluation pass and a sub-bar auxiliary signal. Neither affects actual orders yet; they're purely for watching.&lt;/p&gt;

&lt;p&gt;Watching from the first round in the morning through the evening, the freeze, the execution, and the follow-up rounds all stayed within expected bounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yesterday's two-tier structure — the code is done, but there's no money
&lt;/h2&gt;

&lt;p&gt;The execution code for yesterday's proposed "index-replicating core + small active slice" structure got finished and merged today. It's still sitting there inactive, though.&lt;/p&gt;

&lt;p&gt;The work split into several pieces: a planner for buying the index-tracking ETF and rebalancing it quarterly, logic that aggregates holdings and cash across a group of accounts, and the actual execution entry point.&lt;/p&gt;

&lt;p&gt;When I asked the advisor how much capital to put in and where it should come from, the answer that came back was: fund it with a fresh deposit, buy a set fraction right away, then scale up based on a year-end verdict.&lt;/p&gt;

&lt;p&gt;In the afternoon I corrected a premise — there's no fresh deposit coming in, and no existing holdings are being sold either. That killed the proposal, and the core-plus-active structure is on hold for now. The code is already in place, though, so it can switch on as soon as funding is settled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six pre-registered hypotheses, all failed
&lt;/h2&gt;

&lt;p&gt;The pre-registered hypothesis testing mentioned yesterday — set up specifically to prevent fitting the criteria to the results after the fact — actually ran today. None of the six hypotheses showed a statistically meaningful signal.&lt;/p&gt;

&lt;p&gt;Looking at the results from another angle turned up a bug in one of the implementations. Fixing it and rerunning didn't change the conclusion, and on top of that, the testing design itself was judged too loose to reliably catch an effect even if one existed.&lt;/p&gt;

&lt;p&gt;So instead of testing hypotheses one at a time, the approach got replaced with a longer observation window and a stricter, pre-committed bar. The verdict on that won't come until early next year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The turnover cap advisory had a wrong number, and I caught it
&lt;/h2&gt;

&lt;p&gt;The way the trading turnover cap gets calculated didn't quite fit the early-entry structure anymore. An advisory came in this morning proposing to fold the per-round limit into a single daily limit.&lt;/p&gt;

&lt;p&gt;I pushed back — if a large share of rejected sell orders apparently turned into losses, why not just remove the cap entirely? Asking the advisor to recheck turned up a real error: some of the trades blamed on the turnover cap had actually been rejected by a different limit (the per-stock cap), not the turnover cap at all.&lt;/p&gt;

&lt;p&gt;Once the attribution got sorted out, the recommendation narrowed to: don't remove the cap outright, fold it into a daily limit, and raise the cap itself somewhat. I approved that version and it's now applied, taking effect early next week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;The thread running through today was keeping "the code is ready" and "the conditions to run it are ready" as two separate questions.&lt;/p&gt;

&lt;p&gt;The core-plus-active structure had its code finished but stalled for lack of funding. The hypothesis testing didn't get forced through just because it failed to produce a result — it went straight into a redesign instead.&lt;/p&gt;

&lt;p&gt;The turnover-cap episode is a reminder that an AI advisor can be wrong too. Catching the bad attribution only happened because the numbers got pushed back on instead of taken at face value.&lt;/p&gt;

&lt;p&gt;Early entry went from design to deployment in a single night, but what actually mattered was watching today's first live round all the way through to confirm it behaved as expected.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>수동 실행이 예약 실행으로 오판된 버그 - systemd 활성화 메타데이터가 생각보다 오래 남았다</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:06:34 +0000</pubDate>
      <link>https://dev.to/finaltype/sudong-silhaengi-yeyag-silhaengeuro-opandoen-beogeu-systemd-hwalseonghwa-metadeiteoga-saenggagboda-orae-namassda-4n9o</link>
      <guid>https://dev.to/finaltype/sudong-silhaengi-yeyag-silhaengeuro-opandoen-beogeu-systemd-hwalseonghwa-metadeiteoga-saenggagboda-orae-namassda-4n9o</guid>
      <description>&lt;p&gt;&lt;em&gt;한 달 전에 고쳤다고 믿은 버그가, 완전히 다른 원인으로 다시 터졌습니다&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;제 자동매매 시스템은 하루 중 정해진 시각에 자동으로 매매 라운드를 돕니다. 그런데 종종 제가 직접 라운드를 한 번 더 돌려야 하는 상황이 생깁니다.&lt;/p&gt;

&lt;p&gt;안전장치가 발동해서 한동안 매수가 막혀 있다가, 그 장치를 풀고 수동으로 거래를 재개해야 하는 경우가 그렇습니다. 이 안전장치와 수동 개입 절차는 종목을 고르는 판단 로직이 아니라 주문 실행·안전장치 계층에 속합니다. 전체 설계는 &lt;a href="https://finaltype.github.io/quant-blog/architecture/05_execution_architecture/" rel="noopener noreferrer"&gt;실전 계좌 적용편&lt;/a&gt;(새 창)에 정리해 뒀습니다.&lt;/p&gt;

&lt;p&gt;코드는 "이번 실행이 예약된 자동 라운드인지, 제가 수동으로 돌린 건지"를 매번 구분해야 합니다. 자동 라운드에만 적용되는 지각 가드가 있기 때문입니다.&lt;/p&gt;

&lt;p&gt;예약된 시각보다 한참 늦게 시작된 자동 라운드는 타이밍이 깨졌다는 신호라 발주를 막는 게 맞습니다. 하지만 제가 지금 막 수동으로 돌린 라운드라면 "지각"이라는 판정 자체가 의미가 없습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  무슨 일이 있었나
&lt;/h2&gt;

&lt;p&gt;안전장치 때문에 매수가 막힌 걸 풀고, 수동으로 라운드를 돌렸습니다. 그런데 시스템이 이 실행을 예약된 라운드로 인식하고, 지각 가드를 적용해 발주를 차단했습니다.&lt;/p&gt;

&lt;p&gt;판정 근거를 보니 "원래 정해진 시각보다 한참 지각했다"는 이유였습니다. 지금 막 수동으로 돌린 실행에 "지각"이라는 말 자체가 성립하지 않는 상황이었습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  처음엔 뭐라고 생각했나
&lt;/h2&gt;

&lt;p&gt;이 판별 로직은 한 달쯘 전에도 같은 증상으로 한 번 고친 적이 있었습니다. 그때는 자동화 유닛 설정 파일에 "이건 예약 실행"이라는 값을 아예 박아둔 게 원인이었습니다.&lt;/p&gt;

&lt;p&gt;유닛 설정에 값을 박아두면, 제가 수동으로 그 유닛을 실행시켜도 같은 값이 그대로 따라옵니다. 그래서 그때는 그 하드코딩을 걷어내고, 대신 운영체제가 실행 시점에 넣어주는 표시로 판별하도록 바꿨습니다. 자동화 스케줄러가 타이머로 깨울 때만 붙는 환경변수였습니다.&lt;/p&gt;

&lt;p&gt;문서에도 "제가 수동으로 실행하면 이 표시가 비어 있다"고 적어뒀습니다. 그래서 이번에도 같은 쪽을 의심했습니다. 그 환경변수가 또 어딘가에 잘못 박혀 있는 게 아닐까 하고 설정 파일을 뒤졌습니다.&lt;/p&gt;

&lt;p&gt;설정 파일에는 문제가 없었습니다. 환경변수 주입 방식도 문서에 적힌 그대로였습니다. 뭔가 다른 원인이 있었습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  진짜 원인
&lt;/h2&gt;

&lt;p&gt;실제로 들여다보니, 그 환경변수 자체가 "매번 새로 세팅된다"는 전제가 틀려 있었습니다.&lt;/p&gt;

&lt;p&gt;운영체제 서비스 관리자가 하는 일이었습니다. 타이머가 하루 중 처음으로 어떤 작업을 깨우면, 그 작업에 "너를 깨운 건 이 타이머야"라는 표시를 붙입니다.&lt;/p&gt;

&lt;p&gt;문제는 이 표시가 작업이 끝나고 비활성 상태로 돌아간 뒤에도 지워지지 않는다는 점이었습니다. 그리고 같은 날 두 번째, 세 번째로 같은 타이머가 다시 깨워도 이 표시는 덮어써지지 않았습니다.&lt;/p&gt;

&lt;p&gt;그러니 그날 아침 자동 라운드가 한 번 돌고 난 뒤, 오후에 제가 같은 작업을 수동으로 실행시키면, 아침에 붙었던 "타이머가 깨웠다"는 표시가 그대로 상속됐습니다. 코드는 이 표시만 보고 "이건 타이머가 깨운 예약 실행"이라고 결론 내렸습니다.&lt;/p&gt;

&lt;p&gt;한 달 전 사고와 이번 사고는 증상이 똑같았습니다. 수동 실행이 예약 실행으로 오판된 것입니다. 하지만 원인은 완전히 달랐습니다.&lt;/p&gt;

&lt;p&gt;한 달 전엔 "표시를 누가 박아뒀는가"의 문제였고, 이번엔 "표시가 예상보다 훨씬 오래 산다"는 운영체제 동작 자체의 문제였습니다. 지난번 수선은 표시를 누가 세팅하는지만 고쳤을 뿐, 그 표시의 수명이 얼마나 긴지는 검증한 적이 없었습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  어떻게 고쳤나
&lt;/h2&gt;

&lt;p&gt;처음 떠올린 방법은 그 표시가 얼마나 오래된 값인지를 재서, 너무 오래됐으면 상속된 낡은 표시로 보고 버리는 것이었습니다. 운영체제가 표시와 함께 "이 표시가 만들어진 시각"도 함께 넘겨주기 때문에 가능할 것 같았습니다.&lt;/p&gt;

&lt;p&gt;구현하면서 실제 로그를 찍어보니 이 방법은 쓸 수 없었습니다. 그 "만들어진 시각" 값도 하루 중 첫 번째 발화 시각에 고정된 채, 이후 발화에서 전혀 갱신되지 않았습니다.&lt;/p&gt;

&lt;p&gt;이 값으로 나이를 재면, 그날 진짜로 타이머가 정상적으로 깨운 두 번째·세 번째 라운드까지 "낡은 표시니까 수동 실행"으로 잘못 떨어질 뻔했습니다. 관찰된 사례 하나(그날 첫 발화)에만 맞춘 수정이 다른 정상 사례를 깨뜨릴 뻔한 겁니다. 배포 전에 실측으로 걸러냈습니다.&lt;/p&gt;

&lt;p&gt;최종적으로는 비교 대상을 바꿨습니다. 타이머 쪽에는 "마지막으로 발화한 시각"이 매 발화마다 갱신되는 별도의 값이 있었습니다.&lt;/p&gt;

&lt;p&gt;이 값과, 서비스가 실제로 지금 막 활성화된 시각을 대조했습니다. 두 시각의 간격이 짧으면 방금 그 타이머가 깨운 것이고, 간격이 크게 벌어져 있으면 예전 표시가 상속된 수동 실행으로 판정했습니다.&lt;/p&gt;

&lt;p&gt;둘 중 어느 쪽도 조회나 해석이 안 되는 경우를 대비해 방향도 하나로 정해뒀습니다. 판별이 애매하면 "예약 실행(지각 가드 적용)" 쪽으로 기울이게 했습니다. 자동 라운드를 수동으로 잘못 보고 보호장치를 빼먹는 쪽보다, 수동 라운드를 예약으로 잘못 보고 한 번 막히는 쪽이 훨씬 안전하기 때문입니다.&lt;/p&gt;

&lt;p&gt;막히는 쪽으로 fail-safe를 잡아도 빠져나갈 길은 남겨뒀습니다. 판별 로직과 별개로, 제가 명시적으로 "이건 수동이다/예약이다"라고 선언할 수 있는 값이 있어서, 이 값이 있으면 어떤 추론도 무시하고 그걸 그대로 따릅니다.&lt;/p&gt;

&lt;p&gt;마지막으로 판정에 쓰인 근거(발화와 시작 사이 간격, 재시작 횟수 등)를 실행 기록 한 줄에 같이 남겼습니다. 다음에 같은 종류의 오판이 또 생기면, 로그를 뒤지지 않고 이 한 줄만 보고 원인을 바로 알 수 있게 하기 위해서였습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  일반화하면
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"이 실행이 자동인지 수동인지"를 주변 메타데이터로 추론하려 한다면, 그 메타데이터의 수명을 직접 확인해야 합니다.&lt;/strong&gt; 환경변수든 타임스탬프든, "매번 새로 세팅된다"는 전제는 문서나 직관이 아니라 실측으로 검증해야 합니다.&lt;/p&gt;

&lt;p&gt;이번 사건의 첫 번째 수선도 "사람이 수동으로 실행하면 이 표시가 비어 있다"는, 검증 없이 적어둔 문장이 틀린 전제였습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;같은 증상이 재발했다고 해서 같은 원인이라고 단정하면 안 됩니다.&lt;/strong&gt; 겉보기엔 완전히 같은 버그(수동 실행이 예약으로 오판됨)였지만, 원인 계층이 전혀 달랐습니다.&lt;/p&gt;

&lt;p&gt;전에 고친 지점만 다시 들여다봤다면 시간을 허비했을 겁니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;외부 시스템의 동작은 문서가 아니라 실측으로 확인해야 합니다.&lt;/strong&gt; 운영체제나 프레임워크가 내부적으로 뭘 하는지는 개발자가 통제할 수 없는 영역입니다.&lt;/p&gt;

&lt;p&gt;"당연히 이럴 것"이라는 가정 대신, 실제 로그를 찍어서 확인하는 쪽이 결국 더 쌉니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;관찰된 사례 하나에 맞춘 수정은 다른 사례를 깨뜨릴 수 있습니다.&lt;/strong&gt; 첫 번째 고침 아이디어는 하루 중 첫 번째 발화라는 사례 하나만 보고 설계됐습니다.&lt;/p&gt;

&lt;p&gt;실제로 그대로 적용했다면 같은 날 두 번째·세 번째 발화를 오판했을 겁니다. 배포 전에 여러 사례로 실측해 본 게 이걸 막았습니다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;판별이 애매할 때 어느 쪽으로 기울일지는 미리 정해야 합니다.&lt;/strong&gt; 두 가지 오판 중 하나가 다른 하나보다 덜 위험하다면, 애매한 경우엔 그 안전한 쪽으로 기울여야 합니다.&lt;/p&gt;

&lt;p&gt;그리고 그 fail-safe 때문에 막히는 정당한 경우를 위해, 추론을 무시하고 바로 꽂을 수 있는 명시적 탈출구도 같이 만들어야 합니다.&lt;/p&gt;

&lt;p&gt;이 사건 이후로는 "환경변수나 타임스탬프가 매번 새로 세팅되는가"를 가정하는 코드를 새로 짤 때마다, 그 가정을 실제로 한 번은 눈으로 확인하고 넘어가는 습관이 생겼습니다.&lt;/p&gt;

</description>
      <category>softwarequality</category>
      <category>postmortem</category>
      <category>debugging</category>
      <category>quant</category>
    </item>
    <item>
      <title>A Manual Run Misclassified as a Scheduled Run - systemd Activation Metadata Outlived My Assumptions</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:05:57 +0000</pubDate>
      <link>https://dev.to/finaltype/a-manual-run-misclassified-as-a-scheduled-run-systemd-activation-metadata-outlived-my-assumptions-32jp</link>
      <guid>https://dev.to/finaltype/a-manual-run-misclassified-as-a-scheduled-run-systemd-activation-metadata-outlived-my-assumptions-32jp</guid>
      <description>&lt;p&gt;&lt;em&gt;A bug I thought I'd fixed a month earlier came back with a completely different root cause&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My automated trading system runs trading rounds on a fixed schedule during the day. Every so often, though, I need to kick off an extra round by hand.&lt;/p&gt;

&lt;p&gt;That usually happens when a safety mechanism has blocked buying for a while, and I want to lift it and resume trading manually. This safety mechanism and the manual-intervention procedure aren't part of the stock-picking logic — they belong to the order execution/safety layer, whose full design I've written up separately in &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/05_execution_architecture/" rel="noopener noreferrer"&gt;the live-account post&lt;/a&gt;(new tab).&lt;/p&gt;

&lt;p&gt;The code has to tell, every time it runs, whether this invocation is a scheduled automatic round or one I started by hand. That matters because there's a lateness guard that only applies to automatic rounds.&lt;/p&gt;

&lt;p&gt;An automatic round that starts much later than its scheduled time is a sign something broke in the timing, so blocking it makes sense. But if I just kicked off a round manually right now, calling it "late" doesn't mean anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;I lifted a buying block that a safety mechanism had triggered, then started a round manually. The system classified this run as a scheduled round and applied the lateness guard, which blocked the orders.&lt;/p&gt;

&lt;p&gt;The stated reason was that the run was "much later than its originally scheduled time." For a round I had just started by hand, "late" wasn't a meaningful judgment at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I thought at first
&lt;/h2&gt;

&lt;p&gt;I'd fixed this exact classification logic once before, about a month earlier, for the same symptom. Back then the cause was that a value meaning "this is a scheduled run" had been hardcoded directly into the automation unit's configuration file.&lt;/p&gt;

&lt;p&gt;Once a value like that is baked into the unit config, it follows along even when I start that unit by hand. So that time, I ripped out the hardcoding and switched to a marker the OS itself injects at invocation time — an environment variable the scheduler only attaches when a timer wakes the job.&lt;/p&gt;

&lt;p&gt;I'd even written in the docstring, "if a human starts this manually, this marker is empty." So this time I suspected the same thing — that the environment variable had somehow gotten hardcoded somewhere again — and went digging through the config files.&lt;/p&gt;

&lt;p&gt;The config files were clean. The environment variable injection matched the documentation exactly. Something else was going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual root cause
&lt;/h2&gt;

&lt;p&gt;Digging further, it turned out the assumption that this environment variable was "freshly set every time" was itself wrong.&lt;/p&gt;

&lt;p&gt;This was the OS's service manager at work. When a timer wakes a job for the first time in a day, it attaches a marker to that job saying "a timer woke you up."&lt;/p&gt;

&lt;p&gt;The problem was that this marker doesn't get cleared once the job goes back to inactive. And when the same timer fires again later that day — a second or third time — the marker doesn't get overwritten either.&lt;/p&gt;

&lt;p&gt;So after the automatic round ran once that morning, when I started the same job by hand in the afternoon, the "a timer woke me" marker from the morning was still there, inherited as-is. The code looked at only that marker and concluded "this is a scheduled run that a timer fired."&lt;/p&gt;

&lt;p&gt;The symptom was identical to the incident a month earlier — a manual run misclassified as scheduled. But the root cause was completely different.&lt;/p&gt;

&lt;p&gt;The earlier incident was about who had set the marker. This one was about the marker living far longer than expected — a quirk of how the OS itself behaves. My earlier fix only changed who sets the marker; it never checked how long that marker actually stays valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I fixed it
&lt;/h2&gt;

&lt;p&gt;My first idea was to measure how old the marker was, and discard it as an inherited stale marker if it was too old. The OS also hands back a timestamp for "when this marker was created," so this seemed workable.&lt;/p&gt;

&lt;p&gt;Once I actually logged real values while implementing it, this approach fell apart. That "created at" timestamp was also pinned to the very first firing of the day and never updated on subsequent firings.&lt;/p&gt;

&lt;p&gt;Measuring age against that value would have misjudged the second and third rounds that day — rounds a timer had genuinely, correctly fired — as "marker is old, so this must be manual." A fix tailored to one observed case (the day's first firing) would have broken other, perfectly valid cases. I caught this with real logs before shipping it.&lt;/p&gt;

&lt;p&gt;So I changed what I was comparing. On the timer side, there was a separate value — "the time of the last firing" — that does get updated on every single firing.&lt;/p&gt;

&lt;p&gt;I compared that value against the moment the service itself actually transitioned into an active state. If the gap between the two was short, a timer had genuinely just fired. If the gap was large, the old marker had been inherited by a manual run.&lt;/p&gt;

&lt;p&gt;I also picked a direction in advance for the case where neither value could be looked up or parsed. When the classification is ambiguous, it leans toward "scheduled (lateness guard applies)." Misreading a manual round as scheduled and getting blocked once is far less dangerous than misreading an automatic round as manual and silently skipping the safety guard.&lt;/p&gt;

&lt;p&gt;Even with that fail-safe leaning toward "blocked," I kept an escape hatch. Separate from the inference logic, there's an explicit value I can set myself to declare "this is manual" or "this is scheduled" — if that's set, it overrides any inference entirely.&lt;/p&gt;

&lt;p&gt;Finally, I logged the evidence behind each verdict — the gap between firing and starting, the restart count — as a single field in the execution record. The next time this class of misjudgment shows up, that one line should point straight at the cause instead of forcing a log hunt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generalizing from this
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you're inferring "automatic or manual" from surrounding metadata, you have to verify that metadata's lifetime yourself.&lt;/strong&gt; Whether it's an environment variable or a timestamp, the assumption that it's "freshly set every time" needs to be checked against real behavior, not documentation or intuition.&lt;/p&gt;

&lt;p&gt;Even the first fix in this story rested on an unverified assumption — "this marker is empty when a human runs it manually" — that turned out to be false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A repeat of the same symptom doesn't mean the same root cause.&lt;/strong&gt; On the surface this was the exact same bug (manual run misclassified as scheduled), but the layer where it actually broke was completely different.&lt;/p&gt;

&lt;p&gt;Looking only at the spot I'd patched before would have wasted time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify external systems by observation, not documentation.&lt;/strong&gt; What an OS or framework does internally is outside a developer's control.&lt;/p&gt;

&lt;p&gt;Instead of assuming "it must work this way," logging the real values is cheaper in the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A fix tailored to one observed case can break other cases.&lt;/strong&gt; My first fix idea was designed around a single case — the day's first firing — and nothing else.&lt;/p&gt;

&lt;p&gt;Had I shipped it as-is, it would have misjudged the second and third firings that same day. Testing against multiple real cases before shipping is what caught it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decide in advance which way to lean when a classification is ambiguous.&lt;/strong&gt; If one kind of misjudgment is less dangerous than the other, ambiguous cases should lean toward the safer one.&lt;/p&gt;

&lt;p&gt;And because that fail-safe will sometimes block a legitimate case, you need an explicit escape hatch that overrides the inference entirely.&lt;/p&gt;

&lt;p&gt;Since this incident, every time I write code that assumes an environment variable or timestamp gets freshly set on every invocation, I make it a habit to actually go verify that assumption with my own eyes at least once.&lt;/p&gt;

</description>
      <category>softwarequality</category>
      <category>postmortem</category>
      <category>debugging</category>
      <category>quant</category>
    </item>
    <item>
      <title>"[261021] 자동매매 성과 재점검 - 지수 대비 냉정한 판정과 돈 버는 구조 설계"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:05:56 +0000</pubDate>
      <link>https://dev.to/finaltype/261021-jadongmaemae-seonggwa-jaejeomgeom-jisu-daebi-naengjeonghan-panjeonggwa-don-beoneun-gujo-seolgye-1jon</link>
      <guid>https://dev.to/finaltype/261021-jadongmaemae-seonggwa-jaejeomgeom-jisu-daebi-naengjeonghan-panjeonggwa-don-beoneun-gujo-seolgye-1jon</guid>
      <description>&lt;p&gt;&lt;em&gt;지수보다 못하면 그만둘 이유가 있다는 질문을 AI 자문에게 던져 판정을 받고, 이어서 실제로 비용을 넘어서는 구조 설계안까지 받았습니다.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  지수보다 못한 거 아니냐고 물었다
&lt;/h2&gt;

&lt;p&gt;실거래 계좌 누적 수익률을 보고 있으면 기분이 안 좋아지는 구간이 있습니다.&lt;/p&gt;

&lt;p&gt;그냥 지수를 사서 들고 있는 것보다 못하면, 이 시스템에 계속 자원을 넣을 이유가 없다는 생각이 들었습니다. 그래서 오늘 AI 자문에게 봐주는 것 없이 엄격하게 판정해 달라고 요청했습니다. 정지·축소·유지 중 어느 쪽이 맞는지, 증거가 말해줄 수 있는 것과 없는 것까지 구분해서 달라고 했습니다.&lt;/p&gt;

&lt;p&gt;돌아온 답은 체감과 조금 달랐습니다.&lt;/p&gt;

&lt;p&gt;미리 정해둔 비교 기준(지수 벤치마크)으로 보면 실제로는 열위가 아니었습니다. 다만 그렇다고 "종목을 잘 고르고 있다"는 증거도 없었습니다. 손실 대부분은 그냥 전체 시장이 안 좋았던 탓이고, 선택 자체의 기여는 거의 0에 가깝다는 판정이었습니다.&lt;/p&gt;

&lt;p&gt;반면 회전율에서 나오는 비용은 확정적으로 매일 빠져나가고 있었습니다. 그래서 자문은 "지금 당장 멈출 이유는 없지만, 앞으로 판단 기준에 스킬 증거와 상대 손실 한도를 더 명확히 못박아두자"는 쪽으로 결론을 냈습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  "감시 말고 실제로 돈 버는 구조를 내라"
&lt;/h2&gt;

&lt;p&gt;첫 판정으로는 부족했습니다.&lt;/p&gt;

&lt;p&gt;멈출지 말지를 가르는 감시 규칙 말고, 비용을 떼고도 지수를 이길 수 있는 실제 구조를 설계해보라고 다시 요청했습니다. 3개월 넘게 쌓인 기록이 있으니 이 시스템의 강점과 약점을 이제는 구분할 수 있어야 한다는 전제였습니다.&lt;/p&gt;

&lt;p&gt;이번에 나온 제안은 자산을 두 층으로 나누는 구조였습니다.&lt;/p&gt;

&lt;p&gt;자본의 큰 부분은 지수를 그대로 복제하는 쪽에 묶어서, 적어도 지수보다 못할 위험 자체를 구조적으로 없앱니다. 나머지 작은 비중만 지금까지 관측에서 실제로 신호가 보였던 영역에 집중시키자는 제안이었습니다.&lt;/p&gt;

&lt;p&gt;그리고 그 작은 비중을 키우기 전에, 몇 가지 가설을 미리 정해두고 먼저 검증부터 하자는 절차도 함께 왔습니다. 결과를 보고 나서 기준을 끼워 맞추는 일을 막기 위한 장치입니다.&lt;/p&gt;

&lt;p&gt;두 자문 결과 모두 아직 코드에는 전혀 손대지 않았고, 오너의 승인을 기다리는 상태로 남겨뒀습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  그 외 — 작은 꼬리 하나 닫았다
&lt;/h2&gt;

&lt;p&gt;최근 야간 배치의 지표 정정 꼬리가 예상 범위 안에서 움직이는지 보던 건이 있었는데, 오늘로 사흘 연속 정상 관측이 확인돼 마무리했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  왜 중요한가
&lt;/h2&gt;

&lt;p&gt;오늘의 핵심은 "나쁘다"는 체감과 "증거가 말해주는 것"을 분리한 것입니다.&lt;/p&gt;

&lt;p&gt;누적 수익률만 보면 안 좋아 보이지만, 그게 시장 전체가 나빴기 때문인지 시스템의 선택이 나빴기 때문인지는 다른 질문입니다. 둘을 섞어서 판단하면 엉뚱한 곳을 고치게 됩니다.&lt;/p&gt;

&lt;p&gt;또 하나는 "멈출 것인가 계속할 것인가"라는 이분법에서 벗어난 것입니다.&lt;/p&gt;

&lt;p&gt;지수 추종으로 하방을 막아두고, 검증된 영역에만 작게 베팅하는 구조라면 두 선택지 사이에 길이 하나 더 있었습니다. 다만 이 길도 아직 제안일 뿐, 실제로 돈이 되는지는 승인과 검증을 거쳐야 압니다.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[Oct 21] Rechecking Live Performance — A Cold-Blooded Verdict Against the Index, and a Design That Actually Makes Money"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:05:20 +0000</pubDate>
      <link>https://dev.to/finaltype/oct-21-rechecking-live-performance-a-cold-blooded-verdict-against-the-index-and-a-design-that-52dj</link>
      <guid>https://dev.to/finaltype/oct-21-rechecking-live-performance-a-cold-blooded-verdict-against-the-index-and-a-design-that-52dj</guid>
      <description>&lt;p&gt;&lt;em&gt;Asked an AI advisor for a hard-nosed verdict on whether live returns are beating the index, then pushed it further for a structure that actually pays off after costs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it actually worse than the index?
&lt;/h2&gt;

&lt;p&gt;There are stretches where looking at the live account's cumulative return just feels bad.&lt;/p&gt;

&lt;p&gt;If it's doing worse than simply buying and holding the index, there's no reason to keep pouring resources into this system. So today I asked an AI advisor to give me a verdict with no sugarcoating — spell out whether the right call is to stop, scale down, or keep going, and be explicit about what the evidence can and can't tell us.&lt;/p&gt;

&lt;p&gt;The answer that came back didn't quite match the gut feeling.&lt;/p&gt;

&lt;p&gt;Against the benchmark set in advance, the account wasn't actually behind. But that didn't mean there was any evidence it was picking stocks well, either. Most of the loss traced back to the market being bad overall — the contribution from stock selection itself was close to zero.&lt;/p&gt;

&lt;p&gt;Meanwhile, the costs from turnover were coming out for certain, every single day. So the advisor's conclusion was: no reason to stop right now, but going forward the decision criteria need to pin down evidence of real skill and a hard limit on relative drawdown more explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Don't give me monitoring rules — give me a structure that actually makes money"
&lt;/h2&gt;

&lt;p&gt;The first verdict wasn't enough.&lt;/p&gt;

&lt;p&gt;I asked again — not for rules that just decide when to stop, but for an actual structure that can beat the index after costs. The premise was that with more than three months of track record behind it, the system's strengths and weaknesses should be distinguishable by now.&lt;/p&gt;

&lt;p&gt;What came back this time split the capital into two layers.&lt;/p&gt;

&lt;p&gt;A large share of the capital gets parked in something that simply replicates the index, which structurally removes the risk of underperforming it at all. Only a small remaining slice gets concentrated into the areas where a real signal had actually shown up in past observations.&lt;/p&gt;

&lt;p&gt;And before that small slice gets any bigger, the proposal calls for pre-registering a handful of hypotheses and testing them first. It's a safeguard against fitting the criteria to the results after the fact.&lt;/p&gt;

&lt;p&gt;Neither piece of advice has touched any code yet — both are sitting there waiting on approval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also today — closed out a small loose end
&lt;/h2&gt;

&lt;p&gt;A ticket tracking whether a nightly batch job's data-correction tail stayed within expected bounds got closed today, after three straight days of normal readings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;The core of today was separating "this feels bad" from "what the evidence actually says."&lt;/p&gt;

&lt;p&gt;Looking at cumulative return alone, it looks rough — but whether that's because the whole market was bad or because the system's choices were bad are two different questions. Mixing them up means fixing the wrong thing.&lt;/p&gt;

&lt;p&gt;The other piece was stepping outside the stop-or-continue binary.&lt;/p&gt;

&lt;p&gt;A structure that caps the downside with index replication and only makes small bets where there's validated signal turned out to be a third option between the two. Though it's still just a proposal — whether it actually pays off depends on approval and testing still to come.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[261019] 증권사 경로 단일화 - 모의 계좌 비교 시스템을 새로 갈아끼웠다"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:05:19 +0000</pubDate>
      <link>https://dev.to/finaltype/261019-jeunggweonsa-gyeongro-danilhwa-moyi-gyejwa-bigyo-siseutemeul-saero-galaggiweossda-537c</link>
      <guid>https://dev.to/finaltype/261019-jeunggweonsa-gyeongro-danilhwa-moyi-gyejwa-bigyo-siseutemeul-saero-galaggiweossda-537c</guid>
      <description>&lt;p&gt;&lt;em&gt;오래 쓰던 모의 비교 트랙을 접고 새 시뮬레이션 브로커로 하루 만에 갈아탔고, 되돌리기 경로는 남겨뒀습니다.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  증권사 경로를 하나로 좁혔다
&lt;/h2&gt;

&lt;p&gt;그동안 메인 증권사 말고 다른 증권사도 모의·백업 용도로 같이 써왔습니다.&lt;/p&gt;

&lt;p&gt;오늘 그 경로를 메인 증권사 하나로 좁히는 작업을 마무리했습니다. 개인용 알림 기능이나 수동 리밸런싱처럼 더는 쓰지 않는 보조 기능도 함께 걷어냈습니다.&lt;/p&gt;

&lt;p&gt;경로는 좁혔지만 보조 증권사의 연동 코드와 인증키는 지우지 않고 그대로 남겨뒀습니다. 되돌릴 일이 생겼을 때 바로 다시 쓸 수 있는 경로를 남겨두는 쪽을 택했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  모의 계좌 비교 시스템을 새 걸로 교체했다
&lt;/h2&gt;

&lt;p&gt;오늘 가장 큰 작업은 &lt;a href="https://finaltype.github.io/quant-blog/architecture/06_paper_trading_validation/" rel="noopener noreferrer"&gt;모의 계좌로 완전자동매매를 먼저 검증하는 트랙&lt;/a&gt;(새 창)에서 쓰던 비교 축을 바꾼 것입니다.&lt;/p&gt;

&lt;p&gt;이제까지는 보조 증권사 계좌로 모의매매를 흉내 내는 방식을 썼는데, 오늘부터 직접 만든 시뮬레이션 브로커 기반 트랙으로 넘어갔습니다.&lt;/p&gt;

&lt;p&gt;설계, 구현, 테스트, 배포까지 하루 안에 끝냈습니다. 과거 데이터를 새 트랙으로 옮기는 초기화 작업도 있었는데, 정확한 금액은 기록만 남기고 실행 자체는 자동화에 맡겼습니다.&lt;/p&gt;

&lt;p&gt;전환 과정에서 실거래용 분봉·호가 수집기도 메인 증권사 경로로 바꿔 새로 설치했습니다. 다만 아직은 그림자 모드로만 돌려서 실제 매매 판단에는 영향을 주지 않습니다.&lt;/p&gt;

&lt;p&gt;이만한 전환을 걸어두고 다음 날 아침까지 사람이 지켜볼 수는 없어서, 핵심 확인 항목(동결 상태·비교 트랙 정상 기동·수집기 동작 여부 등)을 사람 없이 자동으로 점검해 결과를 알림으로 보내는 점검기를 같이 만들어뒀습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  그 외 — 대규모 코드리뷰와 다음 실험 예약
&lt;/h2&gt;

&lt;p&gt;최근 며칠 쌓인 변경사항 전체를 AI에게 통째로 검토시켰습니다.&lt;/p&gt;

&lt;p&gt;심각한 문제는 없었고, 중요도 있는 지적은 대부분 당일 안에 고쳐서 반영했습니다.&lt;/p&gt;

&lt;p&gt;별도로 진행 중이던 서술 방식 실험(모델이 분석 결과를 글로 풀어낼 때 쓰는 표현 단위를 바꿔보는 실험) 하나는 설계와 사전등록을 끝내고, 이번 주말 야간 GPU 여유 시간에 테스트를 돌리도록 예약해뒀습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  왜 중요한가
&lt;/h2&gt;

&lt;p&gt;오늘 핵심은 "경로를 단순화하면서도 되돌릴 구멍은 남겨뒀다"는 점입니다.&lt;/p&gt;

&lt;p&gt;보조 증권사 코드와 키를 지우는 선택지도 있었지만, 메인 경로에 문제가 생겼을 때 바로 돌아갈 곳이 없어지는 위험과 바꿨습니다. 단순화와 복원력 중 하나만 고를 필요는 없었습니다.&lt;/p&gt;

&lt;p&gt;교체 자체보다 중요한 건 "지켜보는 장치"를 같이 만든 것이었습니다.&lt;/p&gt;

&lt;p&gt;큰 전환일수록 다음 날 아침에 사람이 로그를 처음부터 끝까지 읽는 방식에 기대면 놓치는 게 생깁니다. 자동 점검기와 자동 코드리뷰 둘 다, 사람이 밤새 지키지 않아도 다음 날 문제를 먼저 알려주게 만드는 장치라는 점에서 같은 방향이었습니다.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[Oct 19] Consolidating Broker Paths — Swapping Out the Paper-Trading Comparison Track"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:03:52 +0000</pubDate>
      <link>https://dev.to/finaltype/oct-19-consolidating-broker-paths-swapping-out-the-paper-trading-comparison-track-2ngb</link>
      <guid>https://dev.to/finaltype/oct-19-consolidating-broker-paths-swapping-out-the-paper-trading-comparison-track-2ngb</guid>
      <description>&lt;p&gt;&lt;em&gt;Retired an old paper-trading comparison track for a new simulated-broker one in a single day, while deliberately leaving a way back.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Narrowing down to one broker path
&lt;/h2&gt;

&lt;p&gt;For a while I'd been running a secondary broker alongside the main one, for paper trading and as a backup.&lt;/p&gt;

&lt;p&gt;Today I finished narrowing that down to a single, main-broker path. I also stripped out side features that weren't earning their keep anymore — a personal-portfolio notification feature and a manual rebalancing flow.&lt;/p&gt;

&lt;p&gt;I narrowed the path, but I didn't delete the secondary broker's integration code or its API keys. I kept a way back in case I ever need that path again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swapping out the paper-trading comparison system
&lt;/h2&gt;

&lt;p&gt;The biggest piece of work today was replacing the comparison track used by the &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/06_paper_trading_validation/" rel="noopener noreferrer"&gt;fully-autonomous paper-trading validation lane&lt;/a&gt;(new tab) — the one that lets the system trade on its own, without human sign-off, before any live-account promotion.&lt;/p&gt;

&lt;p&gt;Until today, that lane used a secondary broker's paper account to simulate trades. Starting today, it runs on a purpose-built simulated broker instead.&lt;/p&gt;

&lt;p&gt;I took it from design through implementation, tests, and deployment in a single day. There was also a one-time migration of historical data into the new track — I kept the exact amount out of the public record and let automation handle the execution itself.&lt;/p&gt;

&lt;p&gt;As part of the switch, I also installed a new minute-bar and order-book collector on the main broker's path. It's still shadow-mode only for now, so it doesn't affect any real trading decisions yet.&lt;/p&gt;

&lt;p&gt;A change this size can't be watched overnight by a person, so I also built a checker that verifies the key signals the next morning on its own — freeze state, the new comparison track starting up correctly, the collector running — and reports the result without anyone needing to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also today — a large code review and a scheduled experiment
&lt;/h2&gt;

&lt;p&gt;I had another AI review everything that had changed over the past several days, all at once.&lt;/p&gt;

&lt;p&gt;Nothing critical came back. Most of the notable findings got fixed and merged the same day.&lt;/p&gt;

&lt;p&gt;Separately, an experiment on how the model phrases its analysis output (a wording-unit variant) finished its design and pre-registration, and got scheduled for this weekend's overnight GPU window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;What mattered today was narrowing the path while deliberately leaving a way back.&lt;/p&gt;

&lt;p&gt;I could have deleted the secondary broker's code and keys outright, but that would trade away a fast recovery path if the main one ever breaks. Simplifying and staying resilient didn't have to be an either/or choice.&lt;/p&gt;

&lt;p&gt;What mattered more than the swap itself was building something to watch it. A big transition is easy to half-miss if the only plan is reading logs top-to-bottom the next morning. Both the automated checker and the automated code review point the same direction: let the system flag problems by morning instead of relying on someone staying up all night.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[261012~261018] 실험 버그, GPU 사고, 실계좌 매수 차단 — 사람 개입을 감시 대상으로 만든 한 주"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:03:51 +0000</pubDate>
      <link>https://dev.to/finaltype/261012261018-silheom-beogeu-gpu-sago-silgyejwa-maesu-cadan-saram-gaeibeul-gamsi-daesangeuro-mandeun-han-ju-3hg9</link>
      <guid>https://dev.to/finaltype/261012261018-silheom-beogeu-gpu-sago-silgyejwa-maesu-cadan-saram-gaeibeul-gamsi-daesangeuro-mandeun-han-ju-3hg9</guid>
      <description>&lt;p&gt;&lt;em&gt;이번 주 터진 사고 세 건 모두 직접 손을 대서 넘겼는데, 그 뒤로 "같은 개입이 다시 생기면 자동으로 기록·경보되게" 만드는 작업이 매번 따라붙었습니다.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;이번 주는 사람이 직접 손을 대야 했던 상황이 세 번 있었습니다.&lt;/p&gt;

&lt;p&gt;실험 인프라 버그, GPU 드라이버 꼬임, 실계좌 매수 차단. 성격은 다 다르지만 처리 방식은 똑같았습니다.&lt;/p&gt;

&lt;p&gt;급한 불을 끄는 것과, 같은 상황이 다시 와도 사람 없이 혹은 최소한 기록이 남도록 장치를 추가하는 것을 분리해서 둘 다 했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  월요일, 실험을 막 돌렸더니 장치에서 버그가 세 번 터졌다
&lt;/h2&gt;

&lt;p&gt;며칠 전 설계해 둔 실험을 이번 주 초저녁에 실제로 돌렸습니다. AI에게 주는 입력에 미국장 시세 정보를 한 줄 더 넣었을 때 판단이 나아지는지 확인하는 A/B 테스트였습니다.&lt;/p&gt;

&lt;p&gt;돌리자마자 버그 세 개가 연달아 나왔습니다. 날짜별 종목 목록을 교집합이 아니라 합집합으로 잡던 로직, 재시도 기능이 옛 결과를 그대로 재활용해버린 충돌, 검증용 표식 문구가 모호해서 모델이 바꿔 쓴 걸 오탐으로 잡아낸 문제였습니다.&lt;/p&gt;

&lt;p&gt;세 버그 모두 사전 점검에서는 안 잡혔고, 실제로 돌려봐야만 드러나는 종류였습니다. 첫 런이 예상보다 빨리 끝났다는 작은 이상 신호를 그냥 넘기지 않은 덕에 두 번째 버그를 알아챘습니다.&lt;/p&gt;

&lt;p&gt;같은 날 수치형 응답을 점검하는 기준선도 손봤습니다. 평균값이 예전보다 낮아진 걸 반영 안 한 옛 규칙이 닷새에 한 번꼴로 가짜 경보를 울리고 있었던 걸 바로잡았습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  화요일, GPU 드라이버가 꼬이고 전체 구조를 처음 그려봤다
&lt;/h2&gt;

&lt;p&gt;밤사이 걸린 자동 업데이트가 그래픽카드 드라이버를 절반만 적용했습니다. 디스크의 라이브러리는 새 버전인데 커널에 올라간 모듈은 옛 버전인 채로 엇갈리면서, GPU를 쓰는 작업이 전부 막히고 진행 중이던 실험도 멈췄습니다.&lt;/p&gt;

&lt;p&gt;재부팅하면 해결될 상황이었지만, 바로 하지 않고 조건을 걸었습니다. 가상머신이 꺼져 있고 백업이 끝나 있고 GPU 작업이 겹치지 않을 때만 자동 재부팅되도록 예약하고, 재부팅 뒤 버전 확인과 멈췄던 실험 재개까지 사람 없이 이어지도록 미리 짜뒀습니다.&lt;/p&gt;

&lt;p&gt;실제로는 예약 시각 전에 직접 재부팅했는데도, 뒤에 걸어둔 확인·재개 절차가 그대로 작동해서 중복 없이 깔끔하게 넘어갔습니다.&lt;/p&gt;

&lt;p&gt;같은 날 &lt;a href="https://finaltype.github.io/quant-blog/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;이 프로젝트&lt;/a&gt;(새 창) 전체가 돈을 어떻게 움직이고 어떤 장치가 지키는지를 한 장의 구성도로 처음 그렸습니다. 여러 달에 걸쳐 조각조각 만들어온 구조라 전체를 한눈에 보는 그림이 그동안 없었습니다.&lt;/p&gt;

&lt;p&gt;이 구성도를 AI에게 통째로 검토시켜서 감시 공백을 찾았습니다. 사전 계산 실패 시 조용히 다른 경로로 넘어가는 지점, 외부 시세 수집 경로가 대체 없이 단일 경로에만 의존하는 점, 백업은 매일 돌지만 실제 복원은 한 번도 안 해본 점이 나왔습니다. 당장 고치지는 않고 우선순위를 매겨 할 일 목록에 올렸습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  수요일, 실계좌 매수가 막혀서 직접 매매를 쳤고 그날 안에 구멍 8개를 메웠다
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://finaltype.github.io/quant-blog/architecture/05_execution_architecture/" rel="noopener noreferrer"&gt;실전 계좌&lt;/a&gt;(새 창)의 한 슬리브에서 손실제한 안전장치가 작동해 매수만 막혔습니다. 하루짜리 장치라 다음 날 풀리는 구조인데, 그날 안에는 매도 대금이 들어올수록 현금 비중만 계속 올라갔습니다.&lt;/p&gt;

&lt;p&gt;현금 비중이 상당히 쌓인 채로 하루를 통째로 날릴 수는 없어서, 직접 차단을 풀고 수동으로 라운드를 돌렸습니다. 그런데 일부 주문이 회전 한도에 걸려 거부됐고 한 건은 사유조차 기록이 안 남아, 결국 증권사 앱으로 직접 매매를 쳐서 메웠습니다.&lt;/p&gt;

&lt;p&gt;이 수작업 체결을 원장에 반영하는 절차가 없었던 탓에 한동안 브로커 계좌와 원장 수치가 눈에 띄게 벌어졌고, 나중에 수작업 부기로 거의 맞췄습니다.&lt;/p&gt;

&lt;p&gt;이 사고를 AI에게 통째로 검토시켜 구멍을 전부 찾아달라고 했고, 8가지가 나와 같은 날 전부 구현했습니다. 수동 개입을 자동 실행으로 오판하던 버그를 고치고, 주문 실패 사유를 저널에 남기고 알림도 걸고, 마감 직전 수동 주문엔 명시적 허용 플래그를 걸고, 수작업 체결을 원장에 반영하는 정식 명령을 만들고, 차단 해제·수동 라운드·앱 매매 세 가지 개입이 전부 감사 기록과 일일 리포트에 남게 했습니다. 수동 라운드에서만 회전 한도를 조정할 수 있는 옵션도 추가했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  토요일, 60일 지켜본 실험을 사전에 정해둔 기준대로 접었다
&lt;/h2&gt;

&lt;p&gt;포지션 크기 조정 방식 하나를 두 달 넘게 실계좌에 넣지 않고 모의로만 돌려온 게 있었습니다. 시작할 때 "얼마나 차이가 나야 채택하고 어느 선 밑이면 접는다"는 기준을 미리 적어뒀습니다.&lt;/p&gt;

&lt;p&gt;60일치 데이터를 다시 계산해 그 기준에 대조해보니, 기존 방식 대비 우위가 통계적으로 의미 있다고 보기엔 부족했고 "더 지켜본다"는 연장 조건에도 못 미쳤습니다. 연장 조건을 억지로 살리려면 몇 년치 데이터가 더 필요하다는 계산까지 나와서, 애매하다고 더 끌지 않고 접었습니다.&lt;/p&gt;

&lt;p&gt;같은 날 가격 데이터 꼬리 정정 작업도 마무리했습니다. 거래소 확정치와 어긋나던 오차를 다시 맞춰 넣었고, 이 오차가 실제 매매에 영향을 줬는지도 점검했습니다. "영향은 있었지만 감지하기 어려운 수준"으로 결론나서 과거 점수를 다시 계산하지는 않고 기록만 남겼습니다.&lt;/p&gt;

&lt;p&gt;화요일 GPU 드라이버 사고의 재발방지도 이날 닫았습니다. 문제가 됐던 패키지들을 자동 업데이트 대상에서 뺐습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  이번 주를 관통한 것
&lt;/h2&gt;

&lt;p&gt;이번 주 세 사고 — 실험 버그, GPU 드라이버, 실계좌 매수 차단 — 는 전부 사람이 직접 손을 대야 끝났습니다.&lt;/p&gt;

&lt;p&gt;그런데 손을 대고 나서가 더 중요했습니다. GPU 사고는 재부팅 조건과 재개 절차를 미리 걸어 다음번엔 사람 없이도 넘어가게 만들었고, 실계좌 사고는 수동 개입 자체가 일어나는 순간을 자동으로 기록·경보하게 만들었습니다.&lt;/p&gt;

&lt;p&gt;토요일의 사전등록 판단도 같은 결이었습니다. 데이터가 애매하면 더 지켜보고 싶어지는 게 사람 마음인데, 미리 적어둔 기준이 그 유혹을 대신 막아줬습니다.&lt;/p&gt;

&lt;p&gt;세 사고가 보여준 공통점은, 방어가 로직 한복판이 아니라 가장자리 — 사전 점검이 못 잡는 실행 환경, 사람이 끼어드는 순간, 판단이 애매해지는 경계 — 에서 뚫린다는 것이었습니다. 다음 주는 이번 주 새로 건 장치들(수동 개입 감시 8건, GPU 재부팅 자동화, 섀도 실험 종료)이 실제로 조용히 작동하는지 지켜보는 구간입니다.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[Oct 12-18] Experiment Bugs, a GPU Incident, and a Live-Account Buy Block — Turning Manual Intervention into Something Monitored"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:03:15 +0000</pubDate>
      <link>https://dev.to/finaltype/oct-12-18-experiment-bugs-a-gpu-incident-and-a-live-account-buy-block-turning-manual-4ca8</link>
      <guid>https://dev.to/finaltype/oct-12-18-experiment-bugs-a-gpu-incident-and-a-live-account-buy-block-turning-manual-4ca8</guid>
      <description>&lt;p&gt;&lt;em&gt;Three incidents this week each got handled by hand — then followed by work to make sure the same intervention would next time be logged and alerted on automatically, or not need a human at all.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three situations this week needed a hand directly on the keyboard: an experiment infrastructure bug, a GPU driver mismatch, and a live-account buy block.&lt;/p&gt;

&lt;p&gt;Different in nature, but handled the same way each time.&lt;/p&gt;

&lt;p&gt;Putting out the immediate fire got separated from adding a device so the same thing wouldn't need a person next time — or would at least leave a trail — and both got done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monday: ran an experiment, and the harness broke three times
&lt;/h2&gt;

&lt;p&gt;An experiment designed a few days earlier finally ran early this week — an A/B test checking whether adding one line of US-market price info to the model's input made judgment better or just added noise.&lt;/p&gt;

&lt;p&gt;Three bugs hit back to back as soon as it ran. The ticker list for each frozen date was built with a union instead of an intersection, a retry mechanism silently reused stale output from an earlier attempt, and an ambiguous marker phrase let the model paraphrase it, which a duplicate-check then misread as a real duplicate.&lt;/p&gt;

&lt;p&gt;None of the three showed up in the pre-run smoke check — they only surfaced once the run was actually underway. Not brushing off a small anomaly (the first run finishing suspiciously fast) is what caught the second bug.&lt;/p&gt;

&lt;p&gt;The same day, the daily sanity-check baseline for numeric responses got recalibrated too. An old rule that hadn't accounted for the recent drop in average values had been firing a false alarm roughly once every five days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuesday: a GPU driver got tangled, and the whole architecture got drawn for the first time
&lt;/h2&gt;

&lt;p&gt;An overnight auto-update applied a new GPU driver only halfway — the library on disk was the new version, but the module already loaded into the kernel was still the old one. The mismatch blocked every GPU job, including an experiment in progress.&lt;/p&gt;

&lt;p&gt;A reboot would fix it, but instead of doing it immediately, conditions got attached. An automatic reboot got scheduled to fire only once VMs were off, backups were done, and no GPU job overlapped — with the post-reboot version check and resuming the stalled experiment both pre-wired to run unattended.&lt;/p&gt;

&lt;p&gt;A manual reboot actually happened before the scheduled time, but the post-reboot check-and-resume routine still ran correctly, so it landed cleanly with no duplication.&lt;/p&gt;

&lt;p&gt;The same day, the whole &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;project&lt;/a&gt;(new tab) — how money moves through it and what guards sit where — got drawn as a single diagram for the first time. The structure had been built piece by piece over many months, with no single picture of the whole thing until now.&lt;/p&gt;

&lt;p&gt;That diagram got handed to an AI for a full review to find monitoring blind spots. It flagged a point where a failed pre-computation silently falls back to another path with no log or alert, an external price-feed path with no fallback, and daily backups that had never actually been restored once. None of it got fixed on the spot — it went onto a prioritized list instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wednesday: a live-account buy block forced a manual trade, and eight gaps got closed the same day
&lt;/h2&gt;

&lt;p&gt;A loss-limiting safety device tripped on one sleeve of a &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/05_execution_architecture/" rel="noopener noreferrer"&gt;live account&lt;/a&gt;(new tab), blocking buys only. It's a one-day device that releases automatically the next day, but within that day, cash kept piling up every time a sell settled.&lt;/p&gt;

&lt;p&gt;With cash sitting idle at a notable level and a full day at risk of being wasted, the block got lifted manually and a round got run by hand. Some orders then got rejected for hitting a turnover limit, one of them with no reason even logged — so the rest got filled by hand through the broker's own app.&lt;/p&gt;

&lt;p&gt;There was no standard procedure for reflecting those manual fills in the ledger, so the broker account and the ledger drifted apart noticeably for a while. A manual bookkeeping script later closed most of the gap.&lt;/p&gt;

&lt;p&gt;That incident got handed to an AI for a full review asking it to find every gap exposed by the manual intervention — eight came back, and all eight got implemented the same day. A bug that misclassified a manual trigger as a scheduled run got fixed, order failure reasons now get journaled and alerted, a manual order near market close requires an explicit opt-in flag, a proper command now reflects manual broker-app fills into the ledger, and all three kinds of intervention — lifting the block, running manually, and trading through the app — now show up in the audit trail and the daily report. An option to adjust the turnover limit for manual rounds only also got added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Saturday: a 60-day experiment got closed by the criteria written down in advance
&lt;/h2&gt;

&lt;p&gt;One position-sizing approach had been running shadow-only against a live account for over two months, never promoted. When it started, criteria for adopting it, extending the watch period, or dropping it had been written down ahead of time.&lt;/p&gt;

&lt;p&gt;Recomputing 60 days of data against those criteria showed the edge over the existing approach wasn't statistically meaningful enough, and didn't clear the bar for extending the watch either. Pushing the extension case through would have needed several more years of data — so instead of letting it drag on as "ambiguous," it got closed.&lt;/p&gt;

&lt;p&gt;Price-data tail corrections also wrapped up the same day. A lingering discrepancy against exchange-confirmed figures got reconciled, and whether it had affected actual trades got checked separately. The conclusion was "there was some effect, but at a level too small to detect cleanly" — so past scores didn't get recomputed, just logged.&lt;/p&gt;

&lt;p&gt;Tuesday's GPU driver incident also got its prevention closed out this day, by excluding the affected packages from auto-updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thread through the week
&lt;/h2&gt;

&lt;p&gt;All three incidents this week — the experiment bug, the GPU driver, the live-account buy block — ended only once a hand went in directly.&lt;/p&gt;

&lt;p&gt;What mattered more was what came after. The GPU incident got a reboot condition and resume routine wired in so it wouldn't need a person next time, and the live-account incident got the act of manual intervention itself turned into something automatically logged and alerted.&lt;/p&gt;

&lt;p&gt;Saturday's pre-registered judgment call ran on the same logic. Ambiguous data tempts a person to keep watching a little longer — a criterion written down in advance is what overrides that temptation.&lt;/p&gt;

&lt;p&gt;What the three incidents had in common is that the defenses break at the edges, not down the middle of the logic — in the execution environment a pre-check can't reach, in the moment a human steps in, at the boundary where a judgment turns ambiguous. Next week is for watching whether the devices added this week — the eight manual-intervention guards, the GPU reboot automation, the closed shadow experiment — actually hold quietly in practice.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[261017] 60일 섀도 실험 종료 — 사전등록한 기준대로 접었다"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:03:14 +0000</pubDate>
      <link>https://dev.to/finaltype/261017-60il-syaedo-silheom-jongryo-sajeondeungroghan-gijundaero-jeobeossda-5b76</link>
      <guid>https://dev.to/finaltype/261017-60il-syaedo-silheom-jongryo-sajeondeungroghan-gijundaero-jeobeossda-5b76</guid>
      <description>&lt;p&gt;&lt;em&gt;통계적으로 기준을 못 넘긴 실험을 미리 정해둔 규칙대로 종료하고, 데이터 정정과 지난 장애 재발방지도 마무리했습니다.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  60일 돌리던 실험을 사전등록한 규칙대로 접었다
&lt;/h2&gt;

&lt;p&gt;얼마 전부터 포지션 크기를 조정하는 방식 하나를 실계좌에 넣지 않고 모의로만(섀도) 60일 넘게 돌리고 있었습니다.&lt;/p&gt;

&lt;p&gt;이 실험을 시작할 때 "얼마나 차이가 나야 채택하고, 얼마나 애매하면 더 지켜보고, 어느 선 밑이면 접는다"는 기준을 미리 적어뒀습니다. 숫자가 나온 다음에 기준을 끼워맞추지 않으려는 목적이었습니다.&lt;/p&gt;

&lt;p&gt;오늘 다른 AI에게 60일치 데이터를 다시 계산시켜서 그 기준에 대조해봤습니다. 기존 방식 대비 순수익 우위가 통계적으로 의미 있다고 보기엔 부족했고, "더 지켜본다"는 연장 조건에도 못 미쳤습니다.&lt;/p&gt;

&lt;p&gt;연장 조건 쪽을 억지로 살려보려면 지금 수준의 차이로는 몇 년치 데이터가 더 필요하다는 계산도 나왔습니다. 그 정도면 "애매해서 더 본다"가 아니라 "기준을 못 넘었다"로 보는 게 맞다고 판단해 실험을 접기로 했습니다.&lt;/p&gt;

&lt;p&gt;이 실험은 원래부터 모의 트랙에만 있었기 때문에 실계좌에는 영향이 없습니다. 접은 건 문서상 지위일 뿐이고, 관측 자체는 비용이 들지 않아서 계속 지켜볼 수 있게 남겨뒀습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  가격 데이터 꼬리 정정을 마무리하고, 지난 드라이버 사고 재발방지도 닫았다
&lt;/h2&gt;

&lt;p&gt;며칠째 붙잡고 있던 가격 데이터 보정 작업을 오늘 마무리했습니다. 거래소 확정치가 나중에 들어오면서 생기는 미세한 오차를 뒤늦게 맞춰 넣는 작업인데, 범위를 넓혀 다시 돌리고 나니 운영 중인 종목 전부가 확정치와 일치하게 됐습니다.&lt;/p&gt;

&lt;p&gt;혹시 이 오차가 실제 매매에 영향을 줬는지도 따로 점검했습니다.&lt;/p&gt;

&lt;p&gt;결론은 "영향은 있었지만 감지하기 어려운 수준"이었습니다. 그래서 과거 점수나 성과를 다시 계산하지는 않고 기록만 남기기로 했습니다.&lt;/p&gt;

&lt;p&gt;그리고 며칠 전 자동 업데이트가 그래픽카드 드라이버를 건드려서 야간 작업이 멈출 뻔했던 사고의 재발방지도 오늘 마무리했습니다. 해당 패키지들을 자동 업데이트 대상에서 뺐습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  그 외
&lt;/h2&gt;

&lt;p&gt;TODO 문서 전체를 훑어서, 완료 처리된 줄 안에 묻혀 있던 후속 작업들을 따로 빼냈습니다. 당장 할 일은 아니지만 놓치지 않으려고 목록에 남겨뒀습니다.&lt;/p&gt;

&lt;p&gt;다른 신호 하나는 통계적으로 애매한 결과가 나와서, 일단 현재 상태를 유지하고 한 달쯤 뒤에 한 번 더 확인하기로 했습니다.&lt;/p&gt;

&lt;h2&gt;
  
  
  왜 중요한가
&lt;/h2&gt;

&lt;p&gt;오늘 가장 중요했던 건 "실험을 접은 것" 자체가 아니라, 접을지 말지를 사전에 정해둔 규칙대로 판단했다는 점입니다.&lt;/p&gt;

&lt;p&gt;데이터가 애매하게 나오면 사람은 보통 더 지켜보자는 쪽으로 기울기 쉽습니다. 미리 기준을 적어두면 그 유혹을 줄일 수 있습니다.&lt;/p&gt;

&lt;p&gt;데이터 정정과 드라이버 재발방지도 같은 맥락입니다. 문제가 생긴 당시 급하게 메우고 끝내는 게 아니라, 영향 범위를 끝까지 확인하고 재발방지까지 닫는 것까지가 하나의 작업입니다.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
    <item>
      <title>"[Oct 17] Ending a 60-Day Shadow Experiment, By the Rule I Set in Advance"</title>
      <dc:creator>finaltype</dc:creator>
      <pubDate>Wed, 21 Oct 2026 13:02:13 +0000</pubDate>
      <link>https://dev.to/finaltype/oct-17-ending-a-60-day-shadow-experiment-by-the-rule-i-set-in-advance-1lgj</link>
      <guid>https://dev.to/finaltype/oct-17-ending-a-60-day-shadow-experiment-by-the-rule-i-set-in-advance-1lgj</guid>
      <description>&lt;p&gt;&lt;em&gt;An experiment that didn't clear its pre-registered statistical bar got retired on schedule, and a data correction plus an old incident's follow-up got closed out the same day.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the English version of a post originally written in Korean for my &lt;a href="https://finaltype.github.io/quant-blog/en/architecture/01_project_architecture/" rel="noopener noreferrer"&gt;algorithmic trading system devlog&lt;/a&gt;(new tab).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing a 60-day experiment by the rule I wrote down in advance
&lt;/h2&gt;

&lt;p&gt;For a while now I'd been running a position-sizing variant in shadow mode only — never on the live account — for more than 60 days straight.&lt;/p&gt;

&lt;p&gt;When I started this experiment, I wrote down in advance what counted as "clearly better, adopt it," "ambiguous, keep watching," and "below this line, drop it." The point was to fix the bar before seeing the numbers, so I couldn't bend the rule to fit whatever came out.&lt;/p&gt;

&lt;p&gt;Today I had another AI recompute the full 60 days of data and check it against that pre-registered bar. The net-return edge over the existing approach wasn't statistically convincing enough, and it also fell short of the threshold for an extension.&lt;/p&gt;

&lt;p&gt;I also ran the numbers on what it would take to rescue the extension case, and at the current size of the gap it would take years more data to get there. At that point it's not "ambiguous, keep watching" anymore — it's just "didn't clear the bar." So I retired the experiment.&lt;/p&gt;

&lt;p&gt;This variant had only ever lived on the shadow track, so the live account was never affected. Retiring it only changes its documented status — the shadow observation itself costs nothing, so I left it running to keep watching anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing out a price-data correction and an old driver incident's follow-up
&lt;/h2&gt;

&lt;p&gt;I finished a price-data correction job I'd been chipping away at for several days. It backfills tiny discrepancies that show up once the exchange's finalized prices arrive later than the provisional ones — widening the backfill window got every stock in production to match the finalized figures.&lt;/p&gt;

&lt;p&gt;I also separately checked whether this discrepancy had actually affected real trades.&lt;/p&gt;

&lt;p&gt;The conclusion: there was some effect, but at a level too small to detect reliably. So I didn't rescore anything or recompute past performance — just logged it for the record.&lt;/p&gt;

&lt;p&gt;And I closed out the follow-up to an incident from a few days back, where an unattended system update had touched the GPU driver and nearly stalled an overnight job. The relevant packages are now excluded from automatic updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also today
&lt;/h2&gt;

&lt;p&gt;I went through the whole TODO document and pulled out follow-up items that had been buried inside already-closed lines. None of them are urgent, but I logged them so they don't get lost.&lt;/p&gt;

&lt;p&gt;Another signal came back with an ambiguous statistical result, so I decided to leave it as-is for now and check again in about a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;What mattered most today wasn't the decision to retire the experiment itself — it was that the call followed a rule set in advance.&lt;/p&gt;

&lt;p&gt;When data comes back ambiguous, the natural pull is to keep watching a little longer. Writing the bar down beforehand cuts against that pull.&lt;/p&gt;

&lt;p&gt;The data correction and the driver-incident follow-up carry the same logic. The job isn't done when you patch the immediate problem — it's done once you've traced the full blast radius and closed the loop on preventing it from happening again.&lt;/p&gt;

</description>
      <category>quant</category>
      <category>trading</category>
      <category>devlog</category>
    </item>
  </channel>
</rss>
