Marktsimulation (Teil 13): Sockets (VII)
Einführung
Im vorangegangenen Artikel „Marktsimulation (Teil 12): Sockets (VI)“ haben wir einen Python-Server erstellt, der auf mehrere Clients reagieren kann. Es funktionierte durch die Verwendung von Threads. Ich glaube, der Kerngedanke, wie Threads funktionieren, ist jetzt klar. Aber vor allem hoffe ich, dass Sie verstehen konnten, wie der Server in gewisser Weise einen kleinen Chat zum Laufen bringt. Der Zweck dieser Erklärungen über Sockets ist es nicht, alles über sie abzudecken. Wir möchten, dass Sie verstehen, wie Sockets funktionieren. Dieses Wissen wird später im Replay-/Simulationssystem benötigt.
Kehren wir zu dem Problem in Excel zurück
Obwohl der Python-Server so funktioniert, dass er von mehreren Clients verwendet werden kann, ist er nicht für die Verwendung in Excel vorgesehen. Das heißt, wenn wir xlwings verwenden, um diesen Server in Excel zu integrieren, werden Sie Probleme bei der Interaktion mit Excel haben. Aber warum, wenn wir Threads verwenden, um ein Blockieren der Codeausführung zu vermeiden?
Dies ist eine etwas komplizierte Frage – zumindest für diejenigen, die sich nicht so gut damit auskennen, wie ein Programm ein anderes Programm verwendet, das nicht Teil davon ist. Vielleicht war dieser Satz etwas verwirrend, aber wir wollen versuchen, ihn zu verstehen. Wenn wir ein VBA-Skript in Excel verwenden, führen wir im Wesentlichen ein Programm aus. Wenn dieses Skript eine andere Anwendung aufruft, wartet Excel in der Regel darauf, dass die Ausführung beendet wird. Dies ist jedoch nicht immer der Fall. Um zu verhindern, dass Excel auf die Fertigstellung der Anwendung warten muss, können wir eine Anwendung verwenden, die nicht mit Excel verknüpft ist. Das heißt, wir haben Excel neben einer anderen Anwendung geöffnet, die mit VBA arbeitet. Beide können koexistieren, ohne um die CPU zu konkurrieren. Das ist ungefähr so, als würde man Excel bitten, Word zu öffnen. Es spielt keine Rolle, wenn Word abstürzt – das beeinträchtigt den Betrieb von Excel nicht.
Dies ist jedoch nicht der Fall bei dem, was hier vorgeschlagen wird, nämlich den Datenaustausch zwischen MetaTrader 5 und Excel zu ermöglichen. Während MetaTrader 5 und Excel nicht um die CPU konkurrieren, tut dies das Python-Skript, das den Socket für diesen Datenaustausch erstellt. Dies ist auf die bisher vorgestellten Servermodelle zurückzuführen. Um die Erklärung zu vereinfachen, nehmen wir das letzte Skript, das im vorigen Artikel besprochen wurde. Es ist unten zu sehen:
01. import socket as sock 02. import threading as thread 03. 04. CONN_LIST = [] 05. 06. def NewClientHandler(conn, addr): 07. global CONN_LIST 08. CONN_LIST.append(conn) 09. print(f"Client [%s:%s] is online..." % addr) 10. while True: 11. msg = conn.recv(512).decode().rstrip(None) 12. if msg: 13. print(f"Message from [%s:%s]: {msg}" % addr) 14. for slave in CONN_LIST: 15. if slave != conn: 16. slave.send(f"[{addr[0]}]: {msg}\n\r".encode()) 17. if msg.lower() == "/see you later": 18. break 19. print(f"Client [%s:%s] is disconnecting..." % addr) 20. conn.close() 21. CONN_LIST.remove(conn) 22. 23. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 24. server.bind(('0.0.0.0', 27015)) 25. server.listen() 26. print("Waiting connections...") 27. while True: 28. conn, addr = server.accept() 29. conn.send("Welcome to Server.\n\r".encode()) 30. thread.Thread(target=NewClientHandler, args=(conn, addr)).start()
Python-Code
Obwohl dieses Skript nicht direkt in xlwings verwendet werden kann, um einen Server zu erstellen, können wir einige kleine Anpassungen vornehmen, und es wird für die Verwendung geeignet sein. Der oben gezeigte Code wird in den folgenden umgewandelt:
01. import socket as sock 02. import threading as thread 03. 04. CONN_LIST = [] 05. 06. def NewClientHandler(conn, addr): 07. global CONN_LIST 08. CONN_LIST.append(conn) 09. print(f"Client [%s:%s] is online..." % addr) 10. while True: 11. msg = conn.recv(512).decode().rstrip(None) 12. if msg: 13. print(f"Message from [%s:%s]: {msg}" % addr) 14. for slave in CONN_LIST: 15. if slave != conn: 16. slave.send(f"[{addr[0]}]: {msg}\n\r".encode()) 17. if msg.lower() == "good bye": 18. break 19. print(f"Client [%s:%s] is disconnecting..." % addr) 20. conn.close() 21. CONN_LIST.remove(conn) 22. 23. def InitServer(HOST, PORT): 24. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 25. server.bind((HOST, PORT)) 26. server.listen() 27. print("Waiting connections...") 28. while True: 29. conn, addr = server.accept() 30. conn.send("Welcome to Server.\n\r".encode()) 31. thread.Thread(target=NewClientHandler, args=(conn, addr)).start() 32. 33. if __name__ == '__main__': 34. InitServer('localhost', 27015)
Python-Code
Also gut. Wie Sie sehen, haben wir nur sehr wenig Code hinzugefügt, aber dadurch können wir das Skript direkt in Python verwenden oder es über VBA in Excel über xlwings ausführen. In VBA kann der folgende Befehl verwendet werden, um den Server von Excel aus zu aktivieren:
Public Sub ExecuteServer()
RunPython ("import Server_Thread; Server_Thread.InitServer('127.0.0.1', 27014)")
End Sub
VBA-Code
Wir werden sehen, was passieren wird. Dies ist für diejenigen, die diese Sequenz nicht verfolgt haben oder nicht wissen, wie man mit VBA arbeitet. Dieses kleine VBA-Skript sollte von einem Steuerelement aufgerufen werden, das wir zu Excel hinzufügen müssen. Es ist eigentlich eine sehr einfache Aufgabe. Wir fügen ein Steuerelement hinzu, z. B. eine Schaltfläche, und geben in den Eigenschaften der Schaltfläche an, dass sie das oben gezeigte Skript ausführen soll.
Aber was macht dieses VBA-Skript eigentlich? Es weist xlwings an, das Python-Skript auszuführen, das wir zuvor gesehen haben. Beachten Sie, dass Sie im Python-Skript den Port und den Host angeben können, auf dem der Server laufen soll. Die Argumente, die wir in Server_Thread.InitServer sehen, geben genau den Host an – genauer gesagt, die Adresse, an der der Server Verbindungen erwartet – sowie den Port. Beachten Sie, dass wir einen anderen Port als den im Python-Skript verwendeten definieren; dies geschieht, um zu verdeutlichen, dass der Server über Excel definiert und aufgerufen wird.
Wenn Sie alle Schritte korrekt ausgeführt haben, werden Sie feststellen, dass Excel Python öffnet und der Server verfügbar wird. Es wird also genauso funktionieren, als ob wir es direkt in Python ausführen würden. Aber es gibt einen wichtigen Punkt: Excel verhält sich dann seltsam, und die Ausführung von Aktionen ist schwierig. Dies liegt daran, dass der Python-Server mit Excel um die CPU-Auslastung konkurriert. „Aber warum passiert das? Verwendet unser Server keine Threads?“ Ja, der Server verwendet Threads, aber nicht auf der gesamten Serverebene – es gibt einen Teil des Codes, der blockiert. Dieser Teil befindet sich in Zeile 29 oder 28 des ursprünglichen Codes. Sie können sehen, dass diese Zeile einen Aufruf der Funktion accept enthält. Genau diese Funktion blockiert und konkurriert mit Excel um die CPU-Nutzung.
Gibt es eine Möglichkeit, dieses Problem zu lösen? Ja, die gibt es. Der entscheidende Punkt ist, wie viel Mühe wir bereit sind, in die Lösung zu investieren. Ich sage das, weil Sie in VBA einen Thread erstellen könnten, sodass das Python-Skript direkt in einem Thread läuft. Oder Sie könnten nach einer anderen Lösung für dasselbe Problem suchen.
Wie auch immer, es muss etwas getan werden, um zu verhindern, dass die Funktion accept weiterhin mit Excel um die CPU-Auslastung konkurriert.
Beginnen wir mit der Funktion SELECT
Eine der einfachsten Lösungen, die keine komplexen Programmiermanöver erfordert, ist die Verwendung der select-Funktion im Servercode. Diese Funktion ist bereits in anderen Codes dieser Socket-Serie aufgetaucht. Sie können dies überprüfen, indem Sie sich die Zeile 69 des in C++ geschriebenen Codes des Mini-Chat-Servers ansehen. Dieser Code ist in dem Artikel zu finden: Marktsimulation (Teil 09): Sockets (III)
Wie Sie sehen können, ist die Lösung gar nicht so kompliziert. Wenn Sie jedoch etwas Ähnliches in Python machen wollen, wird sich der Code etwas von der C++-Version unterscheiden. Wenn Sie den gleichen Code wie in der C++-Version verwenden, stürzt das Programm ab, wenn Sie select statt accept aufrufen. Um dies zu verdeutlichen, sehen wir uns an, wie der Servercode in Python implementiert ist. Dies ist in dem nachstehenden Skript zu sehen:
01. import socket as sock 02. import threading as thread 03. import select 04. import xlwings as xl 05. import time 06. 07. CONN_LIST = [] 08. INPUTS = [] 09. WB = None 10. LINE = 1 11. 12. def WriteMessageInExcel(msg): 13. global WB 14. global LINE 15. if WB == None: 16. WB = xl.Book.caller() 17. WB.sheets[0].cells(LINE, 1).value = msg 18. LINE = LINE + 1 19. 20. def NewClientHandler(conn, addr): 21. global CONN_LIST 22. CONN_LIST.append(conn) 23. print(f"Client [%s:%s] is online..." % addr) 24. while True: 25. msg = conn.recv(512).decode().rstrip(None) 26. if msg: 27. print(f"Message from [%s:%s]: {msg}" % addr) 28. for slave in CONN_LIST: 29. if slave != conn: 30. slave.send(f"[{addr[0]}]: {msg}\n\r".encode()) 31. if msg.lower() == "good bye": 32. break 33. print(f"Client [%s:%s] is disconnecting..." % addr) 34. conn.close() 35. CONN_LIST.remove(conn) 36. 37. def InitServer(HOST, PORT): 38. global INPUTS 39. server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 40. server.bind((HOST, PORT)) 41. server.listen() 42. INPUTS.append(server) 43. WriteMessageInExcel("Waiting connections...") 44. while True: 45. read, write, err = select.select(INPUTS, [], [], 1) 46. for slave in read: 47. if slave is server: 48. conn, addr = slave.accept() 49. conn.send("Welcome to Server in Python.\n\r".encode()) 50. thread.Thread(target=NewClientHandler, args=(conn, addr)).start() 51. WriteMessageInExcel(f"Client [%s:%s] is online..." % addr) 52. WriteMessageInExcel("Ping...") 53. time.sleep(1) 54. 55. if __name__ == '__main__': 56. InitServer('localhost', 27015)
Python-Code
Bitte beachten Sie, dass nur geringfügige Änderungen vorgenommen worden sind. Wir gehen dabei Schritt für Schritt vor, damit Sie genau verstehen können, was geändert wird. Beachten Sie, dass wir nun einige zusätzliche Elemente zwischen den Zeilen 08 und 09 haben. Sie dienen als Unterstützung für select und Excel über xlwings.
Um das Schreiben von Code, die Fehlersuche und die Anzeige von Informationen zu erleichtern, haben wir eine kleine Prozedur in Zeile 12 eingefügt. Sie dient der Eingabe von Daten in Excel. Beachten Sie, dass in Zeile 15 geprüft wird, ob die Variable initialisiert wurde; da sie beim ersten Aufruf nicht initialisiert wird, wird Zeile 16 ausgeführt – allerdings nur einmal. Dann wird in Zeile 17 eine Nachricht in die aktuelle Zeile des Arbeitsblatts geschrieben, und in Zeile 18 wird der Zeilenzähler erhöht, sodass der nächste Aufruf in der nächsten Zeile angezeigt wird.
Ich weiß, dass dieser Code optisch nicht sehr ansprechend ist, aber die Ästhetik hat hier keine Priorität, sondern die Funktionalität. Das Wichtigste ist, dass es funktioniert, nicht, dass es schön aussieht oder sich von VBA aus angenehm verwenden lässt.
Nun möchte ich Ihre Aufmerksamkeit auf Zeile 45 dieses Skripts lenken. Beachten Sie, dass wir hier select aufrufen. Im Gegensatz zu dem bereits erwähnten C++-Code haben wir hier einen zusätzlichen Parameter. Dieser Parameter hat den exakten Wert eins. Er gibt an, wie lange select auf etwas warten wird, das passieren soll. Diese Zeit wird in Sekunden angegeben, sodass select eine Sekunde wartet, bevor es weitergeht. Sie können einen kleineren Wert verwenden und ihn als Gleitkommazahl festlegen. Aber wenn Sie nicht wissen, wie das geht, keine Sorge – wir erklären es später. Es ist jedoch wichtig, Folgendes zu beachten: Wenn wir diesen Wert nicht angeben, wird select blockiert, bis ein Ereignis auf dem Server-Socket eintritt.
Um dies besser zu verstehen, beachten Sie, dass wir in Zeile 52 eine Meldung an Excel ausgeben und in Zeile 53 eine weitere Sekunde warten, wodurch eine Verzögerung von zwei Sekunden in der Schleife entsteht. Für unsere Zwecke ist dies mehr als ausreichend. Wenn Zeile 45 die Ausführung blockiert, während sie auf ein Ereignis auf dem Socket wartet, wird der Ping nicht alle zwei Sekunden gesendet; wenn es keine solche Blockierung gibt, wird der Ping gesendet. Wenn Sie dieses Skript über VBA ausführen, erhalten Sie folgendes Ergebnis:

