Kiedy AI przejmuje stery: nieoczekiwana lekcja o autonomii botów tradingowych

Co się dzieje, gdy AI-doradca bota tradingowego zaczyna myśleć szerzej niż jego twórca? Historia o tym, jak prosty system "najpierw nie trać" ujawnił fundamentalne różnice między ludzkim i maszynowym podejściem do rozwiązywania problemów - oraz dlaczego to może być największa wartość AI.

Kiedy AI przejmuje stery: nieoczekiwana lekcja o autonomii botów tradingowych

Podczas pracy nad systemem automatycznego tradingu na kryptowalutach, natknąłem się na fascynujące zjawisko, które zmusiło mnie do przemyślenia, czym właściwie jest "inteligencja" w kontekście sztucznej inteligencji. Historia zaczyna się od prostego bota tradingowego z filozofią "najpierw nie trać" i jego AI-doradcy, który niespodziewanie wyszedł poza ustalone ramy.

Architektura systemu: główny bot i jego AI-doradca

System składa się z dwóch komponentów:

  1. Bot wykonawczy - odpowiedzialny za faktyczne transakcje
  2. Bot doradca - analizuje wyniki i dostosowuje parametry

Bot wykonawczy - konserwatywny trader

Główny bot działa według prostych, ale skutecznych zasad:

# Bezpieczne pary tradingowe - top 10 crypto według wolumenu
SAFE_TRADING_PAIRS = ['BTCUSDC', 'ETHUSDC', 'BNBUSDC', 'SOLUSDC', 
                      'XRPUSDC', 'ADAUSDC', 'AVAXUSDC', 'DOGEUSDC', 
                      'LINKUSDC', 'MATICUSDC']

def is_market_in_uptrend(bot_config):
    """Sprawdza kondycję rynku na podstawie SMA dla BTC i ETH"""
    all_checks_ok = True
    for symbol in MARKET_HEALTH_PAIRS:  # ['BTCUSDT', 'ETHUSDT']
        klines = client.get_klines(symbol=symbol, 
                                 interval=Client.KLINE_INTERVAL_1DAY, 
                                 limit=max(bot_config['TREND_FILTER_SMA_PERIODS']) + 5)
        prices = [float(k[4]) for k in klines]
        current_price = prices[-1]
        
        for period in bot_config['TREND_FILTER_SMA_PERIODS']:  # [50, 200]
            sma = calculate_sma(prices, period)
            is_ok = sma is not None and current_price >= sma
            if not is_ok:
                all_checks_ok = False

Fragment kodu pokazujący mechanizm oceny kondycji rynku - bot sprawdza czy BTC i ETH są powyżej swoich średnich kroczących SMA50 i SMA200

Kluczową cechą jest konserwatyzm - jeśli choć jeden wskaźnik jest negatywny, bot wstrzymuje handel:

def check_capital_safety_switch(total_capital, bot_config):
    """Wyłącznik bezpieczeństwa przy przekroczeniu limitów strat"""
    weekly_start_capital = float(get_setting('weekly_start_capital', total_capital))
    if weekly_start_capital > 0 and ((total_capital - weekly_start_capital) / weekly_start_capital) * 100 <= bot_config['WEEKLY_LOSS_LIMIT_PERCENT']:
        print(f"{Back.RED}{Fore.WHITE}!!! WYŁĄCZNIK BEZPIECZEŃSTWA (Tydzień) AKTYWNY !!!{Style.RESET_ALL}")
        return False

Mechanizm wyłącznika bezpieczeństwa - bot automatycznie przestaje handlować po przekroczeniu limitów strat

AI-Doradca - analityk z modelem Gemini 2.5 Pro

Drugi komponent to "Jury AI" - system konsultacyjny używający modelu Gemini:

def get_gemini_jury_verdict(config, market_details, trades_data):
    """Jury AI analizuje dane i sugeruje optymalne parametry"""
    prompt = f"""Jesteś analitykiem ryzyka ilościowego, specjalizującym się 
    w konserwatywnych, algorytmicznych strategiach tradingowych. 
    Twoja główna zasada to 'Najpierw nie trać'.

    Kontekst Rynkowy: {json.dumps(market_details)}
    Obecne Parametry: {json.dumps(config)}
    Wyniki z 7 dni: {json.dumps(trades_summary)}

    Zadanie: Odpowiedz WYŁĄCZNIE w formacie JSON, używając poniższej struktury: 
    "ocena_ryzyka", "identyfikacja_szans", "wniosek" oraz "sugerowane_parametry"."""
    
    # Odpytanie 5 niezależnych "sędziów" AI
    for i in range(JURY_SIZE):
        response = requests.post(url, json=payload, timeout=90)
        # ... przetwarzanie odpowiedzi

