Skip to content

Commit 8eb4fab

Browse files
committed
added source display and trust ratings
1 parent 36cb00c commit 8eb4fab

6 files changed

Lines changed: 147 additions & 58 deletions

File tree

cpp_engine/src/IMarketDataSource.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,19 @@
33
#include <optional>
44
#include <string>
55

6+
// A single news item: the headline text plus the publisher it came from
7+
// (e.g. "Reuters", "PRNewswire"). `source` may be empty when the upstream feed
8+
// omits it; downstream treats an empty/unknown source as low trust.
9+
struct Headline
10+
{
11+
std::string text;
12+
std::string source;
13+
};
14+
615
class IMarketDataSource
716
{
817
public:
918
virtual ~IMarketDataSource() = default;
1019

11-
virtual std::optional<std::string> nextHeadline() = 0;
20+
virtual std::optional<Headline> nextHeadline() = 0;
1221
};

cpp_engine/src/LiveRestDataSource.hpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ class LiveRestDataSource : public IMarketDataSource
4040
LiveRestDataSource(const LiveRestDataSource &) = delete;
4141
LiveRestDataSource &operator=(const LiveRestDataSource &) = delete;
4242

43-
std::optional<std::string> nextHeadline() override
43+
std::optional<Headline> nextHeadline() override
4444
{
4545
std::lock_guard<std::mutex> lock(mutex_);
4646
if (headlines_.empty())
4747
return std::nullopt;
4848

49-
std::string headline = std::move(headlines_.front());
49+
Headline headline = std::move(headlines_.front());
5050
headlines_.pop();
5151
return headline;
5252
}
@@ -102,8 +102,15 @@ class LiveRestDataSource : public IMarketDataSource
102102
if (!seenIds_.insert(id).second)
103103
continue;
104104

105+
// Finnhub returns the publisher in "source"; keep it for trust
106+
// scoring downstream. Leave empty when absent (treated as unknown).
107+
std::string source;
108+
const auto sourceIt = item.find("source");
109+
if (sourceIt != item.end() && sourceIt->is_string())
110+
source = sourceIt->get<std::string>();
111+
105112
std::lock_guard<std::mutex> lock(mutex_);
106-
headlines_.push(headlineIt->get<std::string>());
113+
headlines_.push(Headline{headlineIt->get<std::string>(), std::move(source)});
107114
}
108115
}
109116

@@ -122,7 +129,7 @@ class LiveRestDataSource : public IMarketDataSource
122129
std::string apiKey_;
123130
std::atomic<bool> running_;
124131
std::mutex mutex_;
125-
std::queue<std::string> headlines_;
132+
std::queue<Headline> headlines_;
126133
std::unordered_set<int> seenIds_;
127134
std::thread worker_;
128135
};

cpp_engine/src/SimulatedDataSource.hpp

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,49 @@
66
#include <cstddef>
77
#include <optional>
88
#include <string>
9+
#include <utility>
910
#include <vector>
1011