Okay, aber warum benötigen wir die for-Schleife in Zeile 46? Sie wird benötigt, um zu prüfen, welches Ereignis auf dem Socket aufgetreten ist, da wir noch nicht in den Thread eingetreten sind. Wenn also ein Nutzer versucht, eine Verbindung herzustellen, wird diese Schleife verwendet, um festzustellen, was passiert ist. Auf diese Weise bleibt alles andere beim Alten. Ein wichtiger Punkt: Ohne diese for-Schleife würde sich der Server aufhängen, während er darauf wartet, dass in der accept-Funktion, die in Zeile 48 steht, etwas passiert. Aber mit der Prüfung in Zeile 47 innerhalb der Schleife können wir erkennen, dass ein Client versucht, eine Verbindung herzustellen. Obwohl dieser Server immer noch nicht erlaubt, dass Excel läuft, ohne mit Python zu konkurrieren, haben wir das System bereits deutlich verbessert.
Das Problem tritt jedoch in Zeile 44 auf. Die Tatsache, dass wir in dieser Zeile eine Endlosschleife in das Python-Skript eingeben, führt dazu, dass das Skript mit Excel um die CPU-Auslastung konkurriert. Aber wir können diese Schleife nicht entfernen, denn wenn wir das täten – so wie das Skript implementiert ist – würde der Server herunterfahren. Daher müssen wir einige Änderungen vornehmen, um dieses Szenario zu vermeiden. Das heißt, wir müssen in der Lage sein, das Skript einzugeben und zu verlassen, ohne dass der Server seine Verbindungen schließt. Doch zuvor wollen wir uns ein weiteres Stück Zwischencode ansehen, das wir erstellen müssen. Um die Stimmung ein wenig aufzulockern, sollten wir uns einem neuen Thema zuwenden.
Ein Mini-Chat-Server in einer Klasse
Obwohl viele Menschen die Entwicklung oder Implementierung von objektorientiertem Code nicht mögen, hat er klare Vorteile. In erster Linie ermöglicht es uns, den in Python implementierten Server einfacher zu bedienen, zumindest aus Sicht der Programmierung. Der Grund dafür ist, dass wir den Thread sehr leicht aus der im vorherigen Thema gezeigten Version entfernen können. Auf diese Weise erreichen wir eine Funktionalität, die der C++-Implementierung sehr ähnlich ist. Um dies zu erreichen, werden wir einen neuen Code erstellen, der jedoch mit dem bisherigen vollständig kompatibel sein wird. In diesem frühen Stadium werden wir das Programm natürlich nicht mit Excel ausführen. Wir werden Python in seiner reinen Form verwenden. Das neue Skript, das OOP verwendet, ist unten abgebildet:
01. import socket as sock 02. import select 03. import time 04. 05. class MiniChat: 06. def __init__(self, HOST : str = 'localhost', PORT : int = 27015) -> None: 07. self.server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 08. self.server.setblocking(0) 09. self.server.bind((HOST, PORT)) 10. self.server.listen() 11. self.CONN_LIST = [self.server] 12. print(f"Waiting connections in {PORT}...") 13. 14. def BroadCastData(self, conn, info) -> None: 15. for sock in self.CONN_LIST: 16. if sock != self.server and sock != conn: 17. sock.send((info + "\n\r").encode()) 18. 19. def ClientDisconnect(self, conn) -> None: 20. msg = "Client " + str(conn.getpeername()) + " is disconnected..." 21. self.BroadCastData(conn, msg) 22. print(msg) 23. conn.close() 24. self.CONN_LIST.remove(conn) 25. 26. def Shutdown(self) -> None: 27. while self.CONN_LIST: 28. for conn in self.CONN_LIST: 29. self.CONN_LIST.remove(conn) 30. 31. def LoopMessage(self) -> None: 32. while self.CONN_LIST: 33. read, write, err = select.select(self.CONN_LIST, [], [], 1) 34. for sock in read: 35. if sock is self.server: 36. conn, addr = sock.accept() 37. conn.setblocking(0) 38. self.CONN_LIST.append(conn) 39. conn.send("Welcome to Server Chat in Python.\n\r".encode()) 40. self.BroadCastData(conn, "[%s:%s] entered room." % addr) 41. print("Client [%s:%s] connecting..." % addr) 42. else: 43. try: 44. data = sock.recv(512).decode().rstrip(None) 45. if data: 46. if data.lower() == '/shutdown': 47. self.Shutdown() 48. self.BroadCastData(sock, str(sock.getpeername()) + f":{data}") 49. else: 50. self.ClientDisconnect(sock) 51. except: 52. self.ClientDisconnect(sock) 53. time.sleep(0.5) 54. 55. if __name__ == '__main__': 56. MyChat = MiniChat() 57. MyChat.LoopMessage() 58. print("Server Shutdown...")
Python-Code
Wenn Sie Fragen zu diesem Code haben, können Sie sich an die Python-Seite mit der Dokumentation wenden. Aber ich glaube, Sie werden keine großen Schwierigkeiten haben, das Skript zu verstehen. Auch wenn es auf den ersten Blick seltsam erscheinen mag, geht es darum, wie Python implementiert wird. Lesen Sie die Dokumentation, um zu verstehen, wie alles funktioniert. Aber lassen Sie uns einen kurzen Überblick geben, um mehr Klarheit zu schaffen.
Die Klasse beginnt in Zeile 05 und endet in Zeile 53. In Zeile 56 verwenden wir den Klassenkonstruktor, d. h. die Prozedur __init__ in Zeile 06. Mit diesem Verfahren wird der Server effektiv erstellt, obwohl er noch nicht auf Verbindungen wartet. Wenn init jedoch abgeschlossen ist, erfolgt der Aufruf in Zeile 57. Er leitet die Ausführung zur Zeile 31. Beachten Sie, dass wir in diesem Verfahren in Zeile 31 den Thread entfernt haben. Das heißt, der Server verwendet jetzt dieselbe Art von Steuerung wie der C++-Server. Beachten Sie, dass wir uns in Zeile 32 immer noch in einer Schleife befinden. Ich möchte jedoch, dass Sie Folgendes verstehen: Diese Schleife kann sich entweder innerhalb oder außerhalb der Klasse befinden, und der Server wird immer noch in der Lage sein, seine Aufgabe zu erfüllen.
Warum ist das so wichtig für uns? Der Grund dafür ist, dass wir, wenn wir die Schleife außerhalb der Klasse platzieren, eine Möglichkeit brauchen, um zu prüfen, ob der Server aktiv sein kann. Mit der Schleife innerhalb der Klasse benötigen wir zur Erstellung des Servers nur die Zeilen 56 und 57. Dies ermöglicht es uns, diesen Server für den Import in andere Python-Skripte zu verpacken.
Und jetzt kommt der interessante Teil: Wenn wir die Tatsache ignorieren, dass es in Zeile 32 eine Schleife gibt, kann derselbe Server verpackt und in Excel eingebunden werden, sodass xlwings ihn nutzen kann – allerdings nicht auf die bisher gezeigte Weise. Diese Umsetzung wurde jedoch absichtlich vorgenommen. Auf diese Weise können wir die Änderungen zwischen den Versionen des Python-Servers leicht nachvollziehen. So können wir leicht sehen, wie wir den Python-Server davon abhalten können, mit Excel um die CPU-Nutzung zu konkurrieren. Wenn Sie den Code wirklich verstanden haben, werden wir ihn nun so abändern, dass er leicht verpackt und in jedem Skript verwendet werden kann.
01. import socket as sock 02. import select as sel 03. 04. class MiniChat: 05. def __init__(self, HOST : str = 'localhost', PORT : int = 27015) -> None: 06. self.server = sock.socket(sock.AF_INET, sock.SOCK_STREAM) 07. self.server.bind((HOST, PORT)) 08. self.server.listen() 09. self.CONN_LIST = [self.server] 10. print(f"Waiting connections in {PORT}...") 11. 12. def __BroadCastData(self, conn, info) -> None: 13. for sock in self.CONN_LIST: 14. if sock != self.server and sock != conn: 15. sock.send((info + "\n\r").encode()) 16. 17. def __ClientDisconnect(self, conn) -> None: 18. msg = "Client " + str(conn.getpeername()) + " is disconnected..." 19. self.BroadCastData(conn, msg) 20. print(msg) 21. conn.close() 22. self.CONN_LIST.remove(conn) 23. 24. def ExistConnection(self) -> bool: 25. if self.CONN_LIST: 26. return True 27. return False 28. 29. def CheckMessage(self, sleep) -> str: 30. read, write, err = sel.select(self.CONN_LIST, [], [], sleep) 31. for sock in read: 32. if sock is self.server: 33. conn, addr = sock.accept() 34. self.CONN_LIST.append(conn) 35. conn.send("Welcome to Server Chat in Python.\n\r".encode()) 36. self.__BroadCastData(conn, "[%s:%s] entered room." % addr) 37. print("Client [%s:%s] connecting..." % addr) 38. else: 39. try: 40. data = sock.recv(512).decode().rstrip(None) 41. if data: 42. if data.lower() == '/shutdown': 43. self.CONN_LIST.clear() 44. self.__BroadCastData(sock, str(sock.getpeername()) + f":{data}") 45. return str(sock.getpeername()) + f"> {data}" 46. else: 47. self.__ClientDisconnect(sock) 48. except: 49. self.__ClientDisconnect(sock) 50. return "" 51. 52. if __name__ == '__main__': 53. MyChat = MiniChat() 54. while MyChat.ExistConnection(0.2): 55. info = MyChat.CheckMessage() 56. if info: 57. print(f"{info}") 58. print("Server Shutdown...")
Python-Code
OK. Also gut. In dem oben gezeigten Skript haben wir einige Verbesserungen am Code vorgenommen, damit er die zuvor besprochenen Funktionen erfüllen kann, d. h., er kann verpackt und in anderen Anwendungen verwendet werden. Aber schauen wir mal, was jetzt passiert, da dieser Code nicht mehr auf Netzwerkereignisse warten wird.
Erstens sind die Prozeduren BroadCastData und ClientDisconnect innerhalb der Klasse privat, was bedeutet, dass auf sie nicht direkt von außerhalb des Klassenkörpers zugegriffen werden kann. Es gibt zwar Möglichkeiten, auf diese privaten Verfahren zuzugreifen, aber ich werde nicht erklären, wie man das macht, damit Sie nicht in Versuchung kommen, dies in anderen Fällen zu tun. Beachten Sie, dass Sie in Python private Elemente definieren können und nicht versuchen sollten, auf Prozeduren oder Funktionen zuzugreifen, die als privat gelten. Wenn wir dies versuchen, wird Python einen Fehler melden. Auch hier gibt es in Python eine Möglichkeit, auf private Verfahren zuzugreifen, aber das überlasse ich Ihnen. Ich rate jedoch dringend davon ab, diese Praxis anzuwenden, da sie gegen eine der Prämissen der Codekapselung verstößt. Wenn ein Verfahren oder eine Funktion privat ist, gibt es dafür einen Grund.
Es gibt nur noch zwei öffentliche Verfahren: ExistConnection und CheckMessage. ExistConnection gibt, wie in Zeile 24 zu sehen, einen booleschen Wert zurück. Dieser Rückgabewert soll dazu dienen, außerhalb des Klassenkörpers zu prüfen, ob der Server aktiv ist oder nicht, d. h. ob eine aktive Verbindung besteht.
Die Prozedur CheckMessage in Zeile 29 gibt die von einem Client gesendete Nachricht zurück. Dies ist jedoch nicht immer der Fall. Beachten Sie, dass der Server in Zeile 43 auf Anforderung des Clients herunterfährt. Dies mag ein wenig radikal erscheinen, denn im Prinzip sollte nicht jeder Client in der Lage sein, so etwas zu tun. Da dieser Code jedoch nur für Ausbildungszwecke gedacht ist, sehe ich kein Problem darin, ihn auf diese Weise zu implementieren.
In jedem Fall geben wir in Zeile 45 zurück, was der Client geschrieben hat. Wir werden sehen, dass wir sowohl den Namen des Client als auch die eingegebene Nachricht als Ergebnis der Funktion verwenden. Dies ist wichtig für Analysen und Experimente.
Aber was uns wirklich interessiert – und hier beginnt der interessanteste Teil dieses Codes – ist der Inhalt der Zeilen 53 bis 58. Beachten Sie, dass die Schleife innerhalb der Klasse im vorherigen Code entfernt und an diese Stelle verschoben wurde. Genauer gesagt, befindet sie sich jetzt in Zeile 54. In Zeile 55 fangen wir ab, was auch immer der Client an den Socket gesendet hat, und wenn der Wert relevant ist, d. h. Daten enthält, geben wir die empfangenen Informationen mit Python auf der Konsole aus; dies geschieht in Zeile 57. So ist alles, was ein Client sendet, für die anderen sichtbar und kann darüber hinaus direkt auf dem Terminal, auf dem der Server läuft, überwacht werden.
Ein Punkt, der Sie vielleicht interessiert: Wie lange bleibt dieser Server untätig, wenn auf dem Socket, den er überwacht, nichts passiert? Erinnern Sie sich, dass wir im vorigen Code erwähnt haben, dass die Funktion select eine Sekunde lang warten würde? Das war auf einen bestimmten Wert zurückzuführen.
Hier werden wir eine kürzere Zeitspanne abwarten. Da wir den Code der Klasse nicht ändern wollen, übergeben wir den Wert direkt an die Klasse. Dies geschieht in Zeile 54. Beachten Sie, dass wir hier einen Wert kleiner als eins definieren. In diesem Fall ist der Wert 0,2 eingestellt. Dies ist die Zeit in Sekunden, die gewartet wird, d. h., weniger als eine Sekunde, bevor die Funktion select abbricht und die Codeausführung fortsetzt.
Je nach der Arbeit, die mit den an den Socket gesendeten Informationen zu leisten ist, und der Verarbeitungszeit kann dieser Wert noch weiter reduziert werden. Denken Sie daran, dass im Gegensatz zu dem Modell, bei dem wir Threads verwendet haben, der Server hier nur überwacht, was die einzelnen Verbindungen tun. In dem Modell, in dem wir Threads verwendet haben, war jede Verbindung unabhängig. Dadurch wird die Verarbeitung in diesem Modell etwas langsamer als bei der Verwendung von Threads zum Lesen und Schreiben auf dem Socket.
Achten Sie nun auf die folgenden Details. Dieser Server, der im obigen Code zu sehen ist, funktioniert perfekt, wenn er in der Eingabeaufforderung ausgeführt wird. Auch mit der Einschränkung, dass bei vielen Clients die Verbindungen nacheinander gelesen werden.
Für den bidirektionalen Datenaustausch zwischen Excel und MetaTrader 5 ist dieses Python-Skript jedoch noch nicht vollständig geeignet. Obwohl es jetzt auf einem viel fortgeschritteneren Niveau ist, funktioniert alles, was wir bisher gesehen haben, aber nicht so, dass das Python-Skript ausgeführt werden kann, ohne mit Excel um die CPU-Auslastung zu konkurrieren.
Auch dieses letzte Skript erlaubt es uns nicht, dies zu erreichen, obwohl es eine gute Vorstellung davon vermittelt, wie man es tun kann. Wenn Sie unsicher sind, machen Sie sich keine Sorgen – es ist gar nicht so einfach, das zu bemerken; Sie sollten etwas Erfahrung in paralleler Programmierung haben.
Vielleicht ist Ihnen aufgefallen, dass wir in den meisten der letzten Skripte xlwings nicht verwendet haben. Warum? War es dafür nicht geeignet? Alles, was Sie außerhalb der Python-Grundlagen verwenden, wird Ihnen keine gute Erfahrung bei der Verwendung von Excel in Verbindung mit Python bieten. Lassen Sie mich dies klarstellen, um Fehlinterpretationen zu vermeiden.
Wenn wir etwas in xlwings oder einem anderen Paket entwickeln, das das Lesen und Schreiben direkt in Excel ermöglicht, müssen wir beachten, dass alle Programme, Funktionen oder Prozeduren ausgeführt werden und dann ihre Aufgabe beenden. Sie bleiben nicht in einer Schleife, und egal, wie sehr man sich bemüht, die Dinge anders zu machen, solche Lösungen werden nie das erreichen, was wir wirklich brauchen. Das Problem ist, dass das Serverskript immer in einer Schleife bleiben muss, unabhängig davon, ob es auf dem Socket auf etwas wartet oder nicht.
Der Punkt ist, dass das Skript in einer Schleife läuft. Im Falle eines Clients kann die Vorgehensweise etwas anders sein. Der Client verbindet sich mit dem Server, und sobald dies geschieht, sendet der Server die Informationen, die der Client erhalten soll. Wenn der Client sie empfängt, kann er die Verbindung schließen, wodurch das Skript beendet wird und die Daten für eine ordnungsgemäße Verarbeitung in Excel zur Verfügung stehen. Diese Verarbeitung kann entweder von Python oder von VBA aus erfolgen.
Aber das ist eigentlich nicht der wichtigste Punkt. Ich möchte anmerken, dass der Code vollkommen anders aussehen würde, wenn wir einen Client statt eines Servers verwenden würden. Dies liegt daran, dass wir einen in VBA erstellten Timer verwenden können, um den Client in Python auszuführen, ohne dass es zu Problemen mit der CPU-Konkurrenz zu Excel kommt. Daher behaupte ich, dass die Pakete, die viele Menschen regelmäßig nutzen, nicht ausreichen werden. Wir benötigen eine andere Lösung.
Abschließende Gedanken
Im heutigen Artikel haben wir untersucht, wie man einen Python-Server erstellt, der beim Warten auf Socket-Ereignisse nicht blockiert. Keiner der vorgestellten Codes erreicht jedoch das angestrebte Ziel vollständig. Als Programmierer müssen wir aus unserer Komfortzone heraustreten. Beim Programmieren geht es darum, Lösungen für Probleme zu finden, was ganz anders ist, als viele sich das vorstellen – es geht nicht einfach darum, etwas zu kopieren und einzufügen. Programmieren ist etwas Künstlerisches, das von uns verlangt, darüber hinauszugehen, zu studieren und Lösungen für Probleme zu suchen.
Wie bereits am Ende des vorherigen Themas erwähnt, stellt die Verwendung eines in Python entwickelten Clients zusammen mit Excel sicher, dass Excel sich nicht so seltsam verhält, dass es unbrauchbar wird. Ich möchte Ihnen jedoch zeigen, wie Sie einen Python-Server innerhalb von Excel einrichten können, während Excel weiterhin normal verwendet werden kann. Viele mögen denken: „Aber warum Excel offen lassen? Führen Sie die Berechnungen durch, aktualisieren Sie alles mit Pandas, xlwings oder einem anderen Paket, das mit Excel-Dateien arbeitet, und das war's.“
Das wäre die einfachste Lösung. Aber ich möchte zeigen, dass es möglich ist, das zu erreichen, was wir uns ursprünglich vorgenommen haben. Um diese Motivation zu verstehen, stellen Sie sich einen Moment lang einen durchschnittlichen Nutzer vor. Egal wie schön und einfach die in Python erstellte Schnittstelle auch sein mag – sodass der Nutzer kein Excel für die Datenanalyse benötigt –, viele werden unsere Lösung trotzdem anzweifeln. Sie werden zu Recht nach einem Programmierer suchen, der das Problem auf eine Weise lösen kann, die sich ihrem Arbeitsablauf anpasst.
Der Punkt ist, dass derselbe Nutzer Excel verwenden möchte, um zu kontrollieren, was in MetaTrader 5 ausgeführt wird. Und Sie als Programmierer und aufstrebender Fachmann müssen verstehen, dass Sie, wenn es noch keine Lösung gibt, diese selbst schaffen müssen. Daher reicht die Gewohnheit des Kopierens und Einfügens nicht aus.
Im nächsten Artikel werden wir untersuchen, wie man das macht. Studieren Sie dieses Material also sorgfältig – es ist notwendig, um zu verstehen, was im nächsten Teil geschehen wird.
| Datei | Beschreibung |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstriert die Interaktion zwischen Chart Trade und dem Expert Advisor (erfordert Mouse Study für die Interaktion). |
| Indicators\Chart Trade.mq5 | Erzeugt ein Fenster zur Konfiguration des zu versendenden Auftrags (erfordert Mouse Study zur Interaktion). |
| Indicators\Market Replay.mq5 | Erstellt Steuerelemente für die Interaktion mit dem Replay-/Simulationsdienst (erfordert Mouse Study für die Interaktion). |
| Indicators\Mouse Study.mq5 | Ermöglicht die Interaktion zwischen den grafischen Steuerelementen und dem Nutzer (sowohl für die Wiedergabe als auch für den realen Markthandel erforderlich). |
| Services/MarketReplay.mq5 | Erstellt und pflegt den Replay-/Simulationsdienst des Marktes (Hauptdatei des gesamten Systems). |
| VS Code C++Server.cpp | Erstellt und pflegt einen in C++ entwickelten Socket-Server (Mini-Chat-Version). |
| Python Code Server.py | Erstellt und pflegt einen Python-Socket für die Kommunikation zwischen MetaTrader 5 und Excel. |
| ScriptsCheckSocket.mq5 | Ermöglicht das Testen der Verbindung mit einem externen Socket. |
| Indicators\Mini Chat.mq5 | Implementiert einen Mini-Chat unter Verwendung eines Indikators (erfordert den Server, um zu funktionieren). |
| Experts\Mini Chat.mq5 | Implementiert einen Mini-Chat über einen Expert Advisor (erfordert den Server, um zu funktionieren). |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12790
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Neuronale Netze im Handel: Integration der Chaostheorie in die Zeitreihenprognose (Attraos)
Trend-Kriterien. Abschluss
Marktsimulation (Teil 12): Sockets (VI)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.
Veröffentlichter Artikel Marktsimulation (Teil 13): Steckdosen (VII):
Autor: Daniel Jose