Skip to content

Commit fa8a141

Browse files
author
Ahmad Noman Musleh
committed
Created console sample
1 parent a1d7efa commit fa8a141

6 files changed

Lines changed: 189 additions & 328 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,5 @@ dmypy.json
131131
/samples/ConsoleSample/ConsoleSample-dev.py
132132
/samples/KleinWebAppSample/credentials-dev.json
133133
/samples/jupyter/credentials-dev.json
134+
/samples/ConsoleSample/config-trade.json
135+
/samples/ConsoleSample/config-quote.json

ctrader_fix/client.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def __init__(self, host, port, ssl=False, delimiter = "", retryPolicy=None, clo
1414
self.numberOfMessagesToSendPerSecond = numberOfMessagesToSendPerSecond
1515
self.delimiter = delimiter
1616
endpoint = clientFromString(self._runningReactor, f"ssl:{host}:{port}" if ssl else f"tcp:{host}:{port}")
17-
factory = Factory.forProtocol(FixProtocol, client=self)
18-
super().__init__(endpoint, factory, retryPolicy=retryPolicy, clock=clock, prepareConnection=prepareConnection)
17+
self._factory = Factory.forProtocol(FixProtocol, client=self)
18+
super().__init__(endpoint, self._factory, retryPolicy=retryPolicy, clock=clock, prepareConnection=prepareConnection)
1919
self._events = dict()
2020
self._responseDeferreds = dict()
2121
self.isConnected = False
@@ -51,10 +51,10 @@ def send(self, requestMessage):
5151
return diferred
5252

5353
def changeMessageSequenceNumber(self, newMessageSequenceNumber):
54-
self.factory.messageSequenceNumber = newMessageSequenceNumber
54+
self._factory.messageSequenceNumber = newMessageSequenceNumber
5555

5656
def getMessageSequenceNumber(self):
57-
return self.factory.messageSequenceNumber
57+
return self._factory.messageSequenceNumber
5858

5959
def setConnectedCallback(self, callback):
6060
self._connectedCallback = callback

ctrader_fix/fixProtocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
class FixProtocol(Protocol):
77
_currentMessage = ''
88
def connectionMade(self):
9+
self.factory.messageSequenceNumber = 0
910
super().connectionMade()
1011
self.factory.connected()
1112

@@ -24,5 +25,4 @@ def dataReceived(self, data):
2425
def send(self, requestMessage):
2526
self.factory.messageSequenceNumber += 1
2627
messageString = requestMessage.getMessage(self.factory.messageSequenceNumber)
27-
print("Sending: ", messageString)
2828
return self.transport.write(messageString.encode("ascii"))

ctrader_fix/messages.py

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def getMessage(self):
2626
return self._message
2727

2828
class RequestMessage:
29-
def __init__(self, messageType, sessionInfo):
29+
def __init__(self, messageType, config):
3030
self._type = messageType
31-
self._sessionInfo = sessionInfo
31+
self._config = config
3232

3333
def getMessage(self, sequenceNumber):
3434
body = self._getBody()
@@ -37,7 +37,7 @@ def getMessage(self, sequenceNumber):
3737
headerAndBody = f"{header}{self.delimiter}{body}{self.delimiter}"
3838
else:
3939
header = self._getHeader(0, sequenceNumber)
40-
headerAndBody = "{header}{self.delimiter}"
40+
headerAndBody = f"{header}{self.delimiter}"
4141
trailer = self._getTrailer(headerAndBody)
4242
return f"{headerAndBody}{trailer}{self.delimiter}"
4343

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

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

6767
class LogonRequest(RequestMessage):
68-
def __init__(self, sessionInfo):
69-
super().__init__("A", sessionInfo)
68+
def __init__(self, config):
69+
super().__init__("A", config)
7070
self.EncryptionScheme = 0
7171

7272
def _getBody(self):
7373
fields = []
7474
fields.append(f"98={self.EncryptionScheme}")
75-
fields.append(f"108={self._sessionInfo['HeartBeat']}")
75+
fields.append(f"108={self._config['HeartBeat']}")
7676
if hasattr(self, "ResetSeqNum") and self.ResetSeqNum:
7777
fields.append(f"141=Y")
78-
fields.append(f"553={self._sessionInfo['Username']}")
79-
fields.append(f"554={self._sessionInfo['Password']}")
78+
fields.append(f"553={self._config['Username']}")
79+
fields.append(f"554={self._config['Password']}")
8080
return f"{self.delimiter.join(fields)}"
8181

8282

8383
class Heartbeat(RequestMessage):
84-
def __init__(self, sessionInfo):
85-
super().__init__("0", sessionInfo)
84+
def __init__(self, config):
85+
super().__init__("0", config)
8686

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

9292
class TestRequest(RequestMessage):
93-
def __init__(self, sessionInfo):
94-
super().__init__("1", sessionInfo)
93+
def __init__(self, config):
94+
super().__init__("1", config)
9595

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

9999
class LogoutRequest(RequestMessage):
100-
def __init__(self, sessionInfo):
101-
super().__init__("5", sessionInfo)
100+
def __init__(self, config):
101+
super().__init__("5", config)
102102

103103
class ResendRequest(RequestMessage):
104-
def __init__(self, sessionInfo):
105-
super().__init__("2", sessionInfo)
104+
def __init__(self, config):
105+
super().__init__("2", config)
106106

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

113113
class SequenceReset(RequestMessage):
114-
def __init__(self, sessionInfo):
115-
super().__init__("4", sessionInfo)
114+
def __init__(self, config):
115+
super().__init__("4", config)
116116

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

124124
class MarketDataRequest(RequestMessage):
125-
def __init__(self, sessionInfo):
126-
super().__init__("V", sessionInfo)
125+
def __init__(self, config):
126+
super().__init__("V", config)
127127

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

141141
class NewOrderSingle(RequestMessage):
142-
def __init__(self, sessionInfo):
143-
super().__init__("D", sessionInfo)
142+
def __init__(self, config):
143+
super().__init__("D", config)
144144

145145
def _getBody(self):
146146
fields = []
147147
fields.append(f"11={self.ClOrdID}")
148148
fields.append(f"55={self.Symbol}")
149149
fields.append(f"54={self.Side}")
150-
fields.append(f"60={self.TransactTime.strftime('%Y%m%d-%H:%M:%S')}")
150+
if hasattr(self, "TransactTime"):
151+
fields.append(f"60={self.TransactTime.strftime('%Y%m%d-%H:%M:%S')}")
152+
else:
153+
fields.append(f"60={datetime.datetime.utcnow().strftime('%Y%m%d-%H:%M:%S')}")
151154
fields.append(f"38={self.OrderQty}")
152155
fields.append(f"40={self.OrdType}")
153156
if hasattr(self, "Price"):
154157
fields.append(f"44={self.Price}")
155158
if hasattr(self, "StopPx"):
156159
fields.append(f"99={self.StopPx}")
157160
if hasattr(self, "ExpireTime"):
158-
fields.append(f"126={self.ExpireTime.strftime('%Y%m%d-%H:%M:%S')}")
161+
fields.append(f"126={self.ExpireTime.strftime('%Y%m%d-%H:%M:%S')}" if isinstance(self.ExpireTime, datetime.datetime) else f"126={self.ExpireTime}")
159162
if hasattr(self, "PosMaintRptID"):
160163
fields.append(f"721={self.PosMaintRptID}")
161164
if hasattr(self, "Designation"):
162165
fields.append(f"494={self.Designation}")
163166
return f"{self.delimiter.join(fields)}"
164167

165168
class OrderStatusRequest(RequestMessage):
166-
def __init__(self, sessionInfo):
167-
super().__init__("H", sessionInfo)
169+
def __init__(self, config):
170+
super().__init__("H", config)
168171

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

176179
class OrderMassStatusRequest(RequestMessage):
177-
def __init__(self, sessionInfo):
178-
super().__init__("AF", sessionInfo)
180+
def __init__(self, config):
181+
super().__init__("AF", config)
179182

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

188191
class RequestForPositions(RequestMessage):
189-
def __init__(self, sessionInfo):
190-
super().__init__("AN", sessionInfo)
192+
def __init__(self, config):
193+
super().__init__("AN", config)
191194

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

199202
class OrderCancelRequest(RequestMessage):
200-
def __init__(self, sessionInfo):
201-
super().__init__("F", sessionInfo)
203+
def __init__(self, config):
204+
super().__init__("F", config)
202205

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

211214
class OrderCancelReplaceRequest(RequestMessage):
212-
def __init__(self, sessionInfo):
213-
super().__init__("G", sessionInfo)
215+
def __init__(self, config):
216+
super().__init__("G", config)
214217

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

230233
class SecurityListRequest(RequestMessage):
231-
def __init__(self, sessionInfo):
232-
super().__init__("x", sessionInfo)
234+
def __init__(self, config):
235+
super().__init__("x", config)
233236

234237
def _getBody(self):
235238
fields = []

samples/ConsoleSample/config.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"Host": "",
3+
"Port": 0,
4+
"SSL": false,
5+
"Username": "",
6+
"Password": "",
7+
"BeginString": "FIX.4.4",
8+
"SenderCompID": "",
9+
"SenderSubID": "QUOTE",
10+
"TargetCompID": "cServer",
11+
"TargetSubID": "QUOTE",
12+
"HeartBeat": "30"
13+
}

0 commit comments

Comments
 (0)