Algorithmic Trading Syndicate
A concurrent multi-agent quantitative hedge fund in Turn. Three autonomous agents (Technical, Sentiment, and Chairman) run as isolated actors. The technical and sentiment agents analyse an asset concurrently and return typed signals. The Chairman receives both signals via mailbox and synthesizes a final trade decision, gated on confidence.
This example demonstrates:
- Full concurrent multi-agent architecture with
spawn_linkand typed mailbox messaging - Isolated context windows per actor so each agent reasons independently
confidence-gated fallbacks on every decision- Durable trade memory via
rememberandrecall
// Turn Language: The Algorithmic Trading Syndicate
// Concurrent multi-agent hedge fund.
// Demonstrates: spawn_link, struct inference, mailbox messaging, confidence gating.
struct TechnicalSignal { ticker: Str, signal: Str, strength: Num, rationale: Str };
struct SentimentSignal { ticker: Str, sentiment: Str, score: Num, summary: Str };
struct TradeDecision { ticker: Str, action: Str, size_pct: Num, conviction: Num, memo: Str };
turn {
let symbol = "NVDA";
let session = recall("session_count");
if session == null {
session = 1;
} else {
session = session + 1;
}
remember("session_count", session);
call("echo", "=== QUANT SYNDICATE: Session #" + session + " ===");
call("echo", "Target Asset: " + symbol);
let parent = self;
// Spawn technical and sentiment agents concurrently.
// Each actor has its own isolated stack, heap, and context window.
let tech_pid = spawn_link turn() {
call("echo", "[Technical] Analysing " + symbol + "...");
context.system("You are a quantitative technical analyst. Focus on momentum, RSI, and moving averages.");
context.append("Target: " + symbol);
let sig = infer TechnicalSignal {
"Generate a technical analysis signal (BUY, SELL, or HOLD) for " + symbol + " with a strength score 0.0 to 1.0.";
};
send parent, { "from": "technical", "result": sig };
};
let sent_pid = spawn_link turn() {
call("echo", "[Sentiment] Parsing macro signals for " + symbol + "...");
context.system("You are a macro-economic news sentiment analyst.");
context.append("Target: " + symbol);
let sig = infer SentimentSignal {
"Generate a news sentiment score (0.0 to 1.0) and brief summary for " + symbol + ".";
};
send parent, { "from": "sentiment", "result": sig };
};
// Chairman waits for both agents via mailbox.
// No polling, no sleep. The VM yields until messages arrive.
let msg1 = receive;
let msg2 = receive;
let tech_map = msg1["result"];
let sent_map = msg2["result"];
call("echo", "[Technical] Signal: " + tech_map["signal"] + " | Strength: " + tech_map["strength"]);
call("echo", "[Sentiment] Score: " + sent_map["score"] + " | " + sent_map["summary"]);
call("echo", "[Chairman] Synthesizing final trade...");
context.append("Technical Signal: " + tech_map["signal"] + " (Strength: " + tech_map["strength"] + ")");
context.append("Sentiment Score: " + sent_map["score"] + ": " + sent_map["summary"]);
let decision = infer TradeDecision {
"Synthesize a final trade decision for " + symbol + ". If signals conflict, conviction should be low and action should be HOLD.";
};
// Confidence gate: if the model is uncertain, default to safety.
if confidence decision < 0.85 {
call("echo", "WARNING: Confidence threshold not met. Executing fallback to HOLD.");
let fallback = TradeDecision {
ticker: symbol,
action: "HOLD",
size_pct: 0,
conviction: 0,
memo: "Fallback triggered: inference confidence below threshold."
};
return fallback;
}
remember("last_trade_" + symbol, "Session " + session + ": " + decision.action + " at " + decision.size_pct + "%. Conviction: " + decision.conviction);
call("echo", "===================================================");
call("echo", "FINAL TRADE: " + decision.action + " " + decision.size_pct + "%");
call("echo", "Rationale: " + decision.memo);
call("echo", "===================================================");
return decision;
}How It Works
The program spawns two actors concurrently. Each runs its own infer call in an isolated context window, so the technical agent and sentiment agent reason independently without interfering with each other's prompts.
Both agents send their result back to the parent via send parent, .... The Chairman then calls receive twice, collecting both signals from the mailbox regardless of which agent finishes first. This is the idiomatic Turn scatter/gather pattern: no polling, no sleep, no shared files.
The Chairman enriches its context window with both signals before making the final inference. A confidence gate ensures that if the model is uncertain about the trade, the system falls back to HOLD rather than acting on weak signal.
Trade decisions are persisted via remember so each session can inspect what was decided in prior runs with recall.
Running It
export TURN_LLM_PROVIDER=openai
export OPENAI_API_KEY=sk-...
turn run impl/examples/quant_syndicate.tn --id quant_syndicate
The full source is in impl/examples/quant_syndicate.tn.