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
Twitter 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.

Tip: The Harness stores state in Durable Object storage. To reset state, redeploy with a new migration tag in wrangler.jsonc.

For all configuration options and API endpoints, see Configuration.