jgtfx2console 0.4.27__py3-none-any.whl → 0.5.15__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- jgtfx2console/LiveChartDataExport.py +2 -2
- jgtfx2console/__init__.py +1 -1
- jgtfx2console/common_samples/BatchOrderMonitor.py +84 -0
- jgtfx2console/common_samples/OrderMonitor.py +171 -0
- jgtfx2console/common_samples/OrderMonitorNetting.py +195 -0
- jgtfx2console/common_samples/TableListenerContainer.py +197 -0
- jgtfx2console/common_samples/__init__.py +23 -0
- jgtfx2console/common_samples/common.py +219 -0
- jgtfx2console/config_generator.py +12 -5
- jgtfx2console/forexconnect/ForexConnect.py +7 -2
- jgtfx2console/forexconnect/_fix_import.sh +3 -0
- jgtfx2console/forexconnect/lib/windows/ForexConnect.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/__init__.py +0 -0
- jgtfx2console/forexconnect/lib/windows/_depend_on.txt +11 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-console-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-datetime-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-debug-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-errorhandling-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-file-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-file-l1-2-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-file-l2-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-handle-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-heap-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-interlocked-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-libraryloader-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-localization-l1-2-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-memory-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-namedpipe-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-processenvironment-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-processthreads-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-processthreads-l1-1-1.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-profile-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-rtlsupport-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-string-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-synch-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-synch-l1-2-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-sysinfo-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-timezone-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-core-util-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-conio-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-convert-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-environment-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-filesystem-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-heap-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-locale-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-math-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-multibyte-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-private-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-process-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-runtime-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-stdio-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-string-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-time-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/api-ms-win-crt-utility-l1-1-0.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/cacert.pem +3314 -0
- jgtfx2console/forexconnect/lib/windows/concrt140.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/fxcorepy.pyd +0 -0
- jgtfx2console/forexconnect/lib/windows/fxmsg.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/fxtp.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gscurl.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gsexpat.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gslibeay32.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gsssleay32.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gstool3.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/gszlib.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/httplib.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/log4cplus.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/msvcp140.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/pdas.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/pricehistorymgr.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/python3.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/quotesmgr2.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/requirements.txt +5 -0
- jgtfx2console/forexconnect/lib/windows/sqlite3.8.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/ucrtbase.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/vccorlib140.dll +0 -0
- jgtfx2console/forexconnect/lib/windows/vcruntime140.dll +0 -0
- jgtfx2console/fxcli2console.py +12 -7
- {jgtfx2console-0.4.27.dist-info → jgtfx2console-0.5.15.dist-info}/METADATA +18 -21
- jgtfx2console-0.5.15.dist-info/RECORD +112 -0
- {jgtfx2console-0.4.27.dist-info → jgtfx2console-0.5.15.dist-info}/WHEEL +1 -1
- jgtfx2console-0.5.15.dist-info/entry_points.txt +2 -0
- jgtfx2console-0.4.27.dist-info/RECORD +0 -39
- jgtfx2console-0.4.27.dist-info/entry_points.txt +0 -4
- {jgtfx2console-0.4.27.dist-info → jgtfx2console-0.5.15.dist-info}/LICENSE +0 -0
- {jgtfx2console-0.4.27.dist-info → jgtfx2console-0.5.15.dist-info}/top_level.txt +0 -0
@@ -47,7 +47,7 @@ def parse_args():
|
|
47
47
|
metavar="PASSWORD",
|
48
48
|
required=False,
|
49
49
|
help='Your password.')
|
50
|
-
arg_parser.add_argument('-config', metavar="CONFIG_FILE", default='
|
50
|
+
arg_parser.add_argument('-config', metavar="CONFIG_FILE", default='fxliveconfig.xml',
|
51
51
|
help='Config file')
|
52
52
|
|
53
53
|
args = arg_parser.parse_args()
|
@@ -233,7 +233,7 @@ def parse_xml(config_file):
|
|
233
233
|
output_dir = os.getenv('JGTPY_DATA') or jgtconf.get('JGTPY_DATA')
|
234
234
|
dt_separator = find_in_tree(settings, "DateTimeSeparator").text
|
235
235
|
|
236
|
-
if dt_separator == '' or dt_separator is None:
|
236
|
+
if dt_separator == '' or dt_separator is None or dt_separator=='0' or dt_separator=='space' or dt_separator=='\n' :
|
237
237
|
dt_separator = ' '
|
238
238
|
fdp = find_in_tree(settings, "FormatDecimalPlaces").text
|
239
239
|
tzone = find_in_tree(settings, "Timezone").text
|
jgtfx2console/__init__.py
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Copyright 2019 Gehtsoft USA LLC
|
2
|
+
|
3
|
+
# Licensed under the license derived from the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
|
8
|
+
# http://fxcodebase.com/licenses/open-source/license.html
|
9
|
+
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from typing import List
|
17
|
+
|
18
|
+
from forexconnect import fxcorepy
|
19
|
+
|
20
|
+
from common_samples.OrderMonitor import OrderMonitor
|
21
|
+
|
22
|
+
|
23
|
+
class BatchOrderMonitor:
|
24
|
+
__request_ids = None
|
25
|
+
__monitors = []
|
26
|
+
|
27
|
+
def __init__(self) -> None:
|
28
|
+
pass
|
29
|
+
|
30
|
+
@property
|
31
|
+
def monitors(self) -> List[OrderMonitor]:
|
32
|
+
return self.__monitors
|
33
|
+
|
34
|
+
@property
|
35
|
+
def is_batch_executed(self) -> bool:
|
36
|
+
all_completed = True
|
37
|
+
for monitor in self.__monitors:
|
38
|
+
if monitor.is_order_completed:
|
39
|
+
self.remove_request_id(monitor.order.request_id)
|
40
|
+
else:
|
41
|
+
all_completed = False
|
42
|
+
return len(self.__request_ids) == 0 and all_completed
|
43
|
+
|
44
|
+
def set_request_ids(self, request_ids: List[str]) -> None:
|
45
|
+
self.__request_ids = request_ids
|
46
|
+
|
47
|
+
def on_request_completed(self, request_id: str, response: fxcorepy.O2GResponse) -> None:
|
48
|
+
pass
|
49
|
+
|
50
|
+
def remove_request_id(self, request_id: str) -> None:
|
51
|
+
if self.is_own_request(request_id):
|
52
|
+
self.__request_ids.remove(request_id)
|
53
|
+
|
54
|
+
def on_request_failed(self, request_id: str) -> None:
|
55
|
+
self.remove_request_id(request_id)
|
56
|
+
|
57
|
+
def on_trade_added(self, trade_row: fxcorepy.O2GTradeRow) -> None:
|
58
|
+
for monitor in self.__monitors:
|
59
|
+
monitor.on_trade_added(trade_row)
|
60
|
+
|
61
|
+
def on_order_added(self, order: fxcorepy.O2GOrderRow) -> None:
|
62
|
+
request_id = order.request_id
|
63
|
+
print("Order Added " + order.order_id)
|
64
|
+
if self.is_own_request(request_id):
|
65
|
+
if OrderMonitor.is_closing_order(order) or OrderMonitor.is_opening_order(order):
|
66
|
+
self._add_to_monitoring(order)
|
67
|
+
|
68
|
+
def on_order_deleted(self, order: fxcorepy.O2GOrderRow) -> None:
|
69
|
+
for monitor in self.__monitors:
|
70
|
+
monitor.on_order_deleted(order)
|
71
|
+
|
72
|
+
def on_message_added(self, message: fxcorepy.O2GMessageRow) -> None:
|
73
|
+
for monitor in self.__monitors:
|
74
|
+
monitor.on_message_added(message)
|
75
|
+
|
76
|
+
def on_closed_trade_added(self, close_trade_row: fxcorepy.O2GClosedTradeRow) -> None:
|
77
|
+
for monitor in self.__monitors:
|
78
|
+
monitor.on_closed_trade_added(close_trade_row)
|
79
|
+
|
80
|
+
def is_own_request(self, request_id: str) -> bool:
|
81
|
+
return request_id in self.__request_ids
|
82
|
+
|
83
|
+
def _add_to_monitoring(self, order: fxcorepy.O2GOrderRow) -> None:
|
84
|
+
self.__monitors.append(OrderMonitor(order))
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# Copyright 2019 Gehtsoft USA LLC
|
2
|
+
|
3
|
+
# Licensed under the license derived from the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
|
8
|
+
# http://fxcodebase.com/licenses/open-source/license.html
|
9
|
+
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from typing import List
|
17
|
+
from enum import Enum
|
18
|
+
|
19
|
+
from forexconnect import fxcorepy
|
20
|
+
|
21
|
+
|
22
|
+
class OrderMonitor:
|
23
|
+
class ExecutionResult(Enum):
|
24
|
+
EXECUTING = 1
|
25
|
+
EXECUTED = 2
|
26
|
+
PARTIAL_REJECTED = 3
|
27
|
+
FULLY_REJECTED = 4
|
28
|
+
CANCELED = 5
|
29
|
+
|
30
|
+
class OrderState(Enum):
|
31
|
+
ORDER_EXECUTING = 1
|
32
|
+
ORDER_EXECUTED = 2
|
33
|
+
ORDER_CANCELED = 3
|
34
|
+
ORDER_REJECTED = 4
|
35
|
+
|
36
|
+
__market_condition = "5"
|
37
|
+
|
38
|
+
def __init__(self, order: fxcorepy.O2GOrderRow) -> None:
|
39
|
+
self.__order = order
|
40
|
+
self.__trades = []
|
41
|
+
self.__closed_trades = []
|
42
|
+
self.__state = OrderMonitor.OrderState.ORDER_EXECUTING
|
43
|
+
self.__result = OrderMonitor.ExecutionResult.EXECUTING
|
44
|
+
self.__total_amount = 0
|
45
|
+
self.__reject_amount = 0
|
46
|
+
self.__reject_message = ""
|
47
|
+
|
48
|
+
@staticmethod
|
49
|
+
def is_opening_order(order: fxcorepy.O2GOrderRow) -> bool:
|
50
|
+
return order.type.startswith("O")
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def is_closing_order(order: fxcorepy.O2GOrderRow) -> bool:
|
54
|
+
return order.type.startswith("C")
|
55
|
+
|
56
|
+
@property
|
57
|
+
def order_row(self) -> fxcorepy.O2GOrderRow:
|
58
|
+
return self.__order
|
59
|
+
|
60
|
+
@property
|
61
|
+
def trade_rows(self) -> List[fxcorepy.O2GTradeRow]:
|
62
|
+
return self.__trades
|
63
|
+
|
64
|
+
@property
|
65
|
+
def closed_trade_rows(self) -> List[fxcorepy.O2GClosedTradeRow]:
|
66
|
+
return self.__closed_trades
|
67
|
+
|
68
|
+
@property
|
69
|
+
def reject_amount(self) -> int:
|
70
|
+
return self.__reject_amount
|
71
|
+
|
72
|
+
@property
|
73
|
+
def reject_message(self) -> str:
|
74
|
+
return self.__reject_message
|
75
|
+
|
76
|
+
@property
|
77
|
+
def result(self) -> ExecutionResult:
|
78
|
+
return self.__result
|
79
|
+
|
80
|
+
@property
|
81
|
+
def is_order_completed(self) -> bool:
|
82
|
+
print(self.__result)
|
83
|
+
return self.__result != OrderMonitor.ExecutionResult.EXECUTING
|
84
|
+
|
85
|
+
@property
|
86
|
+
def is_all_trades_received(self) -> bool:
|
87
|
+
if self.__state == OrderMonitor.OrderState.ORDER_EXECUTING:
|
88
|
+
return False
|
89
|
+
i_current_total_amount = 0
|
90
|
+
for trade in self.__trades:
|
91
|
+
i_current_total_amount += trade.amount
|
92
|
+
|
93
|
+
for trade in self.__closed_trades:
|
94
|
+
i_current_total_amount += trade.amount
|
95
|
+
|
96
|
+
return i_current_total_amount == self.__total_amount
|
97
|
+
|
98
|
+
def on_trade_added(self, trade: fxcorepy.O2GTradeRow) -> None:
|
99
|
+
trade_order_id = trade.open_order_id
|
100
|
+
order_id = self.__order.order_id
|
101
|
+
if trade_order_id == order_id:
|
102
|
+
self.__trades.append(trade)
|
103
|
+
if self.__state == OrderMonitor.OrderState.ORDER_EXECUTED or \
|
104
|
+
self.__state == OrderMonitor.OrderState.ORDER_REJECTED or \
|
105
|
+
self.__state == OrderMonitor.OrderState.ORDER_CANCELED:
|
106
|
+
if self.is_all_trades_received:
|
107
|
+
self.set_result(True)
|
108
|
+
|
109
|
+
def on_closed_trade_added(self, closed_trade: fxcorepy.O2GClosedTradeRow) -> None:
|
110
|
+
order_id = self.__order.order_id
|
111
|
+
closed_trade_order_id = closed_trade.close_order_id
|
112
|
+
print(closed_trade_order_id, order_id)
|
113
|
+
if order_id == closed_trade_order_id:
|
114
|
+
self.__closed_trades.append(closed_trade)
|
115
|
+
if self.__state == OrderMonitor.OrderState.ORDER_EXECUTED or \
|
116
|
+
self.__state == OrderMonitor.OrderState.ORDER_REJECTED or \
|
117
|
+
self.__state == OrderMonitor.OrderState.ORDER_CANCELED:
|
118
|
+
if self.is_all_trades_received:
|
119
|
+
self.set_result(True)
|
120
|
+
|
121
|
+
def on_order_deleted(self, order: fxcorepy.O2GOrderRow) -> None:
|
122
|
+
deleted_order_id = order.order_id
|
123
|
+
order_id = self.__order.order_id
|
124
|
+
if deleted_order_id == order_id:
|
125
|
+
# Store Reject amount
|
126
|
+
if order.status.startswith("R"):
|
127
|
+
self.__state = OrderMonitor.OrderState.ORDER_REJECTED
|
128
|
+
self.__reject_amount = order.amount
|
129
|
+
self.__total_amount = order.origin_amount - self.__reject_amount
|
130
|
+
if self.__reject_message != "" and self.is_all_trades_received:
|
131
|
+
self.set_result(True)
|
132
|
+
elif order.status.startswith("C"):
|
133
|
+
self.__state = OrderMonitor.OrderState.ORDER_CANCELED
|
134
|
+
self.__reject_amount = order.amount
|
135
|
+
self.__total_amount = order.origin_amount - self.__reject_amount
|
136
|
+
if self.is_all_trades_received:
|
137
|
+
self.set_result(False)
|
138
|
+
else:
|
139
|
+
self.__reject_amount = 0
|
140
|
+
self.__total_amount = order.origin_amount
|
141
|
+
self.__state = OrderMonitor.OrderState.ORDER_EXECUTED
|
142
|
+
if self.is_all_trades_received:
|
143
|
+
self.set_result(True)
|
144
|
+
|
145
|
+
def on_message_added(self, message: fxcorepy.O2GMessageRow) -> None:
|
146
|
+
if self.__state == OrderMonitor.OrderState.ORDER_REJECTED or \
|
147
|
+
self.__state == OrderMonitor.OrderState.ORDER_EXECUTING:
|
148
|
+
is_reject_message = self.check_and_store_message(message)
|
149
|
+
if self.__state == OrderMonitor.OrderState.ORDER_REJECTED and is_reject_message:
|
150
|
+
self.set_result(True)
|
151
|
+
|
152
|
+
def set_result(self, success: bool) -> None:
|
153
|
+
if success:
|
154
|
+
if self.__reject_amount == 0:
|
155
|
+
self.__result = OrderMonitor.ExecutionResult.EXECUTED
|
156
|
+
else:
|
157
|
+
self.__result = OrderMonitor.ExecutionResult.FULLY_REJECTED \
|
158
|
+
if (len(self.__trades) == 0 and len(self.__closed_trades) == 0) \
|
159
|
+
else OrderMonitor.ExecutionResult.PARTIAL_REJECTED
|
160
|
+
|
161
|
+
else:
|
162
|
+
self.__result = OrderMonitor.ExecutionResult.CANCELED
|
163
|
+
|
164
|
+
def check_and_store_message(self, message: fxcorepy.O2GMessageRow) -> bool:
|
165
|
+
feature = message.feature
|
166
|
+
if feature == self.__market_condition:
|
167
|
+
text = message.text
|
168
|
+
if self.__order.order_id in text:
|
169
|
+
self.__reject_message = message.text
|
170
|
+
return True
|
171
|
+
return False
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# Copyright 2019 Gehtsoft USA LLC
|
2
|
+
|
3
|
+
# Licensed under the license derived from the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
|
8
|
+
# http://fxcodebase.com/licenses/open-source/license.html
|
9
|
+
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
from typing import List
|
17
|
+
from enum import Enum
|
18
|
+
|
19
|
+
from forexconnect import fxcorepy
|
20
|
+
|
21
|
+
|
22
|
+
class OrderMonitorNetting:
|
23
|
+
|
24
|
+
class ExecutionResult(Enum):
|
25
|
+
EXECUTING = 1
|
26
|
+
EXECUTED = 2
|
27
|
+
PARTIAL_REJECTED = 3
|
28
|
+
FULLY_REJECTED = 4
|
29
|
+
CANCELED = 5
|
30
|
+
|
31
|
+
class OrderState(Enum):
|
32
|
+
ORDER_EXECUTING = 1
|
33
|
+
ORDER_EXECUTED = 2
|
34
|
+
ORDER_CANCELED = 3
|
35
|
+
ORDER_REJECTED = 4
|
36
|
+
|
37
|
+
__market_condition = "5"
|
38
|
+
|
39
|
+
def __init__(self, order: fxcorepy.O2GOrderRow, i_net_position_amount: int = 0) -> None:
|
40
|
+
self.__order = order
|
41
|
+
self.__trades = []
|
42
|
+
self.__updated_trades = []
|
43
|
+
self.__closed_trades = []
|
44
|
+
self.__state = OrderMonitorNetting.OrderState.ORDER_EXECUTING
|
45
|
+
self.__result = OrderMonitorNetting.ExecutionResult.EXECUTING
|
46
|
+
self.__total_amount = 0
|
47
|
+
self.__reject_amount = 0
|
48
|
+
self.__reject_message = ""
|
49
|
+
self.__initial_amount = i_net_position_amount
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def is_opening_order(order: fxcorepy.O2GOrderRow) -> bool:
|
53
|
+
return order.type.startswith("O")
|
54
|
+
|
55
|
+
@staticmethod
|
56
|
+
def is_closing_order(order: fxcorepy.O2GOrderRow) -> bool:
|
57
|
+
return order.type.startswith("C")
|
58
|
+
|
59
|
+
# Process trade adding during order execution
|
60
|
+
def on_trade_added(self, trade: fxcorepy.O2GTradeRow) -> None:
|
61
|
+
trade_order_id = trade.open_order_id
|
62
|
+
order_id = self.__order.order_id
|
63
|
+
if trade_order_id == order_id:
|
64
|
+
self.__trades.append(trade)
|
65
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_EXECUTED or \
|
66
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_REJECTED or \
|
67
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_CANCELED:
|
68
|
+
if self.is_all_trades_received:
|
69
|
+
self.set_result(True)
|
70
|
+
|
71
|
+
# Process trade updating during order execution
|
72
|
+
def on_trade_updated(self, trade_row: fxcorepy.O2GTradeRow) -> None:
|
73
|
+
s_trade_order_id = trade_row.open_order_id
|
74
|
+
s_order_id = self.__order.order_id
|
75
|
+
if s_trade_order_id == s_order_id:
|
76
|
+
self.__updated_trades.append(trade_row)
|
77
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_EXECUTED or \
|
78
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_REJECTED or \
|
79
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_CANCELED:
|
80
|
+
if self.is_all_trades_received:
|
81
|
+
self.set_result(True)
|
82
|
+
|
83
|
+
# Process trade closing during order execution
|
84
|
+
def on_closed_trade_added(self, closed_trade: fxcorepy.O2GClosedTradeRow) -> None:
|
85
|
+
order_id = self.__order.order_id
|
86
|
+
closed_trade_order_id = closed_trade.close_order_id
|
87
|
+
if order_id == closed_trade_order_id:
|
88
|
+
self.__closed_trades.append(closed_trade)
|
89
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_EXECUTED or \
|
90
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_REJECTED or \
|
91
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_CANCELED:
|
92
|
+
if self.is_all_trades_received:
|
93
|
+
self.set_result(True)
|
94
|
+
|
95
|
+
# Process order deletion as result of execution
|
96
|
+
def on_order_deleted(self, order: fxcorepy.O2GOrderRow) -> None:
|
97
|
+
deleted_order_id = order.order_id
|
98
|
+
order_id = self.__order.order_id
|
99
|
+
if deleted_order_id == order_id:
|
100
|
+
# Store Reject amount
|
101
|
+
if order.Status.startswith("R"):
|
102
|
+
self.__state = OrderMonitorNetting.OrderState.ORDER_REJECTED
|
103
|
+
self.__reject_amount = order.amount
|
104
|
+
self.__total_amount = order.origin_amount - self.__reject_amount
|
105
|
+
if self.__reject_message != "" and self.is_all_trades_received:
|
106
|
+
self.set_result(True)
|
107
|
+
else:
|
108
|
+
if order.Status.startswith("C"):
|
109
|
+
self.__state = OrderMonitorNetting.OrderState.ORDER_CANCELED
|
110
|
+
self.__reject_amount = order.amount
|
111
|
+
self.__total_amount = order.origin_amount - self.__reject_amount
|
112
|
+
if self.is_all_trades_received:
|
113
|
+
self.set_result(False)
|
114
|
+
else:
|
115
|
+
self.__reject_amount = 0
|
116
|
+
self.__total_amount = order.OriginAmount
|
117
|
+
self.__state = OrderMonitorNetting.OrderState.ORDER_EXECUTED
|
118
|
+
if self.is_all_trades_received:
|
119
|
+
self.set_result(True)
|
120
|
+
|
121
|
+
def on_message_added(self, message: fxcorepy.O2GMessageRow) -> None:
|
122
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_REJECTED or \
|
123
|
+
self.__state == OrderMonitorNetting.OrderState.ORDER_EXECUTING:
|
124
|
+
is_reject_message = self.check_and_store_message(message)
|
125
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_REJECTED and is_reject_message:
|
126
|
+
self.set_result(True)
|
127
|
+
|
128
|
+
@property
|
129
|
+
def order_row(self) -> fxcorepy.O2GOrderRow:
|
130
|
+
return self.__order
|
131
|
+
|
132
|
+
@property
|
133
|
+
def trade_rows(self) -> List[fxcorepy.O2GTradeRow]:
|
134
|
+
return self.__trades
|
135
|
+
|
136
|
+
@property
|
137
|
+
def updated_trade_rows(self) -> List[fxcorepy.O2GTradeRow]:
|
138
|
+
return self.__updated_trades
|
139
|
+
|
140
|
+
@property
|
141
|
+
def closed_trade_rows(self) -> List[fxcorepy.O2GClosedTradeRow]:
|
142
|
+
return self.__closed_trades
|
143
|
+
|
144
|
+
@property
|
145
|
+
def reject_amount(self) -> int:
|
146
|
+
return self.__reject_amount
|
147
|
+
|
148
|
+
@property
|
149
|
+
def reject_message(self) -> str:
|
150
|
+
return self.__reject_message
|
151
|
+
|
152
|
+
@property
|
153
|
+
def result(self) -> ExecutionResult:
|
154
|
+
return self.__result
|
155
|
+
|
156
|
+
@property
|
157
|
+
def is_order_completed(self) -> bool:
|
158
|
+
return self.__result != OrderMonitorNetting.ExecutionResult.EXECUTING
|
159
|
+
|
160
|
+
def check_and_store_message(self, message: fxcorepy.O2GMessageRow) -> bool:
|
161
|
+
feature = message.feature
|
162
|
+
if feature == self.__market_condition:
|
163
|
+
text = message.text
|
164
|
+
if self.__order.order_id in text:
|
165
|
+
self.__reject_message = message.text
|
166
|
+
return True
|
167
|
+
return False
|
168
|
+
|
169
|
+
@property
|
170
|
+
def is_all_trades_received(self) -> bool:
|
171
|
+
if self.__state == OrderMonitorNetting.OrderState.ORDER_EXECUTING:
|
172
|
+
return False
|
173
|
+
i_current_total_amount = 0
|
174
|
+
for trade in self.__trades:
|
175
|
+
i_current_total_amount += trade.amount
|
176
|
+
|
177
|
+
for trade in self.__updated_trades:
|
178
|
+
i_current_total_amount += trade.amount
|
179
|
+
|
180
|
+
for trade in self.__closed_trades:
|
181
|
+
i_current_total_amount += trade.amount
|
182
|
+
|
183
|
+
return abs(i_current_total_amount - self.__initial_amount) == self.__total_amount
|
184
|
+
|
185
|
+
def set_result(self, success: bool) -> None:
|
186
|
+
if success:
|
187
|
+
if self.__reject_amount == 0:
|
188
|
+
self.__result = OrderMonitorNetting.ExecutionResult.EXECUTED
|
189
|
+
else:
|
190
|
+
self.__result = OrderMonitorNetting.ExecutionResult.FULLY_REJECTED \
|
191
|
+
if (len(self.__trades) == 0 and len(self.__closed_trades) == 0) \
|
192
|
+
else OrderMonitorNetting.ExecutionResult.PARTIAL_REJECTED
|
193
|
+
|
194
|
+
else:
|
195
|
+
self.__result = OrderMonitorNetting.ExecutionResult.CANCELED
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# Copyright 2019 Gehtsoft USA LLC
|
2
|
+
|
3
|
+
# Licensed under the license derived from the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
|
8
|
+
# http://fxcodebase.com/licenses/open-source/license.html
|
9
|
+
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
import traceback
|
17
|
+
|
18
|
+
from forexconnect import ForexConnect, Common
|
19
|
+
|
20
|
+
from common_samples import OrderMonitor
|
21
|
+
|
22
|
+
|
23
|
+
class TableListenerContainer:
|
24
|
+
__response_listener = None
|
25
|
+
__request_id = ""
|
26
|
+
__order_monitor = None
|
27
|
+
|
28
|
+
def __init__(self, response_listener, fx):
|
29
|
+
self.__response_listener = response_listener
|
30
|
+
self._fx = fx
|
31
|
+
self._listeners = []
|
32
|
+
|
33
|
+
def set_request_id(self, request_id):
|
34
|
+
self.__request_id = request_id
|
35
|
+
|
36
|
+
def _on_added_orders(self, listener, row_id, order_row):
|
37
|
+
del listener, row_id
|
38
|
+
if self.__request_id == order_row.request_id:
|
39
|
+
if OrderMonitor.is_closing_order(
|
40
|
+
order_row) or OrderMonitor.is_opening_order(
|
41
|
+
order_row) and self.__order_monitor is None:
|
42
|
+
print(
|
43
|
+
"The order has been added. Order ID: {0:s}, Rate: {1:.5f}, Time In Force: {2:s}".format(
|
44
|
+
order_row.order_id, order_row.rate,
|
45
|
+
order_row.time_in_force))
|
46
|
+
self.__order_monitor = OrderMonitor(order_row)
|
47
|
+
|
48
|
+
def _on_added_trades(self, listener, row_id, trade_row):
|
49
|
+
del listener, row_id
|
50
|
+
if self.__order_monitor is not None:
|
51
|
+
self.__order_monitor.on_trade_added(trade_row)
|
52
|
+
if self.__order_monitor.is_order_completed:
|
53
|
+
self._print_result()
|
54
|
+
self.__response_listener.stop_waiting()
|
55
|
+
|
56
|
+
def _on_added_closed_trades(self, listener, row_id, closed_trade_row):
|
57
|
+
del listener, row_id
|
58
|
+
if self.__order_monitor is not None:
|
59
|
+
self.__order_monitor.on_closed_trade_added(closed_trade_row)
|
60
|
+
if self.__order_monitor.is_order_completed:
|
61
|
+
self._print_result()
|
62
|
+
self.__response_listener.stop_waiting()
|
63
|
+
|
64
|
+
def _on_added_messages(self, listener, row_id, message_row):
|
65
|
+
del listener, row_id
|
66
|
+
if self.__order_monitor is not None:
|
67
|
+
self.__order_monitor.on_message_added(message_row)
|
68
|
+
if self.__order_monitor.is_order_completed:
|
69
|
+
self._print_result()
|
70
|
+
self.__response_listener.stop_waiting()
|
71
|
+
|
72
|
+
def _on_deleted_orders(self, listener, row_id, row_data):
|
73
|
+
del listener, row_id
|
74
|
+
order_row = row_data
|
75
|
+
if self.__request_id == order_row.request_id:
|
76
|
+
if self.__order_monitor is not None:
|
77
|
+
print("The order has been deleted. Order ID: {0}".format(
|
78
|
+
order_row.order_id))
|
79
|
+
self.__order_monitor.on_order_deleted(order_row)
|
80
|
+
if self.__order_monitor.is_order_completed:
|
81
|
+
self._print_result()
|
82
|
+
self.__response_listener.stop_waiting()
|
83
|
+
|
84
|
+
def _print_result_canceled(self, order_id, trades, closed_trades):
|
85
|
+
if len(trades) > 0:
|
86
|
+
self._print_trades(trades, order_id)
|
87
|
+
self._print_closed_trades(closed_trades, order_id)
|
88
|
+
print("A part of the order has been canceled. Amount = {0}".format(
|
89
|
+
self.__order_monitor.reject_amount))
|
90
|
+
else:
|
91
|
+
print("The order: OrderID = {0} has been canceled".format(
|
92
|
+
order_id))
|
93
|
+
print("The cancel amount = {0}".format(
|
94
|
+
self.__order_monitor.reject_amount))
|
95
|
+
|
96
|
+
def _print_result_fully_rejected(self, order_id, trades, closed_trades):
|
97
|
+
del trades, closed_trades
|
98
|
+
print("The order has been rejected. OrderID = {0}".format(
|
99
|
+
order_id))
|
100
|
+
print("The rejected amount = {0}".format(
|
101
|
+
self.__order_monitor.reject_amount))
|
102
|
+
print("Rejection cause: {0}".format(
|
103
|
+
self.__order_monitor.reject_message))
|
104
|
+
|
105
|
+
def _print_result_partial_rejected(self, order_id, trades, closed_trades):
|
106
|
+
self._print_trades(trades, order_id)
|
107
|
+
self._print_closed_trades(closed_trades, order_id)
|
108
|
+
print("A part of the order has been rejected. Amount = {0}".format(
|
109
|
+
self.__order_monitor.reject_amount))
|
110
|
+
print("Rejection cause: {0} ".format(
|
111
|
+
self.__order_monitor.reject_message))
|
112
|
+
|
113
|
+
def _print_result_executed(self, order_id, trades, closed_trades):
|
114
|
+
self._print_trades(trades, order_id)
|
115
|
+
self._print_closed_trades(closed_trades, order_id)
|
116
|
+
|
117
|
+
def _print_result(self):
|
118
|
+
if self.__order_monitor is not None:
|
119
|
+
result = self.__order_monitor.result
|
120
|
+
order = self.__order_monitor.order_row
|
121
|
+
order_id = order.order_id
|
122
|
+
trades = self.__order_monitor.trade_rows
|
123
|
+
closed_trades = self.__order_monitor.closed_trade_rows
|
124
|
+
|
125
|
+
print_result_func = {
|
126
|
+
OrderMonitor.ExecutionResult.CANCELED: self._print_result_canceled,
|
127
|
+
OrderMonitor.ExecutionResult.FULLY_REJECTED: self._print_result_fully_rejected,
|
128
|
+
OrderMonitor.ExecutionResult.PARTIAL_REJECTED: self._print_result_partial_rejected,
|
129
|
+
OrderMonitor.ExecutionResult.EXECUTED: self._print_result_executed
|
130
|
+
}
|
131
|
+
try:
|
132
|
+
print_result_func[result](order_id, trades, closed_trades)
|
133
|
+
except KeyError:
|
134
|
+
pass
|
135
|
+
except Exception as e:
|
136
|
+
print("Exception: {0}\n".format(e))
|
137
|
+
print(traceback.format_exc())
|
138
|
+
|
139
|
+
@staticmethod
|
140
|
+
def _print_trades(trades, order_id):
|
141
|
+
if len(trades) == 0:
|
142
|
+
return
|
143
|
+
print(
|
144
|
+
"For the order: OrderID = {0} the following positions have been opened:".format(
|
145
|
+
order_id))
|
146
|
+
|
147
|
+
for trade in trades:
|
148
|
+
trade_id = trade.trade_id
|
149
|
+
amount = trade.amount
|
150
|
+
rate = trade.open_rate
|
151
|
+
print(
|
152
|
+
"Trade ID: {0:s}; Amount: {1:d}; Rate: {2:.5f}".format(trade_id,
|
153
|
+
amount,
|
154
|
+
rate))
|
155
|
+
|
156
|
+
@staticmethod
|
157
|
+
def _print_closed_trades(closed_trades, order_id):
|
158
|
+
if len(closed_trades) == 0:
|
159
|
+
return
|
160
|
+
print(
|
161
|
+
"For the order: OrderID = {0} the following positions have been closed: ".format(
|
162
|
+
order_id))
|
163
|
+
|
164
|
+
for closed_trade in closed_trades:
|
165
|
+
trade_id = closed_trade.trade_id
|
166
|
+
amount = closed_trade.amount
|
167
|
+
rate = closed_trade.close_rate
|
168
|
+
print(
|
169
|
+
"Closed Trade ID: {0:s}; Amount: {1:d}; Closed Rate: {2:.5f}".format(
|
170
|
+
trade_id, amount, rate))
|
171
|
+
|
172
|
+
def subscribe_events(self):
|
173
|
+
orders_table = self._fx.get_table(ForexConnect.ORDERS)
|
174
|
+
orders_table_listener = Common.subscribe_table_updates(orders_table,
|
175
|
+
on_add_callback=self._on_added_orders,
|
176
|
+
on_delete_callback=self._on_deleted_orders)
|
177
|
+
self._listeners.append(orders_table_listener)
|
178
|
+
|
179
|
+
trades_table = self._fx.get_table(ForexConnect.TRADES)
|
180
|
+
trades_table_listener = Common.subscribe_table_updates(trades_table,
|
181
|
+
on_add_callback=self._on_added_trades)
|
182
|
+
self._listeners.append(trades_table_listener)
|
183
|
+
|
184
|
+
messages_table = self._fx.get_table(ForexConnect.MESSAGES)
|
185
|
+
messages_table_listener = Common.subscribe_table_updates(messages_table,
|
186
|
+
on_add_callback=self._on_added_messages)
|
187
|
+
self._listeners.append(messages_table_listener)
|
188
|
+
|
189
|
+
closed_trades_table = self._fx.get_table(ForexConnect.CLOSED_TRADES)
|
190
|
+
closed_trades_table_listener = Common.subscribe_table_updates(closed_trades_table,
|
191
|
+
on_add_callback=self._on_added_closed_trades)
|
192
|
+
self._listeners.append(closed_trades_table_listener)
|
193
|
+
|
194
|
+
def unsubscribe_events(self):
|
195
|
+
for listener in self._listeners:
|
196
|
+
listener.unsubscribe()
|
197
|
+
self._listeners = []
|