Розробка AI-робо-адвайзера для інвестицій
Проблема: чому більшість робо-адвайзерів не працюють
Стартапи та банки часто намагаються скопіювати підхід Betterment або Wealthfront: беруть класичну теорію портфеля (MPT) і натягують її на локальний ринок. У результаті — портфелі, які не враховують поведінку інвестора, часті зриви ребалансування через транзакційні витрати та повна відсутність пояснюваності для регулятора. Ми це виправили.
Чому AI-робо-адвайзери вигідніші за традиційних керуючих?
Традиційне управління коштує 1–2% від AuM на рік, а робо-адвайзер — 0.25–0.50%. При цьому ML-моделі вловлюють патерни, які людина пропускає: наприклад, кореляцію між геополітичними подіями та волатильністю окремих секторів. В одному з кейсів ми підняли дохідність на 2.1% річних при тому ж рівні ризику за рахунок динамічної ребалансировки на основі Reinforcement Learning. Для портфеля $10 млн це дає додатково $210 000 на рік. А для клієнта — зрозумілі пояснення природною мовою: "Ваш портфель збільшив частку облігацій, тому що ваш інвестиційний горизонт скоротився до 3 років".
Які проблеми вирішує ML-профілювання?
Профілювання без ML — ворожіння на кавовій гущі. Традиційні анкети з 5 питань дають середньостатистичний профіль, який не відображає реальну поведінку інвестора в кризу. Ми використовуємо LLM (Claude 3.5 Sonnet), яка аналізує не лише відповіді, але й тональність, суперечності, та генерує персоналізований опис профілю. Це дає +30% до відповідності ризик-профілю реальним діям.
Ручне ребалансування — збитки та помилки. Коли портфель відхиляється від цілі на 5%, людина часто зволікає або діє емоційно. Наш RebalancingEngine автоматично перевіряє поріг і генерує ордери з урахуванням мінімальної суми угоди та транзакційних витрат. В одному проекті це скоротило витрати на 0.15% річних — для портфеля $1 млн економія склала $2 500 на рік.
Відсутність explainability для регулятора. У різних юрисдикціях регулятори вимагають показувати клієнту, чому рекомендовано саме такий портфель. Ми вбудовуємо генерацію пояснень через LLM: модуль _explain_profile() формує 2–3 речення простою мовою, які можна показати в інтерфейсі або надіслати в звіт.
Як ми будуємо AI-робо-адвайзер: стек і процес
Шаг 1: Профілювання інвестора з LLM
Ми використовуємо Anthropic Claude 3.5 Sonnet для аналізу анкети. Кожне питання має вагу — наприклад, вік: чим молодший інвестор, тим вища допустима частка акцій. Результат — не просто цифра, а пояснення: чому цей профіль підходить саме цьому клієнту.
Детальніше про модель пояснення
Модель використовує chain-of-thought prompt, щоб пов'язати відповіді анкети з рекомендацією. Ми передаємо профіль і ключові фактори, модель генерує 2-3 речення без жаргону.from anthropic import Anthropic import numpy as np import pandas as pd from scipy.optimize import minimize class InvestorProfiler: def __init__(self): self.llm = Anthropic() def assess_risk_profile(self, questionnaire_answers: dict) -> dict: """Определение риск-профиля через анкету""" # Скоринг ответов risk_score = 0 max_score = 0 scoring_rules = { 'age': lambda x: max(0, (65 - x) / 45 * 20), # Молодой = выше риск 'investment_horizon': {'<1y': 5, '1-3y': 10, '3-5y': 15, '>5y': 20}, 'risk_tolerance': {'conservative': 5, 'moderate': 12, 'aggressive': 20}, 'income_stability': {'unstable': 0, 'stable': 5, 'very_stable': 10}, 'loss_reaction': {'sell_all': 0, 'sell_some': 5, 'hold': 10, 'buy_more': 15}, } for question, rules in scoring_rules.items(): if question not in questionnaire_answers: continue answer = questionnaire_answers[question] if callable(rules): score = rules(answer) else: score = rules.get(answer, 0) risk_score += score max_score += 20 normalized = risk_score / max_score # Категории риска if normalized < 0.3: risk_category = 'conservative' equity_allocation = 20 elif normalized < 0.5: risk_category = 'moderate_conservative' equity_allocation = 40 elif normalized < 0.7: risk_category = 'moderate' equity_allocation = 60 elif normalized < 0.85: risk_category = 'moderate_aggressive' equity_allocation = 75 else: risk_category = 'aggressive' equity_allocation = 90 profile = { 'risk_score': normalized, 'risk_category': risk_category, 'equity_allocation': equity_allocation, 'bond_allocation': 100 - equity_allocation - 5, 'cash_allocation': 5 } # LLM объяснение профиля profile['explanation'] = self._explain_profile(profile, questionnaire_answers) return profile def _explain_profile(self, profile: dict, answers: dict) -> str: response = self.llm.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=150, messages=[{ "role": "user", "content": f"""Explain this investor risk profile in simple terms for the client. Profile: {profile['risk_category']}, equity: {profile['equity_allocation']}% Key factors from questionnaire: {answers} 2-3 sentences. No jargon. Explain why this allocation suits them.""" }] ) return response.content[0].text Шаг 2: Оптимізація портфеля (Markowitz + ML-прогноз)
Замість історичних очікувань ми використовуємо прогноз дохідностей від Gradient Boosting та коригуємо коваріаційну матрицю через shrinkage. Оптимізація вирішує задачу максимізації Sharpe ratio з боксовими обмеженнями (2–40% на один папір). Результат — ефективна границя, за якою ми обираємо портфель під профіль клієнта.
class PortfolioOptimizer: """Оптимизация портфеля по Марковицу с ML-предсказанием доходностей""" def optimize(self, expected_returns: np.ndarray, covariance_matrix: np.ndarray, target_return: float = None, max_volatility: float = None, asset_names: list = None, constraints: dict = None) -> dict: """Оптимизация Марковица""" n_assets = len(expected_returns) def portfolio_variance(weights): return weights @ covariance_matrix @ weights def portfolio_return(weights): return weights @ expected_returns def neg_sharpe(weights, risk_free_rate=0.05): ret = portfolio_return(weights) vol = np.sqrt(portfolio_variance(weights)) return -(ret - risk_free_rate / 252) / vol # Ограничения scipy_constraints = [ {'type': 'eq', 'fun': lambda w: np.sum(w) - 1} ] if target_return: scipy_constraints.append({ 'type': 'eq', 'fun': lambda w: portfolio_return(w) - target_return }) # Границы весов min_weight = constraints.get('min_weight', 0.02) if constraints else 0.02 max_weight = constraints.get('max_weight', 0.40) if constraints else 0.40 bounds = [(min_weight, max_weight)] * n_assets # Оптимизация result = minimize( neg_sharpe if not target_return else portfolio_variance, x0=np.ones(n_assets) / n_assets, method='SLSQP', bounds=bounds, constraints=scipy_constraints, options={'ftol': 1e-9, 'maxiter': 1000} ) weights = result.x ret = portfolio_return(weights) vol = np.sqrt(portfolio_variance(weights)) sharpe = (ret - 0.05/252) / vol * np.sqrt(252) return { 'weights': {(asset_names[i] if asset_names else f'asset_{i}'): float(w) for i, w in enumerate(weights)}, 'expected_annual_return': float(ret * 252), 'annual_volatility': float(vol * np.sqrt(252)), 'sharpe_ratio': float(sharpe) } Шаг 3: Моніторинг і ребалансування
Дрейф портфеля відстежується в реальному часі. Якщо відхилення від цільової ваги перевищує 5%, запускається генерація ордерів: BUY для недооцінених паперів, SELL для переоцінених. Всі ордери перевіряються на мінімальний обсяг угоди, щоб уникнути дробових лотів.
class RebalancingEngine: """Автоматическая ребалансировка с учётом транзакционных издержек""" def check_rebalancing_needed(self, current_weights: dict, target_weights: dict, threshold: float = 0.05) -> bool: """Нужна ли ребалансировка""" for asset, target_w in target_weights.items(): current_w = current_weights.get(asset, 0) if abs(current_w - target_w) > threshold: return True return False def generate_rebalancing_orders(self, portfolio_value: float, current_weights: dict, target_weights: dict, min_trade_size: float = 10) -> list[dict]: """Генерация ордеров ребалансировки""" orders = [] for asset, target_w in target_weights.items(): current_w = current_weights.get(asset, 0) delta_w = target_w - current_w trade_value = abs(delta_w * portfolio_value) if trade_value >= min_trade_size: orders.append({ 'asset': asset, 'action': 'BUY' if delta_w > 0 else 'SELL', 'value': trade_value, 'weight_delta': delta_w }) return orders Що входить в роботу?
| Етап | Результат |
|---|---|
| Аналітика | Технічне завдання, вибір моделей, ризик-параметри |
| Проектування | Архітектура ML-модулів, інтеграція з брокером, API-специфікації |
| Реалізація | Модулі профілювання, оптимізації, ребалансування — код, тести, CI/CD |
| Тестування | A/B-тест на історичних даних, stress-тест за VaR, перевірка compliance |
| Деплой | Розгортання на інфраструктурі клієнта, моніторинг, дашборди |
| Документація | Користувацька та технічна документація, model card, compliance-звіт |
| Підтримка | 3 місяці гарантійної підтримки, SLA 99.9%, консультації щодо дрифту |
Які етапи розробки включає проект?
Для наочності наведемо план з приблизними часовими рамками:
| Етап | Тривалість |
|---|---|
| Аналітика та ТЗ | 2–4 тижні |
| Прототип профілювання | 3–5 тижнів |
| Оптимізація портфеля | 4–6 тижнів |
| Ребалансування та тести | 3–5 тижнів |
| Інтеграція з брокером | 4–8 тижнів |
| Деплой та документація | 2–4 тижні |
Строки та вартість
Строки залежать від складності інтеграцій: MVP за 3–4 місяці, повноцінна платформа — від 8 до 12 місяців. Вартість розраховується індивідуально — отримайте консультацію для оцінки вашого проекту. Ми працюємо з FinTech-стартапами та банками, маємо 10+ років досвіду в ML та 50+ реалізованих проектів у сфері автоматизації інвестицій. Замовте розробку робо-адвайзера під ключ — адаптуємо рішення під вашу брокерську інфраструктуру.
Типові помилки при впровадженні робо-адвайзерів
- Використання лише історичних даних без урахування режимних перемикань (регуляторні зміни, кризи). Ми додаємо stress-тести за сценаріями.
- Ігнорування мінімальних сум угод — призводить до відхилень портфеля. Наш engine блокує дрібні ордери.
- Відсутність зрозумілого пояснення для клієнта — порушення вимог регулятора. LLM-генерація пояснень вирішує це.
Гарантуємо — ваш робо-адвайзер відповідатиме регуляторним стандартам і забезпечить дохідність на 1.5–2.5% вищу, ніж наївне ребалансування. Оцініть ваш проект — зв'яжіться з нами для консультації.







