96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
|
|
#!/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()
|