From 796ef0eb2d6ae64261a673324d2bc9b998e25cf7 Mon Sep 17 00:00:00 2001 From: mx57 <38256814+mx57@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:18:23 +0000 Subject: [PATCH] feat(trading): audit strategies and optimize rules engine - Performed audit of C# trading strategies in `IntelliTrader.Trading` and `IntelliTrader.Rules`. - Optimized `RulesService.CheckConditions` by caching price and spread, reducing redundant service calls. - Introduced `TrailingSafetyOptions` model for future spread-aware trailing logic. - Added `test_audit.py` to verify C# optimizations and models via static analysis. - Updated `agent_tasks.json` with 8 new evolution and AI trend-based tasks. - Validated task manifest and verified C# project builds. --- IntelliTrader.Rules/Services/RulesService.cs | 13 +- IntelliTrader.Rules/test_audit.py | 51 +++++++ .../Models/TrailingSafetyOptions.cs | 27 ++++ magda_agent_system/agent_tasks.json | 134 +++++++++++++++++- magda_agent_system/backlog.md | 3 + .../data_level0.bin | Bin 0 -> 167600 bytes .../header.bin | Bin 0 -> 100 bytes .../length.bin | Bin 0 -> 400 bytes .../link_lists.bin | 0 magda_agent_system/memory_db/chroma.sqlite3 | Bin 0 -> 188416 bytes magda_agent_system/metrics_db.sqlite3 | Bin 0 -> 12288 bytes 11 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 IntelliTrader.Rules/test_audit.py create mode 100644 IntelliTrader.Trading/Models/TrailingSafetyOptions.cs create mode 100644 magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/data_level0.bin create mode 100644 magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/header.bin create mode 100644 magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/length.bin create mode 100644 magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/link_lists.bin create mode 100644 magda_agent_system/memory_db/chroma.sqlite3 create mode 100644 magda_agent_system/metrics_db.sqlite3 diff --git a/IntelliTrader.Rules/Services/RulesService.cs b/IntelliTrader.Rules/Services/RulesService.cs index 5cd839e..4b7fa56 100644 --- a/IntelliTrader.Rules/Services/RulesService.cs +++ b/IntelliTrader.Rules/Services/RulesService.cs @@ -37,8 +37,11 @@ public IModuleRules GetRules(string module) public bool CheckConditions(IEnumerable conditions, Dictionary signals, double? globalRating, string pair, ITradingPair tradingPair) { - if (conditions != null) + if (conditions != null && conditions.Any()) { + decimal currentPrice = tradingService.GetPrice(pair); + decimal currentSpread = tradingService.Exchange.GetPriceSpread(pair); + foreach (var condition in conditions) { ISignal signal = null; @@ -47,10 +50,10 @@ public bool CheckConditions(IEnumerable conditions, Dictionary condition.MaxPrice) || - condition.MinSpread != null && (tradingService.Exchange.GetPriceSpread(pair) < condition.MinSpread) || - condition.MaxSpread != null && (tradingService.Exchange.GetPriceSpread(pair) > condition.MaxSpread) || + if (condition.MinPrice != null && (currentPrice < condition.MinPrice) || + condition.MaxPrice != null && (currentPrice > condition.MaxPrice) || + condition.MinSpread != null && (currentSpread < condition.MinSpread) || + condition.MaxSpread != null && (currentSpread > condition.MaxSpread) || condition.MinArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage < condition.MinArbitrage || condition.MaxArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, diff --git a/IntelliTrader.Rules/test_audit.py b/IntelliTrader.Rules/test_audit.py new file mode 100644 index 0000000..147f6d5 --- /dev/null +++ b/IntelliTrader.Rules/test_audit.py @@ -0,0 +1,51 @@ +import os + +def test_rules_service_optimization() -> None: + """ + Verify that RulesService.cs has been optimized to cache price and spread + before the condition loop. + """ + filepath = "IntelliTrader.Rules/Services/RulesService.cs" + assert os.path.exists(filepath), f"File {filepath} not found" + + with open(filepath, 'r') as f: + content = f.read() + + # Check if we are caching price and spread + assert "decimal currentPrice = tradingService.GetPrice(pair);" in content + assert "decimal currentSpread = tradingService.Exchange.GetPriceSpread(pair);" in content + + # Check if the loop uses the cached variables instead of calling the service + # The original pattern was tradingService.GetPrice(pair) < condition.MinPrice + # The optimized pattern should be currentPrice < condition.MinPrice + + assert "currentPrice < condition.MinPrice" in content + assert "currentPrice > condition.MaxPrice" in content + assert "currentSpread < condition.MinSpread" in content + assert "currentSpread > condition.MaxSpread" in content + + # Ensure no redundant calls are left in the if condition + assert "tradingService.GetPrice(pair) < condition.MinPrice" not in content + +def test_trailing_safety_options_exists() -> None: + """ + Verify that the new TrailingSafetyOptions model exists and contains expected properties. + """ + filepath = "IntelliTrader.Trading/Models/TrailingSafetyOptions.cs" + assert os.path.exists(filepath), f"File {filepath} not found" + + with open(filepath, 'r') as f: + content = f.read() + + assert "public decimal MaxTrailingSpread { get; set; }" in content + assert "public bool PauseOnHighSpread { get; set; }" in content + +if __name__ == "__main__": + # Manual run for debugging + try: + test_rules_service_optimization() + test_trailing_safety_options_exists() + print("All audit tests passed!") + except AssertionError as e: + print(f"Audit test failed: {e}") + exit(1) diff --git a/IntelliTrader.Trading/Models/TrailingSafetyOptions.cs b/IntelliTrader.Trading/Models/TrailingSafetyOptions.cs new file mode 100644 index 0000000..a4f96e8 --- /dev/null +++ b/IntelliTrader.Trading/Models/TrailingSafetyOptions.cs @@ -0,0 +1,27 @@ +using System; + +namespace IntelliTrader.Trading +{ + /// + /// Options for spread-aware trailing safety checks. + /// + public class TrailingSafetyOptions + { + /// + /// Gets or sets the maximum allowed spread percentage to continue trailing. + /// If the spread exceeds this value, trailing may be paused or stopped. + /// + public decimal MaxTrailingSpread { get; set; } + + /// + /// Gets or sets a value indicating whether to pause trailing when spread is high. + /// + public bool PauseOnHighSpread { get; set; } + + /// + /// Gets or sets the minimum price change percentage required to update trailing stop + /// even if spread is high. + /// + public decimal MinPriceChangeWithHighSpread { get; set; } + } +} diff --git a/magda_agent_system/agent_tasks.json b/magda_agent_system/agent_tasks.json index 7c241d7..940e4e3 100644 --- a/magda_agent_system/agent_tasks.json +++ b/magda_agent_system/agent_tasks.json @@ -58,7 +58,7 @@ }, { "id": "trading-strategy-audit", - "status": "todo", + "status": "done", "area": "trading", "risk": "medium", "title": "Audit existing trading strategies for potential improvements", @@ -87,6 +87,138 @@ "acceptance": [ "magda_agent_system/docs/web_enhancements.md exists with a list of proposed features" ] + }, + { + "id": "intellitrader-stats-optimization", + "status": "todo", + "area": "web", + "risk": "low", + "title": "Optimize historical balance calculation in Web Dashboard", + "description": "Refactor `HomeController.Stats` in `IntelliTrader.Web/` to use an O(N) running total algorithm instead of the current O(N^2) approach for historical balance calculation.", + "allowed_paths": [ + "IntelliTrader.Web/Controllers/HomeController.cs", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "The calculation logic in HomeController.Stats is updated to linear complexity", + "Performance improvement verified for large trade logs" + ] + }, + { + "id": "binance-exchange-implementation", + "status": "todo", + "area": "exchange", + "risk": "medium", + "title": "Complete Binance order placement implementation", + "description": "Implement `PlaceOrder`, `CancelOrder`, and `GetOrderDetails` in `BinanceExchangeService.cs`. Address TODOs related to mapping internal models to ExchangeSharp models.", + "allowed_paths": [ + "IntelliTrader.Exchange.Binance/**", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "BinanceExchangeService successfully places real or test orders via ExchangeSharp", + "Model mapping between IOrder and ExchangeOrderRequest is complete" + ] + }, + { + "id": "spread-aware-trailing-logic", + "status": "todo", + "area": "trading", + "risk": "medium", + "title": "Implement spread-aware trailing safety checks", + "description": "Integrate `TrailingSafetyOptions` into `TradingTimedTask`. Modify trailing logic to pause or stop trailing when market spread exceeds a configurable threshold, preventing losses due to slippage.", + "allowed_paths": [ + "IntelliTrader.Trading/TimedTasks/TradingTimedTask.cs", + "IntelliTrader.Trading/Models/TrailingSafetyOptions.cs", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "Trailing logic respects MaxTrailingSpread setting", + "Tests verify that trailing pauses during high spread events" + ] + }, + { + "id": "rules-service-refactoring", + "status": "todo", + "area": "rules", + "risk": "low", + "title": "Refactor RulesService for better extensibility", + "description": "Decompose the monolithic `CheckConditions` method in `RulesService.cs` into smaller, rule-specific evaluator classes (e.g., PriceEvaluator, SignalEvaluator).", + "allowed_paths": [ + "IntelliTrader.Rules/Services/RulesService.cs", + "IntelliTrader.Rules/Models/**", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "RulesService uses a registry of condition evaluators", + "Unit tests confirm that existing rule conditions still work correctly" + ] + }, + { + "id": "trading-task-decomposition", + "status": "todo", + "area": "trading", + "risk": "low", + "title": "Decompose TradingTimedTask into specialized processors", + "description": "Extract buy, sell, and DCA logic from the large `TradingTimedTask.cs` into separate processors to improve maintainability and testability.", + "allowed_paths": [ + "IntelliTrader.Trading/TimedTasks/**", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "TradingTimedTask delegates logic to specialized components", + "The main loop remains stable and passes existing integration tests" + ] + }, + { + "id": "magda-mcp-skill-exporter", + "status": "todo", + "area": "integration", + "risk": "medium", + "title": "Export Magda skills as MCP-compliant tools", + "description": "Inspired by June 2026 AI trends: Implement a module to export existing Magda skills (e.g., search, execute_code) as Model Context Protocol (MCP) tools, enabling interoperability with other agentic systems.", + "allowed_paths": [ + "magda_agent_system/magda_agent/skills/exporter.py", + "magda_agent_system/magda_agent/integration/mcp_exporter.py", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "Skills can be successfully exported in MCP JSON-RPC format", + "Validation script confirms compliance with MCP tool specification" + ] + }, + { + "id": "trading-rl-feedback-loop", + "status": "todo", + "area": "learning", + "risk": "medium", + "title": "Implement Online RL for trading parameter optimization", + "description": "Develop an OpenClaw-RL inspired feedback loop that adjusts trading parameters like `BuyTrailing` and `SellMargin` based on the success of completed trades stored in episodic memory.", + "allowed_paths": [ + "magda_agent_system/magda_agent/learning/online_rl_trading.py", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "Agent can propose parameter adjustments after trade analysis", + "Adjustment history is tracked and evaluated for long-term improvement" + ] + }, + { + "id": "multi-agent-backtesting-orchestrator", + "status": "todo", + "area": "agents", + "risk": "medium", + "title": "Multi-agent strategy backtesting with isolation", + "description": "Utilize Claude SDK Agent Teams and git worktree isolation to run parallel backtesting of multiple trading strategies. Each sub-agent operates in its own worktree to avoid configuration conflicts.", + "allowed_paths": [ + "magda_agent_system/magda_agent/agents/team_isolation.py", + "magda_agent_system/magda_agent/scheduler/backtest_orchestrator.py", + "magda_agent_system/agent_tasks.json" + ], + "acceptance": [ + "Parallel backtesting runs in isolated worktrees", + "Results are aggregated and compared by the lead evaluator agent" + ] } ] } diff --git a/magda_agent_system/backlog.md b/magda_agent_system/backlog.md index 32fe0fb..ff5c063 100644 --- a/magda_agent_system/backlog.md +++ b/magda_agent_system/backlog.md @@ -95,6 +95,7 @@ --- ## ✅ Выполнено +- [x] trading-strategy-audit: Audit existing trading strategies for potential improvements (2026-06-12) - [x] a2a-async-auth-tracing: A2A Enterprise-Ready Async Auth and Tracing - [x] a2a-json-rpc-server: A2A JSON-RPC Server Interface - [x] acs-runtime-safety-controls-v2: ACS runtime safety controls @@ -144,6 +145,8 @@ --- ## 🔍 Обнаружено (добавляется агентом автоматически) +* [ ] IMPROVEMENT: In `RulesService.cs`, redundant calls to `tradingService.GetPrice` and `GetPriceSpread` inside the condition loop can be optimized by caching values (Partially addressed). +* [ ] IMPROVEMENT: Trailing logic in `TradingTimedTask.cs` lacks spread consideration, which can lead to slippage during high volatility. * [x] MODULE: **Conflict Detection Memory** — `magda_agent/memory/semantic.py` и `magda_agent/subconsciousness/reflection.py`. Subconsciousness detects when new information contradicts existing semantic memory and flags conflicts for resolution. * [x] IMPROVEMENT: Добавить механизм выполнения шагов плана и сбора результатов внутри `Consciousness.process_input`. Сейчас планер интегрирован только для генерации и учета в контексте LLM, но сами функции Skills не вызываются автоматически. (2026-06-03) * [x] BUG/IMPROVEMENT: В `Evaluator.evaluate_response` можно добавить логику retry на случай, если LLM возвращает невалидный JSON, вместо того, чтобы сразу падать с ошибкой парсинга. (2026-06-04) diff --git a/magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/data_level0.bin b/magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/data_level0.bin new file mode 100644 index 0000000000000000000000000000000000000000..5efb1b9f398d64303b911a57992cc0b9721a21dc GIT binary patch literal 167600 zcmeIuF#!Mo0K%a4Pi+kkh(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj ZFkrxd0RsjM7%*VKfB^#r3>Yx*JTQ*V00961 literal 0 HcmV?d00001 diff --git a/magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/header.bin b/magda_agent_system/memory_db/4d809711-8e64-4d56-a85b-f99eb6ee2199/header.bin new file mode 100644 index 0000000000000000000000000000000000000000..bb54792626c8e16cb8f8a2b989bd1998268ad65c GIT binary patch literal 100 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 literal 0 HcmV?d00001