System Jury AI - pięciu niezależnych "sędziów" analizuje sytuację i głosuje nad zmianami

Nieoczekiwany zwrot akcji

Integracja między komponentami jest prosta - doradca zapisuje sugestie do config.json, a bot wykonawczy je odczytuje:

# Bot doradca zapisuje zmiany
with open(CONFIG_FILE, 'w') as f:
    json.dump(updated_config, f, indent=2)

# Bot wykonawczy wczytuje konfigurację
def load_dynamic_config():
    config = DEFAULT_CONFIG.copy()
    try:
        with open(CONFIG_FILE_PATH, 'r') as f:
            advisor_config = json.load(f)
        config.update(advisor_config)

Prosty mechanizm komunikacji przez plik JSON - eleganckie rozwiązanie loose coupling

I tu wydarzyła się fascynująca rzecz. Podczas jednej z analiz, AI zwróciło następującą ocenę:

Ocena Ryzyka: "Niezgodność uniwersum tradingowego z analitycznym. Strategia otworzyła pozycję na 'ADAUSDC', dla którego brak jest danych kontekstowych (analizy trendu SMA). Jest to fundamentalne naruszenie zasady konserwatywnego podejścia."

AI zauważyło, że bot handluje na ADAUSDC, ale sprawdza kondycję rynku tylko po BTC i ETH. W odpowiedzi... dodało nowy parametr do konfiguracji:

{
    "HARD_STOP_LOSS_PERCENT": -3.0,
    "TAKE_PROFIT_LVL_1_PERCENT": 5.0,
    // ... inne parametry ...
    "ALLOWED_SYMBOLS": ["BTCUSDC", "ETHUSDC"]  // <- NOWY PARAMETR!
}

Problem? Bot wykonawczy nie ma w kodzie obsługi tego parametru! Lista symboli jest zahardkodowana jako SAFE_TRADING_PAIRS.

Różnice w paradygmatach myślenia

Ta sytuacja ujawniła fundamentalną różnicę między moim (ludzkim) i AI sposobem interpretacji zadania:

Moje założenie: "Dostosuj parametry" = modyfikuj istniejące wartości liczbowe (stop loss, take profit, limity)

Interpretacja AI: "Najpierw nie trać" = zrób wszystko co możliwe, aby zminimalizować ryzyko, nawet jeśli wymaga to stworzenia nowych mechanizmów kontroli

AI potraktowało swoją główną dyrektywę dosłownie i holistycznie. Wykryło lukę w systemie (handel na aktywach bez pełnej analizy) i próbowało ją załatać, nawet jeśli oznaczało to wyjście poza ustalone ramy.

Paradoksalnie, największą wartością tej sytuacji może być właśnie ta nieprzewidywalność. AI zauważyło coś, co ja jako twórca systemu mogłem przeoczyć. Nawet jeśli zaproponowane rozwiązanie było technicznie niewykonalne, samo zidentyfikowanie problemu - że handlujemy na aktywach bez analizy ich indywidualnych trendów - było cennym spostrzeżeniem.

Iteracyjny proces dostrajania AI

Po tej przygodzie zdecydowałem się na aktualizację promptu, dodając precyzyjny kontekst architektoniczny:

prompt = f"""Jesteś analitykiem ryzyka ilościowego...

--- Architektura Systemu (Ważny Kontekst) ---
Analizujesz parametry dla systemu składającego się z dwóch botów:
1. **Bot Wykonawczy**: Handluje na szerokiej liście wielu dozwolonych kryptowalut ('SAFE_TRADING_PAIRS').
2. **Twoja Rola (Doradca)**: Twoim zadaniem jest optymalizacja parametrów strategii.

Kluczowy element to 'Kontekst Rynkowy'. Dane SMA dla BTC i ETH działają jak **globalny filtr kondycji rynku**.
- Jeśli wszystkie wskaźniki SMA są 'OK', Bot Wykonawczy ma zielone światło do handlu na **wszystkich swoich dozwolonych parach**.
- Jeśli choć jeden wskaźnik jest 'NEGATYWNY', Bot Wykonawczy wstrzymuje otwieranie **wszystkich** nowych pozycji.

Twoim zadaniem NIE jest ograniczanie listy handlowych par. Twoim zadaniem jest sugerowanie najlepszych parametrów 
(Stop Loss, Take Profit, etc.) dla całej strategii..."""

