<?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: Kuroshio Data</title>
    <description>The latest articles on DEV Community by Kuroshio Data (@kuroshio_data).</description>
    <link>https://dev.to/kuroshio_data</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%2F4061948%2F5383586f-f583-4d3b-8744-1cdc5e7a6ee5.png</url>
      <title>DEV Community: Kuroshio Data</title>
      <link>https://dev.to/kuroshio_data</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kuroshio_data"/>
    <language>en</language>
    <item>
      <title>Parsing Japan's EDINET: UTF-16 CSVs, tabs inside fields, and semantics hidden in the element ID</title>
      <dc:creator>Kuroshio Data</dc:creator>
      <pubDate>Thu, 06 Aug 2026 09:56:47 +0000</pubDate>
      <link>https://dev.to/kuroshio_data/parsing-japans-edinet-utf-16-csvs-tabs-inside-fields-and-semantics-hidden-in-the-element-id-30nk</link>
      <guid>https://dev.to/kuroshio_data/parsing-japans-edinet-utf-16-csvs-tabs-inside-fields-and-semantics-hidden-in-the-element-id-30nk</guid>
      <description>&lt;p&gt;Japan has a full securities disclosure system, EDINET, run by the Financial Services Agency. It is the structural equivalent of EDGAR: every listed company files there, the filings are public, and since v2 there is a documented REST API with free API keys. On paper it should be as easy to build on as EDGAR.&lt;/p&gt;

&lt;p&gt;In practice, English-language coverage of Japanese corporate events runs days to weeks behind, and for small caps it often never arrives at all. Having spent a while pulling this API apart, I don't think the reason is access. It's that the payload fights you in three specific ways, and only one of them is "it's in Japanese."&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting a document at all
&lt;/h2&gt;

&lt;p&gt;Two endpoints matter. &lt;code&gt;documents.json?date=YYYY-MM-DD&amp;amp;type=2&lt;/code&gt; lists everything disclosed on one day with metadata. Then &lt;code&gt;documents/{docID}?type=5&lt;/code&gt; returns that filing — as a ZIP archive. Inside the archive, alongside the XBRL, there's a CSV that is far easier to work with than the raw XBRL tree.&lt;/p&gt;

&lt;p&gt;That CSV has nine columns: element ID, item name, context ID, relative fiscal year, consolidated/individual, period or instant, unit ID, unit, and value. So far, reasonable.&lt;/p&gt;

&lt;p&gt;Then you decode it and get mojibake, because the file is &lt;strong&gt;UTF-16LE&lt;/strong&gt;, not UTF-8. That one is quick to spot and quick to fix. The next one is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug I shipped
&lt;/h2&gt;

&lt;p&gt;The file is tab-separated, so the obvious parser is &lt;code&gt;split('\n')&lt;/code&gt; then &lt;code&gt;split('\t')&lt;/code&gt;. I wrote that, tested it against several filings, saw sensible output, and shipped it.&lt;/p&gt;

&lt;p&gt;It is wrong, and it fails silently.&lt;/p&gt;

&lt;p&gt;Values in the last column are quoted, and inside those quotes EDINET happily includes &lt;strong&gt;raw tab characters and raw newlines&lt;/strong&gt;. This is not a corner case I constructed: it shows up in the body text of extraordinary reports, where the filer has pasted a table describing a business transfer, and it can show up in the "purpose of holding" field of large-shareholding reports.&lt;/p&gt;

&lt;p&gt;The failure mode is the nasty kind. A stray tab inside a quoted value shifts every subsequent column by one, so &lt;code&gt;value&lt;/code&gt; reads out of the &lt;code&gt;unit&lt;/code&gt; column and comes back empty. A stray newline splits one logical record into two malformed ones. You don't get an exception. You get a filing that parses cleanly and quietly reports nothing where the interesting text should have been. If you're eyeballing a sample of output, everything looks fine — because the records that break are precisely the wordy, unusual ones you're least likely to have in your sample.&lt;/p&gt;

