#!/usr/bin/env python3 # Bot_Allokation.py """ Kapitel Einfuehrung: Erstes Optimierungsmodell mit Google OR-Tools (CP-SAT). Problem: Server-Allokation für Trading-Bots. Modell mit expliziter Nebenbedingung statt versteckter Variablengrenze, vollständiger Statusauswertung und Ergebnisprüfung. """ from ortools.sat.python import cp_model # --- Parameter (die "Fakten" des Problems) --------------------------------- GEWINN_A, GEWINN_B = 150, 250 # Euro pro Bot und Tag CPU_A, CPU_B = 2, 5 # vCPU-Bedarf je Bot RAM_A, RAM_B = 4, 6 # GB Arbeitsspeicher je Bot CPU_GESAMT, RAM_GESAMT = 40, 60 # verfügbare Kapazitäten MAX_ARBITRAGE = 8 # Marktliquiditätsgrenze def loese_bot_allokation(zeitlimit_s: float = 10.0): """Baut das Modell, löst es und gibt eine Auswertung aus.""" # 1. Modell instanziieren modell = cp_model.CpModel() # 2. Entscheidungsvariablen anlegen # NewIntVar(untere_grenze, obere_grenze, name) # Die obere Grenze ist bewusst großzügig; die echte Beschränkung # formulieren wir unten als Nebenbedingung, damit das Modell die # mathematische Formulierung 1:1 abbildet. x_a = modell.NewIntVar(0, 100, "Arbitrage_Bots") x_b = modell.NewIntVar(0, 100, "Trend_Bots") # 3. Nebenbedingungen definieren c_cpu = modell.Add(CPU_A * x_a + CPU_B * x_b <= CPU_GESAMT) # vCPU-Limit c_ram = modell.Add(RAM_A * x_a + RAM_B * x_b <= RAM_GESAMT) # RAM-Limit c_liq = modell.Add(x_a <= MAX_ARBITRAGE) # Marktliquidität # 4. Zielfunktion: Maximiere den Tagesgewinn modell.Maximize(GEWINN_A * x_a + GEWINN_B * x_b) # 5. Solver konfigurieren und ausführen loeser = cp_model.CpSolver() loeser.parameters.max_time_in_seconds = zeitlimit_s status = loeser.Solve(modell) # 6. Status auswerten -- IMMER alle Fälle behandeln status_text = loeser.StatusName(status) if status not in (cp_model.OPTIMAL, cp_model.FEASIBLE): print(f"Keine verwertbare Lösung. Solver-Status: {status_text}") if status == cp_model.INFEASIBLE: print("Das Modell ist unlösbar - die Bedingungen widersprechen sich.") return None a, b = loeser.Value(x_a), loeser.Value(x_b) gewinn = loeser.ObjectiveValue() # 7. Ergebnis ausgeben print("=" * 58) print(" OPTIMALE BOT-ALLOKATION") print("=" * 58) print(f"Solver-Status: {status_text}" f"{' (beweisbar optimal)' if status == cp_model.OPTIMAL else ' (zulässig, nicht bewiesen)'}") print(f"Arbitrage-Bots (x_A): {a} Instanzen") print(f"Trendfolge-Bots (x_B): {b} Instanzen") print(f"Täglicher Max-Gewinn: {gewinn:,.2f} EUR") genutzt_cpu = CPU_A * a + CPU_B * b genutzt_ram = RAM_A * a + RAM_B * b print("-" * 58) print(f"CPU-Auslastung: {genutzt_cpu} / {CPU_GESAMT} vCPUs " f"({genutzt_cpu / CPU_GESAMT * 100:.1f} %)") print(f"RAM-Auslastung: {genutzt_ram} / {RAM_GESAMT} GB " f"({genutzt_ram / RAM_GESAMT * 100:.1f} %)") print(f"Arbitrage-Limit: {a} / {MAX_ARBITRAGE}") # 8. Selbstkontrolle: Sind wirklich alle Bedingungen eingehalten? assert genutzt_cpu <= CPU_GESAMT, "vCPU-Limit verletzt!" assert genutzt_ram <= RAM_GESAMT, "RAM-Limit verletzt!" assert a <= MAX_ARBITRAGE, "Arbitrage-Limit verletzt!" # 9. Zum Vergleich: die naive Strategie "nur der lukrativere Bot" naiv_b = min(CPU_GESAMT // CPU_B, RAM_GESAMT // RAM_B) naiv_gewinn = GEWINN_B * naiv_b print("-" * 58) print(f"Naive Strategie (nur Trendfolge): {naiv_b} Bots -> {naiv_gewinn:,.2f} EUR") print(f"Vorteil der Optimierung: {gewinn - naiv_gewinn:,.2f} EUR pro Tag " f"({(gewinn / naiv_gewinn - 1) * 100:.1f} %)") print("=" * 58) return a, b, gewinn if __name__ == "__main__": loese_bot_allokation()