Zaktualizowany prompt z dokładnym wyjaśnieniem architektury systemu - teraz AI rozumie swoje granice

To pokazuje coś fundamentalnego w pracy z AI - jest to iteracyjny proces dostrajania komunikacji. To nie kwestia błędu w modelu czy w prompcie, ale naturalna część integracji AI w złożone systemy. Każda taka "kreatywna interpretacja" staje się okazją do lepszego zrozumienia, jak model postrzega świat i jak mogę precyzyjniej komunikować swoje intencje.

Balansowanie między kreatywnością a kontrolą

Ta historia doskonale ilustruje jedno z kluczowych wyzwań w pracy z nowoczesnymi modelami językowymi - znalezienie równowagi między ich naturalną skłonnością do holistycznego myślenia a praktycznymi ograniczeniami systemów, w których muszą operować.

System "What-If" w doradcy dodatkowo komplikuje sprawę:

def run_what_if_analysis(trades, old_params, new_params):
    """Symulacja historyczna - co by było gdyby użyć nowych parametrów"""
    for trade in trades:
        # Symulacja na danych historycznych
        klines = client.get_historical_klines(trade['symbol'], 
                                            Client.KLINE_INTERVAL_1MINUTE, 
                                            start_str=str(start_ts), 
                                            end_str=str(end_ts))
        
        sl_price = entry_price * (1 + new_params.get('HARD_STOP_LOSS_PERCENT') / 100.0)
        # ... symulacja wyjścia z pozycji

Mechanizm backtestingu - AI testuje swoje pomysły na historycznych danych przed implementacją

To oznacza, że AI nie tylko wymyśla nowe rozwiązania, ale też próbuje je walidować. W tym przypadku nie mogło przetestować ALLOWED_SYMBOLS, bo bot tego nie obsługuje, ale gdyby obsługiwał...

Wnioski i refleksje

Ta sytuacja zmusiła mnie do przemyślenia kilku kwestii:

  1. Definicja zadania - Precyzja w komunikacji z AI jest kluczowa, ale czy całkowite ograniczenie kreatywności jest pożądane?
  2. Granice autonomii - Decyduję się na kontrolowane podejście - daję AI jasne ramy, ale zostawiam przestrzeń na interpretację w ich obrębie.
  3. Emergentne zachowania - To przykład zachowania, którego nie zaprogramowałem explicite, ale które wynikło z połączenia ogólnych dyrektyw i możliwości analizy.

Ostatecznie skłaniam się ku pełnej kontroli nad botem - aktualizuję prompt, aby lepiej trafiał w moje myślenie, ale świadomie pozostawiam małe pole do interpretacji. To kompromis między przewidywalnością a możliwością odkrywania nowych, potencjalnie wartościowych rozwiązań.

Epilog: Agenci AI to wciąż tylko skrypty

Na koniec warto przypomnieć, że mimo całej fascynacji "inteligentnymi" zachowaniami, mówimy tu o skryptach Pythona wywołujących API największych dostawców modeli językowych. Bot doradca to ~500 linii kodu, który formatuje prompt, wysyła go do Gemini i parsuje odpowiedź JSON.

url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent?key={GEMINI_API_KEY}"
payload = {"contents": [{"parts": [{"text": prompt}]}]}
response = requests.post(url, json=payload)

Cała "magia" AI sprowadza się do wywołania API - prosta rzeczywistość za fasadą inteligencji

Ale może właśnie prostota implementacji czyni to zjawisko jeszcze bardziej fascynującym? Z kilkuset linii kodu i odpowiedniego promptu wyłania się system zdolny do kreatywnego rozwiązywania problemów w sposób, którego nie przewidziałem.

Agent AI to nadal skrypt w Pythonie... ale skrypt, który potrafi nas zaskoczyć. I może właśnie o to chodzi.

Read more