Stage 07.3 - auto trading analysis cycle skeleton

This commit is contained in:
2026-04-28 13:20:59 +03:00
parent 83ab842f6e
commit d639137855
7 changed files with 178 additions and 10 deletions

View File

@@ -15,6 +15,7 @@ from src.trading.auto.service import AutoTradeService
router = Router(name="auto")
# красивое отображение стратегии
def _strategy_label(strategy: str | None) -> str:
mapping = {
"TREND": "📈 Trend Following",
@@ -22,8 +23,9 @@ def _strategy_label(strategy: str | None) -> str:
"SCALP": "⚡ Scalping",
}
return mapping.get(strategy or "", "")
# красивое отображение статуса
def _status_label(status: str) -> str:
mapping = {
"OFF": "⚪ Выключена",
@@ -33,6 +35,17 @@ def _status_label(status: str) -> str:
return mapping.get(status, status)
# красивое отображение сигнала
def _signal_label(signal: str | None) -> str:
mapping = {
"BUY": "🟢 BUY",
"SELL": "🔴 SELL",
"HOLD": "🟡 HOLD",
}
return mapping.get(signal or "", "")
# клавиатура автоторговли
def _auto_keyboard() -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
@@ -48,8 +61,14 @@ def _auto_keyboard() -> InlineKeyboardMarkup:
return builder.as_markup()
# собрать текст экрана
def _build_auto_text() -> str:
state = AutoTradeService().get_state()
service = AutoTradeService()
# выполнить один цикл анализа
service.run_cycle()
state = service.get_state()
strategy = _strategy_label(state.strategy)
risk = f"{state.risk_percent:.1f}%" if state.risk_percent is not None else ""
@@ -61,10 +80,13 @@ def _build_auto_text() -> str:
f"Стратегия: {strategy}\n"
f"Инструмент: {state.symbol}\n"
f"Риск: {risk}\n"
f"PnL: {state.pnl_usd:.2f} USD"
f"PnL: {state.pnl_usd:.2f} USD\n"
f"Последний анализ: {state.last_check_at or ''}\n"
f"Сигнал: {_signal_label(state.last_signal)}"
)
# отрисовать экран
async def _render_auto_screen(
target_message: Message,
*,
@@ -83,12 +105,14 @@ async def _render_auto_screen(
await target_message.answer(text, reply_markup=_auto_keyboard())
# открыть экран из меню
@router.message(F.text.in_({"🤖 Автоторговля", "🤖 Авто"}))
async def open_auto(message: Message, state: FSMContext) -> None:
await state.clear()
await _render_auto_screen(message, edit_mode=False)
# открыть экран callback
@router.callback_query(F.data == "auto:home")
async def open_auto_from_callback(callback: CallbackQuery, state: FSMContext) -> None:
await state.clear()
@@ -101,6 +125,7 @@ async def open_auto_from_callback(callback: CallbackQuery, state: FSMContext) ->
await callback.answer()
# включить активную торговлю
@router.callback_query(F.data == "auto:start")
async def auto_start(callback: CallbackQuery) -> None:
service = AutoTradeService()
@@ -112,6 +137,7 @@ async def auto_start(callback: CallbackQuery) -> None:
await callback.answer(message)
# включить наблюдение
@router.callback_query(F.data == "auto:observe")
async def auto_observe(callback: CallbackQuery) -> None:
service = AutoTradeService()
@@ -123,6 +149,7 @@ async def auto_observe(callback: CallbackQuery) -> None:
await callback.answer(message)
# выключить автоторговлю
@router.callback_query(F.data == "auto:stop")
async def auto_stop(callback: CallbackQuery) -> None:
service = AutoTradeService()

View File

@@ -2,6 +2,9 @@
from __future__ import annotations
import random
from datetime import datetime
from src.core.config import load_settings
from src.trading.auto.state import AutoTradeState
@@ -9,12 +12,13 @@ from src.trading.auto.state import AutoTradeState
class AutoTradeService:
_state = AutoTradeState()
# получить текущее состояние автоторговли
def get_state(self) -> AutoTradeState:
if not self._state.symbol:
self._state.symbol = load_settings().default_symbol
return self._state
# запустить активную торговлю
def start(self) -> tuple[AutoTradeState, str]:
state = self.get_state()
@@ -28,6 +32,7 @@ class AutoTradeService:
state.status = "RUNNING"
return state, "Автоторговля запущена."
# включить режим наблюдения
def observe(self) -> tuple[AutoTradeState, str]:
state = self.get_state()
previous_status = state.status
@@ -42,6 +47,7 @@ class AutoTradeService:
return state, "Автоторговля переведена в режим наблюдения."
# полностью выключить автоторговлю
def stop(self) -> tuple[AutoTradeState, str]:
state = self.get_state()
@@ -51,17 +57,32 @@ class AutoTradeService:
state.status = "OFF"
return state, "Автоторговля выключена."
# установить инструмент
def set_symbol(self, symbol: str) -> AutoTradeState:
state = self.get_state()
state.symbol = symbol
return state
# установить стратегию
def set_strategy(self, strategy: str) -> AutoTradeState:
state = self.get_state()
state.strategy = strategy
return state
# установить риск
def set_risk_percent(self, risk_percent: float) -> AutoTradeState:
state = self.get_state()
state.risk_percent = risk_percent
return state
# выполнить один цикл анализа рынка
def run_cycle(self) -> AutoTradeState:
state = self.get_state()
if state.status == "OFF":
return state
state.last_check_at = datetime.now().strftime("%H:%M:%S")
state.last_signal = random.choice(["BUY", "SELL", "HOLD"])
return state

View File

@@ -7,8 +7,23 @@ from dataclasses import dataclass
@dataclass(slots=True)
class AutoTradeState:
# текущее состояние: OFF / OBSERVING / RUNNING
status: str = "OFF"
# выбранная стратегия: TREND / GRID / SCALP
strategy: str | None = None
# торговый инструмент
symbol: str = ""
# риск на одну сделку в %
risk_percent: float | None = None
pnl_usd: float = 0.0
# текущий PnL
pnl_usd: float = 0.0
# время последней проверки
last_check_at: str | None = None
# последний сигнал стратегии
last_signal: str | None = None

View File

@@ -0,0 +1,19 @@
# 0017 — Auto Analysis Cycle before Background Loop
## Решение
Сначала реализовать run_cycle() и UI integration.
Фоновый loop вынести в следующий подэтап.
## Причины
- проще тестировать;
- безопаснее;
- меньше race conditions.
## Последствия
Анализ пока запускается только при открытии / обновлении экрана.

View File

@@ -98,7 +98,12 @@
✔ risk presets
### 07.3
⏳ background loop
✔ analysis cycle skeleton
✔ mock signals
✔ UI integration
### 07.3.1
⏳ asyncio background loop
### 07.4
⏳ strategy plugin architecture

View File

@@ -23,11 +23,20 @@
---
## 07.3 — Background loop
## 07.3 — Analysis Cycle Skeleton
⏳ scheduler
⏳ market polling
signal loop
✔ run_cycle()
✔ last_check_at
✔ last_signal
✔ mock signals
✔ UI integration
---
## 07.3.1 — Background loop
⏳ asyncio loop
⏳ live cycle
---

View File

@@ -0,0 +1,72 @@
# Stage 07.3 — Auto Trading Analysis Cycle Skeleton
## Что сделано
Реализован skeleton цикла анализа рынка для автоторговли.
---
## 1. Analysis cycle
В `AutoTradeService` добавлен метод:
- run_cycle()
Метод:
- обновляет время анализа
- генерирует mock сигнал
- сохраняет состояние
---
## 2. AutoTradeState update
Добавлены поля:
- last_check_at
- last_signal
---
## 3. UI integration
Экран 🤖 Автоторговля теперь показывает:
- Последний анализ
- Сигнал стратегии
Пример:
Последний анализ: 12:34:56
Сигнал: 🟢 BUY
---
## 4. Mock signal generator
Для MVP используется:
- BUY
- SELL
- HOLD
через random.choice()
---
## 5. Foundation for background loop
Подготовлена база для следующего этапа:
07.3.1 — asyncio background loop
---
## Commit
```
git add .
git commit -m "Stage 07.3 - auto trading analysis cycle skeleton"
git push
```