Локальная аналитика для RL-агентов
Локальная аналитика для RL-агентов
Обзор
Новая архитектура локальной аналитики решает проблему с глобальными снапшотами аналитики, которые не подходят для динамических метрик, требующих пересчёта на каждом шаге симуляции.
Проблемы старой архитектуры
- Глобальные снапшоты — аналитика рассчитывалась один раз для всех агентов
- Неактуальность данных — метрики не обновлялись в процессе симуляции
- Конфликты между тестами — параллельные тесты могли влиять друг на друга
- Низкая производительность — избыточные вычисления и блокировки
Новая архитектура
Архитектура локальной аналитики
graph TB
subgraph "Локальная аналитика"
ALA1[AgentLocalAnalytics 1]
ALA2[AgentLocalAnalytics 2]
ALA3[AgentLocalAnalytics N]
end
subgraph "Пул аналитики"
POOL[AnalyticsPool]
end
subgraph "Данные"
QUOTES1[Quotes Agent 1]
QUOTES2[Quotes Agent 2]
QUOTES3[Quotes Agent N]
end
subgraph "Результаты"
ANALYTICS1[Analytics Agent 1]
ANALYTICS2[Analytics Agent 2]
ANALYTICS3[Analytics Agent N]
end
QUOTES1 --> ALA1
QUOTES2 --> ALA2
QUOTES3 --> ALA3
ALA1 --> ANALYTICS1
ALA2 --> ANALYTICS2
ALA3 --> ANALYTICS3
ALA1 --> POOL
ALA2 --> POOL
ALA3 --> POOL1. AgentLocalAnalytics
Каждый агент имеет свою локальную аналитику:
type AgentLocalAnalytics struct {
agentID string
testID string
quotes []model.Quote
maxWindow int
mu sync.RWMutex
log *slog.Logger
lastHash string
lastAnalytics SymbolAnalytics
}
Особенности:
- Изоляция — каждый агент имеет свою аналитику
- Скользящее окно — поддерживается фиксированный размер буфера котировок
- Кэширование — аналитика пересчитывается только при изменении данных
- Потокобезопасность — использование RWMutex для конкурентного доступа
2. AnalyticsPool
Пул для управления локальной аналитикой агентов:
type AnalyticsPool struct {
agents map[string]*AgentLocalAnalytics
mu sync.RWMutex
log *slog.Logger
}
Функции:
- Создание и управление локальной аналитикой агентов
- Очистка ресурсов по завершении тестов
- Статистика использования
Использование
В симуляторе
func simulateRLAgent(...) {
// Создаём локальную аналитику для агента
agentAnalytics := analytics.NewAgentLocalAnalytics(
agent.GetID(),
testID,
100, // окно 100 котировок
log,
)
for i, quote := range data {
// Добавляем котировку в локальную аналитику
agentAnalytics.AddQuote(quote)
// Получаем актуальную аналитику
analytics := agentAnalytics.GetAnalytics()
agent.SetAnalytics(&analytics)
// Принимаем решение и обновляем модель
action := agent.DecideAction()
agent.Update(action, analytics.Volatility1h, reward, penalty, analytics, exchange, pair)
}
}
В генетическом алгоритме
func createFitnessEvaluator(...) {
return func(agent *rl_agent.RLAgent) (decimalutils.Decimal, error) {
// Создаём локальную аналитику для каждого агента
agentAnalytics := analytics.NewAgentLocalAnalytics(
agent.GetID(),
uniqueTestID,
100,
log,
)
// Симуляция с локальной аналитикой
result, err := simulateRLAgent(...)
return fitness, nil
}
}
Преимущества
- Актуальность — аналитика всегда соответствует текущему состоянию
- Изоляция — каждый агент работает с собственной аналитикой
- Производительность — нет глобальных блокировок, эффективное кэширование
- Масштабируемость — легко добавлять новые агенты и тесты
- Параллелизм — тесты могут выполняться параллельно без конфликтов
Оптимизации
Кэширование по хешу
func (ala *AgentLocalAnalytics) calculateQuotesHash() string {
// Используем последние 20 котировок для хеша
hash := sha256.New()
for i := start; i < len(ala.quotes); i++ {
quote := ala.quotes[i]
hash.Write([]byte(quote.Price.String()))
hash.Write([]byte(quote.Timestamp.Format(time.RFC3339)))
}
return hex.EncodeToString(hash.Sum(nil))
}
Скользящее окно
func (ala *AgentLocalAnalytics) AddQuote(quote model.Quote) {
ala.quotes = append(ala.quotes, quote)
if len(ala.quotes) > ala.maxWindow {
ala.quotes = ala.quotes[1:] // удаляем старые котировки
}
}
Миграция
Для перехода на новую архитектуру:
- Заменить вызовы
anal.GetAnalyticsSnapshot()на созданиеAgentLocalAnalytics - Добавить
AddQuote()в цикл симуляции - Использовать
GetAnalytics()вместо глобального снапшота - Удалить вызовы
ProcessQuotes()иResetForTest()
Мониторинг
// Статистика пула
stats := analyticsPool.GetStats()
log.Info("Статистика аналитики",
slog.Int("total_agents", stats["total_agents"]),
slog.Int("ready_agents", stats["ready_agents"]),
slog.Int("total_quotes", stats["total_quotes"]),
)