1112
class SimulatedDataSource : public IMarketDataSource
1213
{
1314
public:
15+
// Each mock headline is paired with a synthetic publisher spanning trust
16+
// tiers (wire services / major outlets / PR wires / no-name blogs) so the
17+
// source-trust labeling and filtering are exercised end-to-end without a
18+
// live Finnhub key.
1419
SimulatedDataSource()
1520
: headlines_{
16-
"Fed raises rates by 25bps, signals higher-for-longer stance",
17-
"AAPL earnings beat as services revenue hits record",
18-
"US CPI comes in cooler than expected; futures rally",
19-
"Oil jumps 3% on supply disruption concerns in the Middle East",
20-
"NVDA announces new AI chips; shares pop in after-hours",
21-
"Treasury yields fall after weak jobs report; bond market rallies",
22-
"ECB holds rates steady; hints at possible summer cut",
23-
"TSLA deliveries miss estimates; margin pressure returns",
24-
"Gold climbs as dollar weakens and risk-off flows pick up",
25-
"China announces targeted stimulus; industrial metals rise",
26-
"MSFT cloud growth accelerates; upbeat guidance lifts tech",
27-
"Banking sector dips on renewed concerns over CRE exposure",
21+
{"Fed raises rates by 25bps, signals higher-for-longer stance", "Reuters"},
22+
{"AAPL earnings beat as services revenue hits record", "Bloomberg"},
23+
{"US CPI comes in cooler than expected; futures rally", "Associated Press"},
24+
{"Oil jumps 3% on supply disruption concerns in the Middle East", "CNBC"},
25+
{"NVDA announces new AI chips; shares pop in after-hours", "PRNewswire"},
26+
{"Treasury yields fall after weak jobs report; bond market rallies", "Reuters"},
27+
{"ECB holds rates steady; hints at possible summer cut", "Financial Times"},
28+
{"TSLA deliveries miss estimates; margin pressure returns", "MarketWatch"},
29+
{"Gold climbs as dollar weakens and risk-off flows pick up", "Yahoo Finance"},
30+
{"China announces targeted stimulus; industrial metals rise", "Bloomberg"},
31+
{"MSFT cloud growth accelerates; upbeat guidance lifts tech", "GlobeNewswire"},
32+
{"Banking sector dips on renewed concerns over CRE exposure", "Seeking Alpha"},
2833
// Single-stock headlines (no macro keyword) for testing watchlists.
29-
"GOOGL slides after antitrust ruling threatens ad business",
30-
"AMZN holiday sales smash records; cloud margins expand",
31-
"META unveils new AI assistant; shares rally 7% after the bell",
32-
"AMD wins major data-center deal, taking share from NVDA",
33-
"JPM tops estimates as net interest income climbs to a record",
34-
"DIS streaming losses narrow as subscriber growth returns",
35-
"COIN surges as crypto trading volumes spike to yearly high",
36-
"PLTR jumps on raised guidance and new government contracts",
34+
{"GOOGL slides after antitrust ruling threatens ad business", "Reuters"},
35+
{"AMZN holiday sales smash records; cloud margins expand", "PRNewswire"},
36+
{"META unveils new AI assistant; shares rally 7% after the bell", "TechCrunch"},
37+
{"AMD wins major data-center deal, taking share from NVDA", "MarketBeat Blog"},
38+
{"JPM tops estimates as net interest income climbs to a record", "Wall Street Journal"},
39+
{"DIS streaming losses narrow as subscriber growth returns", "CNBC"},
40+
{"COIN surges as crypto trading volumes spike to yearly high", "CoinDesk"},
41+
{"PLTR jumps on raised guidance and new government contracts", "StockTwits"},
3742
}
3843
{
3944
}
4045

41-
explicit SimulatedDataSource(std::vector<std::string> headlines)
46+
explicit SimulatedDataSource(std::vector<Headline> headlines)
4247
: headlines_{std::move(headlines)}
4348
{
4449
}
4550

46-
std::optional<std::string> nextHeadline() override
51+
std::optional<Headline> nextHeadline() override
4752
{
4853
if (headlines_.empty())
4954
return std::nullopt;
@@ -59,15 +64,15 @@ class SimulatedDataSource : public IMarketDataSource
5964
first_tick_ = false;
6065
last_emit_ = now;
6166

62-
const std::string &h = headlines_[next_idx_];
67+
const Headline &h = headlines_[next_idx_];
6368
next_idx_ = (next_idx_ + 1) % headlines_.size();
6469
return h;
6570
}
6671

6772
private:
6873
static constexpr std::chrono::milliseconds kInterval{2000};
6974

70-
std::vector<std::string> headlines_;
75+
std::vector<Headline> headlines_;
7176
std::size_t next_idx_ = 0;
7277
std::chrono::steady_clock::time_point last_emit_{};
7378
bool first_tick_ = true;

cpp_engine/src/ZmqPublisher.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ class ZmqPublisher
2020
ZmqPublisher(const ZmqPublisher &) = delete;
2121
ZmqPublisher &operator=(const ZmqPublisher &) = delete;
2222

23-
void publishHeadline(std::string_view headline)
23+
void publishHeadline(std::string_view headline, std::string_view source)
2424
{
25-
const std::string payload = build_headline_json(headline);
25+
const std::string payload = build_headline_json(headline, source);
2626
sock_.send(zmq::buffer(payload), zmq::send_flags::none);
2727
}
2828

@@ -86,15 +86,17 @@ class ZmqPublisher
8686
return out;
8787
}
8888

89-
static std::string build_headline_json(std::string_view headline)
89+
static std::string build_headline_json(std::string_view headline, std::string_view source)
9090
{
9191
const std::string ts = iso8601_utc_now();
9292
std::string out;
93-
out.reserve(headline.size() + ts.size() + 32);
93+
out.reserve(headline.size() + source.size() + ts.size() + 48);
9494
out += "{\"type\":\"headline\",\"ts\":\"";
9595
out += ts;
9696
out += "\",\"headline\":\"";
9797
out += json_escape(headline);
98+
out += "\",\"source\":\"";
99+
out += json_escape(source);
98100
out += "\"}";
99101
return out;
100102
}

cpp_engine/src/main.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,13 @@ int main(int argc, char **argv)
153153
continue;
154154
}
155155

156-
if (filter.current()->matches(*headline))
156+
if (filter.current()->matches(headline->text))
157157
{
158-
publisher.publishHeadline(*headline);
159-
std::cout << "published: " << *headline << "\n";
158+
publisher.publishHeadline(headline->text, headline->source);
159+
std::cout << "published: " << headline->text
160+
<< " [source: "
161+
<< (headline->source.empty() ? "unknown" : headline->source)
162+
<< "]\n";
160163
}
161164
// Got a headline: loop again immediately to drain any backlog.
162165
}

0 commit comments

Comments
 (0)