&lt;p&gt;The fix is the boring one: scan character by character, track whether you're inside quotes, handle &lt;code&gt;""&lt;/code&gt; as an escaped quote, and only treat a tab or newline as a delimiter when you're outside. About thirty lines. The lesson I'd actually pass on is not "write a real parser" — everyone knows that — it's that a delimiter-based parser on unfamiliar data should be treated as &lt;strong&gt;unvalidated until you've deliberately hunted for the longest, ugliest free-text field in the corpus and confirmed it survives&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part where EDINET is genuinely good
&lt;/h2&gt;

&lt;p&gt;Now the payoff, and it's a real one.&lt;/p&gt;

&lt;p&gt;Japanese listed companies file an &lt;em&gt;extraordinary report&lt;/em&gt; (臨時報告書) when something material happens: an M&amp;amp;A decision, a change of major shareholders or parent company, a change of representative directors, shareholders-meeting resolutions. It's the rough analogue of an 8-K.&lt;/p&gt;

&lt;p&gt;The obvious way to classify these would be to run the Japanese body text through a model and hope. You don't have to. In EDINET's XBRL taxonomy, &lt;strong&gt;the element ID that carries the report body already identifies the event type&lt;/strong&gt;. The body of a share exchange decision arrives under &lt;code&gt;DecisionOnShareExchangeTextBlock&lt;/code&gt;. A change of major shareholders arrives under &lt;code&gt;ChangesInMajorShareholderTextBlock&lt;/code&gt;. Voting results arrive under &lt;code&gt;ResolutionOfShareholdersMeetingTextBlock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These map one-to-one onto the items of Article 19(2) of the Cabinet Office Ordinance — the legal list of things that trigger the filing in the first place. The regulation's structure is carried through into the taxonomy, so classification is a dictionary lookup and it is exact. No NLP, no confidence score, no drift.&lt;/p&gt;

&lt;p&gt;For a sense of what actually flows through: across 94 corporate extraordinary reports I measured over six days in late July 2026, the most common events were 23 shareholders-meeting resolutions, 19 stock option issuances, 17 parent-or-subsidiary changes, 18 significant-financial-event disclosures (impairments, debt waivers, special losses), and 7 major shareholder changes, with M&amp;amp;A decisions — share exchanges, business transfers, subsidiary acquisitions, splits — making up a long tail of one to three each.&lt;/p&gt;

&lt;p&gt;Those numbers sum to more than 94 on purpose. One report can carry several event elements at once, so a single filing shows up under multiple categories. If you're aggregating, count filings and events separately or you'll double-count your way into a phantom M&amp;amp;A wave.&lt;/p&gt;

&lt;p&gt;Volume runs roughly 15–50 per business day, spiking past 150 in late June when AGM voting results land all at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  The English-name asymmetry
&lt;/h2&gt;

&lt;p&gt;Here's a structural quirk that took me a while to understand, and that I think is the real reason naive translation pipelines produce garbage here.&lt;/p&gt;

&lt;p&gt;EDINET knows the official English name of every entity &lt;em&gt;registered as a filer&lt;/em&gt;. So for extraordinary reports, where the company itself is the filer, you get its real registered English name — not a machine translation, the name the company itself uses.&lt;/p&gt;

