Extending Makora
Code examples for common customizations.
For a full customization guide, see Harness. This page provides copy-paste examples.
Signal Format
Data sources return signals in this format:
{
symbol: "AAPL",
source: "my_source",
source_detail: "my_source_v1",
sentiment: 0.65, // -1 to 1, weighted
raw_sentiment: 0.72, // Before source weight applied
volume: 42, // Mention count
freshness: 0.9, // Time decay factor
source_weight: 0.9, // How much to trust this source
reason: "MySource: 42 mentions, 65% bullish"
}
Example: News API Integration
private async gatherNewsAPI(): Promise<Signal[]> {
const signals: Signal[] = [];
try {
const res = await fetch("https://newsapi.example/finance", {
headers: { "Authorization": `Bearer ${this.env.NEWS_API_KEY}` }
});
const data = await res.json();
for (const article of data.articles) {
// Extract tickers from headline
const tickers = extractTickers(article.title + " " + article.description);
// Simple sentiment from title keywords
const sentiment = article.title.toLowerCase().includes("surge") ? 0.7
: article.title.toLowerCase().includes("drop") ? -0.5
: 0.1;
for (const symbol of tickers) {
signals.push({
symbol,
source: "news_api",
source_detail: article.source.name,
sentiment: sentiment * 0.8, // Apply source weight
raw_sentiment: sentiment,
volume: 1,
freshness: calculateTimeDecay(new Date(article.publishedAt).getTime()),
source_weight: 0.8,
reason: `News: ${article.title.slice(0, 60)}...`,
});
}
}
} catch (error) {
this.log("NewsAPI", "error", { message: String(error) });
}
return signals;
}
Example: Custom Exit Logic
Add trailing stops or time-based exits in runAnalyst():
// Trailing stop: sell if we're up 5%+ but have dropped 2% from peak
const entry = this.state.positionEntries[pos.symbol];
if (entry) {
const plPct = ((pos.current_price - entry.entry_price) / entry.entry_price) * 100;
const peakPct = ((entry.peak_price - entry.entry_price) / entry.entry_price) * 100;
if (plPct > 5 && plPct < peakPct - 2) {
await this.executeSell(pos.symbol, pos.qty, "Trailing stop triggered");
}
}
Example: Multi-Source Confirmation
Only trade when multiple sources agree:
// Group signals by symbol
const bySymbol = new Map<string, Signal[]>();
for (const signal of this.state.signalCache) {
const arr = bySymbol.get(signal.symbol) || [];
arr.push(signal);
bySymbol.set(signal.symbol, arr);
}
// Only consider symbols with 2+ sources agreeing
const confirmed = [...bySymbol.entries()]
.filter(([_, signals]) => {
const sources = new Set(signals.map(s => s.source));
return sources.size >= 2;
})
.filter(([_, signals]) => {
const avgSentiment = signals.reduce((a, b) => a + b.sentiment, 0) / signals.length;
return avgSentiment >= 0.5;
});
Ideas for Extension
- News APIs — Benzinga, Polygon, Alpha Vantage
- SEC Filings — Insider trading, 13F filings
- Options Flow — Unusual options activity
- Earnings — Pre/post earnings strategies
- Sector Rotation — Track money flow between sectors
- Analyst Ratings — Track upgrades/downgrades
Tip: Start simple. Add one data source, test it thoroughly, then add more complexity.