custom indicators fixed
This commit is contained in:
@@ -15,6 +15,7 @@ import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@@ -35,7 +36,7 @@ from dexorder.conda_manager import sync_packages, install_packages
|
||||
from dexorder.events import EventType, UserEvent, DeliverySpec
|
||||
from dexorder.impl.charting_api_impl import ChartingAPIImpl
|
||||
from dexorder.impl.data_api_impl import DataAPIImpl
|
||||
from dexorder.tools.python_tools import get_category_manager
|
||||
from dexorder.tools.python_tools import get_category_manager, sanitize_name
|
||||
from dexorder.tools.workspace_tools import get_workspace_store
|
||||
from dexorder.tools.evaluate_indicator import evaluate_indicator
|
||||
from dexorder.tools.backtest_strategy import backtest_strategy
|
||||
@@ -57,6 +58,75 @@ def get_data_dir() -> Path:
|
||||
return DATA_DIR
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Indicator Types Helpers
|
||||
# =============================================================================
|
||||
|
||||
def _build_indicator_type_entry(meta: dict) -> dict:
|
||||
"""Build an indicator_types workspace entry from indicator metadata dict."""
|
||||
name = meta.get('name', '')
|
||||
pandas_ta_name = f"custom_{sanitize_name(name).lower()}"
|
||||
now = int(time.time())
|
||||
return {
|
||||
'pandas_ta_name': pandas_ta_name,
|
||||
'display_name': name,
|
||||
'description': meta.get('description', ''),
|
||||
'metadata': {
|
||||
'display_name': name,
|
||||
'parameters': meta.get('parameters') or {},
|
||||
'input_series': meta.get('input_series') or ['close'],
|
||||
'output_columns': meta.get('output_columns') or [{'name': 'value'}],
|
||||
'pane': meta.get('pane', 'separate'),
|
||||
'filled_areas': meta.get('filled_areas') or [],
|
||||
'bands': meta.get('bands') or [],
|
||||
},
|
||||
'created_at': now,
|
||||
'modified_at': now,
|
||||
}
|
||||
|
||||
|
||||
def _upsert_indicator_type(workspace_store, category_manager, name: str) -> None:
|
||||
"""Read indicator metadata from disk and upsert into indicator_types workspace store."""
|
||||
read_result = category_manager.read('indicator', name)
|
||||
if not read_result.get('exists') or not read_result.get('metadata'):
|
||||
return
|
||||
meta = read_result['metadata']
|
||||
entry = _build_indicator_type_entry(meta)
|
||||
pandas_ta_name = entry['pandas_ta_name']
|
||||
|
||||
# Preserve original created_at if already present
|
||||
existing = workspace_store.read('indicator_types')
|
||||
existing_types = (existing.get('data') or {}).get('types') or {}
|
||||
if pandas_ta_name in existing_types:
|
||||
entry['created_at'] = existing_types[pandas_ta_name].get('created_at', entry['created_at'])
|
||||
|
||||
workspace_store.patch('indicator_types', [
|
||||
{'op': 'add', 'path': f'/types/{pandas_ta_name}', 'value': entry}
|
||||
])
|
||||
logging.info(f"Upserted indicator_types/{pandas_ta_name} for '{name}'")
|
||||
|
||||
|
||||
def _populate_indicator_types_from_disk(workspace_store, category_manager) -> None:
|
||||
"""Scan existing indicators and add any missing entries to indicator_types store."""
|
||||
existing = workspace_store.read('indicator_types')
|
||||
existing_types = (existing.get('data') or {}).get('types') or {}
|
||||
|
||||
list_result = category_manager.list_items('indicator')
|
||||
items = list_result.get('items', [])
|
||||
added = 0
|
||||
for item in items:
|
||||
item_name = item.get('name', '')
|
||||
if not item_name:
|
||||
continue
|
||||
pandas_ta_name = f"custom_{sanitize_name(item_name).lower()}"
|
||||
if pandas_ta_name not in existing_types:
|
||||
_upsert_indicator_type(workspace_store, category_manager, item_name)
|
||||
added += 1
|
||||
|
||||
if added > 0:
|
||||
logging.info(f"Populated {added} indicator type(s) from disk into indicator_types store")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Configuration
|
||||
# =============================================================================
|
||||
@@ -156,6 +226,9 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
|
||||
category_manager = get_category_manager(config.data_dir)
|
||||
logging.info(f"Category manager initialized at {config.data_dir}")
|
||||
|
||||
# Populate indicator_types store from existing indicators on disk (migration/startup sync)
|
||||
_populate_indicator_types_from_disk(workspace_store, category_manager)
|
||||
|
||||
@server.list_resources()
|
||||
async def list_resources():
|
||||
"""List available resources"""
|
||||
@@ -674,6 +747,8 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
|
||||
logging.info(f"python_write '{arguments.get('name')}': returning {len(content)} items, {image_count} images")
|
||||
else:
|
||||
logging.info(f"python_write '{arguments.get('name')}': no execution result (category={arguments.get('category')})")
|
||||
if result.get("success") and arguments.get("category") == "indicator":
|
||||
_upsert_indicator_type(workspace_store, category_manager, arguments.get("name", ""))
|
||||
return content
|
||||
elif name == "python_edit":
|
||||
result = category_manager.edit(
|
||||
@@ -698,6 +773,8 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
|
||||
logging.info(f"python_edit '{arguments.get('name')}': returning {len(content)} items, {image_count} images")
|
||||
else:
|
||||
logging.info(f"python_edit '{arguments.get('name')}': no execution result")
|
||||
if result.get("success") and arguments.get("category") == "indicator":
|
||||
_upsert_indicator_type(workspace_store, category_manager, arguments.get("name", ""))
|
||||
return content
|
||||
elif name == "python_read":
|
||||
return category_manager.read(
|
||||
|
||||
Reference in New Issue
Block a user