&lt;p&gt;But large-shareholding reports (Japan's 5% rule, the 13D/13G analogue) are filed by the &lt;strong&gt;holder&lt;/strong&gt;, not the issuer. So you get the holder's English name, and the company being accumulated appears only as Japanese text plus a securities code. There is no reliable English name for it in the filing.&lt;/p&gt;

&lt;p&gt;The temptation is to machine-translate the Japanese company name. Don't. Japanese corporate names are full of traps — the same characters have multiple valid readings, and the company's own chosen romanization frequently isn't any of them. The securities code is stable, unambiguous, and the thing you should be joining on anyway. Emitting the Japanese name plus the code is the honest output; inventing an English name is how you end up confidently wrong about which company just got a new 8% holder.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 5% filings actually tell you
&lt;/h2&gt;

&lt;p&gt;One detail worth knowing if you ever look at these. Under Japan's rules, a holder crossing 5% must state the &lt;em&gt;purpose&lt;/em&gt; of holding, in prose. Buried in that prose is a specific legal term, 重要提案行為 — "acts of important proposal." It isn't decorative. Declaring it changes which filing regime the holder is under, because it signals intent to push management on things like board composition or capital policy.&lt;/p&gt;

&lt;p&gt;Which means the single most useful activist signal in the corpus is a &lt;strong&gt;legal phrase, not a sentiment&lt;/strong&gt;. You can match on it exactly. A fund you've never heard of that declares it is worth more attention than a famous name that files a routine passive-investment purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versus EDGAR
&lt;/h2&gt;

&lt;p&gt;For contrast, the U.S. side is easier in every mechanical way. SEC EDGAR needs no API key and no registration, the data is public domain, daily indexes are plain text, and Form 4 ownership documents are clean XML. The tradeoffs are volume — a busy day is 500 to 2,000 Form 4s — and that the interesting bit is a flag rather than prose: whether a trade ran under a pre-scheduled Rule 10b5-1 plan, which separates routine sales from discretionary ones.&lt;/p&gt;

&lt;p&gt;Japan gives you fewer filings with more semantics baked into the schema. The U.S. gives you more filings with cleaner mechanics. Both are free and official, and neither is what most people building "financial data" products are actually scraping.&lt;/p&gt;




&lt;p&gt;Everything above is doable with the official API and a free key; nothing here needs a vendor. I do also run hosted versions that emit this as English JSON — &lt;a href="https://apify.com/kuroshio-data/japan-activist-5pct-filings-tracker" rel="noopener noreferrer"&gt;Japan 5% / activist filings&lt;/a&gt;, &lt;a href="https://apify.com/kuroshio-data/japan-corporate-events-monitor" rel="noopener noreferrer"&gt;Japan corporate events&lt;/a&gt;, &lt;a href="https://apify.com/kuroshio-data/sec-form4-insider-trades-tracker" rel="noopener noreferrer"&gt;SEC Form 4&lt;/a&gt; — but the parsing notes are the part I'd have wanted to read six weeks ago.&lt;/p&gt;

&lt;p&gt;— kuroshio-data&lt;/p&gt;

</description>
      <category>japanapidatashowdev</category>
    </item>
    <item>
      <title>Tracking Japanese Activist Investors Without Reading Japanese: 5% Filings from EDINET as English JSON</title>
      <dc:creator>Kuroshio Data</dc:creator>
      <pubDate>Tue, 04 Aug 2026 07:31:34 +0000</pubDate>
      <link>https://dev.to/kuroshio_data/tracking-japanese-activist-investors-without-reading-japanese-5-filings-from-edinet-as-english-4kne</link>
      <guid>https://dev.to/kuroshio_data/tracking-japanese-activist-investors-without-reading-japanese-5-filings-from-edinet-as-english-4kne</guid>
      <description>&lt;p&gt;When Elliott built its position in Dai Nippon Printing, the disclosure was public within days. When Oasis went after Fujitec, same thing. When one of the Murakami-affiliated vehicles like City Index Eleventh starts accumulating a mid-cap, the paper trail is right there, filed under Japan's large shareholding rule — the local equivalent of a SC 13D/13G, triggered when anyone crosses 5% of a listed company, with amendments required for every 1% move after that.&lt;/p&gt;

&lt;p&gt;The catch is where "right there" is. These filings live on EDINET, Japan's EDGAR. EDINET has an API, which is genuinely good news, but the documents themselves are Japanese-language XBRL and CSV wrapped in ZIP archives. The filer's name is in Japanese. The purpose of the holding — the field that tells you whether this is a passive index adjustment or someone planning to show up at the AGM with proposals — is a free-form Japanese paragraph. There is no English feed. If you don't read Japanese, you find out about a 5% crossing when Bloomberg or a fund letter mentions it, which can be weeks later, or never for smaller names.&lt;/p&gt;

&lt;p&gt;I read EDINET filings anyway (I'm a Japanese solo developer, and I was already pulling the API for my own screens), so I automated the part that's annoying even for me: fetching, unpacking the XBRL, normalizing names to English, and classifying the purpose text. The result is an Apify actor called &lt;a href="https://apify.com/kuroshio-data/japan-activist-5pct-filings-tracker" rel="noopener noreferrer"&gt;Japan Activist &amp;amp; 5% Filings Tracker&lt;/a&gt;. It's paid — $0.05 per filing, pay-per-result, more on that below — and it turns each large shareholding report into a record like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"docId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S100XXQ7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submittedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-08-03 09:12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filingType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"amendment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"holderNameEn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sompo Holdings, Inc."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"holderNameJa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SOMPOホールディングス株式会社"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuerNameJa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"株式会社ティアフォー"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issuerSecCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"XXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"holdingRatioPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;17.49&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previousRatioPct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;15.82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ratioChangePct"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.67&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"crossedThreshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"jointHolders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isPotentialActivist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"activistSignal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"purposeEn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pure_investment"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"purposeJa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"純投資（政策投資を含む）"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"edinetDocId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"S100XXQ7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EDINET"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one is boring on purpose: an insurer topping up an existing stake, &lt;code&gt;crossedThreshold: false&lt;/code&gt; because it was already past 5%. The interesting rows are the ones where &lt;code&gt;crossedThreshold&lt;/code&gt; flips to true, or where &lt;code&gt;isPotentialActivist&lt;/code&gt; is set. That flag fires two ways. First, a maintained list of funds with an activist track record in Japan — Oasis, Effissimo, Elliott, the Murakami-affiliated entities, Strategic Capital, and so on, matched against the holder name even when they file through subsidiaries. Second, the purpose text itself: if the stated purpose signals intent to make "important proposals" to management (a specific legal phrase in these filings, because it changes the filing regime the holder is under), that gets flagged regardless of who the filer is. That second path is how you catch a fund you've never heard of before it has a reputation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it
&lt;/h2&gt;

&lt;p&gt;There's nothing to configure for the common case. Open the actor on Apify, hit Run with an empty input, and it fetches the latest large shareholding filings and writes them to a dataset you can read as JSON, CSV, or via the Apify API. To make it a feed rather than a one-off, add a Schedule in the Apify console — daily after Tokyo close works well, since EDINET filings cluster during Japanese business hours. Filers have five business days to disclose a crossing, so daily polling loses you nothing. From there it's a normal dataset: pipe it into a Slack webhook, a spreadsheet, or an LLM agent that reads &lt;code&gt;purposeEn&lt;/code&gt; and &lt;code&gt;holdingRatioPct&lt;/code&gt; and decides whether to wake you up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs, and where it falls short
&lt;/h2&gt;

&lt;p&gt;Pricing is pay-per-result: $0.05 per filing returned, on top of Apify's small compute charge. A typical day in Japan produces a handful to a few dozen large shareholding reports, so a daily schedule usually lands in the range of a few dollars a month. No subscription, no minimum. If a run returns nothing new, you pay for nothing new.&lt;/p&gt;

&lt;p&gt;Two honest limitations, both of which I hit while building it. The purpose field in the source is free-form Japanese prose, so &lt;code&gt;purposeEn&lt;/code&gt; is a best-effort category (&lt;code&gt;pure_investment&lt;/code&gt;, &lt;code&gt;management_involvement&lt;/code&gt;, and a few others) plus the raw Japanese string — for edge cases you'll want the raw text and a translator. And the issuer side stays in Japanese: EDINET gives you the holder's English name but not a reliable English name for the company being bought, so the actor returns &lt;code&gt;issuerNameJa&lt;/code&gt; plus the securities code rather than inventing an English name that might be wrong. The securities code is always there, which is what you should join on anyway.&lt;/p&gt;

&lt;p&gt;If extraordinary reports are more your thing — Japan's rough 8-K equivalent, covering M&amp;amp;A decisions, board and major shareholder changes, AGM voting results — the companion &lt;a href="https://apify.com/kuroshio-data/japan-corporate-events-monitor" rel="noopener noreferrer"&gt;Japan Corporate Events Monitor&lt;/a&gt; does the same treatment for those at $0.03 per filing, and there's a &lt;a href="https://apify.com/kuroshio-data/sec-form4-insider-trades-tracker" rel="noopener noreferrer"&gt;SEC Form 4 insider trades tracker&lt;/a&gt; if you want the US side from the same shape of pipeline.&lt;/p&gt;

&lt;p&gt;Built and maintained by a solo dev. If something in the output looks wrong, the issue tracker on the actor page reaches me directly.&lt;/p&gt;

&lt;p&gt;— kuroshio-data&lt;/p&gt;

</description>
      <category>financeapidajapanta</category>
    </item>
  </channel>
</rss>
