Skip to content
Doheon Kim
Go back

Message Board Sentiment and Retail Order Flow

June 2026 – Present · Independent two-person project

The question

Korean retail investors are active on stock message boards. A teammate and I wanted to know whether those posts point the same way as retail investors’ actual trades: when a board turns bullish on a stock, do individuals really buy it?

We narrowed the scope to two memory-chip stocks, Samsung Electronics and SK hynix, over 134 trading days from January 2 to July 21, 2026. Answering the question takes two things: every post labeled as bullish, bearish, or neutral, and daily trading data to compare against.

This page covers the first half, the data and the model. The comparison with trading data is still in progress.

Message boards Naver stock boards and the DC Inside stock gallery Crawler 1.37M posts over 134 trading days Hand labels 400 posts labeled by two people GPT-5-mini labels 95,976 posts labeled with our final prompt Fine-tuned KLUE-RoBERTa trained on 86,018 of those posts Labels for every post 1.37M posts, then a daily sentiment index
The pipeline so far. Each step is described below.

Collecting the posts

About 1.02M of the posts come from the Naver stock boards for the two companies, collected by a crawler I wrote. The other 352K come from the DC Inside stock gallery and were collected with a separate tool.

The Naver crawler had three properties I cared about:

The full Naver crawl took 12,485 requests and 5.9 hours and was never blocked.

As a quality check, I crawled one day again 20 days later. The count went from 12,970 posts to 12,860, so about 0.85% of posts had been deleted in between.

Deciding what “bullish” means

Sentiment here means the direction a post expects the price to go. It does not mean the mood of the writer. A post that says “just hold on, we win in the end” sounds anxious, but it expects the price to rise, so it is bullish. Two more rules from our guideline:

Our first guideline did not work. My teammate and I each labeled the same 100 posts and agreed on only 40% of them (Cohen’s kappa 0.22). We rewrote the guideline, dropped a fourth “cannot judge” label in favor of three labels plus an irony flag, and labeled again. Agreement on the final labels is 80% (kappa 0.623).

That final set has 400 posts: 100 for developing the labeling prompt and 300 held back as the test set.

Teaching a small model with a large one

Labeling 1.37M posts with a paid model would have been expensive, so the plan was to have a large model label a sample and then train a small model on that sample.

The prompt. I tuned the classification prompt over six rounds against the 100 development posts. Its score moved from 0.794 to 0.797, averaging 0.806 over three runs. Our target was 0.90 and we did not reach it.

The sample. GPT-5-mini labeled all 95,976 posts from five trading days using that prompt.

The model. I fine-tuned KLUE-RoBERTa (base) for 3 epochs with a learning rate of 2e-5, batch size 32, and a maximum length of 128 tokens. The data was split at random, keeping the label balance, into 86,018 training posts and 9,558 validation posts.

Two things went wrong here and were fixed:

Results

All three models were scored on the same 300 hand-labeled posts.

ModelMacro-F1Accuracy
GPT-5-mini (the labeler)0.745
KLUE-RoBERTa (fine-tuned)0.7390.767
KcELECTRA (fine-tuned)0.7250.753

The fine-tuned model lands within 0.006 of the model that labeled its training data. With 300 test posts the standard error is about 0.024, so this is a tie, not a win for either side.

Where the fine-tuned model is wrong matters more than how often. Of its 70 errors, only 6 confuse bullish with bearish. The other 64 confuse a direction with neutral. Per class, its F1 is 0.812 for neutral, 0.738 for bullish, and 0.667 for bearish.

One result changed how I evaluate models. On the validation set, which is labeled by GPT-5-mini, KcELECTRA scored higher than KLUE-RoBERTa (0.824 against 0.797). On the hand-labeled test set the order reversed. Validation scores measured agreement with the labeler, and only the hand labels measured agreement with people.

Labeling every post

The fine-tuned model then labeled all 1,371,257 posts on a Colab T4 with no API cost. I sorted posts by length before batching, so each batch holds posts of similar length and wastes less computation on padding. In a benchmark on my laptop, throughput went from 46 to 102 posts per second after sorting by length and doubling the batch size.

To check that the cloud run and my local setup agree, I ran 3,072 posts through both. Every label matched.

A second opinion

One model’s labels could carry that model’s quirks into everything downstream. So I labeled all 1.37M posts a second time with the fine-tuned KcELECTRA and compared.

Post by post the models differ, but the daily index barely depends on which model made it.

What I would improve

Where it stands

The labeled posts are now joined to daily trading data, and we are running regressions to see how board sentiment relates to retail buying and selling. I’ll add those results here once they are final.


Next Project
Betman Predictor