container lifecycle management
This commit is contained in:
253
gateway/src/workflows/README.md
Normal file
253
gateway/src/workflows/README.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# LangGraph Workflows for Trading
|
||||
|
||||
Complex, stateful workflows built with LangGraph for trading-specific tasks.
|
||||
|
||||
## Overview
|
||||
|
||||
LangGraph provides:
|
||||
- **Stateful execution**: Workflow state persists across failures
|
||||
- **Conditional branching**: Route based on market conditions, backtest results, etc.
|
||||
- **Human-in-the-loop**: Pause for user approval before executing trades
|
||||
- **Loops & retries**: Backtest with different parameters, retry failed operations
|
||||
- **Multi-agent**: Different LLMs for different tasks (analysis, risk, execution)
|
||||
|
||||
## Workflows
|
||||
|
||||
### Strategy Analysis (`strategy-analysis.ts`)
|
||||
|
||||
Multi-step pipeline for analyzing trading strategies:
|
||||
|
||||
```typescript
|
||||
import { buildStrategyAnalysisWorkflow } from './workflows/strategy-analysis.js';
|
||||
|
||||
const workflow = buildStrategyAnalysisWorkflow(model, logger, mcpBacktestFn);
|
||||
|
||||
const result = await workflow.invoke({
|
||||
strategyCode: userStrategy,
|
||||
ticker: 'BTC/USDT',
|
||||
timeframe: '1h',
|
||||
});
|
||||
|
||||
console.log(result.recommendation); // Go/no-go decision
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. **Code Review** - LLM analyzes strategy code for bugs, logic errors
|
||||
2. **Backtest** - Runs backtest via user's MCP server
|
||||
3. **Risk Assessment** - LLM evaluates results (drawdown, Sharpe, etc.)
|
||||
4. **Human Approval** - Pauses for user review
|
||||
5. **Recommendation** - Final go/no-go decision
|
||||
|
||||
**Benefits:**
|
||||
- Stateful: Can resume if server restarts
|
||||
- Human-in-the-loop: User must approve before deployment
|
||||
- Multi-step reasoning: Each step builds on previous
|
||||
|
||||
---
|
||||
|
||||
## Future Workflows
|
||||
|
||||
### Market Scanner
|
||||
|
||||
Scan multiple tickers for trading opportunities:
|
||||
|
||||
```typescript
|
||||
const scanner = buildMarketScannerWorkflow(model, logger);
|
||||
|
||||
const result = await scanner.invoke({
|
||||
tickers: ['BTC/USDT', 'ETH/USDT', 'SOL/USDT'],
|
||||
strategies: ['momentum', 'mean_reversion'],
|
||||
timeframe: '1h',
|
||||
});
|
||||
|
||||
// Returns ranked opportunities
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. **Fetch Data** - Get OHLC for all tickers
|
||||
2. **Apply Strategies** - Run each strategy on each ticker (parallel)
|
||||
3. **Rank Signals** - Score by confidence, risk/reward
|
||||
4. **Filter** - Apply user's risk limits
|
||||
5. **Return Top N** - Best opportunities
|
||||
|
||||
---
|
||||
|
||||
### Portfolio Optimization
|
||||
|
||||
Optimize position sizing across multiple strategies:
|
||||
|
||||
```typescript
|
||||
const optimizer = buildPortfolioOptimizerWorkflow(model, logger);
|
||||
|
||||
const result = await optimizer.invoke({
|
||||
strategies: [strategy1, strategy2, strategy3],
|
||||
totalCapital: 100000,
|
||||
maxRiskPerTrade: 0.02,
|
||||
});
|
||||
|
||||
// Returns optimal allocation
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. **Backtest All** - Run backtests for each strategy
|
||||
2. **Correlation Analysis** - Check strategy correlation
|
||||
3. **Monte Carlo** - Simulate portfolio performance
|
||||
4. **Optimize** - Find optimal weights (Sharpe maximization)
|
||||
5. **Risk Check** - Validate against user limits
|
||||
|
||||
---
|
||||
|
||||
### Trade Execution Monitor
|
||||
|
||||
Monitor trade execution and adapt to market conditions:
|
||||
|
||||
```typescript
|
||||
const monitor = buildTradeExecutionWorkflow(model, logger, exchange);
|
||||
|
||||
const result = await monitor.invoke({
|
||||
tradeId: 'xyz',
|
||||
targetPrice: 45000,
|
||||
maxSlippage: 0.001,
|
||||
timeLimit: 60, // seconds
|
||||
});
|
||||
```
|
||||
|
||||
**Steps:**
|
||||
1. **Place Order** - Submit order to exchange
|
||||
2. **Monitor Fill** - Check fill status every second
|
||||
3. **Adapt** - If not filling, adjust price (within slippage)
|
||||
4. **Retry Logic** - If rejected, retry with backoff
|
||||
5. **Timeout** - Cancel if time limit exceeded
|
||||
6. **Report** - Final execution report
|
||||
|
||||
---
|
||||
|
||||
## Using Workflows in Gateway
|
||||
|
||||
### Simple Chat vs Complex Workflow
|
||||
|
||||
```typescript
|
||||
// gateway/src/orchestrator.ts
|
||||
|
||||
export class MessageOrchestrator {
|
||||
async handleMessage(msg: InboundMessage) {
|
||||
// Route based on complexity
|
||||
if (this.isSimpleQuery(msg)) {
|
||||
// Use agent harness for streaming chat
|
||||
return this.harness.streamMessage(msg);
|
||||
}
|
||||
|
||||
if (this.isWorkflowRequest(msg)) {
|
||||
// Use LangGraph for complex analysis
|
||||
return this.executeWorkflow(msg);
|
||||
}
|
||||
}
|
||||
|
||||
async executeWorkflow(msg: InboundMessage) {
|
||||
const { type, params } = this.parseWorkflowRequest(msg);
|
||||
|
||||
switch (type) {
|
||||
case 'analyze_strategy':
|
||||
const workflow = buildStrategyAnalysisWorkflow(...);
|
||||
return await workflow.invoke(params);
|
||||
|
||||
case 'scan_market':
|
||||
const scanner = buildMarketScannerWorkflow(...);
|
||||
return await scanner.invoke(params);
|
||||
|
||||
// ... more workflows
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benefits for Trading
|
||||
|
||||
### vs Simple LLM Calls
|
||||
|
||||
| Scenario | Simple LLM | LangGraph Workflow |
|
||||
|----------|-----------|-------------------|
|
||||
| "What's the RSI?" | ✅ Fast, streaming | ❌ Overkill |
|
||||
| "Analyze this strategy" | ❌ Limited context | ✅ Multi-step analysis |
|
||||
| "Backtest 10 param combos" | ❌ No loops | ✅ Conditional loops |
|
||||
| "Execute if approved" | ❌ No state | ✅ Human-in-the-loop |
|
||||
| Server crashes mid-analysis | ❌ Lost progress | ✅ Resume from checkpoint |
|
||||
|
||||
### When to Use Workflows
|
||||
|
||||
**Use LangGraph when:**
|
||||
- Multi-step analysis (backtest → risk → approval)
|
||||
- Conditional logic (if bullish → momentum, else → mean-reversion)
|
||||
- Human approval required (pause workflow)
|
||||
- Loops needed (try different parameters)
|
||||
- Long-running (can survive restarts)
|
||||
|
||||
**Use Agent Harness when:**
|
||||
- Simple Q&A ("What is RSI?")
|
||||
- Fast response needed (streaming chat)
|
||||
- Single tool call ("Get my watchlist")
|
||||
- Real-time interaction (Telegram, WebSocket)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### State Persistence
|
||||
|
||||
LangGraph can persist state to database:
|
||||
|
||||
```typescript
|
||||
import { MemorySaver } from '@langchain/langgraph';
|
||||
|
||||
const checkpointer = new MemorySaver();
|
||||
|
||||
const workflow = graph.compile({ checkpointer });
|
||||
|
||||
// Resume from checkpoint
|
||||
const result = await workflow.invoke(input, {
|
||||
configurable: { thread_id: 'user-123-strategy-analysis' }
|
||||
});
|
||||
```
|
||||
|
||||
### Human-in-the-Loop
|
||||
|
||||
Pause workflow for user input:
|
||||
|
||||
```typescript
|
||||
const workflow = graph
|
||||
.addNode('human_approval', humanApprovalNode)
|
||||
.interrupt('human_approval'); // Pauses here
|
||||
|
||||
// User reviews in UI
|
||||
const approved = await getUserApproval(workflowId);
|
||||
|
||||
// Resume workflow
|
||||
await workflow.resume(state, { approved });
|
||||
```
|
||||
|
||||
### Multi-Agent
|
||||
|
||||
Use different models for different tasks:
|
||||
|
||||
```typescript
|
||||
const analysisModel = new ChatAnthropic({ model: 'claude-3-opus' }); // Smart
|
||||
const codeModel = new ChatOpenAI({ model: 'gpt-4o' }); // Good at code
|
||||
const cheapModel = new ChatOpenAI({ model: 'gpt-4o-mini' }); // Fast
|
||||
|
||||
const workflow = graph
|
||||
.addNode('analyze', (state) => analysisModel.invoke(...))
|
||||
.addNode('code_review', (state) => codeModel.invoke(...))
|
||||
.addNode('summarize', (state) => cheapModel.invoke(...));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Implement remaining workflows (scanner, optimizer, execution)
|
||||
2. Add state persistence (PostgreSQL checkpointer)
|
||||
3. Integrate human-in-the-loop with WebSocket
|
||||
4. Add workflow monitoring dashboard
|
||||
5. Performance optimization (parallel execution)
|
||||
162
gateway/src/workflows/strategy-analysis.ts
Normal file
162
gateway/src/workflows/strategy-analysis.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import { StateGraph, Annotation } from '@langchain/langgraph';
|
||||
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
||||
import { HumanMessage, SystemMessage } from '@langchain/core/messages';
|
||||
import type { FastifyBaseLogger } from 'fastify';
|
||||
|
||||
/**
|
||||
* State for strategy analysis workflow
|
||||
*/
|
||||
const StrategyAnalysisState = Annotation.Root({
|
||||
strategyCode: Annotation<string>(),
|
||||
ticker: Annotation<string>(),
|
||||
timeframe: Annotation<string>(),
|
||||
|
||||
// Analysis steps
|
||||
codeReview: Annotation<string | null>({
|
||||
default: () => null,
|
||||
}),
|
||||
backtestResults: Annotation<Record<string, unknown> | null>({
|
||||
default: () => null,
|
||||
}),
|
||||
riskAssessment: Annotation<string | null>({
|
||||
default: () => null,
|
||||
}),
|
||||
humanApproved: Annotation<boolean>({
|
||||
default: () => false,
|
||||
}),
|
||||
|
||||
// Final output
|
||||
recommendation: Annotation<string | null>({
|
||||
default: () => null,
|
||||
}),
|
||||
});
|
||||
|
||||
type StrategyAnalysisStateType = typeof StrategyAnalysisState.State;
|
||||
|
||||
/**
|
||||
* Build strategy analysis workflow using LangGraph
|
||||
*
|
||||
* Workflow steps:
|
||||
* 1. Code review (LLM analyzes strategy code)
|
||||
* 2. Backtest (calls user's MCP backtest tool)
|
||||
* 3. Risk assessment (LLM evaluates results)
|
||||
* 4. Human approval (pause for user review)
|
||||
* 5. Final recommendation
|
||||
*/
|
||||
export function buildStrategyAnalysisWorkflow(
|
||||
model: BaseChatModel,
|
||||
logger: FastifyBaseLogger,
|
||||
mcpBacktestFn: (strategy: string, ticker: string, timeframe: string) => Promise<Record<string, unknown>>
|
||||
) {
|
||||
// Node: Code Review
|
||||
const codeReviewNode = async (state: StrategyAnalysisStateType) => {
|
||||
logger.info('Strategy workflow: Code review');
|
||||
|
||||
const systemPrompt = `You are an expert trading strategy analyst.
|
||||
Review the following strategy code for potential issues, bugs, or improvements.
|
||||
Focus on: logic errors, edge cases, performance, and trading best practices.`;
|
||||
|
||||
const response = await model.invoke([
|
||||
new SystemMessage(systemPrompt),
|
||||
new HumanMessage(`Review this strategy:\n\n${state.strategyCode}`),
|
||||
]);
|
||||
|
||||
return {
|
||||
codeReview: response.content as string,
|
||||
};
|
||||
};
|
||||
|
||||
// Node: Backtest
|
||||
const backtestNode = async (state: StrategyAnalysisStateType) => {
|
||||
logger.info('Strategy workflow: Running backtest');
|
||||
|
||||
const results = await mcpBacktestFn(state.strategyCode, state.ticker, state.timeframe);
|
||||
|
||||
return {
|
||||
backtestResults: results,
|
||||
};
|
||||
};
|
||||
|
||||
// Node: Risk Assessment
|
||||
const riskAssessmentNode = async (state: StrategyAnalysisStateType) => {
|
||||
logger.info('Strategy workflow: Risk assessment');
|
||||
|
||||
const systemPrompt = `You are a risk management expert for trading strategies.
|
||||
Analyze the backtest results and provide a risk assessment.
|
||||
Focus on: drawdown, win rate, Sharpe ratio, position sizing, and risk of ruin.`;
|
||||
|
||||
const response = await model.invoke([
|
||||
new SystemMessage(systemPrompt),
|
||||
new HumanMessage(
|
||||
`Code review: ${state.codeReview}\n\nBacktest results: ${JSON.stringify(state.backtestResults, null, 2)}\n\nProvide risk assessment:`
|
||||
),
|
||||
]);
|
||||
|
||||
return {
|
||||
riskAssessment: response.content as string,
|
||||
};
|
||||
};
|
||||
|
||||
// Node: Human Approval (placeholder - would integrate with UI)
|
||||
const humanApprovalNode = async (state: StrategyAnalysisStateType) => {
|
||||
logger.info('Strategy workflow: Awaiting human approval');
|
||||
|
||||
// In real implementation, this would pause and wait for user input
|
||||
// For now, auto-approve
|
||||
return {
|
||||
humanApproved: true,
|
||||
};
|
||||
};
|
||||
|
||||
// Node: Final Recommendation
|
||||
const recommendationNode = async (state: StrategyAnalysisStateType) => {
|
||||
logger.info('Strategy workflow: Generating recommendation');
|
||||
|
||||
const systemPrompt = `Provide a final recommendation on whether to deploy this trading strategy.
|
||||
Summarize the code review, backtest results, and risk assessment.
|
||||
Give clear go/no-go decision with reasoning.`;
|
||||
|
||||
const response = await model.invoke([
|
||||
new SystemMessage(systemPrompt),
|
||||
new HumanMessage(
|
||||
`Code review: ${state.codeReview}\n\nBacktest: ${JSON.stringify(state.backtestResults)}\n\nRisk: ${state.riskAssessment}\n\nApproved: ${state.humanApproved}\n\nYour recommendation:`
|
||||
),
|
||||
]);
|
||||
|
||||
return {
|
||||
recommendation: response.content as string,
|
||||
};
|
||||
};
|
||||
|
||||
// Build graph
|
||||
const workflow = new StateGraph(StrategyAnalysisState)
|
||||
.addNode('code_review', codeReviewNode)
|
||||
.addNode('backtest', backtestNode)
|
||||
.addNode('risk_assessment', riskAssessmentNode)
|
||||
.addNode('human_approval', humanApprovalNode)
|
||||
.addNode('recommendation', recommendationNode)
|
||||
.addEdge('__start__', 'code_review')
|
||||
.addEdge('code_review', 'backtest')
|
||||
.addEdge('backtest', 'risk_assessment')
|
||||
.addEdge('risk_assessment', 'human_approval')
|
||||
.addConditionalEdges('human_approval', (state) => {
|
||||
return state.humanApproved ? 'recommendation' : '__end__';
|
||||
})
|
||||
.addEdge('recommendation', '__end__');
|
||||
|
||||
return workflow.compile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Example usage:
|
||||
*
|
||||
* const workflow = buildStrategyAnalysisWorkflow(model, logger, mcpBacktestFn);
|
||||
*
|
||||
* const result = await workflow.invoke({
|
||||
* strategyCode: "strategy code here",
|
||||
* ticker: "BTC/USDT",
|
||||
* timeframe: "1h",
|
||||
* });
|
||||
*
|
||||
* console.log(result.recommendation);
|
||||
*/
|
||||
Reference in New Issue
Block a user