├── .gitignore
├── MQL5
├── Experts
│ └── Advisors
│ │ ├── RestApi.ex5
│ │ └── RestApi.mq5
├── Include
│ ├── RestApi.mqh
│ └── json.mqh
└── Libraries
│ ├── LIBEAY32.dll
│ ├── SSLEAY32.dll
│ ├── boost_date_time-vc141-mt-x64-1_69.dll
│ ├── cpprest_2_10.dll
│ ├── docs.html
│ ├── mt5-rest.dll
│ ├── swagger.json
│ └── zlib1.dll
├── README.md
├── mt5-rest.sln
└── mt5-rest
├── basic_controller.cpp
├── basic_controller.hpp
├── controller.hpp
├── main.cpp
├── microsvc_controller.cpp
├── microsvc_controller.hpp
├── mt5-rest.def
├── mt5-rest.vcxproj
├── mt5-rest.vcxproj.user
├── network_utils.cpp
├── network_utils.hpp
├── safe_map.cpp
├── safe_map.hpp
├── safe_vector.cpp
├── safe_vector.hpp
├── session_manager.cpp
├── session_manager.hpp
├── std_micro_service.hpp
├── stdafx.cpp
├── stdafx.h
├── targetver.h
├── types.hpp
└── usr_interrupt_handler.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | x64
2 | packages
3 | .vs
4 | *.suo
5 | *.user
6 | *.userosscache
7 | ipch/
8 | *.aps
9 | *.ncb
10 | *.opendb
11 | *.opensdf
12 | *.sdf
13 | *.cachefile
14 | *.VC.db
15 |
--------------------------------------------------------------------------------
/MQL5/Experts/Advisors/RestApi.ex5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Experts/Advisors/RestApi.ex5
--------------------------------------------------------------------------------
/MQL5/Experts/Advisors/RestApi.mq5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Experts/Advisors/RestApi.mq5
--------------------------------------------------------------------------------
/MQL5/Include/RestApi.mqh:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Include/RestApi.mqh
--------------------------------------------------------------------------------
/MQL5/Include/json.mqh:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Include/json.mqh
--------------------------------------------------------------------------------
/MQL5/Libraries/LIBEAY32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/LIBEAY32.dll
--------------------------------------------------------------------------------
/MQL5/Libraries/SSLEAY32.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/SSLEAY32.dll
--------------------------------------------------------------------------------
/MQL5/Libraries/boost_date_time-vc141-mt-x64-1_69.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/boost_date_time-vc141-mt-x64-1_69.dll
--------------------------------------------------------------------------------
/MQL5/Libraries/cpprest_2_10.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/cpprest_2_10.dll
--------------------------------------------------------------------------------
/MQL5/Libraries/docs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/MQL5/Libraries/mt5-rest.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/mt5-rest.dll
--------------------------------------------------------------------------------
/MQL5/Libraries/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger" : "2.0",
3 | "info" : {
4 | "description" : "Turns Metatrader5 into REST API server",
5 | "version" : "1.0.0",
6 | "title" : "Metatrader5 REST API",
7 | "contact" : {
8 | "email" : "mikhail@dev4traders.com"
9 | }
10 | },
11 | "tags" : [ {
12 | "name" : "symbol",
13 | "description" : "Market symbol"
14 | },{
15 | "name" : "account",
16 | "description" : "MT5 account"
17 | }, {
18 | "name" : "trade",
19 | "description" : "Trade"
20 | }, {
21 | "name" : "position",
22 | "description" : "Position"
23 | }, {
24 | "name" : "deal",
25 | "description" : "Deal/Transaction"
26 | }, {
27 | "name" : "order",
28 | "description" : "Order"
29 | } ],
30 | "paths" : {
31 | "/info" : {
32 | "get" : {
33 | "tags" : [ "account" ],
34 | "security":[{"apiKey":[]}],
35 | "summary" : "get account info",
36 | "operationId" : "info",
37 | "description" : "Loads Account balance, broker, name, etc..\n",
38 | "produces" : [ "application/json" ],
39 | "responses" : {
40 | "200" : {
41 | "description" : "info in json",
42 | "schema" : {
43 | "$ref" : "#/definitions/AccountItem"
44 | }
45 | },
46 | "400" : {
47 | "description" : "bad input parameter"
48 | }
49 | }
50 | }
51 | },
52 | "/balance" : {
53 | "get" : {
54 | "tags" : [ "account" ],
55 | "security":[{"apiKey":[]}],
56 | "summary" : "get account balance",
57 | "operationId" : "balance",
58 | "description" : "Loads Account balance, margin, free margin\n",
59 | "produces" : [ "application/json" ],
60 | "responses" : {
61 | "200" : {
62 | "description" : "balance info in json",
63 | "schema" : {
64 | "$ref" : "#/definitions/BalanceItem"
65 | }
66 | },
67 | "400" : {
68 | "description" : "bad input parameter"
69 | }
70 | }
71 | }
72 | },
73 | "/symbols/{name}" : {
74 | "get" : {
75 | "tags" : [ "symbol" ],
76 | "parameters":[{"name":"name","in":"path","required":true,"type":"string"}],
77 | "security":[{"apiKey":[]}],
78 | "summary" : "get symbol info",
79 | "operationId" : "symbol",
80 | "description" : "Loads Symbol info by name\n",
81 | "produces" : [ "application/json" ],
82 | "responses" : {
83 | "200" : {
84 | "description" : "bid/ask in json",
85 | "schema" : {
86 | "$ref" : "#/definitions/Symbol"
87 | }
88 | },
89 | "400" : {
90 | "description" : "bad input parameter"
91 | }
92 | }
93 | }
94 | },
95 | "/orders" : {
96 | "get" : {
97 | "tags" : [ "order" ],
98 | "security":[{"apiKey":[]}],
99 | "summary" : "get account orders",
100 | "operationId" : "orders",
101 | "description" : "Loads Account Orders\n",
102 | "produces" : [ "application/json" ],
103 | "responses" : {
104 | "200" : {
105 | "description" : "orders array in json",
106 | "schema" : {
107 | "$ref" : "#/definitions/ArrayOfOrders"
108 | }
109 | },
110 | "400" : {
111 | "description" : "bad input parameter"
112 | }
113 | }
114 | }
115 | },
116 | "/orders/{id}" : {
117 | "get" : {
118 | "tags" : [ "order" ],
119 | "parameters":[{"name":"id","in":"path","required":true,"type":"string"}],
120 | "security":[{"apiKey":[]}],
121 | "summary" : "get account order",
122 | "operationId" : "order",
123 | "description" : "Loads Account Order by id\n",
124 | "produces" : [ "application/json" ],
125 | "responses" : {
126 | "200" : {
127 | "description" : "order in json",
128 | "schema" : {
129 | "$ref" : "#/definitions/Order"
130 | }
131 | },
132 | "400" : {
133 | "description" : "bad input parameter"
134 | }
135 | }
136 | }
137 | },
138 | "/history" : {
139 | "get" : {
140 | "tags" : [ "order" ],
141 | "security":[{"apiKey":[]}],
142 | "summary" : "get account orders history",
143 | "operationId" : "orders_history",
144 | "description" : "Loads Account Orders History\n",
145 | "produces" : [ "application/json" ],
146 | "responses" : {
147 | "200" : {
148 | "description" : "orders array in json",
149 | "schema" : {
150 | "$ref" : "#/definitions/ArrayOfOrdersHistory"
151 | }
152 | },
153 | "400" : {
154 | "description" : "bad input parameter"
155 | }
156 | }
157 | }
158 | },
159 | "/history/{id}" : {
160 | "get" : {
161 | "tags" : [ "order" ],
162 | "parameters":[{"name":"id","in":"path","required":true,"type":"string"}],
163 | "security":[{"apiKey":[]}],
164 | "summary" : "get account order from history",
165 | "operationId" : "order_history",
166 | "description" : "Loads Account Order from History by id\n",
167 | "produces" : [ "application/json" ],
168 | "responses" : {
169 | "200" : {
170 | "description" : "order in json",
171 | "schema" : {
172 | "$ref" : "#/definitions/OrderHistory"
173 | }
174 | },
175 | "400" : {
176 | "description" : "bad input parameter"
177 | }
178 | }
179 | }
180 | },
181 | "/deals?offset={offset}&limit={limit}" : {
182 | "get" : {
183 | "tags" : [ "deal" ],
184 | "security":[{"apiKey":[]}],
185 | "summary" : "get account deals",
186 | "operationId" : "dealrequest",
187 | "description" : "Loads Account Deals\n",
188 | "produces" : [ "application/json" ],
189 | "parameters" : [
190 | {
191 | "in" : "query",
192 | "name" : "offset",
193 | "required" : true,
194 | "schema" : {
195 | "type" : "string"
196 | }
197 | },
198 | {
199 | "in" : "query",
200 | "name" : "limit",
201 | "required" : true,
202 | "schema" : {
203 | "type" : "string"
204 | }
205 | } ],
206 | "responses" : {
207 | "200" : {
208 | "description" : "deals array in json",
209 | "schema" : {
210 | "$ref" : "#/definitions/ArrayOfDeals"
211 | }
212 | },
213 | "400" : {
214 | "description" : "bad input parameter"
215 | }
216 | }
217 | }
218 | },
219 | "/deals/{id}" : {
220 | "get" : {
221 | "tags" : [ "deal" ],
222 | "parameters":[{"name":"id","in":"path","required":true,"type":"string"}],
223 | "security":[{"apiKey":[]}],
224 | "summary" : "get account deals",
225 | "operationId" : "deals",
226 | "description" : "Loads Account Deals by id\n",
227 | "produces" : [ "application/json" ],
228 | "responses" : {
229 | "200" : {
230 | "description" : "Deal in json",
231 | "schema" : {
232 | "$ref" : "#/definitions/Deal"
233 | }
234 | },
235 | "400" : {
236 | "description" : "bad input parameter"
237 | }
238 | }
239 | }
240 | },
241 | "/positions" : {
242 | "get" : {
243 | "tags" : [ "position" ],
244 | "security":[{"apiKey":[]}],
245 | "summary" : "get account positions",
246 | "operationId" : "positions",
247 | "description" : "Loads Account Positions\n",
248 | "produces" : [ "application/json" ],
249 | "responses" : {
250 | "200" : {
251 | "description" : "positions array in json",
252 | "schema" : {
253 | "$ref" : "#/definitions/ArrayOfPositions"
254 | }
255 | },
256 | "400" : {
257 | "description" : "bad input parameter"
258 | }
259 | }
260 | }
261 | },
262 | "/positions/{id}" : {
263 | "get" : {
264 | "tags" : [ "position" ],
265 | "parameters":[{"name":"id","in":"path","required":true,"type":"string"}],
266 | "security":[{"apiKey":[]}],
267 | "summary" : "get account position",
268 | "operationId" : "position",
269 | "description" : "Loads Account Position by id\n",
270 | "produces" : [ "application/json" ],
271 | "responses" : {
272 | "200" : {
273 | "description" : "position in json",
274 | "schema" : {
275 | "$ref" : "#/definitions/Position"
276 | }
277 | },
278 | "400" : {
279 | "description" : "bad input parameter"
280 | }
281 | }
282 | }
283 | },
284 | "/trade" : {
285 | "post" : {
286 | "tags" : [ "trade" ],
287 | "security":[{"apiKey":[]}],
288 | "summary" : "Send Order",
289 | "produces" : [ "application/json" ],
290 | "consumes" : [ "application/json" ],
291 | "parameters" : [ {
292 | "in" : "body",
293 | "name" : "body",
294 | "description" : "{\"actionType\": \"ORDER_TYPE_BUY\" }",
295 | "required" : true,
296 | "schema" : {
297 | "$ref" : "#/definitions/TradeInput"
298 | }
299 | } ],
300 | "responses" : {
301 | "200" : {
302 | "description" : "successful operation",
303 | "schema" : {
304 | "$ref" : "#/definitions/TradeReturn"
305 | }
306 | },
307 | "400" : {
308 | "description" : "bad input parameter"
309 | }
310 | }
311 | }
312 | }
313 | },
314 | "securityDefinitions": {
315 | "apiKey": {
316 | "type": "apiKey",
317 | "in": "header",
318 | "name": "Authorization"
319 | }
320 | },
321 | "definitions" : {
322 | "TradeInput" : {
323 | "type" : "object",
324 | "properties" : {
325 | "id" : {
326 | "type" : "integer",
327 | "example" : 1
328 | },
329 | "symbol" : {
330 | "type" : "string",
331 | "example" : "EURUSD"
332 | },
333 | "actionType" : {
334 | "type" : "string",
335 | "enum" : [ "ORDER_TYPE_SELL", "ORDER_TYPE_BUY", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "POSITION_MODIFY", "POSITION_PARTIAL", "POSITION_CLOSE_ID", "POSITION_CLOSE_SYMBOL", "ORDER_MODIFY", "ORDER_CANCEL" ]
336 | },
337 | "volume" : {
338 | "type" : "number",
339 | "example" : 0.1
340 | },
341 | "price" : {
342 | "type" : "number",
343 | "example" : 1.12121
344 | },
345 | "stoploss" : {
346 | "type" : "number",
347 | "example" : 1.12121
348 | },
349 | "takeprofit" : {
350 | "type" : "number",
351 | "example" : 1.12121
352 | }
353 | }
354 | },
355 | "TradeReturn" : {
356 | "type" : "object",
357 | "properties" : {
358 | "error" : {
359 | "type" : "integer",
360 | "example" : 6533
361 | },
362 | "description" : {
363 | "type" : "string",
364 | "example" : "ERR_WRONG_ACTION"
365 | }
366 | }
367 | },
368 | "Symbol" : {
369 | "type" : "object",
370 | "properties" : {
371 | "ask" : {
372 | "type" : "number",
373 | "example" : 1.22323
374 | },
375 | "bid" : {
376 | "type" : "number",
377 | "example" : 1.34341
378 | }
379 | }
380 | },
381 | "AccountItem" : {
382 | "type" : "object",
383 | "properties" : {
384 | "currency" : {
385 | "type" : "string",
386 | "example" : "USD"
387 | },
388 | "server" : {
389 | "type" : "string",
390 | "example" : "MetaQuotes-Demo"
391 | },
392 | "broker" : {
393 | "type" : "string",
394 | "example" : "MetaQuotes Software Corp."
395 | },
396 | "balance" : {
397 | "type" : "number",
398 | "example" : 1000.0
399 | },
400 | "equity" : {
401 | "type" : "number",
402 | "example" : 1000.0
403 | },
404 | "margin" : {
405 | "type" : "number",
406 | "example" : 1000.0
407 | },
408 | "margin_free" : {
409 | "type" : "number",
410 | "example" : 1000.0
411 | },
412 | "margin_level" : {
413 | "type" : "number",
414 | "example" : 1000.0
415 | },
416 | "orders_total" : {
417 | "type" : "integer",
418 | "example" : 2
419 | },
420 | "positions_total" : {
421 | "type" : "integer",
422 | "example" : 2
423 | }
424 | }
425 | },
426 | "BalanceItem" : {
427 | "type" : "object",
428 | "properties" : {
429 | "balance" : {
430 | "type" : "number",
431 | "example" : 1000.0
432 | },
433 | "equity" : {
434 | "type" : "number",
435 | "example" : 1000.0
436 | },
437 | "margin" : {
438 | "type" : "number",
439 | "example" : 1000.0
440 | },
441 | "margin_free" : {
442 | "type" : "number",
443 | "example" : 1000.0
444 | }
445 | }
446 | },
447 | "ArrayOfOrders" : {
448 | "type" : "array",
449 | "items" : {
450 | "type" : "object",
451 | "properties" : {
452 | "id" : {
453 | "type" : "integer",
454 | "example" : 1
455 | },
456 | "type" : {
457 | "type" : "string",
458 | "enum" : [ "ORDER_TYPE_SELL", "ORDER_TYPE_BUY", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "POSITION_MODIFY", "POSITION_PARTIAL", "POSITION_CLOSE_ID", "POSITION_CLOSE_SYMBOL", "ORDER_MODIFY", "ORDER_CANCEL" ]
459 | },
460 | "magic" : {
461 | "type" : "integer",
462 | "example" : 0
463 | },
464 | "symbol" : {
465 | "type" : "string",
466 | "example" : "EURUSD"
467 | },
468 | "time_setup" : {
469 | "type" : "string",
470 | "example" : "2019-06-05T19:35:50.000Z"
471 | },
472 | "open" : {
473 | "type" : "number",
474 | "example" : 1.65656
475 | },
476 | "stoploss" : {
477 | "type" : "number",
478 | "example" : 1.65656
479 | },
480 | "takeprofit" : {
481 | "type" : "number",
482 | "example" : 1.65656
483 | },
484 | "volume" : {
485 | "type" : "number",
486 | "example" : 0.1
487 | }
488 | }
489 | }
490 | },
491 | "Order" : {
492 | "type" : "object",
493 | "properties" : {
494 | "id" : {
495 | "type" : "integer",
496 | "example" : 1
497 | },
498 | "type" : {
499 | "type" : "string",
500 | "enum" : [ "ORDER_TYPE_SELL", "ORDER_TYPE_BUY", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "POSITION_MODIFY", "POSITION_PARTIAL", "POSITION_CLOSE_ID", "POSITION_CLOSE_SYMBOL", "ORDER_MODIFY", "ORDER_CANCEL" ]
501 | },
502 | "magic" : {
503 | "type" : "integer",
504 | "example" : 0
505 | },
506 | "symbol" : {
507 | "type" : "string",
508 | "example" : "EURUSD"
509 | },
510 | "time_setup" : {
511 | "type" : "string",
512 | "example" : "2019-06-05T19:35:50.000Z"
513 | },
514 | "open" : {
515 | "type" : "number",
516 | "example" : 1.65656
517 | },
518 | "stoploss" : {
519 | "type" : "number",
520 | "example" : 1.65656
521 | },
522 | "takeprofit" : {
523 | "type" : "number",
524 | "example" : 1.65656
525 | },
526 | "volume" : {
527 | "type" : "number",
528 | "example" : 0.1
529 | }
530 | }
531 | },
532 | "ArrayOfOrdersHistory" : {
533 | "type" : "array",
534 | "items" : {
535 | "type" : "object",
536 | "properties" : {
537 | "id" : {
538 | "type" : "integer",
539 | "example" : 1
540 | },
541 | "type" : {
542 | "type" : "string",
543 | "enum" : [ "ORDER_TYPE_SELL", "ORDER_TYPE_BUY", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "POSITION_MODIFY", "POSITION_PARTIAL", "POSITION_CLOSE_ID", "POSITION_CLOSE_SYMBOL", "ORDER_MODIFY", "ORDER_CANCEL" ]
544 | },
545 | "state" : {
546 | "type" : "string",
547 | "enum" : [ "ORDER_STATE_STARTED", "ORDER_STATE_PLACED", "ORDER_STATE_CANCELED", "ORDER_STATE_PARTIAL", "ORDER_STATE_FILLED", "ORDER_STATE_REJECTED", "ORDER_STATE_EXPIRED", "ORDER_STATE_REQUEST_ADD", "ORDER_STATE_REQUEST_MODIFY", "ORDER_STATE_REQUEST_CANCEL" ]
548 | },
549 | "magic" : {
550 | "type" : "integer",
551 | "example" : 0
552 | },
553 | "symbol" : {
554 | "type" : "string",
555 | "example" : "EURUSD"
556 | },
557 | "time_setup" : {
558 | "type" : "string",
559 | "example" : "2019-06-05T19:35:50.000Z"
560 | },
561 | "time_done" : {
562 | "type" : "string",
563 | "example" : "2019-06-05T19:35:50.000Z"
564 | },
565 | "open" : {
566 | "type" : "number",
567 | "example" : 1.65656
568 | },
569 | "stoploss" : {
570 | "type" : "number",
571 | "example" : 1.65656
572 | },
573 | "takeprofit" : {
574 | "type" : "number",
575 | "example" : 1.65656
576 | },
577 | "volume" : {
578 | "type" : "number",
579 | "example" : 0.1
580 | }
581 | }
582 | }
583 | },
584 | "OrderHistory" : {
585 | "type" : "object",
586 | "properties" : {
587 | "id" : {
588 | "type" : "integer",
589 | "example" : 1
590 | },
591 | "type" : {
592 | "type" : "string",
593 | "enum" : [ "ORDER_TYPE_SELL", "ORDER_TYPE_BUY", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "POSITION_MODIFY", "POSITION_PARTIAL", "POSITION_CLOSE_ID", "POSITION_CLOSE_SYMBOL", "ORDER_MODIFY", "ORDER_CANCEL" ]
594 | },
595 | "state" : {
596 | "type" : "string",
597 | "enum" : [ "ORDER_STATE_STARTED", "ORDER_STATE_PLACED", "ORDER_STATE_CANCELED", "ORDER_STATE_PARTIAL", "ORDER_STATE_FILLED", "ORDER_STATE_REJECTED", "ORDER_STATE_EXPIRED", "ORDER_STATE_REQUEST_ADD", "ORDER_STATE_REQUEST_MODIFY", "ORDER_STATE_REQUEST_CANCEL" ]
598 | },
599 | "magic" : {
600 | "type" : "integer",
601 | "example" : 0
602 | },
603 | "symbol" : {
604 | "type" : "string",
605 | "example" : "EURUSD"
606 | },
607 | "time_setup" : {
608 | "type" : "string",
609 | "example" : "2019-06-05T19:35:50.000Z"
610 | },
611 | "time_done" : {
612 | "type" : "string",
613 | "example" : "2019-06-05T19:35:50.000Z"
614 | },
615 | "open" : {
616 | "type" : "number",
617 | "example" : 1.65656
618 | },
619 | "stoploss" : {
620 | "type" : "number",
621 | "example" : 1.65656
622 | },
623 | "takeprofit" : {
624 | "type" : "number",
625 | "example" : 1.65656
626 | },
627 | "volume" : {
628 | "type" : "number",
629 | "example" : 0.1
630 | }
631 | }
632 | },
633 | "ArrayOfDeals" : {
634 | "type" : "array",
635 | "items" : {
636 | "type" : "object",
637 | "properties" : {
638 | "id" : {
639 | "type" : "integer",
640 | "example" : 1
641 | },
642 | "price" : {
643 | "type" : "number",
644 | "example" : 1.65656
645 | },
646 | "commission" : {
647 | "type" : "number",
648 | "example" : 0.1
649 | },
650 | "time" : {
651 | "type" : "string",
652 | "example" : "2019-06-05T19:35:50.000Z"
653 | },
654 | "symbol" : {
655 | "type" : "string",
656 | "example" : "EURUSD"
657 | },
658 | "type" : {
659 | "type" : "string",
660 | "enum": [ "DEAL_TYPE_BUY", "DEAL_TYPE_SELL", "DEAL_TYPE_BALANCE", "DEAL_TYPE_CREDIT", "DEAL_TYPE_CHARGE", "DEAL_TYPE_CORRECTION", "DEAL_TYPE_BONUS", "DEAL_TYPE_COMMISSION", "DEAL_TYPE_COMMISSION_DAILY", "DEAL_TYPE_COMMISSION_MONTHLY", "DEAL_TYPE_COMMISSION_AGENT_DAILY", "DEAL_TYPE_COMMISSION_AGENT_MONTHLY", "DEAL_TYPE_INTEREST", "DEAL_TYPE_BUY_CANCELED", "DEAL_TYPE_SELL_CANCELED", "DEAL_DIVIDEND", "DEAL_DIVIDEND_FRANKED", "DEAL_TAX" ]
661 | },
662 | "profit" : {
663 | "type" : "number",
664 | "example" : 0.1
665 | },
666 | "volume" : {
667 | "type" : "number",
668 | "example" : 0.1
669 | },
670 | "position_id" : {
671 | "type" : "integer",
672 | "example" : 1
673 | },
674 | "order_id" : {
675 | "type" : "integer",
676 | "example" : 1
677 | }
678 | }
679 | }
680 | },
681 | "Deal" : {
682 | "type" : "object",
683 | "properties" : {
684 | "id" : {
685 | "type" : "integer",
686 | "example" : 1
687 | },
688 | "price" : {
689 | "type" : "number",
690 | "example" : 1.65656
691 | },
692 | "commission" : {
693 | "type" : "number",
694 | "example" : 0.1
695 | },
696 | "time" : {
697 | "type" : "string",
698 | "example" : "2019-06-05T19:35:50.000Z"
699 | },
700 | "symbol" : {
701 | "type" : "string",
702 | "example" : "EURUSD"
703 | },
704 | "type" : {
705 | "type" : "string",
706 | "enum": [ "DEAL_TYPE_BUY", "DEAL_TYPE_SELL", "DEAL_TYPE_BALANCE", "DEAL_TYPE_CREDIT", "DEAL_TYPE_CHARGE", "DEAL_TYPE_CORRECTION", "DEAL_TYPE_BONUS", "DEAL_TYPE_COMMISSION", "DEAL_TYPE_COMMISSION_DAILY", "DEAL_TYPE_COMMISSION_MONTHLY", "DEAL_TYPE_COMMISSION_AGENT_DAILY", "DEAL_TYPE_COMMISSION_AGENT_MONTHLY", "DEAL_TYPE_INTEREST", "DEAL_TYPE_BUY_CANCELED", "DEAL_TYPE_SELL_CANCELED", "DEAL_DIVIDEND", "DEAL_DIVIDEND_FRANKED", "DEAL_TAX" ]
707 | },
708 | "profit" : {
709 | "type" : "number",
710 | "example" : 0.1
711 | },
712 | "volume" : {
713 | "type" : "number",
714 | "example" : 0.1
715 | },
716 | "position_id" : {
717 | "type" : "integer",
718 | "example" : 1
719 | },
720 | "order_id" : {
721 | "type" : "integer",
722 | "example" : 1
723 | }
724 | }
725 | },
726 | "Position" : {
727 | "type" : "object",
728 | "properties" : {
729 | "id" : {
730 | "type" : "integer",
731 | "example" : 1
732 | },
733 | "type" : {
734 | "type" : "string",
735 | "enum": ["POSITION_TYPE_BUY", "POSITION_TYPE_SELL"]
736 | },
737 | "magic" : {
738 | "type" : "integer",
739 | "example" : 0
740 | },
741 | "symbol" : {
742 | "type" : "string",
743 | "example" : "EURUSD"
744 | },
745 | "time_setup" : {
746 | "type" : "string",
747 | "example" : "2019-06-05T19:35:50.000Z"
748 | },
749 | "open" : {
750 | "type" : "number",
751 | "example" : 1.65656
752 | },
753 | "price_current" : {
754 | "type" : "number",
755 | "example" : 1.65656
756 | },
757 | "stoploss" : {
758 | "type" : "number",
759 | "example" : 1.65656
760 | },
761 | "takeprofit" : {
762 | "type" : "number",
763 | "example" : 1.65656
764 | },
765 | "volume" : {
766 | "type" : "number",
767 | "example" : 0.1
768 | }
769 | }
770 | },
771 | "ArrayOfPositions" : {
772 | "type" : "array",
773 | "items" : {
774 | "type" : "object",
775 | "properties" : {
776 | "id" : {
777 | "type" : "integer",
778 | "example" : 1
779 | },
780 | "type" : {
781 | "type" : "string",
782 | "enum": ["POSITION_TYPE_BUY", "POSITION_TYPE_SELL"]
783 | },
784 | "magic" : {
785 | "type" : "integer",
786 | "example" : 0
787 | },
788 | "symbol" : {
789 | "type" : "string",
790 | "example" : "EURUSD"
791 | },
792 | "time_setup" : {
793 | "type" : "string",
794 | "example" : "2019-06-05T19:35:50.000Z"
795 | },
796 | "open" : {
797 | "type" : "number",
798 | "example" : 1.65656
799 | },
800 | "price_current" : {
801 | "type" : "number",
802 | "example" : 1.65656
803 | },
804 | "stoploss" : {
805 | "type" : "number",
806 | "example" : 1.65656
807 | },
808 | "takeprofit" : {
809 | "type" : "number",
810 | "example" : 1.65656
811 | },
812 | "volume" : {
813 | "type" : "number",
814 | "example" : 0.1
815 | }
816 | }
817 | }
818 | }
819 | },
820 | "host" : "localhost:6542",
821 | "schemes" : [ "http" ]
822 | }
--------------------------------------------------------------------------------
/MQL5/Libraries/zlib1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/MQL5/Libraries/zlib1.dll
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Turns Metatrader5 into REST API server.
2 |
3 | Note: make sure you have VSredist: https://www.microsoft.com/en-ie/download/details.aspx?id=48145
4 |
5 | time_setup, time is MQL5 datetime, check docs at: https://www.mql5.com/en/docs/basis/types/integer/datetime
6 |
7 | check "error" for /trade command on that page: https://www.mql5.com/en/docs/constants/errorswarnings/enum_trade_return_codes
8 |
9 | for enums check that page: https://www.mql5.com/en/docs/constants
10 |
11 | ## how to compile
12 | clone repo to any folder on our PC.
13 |
14 | Copy MQL5 folder to Data folder of mt5 (how to get where is Data folder is localted, open mt5, click File->Open Data Folder or just click Ctrl+Shft+D).
15 | In MT5 Navigator (to show Naavigator click Ctrl+N) select Expert Advisors->Advisors. Right click on RestApi, select Modify. Editor will be opened, press F7 to compile. Done.
16 |
17 | To compile C++ dll use Visual Studio 2017. Use .sln file to open solution in VS, select Realease x64 and press Ctrl+Shft+b to build dll file. Output is located under /x64 folder.
18 |
19 |
20 | ## Commands
21 |
22 | POST /sub - setup web hook. options: callback_url and callback_format (json or x-form)
23 |
24 | GET /symbols/{name} - get symbol informaiton including ask/bid prices.
25 |
26 | GET /info - get account details, number of orders, number of positions
27 |
28 | GET /positions - returns list of positions
29 |
30 | GET /positions/{id} - return position by id
31 |
32 | GET /deals?offset={offset}&limit={limit} - returns list of deals/transactions
33 |
34 | GET /deals/{id} - return deal by id
35 |
36 | GET /orders - returns list of orders
37 |
38 | GET /orders/{id} - return order by id
39 |
40 | GET /history - returns list of history orders
41 |
42 | GET /history/{id} - return order history by id
43 |
44 | POST /trade - open position, details in POST body
45 |
46 | # Example of POST body for trade command
47 |
48 | ## Open Buy
49 | ```json
50 | {
51 | "symbol":"EURUSD",
52 | "actionType": "ORDER_TYPE_BUY",
53 | "volume": 0.1,
54 | "stoploss": 1.3455,
55 | "takeprofit": 1.33333,
56 | "comment": "test buy"
57 | }
58 | ```
59 |
60 | ## Open Sell
61 | ```json
62 | {
63 | "symbol":"EURUSD",
64 | "actionType": "ORDER_TYPE_SELL",
65 | "volume": 0.1,
66 | "stoploss": 1.3455,
67 | "takeprofit": 1.33333,
68 | "comment": "test buy"
69 | }
70 | ```
71 |
72 | ## Open Buy Limit
73 | ```json
74 | {
75 | "symbol":"EURUSD",
76 | "actionType": "ORDER_TYPE_BUY_LIMIT",
77 | "price": 1.4444,
78 | "volume": 0.1,
79 | "stoploss": 1.3455,
80 | "takeprofit": 1.33333,
81 | "comment": "test buy limit"
82 | }
83 | ```
84 |
85 | ## Open Sell Limit
86 | ```json
87 | {
88 | "symbol":"EURUSD",
89 | "actionType": "ORDER_TYPE_SELL_LIMIT",
90 | "price": 1.4444,
91 | "volume": 0.1,
92 | "stoploss": 1.3455,
93 | "takeprofit": 1.33333,
94 | "comment": "test sell limit"
95 | }
96 | ```
97 |
98 | ## Open Buy Stop
99 | ```json
100 | {
101 | "symbol":"EURUSD",
102 | "actionType":
103 | "ORDER_TYPE_BUY_STOP",
104 | "price": 1.4444,
105 | "volume": 0.1,
106 | "stoploss": 1.3455,
107 | "takeprofit": 1.33333,
108 | "comment": "test buy stop"
109 | }
110 | ```
111 |
112 | ## Open Sell Stop
113 | ```json
114 | {
115 | "symbol":"EURUSD",
116 | "actionType": "ORDER_TYPE_SELL_STOP",
117 | "price": 1.4444,
118 | "volume": 0.1,
119 | "stoploss": 1.3455,
120 | "takeprofit": 1.33333,
121 | "comment": "test sell stop"
122 | }
123 | ```
124 |
125 | ## Position Close by ID
126 | ```json
127 | { "actionType": "POSITION_CLOSE_ID", "id": 1212121 }
128 | ```
129 |
130 | ## Order Cancel
131 | ```json
132 | { "actionType": "ORDER_CANCEL", "id": 1212121 }
133 | ```
134 |
135 | ## Order Cancel
136 | ```json
137 | { "actionType": "POSITION_PARTIAL", "id": 1212121, "volume": 0.1 }
138 | ```
139 |
140 |
141 | # Examples of /trade output
142 | ```json
143 | {
144 | "error": 10018,
145 | "desription": "TRADE_RETCODE_MARKET_CLOSED",
146 | "order_id": 0,
147 | "volume": 0,
148 | "price": 0,
149 | "bid": 0,
150 | "ask": 0,
151 | "function": "CRestApi::tradingModule"
152 | }
153 | ```
154 | ```json
155 | {
156 | "error": 10009,
157 | "desription": "TRADE_RETCODE_DONE",
158 | "order_id": 405895526,
159 | "volume": 0.1,
160 | "price": 1.13047,
161 | "bid": 1.13038,
162 | "ask": 1.13047,
163 | "function": "CRestApi::tradingModule"
164 | }
165 | ```
166 |
--------------------------------------------------------------------------------
/mt5-rest.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mt4-rest", "mt5-rest\mt5-rest.vcxproj", "{7359E8DF-CF85-409D-833B-3B66F8DAC462}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Debug|x64.ActiveCfg = Debug|x64
17 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Debug|x64.Build.0 = Debug|x64
18 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Debug|x86.ActiveCfg = Debug|Win32
19 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Debug|x86.Build.0 = Debug|Win32
20 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Release|x64.ActiveCfg = Release|x64
21 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Release|x64.Build.0 = Release|x64
22 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Release|x86.ActiveCfg = Release|Win32
23 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {08964258-A61B-48D7-9E07-0818CDD29B8B}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/mt5-rest/basic_controller.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "network_utils.hpp" // -> these two lines must stay in this order, boost/cpprestsdk header include order breaks "U"
3 | #include "basic_controller.hpp" // -|
4 |
5 | namespace cfx {
6 | BasicController::BasicController() {
7 |
8 | }
9 |
10 | BasicController::~BasicController() {
11 |
12 | }
13 | void BasicController::setEndpoint(const utility::string_t & value) {
14 | uri endpointURI(value);
15 | uri_builder endpointBuilder;
16 |
17 | endpointBuilder.set_scheme(endpointURI.scheme());
18 |
19 | if (endpointURI.host() == U("host_auto_ip4")) {
20 | endpointBuilder.set_host(NetworkUtils::hostIP4());
21 | }
22 | else if (endpointURI.host() == U("host_auto_ip6")) {
23 | endpointBuilder.set_host(NetworkUtils::hostIP6());
24 | }
25 | else {
26 | endpointBuilder.set_host(endpointURI.host());
27 | }
28 | endpointBuilder.set_port(endpointURI.port());
29 | endpointBuilder.set_path(endpointURI.path());
30 |
31 | _listener = http_listener(endpointBuilder.to_uri());
32 | }
33 |
34 | utility::string_t BasicController::endpoint() const {
35 | return _listener.uri().to_string();
36 | }
37 |
38 | utility::string_t BasicController::getHostPort() const {
39 | return _listener.uri().host() + L":" + std::to_wstring( _listener.uri().port() );
40 | }
41 |
42 | pplx::task BasicController::accept() {
43 | initRestOpHandlers();
44 | return _listener.open();
45 | }
46 |
47 | pplx::task BasicController::shutdown() {
48 | return _listener.close();
49 | }
50 |
51 | std::vector BasicController::requestPath(const http_request & message) {
52 | auto relativePath = uri::decode(message.relative_uri().path());
53 | return uri::split_path(relativePath);
54 | }
55 |
56 | std::map BasicController::requestQueryParams(const http_request & message) {
57 | auto query = message.relative_uri().query();
58 | return uri::split_query(query);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/mt5-rest/basic_controller.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "controller.hpp"
7 |
8 | using namespace web;
9 | using namespace http::experimental::listener;
10 |
11 | namespace cfx {
12 | class BasicController {
13 | protected:
14 | http_listener _listener; // main micro service network endpoint
15 |
16 | public:
17 | BasicController();
18 | ~BasicController();
19 |
20 | void setEndpoint(const utility::string_t & value);
21 | utility::string_t endpoint() const;
22 | utility::string_t getHostPort() const;
23 | pplx::task accept();
24 | pplx::task shutdown();
25 |
26 | virtual void initRestOpHandlers() {
27 | /* had to be implemented by the child class */
28 | }
29 |
30 | std::vector requestPath(const http_request & message);
31 | std::map requestQueryParams(const http_request & message);
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/mt5-rest/controller.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Ivan Mejia on 12/03/16.
3 | //
4 | // MIT License
5 | //
6 | // Copyright (c) 2016 ivmeroLabs. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 |
15 | // The above copyright notice and this permission notice shall be included in all
16 | // copies or substantial portions of the Software.
17 |
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | // SOFTWARE.
25 | //
26 |
27 | #pragma once
28 |
29 | #include
30 |
31 | using namespace web;
32 | using namespace http;
33 |
34 | namespace cfx {
35 |
36 | /*!
37 | * Dispatcher class represents the basic interface for a
38 | * web serivce handler.
39 | */
40 | class Controller {
41 | public:
42 | virtual void handleGet(http_request message) = 0;
43 | virtual void handlePut(http_request message) = 0;
44 | virtual void handlePost(http_request message) = 0;
45 | virtual void handleDelete(http_request message) = 0;
46 | virtual void handlePatch(http_request messge) = 0;
47 | virtual void handleHead(http_request message) = 0;
48 | virtual void handleOptions(http_request message) = 0;
49 | virtual void handleTrace(http_request message) = 0;
50 | virtual void handleConnect(http_request message) = 0;
51 | virtual void handleMerge(http_request message) = 0;
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/mt5-rest/main.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikha-dev/mt5-rest/9f286fa1b51a8d9efbfe5e52af2b65cb92cea31b/mt5-rest/main.cpp
--------------------------------------------------------------------------------
/mt5-rest/microsvc_controller.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include "stdafx.h"
5 | #include "microsvc_controller.hpp"
6 | #include "types.hpp"
7 |
8 | #define CMD_DOCS L"docs"
9 | #define CMD_SUB L"sub"
10 | #define CMD_SWAGGER L"swagger.json"
11 | #define CMD_VERSION L"version"
12 |
13 | using namespace web;
14 | using namespace http;
15 |
16 | void MicroserviceController::initRestOpHandlers() {
17 | _listener.support(methods::GET, std::bind(&MicroserviceController::handleGet, this, std::placeholders::_1));
18 | _listener.support(methods::PUT, std::bind(&MicroserviceController::handlePut, this, std::placeholders::_1));
19 | _listener.support(methods::POST, std::bind(&MicroserviceController::handlePost, this, std::placeholders::_1));
20 | _listener.support(methods::DEL, std::bind(&MicroserviceController::handleDelete, this, std::placeholders::_1));
21 | _listener.support(methods::PATCH, std::bind(&MicroserviceController::handlePatch, this, std::placeholders::_1));
22 | _listener.support(methods::HEAD, std::bind(&MicroserviceController::handleHead, this, std::placeholders::_1));
23 |
24 | pushCommand(L"inited", endpoint());
25 | }
26 |
27 | void MicroserviceController::pushCommand(string_t command, string_t options) {
28 |
29 | web::json::value result = web::json::value::object();
30 |
31 | result[U("command")] = web::json::value::string(command);
32 | result[U("options")] = web::json::value::string(options);
33 |
34 | commands.push_back(ws2s(result.serialize()));
35 | }
36 |
37 | const char* MicroserviceController::getCommand() {
38 | string command;
39 |
40 | if (commands.size() < 1)
41 | return NULL;
42 |
43 | command.append(commands.back());
44 | commands.pop_back();
45 |
46 | return command.c_str();
47 | }
48 |
49 | int MicroserviceController::hasCommands() {
50 | return commands.size() > 0;
51 | }
52 |
53 |
54 | void MicroserviceController::setCommandResponse(const char* command, const char* response) {
55 | commandResponses[command] = response;
56 | }
57 |
58 | void MicroserviceController::setCallback(const char* url, const char* format) {
59 | callback_url.clear();
60 | callback_format.clear();
61 | callback_url.append(s2ws(url));
62 | callback_format.append(s2ws(format));
63 | }
64 |
65 | void MicroserviceController::setCommandWaitTimeout(int timeout) {
66 | wait_timeout = timeout*1000;
67 | }
68 |
69 | void MicroserviceController::setPath(const char *_path, const char* _url_swagger) {
70 | path_docs.clear();
71 | path_docs.append(_path);
72 |
73 | url_swagger.clear();
74 | url_swagger.append(_url_swagger);
75 | }
76 |
77 | int MicroserviceController::onEvent(const char* data) {
78 |
79 | if (callback_url.length() < 1)
80 | return -1;
81 |
82 | Concurrency::task task;
83 | http_client callback_client(callback_url);
84 |
85 | try {
86 |
87 | if (callback_format == L"json") {
88 |
89 | task = callback_client.request(methods::POST, "", data);
90 | }
91 | else {
92 | http_request request(methods::POST);
93 | request.headers().add(L"Content-Type", L"application/x-www-form-urlencoded; charset=UTF-8");
94 | request.set_body(data);
95 | task = callback_client.request(request);
96 | }
97 |
98 | task.then([](http_response response) {
99 | //ucout << response.to_string();
100 | if (response.status_code() == status_codes::OK) {
101 | auto body = response.extract_string().get();
102 | ucout << body << std::endl;
103 | }
104 | }).wait();
105 |
106 | return 1;
107 | }
108 | catch (const web::http::http_exception &e) {
109 | ucout << e.error_code() << endl;
110 | }
111 | catch (const std::exception & ex) {
112 | ucout << ex.what() << endl;
113 | }
114 | catch (...) {
115 | }
116 |
117 | return -1;
118 | }
119 |
120 | auto MicroserviceController::formatError(int code, const utility::string_t message) {
121 | web::json::value result = web::json::value::object();
122 |
123 | result[U("message")] = web::json::value::string(message);
124 | result[U("code")] = web::json::value::number(code);
125 |
126 | return result;
127 | }
128 |
129 | auto MicroserviceController::formatError(int code, const char* message) {
130 | wstring msg(message, message + strlen(message));
131 |
132 | return formatError(code, msg);
133 | }
134 |
135 | auto MicroserviceController::formatErrorRequired(utility::string_t field) {
136 | utility::string_t msg(field);
137 |
138 | msg.append(U(" is required"));
139 |
140 | return formatError(402, msg);
141 | }
142 |
143 | void MicroserviceController::handleGet(http_request message) {
144 | auto response = json::value::object();
145 | auto path = requestPath(message);
146 | auto params = requestQueryParams(message);
147 |
148 | try {
149 |
150 | if (path.size() < 1) {
151 | string p(path_docs);
152 | p.append("docs.html");
153 | std::ifstream in(p, ios::in);
154 |
155 | http_response response(status_codes::OK);
156 | response.headers().add(L"Content-Type", L"text/html; charset=UTF-8");
157 |
158 | std::stringstream buffer;
159 | buffer << in.rdbuf();
160 | string b = buffer.str();
161 | in.close();
162 |
163 | response.set_body(b);
164 | message.reply(response);
165 |
166 | return;
167 | }
168 |
169 | if (path[0] == CMD_SWAGGER) {
170 | string p(path_docs);
171 | p.append("swagger.json");
172 | std::ifstream in(p, ios::in);
173 |
174 | http_response response(status_codes::OK);
175 | response.headers().add(L"Content-Type", L"text/json; charset=UTF-8");
176 |
177 | std::stringstream buffer;
178 | buffer << in.rdbuf();
179 | string b = buffer.str();
180 | in.close();
181 |
182 | boost::algorithm::replace_all(b, "localhost:6542", url_swagger);
183 |
184 | response.set_body(b);
185 |
186 | message.reply(response);
187 |
188 | return;
189 | }
190 |
191 | if (!token.empty()) {
192 | if (!message.headers().has(header_names::authorization)) {
193 | message.reply(status_codes::Unauthorized);
194 | return;
195 | }
196 |
197 | auto headers = message.headers();
198 |
199 | if (headers[header_names::authorization] != token) {
200 | message.reply(status_codes::Unauthorized);
201 | return;
202 | }
203 | }
204 |
205 | web::json::value result = web::json::value::object();
206 |
207 | result[L"command"] = web::json::value::string(path[0]);
208 |
209 | if (path.size() > 1) {
210 | result[L"id"] = web::json::value::string(path[1]);
211 | }
212 |
213 | for (auto it = params.begin(); it != params.end(); ++it) {
214 | result[it->first] = web::json::value::string(it->second);
215 | }
216 |
217 | string command = ws2s(result.serialize());
218 | commands.push_back(command);
219 |
220 | DWORD dw1 = GetTickCount();
221 |
222 | while(dw1 + wait_timeout > GetTickCount()) {
223 |
224 | if (commandResponses.contains(command)) {
225 | message.reply(status_codes::OK, commandResponses[command], "application/json");
226 | commandResponses.remove(command);
227 | return;
228 | }
229 |
230 | Sleep(1);
231 | }
232 |
233 | throw exception("Failed to get info, timeout");
234 | }
235 | catch (const FormatException & e) {
236 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
237 | }
238 | catch (const RequiredException & e) {
239 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
240 | }
241 | catch (const json::json_exception & e) {
242 | message.reply(status_codes::BadRequest, formatError(410, e.what()));
243 | ucout << e.what() << endl;
244 | }
245 | catch (const std::exception & ex) {
246 | message.reply(status_codes::BadRequest, formatError(410, ex.what()));
247 | ucout << ex.what() << endl;
248 | }
249 | catch (...) {
250 | message.reply(status_codes::InternalError);
251 | }
252 | }
253 |
254 | void MicroserviceController::handlePost(http_request message) {
255 | auto response = json::value::object();
256 |
257 | try {
258 |
259 | auto path = requestPath(message);
260 | auto params = requestQueryParams(message);
261 |
262 | ucout << message.to_string() << endl;
263 |
264 | if (path.size() < 1) {
265 | message.reply(status_codes::NotFound);
266 | return;
267 | }
268 |
269 | if (!token.empty()) {
270 | if (!message.headers().has(header_names::authorization)) {
271 | message.reply(status_codes::Unauthorized);
272 | return;
273 | }
274 |
275 | auto headers = message.headers();
276 |
277 | if (headers[header_names::authorization] != token) {
278 | message.reply(status_codes::Unauthorized);
279 | return;
280 | }
281 | }
282 |
283 | if (path[0] == CMD_SUB) {
284 | callback_url = params[U("callback_url")];
285 | callback_format = params[U("callback_format")];
286 |
287 | response[U("message")] = json::value::string(L"Succesfully subscribed");
288 | response[U("code")] = json::value::number(200);
289 | message.reply(status_codes::OK, response);
290 |
291 | return;
292 | }
293 |
294 | message.extract_utf8string(true).then([=](std::string body) {
295 |
296 | if (body.length() < 1) {
297 | throw exception("POST body is empty");
298 | }
299 |
300 | std::size_t pos = body.find("}");
301 | std::string command = body.substr(0, pos);
302 |
303 | command.append(",\"command\":\"");
304 | command.append(ws2s(path[0]));
305 | command.append("\"}");
306 |
307 | commands.push_back(command);
308 |
309 | for (int i = 0; i < wait_timeout; i++) {
310 | if (commandResponses.contains(command)) {
311 | message.reply(status_codes::OK, commandResponses[command], "application/json");
312 | commandResponses.remove(command);
313 | return;
314 | }
315 |
316 | Sleep(1);
317 | }
318 |
319 | throw exception("Failed to get info, timeout");
320 | }).wait();
321 |
322 | }
323 | catch (const ManagerException & e) {
324 | message.reply(status_codes::BadRequest, formatError(e.code, e.what()));
325 | }
326 | catch (const FormatException & e) {
327 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
328 | }
329 | catch (const RequiredException & e) {
330 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
331 | }
332 | catch (const json::json_exception & e) {
333 | message.reply(status_codes::BadRequest, formatError(410, e.what()));
334 | ucout << e.what() << endl;
335 | }
336 | catch (const std::exception & ex) {
337 | message.reply(status_codes::BadRequest, formatError(410, ex.what()));
338 | ucout << ex.what() << endl;
339 | }
340 | catch (...) {
341 | message.reply(status_codes::InternalError);
342 | }
343 |
344 | }
345 |
346 | void MicroserviceController::handleDelete(http_request message) {
347 | try {
348 | auto path = requestPath(message);
349 | auto params = requestQueryParams(message);
350 |
351 | if (path.size() < 1) {
352 | message.reply(status_codes::NotFound);
353 | return;
354 | }
355 |
356 | }
357 | catch (const ManagerException & e) {
358 | message.reply(status_codes::BadRequest, formatError(e.code, e.what()));
359 | }
360 | catch (const FormatException & e) {
361 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
362 | }
363 | catch (const RequiredException & e) {
364 | message.reply(status_codes::BadRequest, formatError(405, e.what()));
365 | }
366 | catch (const json::json_exception & e) {
367 | message.reply(status_codes::BadRequest, formatError(410, e.what()));
368 | ucout << e.what() << endl;
369 | }
370 | catch (const std::exception & ex) {
371 | message.reply(status_codes::BadRequest, formatError(410, ex.what()));
372 | ucout << ex.what() << endl;
373 | }
374 | catch (...) {
375 | message.reply(status_codes::InternalError);
376 | }
377 | }
378 |
379 | void MicroserviceController::handleHead(http_request message) {
380 | auto response = json::value::object();
381 | response[U("version")] = json::value::string(U("0.1.1"));
382 | response[U("code")] = json::value::number(200);
383 | message.reply(status_codes::OK, "version");
384 | }
385 |
386 | void MicroserviceController::handleOptions(http_request message) {
387 | http_response response(status_codes::OK);
388 | response.headers().add(U("Allow"), U("GET, POST, OPTIONS"));
389 | response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
390 | response.headers().add(U("Access-Control-Allow-Methods"), U("GET, POST, OPTIONS"));
391 | response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type"));
392 | message.reply(response);
393 | }
394 |
395 | void MicroserviceController::handleTrace(http_request message) {
396 | message.reply(status_codes::NotImplemented, responseNotImpl(methods::TRCE));
397 | }
398 |
399 | void MicroserviceController::handleConnect(http_request message) {
400 | message.reply(status_codes::NotImplemented, responseNotImpl(methods::CONNECT));
401 | }
402 |
403 | void MicroserviceController::handleMerge(http_request message) {
404 | message.reply(status_codes::NotImplemented, responseNotImpl(methods::MERGE));
405 | }
406 |
407 | void MicroserviceController::handlePatch(http_request message) {
408 | message.reply(status_codes::NotImplemented, responseNotImpl(methods::MERGE));
409 | }
410 |
411 | void MicroserviceController::handlePut(http_request message) {
412 | message.reply(status_codes::NotImplemented, responseNotImpl(methods::MERGE));
413 | }
414 |
415 | json::value MicroserviceController::responseNotImpl(const http::method & method) {
416 |
417 | using namespace json;
418 |
419 | auto response = value::object();
420 | response[U("serviceName")] = value::string(U("MT4 REST"));
421 | response[U("http_method")] = value::string(method);
422 |
423 | return response;
424 | }
425 |
--------------------------------------------------------------------------------
/mt5-rest/microsvc_controller.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 | #include
8 | #include "basic_controller.hpp"
9 | #include "session_manager.hpp"
10 | #include "safe_vector.hpp"
11 | #include "safe_map.hpp"
12 |
13 | using namespace cfx;
14 | using namespace std;
15 | using namespace utility;
16 | using namespace web::http::client;
17 |
18 | static string ws2s(const std::wstring& wstr) {
19 | using convert_typeX = std::codecvt_utf8;
20 | std::wstring_convert converterX;
21 |
22 | return converterX.to_bytes(wstr);
23 | };
24 |
25 | static wstring s2ws(const std::string& str) {
26 | using convert_typeX = std::codecvt_utf8;
27 | std::wstring_convert converterX;
28 |
29 | return converterX.from_bytes(str);
30 | };
31 |
32 | class MicroserviceController : public BasicController, Controller {
33 | public:
34 | MicroserviceController() : BasicController() {}
35 | ~MicroserviceController() {
36 | }
37 | void handleGet(http_request message) override;
38 | void handlePut(http_request message) override;
39 | void handlePost(http_request message) override;
40 | void handlePatch(http_request message) override;
41 | void handleDelete(http_request message) override;
42 | void handleHead(http_request message) override;
43 | void handleOptions(http_request message) override;
44 | void handleTrace(http_request message) override;
45 | void handleConnect(http_request message) override;
46 | void handleMerge(http_request message) override;
47 | void initRestOpHandlers() override;
48 |
49 | auto formatError(int code, const utility::string_t message);
50 | auto formatError(int code, const char* message);
51 | auto formatErrorRequired(utility::string_t field);
52 |
53 | const char* getCommand();
54 | void setCommandResponse(const char* command, const char* reponse);
55 | void pushCommand(string_t command, string_t options);
56 | int hasCommands();
57 | int onEvent(const char* data);
58 | void setCommandWaitTimeout(int timeout);
59 | void setPath(const char* path, const char* url_swaggger);
60 | void setCallback(const char* url, const char* format);
61 | void setAuthToken(const char* _token) {
62 | token.clear();
63 | token.append(s2ws(_token));
64 | };
65 |
66 | private:
67 | static json::value responseNotImpl(const http::method & method);
68 | SafeVector commands;
69 | SafeMap commandResponses;
70 | string_t callback_url;
71 | string_t callback_format;
72 | int wait_timeout;
73 | string path_docs;
74 | string url_swagger;
75 | string_t token;
76 | };
77 |
--------------------------------------------------------------------------------
/mt5-rest/mt5-rest.def:
--------------------------------------------------------------------------------
1 | LIBRARY "mt5-rest.dll"
2 | EXPORTS
3 | Init
4 | Deinit
5 | SetAuthToken
--------------------------------------------------------------------------------
/mt5-rest/mt5-rest.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 15.0
23 | {7359E8DF-CF85-409D-833B-3B66F8DAC462}
24 | Win32Proj
25 | microservicetutorial
26 | 10.0.17763.0
27 | mt5-rest
28 |
29 |
30 |
31 | Application
32 | true
33 | v141
34 | Unicode
35 |
36 |
37 | Application
38 | false
39 | v141
40 | true
41 | Unicode
42 |
43 |
44 | Application
45 | true
46 | v141
47 | Unicode
48 |
49 |
50 | DynamicLibrary
51 | false
52 | v141
53 | true
54 | Unicode
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | true
76 |
77 |
78 | true
79 |
80 |
81 | false
82 |
83 |
84 | false
85 | $(SolutionDir)$(Platform)\$(Configuration)\
86 |
87 |
88 |
89 | Use
90 | Level3
91 | Disabled
92 | true
93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
94 | false
95 |
96 |
97 |
98 |
99 | Console
100 | true
101 |
102 |
103 |
104 |
105 | Use
106 | Level3
107 | Disabled
108 | true
109 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
110 | false
111 |
112 |
113 | Console
114 | true
115 |
116 |
117 |
118 |
119 | Use
120 | Level3
121 | MaxSpeed
122 | true
123 | true
124 | true
125 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
126 | true
127 |
128 |
129 | Console
130 | true
131 | true
132 | true
133 |
134 |
135 |
136 |
137 | Use
138 | Level3
139 | MaxSpeed
140 | true
141 | true
142 | true
143 | _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
144 | true
145 | ..\packages\x64-windows\include;..\..\boost_1_69_0;%(AdditionalIncludeDirectories);
146 | Sync
147 |
148 |
149 | Console
150 | true
151 | true
152 | true
153 | ..\packages\x64-windows\lib\;..\..\boost_1_69_0\lib64-msvc-14.1;$(SolutionDir)$(Platform)\$(Configuration);
154 | cpprest_2_10.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
155 | mt5-rest.def
156 |
157 |
158 | taskkill /IM ServiceHub.DataWarehouseHost.exe /F 2>nul 1>nul
159 | Exit 0
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | Create
183 | Create
184 | Create
185 | Create
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/mt5-rest/mt5-rest.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | false
5 |
6 |
--------------------------------------------------------------------------------
/mt5-rest/network_utils.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include "network_utils.hpp"
3 |
4 |
5 | namespace cfx {
6 |
7 | HostInetInfo NetworkUtils::queryHostInetInfo() {
8 | io_service ios;
9 | tcp::resolver resolver(ios);
10 | tcp::resolver::query query(host_name(), "");
11 | return resolver.resolve(query);
12 | }
13 |
14 | utility::string_t NetworkUtils::hostIP(unsigned short family) {
15 | auto hostInetInfo = queryHostInetInfo();
16 | tcp::resolver::iterator end;
17 | while (hostInetInfo != end) {
18 | tcp::endpoint ep = *hostInetInfo++;
19 | sockaddr sa = *ep.data();
20 | if (sa.sa_family == family) {
21 | return utility::conversions::to_string_t(ep.address().to_string());
22 | }
23 | }
24 | return nullptr;
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/mt5-rest/network_utils.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Ivan Mejia on 11/28/16.
3 | //
4 | // MIT License
5 | //
6 | // Copyright (c) 2016 ivmeroLabs. All rights reserved.
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 |
15 | // The above copyright notice and this permission notice shall be included in all
16 | // copies or substantial portions of the Software.
17 |
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | // SOFTWARE.
25 | //
26 |
27 | #pragma once
28 |
29 | #include "stdafx.h"
30 | #include
31 |
32 | using namespace boost::asio;
33 | using namespace boost::asio::ip;
34 |
35 | namespace cfx {
36 |
37 | using HostInetInfo = tcp::resolver::iterator;
38 |
39 | class NetworkUtils {
40 | private:
41 | static HostInetInfo queryHostInetInfo();
42 | static utility::string_t hostIP(unsigned short family);
43 | public:
44 | // gets the host IP4 string formatted
45 | static utility::string_t hostIP4() {
46 | return hostIP(AF_INET);
47 | }
48 |
49 | // gets the host IP6 string formatted
50 | static utility::string_t hostIP6() {
51 |
52 | return hostIP(AF_INET6);
53 | }
54 | static std::string hostName() {
55 | return ip::host_name();
56 | }
57 | };
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/mt5-rest/safe_map.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 | #include
3 | #include
4 | #include "safe_map.hpp"
5 |
6 | string& SafeMap::operator[](string key) {
7 | return data[key];
8 | }
9 |
10 | void SafeMap::add(string key, string value) {
11 | lock_guard lock(mut);
12 | data[key] = value;
13 | cond.notify_one();
14 | }
15 |
16 | bool SafeMap::contains(string key) {
17 | return data.count(key) > 0;
18 | }
19 |
20 | void SafeMap::remove(string key) {
21 | lock_guard lock(mut);
22 | data.erase(key);
23 | cond.notify_one();
24 | }
--------------------------------------------------------------------------------
/mt5-rest/safe_map.hpp:
--------------------------------------------------------------------------------
1 | #ifndef SAFEMAP_HPP
2 | #define SAFEMAP_HPP
3 |
4 | #include