The Harness
Customize the trading agent code.
Overview
The Harness (src/durable-objects/makora-harness.ts) is the core trading agent. It runs as a Cloudflare Durable Object with persistent state and alarm-based scheduling.
This page is for developers who want to modify the trading logic, add data sources, or customize LLM prompts. For setup, see Getting Started. For config options, see Configuration.
Code Markers
The harness file uses markers to help you find what to modify:
| Marker | Meaning |
|---|---|
| [TUNE] | Numeric values you can adjust (thresholds, limits, percentages) |
| [TOGGLE] | Features you can enable/disable |
| [CUSTOMIZABLE] | Sections with code you might want to modify |
Section Guide
| Section | Lines | What to Customize |
|---|---|---|
| AgentConfig | 51-104 | All tunable parameters (risk, sizing, thresholds) |
| SOURCE_CONFIG | 218-252 | Source weights, flair multipliers, time decay |
| Helper Functions | 328-408 | Sentiment detection, ticker extraction |
| Alarm Handler | 430-510 | What runs when, intervals, order of operations |
| Data Gathering | 640-920 | Add new data sources here |
| 980-1200 | Confirmation logic, budget caps | |
| LLM Research | 1210-1540 | Prompts for signal and position analysis |
| Trading Logic | 1550-1730 | Entry/exit rules, position sizing |
| Staleness | 1740-1800 | When to exit positions that lost momentum |
| Options | 1810-2010 | Delta, DTE, options-specific limits |
| Pre-Market | 2020-2140 | Morning analysis and plan execution |
Adding a New Data Source
The most common customization is adding a new data source. Here's how:
1. Create a gather method
private async gatherMySource(): Promise<Signal[]> {
const signals: Signal[] = [];
try {
// Fetch from your API
const res = await fetch("https://your-api.com/data");
const data = await res.json();
for (const item of data.items) {
const tickers = extractTickers(item.text);
const sentiment = detectSentiment(item.text);
for (const symbol of tickers) {
signals.push({
symbol,
source: "my_source",
source_detail: "my_source_detail",
sentiment: sentiment * 0.9, // Apply source weight
raw_sentiment: sentiment,
volume: 1,
freshness: 1.0,
source_weight: 0.9,
reason: `MySource: ${item.summary.slice(0, 50)}`,
});
}
}
} catch (error) {
this.log("MySource", "error", { message: String(error) });
}
return signals;
}
2. Add to runDataGatherers
private async runDataGatherers(): Promise<void> {
const [stocktwitsSignals, redditSignals, cryptoSignals, mySignals] =
await Promise.all([
this.gatherStockTwits(),
this.gatherReddit(),
this.gatherCrypto(),
this.gatherMySource(), // Add here
]);
this.state.signalCache = [
...stocktwitsSignals,
...redditSignals,
...cryptoSignals,
...mySignals, // And here
];
}
3. Add source weight
const SOURCE_CONFIG = {
weights: {
stocktwits: 0.85,
reddit_wallstreetbets: 0.6,
// ...
my_source: 0.9, // Add your source weight
},
// ...
};
Modifying Source Weights
The SOURCE_CONFIG object controls how much to trust each data source:
const SOURCE_CONFIG = {
weights: {
stocktwits: 0.85, // Decent signal, some noise
reddit_wallstreetbets: 0.6, // High volume, lots of memes
reddit_stocks: 0.9, // Higher quality discussions
reddit_investing: 0.8, // Long-term focused
twitter_fintwit: 0.95, // Real traders
},
flairMultipliers: {
"DD": 1.5, // Due Diligence - boost
"YOLO": 0.6, // Entertainment - penalize
"Meme": 0.4,
},
decayHalfLifeMinutes: 120, // How fast old posts lose weight
};
Customizing LLM Prompts
The LLM research prompts are in the researchSignal() and analyzePositionWithLLM() methods. You can modify what context the LLM receives and how it should respond.
wrangler.jsonc.
For all configuration options and API endpoints, see Configuration.