Skip to content

Commit

Permalink
Created console sample
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahmad Noman Musleh committed Mar 10, 2022
1 parent a1d7efa commit fa8a141
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 328 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,5 @@ dmypy.json
/samples/ConsoleSample/ConsoleSample-dev.py
/samples/KleinWebAppSample/credentials-dev.json
/samples/jupyter/credentials-dev.json
/samples/ConsoleSample/config-trade.json
/samples/ConsoleSample/config-quote.json
8 changes: 4 additions & 4 deletions ctrader_fix/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ def __init__(self, host, port, ssl=False, delimiter = "", retryPolicy=None, clo
self.numberOfMessagesToSendPerSecond = numberOfMessagesToSendPerSecond
self.delimiter = delimiter
endpoint = clientFromString(self._runningReactor, f"ssl:{host}:{port}" if ssl else f"tcp:{host}:{port}")
factory = Factory.forProtocol(FixProtocol, client=self)
super().__init__(endpoint, factory, retryPolicy=retryPolicy, clock=clock, prepareConnection=prepareConnection)
self._factory = Factory.forProtocol(FixProtocol, client=self)
super().__init__(endpoint, self._factory, retryPolicy=retryPolicy, clock=clock, prepareConnection=prepareConnection)
self._events = dict()
self._responseDeferreds = dict()
self.isConnected = False
Expand Down Expand Up @@ -51,10 +51,10 @@ def send(self, requestMessage):
return diferred

def changeMessageSequenceNumber(self, newMessageSequenceNumber):
self.factory.messageSequenceNumber = newMessageSequenceNumber
self._factory.messageSequenceNumber = newMessageSequenceNumber

def getMessageSequenceNumber(self):
return self.factory.messageSequenceNumber
return self._factory.messageSequenceNumber

def setConnectedCallback(self, callback):
self._connectedCallback = callback
Expand Down
2 changes: 1 addition & 1 deletion ctrader_fix/fixProtocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
class FixProtocol(Protocol):
_currentMessage = ''
def connectionMade(self):
self.factory.messageSequenceNumber = 0
super().connectionMade()
self.factory.connected()

Expand All @@ -24,5 +25,4 @@ def dataReceived(self, data):
def send(self, requestMessage):
self.factory.messageSequenceNumber += 1
messageString = requestMessage.getMessage(self.factory.messageSequenceNumber)
print("Sending: ", messageString)
return self.transport.write(messageString.encode("ascii"))
85 changes: 44 additions & 41 deletions ctrader_fix/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ def getMessage(self):
return self._message

class RequestMessage:
def __init__(self, messageType, sessionInfo):
def __init__(self, messageType, config):
self._type = messageType
self._sessionInfo = sessionInfo
self._config = config

def getMessage(self, sequenceNumber):
body = self._getBody()
Expand All @@ -37,7 +37,7 @@ def getMessage(self, sequenceNumber):
headerAndBody = f"{header}{self.delimiter}{body}{self.delimiter}"
else:
header = self._getHeader(0, sequenceNumber)
headerAndBody = "{header}{self.delimiter}"
headerAndBody = f"{header}{self.delimiter}"
trailer = self._getTrailer(headerAndBody)
return f"{headerAndBody}{trailer}{self.delimiter}"

Expand All @@ -47,14 +47,14 @@ def _getBody(self):
def _getHeader(self, lenBody, sequenceNumber):
fields = []
fields.append(f"35={self._type}")
fields.append(f"49={self._sessionInfo['SenderCompID']}")
fields.append(f"56={self._sessionInfo['TargetCompID']}")
fields.append(f"57={self._sessionInfo['TargetSubID']}")
fields.append(f"50={self._sessionInfo['SenderSubID']}")
fields.append(f"49={self._config['SenderCompID']}")
fields.append(f"56={self._config['TargetCompID']}")
fields.append(f"57={self._config['TargetSubID']}")
fields.append(f"50={self._config['SenderSubID']}")
fields.append(f"34={sequenceNumber}")
fields.append(f"52={datetime.datetime.utcnow().strftime('%Y%m%d-%H:%M:%S')}")
fieldsJoined = self.delimiter.join(fields)
return f"8={self._sessionInfo['BeginString']}{self.delimiter}9={lenBody+len(fieldsJoined) + 2}{self.delimiter}{fieldsJoined}"
return f"8={self._config['BeginString']}{self.delimiter}9={lenBody+len(fieldsJoined) + 2}{self.delimiter}{fieldsJoined}"

def _getTrailer(self, headerAndBody):
messageBytes = bytes(headerAndBody, "ascii")
Expand All @@ -65,44 +65,44 @@ def _getTrailer(self, headerAndBody):
return f"10={str(checksum).zfill(3)}"

class LogonRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("A", sessionInfo)
def __init__(self, config):
super().__init__("A", config)
self.EncryptionScheme = 0

def _getBody(self):
fields = []
fields.append(f"98={self.EncryptionScheme}")
fields.append(f"108={self._sessionInfo['HeartBeat']}")
fields.append(f"108={self._config['HeartBeat']}")
if hasattr(self, "ResetSeqNum") and self.ResetSeqNum:
fields.append(f"141=Y")
fields.append(f"553={self._sessionInfo['Username']}")
fields.append(f"554={self._sessionInfo['Password']}")
fields.append(f"553={self._config['Username']}")
fields.append(f"554={self._config['Password']}")
return f"{self.delimiter.join(fields)}"


class Heartbeat(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("0", sessionInfo)
def __init__(self, config):
super().__init__("0", config)

def _getBody(self):
if hasattr(self, "TestReqId") is False:
return None
return f"112={self.TestReqId}"

class TestRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("1", sessionInfo)
def __init__(self, config):
super().__init__("1", config)

def _getBody(self):
return f"112={self.TestReqId}"

class LogoutRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("5", sessionInfo)
def __init__(self, config):
super().__init__("5", config)

class ResendRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("2", sessionInfo)
def __init__(self, config):
super().__init__("2", config)

def _getBody(self):
fields = []
Expand All @@ -111,8 +111,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class SequenceReset(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("4", sessionInfo)
def __init__(self, config):
super().__init__("4", config)

def _getBody(self):
fields = []
Expand All @@ -122,8 +122,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class MarketDataRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("V", sessionInfo)
def __init__(self, config):
super().__init__("V", config)

def _getBody(self):
fields = []
Expand All @@ -139,32 +139,35 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class NewOrderSingle(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("D", sessionInfo)
def __init__(self, config):
super().__init__("D", config)

def _getBody(self):
fields = []
fields.append(f"11={self.ClOrdID}")
fields.append(f"55={self.Symbol}")
fields.append(f"54={self.Side}")
fields.append(f"60={self.TransactTime.strftime('%Y%m%d-%H:%M:%S')}")
if hasattr(self, "TransactTime"):
fields.append(f"60={self.TransactTime.strftime('%Y%m%d-%H:%M:%S')}")
else:
fields.append(f"60={datetime.datetime.utcnow().strftime('%Y%m%d-%H:%M:%S')}")
fields.append(f"38={self.OrderQty}")
fields.append(f"40={self.OrdType}")
if hasattr(self, "Price"):
fields.append(f"44={self.Price}")
if hasattr(self, "StopPx"):
fields.append(f"99={self.StopPx}")
if hasattr(self, "ExpireTime"):
fields.append(f"126={self.ExpireTime.strftime('%Y%m%d-%H:%M:%S')}")
fields.append(f"126={self.ExpireTime.strftime('%Y%m%d-%H:%M:%S')}" if isinstance(self.ExpireTime, datetime.datetime) else f"126={self.ExpireTime}")
if hasattr(self, "PosMaintRptID"):
fields.append(f"721={self.PosMaintRptID}")
if hasattr(self, "Designation"):
fields.append(f"494={self.Designation}")
return f"{self.delimiter.join(fields)}"

class OrderStatusRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("H", sessionInfo)
def __init__(self, config):
super().__init__("H", config)

def _getBody(self):
fields = []
Expand All @@ -174,8 +177,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class OrderMassStatusRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("AF", sessionInfo)
def __init__(self, config):
super().__init__("AF", config)

def _getBody(self):
fields = []
Expand All @@ -186,8 +189,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class RequestForPositions(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("AN", sessionInfo)
def __init__(self, config):
super().__init__("AN", config)

def _getBody(self):
fields = []
Expand All @@ -197,8 +200,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class OrderCancelRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("F", sessionInfo)
def __init__(self, config):
super().__init__("F", config)

def _getBody(self):
fields = []
Expand All @@ -209,8 +212,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class OrderCancelReplaceRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("G", sessionInfo)
def __init__(self, config):
super().__init__("G", config)

def _getBody(self):
fields = []
Expand All @@ -228,8 +231,8 @@ def _getBody(self):
return f"{self.delimiter.join(fields)}"

class SecurityListRequest(RequestMessage):
def __init__(self, sessionInfo):
super().__init__("x", sessionInfo)
def __init__(self, config):
super().__init__("x", config)

def _getBody(self):
fields = []
Expand Down
13 changes: 13 additions & 0 deletions samples/ConsoleSample/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Host": "",
"Port": 0,
"SSL": false,
"Username": "",
"Password": "",
"BeginString": "FIX.4.4",
"SenderCompID": "",
"SenderSubID": "QUOTE",
"TargetCompID": "cServer",
"TargetSubID": "QUOTE",
"HeartBeat": "30"
}
Loading

0 comments on commit fa8a141

Please sign in to comment.