data fixes; indicator=>workspace sync

This commit is contained in:
2026-03-31 20:29:12 -04:00
parent 998f69fa1a
commit cd28e18e52
45 changed files with 1324 additions and 1239 deletions

View File

@@ -8,8 +8,9 @@ import type { InboundMessage, OutboundMessage } from '../types/messages.js';
import { MCPClientConnector } from './mcp-client.js';
import { LLMProviderFactory, type ProviderConfig } from '../llm/provider.js';
import { ModelRouter, RoutingStrategy } from '../llm/router.js';
import type { ModelMiddleware } from '../llm/middleware.js';
import type { WorkspaceManager } from '../workspace/workspace-manager.js';
import type { ChannelAdapter } from '../workspace/index.js';
import type { ChannelAdapter, PathTriggerContext } from '../workspace/index.js';
import type { ResearchSubagent } from './subagents/research/index.js';
import type { DynamicStructuredTool } from '@langchain/core/tools';
import { getToolRegistry } from '../tools/tool-registry.js';
@@ -70,10 +71,10 @@ export class AgentHarness {
private config: AgentHarnessConfig;
private modelFactory: LLMProviderFactory;
private modelRouter: ModelRouter;
private middleware: ModelMiddleware | undefined;
private mcpClient: MCPClientConnector;
private workspaceManager?: WorkspaceManager;
private channelAdapter?: ChannelAdapter;
private isFirstMessage: boolean = true;
private researchSubagent?: ResearchSubagent;
private availableMCPTools: MCPToolInfo[] = [];
private researchImageCapture: Array<{ data: string; mimeType: string }> = [];
@@ -94,6 +95,8 @@ export class AgentHarness {
mcpServerUrl: config.mcpServerUrl,
logger: config.logger,
});
this.registerWorkspaceTriggers();
}
/**
@@ -193,7 +196,7 @@ export class AgentHarness {
const { createResearchSubagent } = await import('./subagents/research/index.js');
// Create a model for the research subagent
const model = await this.modelRouter.route(
const { model } = await this.modelRouter.route(
'research analysis', // dummy query
this.config.license,
RoutingStrategy.COMPLEXITY,
@@ -429,11 +432,25 @@ export class AgentHarness {
// 2. Load recent conversation history
const channelKey = this.config.channelType ?? ChannelType.WEBSOCKET;
const storedMessages = this.conversationStore
let storedMessages = this.conversationStore
? await this.conversationStore.getRecentMessages(
this.config.userId, this.config.sessionId, this.config.historyLimit, channelKey
)
: [];
// First turn: seed conversation history with current workspace state
if (storedMessages.length === 0 && this.workspaceManager && this.conversationStore) {
const workspaceJSON = this.workspaceManager.serializeState();
const content = `[Workspace State]\n\`\`\`json\n${workspaceJSON}\n\`\`\``;
await this.conversationStore.saveMessage(
this.config.userId, this.config.sessionId,
'workspace', content, { isWorkspaceContext: true }, channelKey
);
storedMessages = await this.conversationStore.getRecentMessages(
this.config.userId, this.config.sessionId, this.config.historyLimit, channelKey
);
}
const history = this.conversationStore
? this.conversationStore.toLangChainMessages(storedMessages)
: [];
@@ -441,12 +458,13 @@ export class AgentHarness {
// 4. Get the configured model
this.config.logger.debug('Routing to model');
const model = await this.modelRouter.route(
const { model, middleware } = await this.modelRouter.route(
message.content,
this.config.license,
RoutingStrategy.COMPLEXITY,
this.config.userId
);
this.middleware = middleware;
this.config.logger.info({ modelName: model.constructor.name }, 'Model selected');
// 5. Build LangChain messages
@@ -489,6 +507,11 @@ export class AgentHarness {
'Tools loaded for main agent'
);
// Apply middleware (e.g. Anthropic prompt caching)
const processedMessages = this.middleware
? this.middleware.processMessages(langchainMessages, tools)
: langchainMessages;
// 7. Bind tools to model
const modelWithTools = tools.length > 0 && model.bindTools ? model.bindTools(tools) : model;
@@ -501,7 +524,7 @@ export class AgentHarness {
// 8. Call LLM with tool calling loop
this.config.logger.info('Invoking LLM with tool support');
const assistantMessage = await this.executeWithToolCalling(modelWithTools, langchainMessages, tools);
const assistantMessage = await this.executeWithToolCalling(modelWithTools, processedMessages, tools, 10);
this.config.logger.info(
{ responseLength: assistantMessage.length },
@@ -518,11 +541,6 @@ export class AgentHarness {
);
}
// Mark first message as processed
if (this.isFirstMessage) {
this.isFirstMessage = false;
}
return {
messageId: `msg_${Date.now()}`,
sessionId: message.sessionId,
@@ -556,16 +574,10 @@ export class AgentHarness {
private async buildSystemPrompt(): Promise<string> {
// Load template and populate with license info
const template = await AgentHarness.loadSystemPromptTemplate();
let prompt = template
const prompt = template
.replace('{{licenseType}}', this.config.license.licenseType)
.replace('{{features}}', JSON.stringify(this.config.license.features, null, 2));
// Add full workspace state from WorkspaceManager (first message only)
if (this.isFirstMessage && this.workspaceManager) {
const workspaceJSON = this.workspaceManager.serializeState();
prompt += `\n\n# Current Workspace State\n\`\`\`json\n${workspaceJSON}\n\`\`\``;
}
return prompt;
}
@@ -574,9 +586,14 @@ export class AgentHarness {
*/
private getToolLabel(toolName: string): string {
const labels: Record<string, string> = {
research_agent: 'Researching...',
research: 'Researching...',
get_chart_data: 'Fetching chart data...',
symbol_lookup: 'Looking up symbol...',
category_list: 'Seeing what we have...',
category_edit: 'Coding...',
category_write: 'Coding...',
category_read: 'Inspecting...',
execute_research: 'Running script...',
};
return labels[toolName] ?? `Running ${toolName}...`;
}
@@ -685,6 +702,26 @@ export class AgentHarness {
return result;
}
/**
* Register workspace path triggers to record state changes into conversation history.
*/
private registerWorkspaceTriggers(): void {
if (!this.workspaceManager || !this.conversationStore) return;
const channelKey = this.config.channelType ?? ChannelType.WEBSOCKET;
for (const store of ['shapes', 'indicators', 'chartState']) {
this.workspaceManager.onPathChange(`/${store}/*`, async (_old: unknown, newVal: unknown, ctx: PathTriggerContext) => {
const content = `[Workspace Update] ${ctx.store}${ctx.path}\n${JSON.stringify(newVal, null, 2)}`;
await this.conversationStore!.saveMessage(
this.config.userId, this.config.sessionId,
'workspace', content,
{ isWorkspaceUpdate: true, store: ctx.store, seq: ctx.seq },
channelKey
);
});
}
}
/**
* End the session: flush conversation to cold storage, then release resources.
* Called by channel handlers on disconnect, session expiry, or graceful shutdown.

View File

@@ -11,7 +11,7 @@ export interface StoredMessage {
id: string;
userId: string;
sessionId: string;
role: 'user' | 'assistant' | 'system';
role: 'user' | 'assistant' | 'system' | 'workspace';
content: string;
timestamp: number; // microseconds (Iceberg convention)
metadata?: Record<string, unknown>;
@@ -44,7 +44,7 @@ export class ConversationStore {
async saveMessage(
userId: string,
sessionId: string,
role: 'user' | 'assistant' | 'system',
role: 'user' | 'assistant' | 'system' | 'workspace',
content: string,
metadata?: Record<string, unknown>,
channelType?: string
@@ -171,6 +171,8 @@ export class ConversationStore {
return new AIMessage(msg.content);
case 'system':
return new SystemMessage(msg.content);
case 'workspace':
return new HumanMessage(msg.content);
default:
throw new Error(`Unknown role: ${msg.role}`);
}

View File

@@ -59,6 +59,11 @@ Example usage:
- User: "Does Friday price action correlate with Monday?"
- You: Call research tool with instruction="Analyze correlation between Friday and Monday price action during NY trading hours (9:30-4:00 ET)", name="Friday-Monday Correlation"
### category_list
List existing research scripts (category="research").
Use this before calling the research tool to check whether a relevant script already exists.
If one does, pass its exact name to the research tool so the subagent updates it rather than creating a new one.
### symbol-lookup
Look up trading symbols and get metadata.
Use this when users mention tickers or need symbol information.

View File

@@ -12,6 +12,7 @@ maxTokens: 8192
memoryFiles:
- api-reference.md
- usage-examples.md
- pandas-ta-reference.md
# System prompt file
systemPromptFile: system-prompt.md

View File

@@ -0,0 +1,227 @@
# pandas-ta Reference for Research Scripts
The sandbox environment uses **pandas-ta** as the standard indicator library. Always use it for technical indicator calculations; do not write manual rolling/ewm implementations.
```python
import pandas_ta as ta
```
## Calling Convention
pandas-ta functions accept a Series (or OHLCV columns) plus keyword parameters that match pandas-ta's documented argument names:
```python
# Single-series indicator
rsi = ta.rsi(df['close'], length=14) # returns Series
# OHLCV indicator
atr = ta.atr(df['high'], df['low'], df['close'], length=14)
# Multi-output indicator (returns DataFrame)
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
# columns: MACD_12_26_9, MACDh_12_26_9, MACDs_12_26_9
bbands_df = ta.bbands(df['close'], length=20, std=2.0)
# columns: BBL_20_2.0, BBM_20_2.0, BBU_20_2.0, BBB_20_2.0, BBP_20_2.0
```
## Default Parameters
Key defaults to keep in mind:
- Most period/length indicators: `length=14` (use `length=` not `timeperiod=`)
- `bbands`: `length=20, std=2.0` (note: single `std`, not separate upper/lower)
- `macd`: `fast=12, slow=26, signal=9`
- `stoch`: `k=14, d=3, smooth_k=3`
- `psar`: `af0=0.02, af=0.02, max_af=0.2`
- `vwap`: `anchor='D'` (requires DatetimeIndex)
- `ichimoku`: `tenkan=9, kijun=26, senkou=52`
## Available Indicators
These match the indicators supported by the TradingView web client. Use the pandas-ta function name shown here (lowercase):
### Overlap / Moving Averages — plotted on the price pane
| Function | Description |
|----------|-------------|
| `sma` | Simple Moving Average — plain arithmetic mean over `length` periods |
| `ema` | Exponential Moving Average — more weight on recent prices |
| `wma` | Weighted Moving Average — linearly increasing weights |
| `dema` | Double EMA — two layers of EMA to reduce lag |
| `tema` | Triple EMA — three layers of EMA, even less lag than DEMA |
| `trima` | Triangular MA — double-smoothed SMA, very smooth |
| `kama` | Kaufman Adaptive MA — adapts speed to market noise/trending conditions |
| `t3` | T3 Moving Average — Tillson's smooth, low-lag MA using six EMAs |
| `hma` | Hull MA — very low-lag MA using WMAs |
| `alma` | Arnaud Legoux MA — Gaussian-weighted MA with reduced lag and noise |
| `midpoint` | Midpoint of close over `length` periods: (highest + lowest) / 2 |
| `midprice` | Midpoint of high/low over `length` periods |
| `supertrend` | Trend-following band (ATR-based) that flips above/below price |
| `ichimoku` | Ichimoku Cloud — multi-line Japanese trend/support/resistance system |
| `vwap` | Volume-Weighted Average Price — average price weighted by volume, resets on `anchor` |
| `vwma` | Volume-Weighted MA — like SMA but candles weighted by volume |
| `bbands` | Bollinger Bands — SMA ± N standard deviations; returns upper, mid, lower bands |
### Momentum — typically plotted in a separate pane
| Function | Description |
|----------|-------------|
| `rsi` | Relative Strength Index — 0100 oscillator measuring speed of price changes |
| `macd` | MACD — difference of two EMAs plus signal line and histogram |
| `stoch` | Stochastic Oscillator — %K/%D, measures close vs recent high/low range |
| `stochrsi` | Stochastic RSI — applies stochastic formula to RSI values |
| `cci` | Commodity Channel Index — deviation of price from its statistical mean |
| `willr` | Williams %R — inverse stochastic, 100 to 0 oscillator |
| `mom` | Momentum — raw price change over `length` periods |
| `roc` | Rate of Change — percentage price change over `length` periods |
| `trix` | TRIX — 1-period % change of a triple-smoothed EMA |
| `cmo` | Chande Momentum Oscillator — ratio of up/down momentum, 100 to 100 |
| `adx` | Average Directional Index — strength of trend (0100, direction-agnostic) |
| `aroon` | Aroon — measures how recently the highest/lowest price occurred; returns Up, Down, Oscillator |
| `ao` | Awesome Oscillator — difference of 5- and 34-period simple MAs of midprice |
| `bop` | Balance of Power — measures buying vs selling pressure: (closeopen)/(highlow) |
| `uo` | Ultimate Oscillator — weighted combo of three period (fast/medium/slow) buying pressure ratios |
| `apo` | Absolute Price Oscillator — difference between two EMAs (like MACD without signal line) |
| `mfi` | Money Flow Index — RSI-like oscillator using price × volume |
| `coppock` | Coppock Curve — long-term momentum oscillator based on rate-of-change |
| `dpo` | Detrended Price Oscillator — removes trend to show cycle oscillations |
| `fisher` | Fisher Transform — converts price into a Gaussian normal distribution |
| `rvgi` | Relative Vigor Index — compares closeopen to highlow to measure trend vigor |
| `kst` | Know Sure Thing — momentum oscillator from four ROC periods, smoothed |
### Volatility — plotted on price pane or separate
| Function | Description |
|----------|-------------|
| `atr` | Average True Range — average of true range (greatest of HL, HprevC, LprevC) |
| `kc` | Keltner Channels — EMA ± N × ATR bands around price |
| `donchian` | Donchian Channels — highest high / lowest low over `length` periods |
### Volume — plotted in separate pane
| Function | Description |
|----------|-------------|
| `obv` | On Balance Volume — cumulative volume, added on up days, subtracted on down days |
| `ad` | Accumulation/Distribution — running total of the money flow multiplier × volume |
| `adosc` | Chaikin Oscillator — EMA difference of the A/D line |
| `cmf` | Chaikin Money Flow — sum of (money flow volume) / sum of volume over `length` |
| `eom` | Ease of Movement — relates price change to volume; high = price moves easily |
| `efi` | Elder's Force Index — combines price change direction with volume magnitude |
| `kvo` | Klinger Volume Oscillator — EMA difference of volume force |
| `pvt` | Price Volume Trend — cumulative: volume × percentage price change |
### Statistics / Price Transforms
| Function | Description |
|----------|-------------|
| `stdev` | Standard Deviation of close over `length` periods |
| `linreg` | Linear Regression Curve — least-squares line endpoint value over `length` periods |
| `slope` | Linear Regression Slope — gradient of the regression line |
| `hl2` | Median Price — (high + low) / 2 |
| `hlc3` | Typical Price — (high + low + close) / 3 |
| `ohlc4` | Average Price — (open + high + low + close) / 4 |
### Trend
| Function | Description |
|----------|-------------|
| `psar` | Parabolic SAR — trailing stop-and-reverse dots that follow price |
| `vortex` | Vortex Indicator — VI+ / VI lines measuring upward vs downward trend movement |
| `chop` | Choppiness Index — 0100, high = choppy/sideways, low = strong trend |
## Usage Examples
### Single-output indicators
```python
import pandas_ta as ta
df['rsi'] = ta.rsi(df['close'], length=14)
df['ema_20'] = ta.ema(df['close'], length=20)
df['sma_50'] = ta.sma(df['close'], length=50)
df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
df['obv'] = ta.obv(df['close'], df['volume'])
df['adx'] = ta.adx(df['high'], df['low'], df['close'], length=14)['ADX_14']
```
### Multi-output indicators — extract columns by position
```python
# MACD → MACD_12_26_9, MACDh_12_26_9, MACDs_12_26_9
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
df['macd'] = macd_df.iloc[:, 0] # MACD line
df['macd_hist'] = macd_df.iloc[:, 1] # Histogram
df['macd_signal'] = macd_df.iloc[:, 2] # Signal line
# Bollinger Bands → BBL, BBM, BBU, BBB, BBP
bb_df = ta.bbands(df['close'], length=20, std=2.0)
df['bb_lower'] = bb_df.iloc[:, 0] # BBL
df['bb_mid'] = bb_df.iloc[:, 1] # BBM
df['bb_upper'] = bb_df.iloc[:, 2] # BBU
# Stochastic → STOCHk, STOCHd
stoch_df = ta.stoch(df['high'], df['low'], df['close'], k=14, d=3, smooth_k=3)
df['stoch_k'] = stoch_df.iloc[:, 0]
df['stoch_d'] = stoch_df.iloc[:, 1]
# Keltner Channels → KCLe, KCBe, KCUe
kc_df = ta.kc(df['high'], df['low'], df['close'], length=20)
df['kc_lower'] = kc_df.iloc[:, 0]
df['kc_mid'] = kc_df.iloc[:, 1]
df['kc_upper'] = kc_df.iloc[:, 2]
# ADX → ADX_14, DMP_14, DMN_14
adx_df = ta.adx(df['high'], df['low'], df['close'], length=14)
df['adx'] = adx_df.iloc[:, 0] # ADX strength
df['dmp'] = adx_df.iloc[:, 1] # +DI
df['dmn'] = adx_df.iloc[:, 2] # -DI
# Aroon → AROOND_14, AROONU_14, AROONOSC_14
aroon_df = ta.aroon(df['high'], df['low'], length=14)
df['aroon_down'] = aroon_df.iloc[:, 0]
df['aroon_up'] = aroon_df.iloc[:, 1]
# Donchian Channels → DCL, DCM, DCU
dc_df = ta.donchian(df['high'], df['low'], lower_length=20, upper_length=20)
df['dc_lower'] = dc_df.iloc[:, 0]
df['dc_mid'] = dc_df.iloc[:, 1]
df['dc_upper'] = dc_df.iloc[:, 2]
```
### Charting with indicators
```python
import pandas_ta as ta
from dexorder.api import get_api
import asyncio
api = get_api()
df = asyncio.run(api.data.historical_ohlc(
ticker="BINANCE:BTC/USDT",
period_seconds=3600,
start_time="2024-01-01",
end_time="2024-01-08",
extra_columns=["volume"]
))
# Compute indicators
df['ema_20'] = ta.ema(df['close'], length=20)
df['rsi'] = ta.rsi(df['close'], length=14)
macd_df = ta.macd(df['close'])
df['macd'] = macd_df.iloc[:, 0]
df['macd_signal'] = macd_df.iloc[:, 2]
# Main price chart with EMA overlay
fig, ax = api.charting.plot_ohlc(df, title="BTC/USDT 1H", volume=True)
ax.plot(df.index, df['ema_20'], label="EMA 20", color="orange", linewidth=1.5)
ax.legend()
# RSI panel
rsi_ax = api.charting.add_indicator_panel(fig, df, columns=["rsi"], ylabel="RSI", ylim=(0, 100))
rsi_ax.axhline(70, color='red', linestyle='--', alpha=0.5)
rsi_ax.axhline(30, color='green', linestyle='--', alpha=0.5)
# MACD panel
api.charting.add_indicator_panel(fig, df, columns=["macd", "macd_signal"], ylabel="MACD")
```

View File

@@ -112,10 +112,12 @@ fig, ax = api.charting.plot_ohlc(
### Adding Indicator Panels
Use **pandas-ta** for all indicator calculations. Do not write manual rolling/ewm implementations.
```python
from dexorder.api import get_api
import asyncio
import pandas as pd
import pandas_ta as ta
api = get_api()
@@ -127,8 +129,9 @@ df = asyncio.run(api.data.historical_ohlc(
end_time="2021-12-21"
))
# Calculate a simple moving average
df['sma_20'] = df['close'].rolling(window=20).mean()
# Calculate indicators using pandas-ta
df['sma_20'] = ta.sma(df['close'], length=20)
df['rsi'] = ta.rsi(df['close'], length=14)
# Create chart
fig, ax = api.charting.plot_ohlc(df, title="BTC/USDT with SMA")
@@ -138,7 +141,6 @@ ax.plot(df.index, df['sma_20'], label="SMA 20", color="blue", linewidth=2)
ax.legend()
# Add RSI indicator panel below
df['rsi'] = calculate_rsi(df['close'], 14) # Your RSI calculation
rsi_ax = api.charting.add_indicator_panel(
fig, df,
columns=["rsi"],
@@ -149,12 +151,40 @@ rsi_ax.axhline(70, color='red', linestyle='--', alpha=0.5)
rsi_ax.axhline(30, color='green', linestyle='--', alpha=0.5)
```
### Multi-Output Indicators
Some pandas-ta indicators return a DataFrame. Extract the columns you need:
```python
import pandas_ta as ta
# MACD returns: MACD_12_26_9, MACDh_12_26_9, MACDs_12_26_9
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
df['macd'] = macd_df.iloc[:, 0] # MACD line
df['macd_hist'] = macd_df.iloc[:, 1] # Histogram
df['macd_signal'] = macd_df.iloc[:, 2] # Signal line
# Bollinger Bands returns: BBL, BBM, BBU, BBB, BBP
bb_df = ta.bbands(df['close'], length=20, std=2.0)
df['bb_upper'] = bb_df.iloc[:, 2] # BBU
df['bb_mid'] = bb_df.iloc[:, 1] # BBM
df['bb_lower'] = bb_df.iloc[:, 0] # BBL
# Stochastic returns: STOCHk, STOCHd
stoch_df = ta.stoch(df['high'], df['low'], df['close'], k=14, d=3, smooth_k=3)
df['stoch_k'] = stoch_df.iloc[:, 0]
df['stoch_d'] = stoch_df.iloc[:, 1]
# ATR (uses high, low, close)
df['atr'] = ta.atr(df['high'], df['low'], df['close'], length=14)
```
## Complete Example
```python
from dexorder.api import get_api
import asyncio
import pandas as pd
import pandas_ta as ta
# Get API instance
api = get_api()
@@ -168,9 +198,9 @@ df = asyncio.run(api.data.historical_ohlc(
extra_columns=["volume"]
))
# Add some analysis
df['sma_20'] = df['close'].rolling(window=20).mean()
df['sma_50'] = df['close'].rolling(window=50).mean()
# Add moving averages using pandas-ta
df['sma_20'] = ta.sma(df['close'], length=20)
df['ema_50'] = ta.ema(df['close'], length=50)
# Create chart with volume
fig, ax = api.charting.plot_ohlc(
@@ -182,7 +212,7 @@ fig, ax = api.charting.plot_ohlc(
# Overlay moving averages
ax.plot(df.index, df['sma_20'], label="SMA 20", color="blue", linewidth=1.5)
ax.plot(df.index, df['sma_50'], label="SMA 50", color="red", linewidth=1.5)
ax.plot(df.index, df['ema_50'], label="EMA 50", color="red", linewidth=1.5)
ax.legend()
# Print summary statistics

View File

@@ -51,7 +51,140 @@ The API provides two main components:
- `api.data` - DataAPI for fetching OHLC market data
- `api.charting` - ChartingAPI for creating financial charts
See your knowledge base for complete API documentation and examples.
See your knowledge base for complete API documentation, examples, and the full pandas-ta indicator reference (see `pandas-ta-reference.md`).
## Technical Indicators — pandas-ta
The sandbox environment uses **pandas-ta** as the standard indicator library. Always use it for technical indicator calculations; do not write manual rolling/ewm implementations.
```python
import pandas_ta as ta
```
### Calling Convention
pandas-ta functions accept a Series (or OHLCV columns) plus keyword parameters that match pandas-ta's documented argument names:
```python
# Single-series indicator
rsi = ta.rsi(df['close'], length=14) # returns Series
# OHLCV indicator
atr = ta.atr(df['high'], df['low'], df['close'], length=14)
# Multi-output indicator (returns DataFrame)
macd_df = ta.macd(df['close'], fast=12, slow=26, signal=9)
# columns: MACD_12_26_9, MACDh_12_26_9, MACDs_12_26_9
bbands_df = ta.bbands(df['close'], length=20, std=2.0)
# columns: BBL_20_2.0, BBM_20_2.0, BBU_20_2.0, BBB_20_2.0, BBP_20_2.0
```
### Available Indicators (canonical list)
These match the indicators supported by the TradingView web client. Use the pandas-ta function name shown here (lowercase):
**Overlap / Moving Averages** — plotted on the price pane
| Function | Description |
|----------|-------------|
| `sma` | Simple Moving Average — plain arithmetic mean over `length` periods |
| `ema` | Exponential Moving Average — more weight on recent prices |
| `wma` | Weighted Moving Average — linearly increasing weights |
| `dema` | Double EMA — two layers of EMA to reduce lag |
| `tema` | Triple EMA — three layers of EMA, even less lag than DEMA |
| `trima` | Triangular MA — double-smoothed SMA, very smooth |
| `kama` | Kaufman Adaptive MA — adapts speed to market noise/trending conditions |
| `t3` | T3 Moving Average — Tillson's smooth, low-lag MA using six EMAs |
| `hma` | Hull MA — very low-lag MA using WMAs |
| `alma` | Arnaud Legoux MA — Gaussian-weighted MA with reduced lag and noise |
| `midpoint` | Midpoint of close over `length` periods: (highest + lowest) / 2 |
| `midprice` | Midpoint of high/low over `length` periods |
| `supertrend` | Trend-following band (ATR-based) that flips above/below price |
| `ichimoku` | Ichimoku Cloud — multi-line Japanese trend/support/resistance system |
| `vwap` | Volume-Weighted Average Price — average price weighted by volume, resets on `anchor` |
| `vwma` | Volume-Weighted MA — like SMA but candles weighted by volume |
| `bbands` | Bollinger Bands — SMA ± N standard deviations; returns upper, mid, lower bands |
**Momentum** — typically plotted in a separate pane
| Function | Description |
|----------|-------------|
| `rsi` | Relative Strength Index — 0100 oscillator measuring speed of price changes |
| `macd` | MACD — difference of two EMAs plus signal line and histogram |
| `stoch` | Stochastic Oscillator — %K/%D, measures close vs recent high/low range |
| `stochrsi` | Stochastic RSI — applies stochastic formula to RSI values |
| `cci` | Commodity Channel Index — deviation of price from its statistical mean |
| `willr` | Williams %R — inverse stochastic, 100 to 0 oscillator |
| `mom` | Momentum — raw price change over `length` periods |
| `roc` | Rate of Change — percentage price change over `length` periods |
| `trix` | TRIX — 1-period % change of a triple-smoothed EMA |
| `cmo` | Chande Momentum Oscillator — ratio of up/down momentum, 100 to 100 |
| `adx` | Average Directional Index — strength of trend (0100, direction-agnostic) |
| `aroon` | Aroon — measures how recently the highest/lowest price occurred; returns Up, Down, Oscillator |
| `ao` | Awesome Oscillator — difference of 5- and 34-period simple MAs of midprice |
| `bop` | Balance of Power — measures buying vs selling pressure: (closeopen)/(highlow) |
| `uo` | Ultimate Oscillator — weighted combo of three period (fast/medium/slow) buying pressure ratios |
| `apo` | Absolute Price Oscillator — difference between two EMAs (like MACD without signal line) |
| `mfi` | Money Flow Index — RSI-like oscillator using price × volume |
| `coppock` | Coppock Curve — long-term momentum oscillator based on rate-of-change |
| `dpo` | Detrended Price Oscillator — removes trend to show cycle oscillations |
| `fisher` | Fisher Transform — converts price into a Gaussian normal distribution |
| `rvgi` | Relative Vigor Index — compares closeopen to highlow to measure trend vigor |
| `kst` | Know Sure Thing — momentum oscillator from four ROC periods, smoothed |
**Volatility** — plotted on price pane or separate
| Function | Description |
|----------|-------------|
| `atr` | Average True Range — average of true range (greatest of HL, HprevC, LprevC) |
| `kc` | Keltner Channels — EMA ± N × ATR bands around price |
| `donchian` | Donchian Channels — highest high / lowest low over `length` periods |
**Volume** — plotted in separate pane
| Function | Description |
|----------|-------------|
| `obv` | On Balance Volume — cumulative volume, added on up days, subtracted on down days |
| `ad` | Accumulation/Distribution — running total of the money flow multiplier × volume |
| `adosc` | Chaikin Oscillator — EMA difference of the A/D line |
| `cmf` | Chaikin Money Flow — sum of (money flow volume) / sum of volume over `length` |
| `eom` | Ease of Movement — relates price change to volume; high = price moves easily |
| `efi` | Elder's Force Index — combines price change direction with volume magnitude |
| `kvo` | Klinger Volume Oscillator — EMA difference of volume force |
| `pvt` | Price Volume Trend — cumulative: volume × percentage price change |
**Statistics / Price Transforms**
| Function | Description |
|----------|-------------|
| `stdev` | Standard Deviation of close over `length` periods |
| `linreg` | Linear Regression Curve — least-squares line endpoint value over `length` periods |
| `slope` | Linear Regression Slope — gradient of the regression line |
| `hl2` | Median Price — (high + low) / 2 |
| `hlc3` | Typical Price — (high + low + close) / 3 |
| `ohlc4` | Average Price — (open + high + low + close) / 4 |
**Trend**
| Function | Description |
|----------|-------------|
| `psar` | Parabolic SAR — trailing stop-and-reverse dots that follow price |
| `vortex` | Vortex Indicator — VI+ / VI lines measuring upward vs downward trend movement |
| `chop` | Choppiness Index — 0100, high = choppy/sideways, low = strong trend |
### Default Parameters
Key defaults to keep in mind:
- Most period/length indicators: `length=14` (use `length=` not `timeperiod=`)
- `bbands`: `length=20, std=2.0` (note: single `std`, not separate upper/lower)
- `macd`: `fast=12, slow=26, signal=9`
- `stoch`: `k=14, d=3, smooth_k=3`
- `psar`: `af0=0.02, af=0.02, max_af=0.2`
- `vwap`: `anchor='D'` (requires DatetimeIndex)
- `ichimoku`: `tenkan=9, kijun=26, senkou=52`
For multi-output indicator column extraction patterns and complete charting examples, fetch `pandas-ta-reference.md` from your knowledge base.
## Coding Loop Pattern
@@ -59,11 +192,9 @@ When a user requests analysis:
1. **Understand the request**: What data is needed? What analysis? What visualization?
2. **Check for existing scripts**: Use `category_list` to see if a similar script exists
- If exists and suitable: use `category_read` to review it
- Consider editing existing script vs creating new one
2. **Use the provided name**: The instruction will begin with `Research script name: "<name>"`. Always use that exact name when calling `category_write` or `category_edit`. Check first with `category_read` — if the script already exists, use `category_edit` to update it rather than creating a new one with `category_write`.
3. **Write the script**: Use `category_write` (or `category_edit`)
3. **Write the script**: Use `category_write` (new) or `category_edit` (existing)
- Write clean, well-commented Python code
- Include proper error handling
- Use appropriate ticker symbols, time ranges, and periods
@@ -106,10 +237,7 @@ When a user requests analysis:
- Add `conda_packages: ["package-name"]` to metadata
- Packages are auto-installed during validation
- **Script naming**: Choose descriptive, unique names. Examples:
- "BTC Weekly Analysis"
- "ETH Volume Profile"
- "Market Correlation Heatmap"
- **Script naming**: Always use the name provided in the instruction (`Research script name: "<name>"`). Do not invent a different name.
- **Error handling**: Wrap data fetching in try/except to provide helpful error messages