From c979738a8a77b44aa83c78a4487e850d8e88765b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 11 Sep 2024 15:14:49 +0530 Subject: [PATCH 01/47] brokers added --- brokers/pom.xml | 55 + .../io/quantum/trading/BrokerApplication.java | 13 + .../trading/brokers/AllowBacktesting.java | 9 + .../io/quantum/trading/brokers/Broker.java | 33 + .../trading/brokers/BrokerException.java | 27 + .../trading/brokers/BrokerFactory.java | 125 ++ .../brokers/BrokerModifyOrderRequest.java | 127 ++ .../brokers/BrokerPlaceOrderRequest.java | 255 +++ .../trading/brokers/BrokerService.java | 298 ++++ .../quantum/trading/brokers/SellDetail.java | 13 + .../trading/brokers/forex/ForexBroker.java | 12 + .../trading/brokers/forex/mt5/MT5Broker.java | 1090 ++++++++++++ .../brokers/forex/mt5/Mt5BacktestBroker.java | 566 ++++++ .../forex/mt5protocol/Copier5ApiTrader.java | 331 ++++ .../brokers/forex/mt5protocol/Examples.java | 1113 ++++++++++++ .../mt5protocol/mtapi/mt5/AccMethod.java | 37 + .../forex/mt5protocol/mtapi/mt5/Access.java | 6 + .../forex/mt5protocol/mtapi/mt5/AccessEx.java | 6 + .../mt5protocol/mtapi/mt5/AccessInfo.java | 45 + .../mt5protocol/mtapi/mt5/AccessRec.java | 47 + .../mt5protocol/mtapi/mt5/AccessRecEx.java | 58 + .../mt5protocol/mtapi/mt5/AccountLoader.java | 404 +++++ .../mt5protocol/mtapi/mt5/AccountRec.java | 221 +++ .../mt5protocol/mtapi/mt5/Action0Param.java | 6 + .../mt5protocol/mtapi/mt5/AddressRec.java | 31 + .../mt5protocol/mtapi/mt5/AddressRecEx.java | 37 + .../forex/mt5protocol/mtapi/mt5/Bar.java | 35 + .../mt5protocol/mtapi/mt5/BarRecord.java | 35 + .../mt5protocol/mtapi/mt5/BitConverter.java | 155 ++ .../mt5protocol/mtapi/mt5/BitReader.java | 232 +++ .../mtapi/mt5/BitReaderQuotes.java | 379 ++++ .../forex/mt5protocol/mtapi/mt5/Broker.java | 249 +++ .../forex/mt5protocol/mtapi/mt5/BuildRec.java | 63 + .../mt5protocol/mtapi/mt5/ByteLists.java | 29 + .../forex/mt5protocol/mtapi/mt5/C54.java | 50 + .../mtapi/mt5/CalculationMode.java | 46 + .../mt5protocol/mtapi/mt5/CmdHandler.java | 147 ++ .../forex/mt5protocol/mtapi/mt5/Compr.java | 384 ++++ .../mt5protocol/mtapi/mt5/Compressor.java | 368 ++++ .../mtapi/mt5/ConnectEventArgs.java | 10 + .../mtapi/mt5/ConnectException.java | 17 + .../mtapi/mt5/ConnectProgress.java | 21 + .../mt5protocol/mtapi/mt5/Connection.java | 677 +++++++ .../mt5protocol/mtapi/mt5/Connection.trial | 540 ++++++ .../mt5protocol/mtapi/mt5/Connector.java | 102 ++ .../mt5protocol/mtapi/mt5/Container.java | 5 + .../forex/mt5protocol/mtapi/mt5/Convert.java | 13 + .../mt5protocol/mtapi/mt5/ConvertBytes.java | 71 + .../mt5protocol/mtapi/mt5/ConvertTo.java | 31 + .../forex/mt5protocol/mtapi/mt5/Crypt.java | 192 ++ .../mt5protocol/mtapi/mt5/DatHeader.java | 57 + .../mt5protocol/mtapi/mt5/DealInternal.java | 243 +++ .../forex/mt5protocol/mtapi/mt5/DealType.java | 51 + .../mt5protocol/mtapi/mt5/DealsResult.java | 28 + .../forex/mt5protocol/mtapi/mt5/Decoder.java | 51 + .../mt5protocol/mtapi/mt5/Decompressor.java | 319 ++++ .../mt5protocol/mtapi/mt5/Direction.java | 37 + .../mtapi/mt5/DotNetToJavaStringHelper.java | 295 ++++ .../forex/mt5protocol/mtapi/mt5/Encoder.java | 51 + .../mtapi/mt5/ErrorDescription.java | 94 + .../forex/mt5protocol/mtapi/mt5/Event.java | 140 ++ .../mt5protocol/mtapi/mt5/ExecutionType.java | 37 + .../mtapi/mt5/ExecutionWaiters.java | 8 + .../mt5protocol/mtapi/mt5/ExpirationDate.java | 37 + .../mt5protocol/mtapi/mt5/ExpirationType.java | 37 + .../mt5protocol/mtapi/mt5/FillPolicy.java | 36 + .../mt5protocol/mtapi/mt5/FromBufReader.java | 11 + .../forex/mt5protocol/mtapi/mt5/GTCMode.java | 37 + .../forex/mt5protocol/mtapi/mt5/Help.java | 51 + .../mt5protocol/mtapi/mt5/HistHeader.java | 61 + .../mt5protocol/mtapi/mt5/HostAndPort.java | 21 + .../forex/mt5protocol/mtapi/mt5/InBuf.java | 190 ++ .../mt5protocol/mtapi/mt5/IntContainer.java | 19 + .../forex/mt5protocol/mtapi/mt5/Logger.java | 117 ++ .../forex/mt5protocol/mtapi/mt5/LoginId.java | 672 +++++++ .../mtapi/mt5/LoginIdWebServer.java | 76 + .../mt5protocol/mtapi/mt5/LoginRcv1.java | 41 + .../forex/mt5protocol/mtapi/mt5/MD5.java | 198 +++ .../mtapi/mt5/MD5ForBrokerSearch.java | 219 +++ .../mt5protocol/mtapi/mt5/MD5Managed.java | 814 +++++++++ .../mt5protocol/mtapi/mt5/MT4Status.java | 69 + .../forex/mt5protocol/mtapi/mt5/MT5API.java | 1549 +++++++++++++++++ .../mt5protocol/mtapi/mt5/MailRecipient.java | 21 + .../mt5protocol/mtapi/mt5/MarginMode.java | 37 + .../mtapi/mt5/MarketCloseWaiter.java | 73 + .../mtapi/mt5/MarketOpenWaiter.java | 80 + .../mt5protocol/mtapi/mt5/ModifyWaiter.java | 67 + .../forex/mt5protocol/mtapi/mt5/Msg.java | 181 ++ .../forex/mt5protocol/mtapi/mt5/MsgType.java | 27 + .../forex/mt5protocol/mtapi/mt5/MyThread.java | 52 + .../mtapi/mt5/OnConnectProgress.java | 12 + .../mt5protocol/mtapi/mt5/OnMsgHandler.java | 13 + .../mtapi/mt5/OnOrderProgress.java | 12 + .../mt5protocol/mtapi/mt5/OnOrderUpdate.java | 13 + .../forex/mt5protocol/mtapi/mt5/OnQuote.java | 12 + .../mt5protocol/mtapi/mt5/OnQuoteHistory.java | 13 + .../mt5protocol/mtapi/mt5/OnTradeHistory.java | 15 + .../mtapi/mt5/OpnedClosedOrders.java | 432 +++++ .../forex/mt5protocol/mtapi/mt5/Order.java | 317 ++++ .../mt5protocol/mtapi/mt5/OrderDirection.java | 36 + .../mt5protocol/mtapi/mt5/OrderHistory.java | 230 +++ .../mtapi/mt5/OrderHistoryEventArgs.java | 10 + .../mt5protocol/mtapi/mt5/OrderInternal.java | 225 +++ .../mt5protocol/mtapi/mt5/OrderMargin.java | 475 +++++ .../mt5protocol/mtapi/mt5/OrderProfit.java | 297 ++++ .../mt5protocol/mtapi/mt5/OrderProgress.java | 19 + .../mtapi/mt5/OrderProgressEventArgs.java | 42 + .../mt5protocol/mtapi/mt5/OrderSender.java | 136 ++ .../mt5protocol/mtapi/mt5/OrderState.java | 43 + .../mt5protocol/mtapi/mt5/OrderType.java | 42 + .../mt5protocol/mtapi/mt5/OrderUpdate.java | 10 + .../forex/mt5protocol/mtapi/mt5/OutBuf.java | 146 ++ .../mt5protocol/mtapi/mt5/PackDecrypt.java | 30 + .../mt5protocol/mtapi/mt5/PackEncrypt.java | 32 + .../mt5protocol/mtapi/mt5/PacketHdr.java | 34 + .../mt5protocol/mtapi/mt5/PacketHdrEx.java | 43 + .../mtapi/mt5/PendingCloseWaiter.java | 63 + .../mtapi/mt5/PendingOpenWaiter.java | 70 + .../mt5protocol/mtapi/mt5/PlacedType.java | 58 + .../mt5protocol/mtapi/mt5/ProcessEvents.java | 7 + .../forex/mt5protocol/mtapi/mt5/Profit.java | 407 +++++ .../mt5protocol/mtapi/mt5/ProgressType.java | 39 + .../mt5protocol/mtapi/mt5/PumpDeals5D8.java | 70 + .../mt5protocol/mtapi/mt5/PumpDeals698.java | 27 + .../forex/mt5protocol/mtapi/mt5/Quote.java | 44 + .../mt5protocol/mtapi/mt5/QuoteHistory.java | 264 +++ .../mtapi/mt5/QuoteHistoryEventArgs.java | 15 + .../mt5protocol/mtapi/mt5/RefObject.java | 17 + .../mt5protocol/mtapi/mt5/SHA256Native.java | 19 + .../mt5protocol/mtapi/mt5/SecureSocket.java | 187 ++ .../forex/mt5protocol/mtapi/mt5/Server.java | 8 + .../mtapi/mt5/ServerException.java | 17 + .../mt5protocol/mtapi/mt5/ServerGroup.java | 7 + .../mt5protocol/mtapi/mt5/ServerInfo.java | 63 + .../mt5protocol/mtapi/mt5/ServerInfoEx.java | 87 + .../mt5protocol/mtapi/mt5/ServerRec.java | 43 + .../mtapi/mt5/ServersDatLoader.java | 54 + .../forex/mt5protocol/mtapi/mt5/Session.java | 40 + .../forex/mt5protocol/mtapi/mt5/Sha256.java | 29 + .../mt5protocol/mtapi/mt5/Sha256Managed.java | 455 +++++ .../mt5protocol/mtapi/mt5/ShortContainer.java | 9 + .../mt5protocol/mtapi/mt5/SpreadData.java | 40 + .../mt5protocol/mtapi/mt5/SpreadInfo.java | 39 + .../forex/mt5protocol/mtapi/mt5/String8.java | 87 + .../mt5protocol/mtapi/mt5/Subscriber.java | 453 +++++ .../forex/mt5protocol/mtapi/mt5/SwapType.java | 42 + .../mt5protocol/mtapi/mt5/SymBaseInfo.java | 286 +++ .../forex/mt5protocol/mtapi/mt5/SymGroup.java | 306 ++++ .../mt5protocol/mtapi/mt5/SymbolInfo.java | 289 +++ .../mt5protocol/mtapi/mt5/SymbolMargin.java | 785 +++++++++ .../mtapi/mt5/SymbolMarketData.java | 42 + .../mt5protocol/mtapi/mt5/SymbolSessions.java | 8 + .../mt5protocol/mtapi/mt5/SymbolSet.java | 24 + .../forex/mt5protocol/mtapi/mt5/Symbols.java | 63 + .../mt5protocol/mtapi/mt5/ThreadPool.java | 21 + .../mt5protocol/mtapi/mt5/ThreeDaysSwap.java | 40 + .../forex/mt5protocol/mtapi/mt5/TickRec.java | 44 + .../forex/mt5protocol/mtapi/mt5/Ticker.java | 32 + .../mtapi/mt5/TimeoutException.java | 17 + .../mt5protocol/mtapi/mt5/TradeMode.java | 38 + .../mt5protocol/mtapi/mt5/TradeRequest.java | 271 +++ .../mtapi/mt5/TradeRequestInternal.java | 185 ++ .../mt5protocol/mtapi/mt5/TradeResult.java | 53 + .../mt5protocol/mtapi/mt5/TradeType.java | 60 + .../mtapi/mt5/TransactionInfo.java | 82 + .../forex/mt5protocol/mtapi/mt5/Trial.java | 38 + .../mt5protocol/mtapi/mt5/TryParseHelper.java | 72 + .../forex/mt5protocol/mtapi/mt5/UDT.java | 100 ++ .../mt5protocol/mtapi/mt5/UpdateType.java | 31 + .../mt5protocol/mtapi/mt5/V3DaysSwap.java | 40 + .../forex/mt5protocol/mtapi/mt5/vAES.java | 689 ++++++++ .../forex/mt5protocol/mtapi/mt5/vCRC32.java | 315 ++++ .../forex/mt5protocol/mtapi/mt5/vRSA.java | 114 ++ .../forex/mt5protocol/mtapi/mt5/vSHA1.java | 214 +++ .../forex/mt5protocol/mtapi/mt5/vUTF.java | 33 + .../brokers/indian/GlobalDataFeed.java | 61 + .../brokers/indian/IndianDemoBroker.java | 239 +++ .../trading/brokers/indian/NseBroker.java | 131 ++ .../trading/brokers/indian/ZerodhaBroker.java | 1099 ++++++++++++ .../fyers/FyersAuthCodeValidateRequest.java | 13 + .../fyers/FyersAuthCodeValidateResponse.java | 15 + .../brokers/indian/fyers/FyersBroker.java | 310 ++++ .../indian/fyers/FyersHistoricalResponse.java | 20 + .../trading/brokers/indian/kotak/JSON.java | 373 ++++ .../brokers/indian/kotak/KotakApiClient.java | 450 +++++ .../brokers/indian/kotak/KotakBroker.java | 253 +++ .../brokers/indian/kotak/KotakInstrument.java | 27 + .../kotak/KotakInstrumentLinkResponse.java | 9 + .../indian/kotak/KotakLoginPayload.java | 9 + .../indian/kotak/KotakLoginResponse.java | 22 + .../kotak/KotakOrderDetailsResponse.java | 11 + .../indian/kotak/KotakPlaceOrderPayload.java | 23 + .../indian/kotak/KotakPlaceOrderResponse.java | 19 + .../indian/kotak/KotakTokenResponse.java | 21 + .../indian/prostocks/NorenApiJava.java | 236 +++ .../indian/prostocks/NorenRequests.java | 100 ++ .../brokers/indian/prostocks/NorenRoutes.java | 30 + .../indian/prostocks/ProStocksBroker.java | 116 ++ .../trading/provider/DataProvidable.java | 38 + .../provider/ForexMetadataProvidable.java | 10 + .../provider/HistoricalDataProvidable.java | 12 + .../provider/NseMetadataProvidable.java | 8 + 202 files changed, 28416 insertions(+) create mode 100644 brokers/pom.xml create mode 100644 brokers/src/main/java/io/quantum/trading/BrokerApplication.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/AllowBacktesting.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/Broker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/BrokerException.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/BrokerFactory.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/BrokerModifyOrderRequest.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/BrokerPlaceOrderRequest.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/BrokerService.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/SellDetail.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/ForexBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/MT5Broker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/Mt5BacktestBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Copier5ApiTrader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Examples.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccMethod.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Access.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessEx.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRecEx.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountLoader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Action0Param.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRecEx.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Bar.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BarRecord.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitConverter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReaderQuotes.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Broker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BuildRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ByteLists.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/C54.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CalculationMode.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CmdHandler.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compr.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compressor.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectEventArgs.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectException.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectProgress.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connection.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connection.trial create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connector.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Container.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Convert.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertBytes.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertTo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Crypt.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DatHeader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealInternal.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealsResult.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decoder.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decompressor.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Direction.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DotNetToJavaStringHelper.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Encoder.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ErrorDescription.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Event.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionWaiters.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationDate.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FillPolicy.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FromBufReader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/GTCMode.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Help.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HistHeader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HostAndPort.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/InBuf.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/IntContainer.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Logger.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginId.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginIdWebServer.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginRcv1.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5ForBrokerSearch.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5Managed.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT4Status.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT5API.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MailRecipient.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarginMode.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketCloseWaiter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketOpenWaiter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ModifyWaiter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Msg.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MsgType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MyThread.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnConnectProgress.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnMsgHandler.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderProgress.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderUpdate.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuote.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuoteHistory.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnTradeHistory.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OpnedClosedOrders.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Order.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderDirection.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistory.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistoryEventArgs.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderInternal.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderMargin.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProfit.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgress.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgressEventArgs.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderSender.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderState.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderUpdate.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OutBuf.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackDecrypt.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackEncrypt.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdr.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdrEx.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingCloseWaiter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingOpenWaiter.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PlacedType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProcessEvents.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Profit.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProgressType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals5D8.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals698.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Quote.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistory.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistoryEventArgs.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/RefObject.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SHA256Native.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SecureSocket.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Server.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerException.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerGroup.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfoEx.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServersDatLoader.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Session.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256Managed.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ShortContainer.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadData.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/String8.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Subscriber.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SwapType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymBaseInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymGroup.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMargin.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMarketData.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSessions.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSet.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Symbols.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreadPool.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreeDaysSwap.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TickRec.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Ticker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TimeoutException.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeMode.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequest.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequestInternal.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeResult.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TransactionInfo.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Trial.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TryParseHelper.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UDT.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UpdateType.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/V3DaysSwap.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vAES.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vCRC32.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vRSA.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vSHA1.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vUTF.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/GlobalDataFeed.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/IndianDemoBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/NseBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/ZerodhaBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateRequest.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersHistoricalResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/JSON.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakApiClient.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrument.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrumentLinkResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginPayload.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakOrderDetailsResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderPayload.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakTokenResponse.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenApiJava.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRequests.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRoutes.java create mode 100644 brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/ProStocksBroker.java create mode 100644 brokers/src/main/java/io/quantum/trading/provider/DataProvidable.java create mode 100644 brokers/src/main/java/io/quantum/trading/provider/ForexMetadataProvidable.java create mode 100644 brokers/src/main/java/io/quantum/trading/provider/HistoricalDataProvidable.java create mode 100644 brokers/src/main/java/io/quantum/trading/provider/NseMetadataProvidable.java diff --git a/brokers/pom.xml b/brokers/pom.xml new file mode 100644 index 00000000000..5f8b29cf154 --- /dev/null +++ b/brokers/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + io.quantum + trading + 0.0.1-SNAPSHOT + + + brokers + + + 21 + 21 + UTF-8 + + + + + io.quantum + core + 0.0.1-SNAPSHOT + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + repackage + + exec + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + *.java + + + + + + diff --git a/brokers/src/main/java/io/quantum/trading/BrokerApplication.java b/brokers/src/main/java/io/quantum/trading/BrokerApplication.java new file mode 100644 index 00000000000..6e14764a93a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/BrokerApplication.java @@ -0,0 +1,13 @@ +package io.quantum.trading; + +import io.quantum.trading.util.DateUtil; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BrokerApplication { + public static void main(String[] args) { + DateUtil.setTimeZone(); + SpringApplication.run(BrokerApplication.class, args); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/AllowBacktesting.java b/brokers/src/main/java/io/quantum/trading/brokers/AllowBacktesting.java new file mode 100644 index 00000000000..ce3d1263324 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/AllowBacktesting.java @@ -0,0 +1,9 @@ +package io.quantum.trading.brokers; + +public interface AllowBacktesting { + void synchronizeOrders(); + + boolean isInBacktestMode(); + + boolean isUtMode(); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/Broker.java b/brokers/src/main/java/io/quantum/trading/brokers/Broker.java new file mode 100644 index 00000000000..f218a1a6e32 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/Broker.java @@ -0,0 +1,33 @@ +package io.quantum.trading.brokers; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class Broker { + protected static final String TAG_PREFIX = "QUANTUM_BOT"; + protected static final String DUMMY_TRADE = "NO_TRADE"; + + public abstract double getBalance() throws BrokerException; + + public abstract Currency getCurrency() throws BrokerException; + + public abstract boolean testConnection() throws BrokerException; + + public abstract void disConnect() throws BrokerException; + + public abstract BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException; + + public abstract BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException; + + public abstract void cancelOrder(String orderId) throws BrokerException; + + public abstract void closeOrder(String orderId) throws BrokerException; + + public abstract BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException; + + public boolean isDemoAcc() { + return false; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/BrokerException.java b/brokers/src/main/java/io/quantum/trading/brokers/BrokerException.java new file mode 100644 index 00000000000..bb03accbe48 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/BrokerException.java @@ -0,0 +1,27 @@ +package io.quantum.trading.brokers; + +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.exception.EntityException; +import lombok.Getter; + +public class BrokerException extends EntityException { + @Getter private boolean retryable = true; + + public BrokerException(Throwable cause, ApiError apiError, Object... params) { + super(cause, apiError, params); + } + + public BrokerException(ApiError apiError, Object... params) { + super(apiError, params); + } + + public BrokerException(boolean retryable, Throwable cause, ApiError apiError, Object... params) { + super(cause, apiError, params); + this.retryable = retryable; + } + + public BrokerException(boolean retryable, BrokerException exception) { + super(exception); + this.retryable = retryable; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/BrokerFactory.java b/brokers/src/main/java/io/quantum/trading/brokers/BrokerFactory.java new file mode 100644 index 00000000000..8771e40104b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/BrokerFactory.java @@ -0,0 +1,125 @@ +package io.quantum.trading.brokers; + +import io.quantum.trading.brokers.forex.mt5.MT5Broker; +import io.quantum.trading.brokers.forex.mt5.Mt5BacktestBroker; +import io.quantum.trading.brokers.indian.GlobalDataFeed; +import io.quantum.trading.brokers.indian.IndianDemoBroker; +import io.quantum.trading.brokers.indian.NseBroker; +import io.quantum.trading.brokers.indian.ZerodhaBroker; +import io.quantum.trading.brokers.indian.fyers.FyersBroker; +import io.quantum.trading.entities.*; +import io.quantum.trading.entities.BrokerConfigDetails.Mt5BrokerConfigDetails; +import io.quantum.trading.redis.RedisCrudService; +import io.quantum.trading.services.ScriptMetadataRefresherService; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BrokerFactory { + private final BrokerConfigRepository brokerConfigRepository; + private final DemoBrokerRepository demoBrokerRepository; + private final RedisCrudService redisCrudService; + private final ScriptMetadataRefresherService scriptMetadataRefresherService; + private static final Map BROKERS = new ConcurrentHashMap<>(); + + public Broker getBroker(String brokerConfigId) { + synchronized (brokerConfigId.intern()) { + if (!BROKERS.containsKey(brokerConfigId)) { + log.info("Broker, {} is not cached. Creating a new one", brokerConfigId); + var brokerConfigOpt = brokerConfigRepository.findById(brokerConfigId); + if (brokerConfigOpt.isEmpty()) + throw new RuntimeException("BrokerConfigId, " + brokerConfigId + " is not found"); + + var brokerConfig = brokerConfigOpt.get(); + + var broker = + switch (brokerConfig.getType()) { + case BrokerConfig.BrokerType.ZERODHA -> + new ZerodhaBroker(() -> brokerConfigRepository.findById(brokerConfigId).get()); + case BrokerConfig.BrokerType.MT5 -> + new MT5Broker((Mt5BrokerConfigDetails) brokerConfig.getBrokerConfigDetails()); + case BrokerConfig.BrokerType.MT5_BACKTEST -> + new Mt5BacktestBroker( + (BrokerConfigDetails.Mt5BacktestBrokerConfigDetails) + brokerConfig.getBrokerConfigDetails(), + redisCrudService); + case BrokerConfig.BrokerType.GLOBAL_DATAFEEDS -> + new GlobalDataFeed( + (BrokerConfigDetails.GlobalDataFeedConfigDetails) + brokerConfig.getBrokerConfigDetails()); + case BrokerConfig.BrokerType.INDIAN_MKT_DEMO, + BrokerConfig.BrokerType.INDIAN_MKT_BACKTEST -> + new IndianDemoBroker(brokerConfig, redisCrudService, demoBrokerRepository); + case BrokerConfig.BrokerType.FYERS -> + new FyersBroker( + (BrokerConfigDetails.FyersBrokerConfigDetails) + brokerConfig.getBrokerConfigDetails()); + + default -> + throw new IllegalArgumentException( + "Unknown broker type, " + brokerConfig.getType()); + }; + BROKERS.put(brokerConfigId, broker); + synchronized (BrokerFactory.class) { + getScriptMetadataMap(BatchLastRunDetails.BatchType.NSE_SCRIPT_METADATA); + getScriptMetadataMap(BatchLastRunDetails.BatchType.FOREX_SCRIPT_METADATA); + } + } + } + return BROKERS.get(brokerConfigId); + } + + private synchronized void getScriptMetadataMap(BatchLastRunDetails.BatchType batchType) { + var scriptMetadataRes = + scriptMetadataRefresherService.metadataWatcher( + batchType, + "BrokerFactoryCb", + scriptMetadata -> { + updateMetadataInBrokers(scriptMetadata); + return null; + }); + updateMetadataInBrokers(scriptMetadataRes); + } + + private void updateMetadataInBrokers(List scriptMetadata) { + if (!scriptMetadata.isEmpty()) { + if (scriptMetadata.get(0) instanceof NseScriptMetadata) { + log.info("Updating NSE brokers with latest metadata"); + var nseScriptMetadataMap = + ((List) scriptMetadata) + .stream() + .collect( + Collectors.toMap( + NseScriptMetadata::getId, nseScriptMetadata -> nseScriptMetadata)); + // TODO The moment we introduce the 2nd NseBroker, we have to make the below code dynamic + var scriptTokenSymbolMap = + nseScriptMetadataMap.entrySet().stream() + .collect( + Collectors.groupingBy( + e -> + Long.parseLong( + e.getValue() + .getBrokerScriptIdMap() + .get(BrokerConfig.BrokerType.ZERODHA)), + Collectors.mapping(Map.Entry::getKey, Collectors.toList()))); + BROKERS.values().stream() + .filter(broker -> broker instanceof NseBroker) + .forEach( + broker -> { + ((NseBroker) broker) + .updateScriptMetadata(nseScriptMetadataMap, scriptTokenSymbolMap); + }); + } else if (scriptMetadata.get(0) instanceof ForexScriptMetadata) { + // Do nothing + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/BrokerModifyOrderRequest.java b/brokers/src/main/java/io/quantum/trading/brokers/BrokerModifyOrderRequest.java new file mode 100644 index 00000000000..5220cee85ab --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/BrokerModifyOrderRequest.java @@ -0,0 +1,127 @@ +package io.quantum.trading.brokers; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type", + include = JsonTypeInfo.As.EXISTING_PROPERTY, + visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type( + value = BrokerModifyOrderRequest.BuyMarketForex.class, + name = "BUY_MARKET_FOREX"), + @JsonSubTypes.Type( + value = BrokerModifyOrderRequest.SellMarketForex.class, + name = "SELL_MARKET_FOREX"), + @JsonSubTypes.Type( + value = BrokerModifyOrderRequest.BuyLimitForex.class, + name = "BUY_LIMIT_FOREX"), + @JsonSubTypes.Type( + value = BrokerModifyOrderRequest.SellLimitForex.class, + name = "SELL_LIMIT_FOREX"), + @JsonSubTypes.Type(value = BrokerModifyOrderRequest.BuyStopForex.class, name = "BUY_STOP_FOREX"), + @JsonSubTypes.Type(value = BrokerModifyOrderRequest.SellStopForex.class, name = "SELL_STOP_FOREX") + // @JsonSubTypes.Type(value = BrokerModifyOrderRequest.BuyStopLimitForex.class, name = + // "BUY_STOP_LIMIT_FOREX"), + // @JsonSubTypes.Type(value = BrokerModifyOrderRequest.SellStopLimitForex.class, name = + // "SELL_STOP_LIMIT_FOREX") +}) +@Data +@Jacksonized +@SuperBuilder(toBuilder = true) +public class BrokerModifyOrderRequest { + public BrokerOrderType type; + @NonNull private String orderId; + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyMarketForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellMarketForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyLimitForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellLimitForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyStopForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellStopForex extends BrokerModifyOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + // @Data + // @AllArgsConstructor + // public static class BuyStopLimitForex extends BrokerModifyOrderRequest{ + // private double price; + // private double stopLoss; + // private double stopLimitPrice; + // private double takeProfit; + // private String comment; + // } + // + // @Data + // @AllArgsConstructor + // public static class SellStopLimitForex extends BrokerModifyOrderRequest{ + // private double price; + // private double stopLoss; + // private double stopLimitPrice; + // private double takeProfit; + // private String comment; + // } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/BrokerPlaceOrderRequest.java b/brokers/src/main/java/io/quantum/trading/brokers/BrokerPlaceOrderRequest.java new file mode 100644 index 00000000000..9991991a458 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/BrokerPlaceOrderRequest.java @@ -0,0 +1,255 @@ +package io.quantum.trading.brokers; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.quantum.trading.brokers.forex.mt5.MT5Broker; +import io.quantum.trading.entities.BrokerConfigDetails; +import lombok.*; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + property = "type", + include = JsonTypeInfo.As.EXISTING_PROPERTY, + visible = true) +@JsonSubTypes({ + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.BuyMarketForex.class, + name = "BUY_MARKET_FOREX"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.SellMarketForex.class, + name = "SELL_MARKET_FOREX"), + @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.BuyLimitForex.class, name = "BUY_LIMIT_FOREX"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.SellLimitForex.class, + name = "SELL_LIMIT_FOREX"), + @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.BuyStopForex.class, name = "BUY_STOP_FOREX"), + @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.SellStopForex.class, name = "SELL_STOP_FOREX"), + @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.RegularBuyNse.class, name = "REGULAR_BUY_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularSellNse.class, + name = "REGULAR_SELL_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularBuyLimitNse.class, + name = "REGULAR_BUY_LIMIT_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularSellLimitNse.class, + name = "REGULAR_SELL_LIMIT_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularBuySLNse.class, + name = "REGULAR_BUY_SL_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularSellSLNse.class, + name = "REGULAR_SELL_SL_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularBuySLMNse.class, + name = "REGULAR_BUY_SLM_NSE"), + @JsonSubTypes.Type( + value = BrokerPlaceOrderRequest.RegularSellSLMNse.class, + name = "REGULAR_SELL_SLM_NSE") + // @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.BuyStopLimitForex.class, name = + // "BUY_STOP_LIMIT_FOREX"), + // @JsonSubTypes.Type(value = BrokerPlaceOrderRequest.SellStopLimitForex.class, name = + // "SELL_STOP_LIMIT_FOREX"), +}) +@Data +@Jacksonized +@SuperBuilder(toBuilder = true) +public class BrokerPlaceOrderRequest { + @NonNull private BrokerOrderType type; + @NonNull private String exchangeScript; + private double quantity; + @NonNull private String tag; + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyMarketForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellMarketForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyLimitForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellLimitForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class BuyStopForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @EqualsAndHashCode(callSuper = true) + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @ToString(callSuper = true) + public static class SellStopForex extends BrokerPlaceOrderRequest { + private double stopLoss; + private double takeProfit; + private double price; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularBuyNse extends BrokerPlaceOrderRequest { + private double price; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularSellNse extends BrokerPlaceOrderRequest { + private double price; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularBuyLimitNse extends BrokerPlaceOrderRequest { + private double price; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularSellLimitNse extends BrokerPlaceOrderRequest { + private double price; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularBuySLNse extends BrokerPlaceOrderRequest { + private double price; + private double triggerPrice; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularSellSLNse extends BrokerPlaceOrderRequest { + private double price; + private double triggerPrice; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularBuySLMNse extends BrokerPlaceOrderRequest { + private double price; + private double triggerPrice; + @NonNull private String productType; + } + + @Data + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static class RegularSellSLMNse extends BrokerPlaceOrderRequest { + private double price; + private double triggerPrice; + @NonNull private String productType; + } + + public static void main(String[] args) { + var mt5BrokerConfigDetails = new BrokerConfigDetails.Mt5BrokerConfigDetails(); + mt5BrokerConfigDetails.setUsername("212129991"); + mt5BrokerConfigDetails.setPassword("y4umEXA$"); + mt5BrokerConfigDetails.setHost("d51a2s.octanetwork.net"); + mt5BrokerConfigDetails.setPort(443); + MT5Broker mt5Broker = new MT5Broker(mt5BrokerConfigDetails); + // BrokerPlaceOrderRequest brokerPlaceOrderRequest = new BuyMarketForex(0, 0); + // brokerPlaceOrderRequest.setScript("EURUSD"); + // brokerPlaceOrderRequest.setType(BrokerOrderType.BUY_MARKET_FOREX); + // brokerPlaceOrderRequest.setQuantity(2); + // System.out.println(mt5Broker.placeOrder(brokerPlaceOrderRequest)); + System.out.println(mt5Broker.getOrderDetails("5050105719")); + // mt5Broker.cancelOrder("5049561821"); + //// BrokerModifyOrderRequest brokerModifyOrderRequest = + //// new BrokerModifyOrderRequest.BuyLimitForex(142.717, 0, 142.753); + // brokerModifyOrderRequest.setOrderId("5049562761"); + // brokerModifyOrderRequest.setType(BrokerOrderType.BUY_LIMIT_FOREX); + // mt5Broker.modifyOrder(brokerModifyOrderRequest); + } + + // @Data + // @AllArgsConstructor + // public static class BuyStopLimitForex extends BrokerPlaceOrderRequest{ + // private double quantity; + // private double price; + // private double stopLoss; + // private double takeProfit; + // private double stopLimitPrice; + // private String comment; + // } + // + // @Data + // @AllArgsConstructor + // public static class SellStopLimitForex extends BrokerPlaceOrderRequest{ + // private double quantity; + // private double price; + // private double stopLoss; + // private double takeProfit; + // private double stopLimitPrice; + // private String comment; + // } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/BrokerService.java b/brokers/src/main/java/io/quantum/trading/brokers/BrokerService.java new file mode 100644 index 00000000000..248e5bc4b39 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/BrokerService.java @@ -0,0 +1,298 @@ +package io.quantum.trading.brokers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.redis.RedisCrudService; +import io.quantum.trading.util.CandleType; +import io.quantum.trading.util.candles.CandleConfig; +import io.quantum.trading.util.candles.CandleConsumer; +import io.quantum.trading.util.candles.RenkoCandleConsumer; +import java.util.*; +import java.util.concurrent.*; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +@ConditionalOnProperty(prefix = "redis", name = "host") +public class BrokerService { + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private final RedisCrudService redisCrudService; + private Map> inMemCache = new ConcurrentHashMap<>(); + private Map> inMemCandles = new ConcurrentHashMap<>(); + private static final ExecutorService IN_MEM_CACHE_MANAGER = + Executors.newSingleThreadExecutor( + r -> { + var t = new Thread(r, "in-mem-cache-manager"); + t.setDaemon(true); + return t; + }); + private static final String IN_MEM_CACHE_LCK = "IN_MEM_CACHE_LCK"; + private static final long IN_MEM_CACHE_MANAGER_SYNC_WAIT_TIME_MILLIS = 100; + private Future inMemCacheManagerTask; + + private void cacheListsSync(Map> cacheKeys) { + log.info("Cache Manager syncing cacheKeys - {}", cacheKeys); + synchronized (IN_MEM_CACHE_LCK) { + if (this.inMemCacheManagerTask != null) { + log.warn("IN_MEM_CACHE_MANAGER task is running. Killing it first"); + this.inMemCacheManagerTask.cancel(true); + } + inMemCache.clear(); + cacheKeys + .keySet() + .forEach( + cacheKey -> + inMemCache.put(cacheKey, Collections.synchronizedList(new LinkedList<>()))); + } + + this.inMemCacheManagerTask = + IN_MEM_CACHE_MANAGER.submit( + () -> { + synchronized (IN_MEM_CACHE_LCK) { + while (true) { + for (String cacheKey : cacheKeys.keySet()) { + var candleConsumers = cacheKeys.get(cacheKey); + var redisListSize = redisCrudService.llen(cacheKey); + var inMemListSize = inMemCache.get(cacheKey).size(); + var redisFirstEle = + redisListSize > 0 + ? MAPPER.readValue( + redisCrudService.getFromList(cacheKey, 0, 0).get(0).toString(), + LiveTick.class) + : null; + var inMemFirstEle = inMemListSize > 0 ? inMemCache.get(cacheKey).get(0) : null; + + if (redisListSize == 0 && inMemListSize == 0) { + log.debug("Redis and inMemCache is empty for key, {}", cacheKey); + } else if (redisListSize < inMemListSize + || inMemFirstEle == null + || !redisFirstEle.equals(inMemFirstEle)) { + // 1. The inMemListSize is higher than the redis one. + // This cannot happen + // 2. inMemListSize is 0 but redis has data + // 3. The 1st elements in redis and inMemCache doesn't + // match + log.debug("Hard refreshing for key, {}", cacheKey); + var inMemList = inMemCache.get(cacheKey); + inMemList.clear(); // Just to be safe + // In case the response from Redis is empty, the + // imMemCandles are not cleared. So, clearing + candleConsumers.forEach( + candleConsumer -> + inMemCandles.put( + candleConsumer.getCandleConfig(), Collections.emptyList())); + redisCrudService + .getFromList(cacheKey, 0) + .forEach( + item -> { + try { + var liveTick = MAPPER.readValue(item.toString(), LiveTick.class); + inMemList.add(liveTick); + candleConsumers.forEach( + candleConsumer -> { + candleConsumer.consume(liveTick); + inMemCandles.put( + candleConsumer.getCandleConfig(), + candleConsumer.getCandles()); + }); + } catch (JsonProcessingException e) { + log.error("Error while parsing LiveTick", e); + } + }); + } else { + // Soft refresh + log.debug("Soft refreshing for key, {}", cacheKey); + var inMemList = inMemCache.get(cacheKey); + // Here size()-1 is not required. We are asking for the + // next position in the list + redisCrudService + .getFromList(cacheKey, inMemList.size()) + .forEach( + item -> { + try { + var liveTick = MAPPER.readValue(item.toString(), LiveTick.class); + inMemList.add(liveTick); + candleConsumers.forEach( + candleConsumer -> { + candleConsumer.consume(liveTick); + inMemCandles.put( + candleConsumer.getCandleConfig(), + candleConsumer.getCandles()); + }); + } catch (JsonProcessingException e) { + log.error("Error while parsing LiveTick", e); + } + }); + } + } + Thread.sleep(IN_MEM_CACHE_MANAGER_SYNC_WAIT_TIME_MILLIS); + } + } + }); + } + + public void cacheCandles(List candleConfigs) { + Map> cacheKeys = new HashMap<>(); + for (CandleConfig candleConfig : candleConfigs) { + var symbol = candleConfig.getSymbol() + "_today"; + if (!cacheKeys.containsKey(symbol)) { + cacheKeys.put(symbol, new ArrayList<>()); + } + switch (candleConfig.getType()) { + case CandleType.RENKO: + cacheKeys.get(symbol).add(new RenkoCandleConsumer(candleConfig)); + break; + case CandleType.HA: + default: + log.warn("Candle type, {} is not supported", candleConfig.getType()); + } + cacheListsSync(cacheKeys); + } + } + + public List getCandles(CandleConfig candleConfig) { + if (inMemCandles.containsKey(candleConfig)) return inMemCandles.get(candleConfig); + else return Collections.emptyList(); + } + + public Optional> getLatestCandles() { + var dt = new Date(); + List candles = new ArrayList<>(); + while (System.currentTimeMillis() - dt.getTime() < 2000) { + var expectedLastCandleTs = + new DateTime().withMillisOfSecond(0).withSecondOfMinute(0).minusMinutes(1).toDate(); + var expectedNextCandleTs = + new DateTime().withMillisOfSecond(0).withSecondOfMinute(0).toDate(); + Candle lastCandle = null; + Candle nextCandle = null; + try { + lastCandle = + MAPPER.readValue(redisCrudService.getKeyValue("BankNifty").toString(), Candle.class); + nextCandle = + MAPPER.readValue( + redisCrudService.getKeyValue("BankNifty_Next").toString(), Candle.class); + } catch (Throwable e) { + // ignore + log.warn("Unable to get latest candles", e); + return Optional.empty(); + } + if (expectedLastCandleTs.equals(lastCandle.getBeginTs()) + && expectedNextCandleTs.equals(nextCandle.getBeginTs())) { + candles.add(lastCandle); + candles.add(nextCandle); + return Optional.of(candles); + } + } + return Optional.empty(); + } + + public Optional> getLatestCandles(String prefix) { + var dt = new Date(); + List candles = new ArrayList<>(); + while (System.currentTimeMillis() - dt.getTime() < 6000) { + var expectedLastCandleTs = + new DateTime().withMillisOfSecond(0).withSecondOfMinute(0).minusMinutes(1).toDate(); + var expectedNextCandleTs = + new DateTime().withMillisOfSecond(0).withSecondOfMinute(0).toDate(); + Candle lastCandle = null; + Candle nextCandle = null; + try { + lastCandle = + MAPPER.readValue(redisCrudService.getKeyValue(prefix).toString(), Candle.class); + nextCandle = + MAPPER.readValue( + redisCrudService.getKeyValue(prefix + "_Next").toString(), Candle.class); + } catch (Throwable e) { + // ignore + log.warn("Unable to get latest candles", e); + return Optional.empty(); + } + if (expectedLastCandleTs.equals(lastCandle.getBeginTs()) + && expectedNextCandleTs.equals(nextCandle.getBeginTs())) { + candles.add(lastCandle); + candles.add(nextCandle); + return Optional.of(candles); + } + } + return Optional.empty(); + } + + public Candle getLiveTick(String instrumentSymbol, boolean isBacktestMode) { + try { + var redisVal = redisCrudService.getKeyValue(instrumentSymbol); + if (redisVal != null) { + if (isBacktestMode) { + return MAPPER.readValue(redisVal.toString(), Candle.class); + } else { + return MAPPER.readValue(redisVal.toString(), LiveTick.class).toCandle(); + } + } else throw new BrokerException(ApiError.NOT_FOUND, instrumentSymbol); + } catch (JsonProcessingException e) { + log.error("Error while getting LTP from cache store", e); + return getLiveTick(instrumentSymbol, isBacktestMode); + } + } + + public List getTodayLtps(String instrumentSymbol) { + var todayLiveTicksKey = instrumentSymbol + "_today"; + if (inMemCache.containsKey(todayLiveTicksKey)) { + log.debug("Coming from cache"); + return inMemCache.get(todayLiveTicksKey); + } else { + log.debug("Not Coming from cache"); + var liveTicks = new ArrayList(); + redisCrudService + .getFromList(instrumentSymbol + "_today", 0) + .forEach( + item -> { + try { + var liveTick = MAPPER.readValue(item.toString(), LiveTick.class); + liveTicks.add(liveTick); + } catch (JsonProcessingException e) { + log.error("Error while parsing LiveTick", e); + } + }); + return liveTicks; + } + } + + public List getPreviousNCandles(String instrumentToken, int n) { + /*var from = DateTime.now() + .minusMinutes(n) + .withSecondOfMinute(0) + .withMillisOfSecond(0); + var to = from.plusMinutes(n) + .minusMillis(1); + log.info("Starting triggerGetting historical api"); + var candles = getHistoricalData(valueOf(instrumentToken), from, to, MINUTE_1); + log.info("Obtained historical candles for {} - {} as {}", from ,to, candles); + return candles;*/ + return null; + } + + public List getPreviousTodayCandles(String instrumentToken, TimeFrame timeFrame) { + /*var from = DateTime.now() + .withTimeAtStartOfDay() + .withHourOfDay(9) + .withMinuteOfHour(15); + var to = DateTime.now() + .withSecondOfMinute(0) + .withMillisOfSecond(0) + .minusMillis(1); + log.info("Starting triggerGetting historical api"); + var candles = getHistoricalData(valueOf(instrumentToken), from, to, timeFrame); + log.info("Obtained historical candles for {} - {} as {}", from ,to, candles); + return candles;*/ + return null; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/SellDetail.java b/brokers/src/main/java/io/quantum/trading/brokers/SellDetail.java new file mode 100644 index 00000000000..ebe6e682a78 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/SellDetail.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers; + +import lombok.Data; + +@Data +public class SellDetail { + private double sellLtp; + private double parentMin; + private double parentMax; + private double childMin; + private double childMax; + private Double newSL = null; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/ForexBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/ForexBroker.java new file mode 100644 index 00000000000..31eb09d9c06 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/ForexBroker.java @@ -0,0 +1,12 @@ +package io.quantum.trading.brokers.forex; + +import io.quantum.trading.brokers.Broker; +import io.quantum.trading.brokers.BrokerException; + +public abstract class ForexBroker extends Broker { + protected abstract long getServerTimeZoneInMillis(); + + protected abstract String getAccountType(); + + public abstract void partialCloseOrder(String orderId, double qty) throws BrokerException; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/MT5Broker.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/MT5Broker.java new file mode 100644 index 00000000000..3ad61570fba --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/MT5Broker.java @@ -0,0 +1,1090 @@ +package io.quantum.trading.brokers.forex.mt5; + +import io.quantum.trading.brokers.*; +import io.quantum.trading.brokers.Currency; +import io.quantum.trading.brokers.forex.ForexBroker; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.ConnectEventArgs; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.ConnectException; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.FillPolicy; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.MT5API; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.OnQuoteHistory; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.Order; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.OrderState; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.OrderType; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.ServerException; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.SymbolInfo; +import io.quantum.trading.entities.BrokerConfigDetails; +import io.quantum.trading.entities.BrokerConfigDetails.Mt5BrokerConfigDetails; +import io.quantum.trading.entities.NseScriptMetadata; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.provider.DataProvidable; +import io.quantum.trading.provider.ForexMetadataProvidable; +import io.quantum.trading.provider.HistoricalDataProvidable; +import io.quantum.trading.util.CandleBucketingUtil; +import io.quantum.trading.util.OmsUtil; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; +import org.joda.time.Days; + +@Slf4j +public class MT5Broker extends ForexBroker + implements DataProvidable, HistoricalDataProvidable, ForexMetadataProvidable { + private MT5API api; + private int offsetFromUTC; + private final String orderTypeSuffix = "Forex"; + final Set apiOrderTypes = + Arrays.stream(OrderType.values()) + .map(e -> e.name() + orderTypeSuffix) + .collect(Collectors.toSet()); + + // Closed orders alone are stored in this map since we don't have any straight method in mt5 api + // to get it by passing orderId + private final HashMap orderHistory = new HashMap<>(); + private boolean flag = false; + private boolean dealHistoryFlag = false; + + private boolean disconnected = false; + private final Mt5BrokerConfigDetails mt5BrokerConfigDetails; + private Function, Void> liveTickConsumer; + + public MT5Broker(Mt5BrokerConfigDetails mt5BrokerConfigDetails) { + this.mt5BrokerConfigDetails = mt5BrokerConfigDetails; + try { + this.initialize(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + private void initialize() throws ConnectException { + this.api = + new MT5API( + Long.parseLong(mt5BrokerConfigDetails.getUsername()), + mt5BrokerConfigDetails.getPassword(), + mt5BrokerConfigDetails.getHost(), + mt5BrokerConfigDetails.getPort()); + api.OnConnectProgress.addListener(this::reconnect); + log.info("Connecting {}...", mt5BrokerConfigDetails.getUsername()); + api.Connect(); + this.disconnected = false; + + var tz = api.ServerDetails.getKey().TimeZone; + var dst = api.ServerDetails.getKey().DST; + var serverTzInMins = api.ServrTimeZoneInMinutes(); + + log.info( + "{} - Broker TZ - {}; Broker DST - {}, ServerTimeZoneInMinutes - {}", + mt5BrokerConfigDetails.getUsername(), + tz, + dst, + api.ServrTimeZoneInMinutes()); + this.offsetFromUTC = serverTzInMins * 60_000; + + this.addLiveListener(); + this.addHistoryListener(); + this.addOrderUpdateListener(); + } + + private void addOrderUpdateListener() { + this.api.OnOrderUpdate.addListener( + (sender, args) -> { + { + var order = args.Order; + try { + if (orderHistory.containsKey(String.valueOf(order.Ticket))) { + var oldOrder = orderHistory.get(String.valueOf(order.Ticket)); + oldOrder.setAvgClosePrice(order.ClosePrice); + oldOrder.setAvgOpenPrice(order.OpenPrice); + oldOrder.setProfitAndLoss(BigDecimal.valueOf(order.Profit)); + oldOrder.setQuantity(order.Lots); + oldOrder.setFilledQuantity(order.CloseVolume); + oldOrder.setCloseTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0); + log.debug("Update received for account {}, orderId - {}", order.Ticket, oldOrder); + } else { + var res = getBrokerOrderDetail(order, order.OpenPrice, false); + orderHistory.put(String.valueOf(order.Ticket), res); + log.debug("New Update received for account {}, orderId - {}", order.Ticket, res); + } + } catch (Exception e) { + log.error( + "Error in loading update of order for account {}, orderId {}, {}", + sender.User, + order.Ticket, + order.OrderType.name(), + e); + } + } + }); + } + + // TODO: This listener should be registered only when load history is been called, else its a + // unwanted process + private void addHistoryListener() { + this.api.OnTradeHistory.addListener( + (sender, args) -> { + args.Orders.forEach( + order -> { + try { + if (orderHistory.containsKey(String.valueOf(order.Ticket))) { + var oldOrder = orderHistory.get(String.valueOf(order.Ticket)); + oldOrder.setAvgClosePrice(order.ClosePrice); + oldOrder.setAvgOpenPrice(order.OpenPrice); + oldOrder.setProfitAndLoss(BigDecimal.valueOf(order.Profit)); + oldOrder.setQuantity(order.Lots); + oldOrder.setFilledQuantity(order.CloseVolume); + oldOrder.setCloseTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0); + log.debug( + "Update in history received for account {}, orderId - {}", + order.Ticket, + oldOrder); + } else { + var res = getBrokerOrderDetail(order, order.OpenPrice, false); + orderHistory.put(String.valueOf(order.Ticket), res); + log.debug( + "New Update in history received for account {}, orderId - {}", + order.Ticket, + res); + } + } catch (Exception e) { + log.error( + "Error in loading history orders for account {}, orderId - {}, {}", + sender.User, + order.Ticket, + order.OrderType.name(), + e); + } + }); + flag = true; + dealHistoryFlag = true; + }); + } + + private void addLiveListener() { + if (liveTickConsumer != null) { + api.OnQuote.addListener( + (sender, quote) -> { + try { + var script = quote.Symbol; + var bid = quote.Bid; + var ask = quote.Ask; + var ltp = (bid + ask) / 2.0; + var ts = + new DateTime(quote.Time.toInstant(ZoneOffset.UTC).toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis(); + var liveTick = + new LiveTick( + mt5BrokerConfigDetails.getExchange() + ":" + script, + ts, + new Date(), + ltp, + bid, + ask, + quote.Volume); + liveTickConsumer.apply(List.of(liveTick)); + } catch (Throwable t) { + log.error("Error while ingesting candles {}", sender.User, t); + } + }); + } + } + + // TODO: This method has to be synchronised, else its a risk wen used outside without synchronized + // block. Now its been used under synchronized block so ignoring for now. Listener can be added + // and removed within the scope of this method and the flag can be passed to the listener method + // instead of having it as an instance variable. When we keep it as an instance variable there is + // a risk. + private void loadHistoryOrders() { + try { + flag = false; + // This from & to dates are not considered in picking the history orders right now by mt5 api + log.info( + "Started loading history orders for account {}", mt5BrokerConfigDetails.getUsername()); + this.api.RequestOrderHistory(LocalDateTime.now().minusYears(5), LocalDateTime.now()); + while (!flag) { + log.debug("Waiting for historical orders to complete.."); + } + log.info( + "Completed loading history orders for account {}", mt5BrokerConfigDetails.getUsername()); + loadDealHistoryOrders(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // TODO: This method has to be synchronised, else its a risk wen used outside without synchronized + // block. Now its been used under synchronized block so ignoring for now. Listener can be added + // and removed within the scope of this method and the flag can be passed to the listener method + // instead of having it as an instance variable. When we keep it as an instance variable there is + // a risk + public void loadDealHistoryOrders() { + try { + dealHistoryFlag = false; + // This from & to dates are not considered in picking the history orders right now by mt5 api + log.info( + "Started loading deal history orders for account {}", + mt5BrokerConfigDetails.getUsername()); + this.api.RequestDealHistory(LocalDateTime.now().minusYears(5), LocalDateTime.now()); + while (!dealHistoryFlag) { + log.debug("Waiting for deal historical orders to complete.."); + } + log.info( + "Completed loading deal history orders for account {}", + mt5BrokerConfigDetails.getUsername()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void reconnect(MT5API sender, ConnectEventArgs args) { + var reConnectWaitTime = 10_000; + if (disconnected) { + log.info( + "Disconnect called no further reconnect will be attempted on this thread for account {}", + sender.User); + return; + } + switch (args.Progress) { + case Disconnect -> { + log.warn("Try reconnect {}", sender.User); + int i = 0; + while (true) { + try { + log.warn("Reconnect attempt #{} for account {}", i, sender.User); + this.disConnect(); + initialize(); + break; + } catch (Exception e) { + log.warn("Reconnect attempt #{} fail for account {}", i, sender.User, e); + } + i++; + try { + log.warn("Waiting for {} secs before attempting re-connect", reConnectWaitTime / 1000); + Thread.sleep(reConnectWaitTime); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + case Connected -> { + log.info("{} connected", sender.User); + reconnectFeed(); + } + case Exception -> { + log.error("", args.Exception); + this.api.Disconnect(); + try { + this.api.Connect(); + } catch (ConnectException e) { + throw new RuntimeException(e); + } + } + case SendLogin, SendAccountPassword, AcceptAuthorized, RequestTradeInfo -> + log.info("Received event for account {}, {}", sender.User, args.Progress); + default -> log.error("Unhandled event for account {}, {}", sender.User, args.Progress); + } + } + + @Override + public double getBalance() { + return this.api.AccountBalance(); + } + + @Override + public Currency getCurrency() throws BrokerException { + return mt5BrokerConfigDetails.getCurrency(); + } + + @Override + public boolean testConnection() throws BrokerException { + return this.api.Connected(); + } + + @Override + public void cancelOrder(String orderId) { + // TODO implement status check - isStarted + try { + var existingOrder = getOrderDetails(orderId); + this.api.OrderClose( + Long.parseLong(existingOrder.getOrderId()), + existingOrder.getExchangeScript(), + 0, + existingOrder.getQuantity(), + OrderType.valueOf( + BrokerOrderType.findValueByEnum(existingOrder.getType(), apiOrderTypes) + .replace(orderTypeSuffix, "")), + 0, + FillPolicy.FillOrKill); + log.info("Closed " + orderId); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void closeOrder(String orderId) throws BrokerException { + // TODO implement status check - isNotStarted + var existingOrder = getOrderDetails(orderId); + try { + this.api.OrderClose( + Long.parseLong(existingOrder.getOrderId()), + existingOrder.getExchangeScript(), + 0, + existingOrder.getQuantity(), + OrderType.valueOf( + BrokerOrderType.findValueByEnum(existingOrder.getType(), apiOrderTypes) + .replace(orderTypeSuffix, "")), + 0, + FillPolicy.FillOrKill); + log.info("Closed " + orderId); + } catch (Exception e) { + // If order is in PLACED state we are getting INVALID_REQUEST, hence handled. + if (e instanceof ServerException && e.getMessage().equals("POSITION_NOT_EXISTS") + || e.getMessage().equals("INVALID_REQUEST")) { + log.warn("OrderId, {} is already closed", orderId); + } else { + throw new RuntimeException(e); + } + } + } + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + Order[] openOrders = this.api.GetOpenedOrders(); + var opened = + Arrays.stream(openOrders) + .filter(order -> String.valueOf(order.Ticket).equals(orderId)) + .findFirst(); + + if (opened.isPresent()) { + log.debug("Got from open order.."); + return getBrokerOrderDetail(opened.get(), 0, true); + } + if (!orderHistory.isEmpty() + && orderHistory.containsKey(orderId) + && orderHistory.get(orderId).getCloseTs() != 0) { + log.info( + "Got from history for account {}, orderId - {}", + mt5BrokerConfigDetails.getUsername(), + orderId); + + return orderHistory.get(orderId); + } else { + // Its is synchronized to make the loadHistoryOrders method thread safe due to global flag + synchronized (this) { + if (orderHistory.isEmpty() || !orderHistory.containsKey(orderId)) loadHistoryOrders(); + } + if (!orderHistory.isEmpty() + && orderHistory.containsKey(orderId) + && orderHistory.get(orderId).getCloseTs() != 0) { + log.info( + "Got from history after loading for account {}, orderId - {}", + mt5BrokerConfigDetails.getUsername(), + orderId); + return orderHistory.get(orderId); + } + } + throw new RuntimeException("Order details not available for account - " + orderId); + } + + @Override + public void disConnect() { + disconnected = true; + try { + api.Disconnect(); + } catch (Exception e) { + // ignore + } + } + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + try { + Order order = null; + double requestedPrice = 0; + var script = OmsUtil.getScript(brokerPlaceOrderRequest.getExchangeScript()); + switch (brokerPlaceOrderRequest.getType()) { + case BrokerOrderType.BUY_MARKET_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyMarketForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + 0, + OrderType.Buy, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FillOrKill); + requestedPrice = 0; + } + case BrokerOrderType.BUY_LIMIT_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyLimitForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + orderRequest.getPrice(), + OrderType.BuyLimit, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FlashFill); + requestedPrice = orderRequest.getPrice(); + } + case BrokerOrderType.BUY_STOP_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyStopForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + orderRequest.getPrice(), + OrderType.BuyStop, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FlashFill); + requestedPrice = orderRequest.getPrice(); + } + case BrokerOrderType.SELL_MARKET_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellMarketForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + 0, + OrderType.Sell, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FillOrKill); + requestedPrice = 0; + } + case BrokerOrderType.SELL_LIMIT_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellLimitForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + orderRequest.getPrice(), + OrderType.SellLimit, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FlashFill); + requestedPrice = orderRequest.getPrice(); + } + case BrokerOrderType.SELL_STOP_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellStopForex) brokerPlaceOrderRequest; + order = + this.api.OrderSend( + script, + orderRequest.getQuantity(), + orderRequest.getPrice(), + OrderType.SellStop, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + null, + 0, + FillPolicy.FlashFill); + requestedPrice = orderRequest.getPrice(); + } + default -> + throw new IllegalArgumentException( + brokerPlaceOrderRequest.getType() + " orderType not available"); + } + log.info("Placed order successfully - {}", String.valueOf(order.Ticket)); + return getBrokerOrderDetail(order, requestedPrice, false); + } catch (Throwable t) { + throw new BrokerException(t, ApiError.BROKER_PLACE_ORDER); + } + } + + private BrokerOrderDetail getBrokerOrderDetail( + Order order, double requestedPrice, boolean isPositionOpen) { + try { + switch (order.OrderType.name()) { + case "Buy" -> { + return BrokerOrderDetail.BuyMarketForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0 + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "Sell" -> { + return BrokerOrderDetail.SellMarketForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0 + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "BuyLimit" -> { + return BrokerOrderDetail.BuyLimitForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .price(requestedPrice) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "SellLimit" -> { + return BrokerOrderDetail.SellLimitForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .price(requestedPrice) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "BuyStop" -> { + return BrokerOrderDetail.BuyStopForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .price(requestedPrice) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "SellStop" -> { + return BrokerOrderDetail.SellStopForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .price(requestedPrice) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + case "CloseBy" -> { + return BrokerOrderDetail.CloseByForex.builder() + .quantity((Math.round(order.Lots * 100.0) / 100.0)) + .stopLoss(order.StopLoss) + .takeProfit(order.TakeProfit) + .openTs( + new DateTime( + ZonedDateTime.of(order.OpenTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis()) + .closeTs( + (order.CloseTime.isAfter(order.OpenTime)) + ? new DateTime( + ZonedDateTime.of(order.CloseTime, ZoneId.of("UTC")) + .toInstant() + .toEpochMilli()) + .minusMillis(offsetFromUTC) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue(order.OrderType.name() + orderTypeSuffix)) + .exchangeScript(order.Symbol) + .orderId(String.valueOf(order.Ticket)) + .orderStatus( + (isPositionOpen && order.State.name().equals(OrderState.Filled.name())) + ? BrokerOrderStatus.POSITION_OPEN + : BrokerOrderStatus.findEnumByValue(order.State.name())) + .price(requestedPrice) + .avgOpenPrice(order.OpenPrice) + .avgClosePrice(order.ClosePrice) + .profitAndLoss(BigDecimal.valueOf(order.Profit)) + .filledQuantity(order.CloseVolume) + .tag(order.Comment) + .build(); + } + default -> throw new RuntimeException("Error in mapping.." + order.OrderType.name()); + } + } catch (Exception e) { + throw new RuntimeException("Error in formatting - " + order.Ticket, e); + } + } + + @Override + protected long getServerTimeZoneInMillis() { + return ZonedDateTime.of(this.api.ServerTime(), ZoneId.of("UTC")).toInstant().toEpochMilli(); + } + + @Override + protected String getAccountType() { + return this.api.Account.Type; + } + + @Override + public void partialCloseOrder(String orderId, double qty) throws BrokerException { + // TODO implement status check - isNotStarted + var existingOrder = getOrderDetails(orderId); + try { + this.api.OrderClose( + Long.parseLong(existingOrder.getOrderId()), + existingOrder.getExchangeScript(), + 0, + qty, + OrderType.valueOf( + BrokerOrderType.findValueByEnum(existingOrder.getType(), apiOrderTypes) + .replace(orderTypeSuffix, "")), + 0, + FillPolicy.FillOrKill); + log.info("Partially Closed {} with {} qty", orderId, qty); + } catch (Exception e) { + // If order is in PLACED state we are getting INVALID_REQUEST, hence handled. + if (e instanceof ServerException && e.getMessage().equals("POSITION_NOT_EXISTS") + || e.getMessage().equals("INVALID_REQUEST")) { + log.warn("OrderId, {} is already closed", orderId); + } else { + throw new RuntimeException(e); + } + } + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + try { + var orderDetails = getOrderDetails(brokerModifyOrderRequest.getOrderId()); + switch (brokerModifyOrderRequest.getType()) { + case BrokerOrderType.BUY_MARKET_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyMarketForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + 0, + OrderType.Buy, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 100, + "", + 0, + FillPolicy.FillOrKill); + } + case BrokerOrderType.BUY_LIMIT_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyLimitForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + orderRequest.getPrice(), + OrderType.BuyLimit, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + "", + 0, + FillPolicy.FlashFill); + } + case BrokerOrderType.BUY_STOP_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyStopForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + orderRequest.getPrice(), + OrderType.BuyStop, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + "", + 0, + FillPolicy.FlashFill); + } + case BrokerOrderType.SELL_MARKET_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellMarketForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + 0, + OrderType.Sell, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + "", + 0, + FillPolicy.FillOrKill); + } + case BrokerOrderType.SELL_LIMIT_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellLimitForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + orderRequest.getPrice(), + OrderType.SellLimit, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + "", + 0, + FillPolicy.FlashFill); + } + case BrokerOrderType.SELL_STOP_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellStopForex) brokerModifyOrderRequest; + this.api.OrderModify( + Long.parseLong(orderRequest.getOrderId()), + orderDetails.getExchangeScript(), + orderDetails.getQuantity(), + orderRequest.getPrice(), + OrderType.SellStop, + orderRequest.getStopLoss(), + orderRequest.getTakeProfit(), + 0, + "", + 0, + FillPolicy.FlashFill); + } + default -> + throw new IllegalArgumentException( + brokerModifyOrderRequest.getType() + " orderType not available"); + } + log.info("Succesfully modifed " + brokerModifyOrderRequest.getOrderId()); + return getOrderDetails(brokerModifyOrderRequest.getOrderId()); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + @Override + public void startFeed(Function, Void> liveTickConsumer) { + this.liveTickConsumer = liveTickConsumer; + this.addLiveListener(); + } + + @Override + public void disconnectFeed() { + this.liveTickConsumer = null; + api.OnQuote.listeners().clear(); + } + + @Override + public void subscribe(String exchangeScript) { + try { + var script = OmsUtil.getScript(exchangeScript); + api.Subscribe(script); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void unSubscribe(String exchangeScript) { + try { + var script = OmsUtil.getScript(exchangeScript); + api.Unsubscribe(script); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public List getHistoricalDataFromProvider( + NseScriptMetadata nseScriptMetadata, DateTime from, DateTime to, TimeFrame timeFrame) { + List result = new ArrayList<>(); + if (Days.daysBetween(from, to).getDays() <= 25) { + try { + result.addAll(getCandlesForMonth(nseScriptMetadata.getId().getScript(), from, to)); + } catch (IOException e) { + log.error("Exception getting historical data", e); + throw new RuntimeException(e); + } + } else { + var start = from; + do { + try { + result.addAll( + getCandlesForMonth(nseScriptMetadata.getId().getScript(), start, start.plusDays(25))); + start = start.plusDays(25); + } catch (IOException e) { + log.error("Exception getting historical data", e); + throw new RuntimeException(e); + } + } while (start.isBefore(to)); + } + result.sort(Comparator.comparing(Candle::getBeginTs)); + return CandleBucketingUtil.bucketCandles( + nseScriptMetadata.getId().getScript(), result, timeFrame); + } + + public synchronized List getCandlesForMonth( + String instrumentToken, DateTime from, DateTime to) throws IOException { + var candles = Collections.synchronizedList(new ArrayList()); + var countDownLatch = new CountDownLatch(1); + var serverTzInMillis = api.ServrTimeZoneInMinutes() * 60 * 1000; + OnQuoteHistory quoteListener = + (mt5API, args) -> { + if (!args.Bars.isEmpty()) { + log.info( + "{}: {} - {}; Spread - {}", + args.Symbol, + args.Bars.get(0).Time, + args.Bars.get(args.Bars.size() - 1).Time, + args.Bars.get(0).Spread); + } + for (int i = 0; i < args.Bars.size(); i++) { + var bar = args.Bars.get(i); + var epochTs = + bar.Time.atZone(ZoneId.of("UTC")).toInstant().toEpochMilli() - serverTzInMillis; + log.debug("{}", to); + if (new DateTime(epochTs).isAfter(from)) { + var newCandle = + new Candle( + new Date(epochTs), + bar.OpenPrice, + bar.HighPrice, + bar.LowPrice, + bar.ClosePrice, + 0.0, + bar.Spread, + TimeFrame.MINUTE_1); + candles.add(newCandle); + } + } + countDownLatch.countDown(); + }; + api.OnQuoteHistory.addListener(quoteListener); + + api.RequestQuoteHistoryMonth( + instrumentToken, to.getYear(), to.getMonthOfYear(), to.getDayOfMonth()); + try { + countDownLatch.await(); + api.OnQuoteHistory.listeners.clear(); + } catch (InterruptedException e) { + log.error("Exception while waiting for candle fetch", e); + } + candles.sort(Comparator.comparing(Candle::getBeginTs)); + return candles; + } + + @Override + public List getSymbols() { + return List.of(this.api.Symbols.Infos); + } + + @Override + public String getGroupName(String symbol) { + return this.api.Symbols.GetGroup(symbol).GroupName.split("\\\\")[0]; + } + + public static void main(String[] args) throws ConnectException { + var mt5BrokerConfigDetails = new BrokerConfigDetails.Mt5BrokerConfigDetails(); + mt5BrokerConfigDetails.setUsername("210527124"); + mt5BrokerConfigDetails.setPassword("Dy3YPa9E"); + mt5BrokerConfigDetails.setHost("34.105.200.212"); + mt5BrokerConfigDetails.setPort(443); + MT5Broker mt5Broker = new MT5Broker(mt5BrokerConfigDetails); + mt5Broker.loadHistoryOrders(); + // System.out.println(orderHistory.size()); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/Mt5BacktestBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/Mt5BacktestBroker.java new file mode 100644 index 00000000000..388c36a12d5 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5/Mt5BacktestBroker.java @@ -0,0 +1,566 @@ +package io.quantum.trading.brokers.forex.mt5; + +import static io.quantum.trading.brokers.BrokerOrderStatus.*; +import static io.quantum.trading.exception.ApiError.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quantum.trading.brokers.*; +import io.quantum.trading.brokers.Currency; +import io.quantum.trading.brokers.forex.ForexBroker; +import io.quantum.trading.entities.BrokerConfigDetails; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.redis.RedisCrudService; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.ConcurrentSkipListSet; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; + +@Slf4j +public class Mt5BacktestBroker extends ForexBroker implements AllowBacktesting { + private final RedisCrudService redisCrudService; + private final Map brokerOrderDetails; + private final Map> ordersByScript; + + public Mt5BacktestBroker( + BrokerConfigDetails.Mt5BacktestBrokerConfigDetails mt5BacktestBrokerConfigDetails, + RedisCrudService redisCrudService) { + this.redisCrudService = redisCrudService; + this.brokerOrderDetails = new HashMap<>(); + ordersByScript = new HashMap<>(); + } + + @Override + public double getBalance() throws BrokerException { + return 0; + } + + @Override + public Currency getCurrency() throws BrokerException { + return Currency.USD; + } + + @Override + public boolean testConnection() throws BrokerException { + // No-op + return true; + } + + @Override + public void disConnect() throws BrokerException { + // No-op + } + + private Candle getCurrentCandle(String script) { + return redisCrudService.getKeyValue(script, Candle.class).orElseThrow(); + } + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + var currentCandle = getCurrentCandle(brokerPlaceOrderRequest.getExchangeScript()); + var orderTime = new DateTime(currentCandle.getEndTs()).plusMillis(1).getMillis(); + var brokerOrderDetail = + switch (brokerPlaceOrderRequest.getType()) { + case BrokerOrderType.BUY_MARKET_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyMarketForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() <= orderRequest.getStopLoss()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Stoploss, %s should be lesser than current market price, %s", + orderRequest.getStopLoss(), currentCandle.getClose())); + } + yield BrokerOrderDetail.BuyMarketForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.POSITION_OPEN) + .openTs(orderTime) + .orderUpdateTs(orderTime) + .avgOpenPrice(currentCandle.getClose()) + .avgClosePrice(0.0) + .closeTs(0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .filledQuantity(0) + .tag(brokerPlaceOrderRequest.getTag()) + .build(); + } + case BrokerOrderType.BUY_LIMIT_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyLimitForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() <= orderRequest.getPrice()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Limit price, %s should be lesser than current market price, %s", + orderRequest.getPrice(), currentCandle.getClose())); + } + yield BrokerOrderDetail.BuyLimitForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.PLACED) + .openTs(0) + .closeTs(0) + .orderUpdateTs(orderTime) + .avgOpenPrice(orderRequest.getPrice()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .price(orderRequest.getPrice()) + .filledQuantity(0) + .tag(brokerPlaceOrderRequest.getTag()) + .build(); + } + case BrokerOrderType.BUY_STOP_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.BuyStopForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() >= orderRequest.getPrice()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Limit price, %s should be greater than current market price, %s", + orderRequest.getPrice(), currentCandle.getClose())); + } + yield BrokerOrderDetail.BuyStopForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.PLACED) + .openTs(0) + .closeTs(0) + .orderUpdateTs(orderTime) + .avgOpenPrice(orderRequest.getPrice()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .price(orderRequest.getPrice()) + .filledQuantity(0) + .tag(orderRequest.getTag()) + .build(); + } + case BrokerOrderType.SELL_MARKET_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellMarketForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() >= orderRequest.getStopLoss()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Stoploss, %s should be greater than current market price, %s", + orderRequest.getStopLoss(), currentCandle.getClose())); + } + yield BrokerOrderDetail.SellMarketForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.POSITION_OPEN) + .openTs(orderTime) + .closeTs(0) + .orderUpdateTs(orderTime) + .avgOpenPrice(currentCandle.getClose()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .filledQuantity(0) + .tag(orderRequest.getTag()) + .build(); + } + case BrokerOrderType.SELL_LIMIT_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellLimitForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() >= orderRequest.getStopLoss()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Stoploss, %s should be greater than current market price, %s", + orderRequest.getStopLoss(), currentCandle.getClose())); + } + yield BrokerOrderDetail.SellLimitForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.PLACED) + .openTs(0) + .closeTs(0) + .orderUpdateTs(orderTime) + .avgOpenPrice(orderRequest.getPrice()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .price(orderRequest.getPrice()) + .filledQuantity(0) + .tag(orderRequest.getTag()) + .build(); + } + case BrokerOrderType.SELL_STOP_FOREX -> { + var orderRequest = (BrokerPlaceOrderRequest.SellStopForex) brokerPlaceOrderRequest; + if (currentCandle.getClose() <= orderRequest.getPrice()) { + throw new BrokerException( + ApiError.BROKER_ORDER_INVALID, + String.format( + "Limit price, %s should be lesser than current market price, %s", + orderRequest.getPrice(), currentCandle.getClose())); + } + yield BrokerOrderDetail.SellStopForex.builder() + .type(orderRequest.getType()) + .exchangeScript(brokerPlaceOrderRequest.getExchangeScript()) + .quantity(brokerPlaceOrderRequest.getQuantity()) + .stopLoss(orderRequest.getStopLoss()) + .takeProfit(orderRequest.getTakeProfit()) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.PLACED) + .openTs(0) + .closeTs(0) + .orderUpdateTs(orderTime) + .avgOpenPrice(orderRequest.getPrice()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .price(orderRequest.getPrice()) + .filledQuantity(0) + .tag(orderRequest.getTag()) + .build(); + } + default -> + throw new IllegalArgumentException( + brokerPlaceOrderRequest.getType() + " orderType not available"); + }; + + var orderId = brokerOrderDetail.getOrderId(); + var script = brokerPlaceOrderRequest.getExchangeScript(); + brokerOrderDetails.put(orderId, brokerOrderDetail); + if (!ordersByScript.containsKey(script)) { + ordersByScript.put(script, new ConcurrentSkipListSet<>()); + } + ordersByScript.get(script).add(orderId); + + return brokerOrderDetail; + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + var orderDetail = getOrderDetails(brokerModifyOrderRequest.getOrderId()); + switch (brokerModifyOrderRequest.getType()) { + case BrokerOrderType.BUY_MARKET_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyMarketForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + } + case BrokerOrderType.BUY_LIMIT_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyLimitForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + ((BrokerOrderDetail.BuyLimitForex) orderDetail).setPrice(orderRequest.getPrice()); + } + case BrokerOrderType.BUY_STOP_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.BuyStopForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + ((BrokerOrderDetail.BuyStopForex) orderDetail).setPrice(orderRequest.getPrice()); + } + case BrokerOrderType.SELL_MARKET_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellMarketForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + } + case BrokerOrderType.SELL_LIMIT_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellLimitForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + ((BrokerOrderDetail.SellLimitForex) orderDetail).setPrice(orderRequest.getPrice()); + } + case BrokerOrderType.SELL_STOP_FOREX -> { + var orderRequest = (BrokerModifyOrderRequest.SellStopForex) brokerModifyOrderRequest; + orderDetail.setStopLoss(orderRequest.getStopLoss()); + orderDetail.setTakeProfit(orderRequest.getTakeProfit()); + ((BrokerOrderDetail.SellStopForex) orderDetail).setPrice(orderRequest.getPrice()); + } + default -> + throw new IllegalArgumentException( + brokerModifyOrderRequest.getType() + " orderType not available"); + } + ; + + return orderDetail; + } + + @Override + public void cancelOrder(String orderId) throws BrokerException { + var orderDetail = brokerOrderDetails.get(orderId); + if (orderDetail.getOrderStatus() == BrokerOrderStatus.PLACED) { + orderDetail.setOrderStatus(BrokerOrderStatus.CANCELLED); + ordersByScript.get(orderDetail.getExchangeScript()).remove(orderId); + } else { + throw new BrokerException( + ApiError.ORDER_CANNOT_CANCEL, orderId, orderDetail.getOrderStatus()); + } + } + + @Override + public void closeOrder(String orderId) throws BrokerException { + var orderDetail = brokerOrderDetails.get(orderId); + var currentCandle = getCurrentCandle(orderDetail.getExchangeScript()); + if (orderDetail.getOrderStatus() == BrokerOrderStatus.PLACED) { + orderDetail.setOrderStatus(BrokerOrderStatus.CANCELLED); + orderDetail.setAvgClosePrice(0); + orderDetail.setFilledQuantity(0); + orderDetail.setCloseTs(0); + orderDetail.setProfitAndLoss(BigDecimal.valueOf(0)); + } else if (orderDetail.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN) { + orderDetail.setOrderStatus(BrokerOrderStatus.COMPLETE); + orderDetail.setAvgClosePrice(currentCandle.getClose()); + orderDetail.setFilledQuantity(orderDetail.getQuantity()); + orderDetail.setCloseTs(currentCandle.getBeginTs().getTime()); + orderDetail.setProfitAndLoss( + BigDecimal.valueOf(orderDetail.getAvgClosePrice()) + .subtract(BigDecimal.valueOf(orderDetail.getAvgOpenPrice()))); + } + ordersByScript.get(orderDetail.getExchangeScript()).remove(orderId); + } + + private void closeOrder(String orderId, double closePrice) throws BrokerException { + var orderDetail = brokerOrderDetails.get(orderId); + var currentCandle = getCurrentCandle(orderDetail.getExchangeScript()); + if (orderDetail.getOrderStatus() == BrokerOrderStatus.PLACED) { + orderDetail.setOrderStatus(BrokerOrderStatus.CANCELLED); + orderDetail.setAvgClosePrice(0); + orderDetail.setFilledQuantity(0); + orderDetail.setCloseTs(0); + orderDetail.setProfitAndLoss(BigDecimal.valueOf(0)); + } else if (orderDetail.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN) { + orderDetail.setOrderStatus(BrokerOrderStatus.COMPLETE); + orderDetail.setAvgClosePrice(closePrice); + orderDetail.setFilledQuantity(orderDetail.getQuantity()); + orderDetail.setCloseTs(currentCandle.getBeginTs().getTime()); + orderDetail.setProfitAndLoss( + BigDecimal.valueOf(orderDetail.getAvgClosePrice()) + .subtract(BigDecimal.valueOf(orderDetail.getAvgOpenPrice()))); + } + ordersByScript.get(orderDetail.getExchangeScript()).remove(orderId); + } + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + if (brokerOrderDetails.containsKey(orderId)) { + return brokerOrderDetails.get(orderId); + } else { + throw new BrokerException(ApiError.ORDER_NOT_FOUND, orderId); + } + } + + @Override + protected long getServerTimeZoneInMillis() { + return 0; + } + + @Override + protected String getAccountType() { + return null; + } + + @Override + public void partialCloseOrder(String orderId, double qty) throws BrokerException {} + + @Override + public boolean isDemoAcc() { + return true; + } + + @Override + public synchronized void synchronizeOrders() { + for (var script : ordersByScript.keySet()) { + Candle currCandle; + var currCandleOpt = redisCrudService.getKeyValue(script, Candle.class); + if (currCandleOpt.isEmpty()) continue; + currCandle = currCandleOpt.get(); + + for (var orderId : ordersByScript.get(script)) { + var order = brokerOrderDetails.get(orderId); + if (order != null) { + switch (order.getType()) { + case BrokerOrderType.BUY_MARKET_FOREX -> { + if (order.getTakeProfit() != 0.0 && currCandle.getHigh() >= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + } + + if (order.getStopLoss() != 0.0 && currCandle.getLow() <= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + } + case BrokerOrderType.SELL_MARKET_FOREX -> { + if (order.getTakeProfit() != 0.0 && currCandle.getLow() <= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + } + + if (order.getStopLoss() != 0.0 && currCandle.getHigh() >= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + } + case BrokerOrderType.BUY_LIMIT_FOREX -> { + // Order triggered + if (order.getOrderStatus() == BrokerOrderStatus.PLACED + && currCandle.getLow() <= order.getAvgOpenPrice()) { + order.setOrderStatus(BrokerOrderStatus.POSITION_OPEN); + order.setOpenTs(currCandle.getBeginTs().getTime()); + order.setFilledQuantity(order.getQuantity()); + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getLow() <= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + continue; + } + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getLow() <= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + continue; + } + + // TGT triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getTakeProfit() != 0.0 + && currCandle.getHigh() >= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + continue; + } + } + case BrokerOrderType.SELL_LIMIT_FOREX -> { + // Order triggered + if (order.getOrderStatus() == BrokerOrderStatus.PLACED + && currCandle.getHigh() >= order.getAvgOpenPrice()) { + order.setOrderStatus(BrokerOrderStatus.POSITION_OPEN); + order.setOpenTs(currCandle.getBeginTs().getTime()); + order.setFilledQuantity(order.getQuantity()); + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getHigh() >= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + continue; + } + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getHigh() >= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + continue; + } + + // TGT triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getTakeProfit() != 0.0 + && currCandle.getLow() <= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + continue; + } + } + case BrokerOrderType.BUY_STOP_FOREX -> { + // Order triggered + if (order.getOrderStatus() == BrokerOrderStatus.PLACED + && currCandle.getLow() >= order.getAvgOpenPrice()) { + order.setOrderStatus(BrokerOrderStatus.POSITION_OPEN); + order.setOpenTs(currCandle.getBeginTs().getTime()); + order.setFilledQuantity(order.getQuantity()); + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getLow() <= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + continue; + } + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getLow() <= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + continue; + } + + // TGT triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getTakeProfit() != 0.0 + && currCandle.getHigh() >= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + continue; + } + } + case BrokerOrderType.SELL_STOP_FOREX -> { + // Order triggered + if (order.getOrderStatus() == BrokerOrderStatus.PLACED + && currCandle.getLow() <= order.getAvgOpenPrice()) { + order.setOrderStatus(BrokerOrderStatus.POSITION_OPEN); + order.setOpenTs(currCandle.getBeginTs().getTime()); + order.setFilledQuantity(order.getQuantity()); + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getHigh() >= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + } + continue; + } + + // SL triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getStopLoss() != 0.0 + && currCandle.getHigh() >= order.getStopLoss()) { + closeOrder(orderId, order.getStopLoss()); + continue; + } + + // TGT triggered + if (order.getOrderStatus() == BrokerOrderStatus.POSITION_OPEN + && order.getTakeProfit() != 0.0 + && currCandle.getLow() <= order.getTakeProfit()) { + closeOrder(orderId, order.getTakeProfit()); + continue; + } + } + } + } + } + } + } + + @Override + public boolean isInBacktestMode() { + return true; + } + + @Override + public boolean isUtMode() { + return false; + } + + public static void main(String[] args) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + var data = + objectMapper.writeValueAsString( + new Candle( + new Date(), 1.34974, 1.34984, 1.34954, 1.34964, 0.0, 0.0, TimeFrame.MINUTE_1)); + System.out.println(data); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Copier5ApiTrader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Copier5ApiTrader.java new file mode 100644 index 00000000000..04278898a75 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Copier5ApiTrader.java @@ -0,0 +1,331 @@ +package io.quantum.trading.brokers.forex.mt5protocol; // import cn.hutool.core.util.StrUtil; + +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.MT5API; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.OrderType; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.SymbolInfo; +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.ThreadPool; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * @author hipil + */ +public class Copier5ApiTrader extends MT5API { + + public int i; + + public static ScheduledExecutorService scheduledExecutorService = + Executors.newScheduledThreadPool(1000); + + ScheduleTask scheduleTask; + String invalidVolumeCode = "INVALID_VOLUME"; + String marketCloseCode = "MARKET_CLOSED"; + String noMoneyCode = "NO_MONEY"; + String investorMode = "Investor mode"; + String eurusd = "EURUSD"; + + public Copier5ApiTrader(long user, String password, String host, Integer port) { + super(user, password, host, port); + } + + public void connect2Broker() throws Exception { + try { + this.OnConnectProgress.addListener( + (mt5Api, connectEventArgs) -> { + if (connectEventArgs != null) { + // System.out.println(mt5Api.User + "'s progress is " + + // connectEventArgs.Progress); + } + }); + this.OnOrderUpdate.addListener( + (mt5Api, orderUpdate) -> { + if (orderUpdate != null) { + System.out.println(mt5Api.User + " " + orderUpdate.Type); + try { + mt5Api.RequestDealHistory( + LocalDateTime.now().minus(1, ChronoUnit.MONTHS), LocalDateTime.now()); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + System.out.println("orderUpdate is null."); + } + }); + this.OnTradeHistory.addListener((mt5Api, orderHistoryEventArgs) -> {}); + this.Connect(); + // List eurusds = Arrays.stream(this.Symbols.Infos).map(info -> + // info.Name).filter(symbol -> StrUtil.containsAnyIgnoreCase(symbol, + // "EURUSD")).sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList()); + // eurusd = eurusds.get(0); + for (SymbolInfo item : Symbols.Infos) { + if (item.Name.contains("EURUSD") || item.Name.contains("eurusd")) { + eurusd = item.Name; + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new Exception(e.getMessage()); + } finally { + // scheduledExecutorService.scheduleWithFixedDelay(new ScheduleTask(this), 30, 30, + // TimeUnit.SECONDS); + ThreadPool.QueueUserWorkItem(new ScheduleTask(this)); + } + } + + private class ScheduleTask implements Runnable { + Copier5ApiTrader copier5ApiTrader; + + public ScheduleTask(Copier5ApiTrader copier5ApiTrader) { + this.copier5ApiTrader = copier5ApiTrader; + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + try { + // copier5ApiTrader.ExecutionTimeout = 30000; + copier5ApiTrader.OrderSend(eurusd, 0.0, 0.0, OrderType.Buy); + } catch (Exception exception) { + if (invalidVolumeCode.equalsIgnoreCase(exception.getMessage())) { + // System.out.println(i + " " + copier5ApiTrader.User + + // invalidVolumeCode); + } else if (marketCloseCode.equalsIgnoreCase(exception.getMessage())) { + // System.out.println(i + " " + copier5ApiTrader.User + + // marketCloseCode); + } else if (noMoneyCode.equalsIgnoreCase(exception.getMessage())) { + // System.out.println(i + " " + copier5ApiTrader.User + + // noMoneyCode); + } else if (exception.getMessage().contains(investorMode)) { + // System.out.println(i + " " + copier5ApiTrader.User + + // investorMode); + } else { + exception.printStackTrace(); + try { + copier5ApiTrader.Disconnect(); + copier5ApiTrader.Connect(); + // List eurusds = Arrays.stream(copier5ApiTrader.Symbols.Infos).map(info -> + // info.Name).filter(symbol -> StrUtil.containsAnyIgnoreCase(symbol, + // "EURUSD")).sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList()); + // eurusd = eurusds.get(0); + for (SymbolInfo item : Symbols.Infos) { + if (item.Name.contains("EURUSD") || item.Name.contains("eurusd")) { + eurusd = item.Name; + break; + } + } + System.out.println(i + " " + copier5ApiTrader.User + " Reconnected"); + } catch (Exception e) { + e.printStackTrace(); + System.out.println(i + " " + copier5ApiTrader.User + " Not Reconnected"); + } + } + } + } + } + } + + public static void main(String[] args) { + List exclude = new LinkedList<>(); + exclude.add(8); + exclude.add(11); + exclude.add(12); + exclude.add(72); + exclude.add(74); + exclude.add(124); + exclude.add(128); + long[] user = { + 2000072 + }; // , 2000038, 2000112, 2000099, 2000113, 2000100, 2000114, 2000064, 2000095, 2000096, + // 2000123, 2000136, 2000135, 2000076, 2000079, 3000212, 2000147, 3000213, 2000124, 2000025, + // 2000048, 2000110, 2000044, 2000167, 2000155, 2000170, 2000050, 2000179, 2000178, 2000115, + // 2000173, 2000191, 2000193, 2000192, 2000206, 2000119, 2000085, 2000198, 2000200, 2000211, + // 2000213, 2000215, 3000311, 3000312, 3000314, 3000315, 3000316, 3000317, 3000318, 3000319, + // 3000320, 3000321, 3000322, 3000324, 3000323, 3000325, 3000326, 3000327, 3000328, 3000329, + // 3000330, 3000331, 3000333, 3000334, 3000335, 3000336, 3000337, 3000338, 3000340, 3000339, + // 3000341, 3000342, 3000343, 3000344, 3000345, 3000346, 3000347, 3000348, 3000349, 3000350, + // 3000351, 3000352, 3000353, 3000354, 3000358, 3000355, 3000356, 3000357, 3000359, 3000361, + // 3000362, 3000363, 3000364, 3000366, 3000367, 3000368, 3000369, 3000370, 3000365, 3000371, + // 3000372, 3000373, 3000375, 3000376, 3000377, 3000378, 3000379, 3000380, 3000374, 3000383, + // 3000384, 3000385, 3000386, 3000387, 3000388, 3000389, 3000390, 3000391, 3000392, 3000382, + // 3000397, 3000398, 3000399, 3000400, 3000401, 3000402, 3000403, 3000404, 2000890, 2000176, + // 2000236}; + String[] password = + new String[] { + "L9Cl3SP7", + "noah1234", + "lcl1987222", + "lcl1987222", + "L80008ll", + "L80008ll", + "zhu880203", + "segvr12c", + "wangyuhu666", + "wanggang233451", + "yue550077", + "wangyuhu666", + "wangyuhu666", + "wei282828", + "fny212514", + "abcd1234", + "noah1234", + "abcd1234", + "zhang911119", + "bruce123", + "xinhanyang1", + "qq84681067", + "qq84681067", + "lizishequ123", + "zhaobin1990", + "didi1258", + "noah1234", + "G7D8pqdQ", + "E7NOzjKg", + "jjyy1105", + "lk198320", + "W5S6ot8T", + "B9aVecfl", + "T4KArcyU", + "M9n1udDE", + "noah1234", + "XM891121", + "H5T76t0V", + "V1f6HC2F", + "P3C5zUSK", + "X7sRYt9w", + "56215487cc", + "esqvs7bq", + "yj5krdtw", + "1vacddtr", + "dweo1pej", + "vhf0vejq", + "4tasbgkm", + "8sfmykqe", + "ve0yptue", + "nj4zjwdx", + "nkysf0se", + "yrkaq6et", + "k3mewuan", + "dffnf4oa", + "xkha1grf", + "mrnwlho1", + "3fyunscf", + "msruod8w", + "bumy4dmo", + "kjxlqhr2", + "a6hgvprm", + "jqiug2ku", + "hbfwzv1n", + "cf5oytmt", + "tmm5nklh", + "mwydek7t", + "0horilej", + "tkqglk0s", + "4rubwgji", + "nazq0qqa", + "dsg5iddw", + "bmldja0r", + "vj7gjpnn", + "hk7roqor ", + "8covjpan", + "8vzszuid", + "yibc3hfb", + "dlweg2ou", + "6yfomspn", + "xyv6mmwq", + "7nvvykbj", + "tzcwk3no", + "skiojv7o", + "pj1cyqyh", + "q8zcbyvk", + "jvnv1lgh", + "v4fmrrpc", + "e0vuyvvo", + "gugk6ccd", + "f2cykfoi", + "6rraodgq", + "fiafsm4c", + "wqvx3lgr", + "nulruph4", + "jhub0hjg", + "vsynhqt2", + "vnipvck6", + "b4nemfnf", + "xttilvd1", + "llpziiz0", + "hpxh7jcx", + "xv1rdnvw", + "jnbmuxi0", + "bdmrxj6n", + "r2sbjjqg", + "xpries5a", + "ooxvej0a", + "efhcgk8f", + "anncxd8v", + "vmy3txlv", + "czdmjf4g", + "vfrzkdw7", + "qmiuo7pf", + "fqfkkuh4", + "chbr1fay", + "qsar7fqo", + "mxmpxbg7", + "hxzm5eis", + "x5sjaltl", + "c6gfztjw", + "n5eztwpo", + "jqcwh6eg", + "ypm3buje", + " jufyddq6", + "sse4smoj", + "0gzxplzv", + "gnftu7gt", + "245533566", + "wcq666888", + "J5fmHKSe", + "aa080524" + }; + for (int index = 0; index < user.length; index++) { + if (exclude.contains(index)) { + continue; + } + Copier5ApiTrader copier5ApiTrader; + copier5ApiTrader = new Copier5ApiTrader(user[index], password[index], "89.187.118.51", 1951); + // copier5ApiTrader = new Copier5ApiTrader(user[index], password[index], + // "8.208.77.179", 443); + copier5ApiTrader.i = index; + long start = System.currentTimeMillis(), end; + try { + copier5ApiTrader.connect2Broker(); + System.out.println( + index + + " " + + copier5ApiTrader.User + + " connected. time consuming:" + + (System.currentTimeMillis() - start) / 1000.0); + } catch (Exception e) { + if (e.getMessage().contains("INVALID_ACCOUNT")) { + exclude.add(index); + System.out.println(index); + } + e.printStackTrace(); + System.out.println( + index + + " " + + copier5ApiTrader.User + + " not connected.time consuming:" + + (System.currentTimeMillis() - start) / 1000.0); + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Examples.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Examples.java new file mode 100644 index 00000000000..7e20f361802 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/Examples.java @@ -0,0 +1,1113 @@ +package io.quantum.trading.brokers.forex.mt5protocol; + +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.*; +import java.io.IOException; +import java.net.SocketException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +public class Examples { + + static void startThreads() throws ConnectException { + new Thread( + () -> { + for (int i = 0; i < 5; i++) { + System.out.println("Starting thread " + i); + var exnessApi = new MT5API(175763435, "Dinesh@123", "15.184.133.53", 443); + var octaFxapi = new MT5API(213029201, "YYa7G$^j", "d51a1.octanetwork.net", 443); + try { + exnessApi.Connect(); + octaFxapi.Connect(); + } catch (ConnectException e) { + throw new RuntimeException(e); + } + } + }) + .start(); + } + + static void printThreadCount() { + while (true) { + ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); + ThreadGroup parentGroup; + + // Traverse through all thread groups to get all active threads + while ((parentGroup = rootGroup.getParent()) != null) { + rootGroup = parentGroup; + } + + // Get the number of active threads + int activeCount = rootGroup.activeCount(); + + // Create an array to hold the active threads + Thread[] threads = new Thread[activeCount]; + + // Enumerate the active threads into the array + rootGroup.enumerate(threads); + var threadCountMap = + new HashMap<>( + Map.of( + "MT5CmdHandler-175763435", 0L, + "MT5CmdHandler-213029201", 0L, + "MT5ConnectorThread-175763435", 0L, + "MT5ConnectorThread-213029201", 0L)); + threadCountMap + .keySet() + .forEach( + k -> { + threadCountMap.put( + k, + Arrays.stream(threads) + .filter(Objects::nonNull) + .filter(t -> t.getName().startsWith(k)) + .count()); + }); + + System.out.println(threadCountMap); + try { + Thread.sleep(1_000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public static void main(String[] args) throws Exception { + startThreads(); + printThreadCount(); + // Copier5ApiTrader.main(args); + // new Examples().run(); + } + + public void run() throws Exception { + Logger.OnMsg.addListener(new OnMsg()); + // String s = Broker.Search("fxdd"); + /// modifyAsync(); + // quotes(); + // orderHistory2(); + // orderHistory3(); + // orderHistory5(); + // marketOrder(); + // marketOrderAsync(); + // modifyOrder(); + // pendingOrder(); + // serversDat(); + // accountBalanceWithServersDat(); + // brokerSearch(); + // certificate(); + // tradeMode(); + // isInvestor(); + // orderUpdate(); + // partialClose(); + // test(); + // modifyCheck(); + // multipleAccounts(); + // orderHistory1(); + // quoteHitory(); + // tradeTest(); + // onDisconnect(); + // multiThreadTrading(); + // orderUpdate2(); + // pingPong(); + // nettingOrHedging(); + profit(); + } + + private void profit() throws Exception { + // This is a demo account + MT5API api = new MT5API(213029201, "YYa7G$^j", "d51a1.octanetwork.net", 443); + api.Connect(); + System.out.println("Connected. Balance = " + api.AccountBalance()); + // Closing all the other orders so as to not cause any confusions + for (Order order : api.GetOpenedOrders()) { + api.OrderClose(order.Ticket, order.Symbol, 0, order.Lots, order.OrderType, 0); + } + System.out.println("Connected. Balance after closing open orders = " + api.AccountBalance()); + Thread.sleep(5_000); + // Place an order for XAUUSD. + api.OrderSend("XAUUSD", 0.01, 0, OrderType.Buy, 0); + + // Even though the tick value for XAUUSD changes, the Profit field is always zero + while (true) { + for (Order order : api.GetOpenedOrders()) { + System.out.println(order.Ticket + " -- " + order.Symbol + " -- " + order.Profit); + } + Thread.sleep(2_000); + } + } + + private void nettingOrHedging() throws ConnectException { + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + api.Connect(); + System.out.println("Connected " + api.AccountMethod()); + } + + void loginid() throws IOException, ConnectException, TimeoutException { + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + api.LoginIdExServerUrls = new ArrayList(Arrays.asList("http://localhost:5101")); + api.Connect(); + System.out.println("Connected"); + } + + private void modifyAsync() throws ConnectException, IOException { + // MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + api.Connect(); + api.OrderModifyAsync( + api.GetRequestId(), 1792712504, "EURUSD", 0.1, 1, OrderType.BuyLimit, 0, 0, 0, null, 0); + } + + private static void orderUpdate2() + throws TimeoutException, IOException, ConnectException, InterruptedException { + + // MT5API api = new MT5API(2003, "KDejcx$385", "91.223.0.56", 443); + // MT5API api = new MT5API(210527124, "Dy3YPa9E", "34.105.200.212", 443); + MT5API api = new MT5API(21455, "1nJeS+Ae", "95.217.147.61", 443); + api.OnOrderUpdate.addListener( + new OnOrderUpdate() { + + @Override + public void invoke(MT5API sender, OrderUpdate update) { + System.out.println( + update.Type + " " + update.Order.OpenTime + " " + api.GetOpenedOrders().length); + } + }); + + api.Connect(); + api.Subscribe("EURUSD"); + + System.out.println(api.Account.Type); + Thread.sleep(100000); + } + + private void multiThreadTrading() throws Exception { + // MT5API qc2 = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + // MT5API api = new MT5API(5012314181L, "4uxvtuwu", "78.140.180.201", 443); + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + api.Connect(); + String group = api.Account.Type; + String symbol = "EURUSD"; + for (int i = 0; i < 30; i++) { + tradeAsync(api, symbol); + } + System.out.println("Orders sent"); + } + + private void tradeTest() throws ConnectException, IOException, InterruptedException { + String symbol = "EURUSD"; + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + // api.LastMessageFromServerTimeoutMs = 20000; + api.Connect(); + System.out.println("Connect to server successfully at " + new Date()); + System.out.println("Connected. Balance = " + api.AccountBalance()); + api.OnQuote.addListener((sender, quote) -> System.out.print(".")); + api.Subscribe(symbol); + while (true) { + if (!api.Connected()) { + System.out.println("\nThe connected of socket is false at " + new Date()); + try { + Order order = api.OrderSend("EURUSD", 0.01, 0, OrderType.Buy); + if (order != null) { + System.out.println("Order open successfully."); + break; + } else { + System.out.println("Order open fails."); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + System.out.println("\nThe connected of socket is true at " + new Date()); + } + Thread.sleep(20000); + } + } + + void quoteHitory() throws IOException, TimeoutException, ConnectException { + MT5API qc = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + qc.Connect(); + SymbolInfo info = qc.Symbols.GetInfo("EURUSD"); + SymGroup group = qc.Symbols.GetGroup("EURUSD"); + double minlots = group.MinLots(); + double digits = info.Digits; + double tv = qc.GetTickValue("EURUSD"); + double ts = info.tick_size; + + System.out.println("Connected"); + qc.OnQuoteHistory.addListener( + (mt5API, args) -> { + System.out.println(args.Bars.size()); + List h4bars = MT5API.ConvertToTimeframe(args.Bars, 240); + System.out.println(h4bars.size()); + }); + qc.RequestQuoteHistoryMonth("EURUSD", 2023, 3, 1); + } + + private void multipleAccounts() throws IOException, ConnectException, TimeoutException { + MT5API qc1 = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + qc1.Connect(); + System.out.println(qc1.AccountCompanyName() + " connected. Balance = " + qc1.AccountBalance()); + MT5API qc2 = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + qc2.Connect(); + System.out.println(qc2.AccountCompanyName() + " connected. Balance = " + qc2.AccountBalance()); + String symbol = "EURUSD"; + tradeAsync(qc1, symbol); + tradeAsync(qc2, symbol); + } + + void tradeAsync(MT5API qc, String symbol) { + (new Thread( + new Runnable() { + public void run() { + + try { + while (qc.GetQuote(symbol) == null) Thread.sleep(1); + double ask = qc.GetQuote(symbol).Ask; + Order order = qc.OrderSend(symbol, 0.01, ask, OrderType.Buy); + System.out.println(qc.AccountCompanyName() + " opened " + order.Ticket); + double bid = qc.GetQuote(symbol).Bid; + order = qc.OrderClose(order.Ticket, order.Symbol, bid, order.Lots, OrderType.Buy); + System.out.println(qc.AccountCompanyName() + " closed"); + } catch (Exception e) { + e.printStackTrace(); + } + } + })) + .start(); + } + + public void modifyCheck() { + String serverName = "Ava-Demo 1-MT5"; + try { + MT5API mt5Api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + mt5Api.OnOrderUpdate.addListener( + (mt5API, orderUpdate) -> { + if (orderUpdate.Type != null) + System.out.println(orderUpdate.Type + " " + orderUpdate.Order.Ticket); + }); + mt5Api.Connect(); + Arrays.stream(mt5Api.GetOpenedOrders()) + .forEach(o -> System.out.println(o.Symbol + " " + o.Ticket + " " + o.Lots)); + String eurusd = "AUDCAD"; + Order openOrder = + mt5Api.OrderSend( + eurusd, + 0.01, + 0, + OrderType.Buy, + 0.0, + 0.0, + 10000, + "", + 2000060, + mt5Api.GetFillPolicy(eurusd, OrderType.Buy)); + System.out.println("Order " + openOrder.Ticket + " is opened."); + Arrays.stream(mt5Api.GetOpenedOrders()) + .forEach(o -> System.out.println(o.Symbol + " " + o.Ticket + " " + o.Lots)); + mt5Api.OrderModify(openOrder.Ticket, null, 0, 0, openOrder.OrderType, 0.5, 1.5); + Arrays.stream(mt5Api.GetOpenedOrders()) + .forEach(o -> System.out.println(o.Symbol + " " + o.Ticket + " " + o.Lots)); + Order order = + Arrays.stream(mt5Api.GetOpenedOrders()) + .filter(o -> o.Ticket == openOrder.Ticket) + .findFirst() + .orElse(null); + if (order == null) { + System.out.println( + " After modification order " + openOrder.Ticket + " is not in opened orders."); + } else { + System.out.println( + "Order " + order.Ticket + " is still in opened orders and lots is " + order.Lots); + } + while (true) { + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } catch (Exception ignored) { + ignored.printStackTrace(); + } + } + + void test() { + String serverName = "Ava-Demo 1-MT5"; + try { + MT5API mt5Api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + mt5Api.Connect(); + Order openOrder = + mt5Api.OrderSend( + "EURUSD", + 0.01, + 0, + OrderType.Buy, + 0, + 0, + 1000, + "", + 0, + mt5Api.GetFillPolicy("EURUSD", OrderType.Buy)); + System.out.println("Order " + openOrder.Ticket + " is opened."); + mt5Api.OrderClose(openOrder.Ticket, "EURUSD", 0, openOrder.Lots, OrderType.Buy); + System.out.println("Order " + openOrder.Ticket + " is closed."); + Arrays.stream(mt5Api.GetOpenedOrders()) + .forEach(o -> System.out.println(o.Symbol + " " + o.Ticket + " " + o.Lots)); + Order order = + Arrays.stream(mt5Api.GetOpenedOrders()) + .filter(o -> o.Ticket == openOrder.Ticket) + .findFirst() + .orElse(null); + if (order == null) { + System.out.println("Order is not in opened orders."); + } else { + System.out.println( + "Order " + order.Ticket + " is still in opened orders and lots is " + order.Lots); + } + } catch (Exception ignored) { + } + } + + private void partialClose() throws IOException { + String serverName = "Ava-Demo 1-MT5"; + try { + MT5API mt5Api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + mt5Api.Connect(); + Order openOrder = + mt5Api.OrderSend( + "EURUSD", + 0.02, + 0, + OrderType.Buy, + 0, + 0, + 1000, + "", + 0, + mt5Api.GetFillPolicy("EURUSD", OrderType.Buy)); + System.out.println("opened " + openOrder.Ticket); + Order closeOrder = + mt5Api.OrderClose( + openOrder.Ticket, + "EURUSD", + 0, + 0.01, + OrderType.Buy, + 0, + mt5Api.GetFillPolicy("EURUSD", OrderType.Buy)); + Order[] orders = mt5Api.GetOpenedOrders(); + Order order = + Arrays.stream(mt5Api.GetOpenedOrders()) + .filter(o -> o.Ticket == openOrder.Ticket) + .findFirst() + .orElse(null); + if (order == null) { + System.out.println("we didnt find the partial close order."); + } else { + System.out.println("we find the partial close order."); + } + mt5Api.Disconnect(); + mt5Api.Connect(); + order = + Arrays.stream(mt5Api.GetOpenedOrders()) + .filter(o -> o.Ticket == openOrder.Ticket) + .findFirst() + .orElse(null); + if (order == null) { + System.out.println("we didnt find the partial close order."); + } else { + System.out.println("we find the partial close order."); + } + } catch (Exception ignored) { + } + } + + private void marketOrderSafe() throws Exception { + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + api.Connect(); + System.out.println("Connected. Balance = " + api.AccountBalance()); + String symbol = "EURUSD"; + api.Subscribe(symbol); + while (api.GetQuote(symbol) == null) Thread.sleep(1); + System.out.println("Got first quote " + api.GetQuote(symbol)); + Order o; + try { + o = api.OrderSend(symbol, 0.1, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 1000, null, 0); + } catch (java.net.ConnectException ex) { + api.Disconnect(); + api.Connect(); + o = api.OrderSend(symbol, 0.1, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 1000, null, 0); + } catch (SocketException ex) { + api.Disconnect(); + api.Connect(); + o = api.OrderSend(symbol, 0.1, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 1000, null, 0); + } + + System.out.println("Order " + o.Ticket + " opened at " + o.OpenPrice); + System.out.println("Opened orders:"); + for (Order order : api.GetOpenedOrders()) { + System.out.println(order.Ticket); + } + Thread.sleep(1000); + o = api.OrderClose(o.Ticket, o.Symbol, api.GetQuote(symbol).Bid, o.Lots, o.OrderType, 1000); + System.out.println("Closed at " + o.ClosePrice); + } + + private void orderUpdate() throws TimeoutException, IOException, ConnectException { + // MT5API api = new MT5API(66903271, "e3gmvlml", "51.116.173.111", 443); + MT5API api = new MT5API(1009023, "otso123", "20.48.6.157", 443); + api.OnOrderUpdate.addListener( + new OnOrderUpdate() { + @Override + public void invoke(MT5API sender, OrderUpdate update) { + + System.out.println( + update.Type + " " + update.Order.OpenTime + " " + api.GetOpenedOrders().length); + } + }); + api.Connect(); + + System.out.println(api.Account.Type); + } + + private void isInvestor() throws TimeoutException, IOException, ConnectException { + MT5API api = new MT5API(66903271, "e3gmvlml", "51.116.173.111", 443); + api.Connect(); + System.out.println(api.IsInvestor()); + System.out.println(api.Account.Type); + } + + void tradeMode() throws TimeoutException, IOException, ConnectException { + + // var api = new MT5API(62401092, "Chien123456", "35.157.110.232", 443); + // var api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + MT5API api = new MT5API(66903271, "e3gmvlml", "51.116.173.111", 443); + api.Connect(); + SymGroup g = api.Symbols.GetGroup("EURUSD"); + System.out.println(g.TradeMode); + System.out.println(isTradeSession(api, "EURUSD")); + } + + public boolean isTradeSession(MT5API api, String symbol) { + ArrayList> tradeSessionsForWeek = api.Symbols.Sessions.get(symbol).Trades; + ArrayList todaySessions = + tradeSessionsForWeek.get(api.ServerTime().getDayOfWeek().getValue()); + for (Session item : todaySessions) + if (api.ServerTime().getMinute() > item.StartTime + && api.ServerTime().getMinute() < item.EndTime) return true; + return false; + } + + private void certificate() throws IOException, TimeoutException, ConnectException { + String certPath = "C:\\Yandex.Disk\\Tim\\docs\\tmp\\2041174_HungDingFinancial_22.pfx"; + String certPass = "Dev@1234"; + MT5API api = + new MT5API( + 2041174, + "Dev@1234", + "20.48.6.157", + 443, + Files.readAllBytes(Paths.get(certPath)), + certPass); + api.Connect(); + String symbol = "USDCHF"; + System.out.println("Connected"); + Order o = api.OrderSend(symbol, 0.01, 0, OrderType.Buy); + System.out.println("Press any key..."); + } + + private void brokerSearch() throws Exception { + String json = Broker.Search("ava-"); + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine scriptEngine = manager.getEngineByName("JavaScript"); + scriptEngine.put("jsonString", json); + scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)"); + String prettyPrintedJson = (String) scriptEngine.get("result"); + System.out.println(prettyPrintedJson); + } + + private void onDisconnect() throws Exception { + // MT5API api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + api.OnConnectProgress.addListener(new OnConnectProgr()); + System.out.println("Connecting..."); + api.Connect(); + System.out.println("Connected"); + api.OnQuote.addListener((sender, quote) -> System.out.print(".")); + api.Subscribe("EURUSD"); + while (true) { + if (Duration.between(api.TimeOfLastMessageFromServer, LocalDateTime.now()).toMillis() + > 10000) { + System.out.println("Warning: no messsages from server for a long time"); + } + Thread.sleep(5000); + } + } + + class OnConnectProgr implements OnConnectProgress { + @java.lang.Override + public void invoke(MT5API sender, ConnectEventArgs args) { + if (args.Progress == ConnectProgress.Disconnect) { + System.out.println("Try reconnect " + sender.User); + for (int i = 0; i < 100; i++) { + try { + sender.Connect(); + System.out.println(sender.User + " reconnected"); + break; + } catch (Exception e) { + System.out.println("Waring: reconnect attempt #" + i + " fail"); + } + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + } + + private void symbols() throws Exception { + MT5API api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + System.out.println("Connecting..."); + api.Connect(); + System.out.println("Connected"); + for (SymbolInfo symbol : api.Symbols.Infos) System.out.println(symbol.Name); + System.out.println(api.AccountMargin()); + } + + private void accountBalanceWithServersDat() throws IOException { + Map servers = serversDat(); + String serverName = "Acetop-Demo"; + String[] addresses = servers.get(serverName); + for (String address : addresses) { + try { + Map.Entry hostAndPort = parseAdr(address); + MT5API api = new MT5API(8631960, "dd4444vv", hostAndPort.getKey(), hostAndPort.getValue()); + api.Connect(); + System.out.println(api.AccountBalance()); + break; + } catch (Exception e) { + continue; + } + } + } + + Map serversDat() throws IOException { + Server[] servers = MT5API.LoadServersDat("C:\\Users\\sam\\Downloads\\servers (1).dat"); + Map res = new HashMap<>(); + for (Server server : servers) { + String name = null; + if (server.ServerInfo != null) { + System.out.println(server.ServerInfo.ServerName); + name = server.ServerInfo.ServerName; + } + if (server.ServerInfoEx != null) { + System.out.println(server.ServerInfoEx.ServerName); + name = server.ServerInfoEx.ServerName; + } + if (name == null) throw new RuntimeException("Server name is null"); + List adr = new LinkedList<>(); + for (Access access : server.Accesses) + for (AddressRec addr : access.Addresses) { + System.out.println(addr.Address); + adr.add(addr.Address); + } + res.put(name, adr.toArray(new String[0])); + System.out.println(); + } + return res; + } + + Map.Entry parseAdr(String ip) { + int port; + int i = 0; + String host = ""; + while (i < ip.length() && ip.getBytes()[i] != (byte) ':') host += (char) ip.getBytes()[i++]; + if (i == ip.length()) port = 443; + else { + i++; + String strPort = ""; + while (i < ip.length()) strPort += (char) ip.getBytes()[i++]; + port = Integer.parseInt(strPort); + } + return new AbstractMap.SimpleEntry(host.trim(), port); + } + + private void orderHistory1() throws Exception { + // MT5API api = new MT5API(99083840, "ychqluy5", "112.126.96.179", 443); + // MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + MT5API api = new MT5API(75011662, "HArN2w&BC&7&tfK", "dc-r70-03.mt4servers.com", 443); + api.Connect(); + System.out.println("Connected. Balance = " + api.AccountBalance()); + api.OnTradeHistory.addListener( + new OnTradeHistory() { + @Override + public void invoke(MT5API sender, OrderHistoryEventArgs args) { + for (Order order : args.Orders) { + System.out.println(order.Ticket + " " + order.CloseTime); + } + } + }); + api.RequestDealHistory(LocalDateTime.now().minusDays(1000), LocalDateTime.now().plusDays(1)); + } + + void pendingOrder() throws Exception { + MT5API api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + System.out.println("Connecting..."); + api.Connect(); + System.out.println(api.AccountMargin()); + System.out.println("Connected"); + String symbol = "EURUSD.DEMO"; + api.Subscribe(symbol); + while (api.GetQuote(symbol) == null) Thread.sleep(1); + Order o = + api.OrderSend( + symbol, + 0.02, + api.GetQuote(symbol).Ask * 1.1, + OrderType.BuyStop, + 0, + 0, + 100, + null, + 0, + FillPolicy.FlashFill, + 0, + LocalDateTime.now().plusDays(3)); + System.out.println("Order " + o.Ticket + " opened at " + o.OpenPrice); + System.out.println("Opened orders:"); + for (Order order : api.GetOpenedOrders()) { + System.out.println(order.Ticket); + } + // o = api.OrderClose(o.Ticket, o.Symbol, o.OpenPrice, o.Lots, o.OrderType, 0); + // System.out.println("Closed"); + // System.out.println("Opened orders:"); + // for (Order order: api.GetOpenedOrders()) { + // System.out.println(order.Ticket); + // } + } + + void modifyOrder() throws Exception { + MT5API api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + System.out.println("Connecting..."); + api.Connect(); + System.out.println("Connected"); + String symbol = "EURUSD.DEMO"; + api.Subscribe(symbol); + while (api.GetQuote(symbol) == null) Thread.sleep(1); + Order o = + api.OrderSend(symbol, 0.02, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 100, null, 0); + System.out.println("Order " + o.Ticket + " opened at " + o.OpenPrice); + api.OrderModify(o.Ticket, o.Symbol, o.Lots, o.OpenPrice, OrderType.Buy, 0.9, 1.2, 0, null, 0); + System.out.println("Modified"); + for (Order order : api.GetOpenedOrders()) { + System.out.println(order.Ticket); + } + o = api.OrderClose(o.Ticket, o.Symbol, api.GetQuote(symbol).Bid, o.Lots, o.OrderType, 100); + System.out.println("Closed at " + o.ClosePrice); + } + + private void marketOrderAsync() throws Exception { + MT5API api = new MT5API(8631960, "dd4444vv", "mt5demo.acetopfx.com", 443); + api.Connect(); + System.out.println("Connected. Balance = " + api.AccountBalance()); + String symbol = "EURUSD.DEMO"; + api.Subscribe(symbol); + while (api.GetQuote(symbol) == null) Thread.sleep(1); + System.out.println("Got quote"); + api.OnOrderProgress.addListener( + new OnOrderProgress() { + @Override + public void invoke(MT5API sender, OrderProgress progress) { + System.out.println(progress.TradeRequest.RequestId + " " + progress.TradeResult.Status); + } + }); + int id = api.GetRequestId(); + api.OrderSendAsync( + id, symbol, 0.01, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 1000, null, 0); + } + + private void marketOrder() throws Exception { + // MT5API api = new MT5API(2232, "ASDF12345", "2.58.47.226", 443); + // MT5API api = new MT5API(60550297, "4cobucmw", "78.140.180.198", 443); + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + api.Connect(); + api.PlacedType = PlacedType.Manually; + System.out.println("Connected. Balance = " + api.AccountBalance()); + String symbol = "EURUSD"; + while (api.GetQuote(symbol) == null) Thread.sleep(1); + System.out.println("Got first quote " + api.GetQuote(symbol)); + Order o = + api.OrderSend(symbol, 0.1, api.GetQuote(symbol).Ask, OrderType.Buy, 0, 0, 1000, null, 0); + System.out.println("Order " + o.Ticket + " opened at " + o.OpenPrice); + System.out.println("Opened orders:"); + for (Order order : api.GetOpenedOrders()) { + System.out.println(order.Ticket); + } + Thread.sleep(1000); + o = api.OrderClose(o.Ticket, o.Symbol, api.GetQuote(symbol).Bid, o.Lots, o.OrderType, 1000); + System.out.println("Closed at " + o.ClosePrice); + } + + private void orderHistory() throws Exception { + // MT5API api = new MT5API(99083840, "ychqluy5", "112.126.96.179", 443); + // MT5API api = new MT5API(4000000825L, "InvPi8Ya3i3Ai", "185.97.161.224", 443); + // MT5API api = new MT5API(26891038, "v85Tu10mXfN4FpR", "35.230.145.63", 443); + MT5API api = new MT5API(26817585, "Readonly12", "35.230.145.63", 443); + api.Connect(); + + System.out.println("Connected. Balance = " + api.AccountBalance()); + final int[] dealsTotal = {0}; + final int[] ordersTotal = {0}; + + api.OnTradeHistory.addListener( + new OnTradeHistory() { + @Override + public void invoke(MT5API sender, OrderHistoryEventArgs args) + throws IOException, TimeoutException, ConnectException { + if (args.InternalDeals != null) + if (args.InternalDeals.size() > 0) { + DealInternal first = args.InternalDeals.get(0); + DealInternal last = args.InternalDeals.get(args.InternalDeals.size() - 1); + dealsTotal[0] += args.InternalDeals.size(); + System.out.println( + "Got deals " + + args.InternalDeals.size() + + " " + + first.OpenTimeAsDateTime() + + " deals total " + + dealsTotal[0]); + if (args.Action == 14) { + System.out.println("Requesting more deals..."); + sender.RequestDealHistory( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + args.InternalDeals); + } + } + if (args.InternalOrders != null) + if (args.InternalOrders.size() > 0) { + OrderInternal first = args.InternalOrders.get(0); + OrderInternal last = args.InternalOrders.get(args.InternalOrders.size() - 1); + ordersTotal[0] += args.InternalOrders.size(); + System.out.println( + "Got orders " + + args.InternalOrders.size() + + " " + + first.OpenTimeAsDateTime() + + " " + + last.OpenTimeAsDateTime() + + " orders total " + + ordersTotal[0]); + if (args.Action == 14) { + System.out.println("Requesting more orders..."); + sender.RequestOrderHistory( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + args.InternalOrders); + } + } + } + }); + + for (int i = 6; i < 12; i++) { + api.RequestDealHistory(2021, i); + Thread.sleep(1000); + } + for (int i = 1; i < 7; i++) { + api.RequestDealHistory(2022, i); + Thread.sleep(1000); + } + + for (int i = 6; i < 12; i++) { + api.RequestOrderHistory(2021, i); + Thread.sleep(1000); + } + for (int i = 1; i < 7; i++) { + api.RequestOrderHistory(2022, i); + Thread.sleep(1000); + } + } + + private void orderHistory2() throws Exception { + // MT5API api = new MT5API(26891038, "v85Tu10mXfN4FpR", "35.230.145.63", 443); + // MT5API api = new MT5API(1009023, "otso123", "20.48.6.157", 443); + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + + api.Connect(); + System.out.println("Connected to " + api.Account.Login + ". Balance = " + api.AccountBalance()); + SymGroup gr = api.Symbols.GetGroup("GOLD"); + System.out.println("MinVolume " + (double) gr.MinVolume / 100000000); + System.out.println("MaxVolume " + (double) gr.MaxVolume / 100000000); + System.out.println("VolumeStep " + (double) gr.VolumeStep / 100000000); + + LocalDateTime now = LocalDateTime.now(); + + api.OnTradeHistory.addListener( + (sender, args) -> { + System.out.println("Action = " + args.Action); + System.out.println("orders = " + args.Orders.size()); + System.out.println( + "internal_deals = " + + (args.InternalDeals == null ? "null" : args.InternalDeals.size())); + System.out.println( + "internal_orders = " + + (args.InternalOrders == null ? "null" : args.InternalOrders.size())); + System.out.println("end " + LocalDateTime.now()); + + if (args.Action == 14) { + if (args.InternalDeals != null) { + DealInternal last = args.InternalDeals.get(args.InternalDeals.size() - 1); + LocalDateTime start = + LocalDateTime.of( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + 1, + 0, + 0); + System.out.println("Requesting more deals for " + start.getMonth()); + sender.RequestDealHistory( + start.getYear(), start.getMonth().getValue(), args.InternalDeals); + System.out.println("Requesting more deals " + start.plusMonths(1) + " to " + now); + sender.RequestDealHistory(start.plusMonths(1), LocalDateTime.now()); + } else if (args.InternalOrders != null) { + OrderInternal last = args.InternalOrders.get(args.InternalOrders.size() - 1); + LocalDateTime start = + LocalDateTime.of( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + 1, + 0, + 0); + System.out.println( + "Requesting more orders for " + last.OpenTimeAsDateTime().getMonth()); + sender.RequestOrderHistory( + start.getYear(), start.getMonth().getValue(), args.InternalOrders); + System.out.println("Requesting more orders " + start.plusMonths(1) + " to " + now); + sender.RequestOrderHistory(start.plusMonths(1), LocalDateTime.now()); + } + } + }); + System.out.println("start " + LocalDateTime.now()); + // api.RequestOrderHistory(LocalDateTime.of(2005, 1, 1, 0, 0, 0), now); + api.RequestDealHistory(LocalDateTime.of(2005, 1, 1, 0, 0, 0), now); + // api.RequestDealHistory(now, now); + } + + private void orderHistory3() throws Exception { + MT5API api = new MT5API(26891038, "v85Tu10mXfN4FpR", "35.230.145.63", 443); + api.Connect(); + + System.out.println("Connected to " + api.Account.Login + ". Balance = " + api.AccountBalance()); + LocalDateTime now = LocalDateTime.now(); + final boolean[] DealHistoryRequestedByMonth = {false}; + final boolean[] OrderHistoryRequestedByMonth = {false}; + + api.OnTradeHistory.addListener( + (sender, args) -> { + System.out.println("Action = " + args.Action); + System.out.println("orders = " + args.Orders.size()); + System.out.println( + "internal_deals = " + + (args.InternalDeals == null ? "null" : args.InternalDeals.size())); + System.out.println( + "internal_orders = " + + (args.InternalOrders == null ? "null" : args.InternalOrders.size())); + System.out.println("end " + LocalDateTime.now()); + + if (args.Action == 14) { + if (args.InternalDeals != null) { + DealInternal last = args.InternalDeals.get(args.InternalDeals.size() - 1); + LocalDateTime start = + LocalDateTime.of( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + 1, + 0, + 0); + System.out.println("Requesting more deals for " + start.getMonth()); + sender.RequestDealHistory( + start.getYear(), start.getMonth().getValue(), args.InternalDeals); + if (!DealHistoryRequestedByMonth[0]) { + DealHistoryRequestedByMonth[0] = true; + System.out.println( + "Requesting more deals monthly " + last.OpenTimeAsDateTime() + " to " + now); + while (start.isBefore(LocalDateTime.now())) { + start = start.plusMonths(1); + sender.RequestDealHistory(start.getYear(), start.getMonth().getValue()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + } + + } else if (args.InternalOrders != null) { + OrderInternal last = args.InternalOrders.get(args.InternalOrders.size() - 1); + LocalDateTime start = + LocalDateTime.of( + last.OpenTimeAsDateTime().getYear(), + last.OpenTimeAsDateTime().getMonth().getValue(), + 1, + 0, + 0); + System.out.println( + "Requesting more orders for " + last.OpenTimeAsDateTime().getMonth()); + sender.RequestOrderHistory( + start.getYear(), start.getMonth().getValue(), args.InternalOrders); + if (!OrderHistoryRequestedByMonth[0]) { + OrderHistoryRequestedByMonth[0] = true; + System.out.println( + "Requesting more orders monthly " + last.OpenTimeAsDateTime() + " to " + now); + while (start.isBefore(LocalDateTime.now())) { + start = start.plusMonths(1); + sender.RequestOrderHistory(start.getYear(), start.getMonth().getValue()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + } + } + } + } + }); + System.out.println("start " + LocalDateTime.now()); + api.RequestOrderHistory(LocalDateTime.of(2005, 1, 1, 0, 0, 0), now); + // api.RequestDealHistory(LocalDateTime.of(2005, 1, 1, 0, 0, 0), now); + } + + int j = 1; + + private void orderHistory4() throws Exception { + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + api.Connect(); + System.out.println("Connected to " + api.Account.Login + ". Balance = " + api.AccountBalance()); + api.OnTradeHistory.addListener( + (sender, args) -> { + System.out.println("end " + LocalDateTime.now() + " " + j++ + " " + args.Orders.size()); + }); + System.out.println("start " + LocalDateTime.now()); + for (int i = 0; i < 50; i++) { + api.RequestOrderHistory(LocalDateTime.of(2023, 1, 1, 0, 0, 0), LocalDateTime.now()); + api.RequestDealHistory(LocalDateTime.of(2023, 1, 1, 0, 0, 0), LocalDateTime.now()); + Thread.sleep(100); + } + } + + private void orderHistory5() throws Exception { + MT5API api = new MT5API(101240050, "Ava112358", "3.10.134.148", 443); + api.Connect(); + System.out.println("Connected to " + api.Account.Login + ". Balance = " + api.AccountBalance()); + api.OnTradeHistory.addListener( + (sender, args) -> { + System.out.println("end " + LocalDateTime.now() + " " + args.Orders.size()); + }); + System.out.println("start " + LocalDateTime.now()); + // api.RequestOrderHistory(LocalDateTime.of(2023, 1, 1, 0, 0, 0), LocalDateTime.now()); + api.RequestDealHistory(LocalDateTime.of(2023, 1, 1, 0, 0, 0), LocalDateTime.now()); + } + + public void quotes() throws Exception { + // MT5API api = new MT5API(99083840, "ychqluy5", "112.126.96.179", 443); + // MT5API api = new MT5API(9956, "TYTY1212", "2.58.47.214", 443); + // MT5API api = new MT5API(45045, "msms1616", "95.216.98.166", 443); + // MT5API api = new MT5API(45035, "trade45035", "95.216.98.166", 443); + // MT5API api = new MT5API(56655349, "zcqj1aqp", "78.140.180.198", 443); + // MT5API api = new MT5API(50765, "A123456789", "211.72.241.36", 443); + // MT5API api = new MT5API(667028, "jf49fikz", "13.112.1.146", 1953); + // MT5API api = new MT5API(2000025, "bruce123", "89.187.118.51", 1951); + // MT5API api = new MT5API(7556796, "Aa2408550", "82.163.250.77", 443); + // MT5API api = new MT5API(107015354, "Colen@123", "18.143.116.66", 443); + // MT5API api = new MT5API(75071507, "-2QxOkPt", "27.111.161.145", 1950); + MT5API api = new MT5API(62333850, "tecimil4", "78.140.180.198", 443); + // MT5API api = new MT5API(21455, "1nJeS+Ae", "95.217.147.61", 443); + // MT5API api = new MT5API(2100051275 , "6qZpNgH@", "mt5demo.fxdd.com", 443); + api.Connect(); + String n = api.Account.UserName; + + System.out.println("Connected. Balance = " + api.AccountBalance()); + api.OnQuote.addListener( + new OnQuote() { + @Override + public void invoke(MT5API sender, Quote quote) { + System.out.println( + quote + + " " + + sender.AccountProfit() + + " " + + sender.GetOpenedOrders().length + + " " + + quote.Spread); + } + }); + api.Subscribe("EURUSD"); + } + + public void pingPong() throws Exception { + String symbol = "EURUSD"; + MT5API api = new MT5API(2100051275, "6qZpNgH@", "mt5demo.fxdd.com", 443); + api.Connect(); + if (api.PingPong()) System.out.println("PingPing success"); + } + + class OnMsg implements OnMsgHandler { + @Override + public void invoke(Object sender, String msg, MsgType type) { + if (type == MsgType.Trace) return; + String m = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()) + + " " + + type.toString().substring(0, 3) + + ": " + + sender.getClass().getSimpleName(); + m += " " + msg; + System.out.println(m); + } + } + + public static List ConvertToTimeframe(List bars, int minutes) { + if (minutes > 60) + if (minutes % 60 != 0) + throw new RuntimeException("If timeframe > 60 it should be in whole hours"); + if (bars.size() == 0) return new LinkedList(); + int i = 0; + if (minutes <= 60) while (bars.get(i).Time.getMinute() % minutes > 0) i++; + List res = new LinkedList(); + Bar bar = new Bar(); + bar.OpenPrice = bars.get(0).OpenPrice; + bar.Time = bars.get(0).Time; + bar.LowPrice = bars.get(0).LowPrice; + LocalDateTime time = bars.get(0).Time.plusMinutes(minutes); + if (minutes == 1440) + time = LocalDateTime.of(time.getYear(), time.getMonth(), time.getDayOfMonth(), 0, 0, 0); + for (; i < bars.size(); i++) { + if (bars.get(i).Time.isAfter(time) + || bars.get(i).Time.isEqual(time) + || i == bars.size() - 1) { + bar.ClosePrice = bars.get(i - 1).ClosePrice; + res.add(bar); + bar = new Bar(); + bar.OpenPrice = bars.get(i).OpenPrice; + bar.Time = bars.get(i).Time; + bar.LowPrice = bars.get(i).LowPrice; + while (time.isBefore(bar.Time) || time.isEqual(bar.Time)) time = time.plusMinutes(minutes); + if (minutes == 1440) { + LocalDateTime nextday = bars.get(i).Time.plusHours(24); + time = + LocalDateTime.of( + nextday.getYear(), nextday.getMonth(), nextday.getDayOfMonth(), 0, 0, 0); + } + } + if (bars.get(i).HighPrice > bar.HighPrice) bar.HighPrice = bars.get(i).HighPrice; + if (bars.get(i).LowPrice < bar.LowPrice) bar.LowPrice = bars.get(i).LowPrice; + } + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccMethod.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccMethod.java new file mode 100644 index 00000000000..37ca13a1421 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccMethod.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// Position accounting method +public enum AccMethod { + Default(0), + Netting(1), + Hedging(2); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (AccMethod.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private AccMethod(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static AccMethod forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Access.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Access.java new file mode 100644 index 00000000000..1dae9d49357 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Access.java @@ -0,0 +1,6 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class Access { + public AccessRec AccessRec; + public AddressRec[] Addresses; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessEx.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessEx.java new file mode 100644 index 00000000000..a2abe43385d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessEx.java @@ -0,0 +1,6 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class AccessEx { + public AccessRecEx AccessRec; + public AddressRecEx[] Addresses; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessInfo.java new file mode 100644 index 00000000000..0fbe6984cd9 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessInfo.java @@ -0,0 +1,45 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x10C, CharSet = CharSet.Unicode)]*/ +public class AccessInfo extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String ServerName; + /*[FieldOffset(64)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 36)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s40; + public byte[] s40; + /*[FieldOffset(100)]*/ public int s64; + /*[FieldOffset(104)]*/ public int s68; + /*[FieldOffset(108)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 160)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s6C; + public byte[] s6C; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 268; + AccessInfo st = new AccessInfo(); + st.ServerName = GetString(buf.Bytes(64)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s40 = new byte[36]; + st.s40 = new byte[36]; + for (int i = 0; i < 36; i++) { + st.s40[i] = buf.Byte(); + } + st.s64 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s68 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s6C = new byte[160]; + st.s6C = new byte[160]; + for (int i = 0; i < 160; i++) { + st.s6C[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRec.java new file mode 100644 index 00000000000..bf13e1d2815 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRec.java @@ -0,0 +1,47 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x164, CharSet = CharSet.Unicode)]*/ +public class AccessRec extends FromBufReader { + public static final int Size = 356; + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ + public String ServerName; + /*[FieldOffset(64)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s40; + public byte[] s40; + /*[FieldOffset(192)]*/ public int sC0; + /*[FieldOffset(196)]*/ public int sC4; + /*[FieldOffset(200)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 156)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sC8; + public byte[] sC8; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 356; + AccessRec st = new AccessRec(); + st.ServerName = GetString(buf.Bytes(64)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s40 = new byte[128]; + st.s40 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s40[i] = buf.Byte(); + } + st.sC0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC4 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sC8 = new byte[156]; + st.sC8 = new byte[156]; + for (int i = 0; i < 156; i++) { + st.sC8[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRecEx.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRecEx.java new file mode 100644 index 00000000000..58a6b7b162f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccessRecEx.java @@ -0,0 +1,58 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC58, CharSet = CharSet.Unicode)]*/ +public class AccessRecEx extends FromBufReader { + public static final int Size = 3160; + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String s0; + /*[FieldOffset(128)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s80; + /*[FieldOffset(256)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String s100; + /*[FieldOffset(512)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s200; + /*[FieldOffset(576)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 24)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s240; + public byte[] s240; + /*[FieldOffset(600)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String s258; + /*[FieldOffset(856)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 1024)]*/ public String s358; + /*[FieldOffset(2904)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sB58; + public byte[] sB58; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 3160; + AccessRecEx st = new AccessRecEx(); + st.s0 = GetString(buf.Bytes(128)); + st.s80 = GetString(buf.Bytes(128)); + st.s100 = GetString(buf.Bytes(256)); + st.s200 = GetString(buf.Bytes(64)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s240 = new byte[24]; + st.s240 = new byte[24]; + for (int i = 0; i < 24; i++) { + st.s240[i] = buf.Byte(); + } + st.s258 = GetString(buf.Bytes(256)); + st.s358 = GetString(buf.Bytes(2048)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sB58 = new byte[256]; + st.sB58 = new byte[256]; + for (int i = 0; i < 256; i++) { + st.sB58[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountLoader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountLoader.java new file mode 100644 index 00000000000..193720fe314 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountLoader.java @@ -0,0 +1,404 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.net.ConnectException; +import java.util.*; + +public class AccountLoader { + private Logger Log; + private MT5API QuoteClient; + CmdHandler CmdHandler; + Connection Connection; + + public AccountLoader(MT5API qc, CmdHandler cmdHandler, Connection connection) { + Log = new Logger(this); + QuoteClient = qc; + CmdHandler = cmdHandler; + Connection = connection; + QuoteClient.Symbols.Groups.clear(); + QuoteClient.Symbols.Sessions.clear(); + QuoteClient.Orders.Opened.clear(); + } + + public final void Parse(InBuf buf) { + try { + Msg status = Msg.forValue(buf.Int()); + if (status != Msg.DONE) { + throw new RuntimeException("Account info parse status = " + status); + } + while (buf.gethasData()) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var cmd = buf.Byte(); + byte cmd = buf.Byte(); + switch ((cmd & 0xFF)) { + case 0x07: // symbols + LoadSymbols(buf); + break; + case 0x11: // tickers + LoadTickers(buf); + break; + case 0x17: // server + QuoteClient.ServerDetails = LoadServer(buf); + break; + case 0x18: // mail recepients + MailRecipient[] mr = LoadMailRecepients(buf); + break; + case 0x1F: // order + QuoteClient.Orders.AddOrders(LoadOrders(buf)); + break; + case 0x24: // deal + QuoteClient.Orders.AddDeals(LoadDeals(buf)); + break; + case 0x25: // account + QuoteClient.Account = LoadAccount(buf); + break; + case 0x28: // spreads + LoadSpreads(buf); + break; + case 0x67: // subscriptions + LoadSubscriptions(buf); + break; + case 0x69: // subscription categories + int size = buf.Int(); + buf.Bytes(0x80 * size); + break; + case 0x78: // payments + LoadPayments(buf); + break; + case 0x79: // payments + case 0x80: + LoadPayments2(buf); + break; + case 0x84: + LoadSomeAccountData(buf); + break; + default: + break; + } + } + QuoteClient.GotAccountInfo = true; + Connection.SendPacket((byte) 0xA, new OutBuf()); + } catch (Exception ex) { + CmdHandler.AccountLoaderException = ex; + QuoteClient.OnConnectCall(ex, ConnectProgress.Exception); + } + } + + private void LoadSubscriptions(InBuf buf) { + Msg status = Msg.forValue(buf.Int()); + if (status == Msg.OK) return; + if (status != Msg.DONE) throw new RuntimeException(status.toString()); + int num = buf.Int(); + for (int i = 0; i < num; i++) { + buf.Bytes(1240); // vSubscriptionInfo + int size = buf.Int(); + buf.Bytes(size); + int count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(256); + count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(256); + count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(292); + count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(292); + } + } + + private void LoadPayments(InBuf buf) { + int num = buf.Int(); + for (int i = 0; i < num; i++) { + buf.Bytes(776); + int size = buf.Int(); + buf.Bytes(size); + int count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(528); + count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(208); + count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(112); + } + } + + private void LoadPayments2(InBuf buf) { + int num = buf.Int(); + for (int i = 0; i < num; i++) { + buf.Bytes(20); // vPaymentRec + int size = buf.Int(); // vPaymentRec + buf.Bytes(104); // vPaymentRec + buf.Bytes(size); + } + } + + private void LoadSomeAccountData(InBuf buf) { + buf.Bytes(3084); + int num = buf.Int(); + for (int i = 0; i < num; i++) buf.Bytes(1288); + } + + private void LoadSpreads(InBuf buf) { + Log.trace("LoadSpreads"); + Msg status = Msg.forValue(buf.Int()); + if (status == null) throw new RuntimeException("LoadSpreads status is null"); + ; + if (status == Msg.OK) { + return; + } + if (status != Msg.DONE) { + throw new RuntimeException(status.toString()); + } + int num = buf.Int(); + for (int i = 0; i < num; i++) { + LoadSpread(buf); + } + LoadRemoveList(buf); + } + + private void LoadSpread(InBuf buf) { + SpreadInfo si = buf.Struct(new SpreadInfo()); + int num = buf.Int(); + SpreadData[] buy = new SpreadData[num]; + for (int i = 0; i < num; i++) { + buy[i] = buf.Struct(new SpreadData()); + } + num = buf.Int(); + SpreadData[] sell = new SpreadData[num]; + for (int i = 0; i < num; i++) { + sell[i] = buf.Struct(new SpreadData()); + } + } + + private AccountRec LoadAccount(InBuf buf) { + return buf.Struct(new AccountRec()); + } + + private ArrayList LoadDeals(InBuf buf) { + Log.trace("LoadDeals"); + int updateID = buf.Int(); + int num = buf.Int(); + ArrayList list = new ArrayList(); + for (int i = 0; i < num; i++) { + if (Connection.TradeBuild < 1891) { + throw new UnsupportedOperationException(); + } + DealInternal d = buf.Struct(new DealInternal()); + list.add(d); + } + return list; + } + + private ArrayList LoadOrders(InBuf buf) { + Log.trace("LoadOrders"); + int updateID = buf.Int(); + int num = buf.Int(); + ArrayList list = new ArrayList(); + for (int i = 0; i < num; i++) { + if (Connection.TradeBuild < 1891) { + throw new UnsupportedOperationException(); + } + OrderInternal o = buf.Struct(new OrderInternal()); + list.add(o); + } + return list; + } + + private MailRecipient[] LoadMailRecepients(InBuf buf) { + Log.trace("LoadMailRecepients"); + return buf.Array(new MailRecipient()); + } + + private Map.Entry>> LoadServer( + InBuf buf) { + Log.trace("LoadServer"); + ServerRec sr = buf.Struct(new ServerRec()); + int num = buf.Int(); + ArrayList> list = + new ArrayList>(); + for (int i = 0; i < num; i++) list.add(LoadAccess(buf)); + return new AbstractMap.SimpleEntry<>(sr, list); + } + + private Map.Entry LoadAccess(InBuf buf) { + AccessInfo ai = buf.Struct(new AccessInfo()); + int num = buf.Int(); + ArrayList list = new ArrayList(); + for (int i = 0; i < num; i++) { + list.add(buf.Struct(new AddressRec())); + } + return new AbstractMap.SimpleEntry<>(ai, list.toArray(new AddressRec[0])); + } + + private void LoadTickers(InBuf buf) { + Log.trace("LoadTickers"); + int num = buf.Int(); + for (int i = 0; i < num; i++) { + if (Connection.SymBuild <= 1036) { + throw new UnsupportedOperationException(); + } else { + Ticker ticker = buf.Struct(new Ticker()); + } + } + } + + private void LoadSymbols(InBuf buf) throws ConnectException { + Log.trace("LoadSymbols"); + buf.SymBuild = Connection.SymBuild; + LoadSymBase(buf); + if (Connection.SymBuild >= 4072) LoadSymXX4072(buf); + Msg status = Msg.forValue(buf.Int()); + if (status == Msg.OK) { + Log.trace("DeleteDuplicatedSymbols"); + // DeleteDuplicatedSymbols(); + return; + } + if (status != Msg.DONE) { + throw new RuntimeException(status.toString()); + } + if (Connection.SymBuild <= 1891) { + throw new UnsupportedOperationException("SymBuild <= 1891"); + } + int num = buf.Int(); + QuoteClient.Symbols.Infos = new SymbolInfo[num]; + for (int i = 0; i < num; i++) { + // var size = Marshal.SizeOf(typeof(SymbolInfo)); + // var bytes = buf.Bytes(size); + SymbolInfo si = UDT.ReadStruct(buf, new SymbolInfo()); + QuoteClient.Symbols.Infos[i] = si; + // size = Marshal.SizeOf(typeof(SymGroup)); + // bytes = buf.Bytes(size); + SymGroup gr = UDT.ReadStruct(buf, new SymGroup()); + QuoteClient.Symbols.Groups.put(si.Name, gr); + QuoteClient.Symbols.Sessions.put(si.Name, LoadSessions(buf)); + // size = Marshal.SizeOf(typeof(C54)); + // bytes = buf.Bytes(size); + C54 sc54 = UDT.ReadStruct(buf, new C54()); + } + // if (QuoteClient.Symbols.Groups.values().size() < 100 && QuoteClient.Symbols.SymGroups.length + // < 100) + // for(SymGroup main : QuoteClient.Symbols.Groups.values()) + // { + // for (SymGroup slave : QuoteClient.Symbols.SymGroups) + // { + // String regex = slave.GroupName.replace("\\", "\\\\").replace("*", ".*"); + // Pattern pattern = Pattern.compile(regex); + // Matcher matcher = pattern.matcher(main.GroupName); + // if (matcher.matches()) + // main.CopyValues(slave); + // } + // } + LoadRemoveList(buf); + LoadSymbolSets(buf); + } + + void LoadSymXX4072(InBuf buf) { + buf.Bytes(656); // vSymXXInfo + int num = buf.Int(); + for (int i = 0; i < num; i++) { + buf.Bytes(932); // vSymYY + int count = buf.Int(); + for (int j = 0; j < count; j++) buf.Bytes(160); + } + } + + private void LoadRemoveList(InBuf buf) { + int num = buf.Int(); + int[] ar = new int[num]; // m_SymInfo.m_nId + for (int i = 0; i < num; i++) { + ar[i] = buf.Int(); + } + } + + private void LoadSymbolSets(InBuf buf) { + Msg status = Msg.forValue(buf.Int()); + if (status == Msg.OK) { + return; + } + if (status != Msg.DONE) { + throw new RuntimeException(status.toString()); + } + int num = buf.Int(); + for (int i = 0; i < num; i++) { + // var size = Marshal.SizeOf(typeof(SymbolSet)); + // var bytes = buf.Bytes(size); + SymbolSet ss = UDT.ReadStruct(buf, new SymbolSet()); + } + } + + private SymbolSessions LoadSessions(InBuf buf) { + ArrayList> quotes = new ArrayList<>(); + ArrayList> trades = new ArrayList<>(); + for (int i = 0; i < 7; i++) { + int num = buf.Int(); + ArrayList ses = new ArrayList(); + for (int j = 0; j < num; j++) { + Session s = UDT.ReadStruct(buf, new Session()); + ses.add(s); + } + quotes.add(ses); + num = buf.Int(); + ArrayList tr = new ArrayList(); + for (int j = 0; j < num; j++) { + Session s = UDT.ReadStruct(buf, new Session()); + tr.add(s); + } + trades.add(tr); + } + SymbolSessions ss = new SymbolSessions(); + ss.Quotes = quotes; + ss.Trades = trades; + return ss; + } + + private void LoadSymBase(InBuf buf) throws ConnectException { + short build = Connection.SymBuild; + if (build <= 1495) + throw new ConnectException( + "SymBuild: " + Connection.SymBuild); // return LoadBuild1495(pBufMan); + if (build <= 1613) + throw new ConnectException( + "SymBuild: " + Connection.SymBuild); // return LoadBuild1613(pBufMan); + if (build <= 1891) + throw new ConnectException( + "SymBuild: " + Connection.SymBuild); // return LoadBuild1891(pBufMan); + if (build <= 2017) { + // LoadBuild2017(buf); + throw new ConnectException("SymBuild: " + Connection.SymBuild); + // return; + } + if (build <= 2124) { + // LoadBuild2124(buf); + throw new ConnectException("SymBuild: " + Connection.SymBuild); + // return; + } + if (build <= 2204) { + // LoadBuild2204(buf); + throw new ConnectException("SymBuild: " + Connection.SymBuild); + // return; + } + LoadLastBuild(buf); + } + + private void LoadLastBuild(InBuf buf) { + QuoteClient.Symbols.Base = UDT.ReadStruct(buf, new SymBaseInfo()); + int num = buf.Int(); + SymGroup[] ar = new SymGroup[num]; + for (int i = 0; i < num; i++) { + SymGroup gr = UDT.ReadStruct(buf, new SymGroup()); + ar[i] = gr; + } + QuoteClient.Symbols.SymGroups = ar; + num = buf.Int(); + if (num > 0) { + Log.trace("vSymXX count > 0"); + } + for (int i = 0; i < num; i++) { + LoadSymXX(buf); + } + } + + private void LoadSymXX(InBuf buf) { + buf.Bytes(0x38C); // vSymXXInfo + int num = buf.Int(); + for (int i = 0; i < num; i++) { + buf.Bytes(0xA0); // vSymYY + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountRec.java new file mode 100644 index 00000000000..ee936016f1c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AccountRec.java @@ -0,0 +1,221 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xAB4, CharSet = CharSet.Unicode)]*/ +public class AccountRec extends FromBufReader { + /*[FieldOffset(0)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Login; + public long Login; + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s8; + public byte[] s8; + /*[FieldOffset(16)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String Type; + /*[FieldOffset(48)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 152)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s30; + public byte[] s30; + /*[FieldOffset(200)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String UserName; + // #define vDISABLE_ON_SERVER 4 + // #define vINVESTOR_MODE 8 + // #define vALLOWED_TRAILING 0x20 + // #define vALLOWED_AUTOTRADE 0x40 + // #define vACCOUNT_NOT_CONFIRMED 0x200 + // #define vPASS_MUST_BY_CHANGED 0x400 + /*[FieldOffset(456)]*/ public int TradeFlags; + /*[FieldOffset(460)]*/ public int s1CC; + /*[FieldOffset(464)]*/ public int s1D0; + /*[FieldOffset(468)]*/ public int s1D4; + /*[FieldOffset(472)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 56)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s1D8; + public byte[] s1D8; + /*[FieldOffset(528)]*/ public int s210; + /*[FieldOffset(532)]*/ public int s214; + /*[FieldOffset(536)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s218; + public byte[] s218; + /*[FieldOffset(544)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s220; + /*[FieldOffset(608)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s260; + /*[FieldOffset(672)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Country; + /*[FieldOffset(736)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String City; + /*[FieldOffset(800)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String State; + /*[FieldOffset(864)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String ZipCode; + /*[FieldOffset(896)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String UserAddress; + /*[FieldOffset(1152)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Phone; + /*[FieldOffset(1216)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String Email; + /*[FieldOffset(1344)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s540; + /*[FieldOffset(1472)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s5C0; + /*[FieldOffset(1536)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String s600; + /*[FieldOffset(1568)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s620; + /*[FieldOffset(1696)]*/ public int s6A0; + /*[FieldOffset(1700)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 136)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s6A4; + public byte[] s6A4; + /*[FieldOffset(1836)]*/ public double Balance; + /*[FieldOffset(1844)]*/ public double Credit; + /*[FieldOffset(1852)]*/ public double s73C; + /*[FieldOffset(1860)]*/ public double s744; + /*[FieldOffset(1868)]*/ public double s74C; + /*[FieldOffset(1876)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 52)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s754; + public byte[] s754; + /*[FieldOffset(1928)]*/ public double Blocked; + /*[FieldOffset(1936)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 84)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s790; + public byte[] s790; + /*[FieldOffset(2020)]*/ public int Leverage; + /*[FieldOffset(2024)]*/ public int s7E8; + /*[FieldOffset(2028)]*/ public int s7EC; + /*[FieldOffset(2032)]*/ public int s7F0; + /*[FieldOffset(2036)]*/ public double s7F4; + /*[FieldOffset(2044)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 312)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s7FC; + public byte[] s7FC; + /*[FieldOffset(2356)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s934; + public byte[] s934; + /*[FieldOffset(2484)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s9B4; + public byte[] s9B4; + private byte[] sAB4; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 0xBB4; + AccountRec st = new AccountRec(); + st.Login = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s8 = new byte[8]; + st.s8 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s8[i] = buf.Byte(); + } + st.Type = GetString(buf.Bytes(32)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s30 = new byte[152]; + st.s30 = new byte[152]; + for (int i = 0; i < 152; i++) { + st.s30[i] = buf.Byte(); + } + st.UserName = GetString(buf.Bytes(256)); + st.TradeFlags = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1CC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1D0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1D4 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s1D8 = new byte[56]; + st.s1D8 = new byte[56]; + for (int i = 0; i < 56; i++) { + st.s1D8[i] = buf.Byte(); + } + st.s210 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s214 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s218 = new byte[8]; + st.s218 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s218[i] = buf.Byte(); + } + st.s220 = GetString(buf.Bytes(64)); + st.s260 = GetString(buf.Bytes(64)); + st.Country = GetString(buf.Bytes(64)); + st.City = GetString(buf.Bytes(64)); + st.State = GetString(buf.Bytes(64)); + st.ZipCode = GetString(buf.Bytes(32)); + st.UserAddress = GetString(buf.Bytes(256)); + st.Phone = GetString(buf.Bytes(64)); + st.Email = GetString(buf.Bytes(128)); + st.s540 = GetString(buf.Bytes(128)); + st.s5C0 = GetString(buf.Bytes(64)); + st.s600 = GetString(buf.Bytes(32)); + st.s620 = GetString(buf.Bytes(128)); + st.s6A0 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s6A4 = new byte[136]; + st.s6A4 = new byte[136]; + for (int i = 0; i < 136; i++) { + st.s6A4[i] = buf.Byte(); + } + st.Balance = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Credit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s73C = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s744 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s74C = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s754 = new byte[52]; + st.s754 = new byte[52]; + for (int i = 0; i < 52; i++) { + st.s754[i] = buf.Byte(); + } + st.Blocked = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s790 = new byte[84]; + st.s790 = new byte[84]; + for (int i = 0; i < 84; i++) { + st.s790[i] = buf.Byte(); + } + st.Leverage = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s7E8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s7EC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s7F0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s7F4 = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s7FC = new byte[312]; + st.s7FC = new byte[312]; + for (int i = 0; i < 312; i++) { + st.s7FC[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s934 = new byte[128]; + st.s934 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s934[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s9B4 = new byte[256]; + st.s9B4 = new byte[256]; + for (int i = 0; i < 256; i++) { + st.s9B4[i] = buf.Byte(); + } + st.sAB4 = new byte[256]; + for (int i = 0; i < 256; i++) st.sAB4[i] = buf.Byte(); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Action0Param.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Action0Param.java new file mode 100644 index 00000000000..81f6e568ee8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Action0Param.java @@ -0,0 +1,6 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +@FunctionalInterface +public interface Action0Param { + void invoke(); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRec.java new file mode 100644 index 00000000000..0a32a2c40bd --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRec.java @@ -0,0 +1,31 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x94, CharSet = CharSet.Unicode)]*/ +public class AddressRec extends FromBufReader { + public static final int Size = 148; + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String Address; + /*[FieldOffset(128)]*/ public int s80; + /*[FieldOffset(132)]*/ public int s84; + /*[FieldOffset(136)]*/ public int s88; + /*[FieldOffset(140)]*/ public int s8C; + /*[FieldOffset(144)]*/ public int s90; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 148; + AddressRec st = new AddressRec(); + st.Address = GetString(buf.Bytes(128)); + st.s80 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s84 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s88 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s8C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s90 = BitConverter.ToInt32(buf.Bytes(4), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRecEx.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRecEx.java new file mode 100644 index 00000000000..72f6a0e3f64 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/AddressRecEx.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x504, CharSet = CharSet.Unicode)]*/ +public class AddressRecEx extends FromBufReader { + public static final int Size = 1284; + /*[FieldOffset(0)]*/ + public int s0; + /*[FieldOffset(4)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ public String s4; + /*[FieldOffset(516)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ public String s204; + /*[FieldOffset(1028)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s404; + public byte[] s404; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 1284; + AddressRecEx st = new AddressRecEx(); + st.s0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s4 = GetString(buf.Bytes(512)); + st.s204 = GetString(buf.Bytes(512)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s404 = new byte[256]; + st.s404 = new byte[256]; + for (int i = 0; i < 256; i++) { + st.s404[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Bar.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Bar.java new file mode 100644 index 00000000000..e3372f5ec30 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Bar.java @@ -0,0 +1,35 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// C# TO JAVA CONVERTER WARNING: Java does not allow user-defined value types. The behavior of this +// class will differ from the original: +// ORIGINAL LINE: public struct Bar +public final class Bar // sizeof 0x3C d + { + public java.time.LocalDateTime Time = java.time.LocalDateTime.MIN; // 0 + public double OpenPrice; // 8 + public double HighPrice; // 10 + public double LowPrice; // 18 + public double ClosePrice; // 20 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong TickVolume; + public long TickVolume; // 28 + public int Spread; // 30 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Volume; + public long Volume; // 34 + + public Bar clone() { + Bar varCopy = new Bar(); + + varCopy.Time = this.Time; + varCopy.OpenPrice = this.OpenPrice; + varCopy.HighPrice = this.HighPrice; + varCopy.LowPrice = this.LowPrice; + varCopy.ClosePrice = this.ClosePrice; + varCopy.TickVolume = this.TickVolume; + varCopy.Spread = this.Spread; + varCopy.Volume = this.Volume; + + return varCopy; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BarRecord.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BarRecord.java new file mode 100644 index 00000000000..c270644e850 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BarRecord.java @@ -0,0 +1,35 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// C# TO JAVA CONVERTER WARNING: Java does not allow user-defined value types. The behavior of this +// class will differ from the original: +// ORIGINAL LINE: internal struct BarRecord +public final class BarRecord // sizeof 0x30 d + { + public long Time; // 0 + public long OpenPrice; // 8 + public int High; // 10 + public int Low; // 14 + public int Close; // 18 + public int Spread; // 1C + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong TickVolume; + public long TickVolume; // 20 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Volume; + public long Volume; // 28 + + public BarRecord clone() { + BarRecord varCopy = new BarRecord(); + + varCopy.Time = this.Time; + varCopy.OpenPrice = this.OpenPrice; + varCopy.High = this.High; + varCopy.Low = this.Low; + varCopy.Close = this.Close; + varCopy.Spread = this.Spread; + varCopy.TickVolume = this.TickVolume; + varCopy.Volume = this.Volume; + + return varCopy; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitConverter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitConverter.java new file mode 100644 index 00000000000..af01e521f90 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitConverter.java @@ -0,0 +1,155 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +class BitConverter { + public static byte[] GetBytes(short v) { + byte[] writeBuffer = new byte[2]; + writeBuffer[0] = (byte) ((v >>> 0) & 0xFF); + writeBuffer[1] = (byte) ((v >>> 8) & 0xFF); + return writeBuffer; + } + + public static byte[] GetBytes(int v) { + byte[] writeBuffer = new byte[4]; + writeBuffer[3] = (byte) ((v >>> 24) & 0xFF); + writeBuffer[2] = (byte) ((v >>> 16) & 0xFF); + writeBuffer[1] = (byte) ((v >>> 8) & 0xFF); + writeBuffer[0] = (byte) ((v >>> 0) & 0xFF); + return writeBuffer; + } + + private static byte[] getBytes(long v) { + + byte[] writeBuffer = new byte[8]; + writeBuffer[7] = (byte) ((v >>> 56) & 0xFF); + writeBuffer[6] = (byte) ((v >>> 48) & 0xFF); + writeBuffer[5] = (byte) ((v >>> 40) & 0xFF); + writeBuffer[4] = (byte) ((v >>> 32) & 0xFF); + writeBuffer[3] = (byte) ((v >>> 24) & 0xFF); + writeBuffer[2] = (byte) ((v >>> 16) & 0xFF); + writeBuffer[1] = (byte) ((v >>> 8) & 0xFF); + writeBuffer[0] = (byte) ((v >>> 0) & 0xFF); + return writeBuffer; + } + + public static byte[] getBytesLong(long v) { + byte[] writeBuffer = new byte[8]; + writeBuffer[7] = (byte) ((v >>> 56) & 0xFF); + writeBuffer[6] = (byte) ((v >>> 48) & 0xFF); + writeBuffer[5] = (byte) ((v >>> 40) & 0xFF); + writeBuffer[4] = (byte) ((v >>> 32) & 0xFF); + writeBuffer[3] = (byte) ((v >>> 24) & 0xFF); + writeBuffer[2] = (byte) ((v >>> 16) & 0xFF); + writeBuffer[1] = (byte) ((v >>> 8) & 0xFF); + writeBuffer[0] = (byte) ((v >>> 0) & 0xFF); + return writeBuffer; + } + + public static byte[] getBytes(float v) { + return getBytes(Float.floatToRawIntBits(v)); + } + + public static byte[] GetBytes(double v) { + return getBytes(Double.doubleToRawLongBits(v)); + } + + public static byte[] GetBytes(long v) { + return getBytesLong(v); + } + + public static byte[] getBytes(String v) { + if (v == null) { + v = ""; + } + byte[] buf = new byte[4 + v.length()]; + System.arraycopy(getBytes(v.length()), 0, buf, 0, 4); + byte[] vb = v.getBytes(StandardCharsets.UTF_8); + System.arraycopy(vb, 0, buf, 4, vb.length); + return buf; + } + + public static String toString(byte[] data, int offset) { + int length = ToInt32(data, offset); + return new String(data, offset + 4, length, StandardCharsets.UTF_8); + } + + public static float toFloat(byte[] data, int offset) { + return Float.intBitsToFloat(ToInt32(data, offset)); + } + + public static double ToDouble(byte[] data, int offset) { + return Double.longBitsToDouble(ToInt64(data, offset)); + } + + public static short ToInt16(byte[] data, int offset) { + return (short) (data[offset] & 0xFF | (data[offset + 1] & 0xFF) << 8); + } + + public static int ToInt32(byte[] data, int offset) { + return (data[offset] & 0xFF) + | ((data[offset + 1] & 0xFF) << 8) + | ((data[offset + 2] & 0xFF) << 16) + | ((data[offset + 3] & 0xFF) << 24); + } + + public static long ToUInt32(byte[] data, int offset) { + return ((long) data[offset] & 0xFF) + | ((data[offset + 1] & 0xFF) << 8) + | ((data[offset + 2] & 0xFF) << 16) + | ((data[offset + 3] & 0xFF) << 24); + } + + public static long ToInt64(byte[] data, int offset) { + return (((long) (data[offset + 7] & 0xff) << 56) + | ((long) (data[offset + 6] & 0xff) << 48) + | ((long) (data[offset + 5] & 0xff) << 40) + | ((long) (data[offset + 4] & 0xff) << 32) + | ((long) (data[offset + 3] & 0xff) << 24) + | ((long) (data[offset + 2] & 0xff) << 16) + | ((long) (data[offset + 1] & 0xff) << 8) + | (data[offset] & 0xff)); + } + + public static byte[] asByteArray(UUID uuid) { + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + byte[] buffer = new byte[16]; + + for (int i = 0; i < 8; i++) { + buffer[i] = (byte) (msb >>> 8 * (7 - i)); + } + for (int i = 8; i < 16; i++) { + buffer[i] = (byte) (lsb >>> 8 * (7 - i)); + } + return buffer; + } + + public static UUID toUUID(byte[] byteArray) { + return toUUID(byteArray, 0); + } + + private static UUID toUUID(byte[] byteArray, int offset) { + long msb = 0; + long lsb = 0; + + for (int i = offset; i < offset + 8; i++) { + msb = (msb << 8) | (byteArray[i] & 0xff); + } + + for (int i = offset + 8; i < offset + 16; i++) { + lsb = (lsb << 8) | (byteArray[i] & 0xff); + } + + return new UUID(msb, lsb); + } + + public static long ToUInt64(byte[] bytes, int i) { + return ToInt64(bytes, i); + } + + public static short ToUInt16(byte[] bytes, int i) { + return ToInt16(bytes, i); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReader.java new file mode 100644 index 00000000000..e1c810049ce --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReader.java @@ -0,0 +1,232 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class BitReader { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte Align; + private byte Align; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte Blank; + private byte Blank; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] Data; + private byte[] Data; + public int BitSize; + + public int BitPos = 0; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public BitReader(byte[] data, HistHeader hdr) + public BitReader(byte[] data, HistHeader hdr) { + Data = data; + Align = hdr.AlignBit; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: Blank = (byte)((1 << hdr.AlignBit) - 1); + Blank = (byte) (((1 << (hdr.AlignBit & 0xFF)) - 1) & 0xFF); + BitSize = hdr.BitSize; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public BitReader(byte[] data, int bitSize) + public BitReader(byte[] data, int bitSize) { + Data = data; + BitSize = bitSize; + } + + public final long GetLong() { + return BitConverter.ToInt64(GetRecord(8), 0); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal ulong GetULong() + public final long GetULong() { + return BitConverter.ToInt64(GetRecord(8), 0); + } + + public final int GetInt() { + return BitConverter.ToInt32(GetRecord(4), 0); + } + + public final short GetShort() { + return BitConverter.ToInt16(GetRecord(2), 0); + } + + public final int GetSignInt() { + return BitConverter.ToInt32(GetSignRecord(4), 0); + } + + public final long GetSignLong() { + return BitConverter.ToInt64(GetSignRecord(8), 0); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal byte[] GetRecord(int size) + public final byte[] GetRecord(int size) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte buf = 0; + byte buf = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte bitSize = 0; + byte bitSize = 0; + do { + buf = ReadValue(Data, (Align & 0xFF))[0]; + bitSize += (buf & 0xFF); + } while ((buf & 0xFF) == (Blank & 0xFF)); + bitSize *= 2; + if (size * 8 < (bitSize & 0xFF)) { + throw new RuntimeException("size * 8 < bitSize"); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var res = ReadValue(Data, bitSize); + byte[] res = ReadValue(Data, (bitSize & 0xFF)); + return Ret(res, size); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal byte[] ReadValue(byte[] data, int size) + public final byte[] ReadValue(byte[] data, int size) { + if (size + BitPos > BitSize) { + throw new RuntimeException("End of stream"); + } + int startBit = BitPos % 8; + int bits = 0; + int valueInd = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] value = new byte[size]; + byte[] value = new byte[size]; + int dataInd = BitPos / 8; + while (size > 0) { + for (int i = startBit; i < 8; i++) { + if (size == 0) { + break; + } + if (bits >= 8) { + valueInd++; + bits = 0; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var c = data[dataInd]; + byte c = data[dataInd]; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte ch = data[dataInd]; + byte ch = data[dataInd]; + if ((ch & (1 << i)) > 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: value[valueInd] |= (byte)(1 << bits); + value[valueInd] |= (byte) ((1 << bits) & 0xFF); + } else { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: value[valueInd] &= (byte)~(1 << bits); + value[valueInd] &= (byte) (~(1 << bits) & 0xFF); + } + BitPos++; + size--; + bits++; + } + if (size == 0) { + break; + } + dataInd++; + startBit = 0; + } + return value; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void Initialize(byte align, byte blank) + public final void Initialize(byte align, byte blank) { + Align = align; + Blank = blank; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal byte[] GetSignRecord(int size) + public final byte[] GetSignRecord(int size) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte buf = 0; + byte buf = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte sign = 0; + byte sign = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte bitSize = 0; + byte bitSize = 0; + do { + buf = ReadValue(Data, (Align & 0xFF))[0]; + bitSize += (buf & 0xFF); + } while ((buf & 0xFF) == (Blank & 0xFF)); + bitSize *= 2; + if (size * 8 < (bitSize & 0xFF)) { + throw new RuntimeException("size * 8 < bitSize"); + } + sign = ReadValue(Data, 1)[0]; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] data = Ret(ReadValue(Data, bitSize), size); + byte[] data = Ret(ReadValue(Data, (bitSize & 0xFF)), size); + if ((sign & 0xFF) == 0) { + return data; + } + switch (size) { + case 1: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: data[0] = (byte)-data[0]; + data[0] = (byte) (-data[0] & 0xFF); + break; + case 2: + short vs = BitConverter.ToInt16(data, 0); + System.arraycopy(BitConverter.GetBytes(-vs), 0, data, 0, BitConverter.GetBytes(-vs).length); + break; + case 4: + int vi = BitConverter.ToInt32(data, 0); + System.arraycopy(BitConverter.GetBytes(-vi), 0, data, 0, BitConverter.GetBytes(-vi).length); + break; + case 8: + long vl = BitConverter.ToInt64(data, 0); + System.arraycopy(BitConverter.GetBytes(-vl), 0, data, 0, BitConverter.GetBytes(-vl).length); + break; + } + return data; + } + + public final void AlignBitPosition(int pos) { + BitPos = (BitPos / 8 + pos) * 8; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte[] Ret(byte[] data, int size) + private byte[] Ret(byte[] data, int size) { + if (data.length == 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: return new byte[size]; + return new byte[size]; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var result = new byte[size]; + byte[] result = new byte[size]; + if (data.length < result.length) { + System.arraycopy(data, 0, result, 0, data.length); + } else { + System.arraycopy(data, 0, result, 0, result.length); + } + return result; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void SkipRecords(ulong mask, ulong startFlag) + public final void SkipRecords(long mask, long startFlag) { + startFlag <<= 1; + if ((mask < startFlag)) { + return; + } + while (startFlag != 0) { + if (startFlag > 0x8000000000000000L) { + break; + } + if ((startFlag & mask) != 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong value = BitConverter.ToUInt64(GetRecord(8), 0); + long value = BitConverter.ToInt64(GetRecord(8), 0); + } + startFlag <<= 1; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReaderQuotes.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReaderQuotes.java new file mode 100644 index 00000000000..69931e56282 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BitReaderQuotes.java @@ -0,0 +1,379 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class BitReaderQuotes { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte Align; + private byte Align; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte Blank; + private byte Blank; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] Data; + private byte[] Data; + public int BitSize; + + public int BitPos = 0; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public BitReaderQuotes(byte[] data, HistHeader hdr) + public BitReaderQuotes(byte[] data, HistHeader hdr) { + Data = data; + Align = hdr.AlignBit; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: Blank = (byte)((1 << hdr.AlignBit) - 1); + Blank = (byte) (((1 << (hdr.AlignBit & 0xFF)) - 1) & 0xFF); + BitSize = hdr.BitSize; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public BitReaderQuotes(byte[] data, int bitSize) + public BitReaderQuotes(byte[] data, int bitSize) { + Data = data; + BitSize = bitSize; + } + + public final boolean GetLong(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetRecord(8, out buf)) + if (!GetRecord(8, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0L; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt64(buf, 0); + return true; + } + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal bool GetULong(out ulong res) + public final boolean GetULong(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetRecord(8, out buf)) + if (!GetRecord(8, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0L; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt64(buf, 0); + return true; + } + } + + public final boolean GetInt(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetRecord(4, out buf)) + if (!GetRecord(4, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt32(buf, 0); + return true; + } + } + + public final boolean GetShort(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetRecord(2, out buf)) + if (!GetRecord(2, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt16(buf, 0); + return true; + } + } + + public final boolean GetSignInt(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetSignRecord(4, out buf)) + if (!GetSignRecord(4, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt32(buf, 0); + return true; + } + } + + public final boolean GetSignLong(RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!GetSignRecord(8, out buf)) + if (!GetSignRecord(8, tempRef_buf)) { + buf = tempRef_buf.argValue; + res.argValue = 0L; + return false; + } else { + buf = tempRef_buf.argValue; + res.argValue = BitConverter.ToInt64(buf, 0); + return true; + } + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal bool GetRecord(int size, out byte[] res) + public final boolean GetRecord(int size, RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte buf = 0; + byte buf = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte bitSize = 0; + byte bitSize = 0; + do { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] value; + byte[] value = null; + RefObject tempRef_value = new RefObject(value); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!ReadValue(Data, Align, out value)) + if (!ReadValue(Data, (Align & 0xFF), tempRef_value)) { + value = tempRef_value.argValue; + res.argValue = null; + return false; + } else { + value = tempRef_value.argValue; + } + buf = value[0]; + bitSize += (buf & 0xFF); + } while ((buf & 0xFF) == (Blank & 0xFF)); + bitSize *= 2; + if (size * 8 < (bitSize & 0xFF)) { + throw new RuntimeException("size * 8 < bitSize"); + } + if (!ReadValue(Data, (bitSize & 0xFF), res)) { + res.argValue = null; + return false; + } + res.argValue = Ret(res.argValue, size); + return true; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal bool ReadValue(byte[] data, int size, out byte[] res) + public final boolean ReadValue(byte[] data, int size, RefObject res) { + if (size + BitPos > BitSize) { + res.argValue = null; + return false; // throw new Exception("End of stream"); + } + int startBit = BitPos % 8; + int bits = 0; + int valueInd = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] value = new byte[size]; + byte[] value = new byte[size]; + int dataInd = BitPos / 8; + while (size > 0) { + for (int i = startBit; i < 8; i++) { + if (size == 0) { + break; + } + if (bits >= 8) { + valueInd++; + bits = 0; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var c = data[dataInd]; + byte c = data[dataInd]; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte ch = data[dataInd]; + byte ch = data[dataInd]; + if ((ch & (1 << i)) > 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: value[valueInd] |= (byte)(1 << bits); + value[valueInd] |= (byte) ((1 << bits) & 0xFF); + } else { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: value[valueInd] &= (byte)~(1 << bits); + value[valueInd] &= (byte) (~(1 << bits) & 0xFF); + } + BitPos++; + size--; + bits++; + } + if (size == 0) { + break; + } + dataInd++; + startBit = 0; + } + res.argValue = value; + return true; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void Initialize(byte align, byte blank) + public final void Initialize(byte align, byte blank) { + Align = align; + Blank = blank; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal bool GetSignRecord(int size, out byte[] res) + public final boolean GetSignRecord(int size, RefObject res) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte buf = 0; + byte buf = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte sign = 0; + byte sign = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte bitSize = 0; + byte bitSize = 0; + do { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] value; + byte[] value = null; + RefObject tempRef_value = new RefObject(value); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!ReadValue(Data, Align, out value)) + if (!ReadValue(Data, (Align & 0xFF), tempRef_value)) { + value = tempRef_value.argValue; + res.argValue = null; + return false; + } else { + value = tempRef_value.argValue; + } + buf = value[0]; + bitSize += (buf & 0xFF); + } while ((buf & 0xFF) == (Blank & 0xFF)); + bitSize *= 2; + if (size * 8 < (bitSize & 0xFF)) { + throw new RuntimeException("size * 8 < bitSize"); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] val; + byte[] val = null; + RefObject tempRef_val = new RefObject(val); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!ReadValue(Data, 1, out val)) + if (!ReadValue(Data, 1, tempRef_val)) { + val = tempRef_val.argValue; + res.argValue = null; + return false; + } else { + val = tempRef_val.argValue; + } + sign = val[0]; + RefObject tempRef_val2 = new RefObject(val); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!ReadValue(Data, bitSize, out val)) + if (!ReadValue(Data, (bitSize & 0xFF), tempRef_val2)) { + val = tempRef_val2.argValue; + res.argValue = null; + return false; + } else { + val = tempRef_val2.argValue; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] data = Ret(val, size); + byte[] data = Ret(val, size); + if ((sign & 0xFF) == 0) { + res.argValue = data; + return true; + } + switch (size) { + case 1: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: data[0] = (byte)-data[0]; + data[0] = (byte) (-data[0] & 0xFF); + break; + case 2: + short vs = BitConverter.ToInt16(data, 0); + System.arraycopy(BitConverter.GetBytes(-vs), 0, data, 0, BitConverter.GetBytes(-vs).length); + break; + case 4: + int vi = BitConverter.ToInt32(data, 0); + System.arraycopy(BitConverter.GetBytes(-vi), 0, data, 0, BitConverter.GetBytes(-vi).length); + break; + case 8: + long vl = BitConverter.ToInt64(data, 0); + System.arraycopy(BitConverter.GetBytes(-vl), 0, data, 0, BitConverter.GetBytes(-vl).length); + break; + } + res.argValue = data; + return true; + } + + public final void AlignBitPosition(int pos) { + BitPos = (BitPos / 8 + pos) * 8; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte[] Ret(byte[] data, int size) + private byte[] Ret(byte[] data, int size) { + if (data.length == 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: return new byte[size]; + return new byte[size]; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var result = new byte[size]; + byte[] result = new byte[size]; + if (data.length < result.length) { + System.arraycopy(data, 0, result, 0, data.length); + } else { + System.arraycopy(data, 0, result, 0, result.length); + } + return result; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void SkipRecords(ulong mask, ulong startFlag) + public final void SkipRecords(long mask, long startFlag) { + startFlag <<= 1; + if ((mask < startFlag)) { + return; + } + while (startFlag != 0) { + if (startFlag > 0x8000000000000000L) { + break; + } + if ((startFlag & mask) != 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf; + byte[] buf = null; + RefObject tempRef_buf = new RefObject(buf); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: GetRecord(8, out buf); + GetRecord(8, tempRef_buf); + buf = tempRef_buf.argValue; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong value = BitConverter.ToUInt64(buf, 0); + long value = BitConverter.ToUInt64(buf, 0); + } + startFlag <<= 1; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Broker.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Broker.java new file mode 100644 index 00000000000..d26a40c3785 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Broker.java @@ -0,0 +1,249 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; + +public class Broker { + public static String Search(String company) + throws IOException, NoSuchAlgorithmException, KeyManagementException { + URL obj = + new URL( + "http://search.mtapi.io/Search?company=" + + URLEncoder.encode(company, java.nio.charset.StandardCharsets.UTF_8.toString()) + + "&mt5=true"); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + int responseCode = con.getResponseCode(); + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + String r = response.toString(); + r = r.substring(r.indexOf("{")); + return r; + } + + public static String SearchMQ(String company) + throws IOException, NoSuchAlgorithmException, KeyManagementException { + String ver = "mt5"; + String req = "company=" + company + "&code=" + ver; + byte[] sign = GetSignature(req.getBytes(StandardCharsets.US_ASCII)); + req += "&signature="; + for (byte item : sign) { + String b = String.format("%X", item).toLowerCase(); + if (b.length() == 1) { + b = "0" + b; + } + req += b; + } + req += "&ver=2"; + URL obj = new URL("https://updates.metaquotes.net/public/" + ver + "/network"); + HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); + SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$ + sc.init(null, null, new java.security.SecureRandom()); + con.setSSLSocketFactory(sc.getSocketFactory()); + // byte[] data = req.getBytes(StandardCharsets.US_ASCII); + con.setRequestMethod("POST"); + con.setRequestProperty("Accept", "*/*"); + con.setRequestProperty("Accept-Encoding", "gzip, deflate"); + con.setRequestProperty("Accept-Language", "en"); + con.setRequestProperty("UserAgent", "MetaTrader 4 Terminal/4.1380 (Windows NT 6.2.9200; x64"); + con.setRequestProperty("Cookie", GetCookies()); + con.setRequestProperty("ContentType", "application/x-www-form-urlencoded"); + con.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(con.getOutputStream()); + wr.writeBytes(req); + wr.flush(); + wr.close(); + int responseCode = con.getResponseCode(); + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + String r = response.toString(); + r = r.substring(r.indexOf("{")); + return r; + } + + static String GetCookies() { + RuntimeMXBean runtimeMX = ManagementFactory.getRuntimeMXBean(); + Date start = new Date(runtimeMX.getStartTime()); + long ticks = (new java.util.Date()).getTime() - start.getTime(); + long time = + (long) + Duration.between(LocalDateTime.of(1970, 1, 1, 0, 0, 0), LocalDateTime.now()) + .toMillis() + / 1000 + - 16436 * 24 * 3600; + long softid = time | ((ticks & 0x1FFFFFF) << 32) | 0x4200000000000000L; + byte[] key = CreateHardId(); + String commonKey = bytesToHex(key); + commonKey = commonKey.substring(0, 16); + long age = + (long) + Duration.between(LocalDateTime.of(1970, 1, 1, 0, 0, 0), LocalDateTime.now()) + .toMillis() + / 1000 + - 24 * 3600; + return "_fz_uniq=" + softid + ";uniq=" + softid + ";age=" + age + ";tid=" + commonKey + ";"; + } + + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + static byte[] CreateHardId() { + long seed = (new java.util.Date()).getTime(); + byte[] data = new byte[256]; + for (int i = 0; i < 256; i++) { + seed = seed * 214013 + 2531011; + data[i] = (byte) ((seed >> 16) & 0xFF); + } + MD5 md = new MD5(); + byte[] _HardId = md.computeMD5(data); + _HardId[0] = 0; + for (int i = 1; i < 16; i++) _HardId[0] += _HardId[i]; + return _HardId; + } + + // public static Companies ReadToObject(String json) + // { + // Companies deserializedUser = new Companies(); + // MemoryStream ms = new + // MemoryStream(json.getBytes(java.nio.charset.StandardCharsets.UTF_8)); + // DataContractJsonSerializer ser = new + // DataContractJsonSerializer(deserializedUser.getClass()); + // Object tempVar = ser.ReadObject(ms); + // deserializedUser = (Companies)((tempVar instanceof Companies) ? tempVar : null); + // ms.Close(); + // return deserializedUser; + // } + + public static class Result { + private String name; + + public final String getname() { + return name; + } + + public final void setname(String value) { + name = value; + } + + private List access; + + public final List getaccess() { + return access; + } + + public final void setaccess(List value) { + access = value; + } + } + + public static class Company { + private String company; + + public final String getcompany() { + return company; + } + + public final void setcompany(String value) { + company = value; + } + + private List results; + + public final List getresults() { + return results; + } + + public final void setresults(List value) { + results = value; + } + } + + public static class Companies { + private List result; + + public final List getresult() { + return result; + } + + public final void setresult(List value) { + result = value; + } + } + + private static byte[] GetSignature(byte[] data) { + MD5 md = new MD5(); + byte[] key = md.computeMD5(data); + byte[] sign = { + 0x3D, + 0x7B, + 0x15, + 0x16, + (byte) 0xD6, + (byte) 0xEA, + (byte) 0xBB, + 0x34, + (byte) 0xD9, + (byte) 0xD6, + 0x63, + (byte) 0xE3, + 0x62, + 0x3E, + 0x1B, + (byte) 0xD7, + (byte) 0xFB, + (byte) 0xDC, + (byte) 0xAE, + (byte) 0xF4, + 0x57, + 0x3B, + (byte) 0xDF, + 0x35, + 0x7F, + (byte) 0xA8, + (byte) 0xCF, + 0x0B, + (byte) 0xBE, + (byte) 0xAD, + (byte) 0x92, + 0x7F + }; + byte[] d = new byte[48]; + for (int i = 0; i < 16; i++) d[i] = key[i]; + for (int i = 0; i < 32; i++) d[i + 16] = sign[i]; + byte[] res = md.computeMD5(d); + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BuildRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BuildRec.java new file mode 100644 index 00000000000..255250f1c5e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/BuildRec.java @@ -0,0 +1,63 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Size = 0x20, CharSet = CharSet.Unicode)]*/ +public class BuildRec extends FromBufReader // sizeof 0x20 c +{ + /*[FieldOffset(0)]*/ public short s0; + /*[FieldOffset(2)]*/ public Msg StatusCode = Msg.values()[0]; // 2 + /*[FieldOffset(6)]*/ public short Build; // 6 + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] SignData; + public byte[] SignData; // 8 + /*[FieldOffset(0x18)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4)]*/ public short[] + LastBuild; // 0x18 + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 32; + BuildRec st = new BuildRec(); // sizeof 0x20 c(); + st.s0 = BitConverter.ToInt16(buf.Bytes(2), 0); + st.StatusCode = Msg.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.Build = BitConverter.ToInt16(buf.Bytes(2), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.SignData = new byte[16]; + st.SignData = new byte[16]; + for (int i = 0; i < 16; i++) { + st.SignData[i] = buf.Byte(); + } + st.LastBuild = new short[4]; + for (int i = 0; i < 4; i++) { + st.LastBuild[i] = BitConverter.ToInt16(buf.Bytes(2), 0); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} +/***[StructLayout(LayoutKind.Explicit, Size = 0x22, CharSet = CharSet.Unicode)]*/ + +// public class LoginSnd1 //sizeof 0x22 c : FromBufReader +// { +// /*[FieldOffset(0)]*/ public short s0; +// ///*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ +// /*[FieldOffset(0x2)]*/ public byte[] CryptKey; //2 +// ///*[MarshalAsAttribute(UnmanagedType.ByValArray, +// SizeConst = 16)]*/ +// /*[FieldOffset(0x12)]*/ public byte[] Random; //12 +// } + +// public class vLoginRequest //sizeof 0x22 c : FromBufReader +// { +// public sbyte m_cRandom; //0 +// public sbyte m_Type; //1 +// public short m_nRevision; //2 +// public short m_Signature; //4 +// public ulong m_nLogin; //6 +// public byte[] m_Key = new byte[16]; //E +// public int m_Random; //1E +// } diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ByteLists.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ByteLists.java new file mode 100644 index 00000000000..62b6f6d6e82 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ByteLists.java @@ -0,0 +1,29 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// ---------------------------------------------------------------------------------------- +// Copyright © 2007 - 2017 Tangible Software Solutions Inc. +// This class can be used by anyone provided that the copyright notice remains intact. +// +// This class is used to convert between Byte Lists and byte arrays. +// ---------------------------------------------------------------------------------------- +public final class ByteLists { + public static byte[] toArray(java.util.List list) { + byte[] array = new byte[list.size()]; + for (int i = 0; i < list.size(); i++) { + array[i] = list.get(i).byteValue(); + } + return array; + } + + public static void addPrimitiveArrayToList(byte[] array, java.util.List list) { + for (int i = 0; i < array.length; i++) { + list.add(array[i]); + } + } + + public static void addPrimitiveArrayToList(int index, byte[] array, java.util.List list) { + for (int i = 0; i < array.length; i++) { + list.add(index + i, array[i]); + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/C54.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/C54.java new file mode 100644 index 00000000000..eca31816eb9 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/C54.java @@ -0,0 +1,50 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x50, CharSet = CharSet.Unicode)]*/ +public class C54 extends FromBufReader { + /*[FieldOffset(0)]*/ public long s0; + /*[FieldOffset(8)]*/ public long s8; + /*[FieldOffset(16)]*/ public int s10; + /*[FieldOffset(20)]*/ public int s14; + /*[FieldOffset(24)]*/ public int s18; + /*[FieldOffset(28)]*/ public int s1C; + /*[FieldOffset(32)]*/ public long s20; + /*[FieldOffset(40)]*/ public int s28; + /*[FieldOffset(44)]*/ public int s2C; + /*[FieldOffset(48)]*/ public int s30; + /*[FieldOffset(52)]*/ public int s34; + /*[FieldOffset(56)]*/ public int s38; + /*[FieldOffset(60)]*/ public int s3C; + /*[FieldOffset(64)]*/ public int s40; + /*[FieldOffset(68)]*/ public int s44; + /*[FieldOffset(72)]*/ public int s48; + /*[FieldOffset(76)]*/ public int s4C; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 80; + C54 st = new C54(); + st.s0 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s8 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s10 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s14 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s18 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s20 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s28 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s2C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s30 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s34 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s38 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s3C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s40 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s44 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s48 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s4C = BitConverter.ToInt32(buf.Bytes(4), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CalculationMode.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CalculationMode.java new file mode 100644 index 00000000000..217ffd8fdc5 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CalculationMode.java @@ -0,0 +1,46 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum CalculationMode { + Forex(0), + Futures(1), + CFD(2), + CFDIndex(3), + CFDLeverage(4), + CalcMode5(5), + ExchangeStocks(32), + ExchangeFutures(33), + FORTSFutures(34), + ExchangeOption(35), + ExchangeMarginOption(36), + ExchangeBounds(37), + Collateral(64); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (CalculationMode.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private CalculationMode(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static CalculationMode forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CmdHandler.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CmdHandler.java new file mode 100644 index 00000000000..53794b83f74 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/CmdHandler.java @@ -0,0 +1,147 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; + +/// #define TRIAL + +class CmdHandler { + public Exception AccountLoaderException = null; + private MT5API QuoteClient; + private Logger Log; + public Thread Thread; + private static final AtomicLong THREAD_COUNTER = new AtomicLong(0); + + boolean Stop = false; + private boolean Stoped = false; + + private java.time.LocalDateTime LastPing = java.time.LocalDateTime.MIN; + + public CmdHandler(MT5API quoteClient) { + QuoteClient = quoteClient; + Log = quoteClient.Log; + LastPing = java.time.LocalDateTime.now(); + } + + public final void StartCmdHandler(Connection con, long user) { + Thread = + new Thread( + new Runnable() { + public void run() { + Run(con); + } + }); + Thread.setName(String.format("MT5CmdHandler-%s-%s", user, THREAD_COUNTER.incrementAndGet())); + Thread.start(); + } + + public final boolean getRunning() { + return Thread.isAlive() && Stop == false; + } + + private HashMap> Packets = new HashMap>(); + private final short PackCompress = 1; + private final short PackComplete = 2; + + private void Run(Object obj) { + Connection con = (Connection) obj; + java.time.LocalDateTime pingTime1 = java.time.LocalDateTime.now().plusSeconds(10); + java.time.LocalDateTime pingTime2 = java.time.LocalDateTime.now().plusSeconds(20); + byte cmd = 0; + try { + while (!Stop && !QuoteClient.TimeoutDuringConnect) { + io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.InBuf buf = con.RecievePacket(); + QuoteClient.TimeOfLastMessageFromServer = LocalDateTime.now(); + cmd = buf.Hdr.Type; + if ((buf.Hdr.Flags & PackCompress) > 0) { + con.Decompress(buf); + } + if (Packets.containsKey(cmd)) { + if ((buf.Hdr.Flags & PackComplete) > 0) { + ByteLists.addPrimitiveArrayToList(buf.ToBytes(), Packets.get(cmd)); + buf.SetBuf(ByteLists.toArray(Packets.get(cmd))); + Packets.remove(cmd); + } else { + ByteLists.addPrimitiveArrayToList(buf.ToBytes(), Packets.get(cmd)); + continue; + } + } else { + if ((buf.Hdr.Flags & PackComplete) > 0) { + } else { + Packets.put(cmd, new ArrayList()); + ByteLists.addPrimitiveArrayToList(buf.ToBytes(), Packets.get(cmd)); + continue; + } + } + switch (cmd) { + case 0x65: + Log.trace("Cmd TradeHistory"); + QuoteClient.OrderHistory.Parse(buf); + break; + case 0x66: + Log.trace("Cmd QuoteHistory"); + (new QuoteHistory(QuoteClient)).Parse(buf); + break; + case 0xC: + Log.trace("Cmd AccountInfo"); + (new AccountLoader(QuoteClient, this, con)).Parse(buf); + break; + case 0x32: + Log.trace("ParseTicks"); + QuoteClient.Subscriber.Parse(buf); + break; + case 0x36: + Log.trace("RecieveMail"); + break; + case 0x37: + Log.trace("ParseTrades"); + QuoteClient.Orders.ParseTrades(buf); + break; + case 0x33: + Log.trace("ParseSubscribeSymbols"); + QuoteClient.Subscriber.ParseSymbolData(buf); + break; + case 0xA: + Log.trace("Cmd Ping"); + break; + case 0x6C: + Log.trace("Cmd TradeResult"); + ParseResult(buf); + break; + default: + Log.trace("Unknown cmd = " + String.format("%X", cmd & 0xFF)); + break; + } + if (Duration.between(LastPing, LocalDateTime.now()).getSeconds() > 10) { + QuoteClient.Connection.SendPacket((byte) 0xA, new OutBuf()); + LastPing = java.time.LocalDateTime.now(); + } + } + Stop = true; + con.Disconnect(); + QuoteClient.OnDisconnect(null); + } catch (Exception ex) { + Log.exception(ex); + Stop = true; + QuoteClient.OnDisconnect(ex); + try { + con.Close(); + } catch (Exception exc) { + } + } + } + + public final void StopCmdHandler() { + Stop = true; + } + + private void ParseResult(InBuf buf) { + Msg status = Msg.forValue(buf.Int()); + } + + public void stop() { + Stop = true; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compr.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compr.java new file mode 100644 index 00000000000..b7fd46341f9 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compr.java @@ -0,0 +1,384 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; +// +// public class Compr +// { +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void DINDEX1(ref uint d, byte* p) +//// { +//// d = DM(DMUL(0x21, DX3(p, 5, 5, 6)) >> 5); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint DX3(byte* p, int s1, int s2, int s3) +//// { +//// return ((DX2((p) + 1, s2, s3) << (s1)) ^ (p)[0]); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint DX2(byte* p, int s1, int s2) +//// { +//// return (((((uint)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +// int DMUL(int a, int b) +// { +// return ((int)((a) * (b))); +// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint DM(uint v) +//// { +//// return DMS(v, 0); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint DMS(uint v, int s) +//// { +//// return ((uint)(((v) & (D_MASK >> (s))) << (s))); +//// } +// +// private final int D_BITS = 14; +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private uint getD_MASK() +// private int getD_MASK() +// { +// +// return LZO_MASK(D_BITS); +// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint LZO_MASK(int bits) +//// { +//// return (LZO_SIZE(bits) - 1); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint LZO_SIZE(int bits) +//// { +//// return (1u << (bits)); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void GINDEX(ref byte* m_pos, uint m_off, byte*[] dict, uint dindex, byte* inn) +//// { +//// m_pos = dict[dindex]; +//// } +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly uint M4_MAX_OFFSET = 0xbfff; +// private final int M4_MAX_OFFSET = 0xbfff; +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe bool LZO_CHECK_MPOS_NON_DET(ref byte* m_pos, uint m_off, byte* inn, byte* ip, uint +// max_offset) +//// { +//// m_pos = ip - (uint)PTR_DIFF(ip, m_pos); +//// return PTR_LT(m_pos, inn) || (m_off = (uint)PTR_DIFF(ip, m_pos)) == 0 || m_off > max_offset; +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe bool PTR_LT(byte* a, byte* b) +//// { +//// return a < b; +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint PTR_DIFF(byte* a, byte* b) +//// { +//// return (uint)(a - b); +//// } +// +// private final int M2_MAX_OFFSET = 0x0800; +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private uint getD_HIGH() +// private int getD_HIGH() +// { +//// C# TO JAVA CONVERTER WARNING: The right shift operator was not replaced by Java's logical right +// shift operator since the left operand was not confirmed to be of an unsigned type, but you should +// review whether the logical right shift operator (>>>) is more appropriate: +// return ((getD_MASK() & 0xFFFFFFFFL) >> 1) + 1; +// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void DINDEX2(ref uint d, byte* p) +//// { +//// d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void UPDATE_I(byte*[] dict, int drun, uint index, byte* p, byte* inn) +//// { +//// dict[index] = p; +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void MEMCPY_DS(ref byte* dest, ref byte* src, uint len) +//// { +//// do +//// *dest++ = *src++; +//// while (--len > 0); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe bool PS(ref byte* m_pos, ref byte* ip) +//// { +//// return *m_pos++ != *ip++; +//// } +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly uint M3_MAX_OFFSET = 0x4000; +// private final int M3_MAX_OFFSET = 0x4000; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly byte M3_MARKER = 32; +// private final byte M3_MARKER = 32; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly byte M4_MARKER = 16; +// private final byte M4_MARKER = 16; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly byte M3_MAX_LEN =33; +// private final byte M3_MAX_LEN = 33; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: readonly byte M4_MAX_LEN =9; +// private final byte M4_MAX_LEN = 9; +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint DoDeflate(byte* pSrc, uint szSrc, byte* pDst, uint szDst, byte*[] m_pBuffer) +//// { +//// byte* op = pDst; +//// byte* inn = pSrc; +//// byte* ip = inn + 4; +//// byte* ii = inn; +//// byte* in_end = pSrc + szSrc; +//// byte* ip_end = pSrc + szSrc - 13; +//// byte*[] dict = m_pBuffer; +//// if (dict == null) +//// goto end; +//// for (; ;) +//// { +//// byte* m_pos = null; +//// uint m_off = 0; +//// uint dindex = 0; +//// next: +//// DINDEX1(ref dindex, ip); +//// GINDEX(ref m_pos, m_off, dict, dindex, inn); +//// if (LZO_CHECK_MPOS_NON_DET(ref m_pos, m_off, inn, ip, M4_MAX_OFFSET)) +//// goto literal; +//// if ((m_off <= M2_MAX_OFFSET) || (m_pos[3] == ip[3])) +//// if ((m_pos[0] != ip[0]) || (m_pos[1] != ip[1]) || (m_pos[2] != ip[2])) +//// goto literal; +//// else +//// goto after_literal; +//// DINDEX2(ref dindex, ip); +//// GINDEX(ref m_pos, m_off, dict, dindex, inn); +//// if (LZO_CHECK_MPOS_NON_DET(ref m_pos, m_off, inn, ip, M4_MAX_OFFSET)) +//// goto literal; +//// if ((m_off <= M2_MAX_OFFSET) || (m_pos[3] == ip[3])) +//// if ((m_pos[0] != ip[0]) || (m_pos[1] != ip[1]) || (m_pos[2] != ip[2])) +//// goto literal; +//// else +//// goto after_literal; +//// literal: +//// UPDATE_I(dict, 0, dindex, ip, inn); +//// if (++ip >= ip_end) +//// break; +//// continue; +//// after_literal: +//// UPDATE_I(dict, 0, dindex, ip, inn); +//// uint t = Pd(ip, ii); +//// if (t != 0) +//// { +//// if (t <= 3) +//// op[-2] |= (byte)t; +//// else +//// { +//// if (t <= 18) +//// *op++ = (byte)(t - 3); +//// else +//// { +//// uint tt = t - 18; +//// *op++ = 0; +//// if (tt > 255) +//// DZERO_BLK(op, tt); +//// *op++ = (byte)tt; +//// } +//// } +//// MEMCPY_DS(ref op, ref ii, t); //?? +//// } +//// m_pos += 3; +//// ip += 3; +//// +//// if (PS(ref m_pos, ref ip) || PS(ref m_pos, ref ip) || PS(ref m_pos, ref ip) || PS(ref m_pos, +// ref ip) || PS(ref m_pos, ref ip) || PS(ref m_pos, ref ip)) +//// { +//// --ip; +//// uint m_len = Pd(ip, ii); +//// if (m_off <= M2_MAX_OFFSET) +//// { +//// --m_off; +//// *op++ = (byte)(((m_len - 1) << 5) | ((m_off & 7) << 2)); +//// *op++ = (byte)(m_off >> 3); +//// } +//// else if (m_off <= M3_MAX_OFFSET) +//// { +//// --m_off; +//// *op++ = (byte)(M3_MARKER | (m_len - 2)); +//// *op++ = (byte)(m_off << 2); +//// *op++ = (byte)(m_off >> 6); +//// } +//// else +//// { +//// m_off -= 0x4000; +//// *op++ = (byte)(M4_MARKER | ((m_off >> 11) & 8) | (m_len - 2)); +//// *op++ = (byte)(m_off << 2); +//// *op++ = (byte)(m_off >> 6); +//// } +//// } +//// else +//// { +//// while ((ip < in_end) && (*m_pos == *ip)) +//// { +//// m_pos++; +//// ip++; +//// } +//// uint m_len = Pd(ip, ii); +//// if (m_off <= M3_MAX_OFFSET) +//// { +//// m_off -= 1; +//// if (m_len <= M3_MAX_LEN) +//// *op++ = (byte)(M3_MARKER | (m_len - 2)); +//// else +//// { +//// m_len -= M3_MAX_LEN; +//// *op++ = (byte)(M3_MARKER | 0); +//// if (m_len > 255) +//// DZERO_BLK(op, m_len); +//// *op++ = (byte)m_len; +//// } +//// } +//// else +//// { +//// m_off -= 0x4000; +//// if (m_len <= M4_MAX_LEN) +//// *op++ = (byte)(M4_MARKER | ((m_off >> 11) & 8) | (m_len - 2)); +//// else +//// { +//// m_len -= M4_MAX_LEN; +//// *op++ = (byte)(M4_MARKER | ((m_off >> 11) & 8)); +//// if (m_len > 255) +//// DZERO_BLK(op, m_len); +//// *op++ = (byte)m_len; +//// } +//// } +//// *op++ = (byte)(m_off << 2); +//// *op++ = (byte)(m_off >> 6); +//// } +//// ii = ip; +//// goto next; +//// } +//// end: +//// szDst = Pd(op, pDst); +//// return Pd(in_end, ii); +//// } +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: byte[] Src; +// private byte[] Src; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: byte[] Dst; +// private byte[] Dst; +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// public unsafe byte[] Deflate(byte[] src) +//// { +//// Src = src; +//// uint sLen = (uint)src.Length; +//// byte[] dstBuf = new byte[sLen]; +//// Dst = dstBuf; +//// uint dLen = 0; +//// fixed (byte* s = src, d = dstBuf) +//// { +//// byte*[] m_pBuffer = null; +//// ; +//// if (!Deflate(s, sLen, d, &dLen, m_pBuffer)) +//// throw new Exception("Cannot deflate"); +//// } +//// byte[] data = new byte[dLen]; +//// for (int i = 0; i < dLen; i++) +//// data[i] = dstBuf[i]; +//// return data; +//// } +// //+879480 a +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe bool Deflate(byte* pSrc, uint szSrc, byte* pDst, uint* pszDst, byte*[] m_pBuffer) +//// { +//// byte* op = pDst; +//// uint t, szDst = 0; +//// if (szSrc <= 13) +//// t = szSrc; +//// else +//// { +//// if (m_pBuffer == null) +//// m_pBuffer = new byte*[0x4000]; +//// if (m_pBuffer == null) +//// return false; +//// t = DoDeflate(pSrc, szSrc, pDst, szDst, m_pBuffer); +//// op += szDst; +//// } +//// if (t != 0) +//// { +//// byte* ii = pSrc + szSrc - t; +//// if ((op == pDst) && (t <= 238)) +//// *op++ = (byte)(t + 17); +//// else if (t <= 3) +//// op[-2] |= (byte)t; +//// else if (t <= 18) +//// *op++ = (byte)(t - 3); +//// else +//// { +//// *op++ = 0; +//// uint tt = t - 18; +//// if (tt > 255) +//// DZERO_BLK(op, tt); +//// * op++ = (byte)tt; +//// } +//// do +//// *op++ = *ii++; +//// while (--t > 0); +//// } +//// *(ushort*)op = 17; +//// op += 2; +//// *op++ = 0; +//// *pszDst = Pd(op, pDst); +//// return true; +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe uint Pd(byte* a, byte* b) +//// { +//// return ((uint)((a) - (b))); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void DZERO_BLK(byte* s, uint n) +//// { +//// uint sz = (n - 256) / 255 + 1; +//// Memset(s, 0, sz); +//// s += sz; +//// do +//// { +//// n -= 255; +//// } +//// while (--sz > 0); +//// } +// +//// C# TO JAVA CONVERTER TODO TASK: C# 'unsafe' code is not converted by C# to Java Converter: +//// unsafe void Memset(byte* s, byte value, uint size) +//// { +//// for (int i = 0; i < size; i++) +//// s[i] = value; +//// } +// +// } diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compressor.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compressor.java new file mode 100644 index 00000000000..c9b87dd9207 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Compressor.java @@ -0,0 +1,368 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import static io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.String8.dataDistance; +import static io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.String8.nnc; + +public class Compressor { + + public static byte[] compress(byte[] bytes) { + int srcLen = bytes.length; + String8 src = new String8(bytes); + int len = srcLen; + String8 dst = new String8(len); + IntContainer dstLen = IntContainer.fromData(0); + { + String8 p = dst; + if (len > 0xD) { + len = compressData(src, len, dst, dstLen); + p = dst.shift(dstLen.get()); + } + if (len > 0) { + String8 from = String8.from(src, srcLen - len); + if (dataAddress(p) == dataAddress(dst) && len <= 0xEE) { + (p = nnc(p).shift(1)).set(-1, (byte) (len + 0x11)); + } else if (len <= 3) { + p.set(-2, (byte) (Byte.toUnsignedInt(p.get(-2)) | Byte.toUnsignedInt((byte) len))); + } else if (len <= 0x12) { + (p = nnc(p).shift(1)).set(-1, (byte) (len - 3)); + } else { + p = nnc(p).shift(1); + int cnt = len - 0x12; + if (cnt > 0xFF) { + long lbl_U = 0x80808081L * (long) (cnt - 1); + int bl = (int) (lbl_U >>> 39); + p = nnc(p).shift(bl); + for (int i = 0; i < bl; i++) { + cnt -= 0xFF; + } + } + (p = nnc(p).shift(1)).set(-1, (byte) cnt); + } + for (int i = 0; i < len; i++) { + (p = nnc(p).shift(1)).set(-1, (from = nnc(from).shift(1)).get(-1)); + } + p.toString(); + } + (p = p.shift(1)).set(-1, (byte) 0x11); + p = p.shift(2); + dstLen.set((int) dataDistance(dst, p)); + } + byte[] data = new byte[dstLen.get()]; + for (int i = 0; i < dstLen.get(); i++) { + data[i] = dst.get(i); + } + return data; + } + + public static int compressData(String8 src, int sizeSrc, String8 dst, IntContainer sizeDst) { + String8[] buf = new String8[0x4000]; + String8 to = dst; + String8 from = nnc(src).shift(4); + String8 start = src; + String8 end = nnc(src).shift(sizeSrc); + int index = 0; + + final int posNextStep = 1, posCheckEnd = 2; + positionLoop: + for (int pos = 0; true; ) + switch (pos) { + default: + forLoop: + for (; true; pos = 0) + switch (pos) { + default: + index = + ((Byte.toUnsignedInt(from.get(3)) << 6 ^ Byte.toUnsignedInt(from.get(2))) << 5 + ^ Byte.toUnsignedInt(from.get(1))) + << 5 + ^ Byte.toUnsignedInt(from.get(0)); + index += index << 5; + index = index >> 5 & 0x3FFF; + String8 ptr = buf[index]; + if (ptr == null) { + pos = posNextStep; + continue positionLoop; + } + if (dataAddress(ptr) < dataAddress(src)) { + pos = posNextStep; + continue positionLoop; + } + sizeSrc = (int) dataDistance(ptr, from); + if (sizeSrc == 0 || sizeSrc > 0xBFFF) { + pos = posNextStep; + continue positionLoop; + } + if (sizeSrc > 0x800 && ptr.get(3) != from.get(3)) { + index &= 0x7FF; + index ^= 0x201F; + ptr = buf[index]; + if (dataAddress(ptr) < dataAddress(src)) { + pos = posNextStep; + continue positionLoop; + } + sizeSrc = (int) dataDistance(ptr, from); + if (sizeSrc == 0 + || sizeSrc > 0xBFFF + || sizeSrc > 0x800 && ptr.get(3) != from.get(3)) { + pos = posNextStep; + continue positionLoop; + } + } + if (ptr.get(0) != from.get(0) + || ptr.get(1) != from.get(1) + || ptr.get(2) != from.get(2)) { + pos = posNextStep; + continue positionLoop; + } + buf[index] = nnc(from); + int look = (int) dataDistance(start, from); + if (look != 0) { + if (look <= 3) { + to.set( + -2, + (byte) (Byte.toUnsignedInt(to.get(-2)) | Byte.toUnsignedInt((byte) look))); + } else if (look <= 0x12) { + (to = nnc(to).shift(1)).set(-1, (byte) (look - 3)); + } else { + (to = nnc(to).shift(1)).set(-1, (byte) 0); + int cnt = look - 0x12; + if (cnt > 0xFF) { + long lbl_U = 0x80808081L * (long) (cnt - 1); + int bl = (int) (lbl_U >>> 39); + to = nnc(to).shift(bl); + for (int i = 0; i < bl; i++) { + cnt -= 0xFF; + } + } + (to = nnc(to).shift(1)).set(-1, (byte) cnt); + } + look = look; + for (int i = 0; i < look; i++) { + (to = nnc(to).shift(1)).set(-1, (start = nnc(start).shift(1)).get(-1)); + } + } + from.Index += 3; + if ((from = nnc(from).shift(1)).get(-1) == ptr.get(3) + && (from = nnc(from).shift(1)).get(-1) == ptr.get(4) + && (from = nnc(from).shift(1)).get(-1) == ptr.get(5) + && (from = nnc(from).shift(1)).get(-1) == ptr.get(6) + && (from = nnc(from).shift(1)).get(-1) == ptr.get(7) + && (from = nnc(from).shift(1)).get(-1) == ptr.get(8)) { + ptr.Index += 9; + while (dataAddress(from) < dataAddress(end)) { + if (ptr.get() != from.get()) { + break; + } + ptr.Index++; + from.Index++; + } + look = (int) dataDistance(start, from); + if (sizeSrc <= 0x4000) { + sizeSrc--; + if (look <= 0x21) { + (to = nnc(to).shift(1)).set(-1, (byte) (look - 2 | 0x20)); + } else { + (to = nnc(to).shift(1)).set(-1, (byte) 0x20); + int cnt = look - 0x21; + if (cnt > 0xFF) { + long lbl_U = 0x80808081L * (long) (cnt - 1); + int bl = (int) (lbl_U >>> 39); + to = nnc(to).shift(bl); + for (int i = 0; i < bl; i++) { + cnt -= 0xFF; + } + } + (to = nnc(to).shift(1)).set(-1, (byte) cnt); + } + } else { + sizeSrc -= 0x4000; + if (look <= 9) { + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 11 & 8 | look - 2 | 0x10)); + } else { + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 11 & 8 | 0x10)); + int cnt = look - 9; + if (cnt > 0xFF) { + long lbl_U = 0x80808081L * (long) (cnt - 1); + int bl = (int) (lbl_U >>> 39); + to = nnc(to).shift(bl); + for (int i = 0; i < bl; i++) { + cnt -= 0xFF; + } + } + (to = nnc(to).shift(1)).set(-1, (byte) cnt); + } + } + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc << 2)); + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 6)); + } else { + look = (int) dataDistance(start, from = nnc(from).shift(-1)); + if (sizeSrc <= 0x800) { + sizeSrc--; + (to = nnc(to).shift(1)).set(-1, (byte) (look + 7 << 5 | (sizeSrc & 7) << 2)); + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 3)); + } else if (sizeSrc <= 0x4000) { + (to = nnc(to).shift(1)).set(-1, (byte) (look - 2 | 0x20)); + sizeSrc--; + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc << 2)); + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 6)); + } else { + sizeSrc -= 0x4000; + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 11 & 8 | look - 2 | 0x10)); + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc << 2)); + (to = nnc(to).shift(1)).set(-1, (byte) (sizeSrc >> 6)); + } + } + start = nnc(from); + pos = posCheckEnd; + continue positionLoop; + } + case posNextStep: + buf[index] = nnc(from); + from.Index++; + case posCheckEnd: + pos = 0; + if (dataAddress(from) >= dataAddress(nnc(end).shift(-0xD))) { + sizeDst.set(to.Index - dst.Index); + return end.Index - start.Index; + } + } + } + + private static int dataAddress(String8 s) { + return s.Index; + } + + // public static void dzeroBlk(String8[] s_U, long[] n_U) { + // { + // long sz_U = Long.divideUnsigned(n_U[0] - 256, 255) + 1; + // s_U[0].fill(0, (int)sz_U, (byte)0); + // s_U[0] = nnc(s_U[0]).shift((int)sz_U); + // do { + // n_U[0] = n_U[0] - 255; + // } while(Long.compareUnsigned(--sz_U, 0) > 0); + // } + // } + // + // public static long pd_U(String8 a_U, String8 b_U) { + // return dataDistance(b_U, a_U); + // } + // public static boolean compress(String8 pSrc_U, int szSrc_U, String8 pDst_U, int[] pszDst_U) + // { + // Container mPBuffer_U = new Container(0x4000){}; + // if(pSrc_U == null || Long.compareUnsigned(szSrc_U, 1) < 0 || pDst_U == null || pszDst_U + // == null) { + // return false; + // } + // String8[] op_U = {pDst_U}; + // int t_U, szDst_U = 0; + // if(Long.compareUnsigned(szSrc_U, 13) <= 0) { + // t_U = szSrc_U; + // } else { + // String8.from(mPBuffer_U).fill(0, 0x4000 * 4, (byte)0); + // t_U = compressData(pSrc_U, szSrc_U, pDst_U, szDst_U); + // op_U[0] = nnc(op_U[0]).shift((int)szDst_U); + // } + // if(t_U != 0) { + // String8 ii_U = nnc(pSrc_U).shift((int)(szSrc_U - t_U)); + // if(dataAddress(op_U[0]) == dataAddress(pDst_U) && Long.compareUnsigned(t_U, 238) <= + // 0) { + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (byte)(t_U + 17)); + // } else if(Long.compareUnsigned(t_U, 3) <= 0) { + // op_U[0].set(-2, (byte)(Byte.toUnsignedInt(op_U[0].get(-2)) | + // Byte.toUnsignedInt((byte)t_U))); + // } else if(Long.compareUnsigned(t_U, 18) <= 0) { + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (byte)(t_U - 3)); + // } else { + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (byte)0); + // long[] tt_U = {t_U - 18}; + // if(Long.compareUnsigned(tt_U[0], 255) > 0) { + // dzeroBlk(op_U, tt_U); + // } + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (byte)tt_U[0]); + // } + // do { + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (ii_U = nnc(ii_U).shift(1)).get(-1)); + // } while(Long.compareUnsigned(--t_U, 0) > 0); + // } + // ShortContainer.from(op_U[0]).set((short)17); + // op_U[0] = nnc(op_U[0]).shift(2); + // (op_U[0] = nnc(op_U[0]).shift(1)).set(-1, (byte)0); + // pszDst_U[0] = pd_U(op_U[0], pDst_U); + // return true; + // } + + // public static int doDeflate(String8 pSrc_U, long szSrc_U, String8 pDst_U, long[] szDst_U) { + // String8 op_U = null; + // String8 ip_U = null; + // String8 ii_U = null; + // String8 inEnd_U = null; + // byte[] mPos_U = null; + // long dindex_U = 0; + // + // final int posNext = 1, posTryMatch = 2, posLiteral = 3, posEnd = 4; + // positionLoop: + // for(int pos = 0; true;) switch(pos) { + // default: + // String8 mPBuffer_U = null; + // op_U = pDst_U; + // String8 in_U = pSrc_U; + // ip_U = nnc(in_U).shift(4); + // ii_U = in_U; + // inEnd_U = nnc(pSrc_U).shift((int)szSrc_U); + // String8 ipEnd_U = nnc(pSrc_U).shift((int)(szSrc_U - 13)); + // Container dict_U = Container.from(new TypeInfo>(){}, + // mPBuffer_U); + // if(dict_U == null) { + // pos = posEnd; + // continue positionLoop; + // } + // case posNext: + // case posTryMatch: + // case posLiteral: + // forLoop: + // for(;; pos = 0) switch(pos) { + // default: + // long mOff_U = 0; + // case posNext: + // if(true) { + // pos = posLiteral; + // continue positionLoop; + // } + // if(true) { + // pos = posTryMatch; + // continue positionLoop; + // } + // if(true) { + // pos = posLiteral; + // continue positionLoop; + // } + // if(true) { + // pos = posTryMatch; + // continue positionLoop; + // } + // pos = posLiteral; + // continue positionLoop; + // case posTryMatch: + // case posLiteral: + // if(true) { + // if(true) { + // break forLoop; + // } + // continue; + // } + // + // if(true) { + // } else if(true) { + // } else { + // } + // ii_U = ip_U; + // pos = posNext; + // continue positionLoop; + // } + // case posEnd: + // szDst_U[0] = pd_U(op_U, pDst_U); + // return (int)pd_U(inEnd_U, ii_U); + // } + // } + +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectEventArgs.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectEventArgs.java new file mode 100644 index 00000000000..9e5f9fa895e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectEventArgs.java @@ -0,0 +1,10 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Connect event argumnets. */ +public class ConnectEventArgs { + /** Connect exception. Can be null. */ + public Exception Exception; + + /** Connect exception. Can be null. */ + public ConnectProgress Progress = ConnectProgress.values()[0]; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectException.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectException.java new file mode 100644 index 00000000000..45361b9e865 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectException.java @@ -0,0 +1,17 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class ConnectException extends Exception { + public ConnectException() {} + + public ConnectException(String message) { + super(message); + } + + public ConnectException(Throwable cause) { + super(cause); + } + + public ConnectException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectProgress.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectProgress.java new file mode 100644 index 00000000000..d13b935050b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConnectProgress.java @@ -0,0 +1,21 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ConnectProgress { + SendLogin, + SendAccountPassword, + AcceptAuthorized, + RequestTradeInfo, + Connected, + Exception, + Disconnect; + + public static final int SIZE = java.lang.Integer.SIZE; + + public int getValue() { + return this.ordinal(); + } + + public static ConnectProgress forValue(int value) { + return values()[value]; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connection.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connection.java new file mode 100644 index 00000000000..7cbd3f74ab5 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Connection.java @@ -0,0 +1,677 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.*; +import java.security.cert.X509Certificate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import javax.net.ssl.KeyManagerFactory; + +class Connection extends SecureSocket { + + public ArrayList LoginIdWebServerUrls = + new ArrayList(Arrays.asList("http://trial.mtapi.io")); + + public MT5API QC; + + public Connection(MT5API qc) { + QC = qc; + } + + public Connection( + int user, + String pass, + String host, + int port, + String proxyHost, + int proxyPort, + String proxyUser, + String proxyPass, + MT5API qc) { + QC = qc; + } + + public Connection(Connection con) { + QC = con.QC; + } + + public void Login(boolean bDataCenter, Logger log) throws IOException { + + log.trace("Connecting to server"); + try { + Connect(QC.Host, QC.Port); + log.trace("Socket opened " + QC.Host + ":" + QC.Port); + SendLogin(log); + } catch (RuntimeException ex) { + log.trace(ex.toString()); + Close(); + throw ex; + } + } + + private Object SendLock = new Object(); + + public final void SendPacket(byte type, OutBuf buf) throws IOException { + SendPacket(type, buf, false); + } + + public final void SendPacket(byte type, OutBuf buf, boolean compressed) throws IOException { + synchronized (SendLock) { + buf.CreateHeader(type, GetSendId(), compressed); + Encryptor.EncryptPacket(buf); + Send(buf.ToArray()); + } + } + + public final void SendCompress(byte type, OutBuf buf) throws IOException { + // for (int i = 303; i < 353; i++) //TODO + // buf.List[i] = 254; + // var compr = new Compr().Deflate(buf.ToArray()); + byte[] compr = Compressor.compress(buf.ToArray()); + OutBuf res = new OutBuf(); + res.Add(buf.List.size()); + res.Add(compr.length); + res.Add(compr); + SendPacket(type, res, true); + } + + public final InBuf RecievePacket() throws IOException { + byte[] bytes = Receive(9); + PacketHdr hdr = UDT.ReadStruct(bytes, 0, 9, new PacketHdr()); + byte cmd = hdr.Type; + bytes = Receive(hdr.PacketSize); + return new InBuf(Decryptor.Decrypt(bytes), hdr); + } + + private static final Object IdLock = new Object(); + private static int Id = 0; + + private int GetSendId() { + synchronized (IdLock) { + return Id++; + } + } + + private void SendLogin(Logger log) throws IOException { + log.trace("Send login"); + QC.OnConnectCall(null, ConnectProgress.SendLogin); + byte[] buf = new byte[34]; + buf[0] = (byte) LocalDateTime.now().getNano(); // (byte)DateTime.Now.Ticks; TODO + buf[1] = 0; // m_ConnectInfo.m_Server.m_nSrvType + System.arraycopy( + BitConverter.GetBytes((short) CLIENT_BUILD), + 0, + buf, + 2, + BitConverter.GetBytes((short) CLIENT_BUILD).length); // MT5 Terminal build + System.arraycopy( + BitConverter.GetBytes((short) 20813), + 0, + buf, + 4, + BitConverter.GetBytes((short) 20813).length); // MQ + System.arraycopy( + BitConverter.getBytesLong(QC.User), 0, buf, 6, BitConverter.getBytesLong(QC.User).length); + // byte[] key = new byte[] {0x3a, 0x00, (byte) 0xfa, (byte)0x9d, (byte) 0xd4, (byte)0xed, + // 0x15, 0x5f, 0x71, 0x68, (byte)0xe5, (byte)0xf2, 0x66, (byte)0x9f, (byte) 0xd1, + // (byte)0xe8}; //TODO GetCommonKey + byte[] key = new byte[16]; + Random rand = new Random(); + for (int i = 1; i < key.length; i++) { + byte v = (byte) rand.nextInt(); + key[i] = v; + key[0] += v; + } + System.arraycopy(key, 0, buf, 14, key.length); + System.arraycopy( + BitConverter.GetBytes(0x11528a15), + 0, + buf, + 30, + BitConverter.GetBytes(0x11528a15).length); // TODO random + byte[] pack = new byte[9 + buf.length]; + pack[0] = 0; + System.arraycopy( + BitConverter.GetBytes(buf.length), 0, pack, 1, BitConverter.GetBytes(buf.length).length); + byte[] id = BitConverter.GetBytes((short) GetSendId()); + System.arraycopy(id, 0, pack, 5, id.length); + System.arraycopy( + BitConverter.GetBytes(((short) (2 & 0xFFFF)) & 0xFFFF), + 0, + pack, + 7, + BitConverter.GetBytes(((short) (2 & 0xFFFF)) & 0xFFFF).length); // Flags PHF_COMPLETE + Crypt.EasyCrypt(buf); + System.arraycopy(buf, 0, pack, 9, buf.length); + Send(pack); + byte[] res = Receive(9); + PacketHdr hdr = UDT.ReadStruct(res, 0, 9, new PacketHdr()); + if ((hdr.Type & 0xFF) != 0 || hdr.PacketSize != 32) { + throw new RuntimeException("SendAccountPassword expected"); + } + res = Crypt.EasyDecrypt(Receive(hdr.PacketSize)); + BuildRec rec = UDT.ReadStruct(res, 0, 32, new BuildRec()); + if (rec.StatusCode != Msg.DONE) { + throw new RuntimeException(rec.StatusCode.toString()); + } + SignData = rec.SignData; + // SignData = new byte[]{(byte) 0x8f, 0x65, 0x23, 0x5a, (byte) 0xd0, (byte) 0xcf, (byte) 0x9b, + // 0x11, (byte) 0x8d, 0x2b, 0x0d, 0x67, (byte) 0xc8, 0x02, 0x31, 0x44}; + SendAccountPassword(log); + } + + public final void Disconnect() throws IOException { + // SendEncode(new byte[] { 0xD }); + // Thread.Sleep(100); + Close(); + } + + public final void Decompress(InBuf buf) { + int realSize = buf.Int(); + int comprSize = buf.Int(); + byte[] data = buf.Bytes(comprSize); + byte[] res = Decompressor.decompress(data, realSize); + buf.SetBuf(res); + } + + private byte[] SignData; + + private byte[] RandData; + + private void SendAccountPassword(Logger log) throws IOException { + log.trace("SendPassword"); + QC.OnConnectCall(null, ConnectProgress.SendAccountPassword); + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy( + BitConverter.getBytesLong(QC.User), 0, buf, 0, BitConverter.getBytesLong(QC.User).length); + System.arraycopy( + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8, + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy( + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8 + QC.Password.length() * 2, + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte[] cryptKey = MD5.computeMD5(new byte[][] {buf, SignData}); + RandData = + new byte[] { + (byte) 0x84, + 0x3f, + 0x63, + (byte) 0xdb, + (byte) 0xd4, + 0x50, + (byte) 0xd4, + (byte) 0xab, + (byte) 0xdf, + (byte) 0x9a, + (byte) 0xa0, + (byte) 0x86, + 0x5d, + 0x2a, + (byte) 0xc7, + 0x3e + }; // TODO + buf = new byte[0x22]; + System.arraycopy( + BitConverter.GetBytes(((short) (0xbbda & 0xFFFF)) & 0xFFFF), + 0, + buf, + 0, + BitConverter.GetBytes(((short) (0xbbda & 0xFFFF)) & 0xFFFF).length); // s0 TODO rand + System.arraycopy(cryptKey, 0, buf, 2, cryptKey.length); // cryp key + System.arraycopy(cryptKey, 0, buf, 0x12, cryptKey.length); // rand data + byte[] pack = new byte[9 + buf.length]; + pack[0] = 1; // Type + System.arraycopy( + BitConverter.GetBytes(buf.length), + 0, + pack, + 1, + BitConverter.GetBytes(buf.length).length); // size + byte[] id = BitConverter.GetBytes((short) GetSendId()); + System.arraycopy(id, 0, pack, 5, id.length); // ID + System.arraycopy( + BitConverter.GetBytes(((short) (2 & 0xFFFF)) & 0xFFFF), + 0, + pack, + 7, + BitConverter.GetBytes(((short) (2 & 0xFFFF)) & 0xFFFF).length); // Flags PHF_COMPLETE + Crypt.EasyCrypt(buf); + System.arraycopy(buf, 0, pack, 9, buf.length); + Send(pack); + AcceptAuthorized(log); + } + + public String ServerName; + public byte[] SrvCert; + public byte[] AesKey; + public byte[] ProtectKey; + // public byte[] CryptKey; + public PackDecrypt Decryptor; + public PackEncrypt Encryptor; + + public short TradeBuild; + public short SymBuild; + public long LoginId; + public long LoginIdEx; + private final long CLIENT_BUILD = 4424; + + private void AcceptAuthorized(Logger log) throws IOException { + log.trace("AcceptAuthorized"); + QC.OnConnectCall(null, ConnectProgress.AcceptAuthorized); + byte[] res = Receive(9); + PacketHdr hdr = UDT.ReadStruct(res, 0, 9, new PacketHdr()); + if ((hdr.Type & 0xFF) != 1) // || hdr.PacketSize != 32 + { + throw new RuntimeException("AcceptAuthorized expected"); + } + res = Crypt.EasyDecrypt(Receive(hdr.PacketSize)); + LoginRcv1 rec = UDT.ReadStruct(res, 0, 0x2C, new LoginRcv1()); + if (rec.StatusCode != Msg.DONE && rec.StatusCode != Msg.ADVANCED_AUTHORIZATION) { + throw new RuntimeException(rec.StatusCode.toString()); + } + TradeBuild = rec.TradeBuild; + SymBuild = rec.SymBuild; + InBuf buf = new InBuf(res, 0x2C); + while (buf.gethasData()) { + switch ((buf.Byte() & 0xFF)) { + case 0: + ServerName = buf.Str(); + break; + case 1: + SrvCert = buf.ByteAr(); + break; + case 7: + AesKey = buf.ByteAr(); + byte[] key = GetCryptKey(AesKey); + Encryptor = new PackEncrypt(key); + Decryptor = new PackDecrypt(key); + break; + case 0xA: + buf.ByteAr(); // bStatus = bufMan.GetRecord(pConnectTime, szConnectTime); + break; + case 0xB: + buf.ByteAr(); // bStatus = bufMan.GetRecord(pNetAddr, szNetAddr); + break; + case 0xC: + buf.ByteAr(); // bStatus = bufMan.GetRecord(varÑ4, varC0); + break; + case 0x13: + buf.ByteAr(); // bStatus = bufMan.GetRecord(pOneTimeKey, szOneTimeKey); + break; + case 0x1B: + ProtectKey = GetProtectKey(buf.ByteAr()); + break; + case (byte) 0x1C: + LoginId = + new LoginId().Decode(buf.ByteAr()) + ^ QC.User + ^ CLIENT_BUILD + ^ BitConverter.ToInt64(SignData, 0) + ^ 0x05286AED3286692AL; + // LoginId = GetLoginId(LoginIdWebServerUrls, buf.ByteAr(), "CheckMT5Java"); + break; + case 0x23: + LoginIdEx = GetLoginId(QC.LoginIdExServerUrls, buf.ByteAr(), "DecodeEx"); + break; + default: + buf.ByteAr(); + break; + } + } + if (rec.StatusCode == Msg.ADVANCED_AUTHORIZATION) { + try { + KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance("SunX509"); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(new ByteArrayInputStream(QC.PfxFile), QC.PfxFilePassword.toCharArray()); + kmf.init(keystore, QC.PfxFilePassword.toCharArray()); + Enumeration aliases = keystore.aliases(); + PrivateKey key = null; + X509Certificate cert = null; + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + key = (PrivateKey) keystore.getKey(alias, QC.PfxFilePassword.toCharArray()); + if (key != null) cert = (X509Certificate) keystore.getCertificate(alias); + } + if (cert == null) throw new RuntimeException("RSA private key not found"); + byte[] encodedCert = cert.getEncoded(); + Signature privateSignature = Signature.getInstance("SHA1withRSA"); + privateSignature.initSign(key); + privateSignature.update(SignData); + byte[] sign = privateSignature.sign(); + reverse(sign); + OutBuf ob = new OutBuf(); + ob.DataToBuffer(new byte[16]); + ob.ByteToBuffer((byte) 4); + ob.LongToBuffer(sign.length); + ob.DataToBuffer(sign); + ob.ByteToBuffer((byte) 3); + ob.LongToBuffer(encodedCert.length); + ob.DataToBuffer(encodedCert); + SendPacket((byte) 2, ob, false); + } catch (Exception ex) { + throw new IOException("Invalid cerificate: " + ex.getMessage()); + } + } + RequestTradeInfo(); + } + + static void reverse(byte[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + private long GetLoginId(ArrayList urls, byte[] loginhash, String function) + throws IOException { + LoginIdWebServer lid = new LoginIdWebServer(); + long loginid = 0; + boolean got = false; + StringBuilder ex = new StringBuilder(); + for (int i = 0; i < urls.size(); i++) { + try { + loginid = + lid.Decode( + urls.get(i) + "/" + function, + QC.Guid, + loginhash, + QC.LoginIdWebServerTimeout, + QC.Log); + got = true; + break; + } catch (Exception e) { + ex.append(e.getMessage()).append("(").append(urls.get(i)).append("); "); + } + } + if (!got) throw new RuntimeException("Cannot receive LoginID: " + ex.toString()); + return loginid + ^ QC.User + ^ CLIENT_BUILD + ^ BitConverter.ToInt64(SignData, 0) + ^ 0x05286AED3286692AL; + } + + private byte[] GetProtectKey(byte[] protectKey) { + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy( + BitConverter.GetBytes(QC.User), 0, buf, 0, BitConverter.GetBytes(QC.User).length); + System.arraycopy( + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8, + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy( + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8 + QC.Password.length() * 2, + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte[] key = MD5.computeMD5(buf); + byte[] data = new byte[protectKey.length + 16]; + System.arraycopy(key, 0, data, protectKey.length, key.length); + System.arraycopy( + Crypt.Decode(protectKey, key), 0, data, 0, Crypt.Decode(protectKey, key).length); + SHA256Native sha = new SHA256Native(); + return sha.ComputeHash(data); + } + + public final void RequestTradeInfo() throws IOException { + QC.OnConnectCall(null, ConnectProgress.RequestTradeInfo); + // OutBuf ping = new OutBuf(); + // ping.CreateHeader(10, GetSendId()); + // Send(ping.Bytes); + + OutBuf buf = new OutBuf(); + buf.ByteToBuffer((byte) 0x22); // cmd + buf.LongToBuffer(8); // size + buf.LongLongToBuffer(-1); // data + buf.ByteToBuffer((byte) 0x27); // cmd + buf.LongToBuffer(4); // size + buf.LongToBuffer(0); // data m_Config.s10E TODO? + // NEW m_sUtmCampaign and etc... + // if (m_Cfg.m_Common.m_bNewsEnable) + // { + // pBufMan->ByteToBuffer(0x19); //cmd (config News) + // pBufMan->LongToBuffer((m_Cfg.m_Common.m_nNumberLanguages + 1) * 4); //size + // pBufMan->LongToBuffer(m_Cfg.m_Common.m_nNumberLanguages); //data + // pBufMan->DataToBuffer(m_Cfg.m_Common.m_nNewsLanguages, m_Cfg.m_Common.m_nNumberLanguages * + // 4); //data + // pBufMan->ByteToBuffer(0x16); //cmd (request News) + // pBufMan->LongToBuffer(9); //size + // pBufMan->ByteToBuffer(1); //data + // pBufMan->LongLongToBuffer(m_News.GetFileTime()); //data + // } + // else + // { + // pBufMan->ByteToBuffer(0x16); //cmd (request News) + // pBufMan->LongToBuffer(9); //size + // pBufMan->ByteToBuffer(0); //data + // pBufMan->LongLongToBuffer(0); //data + // } + long softid = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE); + buf.ByteToBuffer((byte) 0x6C); // cmd (send soft id) + buf.LongToBuffer(8); // size + buf.LongLongToBuffer(softid); // data + + buf.ByteToBuffer((byte) 0x84); // cmd (send application info) + buf.LongToBuffer(0); // size + buf.ByteToBuffer((byte) 0x7F); + String info = + "file=terminal64.exe\tversion=" + + CLIENT_BUILD + + "\tcert_company=MetaQuotes Ltd\tcert_issuer=DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1\tcert_serial=04390a4c5f8906a1d7052c1768d45047\tos_ver=Windows 11 build 22" + + RandomNumericString(3) + + "\tos_id=" + + RandomNumericString(4) + + "-" + + RandomNumericString(4) + + "-" + + RandomNumericString(4) + + "-AAOEM\tcomputer=" + + RandomString(4) + + "\t\0"; + byte[] infoBytes; + try { + infoBytes = info.getBytes("UNICODE"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + byte[] tmp = new byte[infoBytes.length - 3 + 1]; + for (int i = 0; i < tmp.length - 1; i++) tmp[i] = infoBytes[i + 3]; + infoBytes = tmp; + + buf.LongToBuffer(infoBytes.length); + buf.DataToBuffer(infoBytes); + + buf.ByteToBuffer((byte) 0x18); // cmd (request Mails) + buf.LongToBuffer(8); // size + buf.LongLongToBuffer(0); // data m_Mails.GetLastMailTime() + RequestSymbols(buf); // request Symbols + RequestSpreads(buf); // request Spreads + RequestTickers(buf); // request Tickers + AcceptLoginId(buf); + SendPacket((byte) 0xC, buf); + } + + public static String RandomString(int length) { + String candidateChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(candidateChars.charAt(random.nextInt(candidateChars.length()))); + } + return sb.toString(); + } + + public static String RandomNumericString(int length) { + String candidateChars = "0123456789"; + StringBuilder sb = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < length; i++) { + sb.append(candidateChars.charAt(random.nextInt(candidateChars.length()))); + } + return sb.toString(); + } + + /*void vSubscribeClient::RequestTicks() + { + if (m_pClient->GetServerStatus() != vAcceptAccount) + return; + vSockBufManager bufMan(0); + m_Crit.EnterCriticalSection(); + bufMan.ByteToBuffer(9); + bufMan.LongToBuffer(m_arrTicks.GetSize()); + for (int i = 0; i < m_arrTicks.GetSize(); i++) + bufMan.LongToBuffer(m_arrTicks[i]->m_SubSym.m_nId); + m_bRequest = true; + m_Crit.LeaveCriticalSection(); + m_pClient->SendPacket(0x69, &bufMan); + }*/ + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] GetCryptKey(byte[] aesKey) + private byte[] GetCryptKey(byte[] aesKey) { + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy( + BitConverter.getBytesLong(QC.User), 0, buf, 0, BitConverter.getBytesLong(QC.User).length); + System.arraycopy( + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8, + QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy( + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + buf, + 8 + QC.Password.length() * 2, + ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte key[] = MD5.computeMD5(buf); + // int[] tmp = new int[]{0x41, 0x23, 0xde, 0xfb, 0x6b, 0xb3, 0x40, 0xdb, 0xfc, 0xb0, 0x7a, 0xd9, + // 0x34, 0xa8, 0xf0, 0x9f, 0xaf, 0x1b, 0x31, 0x4d, 0x93, 0xe0, 0xcc, 0x05, 0x0e, 0xc3, 0x65, + // 0xad, 0x00, 0x73, 0x07, 0xf0, 0xf4, 0x9c, 0xbc, 0x86, 0x55, 0x50, 0x5b, 0xb6, 0xdf, 0xab, + // 0xa2, 0x19, 0x53, 0xb7, 0x84, 0xa0, 0x6f, 0x8d, 0xc4, 0xd8, 0x1d, 0xbd, 0xc3, 0x6d, 0xeb, + // 0xf0, 0x97, 0xee, 0xbb, 0xcc, 0x5c, 0xcf, 0xbb, 0x18, 0xcf, 0xb4, 0x98, 0x21, 0x19, 0xed, + // 0xef, 0x5c, 0xe8, 0x3c, 0x05, 0x4c, 0xc4, 0xdf, 0xb7, 0xa6, 0xa1, 0xca, 0xb4, 0xb4, 0xb0, + // 0x35, 0xe9, 0xf7, 0x7b, 0x56, 0x3d, 0x10, 0x31, 0x70, 0x7f, 0xe1, 0x3e, 0x0c, 0x9c, 0xef, + // 0x1f, 0x86, 0x16, 0x0a, 0x75, 0xcc, 0xb1, 0x31, 0x58, 0x63, 0x70, 0xb0, 0xed, 0xab, 0xbf, + // 0x8b, 0x3b, 0x63, 0xf2, 0x1f, 0x3a, 0x6f, 0xee, 0x07, 0x2d, 0xd9, 0x26, 0x3d, 0x32, 0x17, + // 0xca, 0x81, 0x18, 0x8c, 0x3a, 0xff, 0x70, 0x51, 0xc1, 0x2c, 0xe7, 0x34, 0x80, 0xf1, 0xd3, + // 0x01, 0xa8, 0x0b, 0x0b, 0x01, 0xed, 0xb2, 0xfc, 0xc1, 0x37, 0x78, 0xf9, 0x14, 0x9a, 0x75, + // 0xd3, 0x5b, 0x88, 0xa1, 0xaa, 0x04, 0x45, 0x81, 0x02, 0x51, 0x9c, 0x06, 0x19, 0x5b, 0xd1, + // 0xb2, 0x79, 0x56, 0xd6, 0xfc, 0xc9, 0x16, 0xc1, 0xf6, 0xe8, 0xd2, 0x7e, 0x2d, 0x3d, 0x29, + // 0xc1, 0xd0, 0x48, 0x62, 0x3f, 0x15, 0x7d, 0xf8, 0x1e, 0xd9, 0x53, 0x56, 0xaa, 0x87, 0x98, + // 0xdf, 0x49, 0x3a, 0x07, 0x31, 0xb1, 0x26, 0x1c, 0xab, 0x58, 0x34, 0x27, 0x2d, 0x2c, 0xec, + // 0xa0, 0x1e, 0x85, 0x98, 0xba, 0xb4, 0x58, 0xa6, 0x3a, 0x6f, 0xad, 0x4f, 0x8a, 0xe7, 0x53, + // 0x76, 0x09, 0xc8, 0xd2, 0xd3, 0xa6, 0x1b, 0xa1, 0x50, 0xad, 0xc7, 0x99, 0xb3, 0xe0, 0x57, + // 0xaa, 0x7e, 0xca, 0xfd}; + // for (int i = 0; i LoginIdWebServerUrls = new ArrayList(Arrays.asList("http://trial.mtapi.io")); + + public MT5API QC; + + public Connection(MT5API qc) { + QC = qc; + } + + public Connection(int user, String pass, String host, int port, String proxyHost, int proxyPort, String proxyUser, String proxyPass, MT5API qc) + { + QC = qc; + } + + public Connection(Connection con) + { + QC = con.QC; + } + + public void Login(boolean bDataCenter, Logger log) throws IOException { + + log.trace("Connecting to server"); + try { + Connect(QC.Host, QC.Port); + log.trace("Socket opened " + QC.Host + ":" + QC.Port); + SendLogin(log); + } + catch (RuntimeException ex) + { + log.trace(ex.toString()); + Close(); + throw ex; + } + } + + private Object SendLock = new Object(); + + + public final void SendPacket(byte type, OutBuf buf) throws IOException { + SendPacket(type, buf, false); + } + + public final void SendPacket(byte type, OutBuf buf, boolean compressed) throws IOException { + synchronized (SendLock) + { + buf.CreateHeader(type, GetSendId(), compressed); + Encryptor.EncryptPacket(buf); + Send(buf.ToArray()); + } + } + + public final void SendCompress(byte type, OutBuf buf) throws IOException { + //for (int i = 303; i < 353; i++) //TODO + // buf.List[i] = 254; + //var compr = new Compr().Deflate(buf.ToArray()); + byte[] compr = Compressor.compress(buf.ToArray()); + OutBuf res = new OutBuf(); + res.Add(buf.List.size()); + res.Add(compr.length); + res.Add(compr); + SendPacket(type, res, true); + } + + + public final InBuf RecievePacket() throws IOException { + byte[] bytes = Receive(9); + PacketHdr hdr = UDT.ReadStruct(bytes, 0, 9, new PacketHdr()); + byte cmd = hdr.Type; + bytes = Receive(hdr.PacketSize); + return new InBuf(Decryptor.Decrypt(bytes), hdr); + } + + private static final Object IdLock = new Object(); + private static int Id = 0; + + private int GetSendId() + { + synchronized (IdLock) + { + return Id++; + } + } + + private void SendLogin(Logger log) throws IOException { + log.trace("Send login"); + QC.OnConnectCall(null, ConnectProgress.SendLogin); + byte[] buf = new byte[34]; + buf[0] = (byte)LocalDateTime.now().getNano(); //(byte)DateTime.Now.Ticks; TODO + buf[1] = 0; // m_ConnectInfo.m_Server.m_nSrvType + System.arraycopy(BitConverter.GetBytes((short)CLIENT_BUILD), 0, buf, 2, BitConverter.GetBytes((short)CLIENT_BUILD).length); //MT5 Terminal build + System.arraycopy(BitConverter.GetBytes((short)20813), 0, buf, 4, BitConverter.GetBytes((short)20813).length); //MQ + System.arraycopy(BitConverter.getBytesLong(QC.User), 0, buf, 6, BitConverter.getBytesLong(QC.User).length); + //byte[] key = new byte[] {0x3a, 0x00, (byte) 0xfa, (byte)0x9d, (byte) 0xd4, (byte)0xed, 0x15, 0x5f, 0x71, 0x68, (byte)0xe5, (byte)0xf2, 0x66, (byte)0x9f, (byte) 0xd1, (byte)0xe8}; //TODO GetCommonKey + byte[] key = new byte[16]; + Random rand = new Random(); + for (int i = 1; i < key.length; i++) + { + byte v = (byte)rand.nextInt(); + key[i] = v; + key[0] += v; + } + System.arraycopy(key, 0, buf, 14, key.length); + System.arraycopy(BitConverter.GetBytes(0x11528a15), 0, buf, 30, BitConverter.GetBytes(0x11528a15).length); //TODO random + byte[] pack = new byte[9 + buf.length]; + pack[0] = 0; + System.arraycopy(BitConverter.GetBytes(buf.length), 0, pack, 1, BitConverter.GetBytes(buf.length).length); + byte[] id = BitConverter.GetBytes((short)GetSendId()); + System.arraycopy(id, 0, pack, 5, id.length); + System.arraycopy(BitConverter.GetBytes(((short)(2 & 0xFFFF)) & 0xFFFF), 0, pack, 7, BitConverter.GetBytes(((short)(2 & 0xFFFF)) & 0xFFFF).length); //Flags PHF_COMPLETE + Crypt.EasyCrypt(buf); + System.arraycopy(buf, 0, pack, 9, buf.length); + Send(pack); + byte[] res = Receive(9); + PacketHdr hdr = UDT.ReadStruct(res, 0, 9, new PacketHdr()); + if ((hdr.Type & 0xFF) != 0 || hdr.PacketSize != 32) + { + throw new RuntimeException("SendAccountPassword expected"); + } + res = Crypt.EasyDecrypt(Receive(hdr.PacketSize)); + BuildRec rec = UDT.ReadStruct(res, 0, 32, new BuildRec()); + if (rec.StatusCode != Msg.DONE) + { + throw new RuntimeException(rec.StatusCode.toString()); + } + SignData = rec.SignData; + //SignData = new byte[]{(byte) 0x8f, 0x65, 0x23, 0x5a, (byte) 0xd0, (byte) 0xcf, (byte) 0x9b, 0x11, (byte) 0x8d, 0x2b, 0x0d, 0x67, (byte) 0xc8, 0x02, 0x31, 0x44}; + SendAccountPassword(log); + } + + public final void Disconnect() throws IOException { + //SendEncode(new byte[] { 0xD }); + //Thread.Sleep(100); + Close(); + } + + public final void Decompress(InBuf buf) + { + int realSize = buf.Int(); + int comprSize = buf.Int(); + byte[] data = buf.Bytes(comprSize); + byte[] res = Decompressor.decompress(data, realSize); + buf.SetBuf(res); + } + + private byte[] SignData; + + private byte[] RandData; + + private void SendAccountPassword(Logger log) throws IOException { + log.trace("SendPassword"); + QC.OnConnectCall(null, ConnectProgress.SendAccountPassword); + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy(BitConverter.getBytesLong(QC.User), 0, buf, 0, BitConverter.getBytesLong(QC.User).length); + System.arraycopy(QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8, QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy(("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8 + QC.Password.length() * 2, ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte[] cryptKey = MD5.computeMD5(new byte[][] {buf, SignData}); + RandData = new byte[] {(byte)0x84, 0x3f, 0x63, (byte)0xdb,(byte) 0xd4, 0x50, (byte)0xd4, (byte)0xab, (byte)0xdf, (byte)0x9a,(byte) 0xa0,(byte) 0x86, 0x5d, 0x2a, (byte)0xc7, 0x3e}; //TODO + buf = new byte[0x22]; + System.arraycopy(BitConverter.GetBytes(((short)(0xbbda & 0xFFFF)) & 0xFFFF), 0, buf, 0, BitConverter.GetBytes(((short)(0xbbda & 0xFFFF)) & 0xFFFF).length); //s0 TODO rand + System.arraycopy(cryptKey, 0, buf, 2, cryptKey.length); // cryp key + System.arraycopy(cryptKey, 0, buf, 0x12, cryptKey.length); // rand data + byte[] pack = new byte[9 + buf.length]; + pack[0] = 1; //Type + System.arraycopy(BitConverter.GetBytes(buf.length), 0, pack, 1, BitConverter.GetBytes(buf.length).length); //size + byte[] id = BitConverter.GetBytes((short)GetSendId()); + System.arraycopy(id, 0, pack, 5, id.length); //ID + System.arraycopy(BitConverter.GetBytes(((short)(2 & 0xFFFF)) & 0xFFFF), 0, pack, 7, BitConverter.GetBytes(((short)(2 & 0xFFFF)) & 0xFFFF).length); //Flags PHF_COMPLETE + Crypt.EasyCrypt(buf); + System.arraycopy(buf, 0, pack, 9, buf.length); + Send(pack); + AcceptAuthorized(log); + } + + public String ServerName; + public byte[] SrvCert; + public byte[] AesKey; + public byte[] ProtectKey; + //public byte[] CryptKey; + public PackDecrypt Decryptor; + public PackEncrypt Encryptor; + + public short TradeBuild; + public short SymBuild; + public long LoginId; + public long LoginIdEx; + private final long CLIENT_BUILD = 4424; + + private void AcceptAuthorized(Logger log) throws IOException { + log.trace("AcceptAuthorized"); + QC.OnConnectCall(null, ConnectProgress.AcceptAuthorized); + byte[] res = Receive(9); + PacketHdr hdr = UDT.ReadStruct(res, 0, 9, new PacketHdr()); + if ((hdr.Type & 0xFF) != 1) //|| hdr.PacketSize != 32 + { + throw new RuntimeException("AcceptAuthorized expected"); + } + res = Crypt.EasyDecrypt(Receive(hdr.PacketSize)); + LoginRcv1 rec = UDT.ReadStruct(res, 0, 0x2C, new LoginRcv1()); + if (rec.StatusCode != Msg.DONE && rec.StatusCode != Msg.ADVANCED_AUTHORIZATION) + { + throw new RuntimeException(rec.StatusCode.toString()); + } + TradeBuild = rec.TradeBuild; + SymBuild = rec.SymBuild; + InBuf buf = new InBuf(res, 0x2C); + while (buf.gethasData()) + { + switch ((buf.Byte() & 0xFF)) + { + case 0: + ServerName = buf.Str(); + break; + case 1: + SrvCert = buf.ByteAr(); + break; + case 7: + AesKey = buf.ByteAr(); + byte[] key = GetCryptKey(AesKey); + Encryptor = new PackEncrypt(key); + Decryptor = new PackDecrypt(key); + break; + case 0xA: + buf.ByteAr(); //bStatus = bufMan.GetRecord(pConnectTime, szConnectTime); + break; + case 0xB: + buf.ByteAr(); //bStatus = bufMan.GetRecord(pNetAddr, szNetAddr); + break; + case 0xC: + buf.ByteAr(); //bStatus = bufMan.GetRecord(varÑ4, varC0); + break; + case 0x13: + buf.ByteAr(); //bStatus = bufMan.GetRecord(pOneTimeKey, szOneTimeKey); + break; + case 0x1B: + ProtectKey = GetProtectKey(buf.ByteAr()); + break; + case (byte)0x1C: + //LoginId = new LoginId().Decode(buf.ByteAr()) ^ QC.User ^ CLIENT_BUILD ^ BitConverter.ToInt64(SignData, 0) ^ 0x05286AED3286692AL; + LoginId = GetLoginId(LoginIdWebServerUrls, buf.ByteAr(), "CheckMT5Java"); + break; + case 0x23: + LoginIdEx = GetLoginId(QC.LoginIdExServerUrls, buf.ByteAr(), "DecodeEx"); + break; + default: + buf.ByteAr(); + break; + } + } + if (rec.StatusCode == Msg.ADVANCED_AUTHORIZATION) + { + try { + KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance("SunX509"); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(new ByteArrayInputStream(QC.PfxFile), QC.PfxFilePassword.toCharArray()); + kmf.init(keystore, QC.PfxFilePassword.toCharArray()); + Enumeration aliases = keystore.aliases(); + PrivateKey key = null; + X509Certificate cert = null; + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + key = (PrivateKey) keystore.getKey(alias, QC.PfxFilePassword.toCharArray()); + if (key != null) + cert = (X509Certificate) keystore.getCertificate(alias); + } + if (cert == null) + throw new RuntimeException("RSA private key not found"); + byte[] encodedCert = cert.getEncoded(); + Signature privateSignature = Signature.getInstance("SHA1withRSA"); + privateSignature.initSign(key); + privateSignature.update(SignData); + byte[] sign = privateSignature.sign(); + reverse(sign); + OutBuf ob = new OutBuf(); + ob.DataToBuffer(new byte[16]); + ob.ByteToBuffer((byte) 4); + ob.LongToBuffer(sign.length); + ob.DataToBuffer(sign); + ob.ByteToBuffer((byte) 3); + ob.LongToBuffer(encodedCert.length); + ob.DataToBuffer(encodedCert); + SendPacket((byte) 2, ob, false); + } + catch (Exception ex) + { + throw new IOException("Invalid cerificate: " + ex.getMessage()); + } + } + RequestTradeInfo(); + } + + static void reverse(byte[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + private long GetLoginId(ArrayList urls, byte[] loginhash, String function) throws IOException { + LoginIdWebServer lid = new LoginIdWebServer(); + long loginid = 0; + boolean got = false; + Exception ex = null; + for (int i = 0; i < urls.size(); i++) { + try { + loginid = lid.Decode(urls.get(i) + "/" + function, QC.Guid, loginhash, QC.LoginIdWebServerTimeout, QC.Log); + got = true; + break; + }catch(Exception e) + { + ex = e; + } + } + if(!got) + if(ex == null) + throw new RuntimeException("Cannot recieve LoginID"); + else + throw new RuntimeException("Cannot recieve LoginID: " + ex.getMessage()); + return loginid ^ QC.User ^ CLIENT_BUILD ^ BitConverter.ToInt64(SignData, 0) ^ 0x05286AED3286692AL; + } + + + + private byte[] GetProtectKey(byte[] protectKey) + { + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy(BitConverter.GetBytes(QC.User), 0, buf, 0, BitConverter.GetBytes(QC.User).length); + System.arraycopy(QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8, QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy(("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8 + QC.Password.length() * 2, ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte[] key = MD5.computeMD5(buf); + byte[] data = new byte[protectKey.length + 16]; + System.arraycopy(key, 0, data, protectKey.length, key.length); + System.arraycopy(Crypt.Decode(protectKey, key), 0, data, 0, Crypt.Decode(protectKey, key).length); + SHA256Native sha = new SHA256Native(); + return sha.ComputeHash(data); + } + + public final void RequestTradeInfo() throws IOException { + QC.OnConnectCall(null, ConnectProgress.RequestTradeInfo); + //OutBuf ping = new OutBuf(); + //ping.CreateHeader(10, GetSendId()); + //Send(ping.Bytes); + + OutBuf buf = new OutBuf(); + buf.ByteToBuffer((byte)0x22); //cmd + buf.LongToBuffer(8); //size + buf.LongLongToBuffer(-1); //data + buf.ByteToBuffer((byte)0x27); //cmd + buf.LongToBuffer(4); //size + buf.LongToBuffer(0); //data m_Config.s10E TODO? + // NEW m_sUtmCampaign and etc... + // if (m_Cfg.m_Common.m_bNewsEnable) + // { + // pBufMan->ByteToBuffer(0x19); //cmd (config News) + // pBufMan->LongToBuffer((m_Cfg.m_Common.m_nNumberLanguages + 1) * 4); //size + // pBufMan->LongToBuffer(m_Cfg.m_Common.m_nNumberLanguages); //data + // pBufMan->DataToBuffer(m_Cfg.m_Common.m_nNewsLanguages, m_Cfg.m_Common.m_nNumberLanguages * 4); //data + // pBufMan->ByteToBuffer(0x16); //cmd (request News) + // pBufMan->LongToBuffer(9); //size + // pBufMan->ByteToBuffer(1); //data + // pBufMan->LongLongToBuffer(m_News.GetFileTime()); //data + // } + // else + // { + // pBufMan->ByteToBuffer(0x16); //cmd (request News) + // pBufMan->LongToBuffer(9); //size + // pBufMan->ByteToBuffer(0); //data + // pBufMan->LongLongToBuffer(0); //data + // } + long softid = ThreadLocalRandom.current().nextLong(Long.MAX_VALUE); + buf.ByteToBuffer((byte)0x6C); //cmd (send soft id) + buf.LongToBuffer(8); //size + buf.LongLongToBuffer(softid); //data + + buf.ByteToBuffer((byte)0x84); //cmd (send application info) + buf.LongToBuffer(0); //size + buf.ByteToBuffer((byte)0x7F); + String info = "file=terminal64.exe\tversion="+ + CLIENT_BUILD+ + "\tcert_company=MetaQuotes Ltd\tcert_issuer=DigiCert Trusted G4 Code Signing RSA4096 SHA384 2021 CA1\tcert_serial=04390a4c5f8906a1d7052c1768d45047\tos_ver=Windows 11 build 22"+ + RandomNumericString(3) + "\tos_id=" + RandomNumericString(4) + "-" + RandomNumericString(4) + "-" + + RandomNumericString(4) + "-AAOEM\tcomputer=" + RandomString(4) + "\t\0"; + byte[] infoBytes; + try { + infoBytes = info.getBytes("UNICODE"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + byte[] tmp = new byte[infoBytes.length - 3 + 1]; + for (int i = 0; i < tmp.length - 1; i++) + tmp[i] = infoBytes[i+3]; + infoBytes = tmp; + + buf.LongToBuffer(infoBytes.length); + buf.DataToBuffer(infoBytes); + + buf.ByteToBuffer((byte)0x18); //cmd (request Mails) + buf.LongToBuffer(8); //size + buf.LongLongToBuffer(0); //data m_Mails.GetLastMailTime() + RequestSymbols(buf); //request Symbols + RequestSpreads(buf); //request Spreads + RequestTickers(buf); //request Tickers + AcceptLoginId(buf); + SendPacket((byte)0xC, buf); + } + + public static String RandomString (int length) { + String candidateChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder (); + Random random = new Random (); + for (int i = 0; i < length; i ++) { + sb.append (candidateChars.charAt (random.nextInt (candidateChars + .length ()))); + } + return sb.toString (); + } + + public static String RandomNumericString (int length) { + String candidateChars = "0123456789"; + StringBuilder sb = new StringBuilder (); + Random random = new Random (); + for (int i = 0; i < length; i ++) { + sb.append (candidateChars.charAt (random.nextInt (candidateChars + .length ()))); + } + return sb.toString (); + } + + /*void vSubscribeClient::RequestTicks() + { + if (m_pClient->GetServerStatus() != vAcceptAccount) + return; + vSockBufManager bufMan(0); + m_Crit.EnterCriticalSection(); + bufMan.ByteToBuffer(9); + bufMan.LongToBuffer(m_arrTicks.GetSize()); + for (int i = 0; i < m_arrTicks.GetSize(); i++) + bufMan.LongToBuffer(m_arrTicks[i]->m_SubSym.m_nId); + m_bRequest = true; + m_Crit.LeaveCriticalSection(); + m_pClient->SendPacket(0x69, &bufMan); + }*/ + + + +//C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//ORIGINAL LINE: byte[] GetCryptKey(byte[] aesKey) + private byte[] GetCryptKey(byte[] aesKey) + { + byte[] buf = new byte[8 + QC.Password.length() * 2 + 2 * 2]; + System.arraycopy(BitConverter.getBytesLong(QC.User), 0, buf, 0, BitConverter.getBytesLong(QC.User).length); + System.arraycopy(QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8, QC.Password.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + System.arraycopy(("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE), 0, buf, 8 + QC.Password.length() * 2, ("MQ").getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + byte key[] = MD5.computeMD5(buf); + //int[] tmp = new int[]{0x41, 0x23, 0xde, 0xfb, 0x6b, 0xb3, 0x40, 0xdb, 0xfc, 0xb0, 0x7a, 0xd9, 0x34, 0xa8, 0xf0, 0x9f, 0xaf, 0x1b, 0x31, 0x4d, 0x93, 0xe0, 0xcc, 0x05, 0x0e, 0xc3, 0x65, 0xad, 0x00, 0x73, 0x07, 0xf0, 0xf4, 0x9c, 0xbc, 0x86, 0x55, 0x50, 0x5b, 0xb6, 0xdf, 0xab, 0xa2, 0x19, 0x53, 0xb7, 0x84, 0xa0, 0x6f, 0x8d, 0xc4, 0xd8, 0x1d, 0xbd, 0xc3, 0x6d, 0xeb, 0xf0, 0x97, 0xee, 0xbb, 0xcc, 0x5c, 0xcf, 0xbb, 0x18, 0xcf, 0xb4, 0x98, 0x21, 0x19, 0xed, 0xef, 0x5c, 0xe8, 0x3c, 0x05, 0x4c, 0xc4, 0xdf, 0xb7, 0xa6, 0xa1, 0xca, 0xb4, 0xb4, 0xb0, 0x35, 0xe9, 0xf7, 0x7b, 0x56, 0x3d, 0x10, 0x31, 0x70, 0x7f, 0xe1, 0x3e, 0x0c, 0x9c, 0xef, 0x1f, 0x86, 0x16, 0x0a, 0x75, 0xcc, 0xb1, 0x31, 0x58, 0x63, 0x70, 0xb0, 0xed, 0xab, 0xbf, 0x8b, 0x3b, 0x63, 0xf2, 0x1f, 0x3a, 0x6f, 0xee, 0x07, 0x2d, 0xd9, 0x26, 0x3d, 0x32, 0x17, 0xca, 0x81, 0x18, 0x8c, 0x3a, 0xff, 0x70, 0x51, 0xc1, 0x2c, 0xe7, 0x34, 0x80, 0xf1, 0xd3, 0x01, 0xa8, 0x0b, 0x0b, 0x01, 0xed, 0xb2, 0xfc, 0xc1, 0x37, 0x78, 0xf9, 0x14, 0x9a, 0x75, 0xd3, 0x5b, 0x88, 0xa1, 0xaa, 0x04, 0x45, 0x81, 0x02, 0x51, 0x9c, 0x06, 0x19, 0x5b, 0xd1, 0xb2, 0x79, 0x56, 0xd6, 0xfc, 0xc9, 0x16, 0xc1, 0xf6, 0xe8, 0xd2, 0x7e, 0x2d, 0x3d, 0x29, 0xc1, 0xd0, 0x48, 0x62, 0x3f, 0x15, 0x7d, 0xf8, 0x1e, 0xd9, 0x53, 0x56, 0xaa, 0x87, 0x98, 0xdf, 0x49, 0x3a, 0x07, 0x31, 0xb1, 0x26, 0x1c, 0xab, 0x58, 0x34, 0x27, 0x2d, 0x2c, 0xec, 0xa0, 0x1e, 0x85, 0x98, 0xba, 0xb4, 0x58, 0xa6, 0x3a, 0x6f, 0xad, 0x4f, 0x8a, 0xe7, 0x53, 0x76, 0x09, 0xc8, 0xd2, 0xd3, 0xa6, 0x1b, 0xa1, 0x50, 0xad, 0xc7, 0x99, 0xb3, 0xe0, 0x57, 0xaa, 0x7e, 0xca, 0xfd}; + //for (int i = 0; i { + ConnectToAccount(); + }; + ConnectThread = new MyThread(runnable, QC.User); + if (QC.CmdHandler != null) QC.CmdHandler.stop(); + if (QC.Connection != null) QC.Connection.Close(); + QC.GotAccountInfo = false; + QC.TimeoutDuringConnect = false; + ConnectThread.Start(); + break; + } + } + if (ConnectThread.Join(msTimeout)) { + if (ConnectThread.Exception == null) { + LinkedList subscriptions = + new LinkedList(Arrays.asList(QC.Subscriptions())); + for (Order order : QC.GetOpenedOrders()) + if (order.OrderType == OrderType.Buy || order.OrderType == OrderType.Sell) + if (!subscriptions.contains(order.Symbol)) subscriptions.add(order.Symbol); + if (subscriptions.size() > 0) + QC.Subscriber.Subscribe(subscriptions.toArray(new String[0])); // in case of reconnection + QC.OnConnectCall(null, ConnectProgress.Connected); + } else { + QC.OnConnectCall(ConnectThread.Exception, ConnectProgress.Exception); + throw ConnectThread.Exception; + } + } else { + QC.TimeoutDuringConnect = true; + TimeoutException ex = new TimeoutException("Not connected in " + msTimeout + " ms", QC.Log); + QC.OnConnectCall(ex, ConnectProgress.Exception); + throw ex; + } + } + + public final void runAsync(int msTimeout) { + new Thread( + () -> { + try { + go(msTimeout, QC.Host, QC.Port); + } catch (Exception e) { + } + }) + .start(); + } + + public final void stop() { + stop.set(true); + } + + private void ConnectToAccount() throws Exception { + Connection con = new Connection(QC); + con.Login(false, QC.Log); // login + CmdHandler cmd = new CmdHandler(QC); + cmd.StartCmdHandler(con, QC.User); + QC.Connection = con; + QC.CmdHandler = cmd; + while (QC.GotAccountInfo == false) { + if (stop.get()) { + break; + } + if (cmd.AccountLoaderException != null) throw cmd.AccountLoaderException; + Thread.sleep(1); + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Container.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Container.java new file mode 100644 index 00000000000..4f67a92cf6d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Container.java @@ -0,0 +1,5 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class Container { + public Container(int i) {} +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Convert.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Convert.java new file mode 100644 index 00000000000..b8d26d96fe9 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Convert.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class Convert { + public static long ToUInt64(boolean b) { + if (b) return 1; + else return 0; + } + + public static boolean ToBoolean(long l) { + if (l == 0) return false; + else return true; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertBytes.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertBytes.java new file mode 100644 index 00000000000..57300836e7e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertBytes.java @@ -0,0 +1,71 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; + +/** Convert byte array to strings. */ +class ConvertBytes { + /** Convert to HEX string. */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static string ToHex(byte[] bytes) + public static String ToHex(byte[] bytes) { + String res = ""; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: foreach (byte b in bytes) + for (byte b : bytes) { + res += DotNetToJavaStringHelper.padLeft(String.format("%X", b), 2, '0') + " "; + } + return res; + } + + /** Convert to ASCII string. */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static string ToAscii(byte[] bytes) + public static String ToAscii(byte[] bytes) { + return readStringASCII(bytes, 0, bytes.length); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static string ToUnicode(byte[] bytes) + public static String ToUnicode(byte[] bytes) { + return readString(bytes, 0, bytes.length); + } + + static String readString(byte[] buf, int of, int len) { + ArrayList res = new ArrayList(); + for (int i = 0; i < len; i += 2) { + if (buf[of + i] == 0 && buf[of + i + 1] == 0) break; + res.add(buf[of + i]); + res.add(buf[of + i + 1]); + } + byte[] array = new byte[res.size()]; + for (int i = 0; i < res.size(); i++) array[i] = res.get(i); + + try { + return new String(array, Charset.defaultCharset().toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + static String readStringASCII(byte[] buf, int of, int len) { + ArrayList res = new ArrayList(); + for (int i = 0; i < len; i += 1) { + if (buf[of + i] == 0) // && buf[of + i + 1] == 0 + if (i > 0) break; + res.add(buf[of + i]); + // res.Add(buf[of + i + 1]); + } + if (res.size() > 0) if (res.get(0) == 0) res.remove(0); + byte[] array = new byte[res.size()]; + for (int i = 0; i < res.size(); i++) array[i] = res.get(i); + String r = null; + try { + r = new String(array, Charset.forName("ASCII").toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return r; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertTo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertTo.java new file mode 100644 index 00000000000..113f15a8ee4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ConvertTo.java @@ -0,0 +1,31 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; + +class ConvertTo { + private static java.time.LocalDateTime StartTime = + java.time.LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0); + + public static java.time.LocalDateTime DateTime(long time) { + return StartTime.plusSeconds(time); + } + + public static java.time.LocalDateTime DateTimeMs(long time) { + return StartTime.plusNanos(time * 1000000); + } + + public static long Long(java.time.LocalDateTime time) { + return Duration.between(StartTime, time).getSeconds(); + } + + public static double LongLongToDouble(int digits, long value) { + digits = Math.min(digits, 11); + return Math.round(((double) (value) / DegreeP[digits]) * Math.pow(10, digits)) + / Math.pow(10, digits); + } + + private static double[] DegreeP = { + 1.0, 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5, 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10, 1.0e11, 1.0e12, + 1.0e13, 1.0e14, 1.0e15 + }; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Crypt.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Crypt.java new file mode 100644 index 00000000000..8a3692fafd8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Crypt.java @@ -0,0 +1,192 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class Crypt { + public static byte[] CryptKey = + new byte[] { + 0x41, + (byte) 0xB6, + 0x7F, + 0x58, + 0x38, + 0x0C, + (byte) 0xF0, + 0x2D, + 0x7B, + 0x39, + 0x08, + (byte) 0xFE, + 0x21, + (byte) 0xBB, + 0x41, + 0x58 + }; + + public static byte[] EasyCrypt(byte[] buf) { + int Last = 0; + for (int i = 0; i < buf.length; i++) { + buf[i] ^= (byte) ((Last + (CryptKey[i & 0xF] & 0xFF)) & 0xFF); + Last = (buf[i] & 0xFF); + } + return buf; + } + + public static byte[] EasyDecrypt(byte[] buf) { + int Last = 0; + for (int i = 0; i < buf.length; i++) { + int lst = Last; + Last = (buf[i] & 0xFF); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf[i] ^= (byte)(lst + CryptKey[i & 0xF]); + buf[i] ^= (byte) ((lst + (CryptKey[i & 0xF] & 0xFF)) & 0xFF); + } + return buf; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] Encrypt(byte[] buf) + public static byte[] Encrypt(byte[] buf) { + int Last = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[buf.Length]; + byte[] res = new byte[buf.length]; + for (int i = 0; i < buf.length; i++) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + CryptKey[i & 0xF])); + res[i] = (byte) (((buf[i] & 0xFF) ^ (Last + (CryptKey[i & 0xF] & 0xFF))) & 0xFF); + Last = (res[i] & 0xFF); + } + return buf; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] Decrypt(byte[] buf) + public static byte[] Decrypt(byte[] buf) { + int Last = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[buf.Length]; + byte[] res = new byte[buf.length]; + for (int i = 0; i < buf.length; i++) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + CryptKey[i & 0xF])); + res[i] = (byte) (((buf[i] & 0xFF) ^ (Last + (CryptKey[i & 0xF] & 0xFF))) & 0xFF); + Last = (buf[i] & 0xFF); + } + return res; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] Encode(byte[] buf, byte[] key) + // public static byte[] Encode(byte[] buf, byte[] key) + // { + //// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + //// ORIGINAL LINE: key = new byte[] { 0x43, 0xb0, 0xbe, 0xc7, 0xce, 0x46, 0x75, 0x9d, 0x88, 0xee, + // 0xb1, 0x04, 0x00, 0x2e, 0xd6, 0x57, 0x98, 0x75, 0xf5, 0xca, 0x23, 0xb5, 0x56, 0xb0, 0x83, 0xd6, + // 0xef, 0xcc, 0x20, 0xfb, 0x1f, 0xc0, 0x63, 0xe2, 0xc8, 0xeb, 0x0a, 0x90, 0xc2, 0xe4, 0x6b, 0x1a, + // 0x8d, 0xf5, 0x3c, 0xc6, 0x68, 0x88, 0x8e, 0xec, 0x20, 0x4a, 0x89, 0x14, 0xd1, 0x1f, 0x29, 0x41, + // 0xf6, 0x86, 0x32, 0x59, 0xda, 0xcc, 0xa4, 0x87, 0x1f, 0xa2, 0xff, 0xc6, 0xf6, 0xa5, 0x9f, 0x50, + // 0x6b, 0xa6, 0xf8, 0xbb, 0x46, 0x3d, 0xa0, 0x1d, 0xc1, 0x47, 0xc5, 0x4f, 0xe0, 0x35, 0xe6, 0xdd, + // 0x43, 0x80, 0xc9, 0xe8, 0xc8, 0x0e, 0x7f, 0xde, 0xd0, 0x33, 0x33, 0x8d, 0x0b, 0x0b, 0xcc, 0x7d, + // 0x74, 0xf5, 0x7b, 0x12, 0xbd, 0x16, 0x93, 0xf1, 0x01, 0xca, 0x8b, 0x38, 0xa0, 0xfc, 0x0e, 0x1d, + // 0x24, 0xac, 0x4d, 0xc3, 0xbe, 0x6a, 0xe7, 0xa6, 0xc6, 0x1b, 0x1b, 0xd9, 0x3e, 0x98, 0x57, 0x0f, + // 0xdf, 0x12, 0x6f, 0x49, 0x47, 0x6b, 0xb6, 0x0c, 0x00, 0x76, 0xd3, 0x70, 0xa6, 0x0d, 0x0f, 0x41, + // 0xc7, 0x55, 0x98, 0x12, 0xfa, 0x86, 0x75, 0x0e, 0xbc, 0x44, 0xe8, 0x24, 0xcf, 0x0f, 0x18, 0xf9, + // 0xb4, 0x18, 0xae, 0x0e, 0x57, 0x0b, 0x16, 0xc9, 0xa2, 0x6a, 0xd7, 0xb4, 0x81, 0x7f, 0x40, 0xef, + // 0x87, 0xd8, 0xcb, 0xc7, 0x53, 0x6c, 0xd9, 0xe5, 0x03, 0x72, 0x2d, 0xdb, 0xb4, 0x25, 0x61, 0xce, + // 0x43, 0xbb, 0x2a, 0x72, 0x27, 0xaf, 0x06, 0x71, 0x63, 0xa2, 0x74, 0xea, 0x00, 0x1d, 0xa7, 0xd7, + // 0x1d, 0x42, 0x3d, 0x6e, 0xcf, 0x49, 0x5f, 0x2b, 0x32, 0x2f, 0x31, 0xd7, 0x8d, 0xc6, 0x20, 0x70, + // 0x98, 0xb6, 0x8c, 0xc7, 0x6f, 0x72, 0x57, 0xd2, 0x35, 0x75, 0x5d, 0x43, 0x76, 0x05, 0x17, 0x78, + // 0xd7, 0xa1, 0xc4, 0x2d, 0x36, 0x28 }; + // key = new byte[] {0x43, (byte)0xb0, (byte)0xbe, (byte)0xc7, (byte)0xce, 0x46, 0x75, + // (byte)0x9d, (byte)0x88, (byte)0xee, (byte)0xb1, 0x04, 0x00, 0x2e, (byte)0xd6, + // 0x57, (byte)0x98, 0x75, (byte)0xf5, (byte)0xca, 0x23, (byte)0xb5, 0x56, (byte)0xb0, + // (byte)0x83, (byte)0xd6, (byte)0xef, (byte)0xcc, 0x20, (byte)0xfb, + // 0x1f, (byte)0xc0, 0x63, (byte)0xe2, (byte)0xc8, (byte)0xeb, 0x0a, (byte)0x90, (byte)0xc2, + // (byte)0xe4, 0x6b, 0x1a, (byte)0x8d, (byte)0xf5, 0x3c, (byte)0xc6, + // 0x68, (byte)0x88, (byte)0x8e, (byte)0xec, 0x20, 0x4a, (byte)0x89, 0x14,(byte) 0xd1, 0x1f, + // 0x29, 0x41, (byte)0xf6, (byte)0x86, 0x32, 0x59, (byte)0xda, + // (byte)0xcc, (byte)0xa4, (byte)0x87, 0x1f, (byte)0xa2, (byte)0xff, (byte)0xc6, (byte)0xf6, + // (byte)0xa5, (byte)0x9f, 0x50, 0x6b, (byte)0xa6, (byte)0xf8, + // (byte)0xbb, 0x46, 0x3d, (byte)0xa0, 0x1d, (byte)0xc1, 0x47,(byte) 0xc5, 0x4f,(byte) 0xe0, + // 0x35,(byte) 0xe6, (byte)0xdd, 0x43, (byte)0x80, (byte)0xc9, + // (byte)0xe8, (byte)0xc8, 0x0e, 0x7f, (byte)0xde,(byte) 0xd0, 0x33, 0x33, (byte)0x8d, 0x0b, + // 0x0b, (byte)0xcc, 0x7d, 0x74, (byte)0xf5, 0x7b, 0x12, (byte)0xbd, + // (byte)0x16, (byte)0x93, (byte)0xf1, 0x01, (byte)0xca, (byte)0x8b, 0x38, 0(byte)xa0, 0xfc, + // 0x0e, 0x1d, 0x24, 0xac, 0x4d, 0xc3, 0xbe, 0x6a, 0xe7, 0xa6, 0xc6, 0x1b, 0x1b, 0xd9, 0x3e, 0x98, + // 0x57, 0x0f, 0xdf, 0x12, 0x6f, 0x49, 0x47, 0x6b, 0xb6, 0x0c, 0x00, 0x76, 0xd3, 0x70, 0xa6, 0x0d, + // 0x0f, 0x41, 0xc7, 0x55, 0x98, 0x12, 0xfa, 0x86, 0x75, 0x0e, 0xbc, 0x44, 0xe8, 0x24, 0xcf, 0x0f, + // 0x18, 0xf9, 0xb4, 0x18, 0xae, 0x0e, 0x57, 0x0b, 0x16, 0xc9, 0xa2, 0x6a, 0xd7, 0xb4, 0x81, 0x7f, + // 0x40, 0xef, 0x87, 0xd8, 0xcb, 0xc7, 0x53, 0x6c, 0xd9, 0xe5, 0x03, 0x72, 0x2d, 0xdb, 0xb4, 0x25, + // 0x61, 0xce, 0x43, 0xbb, 0x2a, 0x72, 0x27, 0xaf, 0x06, 0x71, 0x63, 0xa2, 0x74, 0xea, 0x00, 0x1d, + // 0xa7, 0xd7, 0x1d, 0x42, 0x3d, 0x6e, 0xcf, 0x49, 0x5f, 0x2b, 0x32, 0x2f, 0x31, 0xd7, 0x8d, 0xc6, + // 0x20, 0x70, 0x98, 0xb6, 0x8c, 0xc7, 0x6f, 0x72, 0x57, 0xd2, 0x35, 0x75, 0x5d, 0x43, 0x76, 0x05, + // 0x17, 0x78, 0xd7, 0xa1, 0xc4, 0x2d, 0x36, 0x28}; + // int Last = 0; + //// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + //// ORIGINAL LINE: byte[] res = new byte[buf.Length]; + // byte[] res = new byte[buf.length]; + // for (int i = 0; i < buf.length; i++) + // { + //// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + //// ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + key[i % key.Length])); + // res[i] = (byte)(((buf[i] & 0xFF) ^ (Last + (key[i % key.length] & 0xFF))) & 0xFF); + // Last = (res[i] & 0xFF); + // } + // return res; + // } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] Decode(byte[] buf, byte[] key) + public static byte[] Decode(byte[] buf, byte[] key) { + // var decoded = new byte[] + int Last = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[buf.Length]; + byte[] res = new byte[buf.length]; + for (int i = 0; i < buf.length; i++) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + key[i % key.Length])); + res[i] = (byte) (((buf[i] & 0xFF) ^ (Last + (key[i % key.length] & 0xFF))) & 0xFF); + Last = (buf[i] & 0xFF); + } + return res; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: static byte[] _HardId = new byte[16]; + private static byte[] _HardId = new byte[16]; + + private static void CreateHardId() { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: uint seed = (uint)DateTime.Now.Ticks; + int seed = (int) (java.time.LocalDateTime.now().getNano() & 0xFFFFFFFFL); // 522441350;// + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] data = new byte[256]; + byte[] data = new byte[256]; + for (int i = 0; i < 256; i++) { + seed = seed * 214013 + 2531011; + // C# TO JAVA CONVERTER WARNING: The right shift operator was not replaced by Java's logical + // right shift operator since the left operand was not confirmed to be of an unsigned type, + // but you should review whether the logical right shift operator (>>>) is more appropriate: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: data[i] = (byte)((seed >> 16) & 0xFF); + data[i] = (byte) (((seed & 0xFFFFFFFFL) >> 16) & 0xFF); + } + // MD5 md = new MD5(); + _HardId = MD5.computeMD5(data); + _HardId[0] = 0; + for (int i = 1; i < 16; i++) { + _HardId[0] += (_HardId[i] & 0xFF); + } + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] GetHardId() + public static byte[] GetHardId() { + synchronized (_HardId) { + if ((_HardId[0] & 0xFF) == 0 || (_HardId[15] & 0xFF) == 0) { + CreateHardId(); + } + } + return _HardId; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DatHeader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DatHeader.java new file mode 100644 index 00000000000..281b0473a7e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DatHeader.java @@ -0,0 +1,57 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x1AC, CharSet = CharSet.Unicode)]*/ +public class DatHeader extends FromBufReader { + /*[FieldOffset(0)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint Id; + public int Id; + /*[FieldOffset(4)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String Copyright; + /*[FieldOffset(132)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String DataType; + /*[FieldOffset(164)]*/ public long FileTime; + /*[FieldOffset(172)]*/ public int ObjNumber; + /*[FieldOffset(176)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] Md5Key; + public byte[] Md5Key; + /*[FieldOffset(192)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 228)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sC0; + public byte[] sC0; + /*[FieldOffset(420)]*/ public int s1A4; + /*[FieldOffset(424)]*/ public int s1A8; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 428; + DatHeader st = new DatHeader(); + st.Id = (int) BitConverter.ToUInt32(buf.Bytes(4), 0); + st.Copyright = GetString(buf.Bytes(128)); + st.DataType = GetString(buf.Bytes(32)); + st.FileTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ObjNumber = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.Md5Key = new byte[16]; + st.Md5Key = new byte[16]; + for (int i = 0; i < 16; i++) { + st.Md5Key[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sC0 = new byte[228]; + st.sC0 = new byte[228]; + for (int i = 0; i < 228; i++) { + st.sC0[i] = buf.Byte(); + } + st.s1A4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1A8 = BitConverter.ToInt32(buf.Bytes(4), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealInternal.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealInternal.java new file mode 100644 index 00000000000..cedbb0da213 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealInternal.java @@ -0,0 +1,243 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x2A0, CharSet = CharSet.Unicode)]*/ +public class DealInternal extends FromBufReader { + /** Deal ticket */ + /*[FieldOffset(0)]*/ public long TicketNumber; + + /** Text id */ + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Id; + + /** Account login */ + /*[FieldOffset(72)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Login; + public long Login; + + /** History time (as FileTime format) */ + /*[FieldOffset(80)]*/ public long HistoryTime; + + /** Order ticket */ + /*[FieldOffset(88)]*/ public long OrderTicket; + + /*[FieldOffset(96)]*/ public long s60; + + /** Open time */ + /*[FieldOffset(104)]*/ public long OpenTime; + + /*[FieldOffset(112)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s70; + public byte[] s70; + + /** Symbol currency */ + /*[FieldOffset(120)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Symbol; + + /** Deal type */ + /*[FieldOffset(184)]*/ public DealType Type; + + /** Deal direction */ + /*[FieldOffset(188)]*/ public Direction Direction; + + /** Open price */ + /*[FieldOffset(192)]*/ public double OpenPrice; + + /** Price */ + /*[FieldOffset(200)]*/ public double Price; + + /** Stop loss */ + /*[FieldOffset(208)]*/ public double StopLoss; + + /** Take profit */ + /*[FieldOffset(216)]*/ public double TakeProfit; + + /** Volume */ + /*[FieldOffset(224)]*/ public double Lots; + + /** Profit (money digits) */ + /*[FieldOffset(232)]*/ public double Profit; + + /** Profit rate */ + /*[FieldOffset(240)]*/ public double ProfitRate; + + /** Volume rate */ + /*[FieldOffset(248)]*/ public double VolumeRate; + + /** Commission (money digits) */ + /*[FieldOffset(256)]*/ public double Commission; + + /*[FieldOffset(264)]*/ public double s108; + + /** Swap */ + /*[FieldOffset(272)]*/ public double Swap; + + /** Expert id */ + /*[FieldOffset(280)]*/ public long ExpertId; + + /** Position ticket */ + /*[FieldOffset(288)]*/ public long PositionTicket; + + /** Text comment */ + /*[FieldOffset(296)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Comment; + + /** Lots */ + /*[FieldOffset(360)]*/ public double ContractSize; + + /** Significant digits */ + /*[FieldOffset(368)]*/ public int Digits; + + /** Money significant digits */ + /*[FieldOffset(372)]*/ public int MoneyDigits; + + /*[FieldOffset(376)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 132)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s178; + public byte[] s178; + + /** Free profit */ + /*[FieldOffset(508)]*/ public double FreeProfit; + + /*[FieldOffset(516)]*/ public long s204; + /*[FieldOffset(524)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s20C; + public byte[] s20C; + + /** Trail rounder */ + /*[FieldOffset(532)]*/ public double TrailRounder; + + /*[FieldOffset(540)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s21C; + public byte[] s21C; + + /** Open time (ms) */ + /*[FieldOffset(548)]*/ public long OpenTimeMs; + + /*[FieldOffset(556)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s22C; + public byte[] s22C; + /*[FieldOffset(564)]*/ public long s234; + /*[FieldOffset(572)]*/ public long s23C; + /*[FieldOffset(580)]*/ public long s244; + /*[FieldOffset(588)]*/ public long s24C; + /*[FieldOffset(596)]*/ public long s254; + + /** Placed type */ + /*[FieldOffset(604)]*/ public PlacedType PlacedType; + + /*[FieldOffset(608)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s260; + public byte[] s260; + + /** Volume */ + public long Volume; + + public int PlacedTypeInt; + public int TypeInt; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 672; + DealInternal st = new DealInternal(); + st.TicketNumber = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Id = GetString(buf.Bytes(64)); + st.Login = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.HistoryTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.OrderTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s60 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.OpenTime = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s70 = new byte[8]; + st.s70 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s70[i] = buf.Byte(); + } + st.Symbol = GetString(buf.Bytes(64)); + st.TypeInt = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Type = DealType.forValue(st.TypeInt); + st.Direction = Direction.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.OpenPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Price = BitConverter.ToDouble(buf.Bytes(8), 0); + st.StopLoss = BitConverter.ToDouble(buf.Bytes(8), 0); + st.TakeProfit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Volume = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.Lots = BigDecimal.valueOf(st.Volume).divide(BigDecimal.valueOf(100000000)).doubleValue(); + st.Profit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.ProfitRate = BitConverter.ToDouble(buf.Bytes(8), 0); + st.VolumeRate = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Commission = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s108 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Swap = BitConverter.ToDouble(buf.Bytes(8), 0); + st.ExpertId = BitConverter.ToInt64(buf.Bytes(8), 0); + st.PositionTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Comment = GetString(buf.Bytes(64)); + st.ContractSize = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Digits = BitConverter.ToInt32(buf.Bytes(4), 0); + st.MoneyDigits = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s178 = new byte[132]; + st.s178 = new byte[132]; + for (int i = 0; i < 132; i++) { + st.s178[i] = buf.Byte(); + } + st.FreeProfit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s204 = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s20C = new byte[8]; + st.s20C = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s20C[i] = buf.Byte(); + } + st.TrailRounder = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s21C = new byte[8]; + st.s21C = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s21C[i] = buf.Byte(); + } + st.OpenTimeMs = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s22C = new byte[8]; + st.s22C = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s22C[i] = buf.Byte(); + } + st.s234 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s23C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s244 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s24C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s254 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.PlacedTypeInt = BitConverter.ToInt32(buf.Bytes(4), 0); + st.PlacedType = PlacedType.forValue(st.PlacedTypeInt); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s260 = new byte[64]; + st.s260 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s260[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } + + public LocalDateTime OpenTimeAsDateTime() { + return ConvertTo.DateTime(OpenTime); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealType.java new file mode 100644 index 00000000000..2f75c554382 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealType.java @@ -0,0 +1,51 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Deal type */ +public enum DealType { + DealBuy(0), + DealSell(1), + Balance(2), + Credit(3), + Charge(4), + Correction(5), + Bonus(6), + Commission(7), + DailyCommission(8), + MonthlyCommission(9), + DailyAgentCommission(10), + MonthlyAgentCommission(11), + InterestRate(12), + CanceledBuy(13), + CanceledSell(14), + Dividend(15), + Tax(17); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (DealType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private DealType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static DealType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealsResult.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealsResult.java new file mode 100644 index 00000000000..f189f67bf0f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DealsResult.java @@ -0,0 +1,28 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x18B, CharSet = CharSet.Unicode)]*/ +public class DealsResult extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 395)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s0; + public byte[] s0; + private int Size = 396; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + Size; + DealsResult st = new DealsResult(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s0 = new byte[Size]; + st.s0 = new byte[Size]; + for (int i = 0; i < Size; i++) { + st.s0[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decoder.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decoder.java new file mode 100644 index 00000000000..02a1df71427 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decoder.java @@ -0,0 +1,51 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class Decoder { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte Last = 0; + private byte Last = 0; + private int KeyInd = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte[] Key; + private byte[] Key; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public Decoder(byte[] key) + public Decoder(byte[] key) { + Key = key; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void ChangeKey(byte[] key) + public final void ChangeKey(byte[] key) { + Key = key; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] GetKey() + public final byte[] GetKey() { + return Key; + } + + public final void Reset() { + Last = 0; + KeyInd = 0; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] Decode(byte[] buf) + public final byte[] Decode(byte[] buf) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[buf.Length]; + byte[] res = new byte[buf.length]; + for (int i = 0; i < buf.length; i++) { + KeyInd &= 0xF; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + Key[KeyInd])); + res[i] = (byte) (((buf[i] & 0xFF) ^ ((Last & 0xFF) + (Key[KeyInd] & 0xFF))) & 0xFF); + KeyInd++; + Last = res[i]; + } + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decompressor.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decompressor.java new file mode 100644 index 00000000000..a4e369a9245 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Decompressor.java @@ -0,0 +1,319 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import static io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.String8.dataDistance; +import static io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.String8.nnc; + +class Decompressor { + + static byte[] decompress(byte[] src, int maxSize) { + int dLen = maxSize; + byte[] data; + byte[] dst = new byte[dLen + 0x800]; + String8 s = String8.fromData(src); + String8 d = String8.fromData(dst); + int[] dLenPointer = new int[] {dLen}; + int res = new Decompressor().inflate(s, src.length, d, dLenPointer); + byte[] buf = new byte[dLenPointer[0]]; + for (int i = 0; i < dLenPointer[0]; i++) buf[i] = d.get(i); + return buf; + } + + public static final int M2_MAX_OFFSET = 0x0800; + + private static long pd_U(String8 a_U, String8 b_U) { + return dataDistance(b_U, a_U); + } + + private static void memcpyDs(String8[] dest_U, String8[] src_U, long[] len_U) { + do { + (dest_U[0] = String8.nnc(dest_U[0]).shift(1)) + .set(-1, (src_U[0] = nnc(src_U[0]).shift(1)).get(-1)); + } while (Long.compareUnsigned(len_U[0] = len_U[0] - 1, 0) > 0); + } + + static int inflate(String8 pSrc_U, int szSrc_U, String8 pDst_U, int[] pszDst_U) { + // if (!pSrc || (szSrc < 3) || !pszDst || !pDst || (*pszDst < 1)) + // return(0); + long[] t_U = null; + String8[] ip_U = null; + String8[] op_U = null; + String8 ipEnd_U = null; + String8 opEnd_U = null; + String8[] mPos_U = null; + + final int posFirstLiteralRun = 1, + posMatch = 2, + posCopyMatch = 3, + posMatchDone = 4, + posMatchNext = 5, + posEofFound = 6, + posInputOverrun = 7, + posOutputOverrun = 8, + posLookbehindOverrun = 9; + positionLoop: + for (int pos = 0; true; ) + switch (pos) { + default: + t_U = new long[1]; + ip_U = new String8[] {pSrc_U}; + op_U = new String8[] {pDst_U}; + ipEnd_U = nnc(pSrc_U).shift(szSrc_U); + opEnd_U = nnc(pDst_U).shift(pszDst_U[0]); + mPos_U = new String8[1]; + pszDst_U[0] = 0; + if (ipEnd_U.get(-1) != 0 + || ipEnd_U.get(-2) != 0 + || Byte.toUnsignedInt(ipEnd_U.get(-3)) != 17) { + return 0; + } + if (Byte.toUnsignedInt(ip_U[0].get()) > 17) { + t_U[0] = Byte.toUnsignedInt((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)) - 17; + if (Long.compareUnsigned(t_U[0], 4) < 0) { + pos = posMatchNext; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), t_U[0]) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), t_U[0] + 1) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + memcpyDs(op_U, ip_U, t_U); + pos = posFirstLiteralRun; + continue positionLoop; + } + case posFirstLiteralRun: + case posMatch: + case posCopyMatch: + case posMatchDone: + case posMatchNext: + forLoop: + for (; ; pos = 0) + switch (pos) { + default: + // if(dataAddress(ip_U[0]) >= dataAddress(ipEnd_U)) + if (dataDistance(ip_U[0], ipEnd_U) <= 0) { + break forLoop; + } + t_U[0] = Byte.toUnsignedLong((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + if (Long.compareUnsigned(t_U[0], 16) >= 0) { + pos = posMatch; + continue positionLoop; + } + if (t_U[0] == 0) { + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + while (Byte.toUnsignedInt(ip_U[0].get()) == 0) { + t_U[0] = t_U[0] + 255; + ip_U[0] = nnc(ip_U[0]).shift(1); + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + } + t_U[0] = + t_U[0] + (15 + Byte.toUnsignedInt((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1))); + } + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), t_U[0] + 3) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), t_U[0] + 4) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + memcpyDs(op_U, ip_U, t_U); + case posFirstLiteralRun: + t_U[0] = Byte.toUnsignedLong((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + if (Long.compareUnsigned(t_U[0], 16) >= 0) { + pos = posMatch; + continue positionLoop; + } + mPos_U[0] = nnc(op_U[0]).shift(-(1 + M2_MAX_OFFSET)); + mPos_U[0] = nnc(mPos_U[0]).shift((int) -(t_U[0] >>> 2)); + mPos_U[0] = + nnc(mPos_U[0]) + .shift( + -(Byte.toUnsignedInt((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)) << 2)); + // if(dataAddress(mPos_U[0]) < dataAddress(pDst_U)) { + if (dataDistance(mPos_U[0], pDst_U) > 0) { + pos = posLookbehindOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), (long) 3) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + pos = posMatchDone; + continue positionLoop; + case posMatch: + case posCopyMatch: + case posMatchDone: + case posMatchNext: + forLoop2: + for (; ; pos = 0) + switch (pos) { + default: + if (Long.compareUnsigned(t_U[0], 64) >= 0) { + mPos_U[0] = nnc(op_U[0]).shift(-1); + mPos_U[0] = nnc(mPos_U[0]).shift((int) -(t_U[0] >>> 2 & 7)); + mPos_U[0] = + nnc(mPos_U[0]) + .shift( + -(Byte.toUnsignedInt((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)) + << 3)); + t_U[0] = (t_U[0] >>> 5) - 1; + pos = posCopyMatch; + continue positionLoop; + } else if (Long.compareUnsigned(t_U[0], 32) >= 0) { + t_U[0] = t_U[0] & 31; + if (t_U[0] == 0) { + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + while (Byte.toUnsignedInt(ip_U[0].get()) == 0) { + t_U[0] = t_U[0] + 255; + ip_U[0] = nnc(ip_U[0]).shift(1); + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) + < 0) { + pos = posInputOverrun; + continue positionLoop; + } + } + t_U[0] = + t_U[0] + + (31 + + Byte.toUnsignedInt( + (ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1))); + } + mPos_U[0] = nnc(op_U[0]).shift(-1); + mPos_U[0] = + nnc(mPos_U[0]) + .shift( + -((Byte.toUnsignedInt(ip_U[0].get(0)) >> 2) + + (Byte.toUnsignedInt(ip_U[0].get(1)) << 6))); + ip_U[0] = nnc(ip_U[0]).shift(2); + } else if (Long.compareUnsigned(t_U[0], 16) >= 0) { + mPos_U[0] = op_U[0]; + mPos_U[0] = nnc(mPos_U[0]).shift((int) -((t_U[0] & 8) << 11)); + t_U[0] = t_U[0] & 7; + if (t_U[0] == 0) { + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + while (Byte.toUnsignedInt(ip_U[0].get()) == 0) { + t_U[0] = t_U[0] + 255; + ip_U[0] = nnc(ip_U[0]).shift(1); + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), (long) 0) + < 0) { + pos = posInputOverrun; + continue positionLoop; + } + } + t_U[0] = + t_U[0] + + (7 + + Byte.toUnsignedInt( + (ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1))); + } + mPos_U[0] = + nnc(mPos_U[0]) + .shift( + -((Byte.toUnsignedInt(ip_U[0].get(0)) >> 2) + + (Byte.toUnsignedInt(ip_U[0].get(1)) << 6))); + ip_U[0] = nnc(ip_U[0]).shift(2); + // if(dataAddress(mPos_U[0]) == dataAddress(op_U[0])) { + if (dataDistance(mPos_U[0], op_U[0]) == 0) { + pos = posEofFound; + continue positionLoop; + } + mPos_U[0] = nnc(mPos_U[0]).shift(-0x4000); + } else { + mPos_U[0] = nnc(op_U[0]).shift(-1); + mPos_U[0] = nnc(mPos_U[0]).shift((int) -(t_U[0] >>> 2)); + mPos_U[0] = + nnc(mPos_U[0]) + .shift( + -(Byte.toUnsignedInt((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)) + << 2)); + // if(dataAddress(mPos_U[0]) < dataAddress(pDst_U)) { + if (dataDistance(mPos_U[0], pDst_U) > 0) { + pos = posLookbehindOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), (long) 2) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + pos = posMatchDone; + continue positionLoop; + } + case posCopyMatch: + // if(dataAddress(mPos_U[0]) < dataAddress(pDst_U)) { + if (dataDistance(mPos_U[0], pDst_U) > 0) { + pos = posLookbehindOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), t_U[0] + 2) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + (op_U[0] = nnc(op_U[0]).shift(1)) + .set(-1, (mPos_U[0] = nnc(mPos_U[0]).shift(1)).get(-1)); + memcpyDs(op_U, mPos_U, t_U); + case posMatchDone: + t_U[0] = Byte.toUnsignedInt(ip_U[0].get(-2)) & 3; + if (t_U[0] == 0) { + break forLoop2; + } + case posMatchNext: + if (Long.compareUnsigned(dataDistance(op_U[0], opEnd_U), t_U[0]) < 0) { + pos = posOutputOverrun; + continue positionLoop; + } + if (Long.compareUnsigned(dataDistance(ip_U[0], ipEnd_U), t_U[0] + 1) < 0) { + pos = posInputOverrun; + continue positionLoop; + } + memcpyDs(op_U, ip_U, t_U); + t_U[0] = Byte.toUnsignedLong((ip_U[0] = nnc(ip_U[0]).shift(1)).get(-1)); + // if(dataAddress(ip_U[0]) >= dataAddress(ipEnd_U)) + if (dataDistance(ip_U[0], ipEnd_U) <= 0) { + break forLoop2; + } + } + } + case posEofFound: + pszDst_U[0] = (int) pd_U(op_U[0], pDst_U); + return dataDistance(ip_U[0], ipEnd_U) == 0 ? 1 : 0; + case posInputOverrun: + case posOutputOverrun: + case posLookbehindOverrun: + return 0; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Direction.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Direction.java new file mode 100644 index 00000000000..c824c58fac7 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Direction.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum Direction { + In(0), + Out(1), + InOut(2), + OutBy(3); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (Direction.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private Direction(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static Direction forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DotNetToJavaStringHelper.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DotNetToJavaStringHelper.java new file mode 100644 index 00000000000..24207527e93 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/DotNetToJavaStringHelper.java @@ -0,0 +1,295 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// ---------------------------------------------------------------------------------------- +// Copyright © 2007 - 2017 Tangible Software Solutions Inc. +// This class can be used by anyone provided that the copyright notice remains intact. +// +// This class is used to simulate some .NET string methods in Java. +// ---------------------------------------------------------------------------------------- +final class DotNetToJavaStringHelper { + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'Substring' when 'start' is a method + // call or calculated value to ensure that 'start' is obtained just once. + // ------------------------------------------------------------------------------------ + public static String substring(String string, int start, int length) { + if (length < 0) throw new IndexOutOfBoundsException("Parameter length cannot be negative."); + + return string.substring(start, start + length); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET static string method 'IsNullOrEmpty'. + // ------------------------------------------------------------------------------------ + public static boolean isNullOrEmpty(String string) { + return string == null || string.length() == 0; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET static string method 'IsNullOrWhiteSpace'. + // ------------------------------------------------------------------------------------ + public static boolean isNullOrWhiteSpace(String string) { + if (string == null) return true; + + for (int index = 0; index < string.length(); index++) { + if (!Character.isWhitespace(string.charAt(index))) return false; + } + + return true; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET static string method 'Join' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static String join(String separator, String[] stringArray) { + if (stringArray == null) return null; + else return join(separator, stringArray, 0, stringArray.length); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET static string method 'Join' (4 parameter version). + // ------------------------------------------------------------------------------------ + public static String join(String separator, String[] stringArray, int startIndex, int count) { + String result = ""; + + if (stringArray == null) return null; + + for (int index = startIndex; + index < stringArray.length && index - startIndex < count; + index++) { + if (separator != null && index > startIndex) result += separator; + + if (stringArray[index] != null) result += stringArray[index]; + } + + return result; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'Remove' (1 parameter version). + // ------------------------------------------------------------------------------------ + public static String remove(String string, int start) { + return string.substring(0, start); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'Remove' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static String remove(String string, int start, int count) { + return string.substring(0, start) + string.substring(start + count); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'TrimEnd'. + // ------------------------------------------------------------------------------------ + public static String trimEnd(String string, Character... charsToTrim) { + if (string == null || charsToTrim == null) return string; + + int lengthToKeep = string.length(); + for (int index = string.length() - 1; index >= 0; index--) { + boolean removeChar = false; + if (charsToTrim.length == 0) { + if (Character.isWhitespace(string.charAt(index))) { + lengthToKeep = index; + removeChar = true; + } + } else { + for (int trimCharIndex = 0; trimCharIndex < charsToTrim.length; trimCharIndex++) { + if (string.charAt(index) == charsToTrim[trimCharIndex]) { + lengthToKeep = index; + removeChar = true; + break; + } + } + } + if (!removeChar) break; + } + return string.substring(0, lengthToKeep); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'TrimStart'. + // ------------------------------------------------------------------------------------ + public static String trimStart(String string, Character... charsToTrim) { + if (string == null || charsToTrim == null) return string; + + int startingIndex = 0; + for (int index = 0; index < string.length(); index++) { + boolean removeChar = false; + if (charsToTrim.length == 0) { + if (Character.isWhitespace(string.charAt(index))) { + startingIndex = index + 1; + removeChar = true; + } + } else { + for (int trimCharIndex = 0; trimCharIndex < charsToTrim.length; trimCharIndex++) { + if (string.charAt(index) == charsToTrim[trimCharIndex]) { + startingIndex = index + 1; + removeChar = true; + break; + } + } + } + if (!removeChar) break; + } + return string.substring(startingIndex); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'Trim' when arguments are used. + // ------------------------------------------------------------------------------------ + public static String trim(String string, Character... charsToTrim) { + return trimEnd(trimStart(string, charsToTrim), charsToTrim); + } + + // ------------------------------------------------------------------------------------ + // This method is used for string equality comparisons when the option + // 'Use helper 'stringsEqual' method to handle null strings' is selected + // (The Java String 'equals' method can't be called on a null instance). + // ------------------------------------------------------------------------------------ + public static boolean stringsEqual(String s1, String s2) { + if (s1 == null && s2 == null) return true; + else return s1 != null && s1.equals(s2); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'PadRight' (1 parameter version). + // ------------------------------------------------------------------------------------ + public static String padRight(String string, int totalWidth) { + return padRight(string, totalWidth, ' '); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'PadRight' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static String padRight(String string, int totalWidth, char paddingChar) { + StringBuilder sb = new StringBuilder(string); + + while (sb.length() < totalWidth) { + sb.append(paddingChar); + } + + return sb.toString(); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'PadLeft' (1 parameter version). + // ------------------------------------------------------------------------------------ + public static String padLeft(String string, int totalWidth) { + return padLeft(string, totalWidth, ' '); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'PadLeft' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static String padLeft(String string, int totalWidth, char paddingChar) { + StringBuilder sb = new StringBuilder(""); + + while (sb.length() + string.length() < totalWidth) { + sb.append(paddingChar); + } + + sb.append(string); + return sb.toString(); + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'LastIndexOf' (char version). + // ------------------------------------------------------------------------------------ + public static int lastIndexOf(String string, char value, int startIndex, int count) { + int leftMost = startIndex + 1 - count; + int rightMost = startIndex + 1; + String substring = string.substring(leftMost, rightMost); + int lastIndexInSubstring = substring.lastIndexOf(value); + if (lastIndexInSubstring < 0) return -1; + else return lastIndexInSubstring + leftMost; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'LastIndexOf' (string version). + // ------------------------------------------------------------------------------------ + public static int lastIndexOf(String string, String value, int startIndex, int count) { + int leftMost = startIndex + 1 - count; + int rightMost = startIndex + 1; + String substring = string.substring(leftMost, rightMost); + int lastIndexInSubstring = substring.lastIndexOf(value); + if (lastIndexInSubstring < 0) return -1; + else return lastIndexInSubstring + leftMost; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'IndexOfAny' (1 parameter version). + // ------------------------------------------------------------------------------------ + public static int indexOfAny(String string, char[] anyOf) { + int lowestIndex = -1; + for (char c : anyOf) { + int index = string.indexOf(c); + if (index > -1) { + if (lowestIndex == -1 || index < lowestIndex) { + lowestIndex = index; + + if (index == 0) break; + } + } + } + + return lowestIndex; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'IndexOfAny' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static int indexOfAny(String string, char[] anyOf, int startIndex) { + int indexInSubstring = indexOfAny(string.substring(startIndex), anyOf); + if (indexInSubstring == -1) return -1; + else return indexInSubstring + startIndex; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'IndexOfAny' (3 parameter version). + // ------------------------------------------------------------------------------------ + public static int indexOfAny(String string, char[] anyOf, int startIndex, int count) { + int endIndex = startIndex + count; + int indexInSubstring = indexOfAny(string.substring(startIndex, endIndex), anyOf); + if (indexInSubstring == -1) return -1; + else return indexInSubstring + startIndex; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'LastIndexOfAny' (1 parameter version). + // ------------------------------------------------------------------------------------ + public static int lastIndexOfAny(String string, char[] anyOf) { + int highestIndex = -1; + for (char c : anyOf) { + int index = string.lastIndexOf(c); + if (index > highestIndex) { + highestIndex = index; + + if (index == string.length() - 1) break; + } + } + + return highestIndex; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'LastIndexOfAny' (2 parameter version). + // ------------------------------------------------------------------------------------ + public static int lastIndexOfAny(String string, char[] anyOf, int startIndex) { + String substring = string.substring(0, startIndex + 1); + int lastIndexInSubstring = lastIndexOfAny(substring, anyOf); + if (lastIndexInSubstring < 0) return -1; + else return lastIndexInSubstring; + } + + // ------------------------------------------------------------------------------------ + // This method replaces the .NET string method 'LastIndexOfAny' (3 parameter version). + // ------------------------------------------------------------------------------------ + public static int lastIndexOfAny(String string, char[] anyOf, int startIndex, int count) { + int leftMost = startIndex + 1 - count; + int rightMost = startIndex + 1; + String substring = string.substring(leftMost, rightMost); + int lastIndexInSubstring = lastIndexOfAny(substring, anyOf); + if (lastIndexInSubstring < 0) return -1; + else return lastIndexInSubstring + leftMost; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Encoder.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Encoder.java new file mode 100644 index 00000000000..dacfdd9622c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Encoder.java @@ -0,0 +1,51 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class Encoder { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte Last = 0; + private byte Last = 0; + private int KeyInd = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte[] Key; + private byte[] Key; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public Encoder(byte[] key) + public Encoder(byte[] key) { + Key = key; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void ChangeKey(byte[] key) + public final void ChangeKey(byte[] key) { + Key = key; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] GetKey() + public final byte[] GetKey() { + return Key; + } + + public final void Reset() { + Last = 0; + KeyInd = 0; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] Encode(byte[] buf) + public final byte[] Encode(byte[] buf) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[buf.Length]; + byte[] res = new byte[buf.length]; + for (int i = 0; i < buf.length; i++) { + KeyInd &= 0xF; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(buf[i] ^ (Last + Key[KeyInd])); + res[i] = (byte) (((buf[i] & 0xFF) ^ ((Last & 0xFF) + (Key[KeyInd] & 0xFF))) & 0xFF); + KeyInd++; + Last = buf[i]; + } + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ErrorDescription.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ErrorDescription.java new file mode 100644 index 00000000000..83ebd65deac --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ErrorDescription.java @@ -0,0 +1,94 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; + +public class ErrorDescription { + private static HashMap keys; + private static HashMap strings; + + static { + keys = new HashMap(); + keys.put(0x0, 0x572608); + keys.put(0x1, 0x572604); + keys.put(0x2, 0x5725F4); + keys.put(0x3, 0x5725E0); + keys.put(0x4, 0x5725D0); + keys.put(0x5, 0x5725C4); + keys.put(0x6, 0x5725B4); + keys.put(0x7, 0x5725A0); + keys.put(0x8, 0x572588); + keys.put(0xD, 0x572580); + keys.put(0xE, 0x57257C); + keys.put(0x40, 0x572574); + keys.put(0x41, 0x572564); + keys.put(0x80, 0x572554); + keys.put(0x81, 0x572544); + keys.put(0x82, 0x572530); + keys.put(0x83, 0x572520); + keys.put(0x84, 0x57250C); + keys.put(0x85, 0x5724F8); + keys.put(0x86, 0x5724E4); + keys.put(0x87, 0x5724D0); + keys.put(0x88, 0x5724C4); + keys.put(0x89, 0x5724B4); + keys.put(0x8A, 0x5724AC); + keys.put(0x8B, 0x57249C); + keys.put(0x8C, 0x57247C); + keys.put(0x8D, 0x572468); + keys.put(0x8E, 0x572454); + keys.put(0x8F, 0x572440); + keys.put(0x90, 0x572424); + keys.put(0x91, 0x5723DC); + keys.put(0x92, 0x57240C); + keys.put(0x93, 0x5723B0); + keys.put(0x94, 0x5723A0); + keys.put(0x95, 0x572390); + keys.put(0x96, 0x572380); + strings = new HashMap(); + strings.put(0x00572380, "Prohibited by FIFO rule"); + strings.put(0x00572390, "Hedge is prohibited"); + strings.put(0x005723A0, "Too many open orders"); + strings.put(0x005723B0, "Expiration for pending orders is disabled"); + strings.put(0x0057240C, "Trade context is busy"); + strings.put(0x005723DC, "Modification denied. Order too close to market"); + strings.put(0x00572424, "Request canceled by client"); + strings.put(0x00572440, "Order is in process"); + strings.put(0x00572454, "Order is accepted"); + strings.put(0x00572468, "Too many requests"); + strings.put(0x0057247C, "Only long positions are allowed"); + strings.put(0x0057249C, "Order is locked"); + strings.put(0x005724AC, "Requote"); + strings.put(0x005724B4, "Broker is busy"); + strings.put(0x005724C4, "Off quotes"); + strings.put(0x005724D0, "Price is changed"); + strings.put(0x005724E4, "Not enough money"); + strings.put(0x005724F8, "Trade is disabled"); + strings.put(0x0057250C, "Market is closed"); + strings.put(0x00572520, "Invalid volume"); + strings.put(0x00572530, "Invalid S/L or T/P"); + strings.put(0x00572544, "Invalid prices"); + strings.put(0x00572554, "Trade timeout"); + strings.put(0x00572564, "Invalid account"); + strings.put(0x00572574, "Account disabled"); + strings.put(0x0057257C, "Invalid one-time password"); + strings.put(0x00572580, "Secret key for one-time password is required"); + strings.put(0x00572588, "Too frequent requests"); + strings.put(0x005725A0, "Not enough rights"); + strings.put(0x005725B4, "No connection"); + strings.put(0x005725C4, "Old version"); + strings.put(0x005725D0, "Server is busy"); + strings.put(0x005725E0, "Invalid parameters"); + strings.put(0x005725F4, "Common error"); + strings.put(0x00572604, "OK"); + strings.put(0x00572608, "Done"); + } + + public static String get(int code) { + try { + int key = keys.get(code); + return strings.get(key); + } catch (RuntimeException e) { + return "Unknown server reply " + Integer.toHexString(code).toUpperCase(); + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Event.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Event.java new file mode 100644 index 00000000000..8020b0f2663 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Event.java @@ -0,0 +1,140 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public final class Event { + public java.util.List listeners = new java.util.ArrayList(); + + public void addListener(T unnamedEventHandlerMethod) { + listeners.add(unnamedEventHandlerMethod); + } + + public java.util.List listeners() { + java.util.List allListeners = new java.util.ArrayList(); + allListeners.addAll(listeners); + return allListeners; + } + + public void Invoke(MT5API api, ConnectEventArgs args) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (Object obj : listeners) { + OnConnectProgress event = (OnConnectProgress) obj; + event.invoke(api, args); + } + } catch (RuntimeException ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } + + public void Invoke(MT5API api, Quote args) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (Object obj : listeners) { + SymbolInfo info = api.Symbols.GetInfo(args.Symbol); + args.Spread = (int) Math.round((args.Ask - args.Bid) / info.Points); + OnQuote event = (OnQuote) obj; + event.invoke(api, args); + } + } catch (RuntimeException ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } + + public void Invoke(MT5API api, OrderHistoryEventArgs args) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (Object obj : listeners) { + OnTradeHistory event = (OnTradeHistory) obj; + event.invoke(api, args); + } + } catch (Exception ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } + + public void Invoke(MT5API api, OrderProgress[] progr) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (OrderProgress item : progr) { + for (Object obj : listeners) { + OnOrderProgress event = (OnOrderProgress) obj; + event.invoke(api, item); + } + } + } catch (RuntimeException ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } + + void processEvent(MT5API api, Runnable func) { + if (api.ProcessEventsIn == ProcessEvents.ThreadPool) ThreadPool.QueueUserWorkItem(func); + else if (api.ProcessEventsIn == ProcessEvents.NewThread) new Thread(func).start(); + else func.run(); + } + + public void Invoke(MT5API api, OrderUpdate[] update) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (OrderUpdate item : update) { + for (Object obj : listeners) { + if (item.Type != UpdateType.Cancelling + && item.Type != UpdateType.Started + && item.Type != UpdateType.Filled + && item.Type != UpdateType.Unknown) { + OnOrderUpdate event = (OnOrderUpdate) obj; + event.invoke(api, item); + } + } + } + } catch (RuntimeException ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } + + public void Invoke(MT5API api, QuoteHistoryEventArgs args) { + Runnable func = + new Runnable() { + @Override + public void run() { + try { + for (Object obj : listeners) { + OnQuoteHistory event = (OnQuoteHistory) obj; + event.invoke(api, args); + } + } catch (RuntimeException ex) { + api.Log.exception(ex); + } + } + }; + processEvent(api, func); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionType.java new file mode 100644 index 00000000000..d13f7505c79 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionType.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ExecutionType { + Request(0), + Instant(1), + Market(2), + Exchange(3); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (ExecutionType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private ExecutionType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static ExecutionType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionWaiters.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionWaiters.java new file mode 100644 index 00000000000..cea94332730 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExecutionWaiters.java @@ -0,0 +1,8 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; + +// public class ExecutionWaiters +// { +// private LinkedList Queue = new Queue(); +// } diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationDate.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationDate.java new file mode 100644 index 00000000000..1fe972ce47c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationDate.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ExpirationDate { + GTC(0), + Today(1), + Specified(2), + SpecifiedDay(3); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (ExpirationDate.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private ExpirationDate(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static ExpirationDate forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationType.java new file mode 100644 index 00000000000..af827424bd7 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ExpirationType.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ExpirationType { + GTC(0), + Today(1), + Specified(2), + SpecifiedDay(3); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (ExpirationType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private ExpirationType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static ExpirationType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FillPolicy.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FillPolicy.java new file mode 100644 index 00000000000..11d6fe9d400 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FillPolicy.java @@ -0,0 +1,36 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum FillPolicy { + FillOrKill(0), + ImmediateOrCancel(1), + FlashFill(2); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (FillPolicy.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private FillPolicy(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static FillPolicy forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FromBufReader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FromBufReader.java new file mode 100644 index 00000000000..05d9270b3ee --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/FromBufReader.java @@ -0,0 +1,11 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public abstract class FromBufReader { + public abstract Object ReadFromBuf(InBuf buf); + + public FromBufReader() {} + + public final String GetString(byte[] buf) { + return UDT.readString(buf, 0, buf.length); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/GTCMode.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/GTCMode.java new file mode 100644 index 00000000000..27d8c076427 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/GTCMode.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum GTCMode // Good till +{ + Cancelled(0), + TodayIncludeSL_TP(1), + TodayExcludeSL_TP(2); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (GTCMode.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private GTCMode(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static GTCMode forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Help.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Help.java new file mode 100644 index 00000000000..7bbda62ee0d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Help.java @@ -0,0 +1,51 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +class Help { + public static void check(byte[] src) { + try { + List lines = + Files.readAllLines(Paths.get("C:\\YandexDisk\\Tim\\java\\mt5api\\ar.txt")); + int ind = 0; + for (String line : lines) { + if (!line.trim().startsWith("[")) continue; + ind = Integer.parseInt(line.substring(line.indexOf("[") + 1, line.indexOf("]"))); + byte val = hexToByte(line.substring(line.indexOf("0x") + 2, line.indexOf("byte"))); + if (ind > src.length - 1) throw new RuntimeException(ind + " big " + (src.length - 1)); + if (src[ind] != val) throw new RuntimeException(ind + " " + src[ind] + " not " + val); + } + if (ind != src.length - 1) throw new RuntimeException(ind + "!=" + (src.length - 1)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static byte hexToByte(String hexString) { + int firstDigit = toDigit(hexString.charAt(0)); + int secondDigit = toDigit(hexString.charAt(1)); + return (byte) ((firstDigit << 4) + secondDigit); + } + + public static int toDigit(char hexChar) { + int digit = Character.digit(hexChar, 16); + if (digit == -1) { + throw new IllegalArgumentException("Invalid Hexadecimal Character: " + hexChar); + } + return digit; + } + + public static void equal(byte[] ar1, byte[] ar2) { + if (ar1.length != ar2.length) { + throw new RuntimeException("len not equal"); + } + for (int i = 0; i < ar1.length; i++) { + if ((ar1[i] & 0xFF) != (ar2[i] & 0xFF)) { + throw new RuntimeException("not equal " + i); + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HistHeader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HistHeader.java new file mode 100644 index 00000000000..e0de5905f14 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HistHeader.java @@ -0,0 +1,61 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x81, CharSet = CharSet.Unicode)]*/ +public class HistHeader extends FromBufReader { + /*[FieldOffset(0)]*/ public short HdrSize; + /*[FieldOffset(2)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Currency; + /*[FieldOffset(66)]*/ public short s42; + /*[FieldOffset(68)]*/ public short Date; + /*[FieldOffset(70)]*/ public short s46; + /*[FieldOffset(72)]*/ public int DataSize; + /*[FieldOffset(76)]*/ public int InflateSize; + /*[FieldOffset(80)]*/ public int BitSize; + /*[FieldOffset(84)]*/ public int NumberBars; + /*[FieldOffset(88)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte AlignBit; + public byte AlignBit; + /*[FieldOffset(89)]*/ public int Time; + /*[FieldOffset(93)]*/ public short Digits; + /*[FieldOffset(95)]*/ public short Flags; + /*[FieldOffset(97)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint LimitPoints; + public int LimitPoints; + /*[FieldOffset(101)]*/ public int Spread; + /*[FieldOffset(105)]*/ public float fSwapLong; + /*[FieldOffset(109)]*/ public float fSwapShort; + /*[FieldOffset(113)]*/ public long s71; + /*[FieldOffset(121)]*/ public long s79; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 129; + HistHeader st = new HistHeader(); + st.HdrSize = BitConverter.ToInt16(buf.Bytes(2), 0); + st.Currency = GetString(buf.Bytes(64)); + st.s42 = BitConverter.ToInt16(buf.Bytes(2), 0); + st.Date = BitConverter.ToInt16(buf.Bytes(2), 0); + st.s46 = BitConverter.ToInt16(buf.Bytes(2), 0); + st.DataSize = BitConverter.ToInt32(buf.Bytes(4), 0); + st.InflateSize = BitConverter.ToInt32(buf.Bytes(4), 0); + st.BitSize = BitConverter.ToInt32(buf.Bytes(4), 0); + st.NumberBars = BitConverter.ToInt32(buf.Bytes(4), 0); + st.AlignBit = buf.Byte(); + st.Time = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Digits = BitConverter.ToInt16(buf.Bytes(2), 0); + st.Flags = BitConverter.ToInt16(buf.Bytes(2), 0); + st.LimitPoints = (int) BitConverter.ToUInt32(buf.Bytes(4), 0); + st.Spread = BitConverter.ToInt32(buf.Bytes(4), 0); + st.fSwapLong = (float) BitConverter.ToInt32(buf.Bytes(4), 0); + st.fSwapShort = (float) BitConverter.ToInt32(buf.Bytes(4), 0); + st.s71 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s79 = BitConverter.ToInt64(buf.Bytes(8), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HostAndPort.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HostAndPort.java new file mode 100644 index 00000000000..e6b8fedfd84 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/HostAndPort.java @@ -0,0 +1,21 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.AbstractMap; +import java.util.Map; + +public class HostAndPort { + public static Map.Entry Parse(String ip) { + int port; + int i = 0; + String host = ""; + while (i < ip.length() && ip.getBytes()[i] != (byte) ':') host += (char) ip.getBytes()[i++]; + if (i == ip.length()) port = 443; + else { + i++; + String strPort = ""; + while (i < ip.length()) strPort += (char) ip.getBytes()[i++]; + port = Integer.parseInt(strPort); + } + return new AbstractMap.SimpleEntry(host.trim(), port); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/InBuf.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/InBuf.java new file mode 100644 index 00000000000..dfa09d528f9 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/InBuf.java @@ -0,0 +1,190 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.lang.reflect.Array; +import java.util.*; + +class InBuf { + short SymBuild; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] Buf; + byte[] Buf; + int Ind; + public PacketHdr Hdr; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public InBuf(byte[] buf, int start) + public InBuf(byte[] buf, int start) { + Buf = buf; + Ind = start; + } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public T Struct() where T : FromBufReader, new() + public final T Struct(T t) { + // var size = Marshal.SizeOf(typeof(T)); + T res = UDT.ReadStruct(this, t); + // Ind += size; + return res; + } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public T CryptStruct(int size) where T : FromBufReader, new() + public final T CryptStruct(int size, T t) { + // var size = Marshal.SizeOf(typeof(T)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var bytes = Bytes(size); + byte[] bytes = Bytes(size); + Crypt.EasyDecrypt(bytes); + return UDT.ReadStruct(bytes, 0, size, t); + } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public T[] CryptArray(int size) where T : FromBufReader, new() + public final T[] CryptArray(int size, T t) { + int num = Int(); + T[] ar = (T[]) Array.newInstance(t.getClass(), num); + for (int i = 0; i < num; i++) { + ar[i] = this.CryptStruct(size, t); + } + return ar; + } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public T[] Array() where T : FromBufReader, new() + public final T[] Array(T t) { + int num = Int(); + T[] ar = (T[]) Array.newInstance(t.getClass(), num); + for (int i = 0; i < num; i++) { + ar[i] = this.Struct(t); + } + return ar; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void SetBuf(byte[] buf) + public final void SetBuf(byte[] buf) { + Buf = buf; + Hdr.PacketSize = buf.length; + Ind = 0; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public InBuf(byte[] buf, PacketHdr hdr) + public InBuf(byte[] buf, PacketHdr hdr) { + Buf = buf; + Hdr = hdr; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] ToBytes() + public final byte[] ToBytes() { + return Buf; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte Byte() + public final byte Byte() { + return Buf[Ind++]; + } + + public final int getLeft() { + return Buf.length - Ind; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] Bytes(int count) + public final byte[] Bytes(int count) { + if (Ind + count > Buf.length) { + throw new RuntimeException("Not enough data"); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var res = new byte[count]; + byte[] res = new byte[count]; + for (int i = 0; i < count; i++) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = Byte(); + res[i] = Byte(); + } + return res; + } + + public final int Int() { + + int res = BitConverter.ToInt32(Buf, Ind); + Ind += 4; + return res; + } + + public final long Long() { + long res = BitConverter.ToInt64(Buf, Ind); + Ind += 8; + return res; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ushort UShort() + public final short Short() { + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ushort res = BitConverter.ToUInt16(Buf, Ind); + short res = BitConverter.ToInt16(Buf, Ind); + Ind += 2; + return res; + } + + public final String Str() { + + int sz = Int(); + String res = UDT.readString(Buf, Ind, sz); + Ind += sz; + return res; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] ByteAr() + public final byte[] ByteAr() { + + int sz = Int(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var res = new byte[sz]; + byte[] res = new byte[sz]; + System.arraycopy(Buf, Ind, res, 0, sz); + Ind += sz; + return res; + } + + public final boolean gethasData() { + return Ind < Buf.length; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void AddRange(List list) + public final void AddRange(ArrayList list) { + if (Ind > 0) { + throw new RuntimeException("Cannot insert"); + } + ByteLists.addPrimitiveArrayToList(Buf, list); + Buf = ByteLists.toArray(list); + } + + public short UShort() { + return Short(); + } + + public HashMap> ArrayDeal() { + int num = Int(); + HashMap> res = new HashMap>(); + for (int i = 0; i < num; i++) { + DealInternal deal = Struct(new DealInternal()); + if (!res.containsKey(deal.PositionTicket)) + res.put(deal.PositionTicket, new LinkedList()); + res.get(deal.PositionTicket).add(deal); + } + return res; + } + + // public void Skip() + // { + // Ind += Int(); + // } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/IntContainer.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/IntContainer.java new file mode 100644 index 00000000000..e015f782d58 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/IntContainer.java @@ -0,0 +1,19 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class IntContainer { + public int Value; + + public static IntContainer fromData(int i) { + IntContainer c = new IntContainer(); + c.Value = i; + return c; + } + + public int get() { + return Value; + } + + public void set(int i) { + Value = i; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Logger.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Logger.java new file mode 100644 index 00000000000..1eff0d441aa --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Logger.java @@ -0,0 +1,117 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// C# TO JAVA CONVERTER TODO TASK: There is no preprocessor in Java: +// #if TradingAPI +// #else + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** Log output. */ +public class Logger { + + /** New message event. */ + public static Event OnMsg = new Event(); + + /** Enable output debug info in the exception messages. */ + public static boolean DebugInfo = false; + + private Object Parent; + + /** + * Initialize new Logger. + * + * @param parent Parent object + */ + public Logger(Object parent) { + Parent = parent; + } + + /** + * Trace message. + * + * @param msg Message + */ + public final void trace(String msg) { + if (OnMsg != null) { + // onMsg(new object[] { msg, MsgType.Trace }); + ThreadPool.QueueUserWorkItem(() -> onMsg(new Object[] {msg, MsgType.Trace})); + } + } + + private void onMsg(Object obj) { + try { + Object[] args = (Object[]) obj; + for (OnMsgHandler listener : OnMsg.listeners()) { + listener.invoke(Parent, (String) args[0], (MsgType) args[1]); + } + } catch (RuntimeException e) { + e.printStackTrace(); + } + } + + /** + * Debug message. + * + * @param msg Message + */ + public final void debug(String msg) { + if (OnMsg != null) { + ThreadPool.QueueUserWorkItem(() -> onMsg(new Object[] {msg, MsgType.Debug})); + } + } + + /** + * Information message. + * + * @param msg Message + */ + public final void info(String msg) { + if (OnMsg != null) { + ThreadPool.QueueUserWorkItem(() -> onMsg(new Object[] {msg, MsgType.Info})); + } + } + + /** + * Warning message. + * + * @param msg Message + */ + public final void warn(String msg) { + if (OnMsg != null) { + ThreadPool.QueueUserWorkItem(() -> onMsg(new Object[] {msg, MsgType.Warn})); + } + } + + /** + * Error message. + * + * @param msg Message + */ + public final void error(String msg) { + if (OnMsg != null) { + ThreadPool.QueueUserWorkItem(() -> onMsg(new Object[] {msg, MsgType.Error})); + } + } + + /** + * Exception message. + * + * @param ex Exception + */ + public final void exception(Exception ex) { + if (OnMsg != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + ex.printStackTrace(pw); + String sStackTrace = sw.toString(); + ThreadPool.QueueUserWorkItem( + () -> + onMsg( + new Object[] { + ex.getMessage() + System.lineSeparator() + sStackTrace, MsgType.Exception + })); + } + } +} +// #endif diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginId.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginId.java new file mode 100644 index 00000000000..61191de3fc8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginId.java @@ -0,0 +1,672 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class LoginId { + + public static class GlobalMembers { + public static long[] t00000B98 = { + 0x0000000000000001L, + 0x0000000000000002L, + 0x0000000000000004L, + 0x0000000000000008L, + 0x0000000000000010L, + 0x0000000000000020L, + 0x0000000000000040L, + 0x0000000000000080L, + 0x0000000000000100L, + 0x0000000000000200L, + 0x0000000000000400L, + 0x0000000000000800L, + 0x0000000000001000L, + 0x0000000000002000L, + 0x0000000000004000L, + 0x0000000000008000L, + 0x0000000000010000L, + 0x0000000000020000L, + 0x0000000000040000L, + 0x0000000000080000L, + 0x0000000000100000L, + 0x0000000000200000L, + 0x0000000000400000L, + 0x0000000000800000L, + 0x0000000001000000L, + 0x0000000002000000L, + 0x0000000004000000L, + 0x0000000008000000L, + 0x0000000010000000L, + 0x0000000020000000L, + 0x0000000040000000L, + 0x0000000080000000L, + 0x0000000100000000L, + 0x0000000200000000L, + 0x0000000400000000L, + 0x0000000800000000L, + 0x0000001000000000L, + 0x0000002000000000L, + 0x0000004000000000L, + 0x0000008000000000L, + 0x0000010000000000L, + 0x0000020000000000L, + 0x0000040000000000L, + 0x0000080000000000L, + 0x0000100000000000L, + 0x0000200000000000L, + 0x0000400000000000L, + 0x0000800000000000L, + 0x0001000000000000L, + 0x0002000000000000L, + 0x0004000000000000L, + 0x0008000000000000L, + 0x0010000000000000L, + 0x0020000000000000L, + 0x0040000000000000L, + 0x0080000000000000L, + 0x0100000000000000L, + 0x0200000000000000L, + 0x0400000000000000L, + 0x0800000000000000L, + 0x1000000000000000L, + 0x2000000000000000L, + 0x4000000000000000L, + 0x8000000000000000L + }; + } + + long s0; + long s8; + long s10; + long s18; + byte[] s20 = new byte[1024]; + int s420; + int _align; + long s428; + + public long Decode(byte[] data) { + long[] d = new long[data.length / 8]; + for (int i = 0; i < d.length; i++) { + d[i] = BitConverter.ToUInt64(data, i * 8); + } + return Decode(d, (int) d.length, 0); + } + + long Decode(long[] pData, int szData, int arg8) { + long[] data = new long[53]; + + // ORIGINAL LINE: long& v0 = data[0]; + long v0 = data[0]; + + // ORIGINAL LINE: long& v8 = data[1]; + long v8 = data[1]; + + // ORIGINAL LINE: long& v10 = data[2]; + long v10 = data[2]; + + // ORIGINAL LINE: long& v18 = data[3]; + long v18 = data[3]; + + // ORIGINAL LINE: long& v20 = data[4]; + long v20 = data[4]; + + // ORIGINAL LINE: long& v28 = data[5]; + long v28 = data[5]; + + // ORIGINAL LINE: long& v30 = data[6]; + long v30 = data[6]; + + // ORIGINAL LINE: long& v38 = data[7]; + long v38 = data[7]; + + // ORIGINAL LINE: long& v40 = data[8]; + long v40 = data[8]; + + // ORIGINAL LINE: long& v48 = data[9]; + long v48 = data[9]; + + // ORIGINAL LINE: long& v50 = data[10]; + long v50 = data[10]; + + // ORIGINAL LINE: long& v58 = data[11]; + long v58 = data[11]; + + // ORIGINAL LINE: long& v60 = data[12]; + long v60 = data[12]; + + // ORIGINAL LINE: long& v68 = data[13]; + long v68 = data[13]; + + // ORIGINAL LINE: long& v70 = data[14]; + long v70 = data[14]; + + // ORIGINAL LINE: long& v78 = data[15]; + long v78 = data[15]; + + // ORIGINAL LINE: long& v80 = data[16]; + long v80 = data[16]; + + // ORIGINAL LINE: long& v88 = data[17]; + long v88 = data[17]; + + // ORIGINAL LINE: long& v90 = data[18]; + long v90 = data[18]; + + // ORIGINAL LINE: long& v98 = data[19]; + long v98 = data[19]; + + // ORIGINAL LINE: long& vA0 = data[20]; + long vA0 = data[20]; + + // ORIGINAL LINE: long& vA8 = data[21]; + long vA8 = data[21]; + + // ORIGINAL LINE: long& vB0 = data[22]; + long vB0 = data[22]; + + // ORIGINAL LINE: long& vB8 = data[23]; + long vB8 = data[23]; + + // ORIGINAL LINE: long& vC0 = data[24]; + long vC0 = data[24]; + + // ORIGINAL LINE: long& vC8 = data[25]; + long vC8 = data[25]; + + // ORIGINAL LINE: long& vD0 = data[26]; + long vD0 = data[26]; + + // ORIGINAL LINE: long& vD8 = data[27]; + long vD8 = data[27]; + + // ORIGINAL LINE: long& vE0 = data[28]; + long vE0 = data[28]; + + // ORIGINAL LINE: long& vE8 = data[29]; + long vE8 = data[29]; + + // ORIGINAL LINE: long& vF0 = data[30]; + long vF0 = data[30]; + + // ORIGINAL LINE: long& vF8 = data[31]; + long vF8 = data[31]; + + // ORIGINAL LINE: long& v100 = data[32]; + long v100 = data[32]; + + // ORIGINAL LINE: long& v108 = data[33]; + long v108 = data[33]; + + // ORIGINAL LINE: long& v110 = data[34]; + long v110 = data[34]; + + // ORIGINAL LINE: long& v118 = data[35]; + long v118 = data[35]; + + // ORIGINAL LINE: long& v120 = data[36]; + long v120 = data[36]; + + // ORIGINAL LINE: long& v128 = data[37]; + long v128 = data[37]; + + // ORIGINAL LINE: long& v130 = data[38]; + long v130 = data[38]; + + // ORIGINAL LINE: long& v138 = data[39]; + long v138 = data[39]; + + // ORIGINAL LINE: long& v140 = data[40]; + long v140 = data[40]; + + // ORIGINAL LINE: long& v148 = data[41]; + long v148 = data[41]; + + // ORIGINAL LINE: long& v150 = data[42]; + long v150 = data[42]; + + // ORIGINAL LINE: long& v158 = data[43]; + long v158 = data[43]; + + // ORIGINAL LINE: long& v160 = data[44]; + long v160 = data[44]; + + // ORIGINAL LINE: long& v168 = data[45]; + long v168 = data[45]; + + // ORIGINAL LINE: long& v170 = data[46]; + long v170 = data[46]; + + // ORIGINAL LINE: long& v178 = data[47]; + long v178 = data[47]; + + // ORIGINAL LINE: long& v180 = data[48]; + long v180 = data[48]; + + // ORIGINAL LINE: long& v188 = data[49]; + long v188 = data[49]; + + // ORIGINAL LINE: long& v190 = data[50]; + long v190 = data[50]; + + // ORIGINAL LINE: long& v198 = data[51]; + long v198 = data[51]; + + // ORIGINAL LINE: long& v1A0 = data[52]; + long v1A0 = data[52]; + // LONGLONG v0, v8, v10, v18, v20, v28, v30, v38, v40, v48, v58, v60, v68, v78, v80, v88, v90, + // v98, vA0, vA8, vB0, vB8, vC0, vC8, + // vD0, vD8, vE0, vF0, vF8, v100, v108, v110, v118, v128, v138, v140, v148, v150, v158, v160, + // v168, v170, v178, v180, v190, v198; + // BEGIN_PARSE (��� ��������� �� END_PARSE ������ ���� ������������� � �� ��������� ������������ + // (� style)) + v158 = pData[0]; + v140 = 0; + s8 = 0; + while (true) { + l00C144D7: + v70 = v158; + vD0 = v158; + vC8 = pData[(int) s8]; + v88 = 0; + v98 = 0; + for (int i = 0; i < 0x40; i++) { + vA0 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + v78 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v48 = vA0 ^ v78 ^ v98; + v98 = + ((Convert.ToBoolean(vA0) & Convert.ToBoolean(v78)) + || (Convert.ToBoolean(vA0) & Convert.ToBoolean(v98))) + ? 1 + : (v78 & v98); + v68 = v48 != 0L ? ~0L : 0L; + v88 |= GlobalMembers.t00000B98[i] & v68; + } + vD0 = v88; + vC8 = ~v70 + 1; + v88 = 0; + v98 = 0; + for (int i = 0; i < 0x40; i++) { + vA0 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v78 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + vD8 = vA0 ^ v78 ^ v98; + v98 = (Convert.ToBoolean(vA0 & v78) || Convert.ToBoolean(vA0 & v98)) ? 1 : (v78 & v98); + v58 = vD8 != 0L ? ~0L : 0L; + v88 |= GlobalMembers.t00000B98[i] & v58; + } + v100 = 0x1C; + v138 = 0x15; + // MAKELONGLONG(HIDWORD(v88), LODWORD(v88)); + v150 = + ((long) + ((((((((long) (v88)) >>> 32) & 0xffffffff)))) + | (((long) ((((((long) (v88)) & 0xffffffff))))) << 32))); + v128 = ~v138 + 1; + v78 = 0; + vB0 = 0; + for (int i = 0; i < 0x40; i++) { + vB8 = Convert.ToUInt64((v100 & GlobalMembers.t00000B98[i]) != 0); + v98 = Convert.ToUInt64((v128 & GlobalMembers.t00000B98[i]) != 0); + v90 = vB8 ^ v98 ^ vB0; + vB0 = (Convert.ToBoolean(vB8 & v98) || Convert.ToBoolean(vB0 & vB8)) ? 1 : (v98 & vB0); + v68 = v90 != 0 ? ~0L : 0L; + v78 |= GlobalMembers.t00000B98[i] & v68; + } + v150 = + ((long) + (((((((long) (v150)) & 0xffffffff)) >>> 0x15)) + | (((long) + ((((((((long) (v150)) >>> 32) & 0xffffffff)) >>> 0x15) + | (((((long) (v150)) & 0xffffffff)) << (32 - 0x15))))) + << 32))); + v0 = v78 + 1; + v18 = ~0L << ((int) (((long) (v0)) & 0xffffffff)); + v20 = + ((long) + ((((((((long) (v150)) >>> 32) & 0xffffffff)))) + | (((long) ((((((long) (v150)) & 0xffffffff))))) << 32))); + v140 = v20 & ~v18; + vC8 = 0xD8; + vA8 = v140; + vD0 = ~vC8 + 1; + vC0 = 0; + vB8 = 0; + for (int i = 0; i < 0x40; i++) { + v88 = Convert.ToUInt64((vA8 & GlobalMembers.t00000B98[i]) != 0); + vA0 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v68 = v88 ^ vA0 ^ vB8; + vB8 = (Convert.ToBoolean(v88 & vA0) || Convert.ToBoolean(vB8 & v88)) ? 1 : (vA0 & vB8); + v60 = v68 != 0 ? ~0L : 0L; + vC0 |= GlobalMembers.t00000B98[i] & v60; + } + if (vC0 == 0) { + v18 = s0; + s0 = 0; + return v18; + } + s10 = pData[((int) (((long) (s8 + 1)) & 0xffffffff))]; + v60 = s8; + v68 = 2; + do { + v68 = v60 ^ v68; + v60 = (v60 & ~v68) << 1; + } while (v60 != 0); + s18 = pData[((int) (((long) (v68)) & 0xffffffff))]; + v100 = 0x1C; + v138 = 0x15; + v150 = + ((long) + ((((((((long) (s10)) >>> 32) & 0xffffffff)))) + | (((long) ((((((long) (s10)) & 0xffffffff))))) << 32))); + v128 = ~v138 + 1; + v78 = 0; + vB0 = 0; + for (int i = 0; i < 0x40; i++) { + vB8 = Convert.ToUInt64((v100 & GlobalMembers.t00000B98[i]) != 0); + v98 = Convert.ToUInt64((v128 & GlobalMembers.t00000B98[i]) != 0); + v90 = vB8 ^ v98 ^ vB0; + vB0 = (Convert.ToBoolean(vB8 & v98) || Convert.ToBoolean(vB0 & vB8)) ? 1 : (v98 & vB0); + v68 = v90 != 0 ? ~0L : 0; + v78 |= GlobalMembers.t00000B98[i] & v68; + } + v150 = + ((long) + (((((((long) (v150)) & 0xffffffff)) >>> 0x15)) + | (((long) + ((((((((long) (v150)) >>> 32) & 0xffffffff)) >>> 0x15) + | (((((long) (v150)) & 0xffffffff)) << (32 - 0x15))))) + << 32))); + v0 = v78 + 1; + v18 = ~0L << ((int) (((long) (v0)) & 0xffffffff)); + v20 = + ((long) + ((((((((long) (v150)) >>> 32) & 0xffffffff)))) + | (((long) ((((((long) (v150)) & 0xffffffff))))) << 32))); + v68 = v20 & ~v18; + v80 = ~(v68 ^ ~0xF5L); + if (v80 == 0) { + s10 = s0; + } + vC8 = 0x54; + vA8 = v140; + vD0 = ~vC8 + 1; + vC0 = 0; + vB8 = 0; + for (int i = 0; i < 0x40; i++) { + v88 = Convert.ToUInt64((vA8 & GlobalMembers.t00000B98[i]) != 0); + vA0 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v78 = v88 ^ vA0 ^ vB8; + vB8 = (Convert.ToBoolean(v88 & vA0) || Convert.ToBoolean(v88 & vB8)) ? 1 : (vB8 & vA0); + v68 = v78 != 0 ? ~0L : 0L; + vC0 |= GlobalMembers.t00000B98[i] & v68; + } + if (vC0 == 0) { + s0 = s10 & s18; + } + v128 = 0x70; + vC8 = v140; + v100 = ~v128 + 1; + vA0 = 0; + v90 = 0; + for (int i = 0; i < 0x40; i++) { + v78 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + vB8 = Convert.ToUInt64((v100 & GlobalMembers.t00000B98[i]) != 0); + v58 = v78 ^ vB8 ^ v90; + v90 = (Convert.ToBoolean(v78 & vB8) || Convert.ToBoolean(v78 & v90)) ? 1 : (vB8 & v90); + v68 = v58 != 0 ? ~0L : 0; + vA0 |= GlobalMembers.t00000B98[i] & v68; + } + if (vA0 == 0) { + s0 = s10 | s18; + } + v100 = 0x91; + vD0 = v140; + vC8 = ~v100 + 1; + v88 = 0; + v98 = 0; + for (int i = 0; i < 0x40; i++) { + vA0 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v78 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + v68 = vA0 ^ v78 ^ v98; + v98 = (Convert.ToBoolean(v78 & vA0) || Convert.ToBoolean(vA0 & v98)) ? 1 : (v78 & v98); + v60 = v68 != 0 ? ~0L : 0; + v88 |= GlobalMembers.t00000B98[i] & v60; + } + if (v88 == 0) { + s0 = s10 ^ s18; + } + v100 = 0xAB; + vD0 = v140; + vC8 = ~v100 + 1; + v88 = 0; + v98 = 0; + for (int i = 0; i < 0x40; i++) { + vA0 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v78 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + v58 = vA0 ^ v78 ^ v98; + v98 = (Convert.ToBoolean(vA0 & v78) || Convert.ToBoolean(vA0 & v98)) ? 1 : (v78 & v98); + v68 = v58 != 0 ? ~0L : 0; + v88 |= GlobalMembers.t00000B98[i] & v68; + } + if (v88 == 0) { + v58 = s10; + v60 = s18; + do { + v60 = v58 ^ v60; + v58 = (v58 & ~v60) << 1; + } while (v58 != 0); + s0 = v60; + } + vD0 = 0xA9; + vB0 = v140; + vA8 = ~vD0 + 1; + vE0 = 0; + v78 = 0; + for (int i = 0; i < 0x40; i++) { + vC0 = Convert.ToUInt64((vB0 & GlobalMembers.t00000B98[i]) != 0); + v88 = Convert.ToUInt64((vA8 & GlobalMembers.t00000B98[i]) != 0); + v68 = vC0 ^ v88 ^ v78; + v78 = (Convert.ToBoolean(vC0 & v88) || Convert.ToBoolean(vC0 & v78)) ? 1 : (v88 & v78); + v58 = v68 != 0 ? ~0L : 0; + vE0 |= GlobalMembers.t00000B98[i] & v58; + } + if (vE0 == 0) { + v40 = s10; + v48 = ~s18 + 1; + do { + v48 = v40 ^ v48; + v40 = (v40 & ~v48) << 1; + } while (v40 != 0); + s0 = v48; + } + vD0 = 0xB1; + vB0 = v140; + vA8 = ~vD0 + 1; + vE0 = 0; + v78 = 0; + for (int i = 0; i < 0x40; i++) { + vC0 = Convert.ToUInt64((vB0 & GlobalMembers.t00000B98[i]) != 0); + v88 = Convert.ToUInt64((vA8 & GlobalMembers.t00000B98[i]) != 0); + vA0 = vC0 ^ v88 ^ v78; + v78 = (Convert.ToBoolean(vC0 & v88) || Convert.ToBoolean(vC0 & v78)) ? 1 : (v88 & v78); + v68 = vA0 != 0 ? ~0L : 0; + vE0 |= GlobalMembers.t00000B98[i] & v68; + } + if (vE0 == 0) { + v150 = Long.remainderUnsigned(s18, 24); + v70 = s10; + v148 = 0; + while (true) { + v98 = v148; + vB8 = v150; + v90 = ~vB8 + 1; + v160 = 0; + v178 = 1; + v170 = 1; + v168 = 0; + vB0 = 0; + vA8 = 0; + vD0 = 0; + vC8 = 0; + for (int i = 0; i < 0x40; i++) { + vB0 = Convert.ToUInt64((v98 & GlobalMembers.t00000B98[i]) != 0); + vA8 = Convert.ToUInt64((vB8 & GlobalMembers.t00000B98[i]) != 0); + vD0 = Convert.ToUInt64((v90 & GlobalMembers.t00000B98[i]) != 0); + if (vB0 != 0 && vA8 == 0) { + v160 = 0; + } + if (vB0 == 0 && vA8 != 0) { + v160 = 1; + } + v180 = vB0 ^ vD0 ^ vC8; + vC8 = (Convert.ToBoolean(vB0 & vD0) || Convert.ToBoolean(vB0 & vC8)) ? 1 : (vD0 & vC8); + if (v180 != 0) { + v170 = 0; + if (i <= 7) { + v178 = Convert.ToUInt64(v178 == 0); + } + } + } + if (vB0 == 0 && vA8 != 0 && v180 != 0) { + v168 = 1; + } + if (vB0 != 0 && vA8 == 0 && v180 == 0) { + v168 = 1; + } + if (v160 == 0) { + s0 = v70; + break; + } + vD0 = v70; + vC8 = v70; + v88 = 0; + v98 = 0; + for (int i = 0; i < 0x40; i++) { + vA0 = Convert.ToUInt64((vC8 & GlobalMembers.t00000B98[i]) != 0); + v78 = Convert.ToUInt64((vD0 & GlobalMembers.t00000B98[i]) != 0); + v48 = vA0 ^ v78 ^ v98; + v98 = (Convert.ToBoolean(vA0 & v78) || Convert.ToBoolean(vA0 & v98)) ? 1 : (v78 & v98); + v68 = v48 != 0 ? ~0L : 0; + v88 |= GlobalMembers.t00000B98[i] & v68; + } + v70 = v88; + v148++; + } + } + v80 = v140 ^ 0xC8; + if (v80 == 0) { + v1A0 = Long.remainderUnsigned(s18, 24); + v190 = s10; + v100 = 0; + v160 = 0; + while (true) { + v110 = v160; + vF0 = 0x40; + vE0 = ~vF0 + 1; + v178 = 0; + v170 = 1; + v180 = 1; + v198 = 0; + vC0 = 0; + v88 = 0; + vA0 = 0; + v78 = 0; + for (int i = 0; i < 0x40; i++) { + vC0 = Convert.ToUInt64((v110 & GlobalMembers.t00000B98[i]) != 0); + v88 = Convert.ToUInt64((vF0 & GlobalMembers.t00000B98[i]) != 0); + vA0 = Convert.ToUInt64((vE0 & GlobalMembers.t00000B98[i]) != 0); + if (vC0 != 0 && v88 == 0) { + v178 = 0; + } + if (vC0 == 0 && v88 != 0) { + v178 = 1; + } + v168 = vC0 ^ vA0 ^ v78; + v78 = (Convert.ToBoolean(vC0 & vA0) || Convert.ToBoolean(vC0 & v78)) ? 1 : (vA0 & v78); + if (v168 != 0) { + v180 = 0; + if (i <= 7) { + v170 = Convert.ToUInt64(v170 == 0); + } + } + } + if (vC0 == 0 && v88 != 0 && v168 != 0) { + v198 = 1; + } + if (vC0 != 0 && v88 == 0 && v168 == 0) { + v198 = 1; + } + if (v178 == 0) { + s0 = v100; + break; + } + v80 = v190 & GlobalMembers.t00000B98[(int) v160]; + if (v80 != 0 && (v160 >= v1A0)) { + vD0 = v1A0; + vB0 = v160; + vA8 = ~vD0 + 1; + vE0 = 0; + v78 = 0; + for (int i = 0; i < 0x40; i++) { + vC0 = Convert.ToUInt64((vB0 & GlobalMembers.t00000B98[i]) != 0); + v88 = Convert.ToUInt64((vA8 & GlobalMembers.t00000B98[i]) != 0); + vA0 = vC0 ^ v88 ^ v78; + v78 = + (Convert.ToBoolean(vC0 & v88) || Convert.ToBoolean(vC0 & v78)) ? 1 : (v88 & v78); + v58 = vA0 != 0 ? ~0L : 0; + vE0 |= GlobalMembers.t00000B98[i] & v58; + } + v100 ^= GlobalMembers.t00000B98[(int) vE0]; + } + v160++; + } + } + s8 += 3; + } // goto l00C144D7; // repid main cycle + // END_PARSE + } + + public long DecodeData(byte[] data) { + long[] d = new long[data.length / 8]; + for (int i = 0; i < d.length; i++) { + d[i] = BitConverter.ToUInt64(data, i * 8); + } + return DecodeData(d, d.length); + } + + // +4012D0 gp + public long DecodeData(long[] pData, int szData) { + long id = 0; + for (int i = 0; i < szData; i += 3) { + byte depth = (byte) (pData[i] >>> 14); + long value = pData[i + 2]; + if (((pData[i + 1] & 0x3FC000) ^ 0x14C000) != 0) { + id = pData[i + 1]; + } + switch (depth) { + case 0x12: + id |= value; + break; + case 0x3A: + id -= value; + break; + case 0x40: + id >>>= (int) Long.remainderUnsigned(value, 24); + break; + case 0x79: + id ^= value; + break; + case (byte) 0x8B: + id += value; + break; + case (byte) 0xB5: + id = (id & 0xffffffffL) | (((value >>> 32) & 0xffffffffL) << 32); + break; + case (byte) 0xC2: + id <<= (int) Long.remainderUnsigned(value, 24); + break; + case (byte) 0xE1: + id &= value; + break; + default: + return 0; + } + } + return id; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginIdWebServer.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginIdWebServer.java new file mode 100644 index 00000000000..f1f8a3ebb84 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginIdWebServer.java @@ -0,0 +1,76 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Base64; + +class LoginIdWebServer { + class Run implements Runnable { + String Url; + byte[] Data; + boolean LoginIdData; + + long Id; + IOException Ex; + + @Override + public void run() { + try { + URL obj = new URL(Url); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + // add reuqest header + con.setRequestMethod("POST"); + con.setRequestProperty("User-Agent", "FIREFOX"); + con.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); + con.setRequestProperty("Content-Type", "text/plain;charset=UTF-8"); + // Send post request + con.setDoOutput(true); + DataOutputStream wr = new DataOutputStream(con.getOutputStream()); + String str = Base64.getEncoder().encodeToString(Data); + if (LoginIdData) str = "loginiddata" + str; + wr.writeBytes(str); + wr.flush(); + wr.close(); + int responseCode = con.getResponseCode(); + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + if (response.toString().equals("OK")) return; + if (!Character.isDigit(response.toString().charAt(0))) + throw new IOException("LoginIdWebServer: " + response.toString()); + Id = new BigInteger(response.toString(), 10).longValue(); + } catch (IOException e) { + Ex = new IOException("LoginIdWebServer: " + e.getMessage()); + } + } + } + + long Decode(String url, String guid, byte[] data, int timeout, Logger log) + throws IOException, TimeoutException { + + Run run = new Run(); + run.Url = url + "?guid=" + guid; + run.Data = data; + run.LoginIdData = false; + Thread th = new Thread(run); + th.start(); + try { + th.join(timeout); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (th.isAlive()) + throw new TimeoutException("No reply from login id web server in " + timeout + "ms", log); + if (run.Ex != null) throw run.Ex; + return run.Id; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginRcv1.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginRcv1.java new file mode 100644 index 00000000000..8de49ba6160 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/LoginRcv1.java @@ -0,0 +1,41 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Size = 0x2C, CharSet = CharSet.Unicode)]*/ +public class LoginRcv1 extends FromBufReader { + /*[FieldOffset(0)]*/ public int s0; + /*[FieldOffset(4)]*/ public Msg StatusCode = Msg.values()[0]; // 4 + /*[FieldOffset(8)]*/ public int CertType; // 8 + /*[FieldOffset(0xC)]*/ private long SerialNumber; // C + /*[FieldOffset(0x14)]*/ public int PassLength; // 14 + /*[FieldOffset(0x18)]*/ public short TradeBuild; // 18 + /*[FieldOffset(0x1A)]*/ public short SymBuild; // 1A + /*[FieldOffset(0x1C)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] CryptKey; + public byte[] CryptKey; // 1C + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 44; + LoginRcv1 st = new LoginRcv1(); + st.s0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.StatusCode = Msg.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.CertType = BitConverter.ToInt32(buf.Bytes(4), 0); + st.SerialNumber = BitConverter.ToInt64(buf.Bytes(8), 0); + st.PassLength = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TradeBuild = BitConverter.ToInt16(buf.Bytes(2), 0); + st.SymBuild = BitConverter.ToInt16(buf.Bytes(2), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.CryptKey = new byte[16]; + st.CryptKey = new byte[16]; + for (int i = 0; i < 16; i++) { + st.CryptKey[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5.java new file mode 100644 index 00000000000..9dad226b1ae --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5.java @@ -0,0 +1,198 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class MD5 { + + private static final int INIT_A = 0x67452301; + private static final int INIT_B = (int) 0xEFCDAB89L; + private static final int INIT_C = (int) 0x98BADCFEL; + private static final int INIT_D = 0x10325476; + + private static final int[] SHIFT_AMTS = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + + private static final int[] TABLE_T = new int[64]; + + static { + for (int i = 0; i < 64; i++) TABLE_T[i] = (int) (long) ((1L << 32) * Math.abs(Math.sin(i + 1))); + } + + public static byte[] computeMD5(byte[][] messages) { + byte[] md5 = new byte[16]; + int a = INIT_A; + int b = INIT_B; + int c = INIT_C; + int d = INIT_D; + for (byte[] message : messages) { + int messageLenBytes = message.length; + int numBlocks = ((messageLenBytes + 8) >>> 6) + 1; + int totalLen = numBlocks << 6; + byte[] paddingBytes = new byte[totalLen - messageLenBytes]; + paddingBytes[0] = (byte) 0x80; + + long messageLenBits = (long) messageLenBytes << 3; + for (int i = 0; i < 8; i++) { + paddingBytes[paddingBytes.length - 8 + i] = (byte) messageLenBits; + messageLenBits >>>= 8; + } + int[] buffer = new int[16]; + for (int i = 0; i < numBlocks; i++) { + int index = i << 6; + for (int j = 0; j < 64; j++, index++) + buffer[j >>> 2] = + ((int) + ((index < messageLenBytes) + ? message[index] + : paddingBytes[index - messageLenBytes]) + << 24) + | (buffer[j >>> 2] >>> 8); + int originalA = a; + int originalB = b; + int originalC = c; + int originalD = d; + for (int j = 0; j < 64; j++) { + int div16 = j >>> 4; + int f = 0; + int bufferIndex = j; + switch (div16) { + case 0: + f = (b & c) | (~b & d); + break; + + case 1: + f = (b & d) | (c & ~d); + bufferIndex = (bufferIndex * 5 + 1) & 0x0F; + break; + + case 2: + f = b ^ c ^ d; + bufferIndex = (bufferIndex * 3 + 5) & 0x0F; + break; + + case 3: + f = c ^ (b | ~d); + bufferIndex = (bufferIndex * 7) & 0x0F; + break; + } + int temp = + b + + Integer.rotateLeft( + a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)]); + a = d; + d = c; + c = b; + b = temp; + } + + a += originalA; + b += originalB; + c += originalC; + d += originalD; + } + } + int count = 0; + for (int i = 0; i < 4; i++) { + int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d)); + for (int j = 0; j < 4; j++) { + md5[count++] = (byte) n; + n >>>= 8; + } + } + return md5; + } + + public static byte[] computeMD5(byte[] message) { + int messageLenBytes = message.length; + int numBlocks = ((messageLenBytes + 8) >>> 6) + 1; + int totalLen = numBlocks << 6; + byte[] paddingBytes = new byte[totalLen - messageLenBytes]; + paddingBytes[0] = (byte) 0x80; + + long messageLenBits = (long) messageLenBytes << 3; + for (int i = 0; i < 8; i++) { + paddingBytes[paddingBytes.length - 8 + i] = (byte) messageLenBits; + messageLenBits >>>= 8; + } + + int a = INIT_A; + int b = INIT_B; + int c = INIT_C; + int d = INIT_D; + int[] buffer = new int[16]; + for (int i = 0; i < numBlocks; i++) { + int index = i << 6; + for (int j = 0; j < 64; j++, index++) + buffer[j >>> 2] = + ((int) + ((index < messageLenBytes) + ? message[index] + : paddingBytes[index - messageLenBytes]) + << 24) + | (buffer[j >>> 2] >>> 8); + int originalA = a; + int originalB = b; + int originalC = c; + int originalD = d; + for (int j = 0; j < 64; j++) { + int div16 = j >>> 4; + int f = 0; + int bufferIndex = j; + switch (div16) { + case 0: + f = (b & c) | (~b & d); + break; + + case 1: + f = (b & d) | (c & ~d); + bufferIndex = (bufferIndex * 5 + 1) & 0x0F; + break; + + case 2: + f = b ^ c ^ d; + bufferIndex = (bufferIndex * 3 + 5) & 0x0F; + break; + + case 3: + f = c ^ (b | ~d); + bufferIndex = (bufferIndex * 7) & 0x0F; + break; + } + int temp = + b + + Integer.rotateLeft( + a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)]); + a = d; + d = c; + c = b; + b = temp; + } + + a += originalA; + b += originalB; + c += originalC; + d += originalD; + } + + byte[] md5 = new byte[16]; + int count = 0; + for (int i = 0; i < 4; i++) { + int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d)); + for (int j = 0; j < 4; j++) { + md5[count++] = (byte) n; + n >>>= 8; + } + } + return md5; + } + + public static String toHexString(byte[] b) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < b.length; i++) { + sb.append(String.format("%02X", b[i] & 0xFF)); + } + return sb.toString(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5ForBrokerSearch.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5ForBrokerSearch.java new file mode 100644 index 00000000000..3ddfc04e60c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5ForBrokerSearch.java @@ -0,0 +1,219 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class MD5ForBrokerSearch { + + private static int INIT_A = 0x67452301; + private static int INIT_B = (int) 0xEFCDAB89L; + private static int INIT_C = (int) 0x98BADCFEL; + private static int INIT_D = 0x10325476; + + private static final int[] SHIFT_AMTS = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + + private static final int[] TABLE_T = new int[64]; + + static { + for (int i = 0; i < 64; i++) TABLE_T[i] = (int) (long) ((1L << 32) * Math.abs(Math.sin(i + 1))); + } + + public byte[] computeMD5(byte[][] messages) { + byte[] md5 = new byte[16]; + // int a = INIT_A; + // int b = INIT_B; + // int c = INIT_C; + // int d = INIT_D; + for (byte[] message : messages) { + int messageLenBytes = message.length; + int numBlocks = ((messageLenBytes + 8) >>> 6) + 1; + int totalLen = numBlocks << 6; + byte[] paddingBytes = new byte[totalLen - messageLenBytes]; + paddingBytes[0] = (byte) 0x80; + + long messageLenBits = (long) messageLenBytes << 3; + for (int i = 0; i < 8; i++) { + paddingBytes[paddingBytes.length - 8 + i] = (byte) messageLenBits; + messageLenBits >>>= 8; + } + int[] buffer = new int[16]; + for (int i = 0; i < numBlocks; i++) { + int index = i << 6; + for (int j = 0; j < 64; j++, index++) + buffer[j >>> 2] = + ((int) + ((index < messageLenBytes) + ? message[index] + : paddingBytes[index - messageLenBytes]) + << 24) + | (buffer[j >>> 2] >>> 8); + int originalA = a; + int originalB = b; + int originalC = c; + int originalD = d; + for (int j = 0; j < 64; j++) { + int div16 = j >>> 4; + int f = 0; + int bufferIndex = j; + switch (div16) { + case 0: + f = (b & c) | (~b & d); + break; + + case 1: + f = (b & d) | (c & ~d); + bufferIndex = (bufferIndex * 5 + 1) & 0x0F; + break; + + case 2: + f = b ^ c ^ d; + bufferIndex = (bufferIndex * 3 + 5) & 0x0F; + break; + + case 3: + f = c ^ (b | ~d); + bufferIndex = (bufferIndex * 7) & 0x0F; + break; + } + int temp = + b + + Integer.rotateLeft( + a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)]); + a = d; + d = c; + c = b; + b = temp; + } + + a += originalA; + b += originalB; + c += originalC; + d += originalD; + } + } + int count = 0; + for (int i = 0; i < 4; i++) { + int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d)); + for (int j = 0; j < 4; j++) { + md5[count++] = (byte) n; + n >>>= 8; + } + } + return md5; + } + + // byte[] padding; + int a = INIT_A; + int b = INIT_B; + int c = INIT_C; + int d = INIT_D; + + public MD5ForBrokerSearch() { + // padding = new byte[64]; + // padding[0] = (byte)0x80; + } + + public void Init() { + a = INIT_A; + b = INIT_B; + c = INIT_C; + d = INIT_D; + } + + public byte[] computeMD5(byte[] message) { + int messageLenBytes = message.length; + int numBlocks = ((messageLenBytes + 8) >>> 6) + 1; + int totalLen = numBlocks << 6; + byte[] paddingBytes = new byte[totalLen - messageLenBytes]; + paddingBytes[0] = (byte) 0x80; + // for (int i = 0; i < paddingBytes.length; i++) + // paddingBytes[i] = padding[i]; + + long messageLenBits = (long) messageLenBytes << 3; + for (int i = 0; i < 8; i++) { + paddingBytes[paddingBytes.length - 8 + i] = (byte) messageLenBits; + messageLenBits >>>= 8; + } + + int[] buffer = new int[16]; + for (int i = 0; i < numBlocks; i++) { + int index = i << 6; + for (int j = 0; j < 64; j++, index++) + buffer[j >>> 2] = + ((int) + ((index < messageLenBytes) + ? message[index] + : paddingBytes[index - messageLenBytes]) + << 24) + | (buffer[j >>> 2] >>> 8); + int originalA = a; + int originalB = b; + int originalC = c; + int originalD = d; + for (int j = 0; j < 64; j++) { + int div16 = j >>> 4; + int f = 0; + int bufferIndex = j; + switch (div16) { + case 0: + f = (b & c) | (~b & d); + break; + + case 1: + f = (b & d) | (c & ~d); + bufferIndex = (bufferIndex * 5 + 1) & 0x0F; + break; + + case 2: + f = b ^ c ^ d; + bufferIndex = (bufferIndex * 3 + 5) & 0x0F; + break; + + case 3: + f = c ^ (b | ~d); + bufferIndex = (bufferIndex * 7) & 0x0F; + break; + } + int temp = + b + + Integer.rotateLeft( + a + f + buffer[bufferIndex] + TABLE_T[j], SHIFT_AMTS[(div16 << 2) | (j & 3)]); + a = d; + d = c; + c = b; + b = temp; + } + + a += originalA; + b += originalB; + c += originalC; + d += originalD; + } + // INIT_A = a; + // INIT_B = b; + // INIT_C = c; + // INIT_D = d; + // for (int i = 0; i < paddingBytes.length; i++) + // padding[i] = paddingBytes[i]; + byte[] md5 = new byte[16]; + int count = 0; + for (int i = 0; i < 4; i++) { + int n = (i == 0) ? a : ((i == 1) ? b : ((i == 2) ? c : d)); + for (int j = 0; j < 4; j++) { + md5[count++] = (byte) n; + n >>>= 8; + } + } + return md5; + } + + public static String toHexString(byte[] b) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < b.length; i++) { + sb.append(String.format("%02X", b[i] & 0xFF)); + } + return sb.toString(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5Managed.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5Managed.java new file mode 100644 index 00000000000..6e730ea60cf --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MD5Managed.java @@ -0,0 +1,814 @@ +// package mtapi.mt5; +// +// import java.util.Arrays; +// +/// ** +// MD5Managed: A HashAlgorithm implementation that acts as a thin wrapper +// around a C# translation of the MD5 reference implementation. The C code +// has been translated as closely as possible so that most of the original +// structure remains and comparisons between the two are straightforward. +// +// +// Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. +// +// Specification: +// RFC1321 - The MD5 Message-Digest Algorithm +// http: //www.faqs.org/rfcs/rfc1321.html +// +// Original license: +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. +// +// */ +// public class MD5Managed +// { +// // Current context +// private final MD5_CTX _context = new MD5_CTX(); +// // Last hash result +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private readonly byte[] _digest = new byte[16]; +// private final byte[] _digest = new byte[16]; +// // True if HashCore has been called +// private boolean _hashCoreCalled; +// // True if HashFinal has been called +// private boolean _hashFinalCalled; +// +// /** +// Initializes a new instance. +// */ +// public MD5Managed() +// { +// InitializeVariables(true); +// } +// +// /** +// Initializes internal state. +// */ +// public final void Initialize(boolean clearConetext) +// { +// InitializeVariables(clearConetext); +// } +// +// /** +// Initializes variables. +// */ +// private void InitializeVariables(boolean clearConetext) +// { +// MD5Init(_context, clearConetext); +// _hashCoreCalled = false; +// _hashFinalCalled = false; +// } +// +// /** +// Updates the hash code with the data provided. +// +// @param array Data to hash. +// @param ibStart Start position. +// @param cbSize Number of bytes. +// */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public void HashCore(byte[] array, int ibStart, int cbSize) +// public final void HashCore(byte[] array, int ibStart, int cbSize) +// { +// if (null == array) +// { +// throw new IllegalArgumentException("array"); +// } +// +// if (_hashFinalCalled) +// { +// throw new RuntimeException("Hash not valid for use in specified state."); +// } +// _hashCoreCalled = true; +// MD5Update(_context, array, (int)ibStart, (int)cbSize); +// } +// +// /** +// Finalizes the hash code and returns it. +// +// @return +// */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public byte[] HashFinal() +// public final byte[] HashFinal() +// { +// _hashFinalCalled = true; +// MD5Final(_digest, _context); +// return getHash(); +// } +// +// /** +// Returns the hash as an array of bytes. +// */ +// //[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", +// Justification = "Matching .NET behavior by throwing here.")] +// //[SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Justification = +// "Matching .NET behavior by throwing NullReferenceException.")] +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public byte[] getHash() +// public final byte[] getHash() +// { +// if (!_hashCoreCalled) +// { +// throw new NullPointerException(); +// } +// if (!_hashFinalCalled) +// { +// // Note: Not CryptographicUnexpectedOperationException because that can't be instantiated on +// Silverlight 4 +// throw new RuntimeException("Hash must be finalized before the hash value is retrieved."); +// } +// +// return _digest; +// } +// +// // Return size of hash in bits. +// public final int getHashSize() +// { +// return _digest.length * 8; +// } +// +// /////////////////////////////////////////////// +// // MD5 reference implementation begins here. // +// /////////////////////////////////////////////// +// +// /* MD5 context. */ +// private static class MD5_CTX +// { +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public readonly uint[] state; +// public int[] state; // state (ABCD) +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public readonly uint[] count; +// public int[] count; // number of bits, modulo 2^64 (lsb first) +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: public readonly byte[] buffer; +// public byte[] buffer; // input buffer +// +// public MD5_CTX() +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: state = new uint[4]; +// state = new int[4]; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: count = new uint[2]; +// count = new int[2]; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: buffer = new byte[64]; +// buffer = new byte[64]; +// } +// +// public final void Clear() +// { +// Arrays.fill(state, 0); +// Arrays.fill(count, 0); +// Arrays.fill(buffer, (byte)0); +// } +// } +// +// /* Constants for MD5Transform routine. */ +// private static final int S11 = 7; +// private static final int S12 = 12; +// private static final int S13 = 17; +// private static final int S14 = 22; +// private static final int S21 = 5; +// private static final int S22 = 9; +// private static final int S23 = 14; +// private static final int S24 = 20; +// private static final int S31 = 4; +// private static final int S32 = 11; +// private static final int S33 = 16; +// private static final int S34 = 23; +// private static final int S41 = 6; +// private static final int S42 = 10; +// private static final int S43 = 15; +// private static final int S44 = 21; +// +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static byte[] PADDING; +// private static byte[] PADDING; +// +// //[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", +// Justification = "More compact this way")] +// static +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: PADDING = new byte[64]; +// PADDING = new byte[64]; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: PADDING[0] = 0x80; +// PADDING[0] = (byte)0x80; +// } +// +// /* F, G, H and I are basic MD5 functions. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static uint F(uint x, uint y, uint z) +// private static int F(int x, int y, int z) +// { +// return (((x) & (y)) | ((~x) & (z))); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static uint G(uint x, uint y, uint z) +// private static int G(int x, int y, int z) +// { +// return (((x) & (z)) | ((y) & (~z))); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static uint H(uint x, uint y, uint z) +// private static int H(int x, int y, int z) +// { +// return ((x) ^ (y) ^ (z)); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static uint I(uint x, uint y, uint z) +// private static int I(int x, int y, int z) +// { +// return ((y) ^ ((x) | (~z))); +// } +// +// /* ROTATE_LEFT rotates x left n bits. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static uint ROTATE_LEFT(uint x, int n) +// private static int ROTATE_LEFT(int x, int n) +// { +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +// return (((x) << (n)) | ((x) >>> (32 - (n)))); +// } +// +// /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void FF(ref uint a, uint b, uint c, uint d, uint x, int s, uint +// ac) +// private static void FF(mtapi.mt5.RefObject a, int b, int c, int d, int x, int s, int ac) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: (a) += F((b), (c), (d)) + (x) + (uint)(ac); +// (a.argValue) += F((b), (c), (d)) + (x) + (int)(ac); +// (a.argValue) = ROTATE_LEFT((a.argValue), (s)); +// (a.argValue) += (b); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void GG(ref uint a, uint b, uint c, uint d, uint x, int s, uint +// ac) +// private static void GG(mtapi.mt5.RefObject a, int b, int c, int d, int x, int s, int ac) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: (a) += G((b), (c), (d)) + (x) + (uint)(ac); +// (a.argValue) += G((b), (c), (d)) + (x) + (int)(ac); +// (a.argValue) = ROTATE_LEFT((a.argValue), (s)); +// (a.argValue) += (b); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void HH(ref uint a, uint b, uint c, uint d, uint x, int s, uint +// ac) +// private static void HH(mtapi.mt5.RefObject a, int b, int c, int d, int x, int s, int ac) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: (a) += H((b), (c), (d)) + (x) + (uint)(ac); +// (a.argValue) += H((b), (c), (d)) + (x) + (int)(ac); +// (a.argValue) = ROTATE_LEFT((a.argValue), (s)); +// (a.argValue) += (b); +// } +// //C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void II(ref uint a, uint b, uint c, uint d, uint x, int s, uint +// ac) +// private static void II(mtapi.mt5.RefObject a, int b, int c, int d, int x, int s, int ac) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: (a) += I((b), (c), (d)) + (x) + (uint)(ac); +// (a.argValue) += I((b), (c), (d)) + (x) + (int)(ac); +// (a.argValue) = ROTATE_LEFT((a.argValue), (s)); +// (a.argValue) += (b); +// } +// +// /* MD5 initialization. Begins an MD5 operation, writing a new context. */ +// private static void MD5Init(MD5_CTX context, boolean clearConetext) // context +// { +// context.count[0] = context.count[1] = 0; +// +// /* Load magic initialization constants. */ +// if (clearConetext) +// { +// context.state[0] = 0x67452301; +// context.state[1] = (int)0xefcdab89; +// context.state[2] = (int)0x98badcfe; +// context.state[3] = 0x10325476; +// } +// } +// +// /* MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void MD5Update(MD5_CTX context, byte[] input, uint inputIndex, +// uint inputLen) +// private static void MD5Update(MD5_CTX context, byte[] input, int inputIndex, int inputLen) // +// length of input block - Starting index for input block - input block - context +// { +// /* Compute number of bytes mod 64 */ +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint index = (uint)((context.count[0] >> 3) & 0x3F); +// int index = (int)((context.count[0] >>> 3) & 0x3F); +// +// /* Update number of bits */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: if ((context.count[0] += ((uint)inputLen << 3)) < ((uint)inputLen << 3)) +// if ((context.count[0] += ((int)inputLen << 3)) < ((int)inputLen << 3)) +// { +// context.count[1]++; +// } +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: context.count[1] += ((uint)inputLen >> 29); +// context.count[1] += ((int)inputLen >>> 29); +// +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint partLen = 64 - index; +// int partLen = 64 - index; +// +// /* Transform as many times as possible. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint i = 0; +// int i = 0; +// if (inputLen >= partLen) +// { +// System.arraycopy(input, (int)inputIndex, context.buffer, (int)index, (int)partLen); +// MD5Transform(context.state, context.buffer, 0); +// +// for (i = partLen; i + 63 < inputLen; i += 64) +// { +// MD5Transform(context.state, input, inputIndex + i); +// } +// +// index = 0; +// } +// +// /* Buffer remaining input */ +// System.arraycopy(input, (int)(inputIndex + i), context.buffer, (int)index, (int)(inputLen - i)); +// } +// +// /* MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void MD5Final(byte[] digest, MD5_CTX context) +// private static void MD5Final(byte[] digest, MD5_CTX context) // context - message digest +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: byte[] bits = new byte[8]; +// byte[] bits = new byte[8]; +// +// /* Save number of bits */ +// Encode(bits, context.count, 8); +// +// /* Pad out to 56 mod 64. */ +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint index = (uint)((context.count[0] >> 3) & 0x3f); +// int index = (int)((context.count[0] >>> 3) & 0x3f); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint padLen = (index < 56) ? (56 - index) : (120 - index); +// int padLen = (index < 56) ? (56 - index) : (120 - index); +// MD5Update(context, PADDING, 0, padLen); +// +// /* Append length (before padding) */ +// MD5Update(context, bits, 0, 8); +// +// /* Store state in digest */ +// Encode(digest, context.state, 16); +// +// /* Zeroize sensitive information. */ +// //context.Clear(); +// } +// +// /* MD5 basic transformation. Transforms state based on block. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void MD5Transform(uint[] state, byte[] block, uint blockIndex) +// private static void MD5Transform(int[] state, byte[] block, int blockIndex) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint a = state[0], b = state[1], c = state[2], d = state[3]; +// int a = state[0], b = state[1], c = state[2], d = state[3]; +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: uint[] x = new uint[16]; +// int[] x = new int[16]; +// +// Decode(x, block, blockIndex, 64); +// +// /* Round 1 */ +// mtapi.mt5.RefObject tempRef_a = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref a, b, c, d, x[0], S11, 0xd76aa478); +// FF(tempRef_a, b, c, d, x[0], S11, 0xd76aa478); // 1 +// a = tempRef_a.argValue; +// mtapi.mt5.RefObject tempRef_d = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref d, a, b, c, x[1], S12, 0xe8c7b756); +// FF(tempRef_d, a, b, c, x[1], S12, 0xe8c7b756); // 2 +// d = tempRef_d.argValue; +// mtapi.mt5.RefObject tempRef_c = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref c, d, a, b, x[2], S13, 0x242070db); +// FF(tempRef_c, d, a, b, x[2], S13, 0x242070db); // 3 +// c = tempRef_c.argValue; +// mtapi.mt5.RefObject tempRef_b = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref b, c, d, a, x[3], S14, 0xc1bdceee); +// FF(tempRef_b, c, d, a, x[3], S14, 0xc1bdceee); // 4 +// b = tempRef_b.argValue; +// mtapi.mt5.RefObject tempRef_a2 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref a, b, c, d, x[4], S11, 0xf57c0faf); +// FF(tempRef_a2, b, c, d, x[4], S11, 0xf57c0faf); // 5 +// a = tempRef_a2.argValue; +// mtapi.mt5.RefObject tempRef_d2 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref d, a, b, c, x[5], S12, 0x4787c62a); +// FF(tempRef_d2, a, b, c, x[5], S12, 0x4787c62a); // 6 +// d = tempRef_d2.argValue; +// mtapi.mt5.RefObject tempRef_c2 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref c, d, a, b, x[6], S13, 0xa8304613); +// FF(tempRef_c2, d, a, b, x[6], S13, 0xa8304613); // 7 +// c = tempRef_c2.argValue; +// mtapi.mt5.RefObject tempRef_b2 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref b, c, d, a, x[7], S14, 0xfd469501); +// FF(tempRef_b2, c, d, a, x[7], S14, 0xfd469501); // 8 +// b = tempRef_b2.argValue; +// mtapi.mt5.RefObject tempRef_a3 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref a, b, c, d, x[8], S11, 0x698098d8); +// FF(tempRef_a3, b, c, d, x[8], S11, 0x698098d8); // 9 +// a = tempRef_a3.argValue; +// mtapi.mt5.RefObject tempRef_d3 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref d, a, b, c, x[9], S12, 0x8b44f7af); +// FF(tempRef_d3, a, b, c, x[9], S12, 0x8b44f7af); // 10 +// d = tempRef_d3.argValue; +// mtapi.mt5.RefObject tempRef_c3 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref c, d, a, b, x[10], S13, 0xffff5bb1); +// FF(tempRef_c3, d, a, b, x[10], S13, 0xffff5bb1); // 11 +// c = tempRef_c3.argValue; +// mtapi.mt5.RefObject tempRef_b3 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref b, c, d, a, x[11], S14, 0x895cd7be); +// FF(tempRef_b3, c, d, a, x[11], S14, 0x895cd7be); // 12 +// b = tempRef_b3.argValue; +// mtapi.mt5.RefObject tempRef_a4 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref a, b, c, d, x[12], S11, 0x6b901122); +// FF(tempRef_a4, b, c, d, x[12], S11, 0x6b901122); // 13 +// a = tempRef_a4.argValue; +// mtapi.mt5.RefObject tempRef_d4 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref d, a, b, c, x[13], S12, 0xfd987193); +// FF(tempRef_d4, a, b, c, x[13], S12, 0xfd987193); // 14 +// d = tempRef_d4.argValue; +// mtapi.mt5.RefObject tempRef_c4 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref c, d, a, b, x[14], S13, 0xa679438e); +// FF(tempRef_c4, d, a, b, x[14], S13, 0xa679438e); // 15 +// c = tempRef_c4.argValue; +// mtapi.mt5.RefObject tempRef_b4 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: FF(ref b, c, d, a, x[15], S14, 0x49b40821); +// FF(tempRef_b4, c, d, a, x[15], S14, 0x49b40821); // 16 +// b = tempRef_b4.argValue; +// +// /* Round 2 */ +// mtapi.mt5.RefObject tempRef_a5 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref a, b, c, d, x[1], S21, 0xf61e2562); +// GG(tempRef_a5, b, c, d, x[1], S21, 0xf61e2562); // 17 +// a = tempRef_a5.argValue; +// mtapi.mt5.RefObject tempRef_d5 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref d, a, b, c, x[6], S22, 0xc040b340); +// GG(tempRef_d5, a, b, c, x[6], S22, 0xc040b340); // 18 +// d = tempRef_d5.argValue; +// mtapi.mt5.RefObject tempRef_c5 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref c, d, a, b, x[11], S23, 0x265e5a51); +// GG(tempRef_c5, d, a, b, x[11], S23, 0x265e5a51); // 19 +// c = tempRef_c5.argValue; +// mtapi.mt5.RefObject tempRef_b5 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref b, c, d, a, x[0], S24, 0xe9b6c7aa); +// GG(tempRef_b5, c, d, a, x[0], S24, 0xe9b6c7aa); // 20 +// b = tempRef_b5.argValue; +// mtapi.mt5.RefObject tempRef_a6 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref a, b, c, d, x[5], S21, 0xd62f105d); +// GG(tempRef_a6, b, c, d, x[5], S21, 0xd62f105d); // 21 +// a = tempRef_a6.argValue; +// mtapi.mt5.RefObject tempRef_d6 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref d, a, b, c, x[10], S22, 0x02441453); +// GG(tempRef_d6, a, b, c, x[10], S22, 0x02441453); // 22 +// d = tempRef_d6.argValue; +// mtapi.mt5.RefObject tempRef_c6 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref c, d, a, b, x[15], S23, 0xd8a1e681); +// GG(tempRef_c6, d, a, b, x[15], S23, 0xd8a1e681); // 23 +// c = tempRef_c6.argValue; +// mtapi.mt5.RefObject tempRef_b6 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref b, c, d, a, x[4], S24, 0xe7d3fbc8); +// GG(tempRef_b6, c, d, a, x[4], S24, 0xe7d3fbc8); // 24 +// b = tempRef_b6.argValue; +// mtapi.mt5.RefObject tempRef_a7 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref a, b, c, d, x[9], S21, 0x21e1cde6); +// GG(tempRef_a7, b, c, d, x[9], S21, 0x21e1cde6); // 25 +// a = tempRef_a7.argValue; +// mtapi.mt5.RefObject tempRef_d7 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref d, a, b, c, x[14], S22, 0xc33707d6); +// GG(tempRef_d7, a, b, c, x[14], S22, 0xc33707d6); // 26 +// d = tempRef_d7.argValue; +// mtapi.mt5.RefObject tempRef_c7 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref c, d, a, b, x[3], S23, 0xf4d50d87); +// GG(tempRef_c7, d, a, b, x[3], S23, 0xf4d50d87); // 27 +// c = tempRef_c7.argValue; +// mtapi.mt5.RefObject tempRef_b7 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref b, c, d, a, x[8], S24, 0x455a14ed); +// GG(tempRef_b7, c, d, a, x[8], S24, 0x455a14ed); // 28 +// b = tempRef_b7.argValue; +// mtapi.mt5.RefObject tempRef_a8 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref a, b, c, d, x[13], S21, 0xa9e3e905); +// GG(tempRef_a8, b, c, d, x[13], S21, 0xa9e3e905); // 29 +// a = tempRef_a8.argValue; +// mtapi.mt5.RefObject tempRef_d8 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref d, a, b, c, x[2], S22, 0xfcefa3f8); +// GG(tempRef_d8, a, b, c, x[2], S22, 0xfcefa3f8); // 30 +// d = tempRef_d8.argValue; +// mtapi.mt5.RefObject tempRef_c8 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref c, d, a, b, x[7], S23, 0x676f02d9); +// GG(tempRef_c8, d, a, b, x[7], S23, 0x676f02d9); // 31 +// c = tempRef_c8.argValue; +// mtapi.mt5.RefObject tempRef_b8 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: GG(ref b, c, d, a, x[12], S24, 0x8d2a4c8a); +// GG(tempRef_b8, c, d, a, x[12], S24, 0x8d2a4c8a); // 32 +// b = tempRef_b8.argValue; +// +// /* Round 3 */ +// mtapi.mt5.RefObject tempRef_a9 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref a, b, c, d, x[5], S31, 0xfffa3942); +// HH(tempRef_a9, b, c, d, x[5], S31, 0xfffa3942); // 33 +// a = tempRef_a9.argValue; +// mtapi.mt5.RefObject tempRef_d9 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref d, a, b, c, x[8], S32, 0x8771f681); +// HH(tempRef_d9, a, b, c, x[8], S32, 0x8771f681); // 34 +// d = tempRef_d9.argValue; +// mtapi.mt5.RefObject tempRef_c9 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref c, d, a, b, x[11], S33, 0x6d9d6122); +// HH(tempRef_c9, d, a, b, x[11], S33, 0x6d9d6122); // 35 +// c = tempRef_c9.argValue; +// mtapi.mt5.RefObject tempRef_b9 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref b, c, d, a, x[14], S34, 0xfde5380c); +// HH(tempRef_b9, c, d, a, x[14], S34, 0xfde5380c); // 36 +// b = tempRef_b9.argValue; +// mtapi.mt5.RefObject tempRef_a10 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref a, b, c, d, x[1], S31, 0xa4beea44); +// HH(tempRef_a10, b, c, d, x[1], S31, 0xa4beea44); // 37 +// a = tempRef_a10.argValue; +// mtapi.mt5.RefObject tempRef_d10 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref d, a, b, c, x[4], S32, 0x4bdecfa9); +// HH(tempRef_d10, a, b, c, x[4], S32, 0x4bdecfa9); // 38 +// d = tempRef_d10.argValue; +// mtapi.mt5.RefObject tempRef_c10 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref c, d, a, b, x[7], S33, 0xf6bb4b60); +// HH(tempRef_c10, d, a, b, x[7], S33, 0xf6bb4b60); // 39 +// c = tempRef_c10.argValue; +// mtapi.mt5.RefObject tempRef_b10 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref b, c, d, a, x[10], S34, 0xbebfbc70); +// HH(tempRef_b10, c, d, a, x[10], S34, 0xbebfbc70); // 40 +// b = tempRef_b10.argValue; +// mtapi.mt5.RefObject tempRef_a11 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref a, b, c, d, x[13], S31, 0x289b7ec6); +// HH(tempRef_a11, b, c, d, x[13], S31, 0x289b7ec6); // 41 +// a = tempRef_a11.argValue; +// mtapi.mt5.RefObject tempRef_d11 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref d, a, b, c, x[0], S32, 0xeaa127fa); +// HH(tempRef_d11, a, b, c, x[0], S32, 0xeaa127fa); // 42 +// d = tempRef_d11.argValue; +// mtapi.mt5.RefObject tempRef_c11 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref c, d, a, b, x[3], S33, 0xd4ef3085); +// HH(tempRef_c11, d, a, b, x[3], S33, 0xd4ef3085); // 43 +// c = tempRef_c11.argValue; +// mtapi.mt5.RefObject tempRef_b11 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref b, c, d, a, x[6], S34, 0x04881d05); +// HH(tempRef_b11, c, d, a, x[6], S34, 0x04881d05); // 44 +// b = tempRef_b11.argValue; +// mtapi.mt5.RefObject tempRef_a12 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref a, b, c, d, x[9], S31, 0xd9d4d039); +// HH(tempRef_a12, b, c, d, x[9], S31, 0xd9d4d039); // 45 +// a = tempRef_a12.argValue; +// mtapi.mt5.RefObject tempRef_d12 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref d, a, b, c, x[12], S32, 0xe6db99e5); +// HH(tempRef_d12, a, b, c, x[12], S32, 0xe6db99e5); // 46 +// d = tempRef_d12.argValue; +// mtapi.mt5.RefObject tempRef_c12 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref c, d, a, b, x[15], S33, 0x1fa27cf8); +// HH(tempRef_c12, d, a, b, x[15], S33, 0x1fa27cf8); // 47 +// c = tempRef_c12.argValue; +// mtapi.mt5.RefObject tempRef_b12 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: HH(ref b, c, d, a, x[2], S34, 0xc4ac5665); +// HH(tempRef_b12, c, d, a, x[2], S34, 0xc4ac5665); // 48 +// b = tempRef_b12.argValue; +// +// /* Round 4 */ +// mtapi.mt5.RefObject tempRef_a13 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref a, b, c, d, x[0], S41, 0xf4292244); +// II(tempRef_a13, b, c, d, x[0], S41, 0xf4292244); // 49 +// a = tempRef_a13.argValue; +// mtapi.mt5.RefObject tempRef_d13 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref d, a, b, c, x[7], S42, 0x432aff97); +// II(tempRef_d13, a, b, c, x[7], S42, 0x432aff97); // 50 +// d = tempRef_d13.argValue; +// mtapi.mt5.RefObject tempRef_c13 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref c, d, a, b, x[14], S43, 0xab9423a7); +// II(tempRef_c13, d, a, b, x[14], S43, 0xab9423a7); // 51 +// c = tempRef_c13.argValue; +// mtapi.mt5.RefObject tempRef_b13 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref b, c, d, a, x[5], S44, 0xfc93a039); +// II(tempRef_b13, c, d, a, x[5], S44, 0xfc93a039); // 52 +// b = tempRef_b13.argValue; +// mtapi.mt5.RefObject tempRef_a14 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref a, b, c, d, x[12], S41, 0x655b59c3); +// II(tempRef_a14, b, c, d, x[12], S41, 0x655b59c3); // 53 +// a = tempRef_a14.argValue; +// mtapi.mt5.RefObject tempRef_d14 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref d, a, b, c, x[3], S42, 0x8f0ccc92); +// II(tempRef_d14, a, b, c, x[3], S42, 0x8f0ccc92); // 54 +// d = tempRef_d14.argValue; +// mtapi.mt5.RefObject tempRef_c14 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref c, d, a, b, x[10], S43, 0xffeff47d); +// II(tempRef_c14, d, a, b, x[10], S43, 0xffeff47d); // 55 +// c = tempRef_c14.argValue; +// mtapi.mt5.RefObject tempRef_b14 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref b, c, d, a, x[1], S44, 0x85845dd1); +// II(tempRef_b14, c, d, a, x[1], S44, 0x85845dd1); // 56 +// b = tempRef_b14.argValue; +// mtapi.mt5.RefObject tempRef_a15 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref a, b, c, d, x[8], S41, 0x6fa87e4f); +// II(tempRef_a15, b, c, d, x[8], S41, 0x6fa87e4f); // 57 +// a = tempRef_a15.argValue; +// mtapi.mt5.RefObject tempRef_d15 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref d, a, b, c, x[15], S42, 0xfe2ce6e0); +// II(tempRef_d15, a, b, c, x[15], S42, 0xfe2ce6e0); // 58 +// d = tempRef_d15.argValue; +// mtapi.mt5.RefObject tempRef_c15 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref c, d, a, b, x[6], S43, 0xa3014314); +// II(tempRef_c15, d, a, b, x[6], S43, 0xa3014314); // 59 +// c = tempRef_c15.argValue; +// mtapi.mt5.RefObject tempRef_b15 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref b, c, d, a, x[13], S44, 0x4e0811a1); +// II(tempRef_b15, c, d, a, x[13], S44, 0x4e0811a1); // 60 +// b = tempRef_b15.argValue; +// mtapi.mt5.RefObject tempRef_a16 = new mtapi.mt5.RefObject(a); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref a, b, c, d, x[4], S41, 0xf7537e82); +// II(tempRef_a16, b, c, d, x[4], S41, 0xf7537e82); // 61 +// a = tempRef_a16.argValue; +// mtapi.mt5.RefObject tempRef_d16 = new mtapi.mt5.RefObject(d); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref d, a, b, c, x[11], S42, 0xbd3af235); +// II(tempRef_d16, a, b, c, x[11], S42, 0xbd3af235); // 62 +// d = tempRef_d16.argValue; +// mtapi.mt5.RefObject tempRef_c16 = new mtapi.mt5.RefObject(c); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref c, d, a, b, x[2], S43, 0x2ad7d2bb); +// II(tempRef_c16, d, a, b, x[2], S43, 0x2ad7d2bb); // 63 +// c = tempRef_c16.argValue; +// mtapi.mt5.RefObject tempRef_b16 = new mtapi.mt5.RefObject(b); +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: II(ref b, c, d, a, x[9], S44, 0xeb86d391); +// II(tempRef_b16, c, d, a, x[9], S44, 0xeb86d391); // 64 +// b = tempRef_b16.argValue; +// +// state[0] += a; +// state[1] += b; +// state[2] += c; +// state[3] += d; +// +// /* Zeroize sensitive information. */ +// Arrays.fill(x, 0); +// } +// +// /* Encodes input (UINT4) into output (unsigned char). Assumes len is +// a multiple of 4. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void Encode(byte[] output, uint[] input, uint len) +// private static void Encode(byte[] output, int[] input, int len) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: for (uint i = 0, j = 0; j < len; i++, j += 4) +// for (int i = 0, j = 0; j < len; i++, j += 4) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: output[j] = (byte)(input[i] & 0xff); +// output[j] = (byte)(input[i] & 0xff); +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: output[j + 1] = (byte)((input[i] >> 8) & 0xff); +// output[j + 1] = (byte)((input[i] >>> 8) & 0xff); +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: output[j + 2] = (byte)((input[i] >> 16) & 0xff); +// output[j + 2] = (byte)((input[i] >>> 16) & 0xff); +//// C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right +// shift operator since the left operand was originally of an unsigned type, but you should confirm +// this replacement: +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: output[j + 3] = (byte)((input[i] >> 24) & 0xff); +// output[j + 3] = (byte)((input[i] >>> 24) & 0xff); +// } +// } +// +// /* Decodes input (unsigned char) into output (UINT4). Assumes len is +// a multiple of 4. */ +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: private static void Decode(uint[] output, byte[] input, uint inputIndex, uint +// len) +// private static void Decode(int[] output, byte[] input, int inputIndex, int len) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: for (uint i = 0, j = 0; j < len; i++, j += 4) +// for (int i = 0, j = 0; j < len; i++, j += 4) +// { +//// C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: +//// ORIGINAL LINE: output[i] = ((uint)input[inputIndex + j]) | (((uint)input[inputIndex + j + 1]) +// << 8) | (((uint)input[inputIndex + j + 2]) << 16) | (((uint)input[inputIndex + j + 3]) << 24); +// output[i] = ((int)input[inputIndex + j]) | (((int)input[inputIndex + j + 1]) << 8) | +// (((int)input[inputIndex + j + 2]) << 16) | (((int)input[inputIndex + j + 3]) << 24); +// } +// } +// } diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT4Status.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT4Status.java new file mode 100644 index 00000000000..2b01f0a55c2 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT4Status.java @@ -0,0 +1,69 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum MT4Status { + OK_ANSWER(0), + OK_REQUEST(1), + COMMON_ERROR(2), + INVALID_PARAM(3), + SERVER_BUSY(4), + OLD_VERSION(5), + NO_CONNECT(6), + NOT_ENOUGH_RIGHTS(7), + TOO_FREQUENT_REQUEST(8), + SECRET_KEY_REQUIRED(0xD), + INVALID_ONETIME_PASSWORD(0xE), + ACCOUNT_DISABLED(0x40), + INVALID_ACCOUNT(0x41), + TRADE_TIMEOUT(0x80), + INVALID_PRICES(0x81), + INVALID_SL_TP(0x82), + INVALID_VOLUME(0x83), + MARKET_CLOSED(0x84), + TRADE_DISABLED(0x85), + NOT_MONEY(0x86), + PRICE_CHANGED(0x87), + OFF_QUOTES(0x88), + BROKER_BUSY(0x89), + REQUOTE(0x8A), + ORDER_LOCKED(0x8B), + LONG_POS_ALLOWED(0x8C), + TOO_MANY_REQUESTS(0x8D), + ORDER_ACCEPTED(0x8E), + ORDER_IN_PROCESS(0x8F), + REQUEST_CANCELLED(0x90), + MODIFICATIONS_DENIED(0x91), + TRADE_CONTEXT_BUSY(0x92), + EXPIRATION_DISABLED(0x93), + TOO_MANY_ORDERS(0x94), + HEDGE_PROHIBITED(0x95), + RPROHIBITED_FIFO(0x96); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (MT4Status.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private MT4Status(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static MT4Status forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT5API.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT5API.java new file mode 100644 index 00000000000..7fcabea6ad4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MT5API.java @@ -0,0 +1,1549 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.math.BigDecimal; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class MT5API { + + public ArrayList LoginIdExServerUrls = + new ArrayList(Arrays.asList("http://loginid-mt5.mtapi.io")); + + /** + * Cluster general information. + * + * @return + */ + public ServerRec ClusterSummary() { + return ServerDetails.getKey(); + } + + /** Local time of last message recieved from server */ + public LocalDateTime TimeOfLastMessageFromServer; + + /** Cluster members */ + public ArrayList> ClusterMembers() { + return ServerDetails.getValue(); + } + + boolean TimeoutDuringConnect; + String Guid = "1288942f-aadb-4d98-8cc1-c06f33730d76"; + public int LoginIdWebServerTimeout = 10000; + + /** Manually or EA */ + public PlacedType PlacedType = + io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.PlacedType.Manually; + + private static HashMap Clients = new HashMap(); + + public ProcessEvents ProcessEventsIn = ProcessEvents.ThreadPool; + + Queue ProgressWaiters = new ConcurrentLinkedQueue(); + + Queue UpdateWaiters = new ConcurrentLinkedQueue(); + + /** Account currency */ + public final String AccountCurrency() { + return Symbols.Base.Currency; + } + + /** Company name */ + public final String AccountCompanyName() { + return Symbols.Base.CompanyName; + } + + /** Netting or hedging */ + public final AccMethod AccountMethod() { + return Symbols.Base.AccMethod; + } + + /** Force update order profits */ + public final void UpdateProfits() throws IOException { + for (Order item : GetOpenedOrders()) { + if (item.OrderType == OrderType.Buy || item.OrderType == OrderType.Sell) { + while (GetQuote(item.Symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + Quote quote = GetQuote(item.Symbol); + OrderProfit.Update(item, quote.Bid, quote.Ask); + } + } + } + + /** Account used margin */ + public final double AccountMargin() throws IOException { + HashMap syms = new HashMap(); + for (Order order : GetOpenedOrders()) { + if (!syms.containsKey(order.Symbol)) { + syms.put(order.Symbol, new SymbolMargin(this, order.Symbol)); + } + if (order.DealInternalIn != null) { + syms.get(order.Symbol).AcceptDeal(order.DealInternalIn); + } else if (order.OrderInternal != null) { + syms.get(order.Symbol).AcceptOrder(order.OrderInternal, false); + } + } + double sum = 0; + for (SymbolMargin item : syms.values()) { + sum += item.GetTradeMargin(); + } + return sum; + } + + /** Account free margin */ + public final double AccountFreeMargin() throws IOException { + return AccountEquity() - AccountMargin(); + } + + /** Maximum ms to wait for execution */ + public int ExecutionTimeout = 30000; + + private static int RequestId = 1; + private static final Object RequestIdLock = new Object(); + + public AccountRec Account; + + /** + * Get uniq request ID for async trading + * + * @return + */ + public final int GetRequestId() { + synchronized (RequestIdLock) { + return RequestId++; + } + } + + /** + * Send order and don't wait execution. Use OnOrderProgress event to get result. + * + * @param requestId Uniq temporary ID that can be used before ticket would be assigned. You can + * use GetRequestID() + * @param symbol Symbol + * @param lots Lots + * @param price Price + * @param type Order type + * @param sl Stop Loss + * @param tp Take Profit + * @param deviation Max deviation from specified price also known as Slppage + * @param comment String comment + * @param expertID Also known as magic number + * @param fillPolicy Fill policy depends on symbol settings on broker + */ + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy) + throws IOException { + OrderSendAsync( + requestId, symbol, lots, price, type, sl, tp, deviation, comment, expertID, fillPolicy, 0); + } + + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID) + throws IOException { + OrderSendAsync( + requestId, + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + FillPolicy.FillOrKill, + 0); + } + + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment) + throws IOException { + OrderSendAsync( + requestId, + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + 0, + FillPolicy.FillOrKill, + 0); + } + + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation) + throws IOException { + OrderSendAsync( + requestId, symbol, lots, price, type, sl, tp, deviation, null, 0, FillPolicy.FillOrKill, 0); + } + + public final void OrderSendAsync( + int requestId, String symbol, double lots, double price, OrderType type, double sl, double tp) + throws IOException { + OrderSendAsync( + requestId, symbol, lots, price, type, sl, tp, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final void OrderSendAsync( + int requestId, String symbol, double lots, double price, OrderType type, double sl) + throws IOException { + OrderSendAsync( + requestId, symbol, lots, price, type, sl, 0, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final void OrderSendAsync( + int requestId, String symbol, double lots, double price, OrderType type) throws IOException { + OrderSendAsync( + requestId, symbol, lots, price, type, 0, 0, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit) + throws IOException { + OrderSendAsync( + requestId, + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + stoplimit, + null); + } + + public final void OrderSendAsync( + int requestId, + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit, + LocalDateTime expiration) + throws IOException { + TradeRequest req = new TradeRequest(); + req.Flags &= ~0x100; + req.Flags &= ~0x200; + req.PlacedType = PlacedType; + req.Login = User; + req.Digits = Symbols.GetInfo(symbol).Digits; + + req.OrderPrice = stoplimit; + req.Price = price; + req.Lots = BigDecimal.valueOf(lots).multiply(new BigDecimal(100000000)).longValue(); + req.Currency = symbol; + req.RequestId = requestId; + if (type == OrderType.Buy || type == OrderType.Sell) + req.TradeType = TradeType.forValue(Symbols.GetGroup(symbol).TradeType.getValue() + 1); + else req.TradeType = TradeType.SetOrder; + req.OrderType = type; + req.StopLoss = sl; + req.TakeProfit = tp; + req.Deviation = deviation; + req.Comment = comment; + req.ExpertId = expertID; + req.FillPolicy = fillPolicy; + if (expiration != null) { + req.ExpirationType = ExpirationType.Specified; + req.ExpirationTime = ConvertTo.Long(expiration); + } + (new OrderSender(Connection)).Send(req); + } + + /** + * Send order and don't wait execution. Use OnOrderProgress event to get result. + * + * @param symbol Symbol + * @param lots Lots + * @param price Price + * @param type Order type + * @param sl Stop Loss + * @param tp Take Profit + * @param deviation Max deviation from specified price also known as Slppage + * @param comment String comment + * @param expertID Also known as magic number + * @param fillPolicy Fill policy depends on symbol settings on broker + */ + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy) + throws IOException { + return OrderSend( + symbol, lots, price, type, sl, tp, deviation, comment, expertID, fillPolicy, 0); + } + + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit) + throws IOException { + return OrderSend( + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + stoplimit, + null); + } + + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID) + throws IOException { + return OrderSend( + symbol, lots, price, type, sl, tp, deviation, comment, expertID, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment) + throws IOException { + return OrderSend( + symbol, lots, price, type, sl, tp, deviation, comment, 0, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation) + throws IOException { + return OrderSend( + symbol, lots, price, type, sl, tp, deviation, null, 0, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend( + String symbol, double lots, double price, OrderType type, double sl, double tp) + throws IOException { + return OrderSend(symbol, lots, price, type, sl, tp, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend(String symbol, double lots, double price, OrderType type, double sl) + throws IOException { + return OrderSend(symbol, lots, price, type, sl, 0, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend(String symbol, double lots, double price, OrderType type) + throws IOException { + return OrderSend(symbol, lots, price, type, 0, 0, 0, null, 0, FillPolicy.FillOrKill, 0); + } + + public final Order OrderSend( + String symbol, + double lots, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit, + LocalDateTime expiration) + throws IOException { + int id = GetRequestId(); + Order order; + if (type == OrderType.Buy || type == OrderType.Sell) { + MarketOpenWaiter waiter = new MarketOpenWaiter(this, id, ExecutionTimeout); + OrderSendAsync( + id, + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + stoplimit, + expiration); + order = waiter.Wait(); + } else { + PendingOpenWaiter waiter = new PendingOpenWaiter(this, id, ExecutionTimeout); + OrderSendAsync( + id, + symbol, + lots, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + stoplimit, + expiration); + order = waiter.Wait(); + } + if (AccountMethod() == AccMethod.Hedging) { + Order value = Orders.Opened.get(order.Ticket); + if (value != null) value.Update(order); + else Orders.Opened.put(order.Ticket, order); + } + return order; + } + + /** + * Send order close request and don't wait execution. Use OnOrderProgress event to get result. + * + * @param requestId Uniq temporary ID that can be used before ticket would be assigned. You can + * use GetID() + * @param ticket Order ticket + * @param lots Symbol + * @param lots Volume + * @param price Price + * @param type Order type + * @param deviation Max deviation from specified price also known as Slppage + */ + public final void OrderCloseAsync( + int requestId, + long ticket, + String symbol, + double price, + double lots, + OrderType type, + long deviation) + throws IOException { + OrderCloseAsync(requestId, ticket, symbol, price, lots, type, deviation, FillPolicy.FillOrKill); + } + + public final void OrderCloseAsync( + int requestId, long ticket, String symbol, double price, double lots, OrderType type) + throws IOException { + OrderCloseAsync(requestId, ticket, symbol, price, lots, type, 0, FillPolicy.FillOrKill); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: public void OrderCloseAsync(int requestId, long ticket, string symbol, double + // price, double lots, OrderType type, ulong deviation = 0, FillPolicy fillPolicy = + // FillPolicy.FillOrKill) + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + public final void OrderCloseAsync( + int requestId, + long ticket, + String symbol, + double price, + double lots, + OrderType type, + long deviation, + FillPolicy fillPolicy) + throws IOException { + + TradeRequest req = new TradeRequest(); + req.Flags &= ~0x100; + req.Flags &= ~0x200; + req.PlacedType = PlacedType; + req.Login = User; + req.Digits = Symbols.GetInfo(symbol).Digits; + req.RequestId = requestId; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: req.Lots = (ulong)(lots * 100000000); + req.Lots = BigDecimal.valueOf(lots).multiply(new BigDecimal(100000000)).longValue(); + if (req.Lots == 0) { + throw new RuntimeException("Request Lots = 0 "); + } + req.Currency = symbol; + if (type == OrderType.Buy || type == OrderType.Sell) { + req.Price = price; + req.DealTicket = ticket; + req.FillPolicy = fillPolicy; + req.TradeType = TradeType.forValue(Symbols.GetGroup(symbol).TradeType.getValue() + 1); + if (type == OrderType.Buy) { + req.OrderType = OrderType.Sell; + } else if (type == OrderType.Sell) { + req.OrderType = OrderType.Buy; + } + req.Deviation = deviation; + // req.Flags = 2; + // MarketCloseRequests.Add(ticket, requestId); + } else { + req.OrderTicket = ticket; + req.TradeType = TradeType.CancelOrder; + req.OrderType = type; + // PendingCloseRequests.Add(ticket, requestId); + } + if (req.FillPolicy == null) req.FillPolicy = FillPolicy.FillOrKill; + (new OrderSender(Connection)).Send(req); + } + + /** + * Send order close request and wait for execution. + * + * @param ticket Order ticket + * @param symbol Symbol + * @param lots Volume + * @param price Price + * @param type Order type + * @param deviation Max deviation from specified price also known as Slppage + * @return Closed order + */ + public final Order OrderClose( + long ticket, String symbol, double price, double lots, OrderType type, long deviation) + throws IOException { + return OrderClose(ticket, symbol, price, lots, type, deviation, FillPolicy.FillOrKill); + } + + public final Order OrderClose( + long ticket, String symbol, double price, double lots, OrderType type) throws IOException { + return OrderClose(ticket, symbol, price, lots, type, 0, FillPolicy.FillOrKill); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: public Order OrderClose(long ticket, string symbol, double price, double lots, + // OrderType type, ulong deviation = 0, FillPolicy fillPolicy = FillPolicy.FillOrKill) + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + public final Order OrderClose( + long ticket, + String symbol, + double price, + double lots, + OrderType type, + long deviation, + FillPolicy fillPolicy) + throws IOException { + Order order; + if (type == OrderType.Buy || type == OrderType.Sell) { + int id = GetRequestId(); + MarketCloseWaiter waiter = new MarketCloseWaiter(this, id, ExecutionTimeout, ticket); + OrderCloseAsync(id, ticket, symbol, price, lots, type, deviation, fillPolicy); + order = waiter.Wait(); + } else { + int id = GetRequestId(); + PendingCloseWaiter waiter = new PendingCloseWaiter(this, id, ExecutionTimeout, ticket); + OrderCloseAsync(id, ticket, symbol, price, lots, type, deviation, fillPolicy); + order = waiter.Wait(); + } + return order; + } + + public final void OrderModify( + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy) + throws IOException { + OrderModify( + ticket, symbol, volume, price, type, sl, tp, deviation, comment, expertID, fillPolicy, 0); + } + + public final void OrderModify( + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID) + throws IOException { + OrderModify( + ticket, + symbol, + volume, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + FillPolicy.FillOrKill, + 0); + } + + public final void OrderModify( + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment) + throws IOException { + OrderModify( + ticket, + symbol, + volume, + price, + type, + sl, + tp, + deviation, + comment, + 0, + FillPolicy.FillOrKill, + 0); + } + + public final void OrderModify( + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation) + throws IOException { + OrderModify( + ticket, symbol, volume, price, type, sl, tp, deviation, "", 0, FillPolicy.FillOrKill, 0); + } + + public final void OrderModify( + long ticket, String symbol, double volume, double price, OrderType type, double sl, double tp) + throws IOException { + OrderModify(ticket, symbol, volume, price, type, sl, tp, 0, "", 0, FillPolicy.FillOrKill, 0); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: public void OrderModify(long ticket, string symbol, double volume, double price, + // OrderType type, double sl, double tp, ulong deviation = 0, string comment = "", long expertID = + // 0, FillPolicy fillPolicy = FillPolicy.FillOrKill, double stoplimit = 0) + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + public final void OrderModify( + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit) + throws IOException { + int id = GetRequestId(); + ModifyWaiter waiter = new ModifyWaiter(this, id, ExecutionTimeout, ticket); + OrderModifyAsync( + id, + ticket, + symbol, + volume, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + stoplimit); + waiter.Wait(); + } + + public final void OrderModifyAsync( + int requestId, + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy) + throws IOException { + OrderModifyAsync( + requestId, + ticket, + symbol, + volume, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + fillPolicy, + 0); + } + + public final void OrderModifyAsync( + int requestId, + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID) + throws IOException { + OrderModifyAsync( + requestId, + ticket, + symbol, + volume, + price, + type, + sl, + tp, + deviation, + comment, + expertID, + FillPolicy.FillOrKill, + 0); + } + + public final void OrderModifyAsync( + int requestId, + long ticket, + String symbol, + double volume, + double price, + OrderType type, + double sl, + double tp, + long deviation, + String comment, + long expertID, + FillPolicy fillPolicy, + double stoplimit) + throws IOException { + TradeRequest req = new TradeRequest(); + req.PlacedType = PlacedType; + req.Lots = BigDecimal.valueOf(volume).multiply(new BigDecimal(100000000)).longValue(); + req.Currency = symbol; + req.RequestId = requestId; + req.OrderPrice = stoplimit; + req.Price = price; + if (type == OrderType.Buy || type == OrderType.Sell) { + req.DealTicket = ticket; + req.TradeType = TradeType.ModifyDeal; + } else { + req.OrderTicket = ticket; + req.TradeType = TradeType.ModifyOrder; + } + req.OrderType = type; + req.StopLoss = sl; + req.TakeProfit = tp; + req.Deviation = deviation; + req.Comment = comment; + req.ExpertId = expertID; + req.FillPolicy = fillPolicy; + (new OrderSender(Connection)).Send(req); + } + + /** Symbols information */ + public final Symbols Symbols = new Symbols(); + + //// + /** New quote */ + public Event OnQuote = new Event(); + + //// + /** Order history */ + public Event OnTradeHistory = new Event(); + + //// + /** Order update notification from server */ + public Event OnOrderUpdate = new Event(); + + //// + /** Connect progress notification */ + public Event OnConnectProgress = new Event(); + + /** Open/close progress of the order before ticket number assign. */ + public Event OnOrderProgress = new Event(); + + /// + /// Quote history event. Use RequestQuoteHistory to request history. + /// + public Event OnQuoteHistory = new Event<>(); + + /** Server time, refreshing goes with using incoming quotes. */ + public java.time.LocalDateTime ServerTime() { + return LocalDateTime.now(ZoneOffset.UTC) + .plusMinutes(ServerDetails.getKey().TimeZone) + .plusHours(ServerDetails.getKey().DST); + } + + /** + * Server time offset from UTC in minutes + * + * @return + */ + public int ServrTimeZoneInMinutes() { + return ServerDetails.getKey().TimeZone + 60 * ServerDetails.getKey().DST; + } + + /** + * Is imvestor mode + * + * @return + */ + public boolean IsInvestor() { + int InvestorFlag = 8; + if ((Account.TradeFlags & InvestorFlag) != 0) return true; + return false; + } + + // public Workaround Workaround; + public Map.Entry>> ServerDetails; + + /** Account profit */ + public final double AccountProfit() { + double sum = 0; + for (Order item : GetOpenedOrders()) { + sum += item.Profit + item.Commission + item.Swap; + } + return Math.round((sum) * Math.pow(10, 2)) / Math.pow(10, 2); + } + + /** Account equity. */ + public final double AccountEquity() { + return Math.round((Account.Balance + AccountProfit()) * Math.pow(10, 2)) / Math.pow(10, 2); + } + + /** Account balance. */ + public final double AccountBalance() { + if (Account == null) throw new RuntimeException("Not connected"); + return Account.Balance; + } + + /** Check connection state. */ + public final boolean Connected() { + if (CmdHandler == null) return false; + if (GotAccountInfo == false) return false; + if (CmdHandler.Stop) return false; + if (Connection == null) return false; + if (Connection.Sock == null) return false; + if (!Connection.Sock.isConnected()) return false; + // if (Duration.between(CmdHandler.LastMessageFromServer, LocalDateTime.now()).toMillis() > + // LastMessageFromServerTimeoutMs) + // return false; + return CmdHandler.getRunning(); + } + + public boolean GotAccountInfo = false; + + // C# TO JAVA CONVERTER TODO TASK: There is no preprocessor in Java: + // #if LoginDLL + // #else + + // #endif + + private Object ConnectLock = new Object(); + public Logger Log; + public java.time.LocalDateTime Time = java.time.LocalDateTime.MIN; + + CmdHandler CmdHandler; + Connection Connection; + Subscriber Subscriber; + OpnedClosedOrders Orders; + OrderHistory OrderHistory; + OrderProfit OrderProfit; + Connector Connector; + + /** User */ + public long User; + + /** Password */ + public String Password; + + /** Host */ + public String Host; + + /** Account currency */ + public int Port; + + /** Certificate *.pfx file */ + public byte[] PfxFile; + + /** Pfx file password */ + public String PfxFilePassword; + + public int _SleepTime; + + public static Server[] LoadServersDat(String path) throws IOException { + return (new ServersDatLoader()).Load(path); + } + + String guid = "1288942f-aadb-4d98-8cc1-c06f33730d76"; + + public MT5API(long user, String password, String host, int port) { + Trial.check(guid); + Log = new Logger(this); + User = user; + Password = password; + Host = host; + Port = port; + Connection = new Connection(this); + Subscriber = new Subscriber(this, Log); + Orders = new OpnedClosedOrders(this, Log); + OrderHistory = new OrderHistory(this, Log); + OrderProfit = new OrderProfit(this); + Connector = new Connector(this); + } + + public MT5API( + long user, String password, String host, int port, byte[] pfxFile, String pfxFilePassword) { + Trial.check(guid); + Log = new Logger(this); + User = user; + Password = password; + Host = host; + Port = port; + PfxFile = pfxFile; + PfxFilePassword = pfxFilePassword; + Connection = new Connection(this); + Subscriber = new Subscriber(this, Log); + Orders = new OpnedClosedOrders(this, Log); + OrderHistory = new OrderHistory(this, Log); + OrderProfit = new OrderProfit(this); + Connector = new Connector(this); + } + + /** + * Contruct size + * + * @param symbol Symbol + * @return + */ + public final double GetContaractSize(String symbol) { + return Symbols.GetInfo(symbol).ContractSize; + } + + /** + * Request closed orders. + * + * @param from Start time of history. + * @param to End time of history. + * @return Array of orders. + */ + public final void RequestDealHistory(java.time.LocalDateTime from, java.time.LocalDateTime to) + throws IOException { + Log.trace("RequestOrderHistory"); + if (!Connected()) { + throw new RuntimeException("Not connected"); + } + OrderHistory.Request(from, to, 0, 0, true); + } + + /** + * Reuest closed orders. + * + * @param year Required year. + * @param month Required month. + * @param exist Existing deals. + * @return Array of orders. + */ + public void RequestDealHistory(int year, int month, List exist) + throws ConnectException, IOException { + Log.trace("RequestOrderHistoryMonth"); + if (!Connected()) Connect(); + LocalDateTime start = LocalDateTime.of(year, month, 1, 0, 0); + LocalDateTime end = start.plusMonths(1).plusSeconds(-1); + if (exist == null) OrderHistory.Request(start, end, 0, 0, true); + else { + long max = 0; + for (DealInternal item : exist) if (item.HistoryTime > max) max = item.HistoryTime; + OrderHistory.Request(start, end, exist.size(), max, true); + } + } + + /** + * Reuest closed orders. + * + * @param year Required year. + * @param month Required month. + * @return Array of orders. + */ + public void RequestDealHistory(int year, int month) throws IOException, ConnectException { + RequestDealHistory(year, month, null); + } + + /** + * Request closed orders. + * + * @param from Start time of history. + * @param to End time of history. + * @return Array of orders. + */ + public final void RequestOrderHistory(java.time.LocalDateTime from, java.time.LocalDateTime to) + throws IOException { + Log.trace("RequestOrderHistory"); + if (!Connected()) { + throw new RuntimeException("Not connected"); + } + OrderHistory.Request(from, to, 0, 0, false); + } + + /** + * Reuest closed orders. + * + * @param year Required year. + * @param month Required month. + * @param exist Existing deals. + * @return Array of orders. + */ + public void RequestOrderHistory(int year, int month, List exist) + throws IOException, ConnectException { + Log.trace("RequestOrderHistoryMonth"); + if (!Connected()) Connect(); + LocalDateTime start = LocalDateTime.of(year, month, 1, 0, 0); + LocalDateTime end = start.plusMonths(1).plusSeconds(-1); + if (exist == null) OrderHistory.Request(start, end, 0, 0, false); + else { + long max = 0; + for (OrderInternal item : exist) if (item.HistoryTime > max) max = item.HistoryTime; + OrderHistory.Request(start, end, exist.size(), max, false); + } + } + + /** + * Reuest closed orders. + * + * @param year Required year. + * @param month Required month. + * @return Array of orders. + */ + public void RequestOrderHistory(int year, int month) throws IOException, ConnectException { + RequestOrderHistory(year, month, null); + } + + /** Milliseconds. Throw ConnectException if not connected at this period. */ + public int ConnectTimeout = 30000000; + + /** + * Connect to server. + * + * @throws ServerException Unable to login for some reason. + * @throws TimeoutException No reply from server. + */ + public final void Connect() throws ConnectException { + Exception ex = null; + try { + Time = LocalDateTime.MIN; + Connector.go(ConnectTimeout, Host, Port); + return; + } catch (Exception e) { + ex = e; + } + if (ServerDetails != null) { + for (Map.Entry item : ServerDetails.getValue()) { + try { + Map.Entry hostport = HostAndPort.Parse(item.getValue()[0].Address); + Connector.go(ConnectTimeout, hostport.getKey(), hostport.getValue()); + return; + } catch (Exception e) { + ex = e; + } + } + } + if (!Connected()) + if (ex != null) { + Connector.stop(); + throw new ConnectException(ex); + } + } + + public final void Disconnect() { + if (CmdHandler != null) { + CmdHandler.StopCmdHandler(); + try { + CmdHandler.Thread.join(1000); + } catch (InterruptedException e) { + } + } + } + + public final void OnConnectCall(Exception exception, ConnectProgress progress) { + ConnectEventArgs args = new ConnectEventArgs(); + args.Exception = exception; + args.Progress = progress; + OnConnectProgress.Invoke(this, args); + } + + // public int DisconnectTimeout { get; internal set; } + + public final void OnDisconnect(Exception ex) { + if (ex != null) { + Log.exception(ex); + } else { + Log.trace("onDisconnect"); + } + OnConnectCall(ex, ConnectProgress.Disconnect); + } + + public final void OnQuoteHistory(String symbol, List bars) { + QuoteHistoryEventArgs args = new QuoteHistoryEventArgs(); + args.Symbol = symbol; + args.Bars = bars; + OnQuoteHistory.Invoke(this, args); + } + + // private void onQuoteHist(object args) + // { + // try + // { + // if (OnQuoteHistory != null) + // OnQuoteHistory(this, (QuoteHistoryEventArgs)args); + // } + // catch (Exception ex) + // { + // Log.exception(ex); + // } + // } + + /** + * Subscribe trading instrument. + * + * @param symbol Symbol for trading. + * @throws Exception Not connected. + */ + public final void Subscribe(String symbol) throws IOException { + Log.trace("Subscribe"); + if (!Connected()) { + throw new RuntimeException("Not connected"); + } + Subscriber.Subscribe(symbol); + } + + /** + * Unsubscribe trading instrument. + * + * @param symbol Symbol for trading. + * @throws Exception Not connected. + */ + public final void Unsubscribe(String symbol) throws IOException { + Log.trace("Unsubscribe"); + if (!Connected()) { + throw new RuntimeException("Not connected"); + } + // if (CalculateTradeProps) + // { + // foreach (var order in GetOpenedOrders()) + // { + // if ((order.Type == Op.Buy || order.Type == Op.Sell) && order.Symbol == symbol) + // return; + // } + // } + Subscriber.Unsubscribe(symbol); + } + + /** + * Latest quote for the symbol. + * + * @param symbol Symbol for trading. + * @return Return null if no quotes for specified symbol avalible, otherwise return quote event + * arguments. + * @throws Exception Symbol not subscribed. + */ + public final Quote GetQuote(String symbol) throws IOException { + Quote q = Subscriber.GetQuote(symbol); + if (q != null) { + if (q.Bid == 0 || q.Ask == 0) { + return null; + } + } + return q; + } + + /** + * Latest quote for the symbol. + * + * @param symbol Symbol for trading. + * @return Return null if no quotes for specified symbol avalible, otherwise return quote event + * arguments. + * @throws Exception Symbol not subscribed. + */ + public Quote GetQuote(String symbol, int msTimeout) + throws IOException, TimeoutException, ConnectException { + if (!Connected()) Connect(); + if (!Symbols.Exist(symbol)) throw new RuntimeException("Symbol " + symbol + " not exist"); + LocalDateTime start = LocalDateTime.now(); + while (true) { + Quote quote = Subscriber.GetQuote(symbol); + if (quote != null) + if (quote.Bid == 0 || quote.Ask == 0) continue; + else return quote; + if (Duration.between(start, LocalDateTime.now()).toMillis() > msTimeout) + throw new TimeoutException("Cannot get quote in " + msTimeout + " ms", Log); + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + } + + public final Order[] GetOpenedOrders() { + return Orders.Opened.values().toArray(new Order[0]); + } + + public final Order[] GetCloseddOrders() { + return Orders.Closed.values().toArray(new Order[0]); + } + + public final void OnOrderHisotyCall(OrderHistoryEventArgs args) { + OnTradeHistory.Invoke(this, args); + } + + public final void OnQuoteCall(Quote quote) { + // ServerTime = quote.Time; + ThreadPool.QueueUserWorkItem(() -> OnCalcProfitThread(quote)); + OnQuote.Invoke(this, quote); + } + + private void OnCalcProfitThread(Object args) { + try { + Quote quote = (Quote) args; + // Profit.Calculate(quote); + for (Order item : GetOpenedOrders()) { + if (item.OrderType == OrderType.Buy || item.OrderType == OrderType.Sell) { + if (DotNetToJavaStringHelper.stringsEqual(item.Symbol, quote.Symbol)) { + try { + OrderProfit.Update(item, quote.Bid, quote.Ask); + } catch (RuntimeException | IOException ex) { + Log.exception(ex); + } + } + } + } + } catch (RuntimeException ex) { + Log.exception(ex); + } + } + + public final void OnOrderUpdateCall(OrderUpdate[] update) { + try { + for (OrderUpdate item : update) { + Orders.Api_OnOrderUpdate(this, item); + } + } catch (RuntimeException ex) { + Log.exception(ex); + } + try { + for (OrderUpdate item : update) { + for (OnOrderUpdate waiter : UpdateWaiters) { + waiter.invoke(this, item); + } + } + } catch (RuntimeException ex) { + Log.exception(ex); + } + try { + for (Order order : GetOpenedOrders()) + if (order.OrderType == OrderType.Buy || order.OrderType == OrderType.Sell) + Subscribe(order.Symbol); + } catch (Exception ex) { + Log.exception(ex); + } + OnOrderUpdate.Invoke(this, update); + } + + public final void OnOrderProgressCall(OrderProgress[] progr) { + try { + for (OrderProgress item : progr) { + for (OnOrderProgress waiter : ProgressWaiters) { + waiter.invoke(this, item); + } + } + } catch (RuntimeException ex) { + Log.exception(ex); + } + OnOrderProgress.Invoke(this, progr); + } + + public FillPolicy GetFillPolicy(String symbol, OrderType type) { + boolean pendingOrder = true; + if (type == OrderType.Buy || type == OrderType.Sell || type == OrderType.CloseBy) + pendingOrder = false; + FillPolicy fp; + SymGroup group = Symbols.GetGroup(symbol); + if (group.TradeType == ExecutionType.Request || group.TradeType == ExecutionType.Instant) { + if (pendingOrder) fp = FillPolicy.FlashFill; + else fp = FillPolicy.FillOrKill; + } else { + if (pendingOrder) fp = FillPolicy.FlashFill; + else if (group.FillPolicy == 2) fp = FillPolicy.ImmediateOrCancel; + else fp = FillPolicy.FillOrKill; // FOK or ANY + } + return fp; + } + + public String[] Subscriptions() { + return Subscriber.Subscriptions(); + } + + /// + /// Request 1 minute bar hsitory for one month back from specifeid date + /// + /// Symbol + /// Year + /// Month + /// Day + + /** + * Request 1 minute bar hsitory for one month back from specifeid date + * + * @param symbol + * @param year + * @param month + * @param day + */ + public void RequestQuoteHistoryMonth(String symbol, int year, int month, int day) + throws IOException { + if (!Symbols.Exist(symbol)) throw new RuntimeException(symbol + " not exist"); + QuoteHistory.ReqSend(Connection, symbol, toShort(4, 1, 1), toShort(day, month, year - 1970)); + } + + short toShort(int day, int month, int year) { + // [BitfieldLength(5)] + // public uint Day; + // [BitfieldLength(4)] + // public uint Month; + // [BitfieldLength(7)] + // public uint Year; + short res = (short) (day | (month << 5) | (year << 9)); + return res; + } + + /// + /// Request 1 minute bar hsitory for today + /// + /// Symbol + public void RequestQuoteHistoryToday(String symbol) throws IOException { + if (!Symbols.Exist(symbol)) throw new RuntimeException(symbol + " not exist"); + QuoteHistory.ReqStart(Connection, symbol, (byte) 33); + } + + public static List ConvertToTimeframe(List bars, int minutes) { + if (minutes > 60) + if (minutes % 60 != 0) + throw new RuntimeException("If timeframe > 60 it should be in whole hours"); + if (bars.size() == 0) return new LinkedList(); + int i = 0; + if (minutes <= 60) while (bars.get(i).Time.getMinute() % minutes > 0) i++; + List res = new LinkedList(); + Bar bar = new Bar(); + bar.OpenPrice = bars.get(0).OpenPrice; + bar.Time = bars.get(0).Time; + bar.LowPrice = bars.get(0).LowPrice; + LocalDateTime time = bars.get(0).Time.plusMinutes(minutes); + if (minutes == 1440) + time = LocalDateTime.of(time.getYear(), time.getMonth(), time.getDayOfMonth(), 0, 0, 0); + for (; i < bars.size(); i++) { + if (bars.get(i).Time.isAfter(time) + || bars.get(i).Time.isEqual(time) + || i == bars.size() - 1) { + bar.ClosePrice = bars.get(i - 1).ClosePrice; + res.add(bar); + bar = new Bar(); + bar.OpenPrice = bars.get(i).OpenPrice; + bar.Time = bars.get(i).Time; + bar.LowPrice = bars.get(i).LowPrice; + while (time.isBefore(bar.Time) || time.isEqual(bar.Time)) time = time.plusMinutes(minutes); + if (minutes == 1440) { + LocalDateTime nextday = bars.get(i).Time.plusHours(24); + time = + LocalDateTime.of( + nextday.getYear(), nextday.getMonth(), nextday.getDayOfMonth(), 0, 0, 0); + } + } + if (bars.get(i).HighPrice > bar.HighPrice) bar.HighPrice = bars.get(i).HighPrice; + if (bars.get(i).LowPrice < bar.LowPrice) bar.LowPrice = bars.get(i).LowPrice; + } + return res; + } + + /// + /// Tick value in base currency + /// + /// + /// + public double GetAskTickValue(Quote quote) throws IOException { + SymbolInfo i = Symbols.GetInfo(quote.Symbol); + new OrderProfit(this).UpdateSymbolTick(i, quote.Bid, quote.Ask); + return i.ask_tickvalue; + } + + /// + /// Tick value in base currency + /// + /// + /// + public double GetBidTickValue(Quote quote) throws IOException { + SymbolInfo i = Symbols.GetInfo(quote.Symbol); + new OrderProfit(this).UpdateSymbolTick(i, quote.Bid, quote.Ask); + return i.bid_tickvalue; + } + + /// + /// Tick value in base currency + /// + /// + /// + public double GetTickValue(String symbol) throws IOException, TimeoutException, ConnectException { + SymbolInfo i = Symbols.GetInfo(symbol); + Quote quote = GetQuote(symbol, 5000); + new OrderProfit(this).UpdateSymbolTick(i, quote.Bid, quote.Ask); + return (i.bid_tickvalue + i.ask_tickvalue) / 2; + } + + /** + * Estimate connection time in milliseconds + * + * @param host Host + * @param port Port + * @param timeoutMs Timeout in milliseconds + * @return Connection time in milliseconds + * @throws UnknownHostException If host is unknown + */ + public static int PingHost(String host, int port, int timeoutMs) throws UnknownHostException { + LocalDateTime start = LocalDateTime.now(); + int res = -1; + try { + try (Socket client = new Socket()) { + client.connect(new InetSocketAddress(host, port), timeoutMs); + res = (int) Duration.between(start, LocalDateTime.now()).toMillis(); + } + } catch (UnknownHostException ex) { + throw ex; + } catch (Exception ignored) { + } + return res; + } + + public boolean PingPong() { + try { + OrderClose(0, Symbols.Infos[0].Name, 0, -1, OrderType.Buy); + } catch (ServerException ex) { + return true; + } catch (Exception e) { + return false; + } + return false; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MailRecipient.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MailRecipient.java new file mode 100644 index 00000000000..46a4f1a9df6 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MailRecipient.java @@ -0,0 +1,21 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x88, CharSet = CharSet.Unicode)]*/ +public class MailRecipient extends FromBufReader { + /*[FieldOffset(0)]*/ public long Id; + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String Name; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 136; + MailRecipient st = new MailRecipient(); + st.Id = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Name = GetString(buf.Bytes(128)); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarginMode.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarginMode.java new file mode 100644 index 00000000000..173f6f05ef2 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarginMode.java @@ -0,0 +1,37 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum MarginMode { + MarginForex(0), + MarginFutures(1), + vMarginCFD(2), + MarginCFDIndex(3); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (MarginMode.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private MarginMode(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static MarginMode forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketCloseWaiter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketCloseWaiter.java new file mode 100644 index 00000000000..477ec638bd4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketCloseWaiter.java @@ -0,0 +1,73 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.concurrent.ConcurrentLinkedQueue; + +class MarketCloseWaiter implements OnOrderProgress, OnOrderUpdate { + private MT5API Client; + private int Id; + private int Timeout; + private ConcurrentLinkedQueue Progr = new ConcurrentLinkedQueue(); + private Order Order; + private long Ticket; + + public MarketCloseWaiter(MT5API client, int id, int timeout, long ticket) { + Client = client; + Id = id; + Timeout = timeout; + Client.ProgressWaiters.add(this); + Client.UpdateWaiters.add(this); + Ticket = ticket; + } + + public void invoke(MT5API sender, OrderUpdate update) { + if (update.Deal != null) { + if (update.Deal.PositionTicket == Ticket) { + Order = new Order(new DealInternal[] {update.Deal, update.OppositeDeal}, Id); + } + } + } + + public void invoke(MT5API sender, OrderProgress progress) { + if (progress.TradeRequest.RequestId == Id) { + Progr.add(progress); + } + } + + public Order Wait() { + try { + return WaitInternal(); + } finally { + Client.ProgressWaiters.remove(this); + Client.UpdateWaiters.remove(this); + Progr.clear(); + } + } + + public Order WaitInternal() { + java.time.LocalDateTime start = java.time.LocalDateTime.now(); + while (true) { + if (Duration.between(start, LocalDateTime.now()).toMillis() > Timeout) + throw new RuntimeException("Trade timeout"); + for (OrderProgress progr : Progr) { + Msg status = progr.TradeResult.Status; + if (status != Msg.REQUEST_ACCEPTED + && status != Msg.REQUEST_ON_WAY + && status != Msg.REQUEST_EXECUTED + && status != Msg.DONE + && status != Msg.ORDER_PLACED) { + throw new ServerException(status); + } + } + if (Order != null) { + Order.RequestId = Id; + return Order; + } + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketOpenWaiter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketOpenWaiter.java new file mode 100644 index 00000000000..86675c5d3e1 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MarketOpenWaiter.java @@ -0,0 +1,80 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.concurrent.ConcurrentLinkedQueue; + +class MarketOpenWaiter implements OnOrderProgress, OnOrderUpdate { + private MT5API Client; + private int Id; + private int Timeout; + private ConcurrentLinkedQueue Progr = new ConcurrentLinkedQueue(); + private ConcurrentLinkedQueue Updates = new ConcurrentLinkedQueue(); + private Order Order; + private long Ticket; + + public MarketOpenWaiter(MT5API client, int id, int timeout) { + Client = client; + Id = id; + Timeout = timeout; + Client.ProgressWaiters.add(this); + Client.UpdateWaiters.add(this); + } + + public void invoke(MT5API sender, OrderUpdate update) { + if (update.Deal != null) + if (Ticket == 0) Updates.add(update); + else if (update.Deal.OrderTicket == Ticket) + Order = new Order(new DealInternal[] {update.Deal}, Id); + } + + public void invoke(MT5API sender, OrderProgress progress) { + if (progress.TradeRequest.RequestId == Id) Progr.add(progress); + } + + public Order Wait() { + try { + return WaitInternal(); + } finally { + Client.ProgressWaiters.remove(this); + Client.UpdateWaiters.remove(this); + Progr.clear(); + Updates.clear(); + } + } + + Order WaitInternal() { + LocalDateTime start = LocalDateTime.now(); + while (true) { + if (Duration.between(start, LocalDateTime.now()).toMillis() > Timeout) { + LocalDateTime now = LocalDateTime.now(); + throw new RuntimeException("Trade timeout"); + } + for (OrderProgress progr : Progr) { + Msg status = progr.TradeResult.Status; + if (status != Msg.REQUEST_ACCEPTED + && status != Msg.REQUEST_ON_WAY + && status != Msg.REQUEST_EXECUTED + && status != Msg.DONE + && status != Msg.ORDER_PLACED) throw new ServerException(status); + } + for (OrderProgress progr : Progr) + if (progr.TradeResult.TicketNumber != 0) { + Ticket = progr.TradeResult.TicketNumber; + for (OrderUpdate update : Updates) + if (update.Deal.PositionTicket == Ticket || update.Deal.OrderTicket == Ticket) + Order = new Order(new DealInternal[] {update.Deal}, Id); + } + if (Order != null) { + Order.RequestId = Id; + if (Client.AccountMethod() == AccMethod.Hedging) + Client.Orders.Opened.put(Order.Ticket, Order); + return Order; + } + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ModifyWaiter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ModifyWaiter.java new file mode 100644 index 00000000000..57637bed4ed --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ModifyWaiter.java @@ -0,0 +1,67 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; + +public class ModifyWaiter implements OnOrderProgress { + private MT5API Client; + private int Id; + private int Timeout; + private ArrayList Progr = new ArrayList(); + private Order Order; + private long Ticket; + + public ModifyWaiter(MT5API client, int id, int timeout, long ticket) { + Client = client; + Id = id; + Timeout = timeout; + Ticket = ticket; + synchronized (Client.ProgressWaiters) { + Client.ProgressWaiters.add(this); + } + } + + public void invoke(MT5API sender, OrderProgress progress) { + if (progress.TradeRequest.RequestId == Id) { + synchronized (Progr) { + Progr.add(progress); + } + } + } + + public final Order Wait() { + java.time.LocalDateTime start = java.time.LocalDateTime.now(); + while (true) { + if (Duration.between(start, LocalDateTime.now()).toMillis() > Timeout) { + throw new RuntimeException("Trade timeout"); + } + if (Progr == null) { + continue; + } + synchronized (Progr) { + for (OrderProgress progr : Progr) { + Msg status = progr.TradeResult.Status; + if (status != Msg.REQUEST_ACCEPTED + && status != Msg.REQUEST_ON_WAY + && status != Msg.REQUEST_EXECUTED + && status != Msg.DONE + && status != Msg.ORDER_PLACED) { + throw new RuntimeException(status.toString()); + } + if (status == Msg.REQUEST_EXECUTED) { + synchronized (Client.ProgressWaiters) { + Client.ProgressWaiters.remove(this); + } + return Order; + } + } + } + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Msg.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Msg.java new file mode 100644 index 00000000000..62337a727cb --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Msg.java @@ -0,0 +1,181 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum Msg { + DONE(0), + OK(1), + COMMON_ERROR(2), + INVALID_PARAM(3), + INVALID_DATA(4), + DISK_ERROR(5), + MEMORY_ERROR(6), + NETWORK_ERROR(7), + NOT_PERMISSION(8), + OPERATION_TIMEOUT(9), + NO_CONNECTION(0xA), + SERVICE_NOT_AVAILABLE(0xB), + TOO_FREQUENT_REQUEST(0xC), + NOT_FOUND(0xD), + SERVER_SHUTDOWN(0xF), + OPERATION_CANCELED(0x10), + DUPLICATE_ATTEMPT(0x11), + INVALID_TERMINAL(0x3E8), + INVALID_ACCOUNT(0x3E9), + ACCOUNT_DISABLED(0x3EA), + ADVANCED_AUTHORIZATION(0x3EB), + CERTIFICATE_REQUIRED(0x3EC), + INVALID_CERTIFICATE(0x3ED), + CERTIFICATE_NOT_CONFIRM(0x3EE), + ATTEMPT_CONNECT_NON_ACCESS_SERVER(0x3EF), + INVALID_SERVER(0x3F0), + ONLY_UPDATES_AVAILABLE(0x3F1), + OLD_VERSION(0x3F2), + ACCOUNT_NOT_MANAGER_CONFIG(0x3F3), + UNALLOWED_IP_ADDRESS(0x3F4), + GROUP_NOT_INITIALIZED(0x3F5), + CERTIFICATE_GENERATION_DISABLED(0x3F6), + INVALID_SERVER_ID(0x3F7), + UNALLOWED_ADDRESS(0x3F8), + INVALID_SERVER_TYPE(0x3F9), + SERVER_BUSY(0x3FA), + INVALID_SERVER_CERTIFICATE(0x3FB), + UNKNOWN_ACCOUNT(0x3FC), + OLD_SERVER(0x3FD), + SERVER_LICENSE_LIMITATION(0x3FE), + MOBILE_TERMINAL_NOT_ALLOWED(0x3FF), + CONNECTION_NOT_PERMITTED(0x400), + DEMO_DISABLED(0x401), + PASSWORD_MUST_CHANGED(0x402), + INVALID_ONETIME_PASSWORD(0x403), + SECRET_KEY_REQUIRED(0x404), + MT4_MIGRATION_PASSWORD(0x405), + MT5_MIGRATION_PASSWORD(0x406), + LAST_CONFIG_CANT_DELETE(0x7D0), + LAST_GROUP_CANT_DELETE(0x7D1), + ACCOUNTS_IN_GROUP(0x7D3), + INVALID_ACCOUNT_OR_TRADE_RANGES(0x7D4), + ACCOUNT_NOT_GROUP(0x7D5), + PROTECTED_CONFIG(0x7D6), + CONFIG_DUPLICATE(0x7D7), + CONFIG_LIMIT_REACHED(0x7D8), + INVALID_NETWORK_CONFIG(0x7D9), + DIALER_ID_ALREADY_EXISTS(0x7DA), + ADDRESS_ALREADY_EXISTS(0x7DB), + ATTEMPT_DELETE_WORKING_SERVER(0x7DC), + GATEWAY_NAME_ALREADY_EXISTS(0x7DD), + SERWER_SWITCHED_TO_BACKUP(0x7DE), + BACKUP_MODULE_ABSENT(0x7DF), + TRADE_MODULE_ABSENT(0x7E0), + HISTORY_MODULE_ABSENT(0x7E1), + ANOTHER_PROCESS_IN_PROGRESS(0x7E2), + LAST_ACCOUNT_CANT_DELETE(0xBB9), + LOGIN_RANGE_EXHAUSTED(0xBBA), + LOGIN_RESERVED(0xBBB), + ACCOUNT_ALREADY_EXISTS(0xBBC), + ATTEMPT_SELF_DELETION(0xBBD), + INVALID_PASSWORD(0xBBE), + USERS_LIMIT_REACHED(0xBBF), + ACCOUNT_HAS_OPEN_TRADES(0xBC0), + ATTEMPT_TO_MORE_ACCOUNT(0xBC1), + ATTEMPT_TO_MORE_GROUP(0xBC2), + ACCOUNT_BALANCE_ERROR(0xBC3), + ACCOUNT_HAS_INVALID_GROUP(0xBC4), + ONETRADE_ACCOUNT_ALREADY_EXISTS(0xBC5), + ACCOUNT_TRADE_DATA_IMPORT_ERROR(0xBC6), + ACCOUNT_TRADE_POSITION_IMPORT_ERROR(0xBC7), + ACCOUNT_OPEN_ORDERS_IMPORT_ERROR(0xBC8), + ACCOUNT_DEALS_HISTORY_IMPORT_ERROR(0xBC9), + ACCOUNT_ORDERS_HISTORY_IMPORT_ERROR(0xBCA), + ORDER_LIMIT_REACHED(0xFA1), + ORDER_ALREADY_EXISTS(0xFA2), + ORDER_RANGE_EXHAUSTED(0xFA3), + DEAL_RANGE_EXHAUSTED(0xFA4), + MONEY_LIMIT_REACHED(0xFA5), + DEAL_ALREADY_EXISTS(0xFA6), + ORDER_TICKET_RESERVED(0xFA7), + DEAL_TICKET_RESERVED(0xFA8), + BASE_SNAPSHOT_ERROR(0x1389), + METHOD_NOT_SUPPORTED(0x138A), + NO_DATA_REPORT(0x138B), + BAD_TEMPLATE(0x138C), + END_OF_TEMPLATE(0x138D), + INVALID_ROW_SIZE(0x138E), + REPEAT_LIMIT_REACHED(0x138F), + REPORT_SIZE_LIMIT_REACHED(0x1390), + SYMBOL_NOT_FOUND(0x1771), + REQUEST_ON_WAY(0x2711), + REQUEST_ACCEPTED(0x2712), + REQUEST_PROCESSED(0x2713), + REQUOTE(0x2714), + PRICES(0x2715), + REQUEST_REJECTED(0x2716), + REQUEST_CANCELLED(0x2717), + ORDER_PLACED(0x2718), + REQUEST_EXECUTED(0x2719), + REQUEST_EXECUTED_PARTIALLY(0x271A), + REQUEST_ERROR(0x271B), + REQUEST_TIMEOUT(0x271C), + INVALID_REQUEST(0x271D), + INVALID_VOLUME(0x271E), + INVALID_PRICE(0x271F), + INVALID_STOPS(0x2720), + TRADE_DISABLED(0x2721), + MARKET_CLOSED(0x2722), + NO_MONEY(0x2723), + PRICE_CHANGED(0x2724), + NO_PRICES(0x2725), + INVALID_EXPIRATION(0x2726), + ORDER_ALREADY_CHANGED(0x2727), + TOO_MANY_TRADE_REQUESTS(0x2728), + NO_CHANGES(0x2729), + AUTOTRADE_DISABLE_SERVER(0x272A), + AUTOTRADE_DISABLE_CLIENT(0x272B), + MODIFICATION_FAILED(0x272D), + UNSUPPORTED_FILLING_MODE(0x272E), + REQUEST_REJECTED_TO_ABSENCE_CONNECT(0x272F), + ALLOWED_REAL_ACCOUNTS_ONLY(0x2730), + ORDER_LIMIT_REACHED_(0x2731), + VOLUME_LIMIT_REACHED(0x2732), + INVALID_ORDER(0x2733), + POSITION_NOT_EXISTS(0x2734), + EXECUTION_NOT_BELONG_SERVER(0x2735), + VOLUME_CLOSE_EXCEEDS_POSITION(0x2736), + ORDER_CLOSE_ALREADY_EXISTS(0x2737), + POSITION_LIMIT_REACHED(0x2738), + ORDER_CANCELLED(0x2739), + ONLY_LONG_POSITION(0x273A), + ONLY_SHORT_POSITION(0x273B), + ONLY_CLOSE_POSITION(0x273C), + REQUEST_RETURNED_IN_QUEUE(0x2AF8), + REQUEST_EXECUTED_PARTIALLY_(0x2AF9), + REQUOTE_(0x2AFA), + NETWORK_PROBLEM(0x7fffffff); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (Msg.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private Msg(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static Msg forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MsgType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MsgType.java new file mode 100644 index 00000000000..eb435d2faf0 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MsgType.java @@ -0,0 +1,27 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Message type. */ +public enum MsgType { + /** Trace. */ + Trace, + /** Debug. */ + Debug, + /** Information. */ + Info, + /** Warning. */ + Warn, + /** Error. */ + Error, + /** Exception. */ + Exception; + + public static final int SIZE = java.lang.Integer.SIZE; + + public int getValue() { + return this.ordinal(); + } + + public static MsgType forValue(int value) { + return values()[value]; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MyThread.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MyThread.java new file mode 100644 index 00000000000..e1a640ee072 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/MyThread.java @@ -0,0 +1,52 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.concurrent.atomic.AtomicLong; + +class MyThread { + public interface ThrowableRunnable { + void run() throws Exception; + } + + private Thread Thread; + public Exception Exception = null; + public boolean Timeout = false; + private static final AtomicLong THREAD_COUNTER = new AtomicLong(0); + + public MyThread(ThrowableRunnable throwableRunnable, long user) { + Runnable runnable = + () -> { + try { + throwableRunnable.run(); + } catch (Exception ex) { + Exception = ex; + } + }; + Thread = new Thread(runnable); + Thread.setName( + String.format("MT5ConnectorThread-%s-%s", user, THREAD_COUNTER.incrementAndGet())); + } + + // Thread methods / properties + public void Start() { + Thread.start(); + } + + public void Join() throws InterruptedException { + Thread.join(); + } + + public boolean Join(int timeout) { + try { + Thread.join(timeout); + } catch (InterruptedException e) { + return false; + } + if (!Thread.isAlive()) return true; + Timeout = true; + return false; + } + + public boolean IsAlive() { + return Thread.isAlive(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnConnectProgress.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnConnectProgress.java new file mode 100644 index 00000000000..4971552f521 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnConnectProgress.java @@ -0,0 +1,12 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** + * Connect progress. + * + * @param sender Object that sent event + * @param args Event arguments + */ +@FunctionalInterface +public interface OnConnectProgress { + void invoke(MT5API sender, ConnectEventArgs args); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnMsgHandler.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnMsgHandler.java new file mode 100644 index 00000000000..ee83e877783 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnMsgHandler.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +@FunctionalInterface +public interface OnMsgHandler { + /** + * New message event handler. + * + * @param sender Object that sent message + * @param msg Message + * @param type Message type + */ + void invoke(Object sender, String msg, MsgType type); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderProgress.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderProgress.java new file mode 100644 index 00000000000..06d8b0ebd7b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderProgress.java @@ -0,0 +1,12 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** + * Order update notification from server + * + * @param sender Sender /// @param update Progress details + * @param order Order + */ +@FunctionalInterface +public interface OnOrderProgress { + void invoke(MT5API sender, OrderProgress progress); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderUpdate.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderUpdate.java new file mode 100644 index 00000000000..abb59ada602 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnOrderUpdate.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** + * Order update notification from server + * + * @param sender Sender + * @param update Update details + * @param order Order + */ +@FunctionalInterface +public interface OnOrderUpdate { + void invoke(MT5API sender, OrderUpdate update); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuote.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuote.java new file mode 100644 index 00000000000..66d12ee4e17 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuote.java @@ -0,0 +1,12 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** + * New quote + * + * @param api Sender + * @param quote Quote + */ +@FunctionalInterface +public interface OnQuote { + void invoke(MT5API sender, Quote quote); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuoteHistory.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuoteHistory.java new file mode 100644 index 00000000000..a21304a17d2 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnQuoteHistory.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** + * Order update notification from server + * + * @param sender Sender + * @param update Update details + * @param order Order + */ +@FunctionalInterface +public interface OnQuoteHistory { + void invoke(MT5API sender, QuoteHistoryEventArgs args); +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnTradeHistory.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnTradeHistory.java new file mode 100644 index 00000000000..c6f3ac9fec2 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OnTradeHistory.java @@ -0,0 +1,15 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; + +/** + * Order history + * + * @param api Sender + * @param orders Orders + */ +@FunctionalInterface +public interface OnTradeHistory { + void invoke(MT5API sender, OrderHistoryEventArgs args) + throws IOException, TimeoutException, ConnectException; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OpnedClosedOrders.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OpnedClosedOrders.java new file mode 100644 index 00000000000..d9348d7b46d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OpnedClosedOrders.java @@ -0,0 +1,432 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +class OpnedClosedOrders { + private Logger Log; + private MT5API Api; + + ConcurrentHashMap Opened = new ConcurrentHashMap(); + ConcurrentHashMap Closed = new ConcurrentHashMap(); + + public OpnedClosedOrders(MT5API api, Logger log) { + Api = api; + Log = log; + } + + String Proc; + + public final synchronized void Api_OnOrderUpdate(MT5API sender, OrderUpdate update) { + Proc = "0 "; + try { + Process(sender, update); + Proc += "36 "; + } catch (Exception ex) { + Log.warn(ex.getMessage()); + } + } + + void Process(MT5API sender, OrderUpdate update) { + Proc += "1 "; + if (update.OrderInternal != null) { + if (update.OrderInternal.State == OrderState.Placed) { + Proc += "2 "; + if (update.OrderInternal.DealTicket == 0) { + Order order = Opened.get(update.OrderInternal.TicketNumber); + if (order != null) { + Proc += "3 "; + order.Update(new Order(update.OrderInternal)); + update.Type = UpdateType.PendingModify; + } else { + Proc += "4 "; + order = new Order(update.OrderInternal); + Opened.put(update.OrderInternal.TicketNumber, order); + update.Type = UpdateType.PendingOpen; + } + update.Order = order; + } + } else if (update.OrderInternal.State == OrderState.Cancelled + || update.OrderInternal.State == OrderState.Expired + || update.OrderInternal.State == OrderState.Rejected) { + Proc += "5 "; + Order order = Opened.get(update.OrderInternal.TicketNumber); + if (order != null) { + Proc += "6 "; + order.Update(new Order(update.OrderInternal)); + Closed.put(update.OrderInternal.TicketNumber, order); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + Opened.remove(update.OrderInternal.TicketNumber); + update.Order = order; + } else update.Order = new Order(update.OrderInternal); + Proc += "7 "; + if (update.OrderInternal.State == OrderState.Expired) update.Type = UpdateType.Expired; + else if (update.OrderInternal.State == OrderState.Rejected) + update.Type = UpdateType.Rejected; + else update.Type = UpdateType.PendingClose; + } else if (update.OrderInternal.State == OrderState.Started) { + Proc += "8 "; + update.Order = new Order(update.OrderInternal); + update.Type = UpdateType.Started; + } else if (update.OrderInternal.State == OrderState.Filled) { + Proc += "9 "; + Order order = Opened.get(update.OrderInternal.TicketNumber); + if (order != null) update.Order = order; + else update.Order = new Order(update.OrderInternal); + update.Type = UpdateType.Filled; + } else if (update.OrderInternal.State == OrderState.RequestCancelling) { + Proc += "10 "; + Order order = Opened.get(update.OrderInternal.TicketNumber); + if (order != null) update.Order = order; + else update.Order = new Order(update.OrderInternal); + update.Type = UpdateType.Cancelling; + } + } + if (update.Deal != null) { + Proc += "11 "; + if (update.Deal.Type == DealType.Balance) { + Proc += "12 "; + long ticket = update.Deal.PositionTicket; + update.Order = new Order(update.Deal); + update.Type = UpdateType.Balance; + Closed.put(ticket, update.Order); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + } else { + Proc += "14 "; + long ticket = update.Deal.PositionTicket; + int closeByTicket = 0; + if (update.Deal.Comment.contains("by #")) + closeByTicket = + Integer.parseInt( + update.Deal.Comment.substring( + update.Deal.Comment.indexOf("by #") + "by #".length())); + if (ticket == 0) { + Proc += "15 "; + ticket = update.OppositeDeal.PositionTicket; + if (update.Deal.OpenTimeMs != update.OppositeDeal.OpenTimeMs) { + Proc += "16 "; + Order openedOrder = Opened.get(ticket); + if (openedOrder != null) { + Proc += "17 "; + int digits = Api.Symbols.GetInfo(update.OppositeDeal.Symbol).Digits; + double mul = Math.pow(10, digits); + if (Math.round(update.OppositeDeal.Price * mul) / mul + == Math.round(update.OppositeDeal.StopLoss * mul) / mul + || update.Deal.PlacedType == PlacedType.OnSL) { + Proc += "18 "; + openedOrder.UpdateOnStop(update.OppositeDeal, true); + update.Order = openedOrder; + update.Type = UpdateType.OnStopLoss; + Closed.put(ticket, update.Order); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + Opened.remove(ticket); + } else if (Math.round(update.OppositeDeal.Price * mul) / mul + == Math.round(update.OppositeDeal.TakeProfit * mul) / mul + || update.Deal.PlacedType == PlacedType.OnTP) { + Proc += "19 "; + openedOrder.UpdateOnStop(update.OppositeDeal, true); + update.Order = openedOrder; + update.Type = UpdateType.OnTakeProfit; + Closed.put(ticket, update.Order); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + Opened.remove(ticket); + } else { + Proc += "20 "; + openedOrder.Update(new Order(update.OppositeDeal)); + update.Order = openedOrder; + update.Type = UpdateType.MarketModify; + } + } + } + } else if (update.Deal.OpenTimeMs == update.OppositeDeal.OpenTimeMs + && Api.AccountMethod() == AccMethod.Hedging) { + Proc += "21 "; + Order order = Opened.get(ticket); + if (order != null) order.Update(new Order(update.Deal)); + else { + order = new Order(update.Deal); + Opened.put(ticket, order); + } + update.Order = order; + update.Type = UpdateType.MarketOpen; + + } else { + Proc += "23 "; + if (Api.AccountMethod() == AccMethod.Netting + || Api.AccountMethod() == AccMethod.Default) { + long nettingTicket = update.Deal.PositionTicket; + for (Order item : Opened.values()) + if (item.Symbol.equals(update.Deal.Symbol)) + if (item.OrderType == OrderType.Buy || item.OrderType == OrderType.Sell) { + nettingTicket = item.Ticket; + break; + } + Order order = Opened.get(nettingTicket); + if (nettingTicket > 0 && order != null) // in case of exvents not sorted by time + { + Proc += "27 "; + if (nettingTicket <= update.OppositeDeal.TicketNumber) { + Order open = order.Clone(); + order.Update(new Order(update.OppositeDeal)); + order.Ticket = update.OppositeDeal.TicketNumber; + Opened.remove(nettingTicket); + if (update.OppositeDeal.Volume > 0) { + Opened.put(order.Ticket, order); + update.Type = UpdateType.MarketOpen; + update.Order = new Order(update.Deal); + } else { + Proc += "28 "; + update.Type = UpdateType.MarketClose; + Order value = Closed.get(ticket); + if (value != null) value.Update(open); + else { + Closed.put(ticket, open); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + } + update.Order = new Order(new DealInternal[] {update.Deal, update.OppositeDeal}); + } + } + } else { + Order newOrder = new Order(update.Deal); + Order oldOrder = Opened.get(ticket); + if (oldOrder != null) oldOrder.Update(newOrder); + else Opened.put(ticket, newOrder); + update.Type = UpdateType.MarketOpen; + update.Order = newOrder; + } + } else if (Opened.containsKey(ticket)) { + Order open = Opened.get(ticket); + if (open != null) { + Proc += "24 "; + if (update.Deal.Direction == Direction.Out + && (update.Deal.PlacedType == PlacedType.OnSL + || update.Deal.PlacedType == PlacedType.OnTP + || update.Deal.PlacedType == PlacedType.OnStopOut)) { + Proc += "25 "; + if (update.Deal.PlacedType == PlacedType.OnSL) update.Type = UpdateType.OnStopLoss; + else if (update.Deal.PlacedType == PlacedType.OnTP) + update.Type = UpdateType.OnTakeProfit; + else update.Type = UpdateType.OnStopOut; + open.UpdateOnStop(update.Deal, false); + Closed.put(ticket, open); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + Opened.remove(ticket); + update.Order = open; + } else if (update.Deal.Direction == Direction.In) { + + } else { + Proc += "26 "; + double closeLots = new Order(update.Deal).Lots; + double mul = Math.pow(10, 8); + if ((double) Math.round(open.Lots * mul) / mul == closeLots) { + open.Lots = 0; + Opened.remove(ticket); + Opened.remove(update.Deal.OrderTicket); // CLOSE BY + Opened.remove(closeByTicket); // CLOSE BY + update.Type = UpdateType.MarketClose; + } else { + open.Lots -= closeLots; + update.Type = UpdateType.PartialClose; + } + open.Update(update.Deal); + update.Order = open; + if (update.Type == UpdateType.MarketClose) + update.Order.Profit = update.Order.CloseProfit; + Proc += "30 "; + Order value = Closed.get(ticket); + if (value != null) value.Update(open); + else { + Closed.put(ticket, open); + while (Closed.size() > 10) Closed.remove(Collections.min(Closed.keySet())); + } + } + } + } else if (Opened.containsKey(update.OppositeDeal.PositionTicket)) { + Order order = Opened.get(update.OppositeDeal.PositionTicket); + if (order != null) { + Proc += "32 "; + order.Update(new Order(update.OppositeDeal)); + update.Order = order; + update.Type = UpdateType.MarketModify; + } + } else if (Api.AccountMethod() == AccMethod.Netting + || Api.AccountMethod() == AccMethod.Default) + Opened.put(ticket, new Order(update.OppositeDeal)); + } + } + // round to 2 decimal to retain precision with double + long factor = (long) Math.pow(10, 2); + double value = + (Api.Account.Balance + update.Deal.Profit + update.Deal.Swap + update.Deal.Commission) + * factor; + long tmp = Math.round(value); + value = (double) tmp / factor; + Api.Account.Balance = value; + } + Proc += "33 "; + if (update.Order != null) + update.Order = update.Order.Clone(); // to avoid changes by further events during processing + Proc += "34 "; + } + + public final void AddDeals(ArrayList deals) { + for (DealInternal item : deals) { + Opened.put(item.TicketNumber, new Order(item)); + } + } + + public final void AddOrders(ArrayList orders) { + for (OrderInternal item : orders) { + Opened.put(item.TicketNumber, new Order(item)); + } + } + + // internal Order[] GetOpenedOrders() + // { + // Log.trace("GetOpenedOrders"); + // if (Orders == null || Deals == null) + // throw new Exception("Opened orders and deals not loaded yet"); + // List res = new List(); + // foreach (var item in Orders) + // res.Add(new Order(item)); + // foreach (var item in Deals) + // res.Add(new Order(item)); + // return res.ToArray(); + // } + + public final void ParseTrades(InBuf buf) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var cmd = buf.Byte(); + byte cmd = buf.Byte(); + switch ((cmd & 0xFF)) { + case 7: // symbol configuration + UpdateSymbols(buf); + break; + case 8: // symbol group configuration + UpdateSymbolGroup(buf); + break; + case 9: // group configuration + UpdateSymbolsBase(buf); + break; + case 0x11: // tickers + UpdateTickers(buf); + break; + case 0x13: // users + UpdateAccount(buf); + break; + case 0x1F: // orders + UpdateOrders(buf); + break; + case 0x20: // history of orders + UpdateHistoryOrders(buf); + break; + case 0x21: // all deals + UpdateDeals(buf); + break; + case 0x23: // request + UpdateTradeRequest(buf); + break; + case 0x28: // spread config + UpdateSpreads(buf); + break; + default: + String.valueOf(cmd); + break; + } + } + + private void UpdateSpreads(InBuf buf) { + Log.trace("UpdateSpreads"); + } + + private void UpdateTradeRequest(InBuf buf) { + Log.trace("UpdateTradeRequest"); + int num = buf.Int(); + OrderProgress[] array = new OrderProgress[num]; + int count = buf.getLeft() / num - 1212; // size TransactionInfo + TradeRequest + TradeResult 476 + for (int i = 0; i < num; i++) { + OrderProgress progress = new OrderProgress(); + progress.OrderUpdate = buf.Struct(new TransactionInfo()); + if (Api.Connection.TradeBuild <= 1891) { + throw new UnsupportedOperationException("TradeBuild <= 1891"); + } + progress.TradeRequest = buf.Struct(new TradeRequest()); + progress.TradeResult = buf.Struct(new TradeResult()); + // progress.DealsResult = buf.Struct(); + buf.Bytes(count); + array[i] = progress; + + long closeByTicket = progress.TradeRequest.ByCloseTicket; + if (0 != closeByTicket + && null != progress.TradeResult + && progress.TradeResult.Status == Msg.REQUEST_EXECUTED) { + Opened.remove(closeByTicket); + } + } + Api.OnOrderProgressCall(array); + } + + private void UpdateDeals(InBuf buf) { + Log.trace("UpdateDeals"); + int num = buf.Int(); + OrderUpdate[] ar = new OrderUpdate[num]; + if (Api.Connection.TradeBuild <= 1892) { + throw new UnsupportedOperationException("TradeBuild <= 1892"); + } + for (int i = 0; i < num; i++) { + OrderUpdate ou = new OrderUpdate(); + ou.Trans = buf.Struct(new TransactionInfo()); + ou.Deal = buf.Struct(new DealInternal()); + ou.OppositeDeal = buf.Struct(new DealInternal()); + PumpDeals5D8 s5 = buf.Struct(new PumpDeals5D8()); + PumpDeals698 s6 = buf.Struct(new PumpDeals698()); + if (Api.Connection.TradeBuild <= 1241) { + continue; + } + DealInternal[] deals = buf.Array(new DealInternal()); + DealInternal[] opposite = buf.Array(new DealInternal()); + ar[i] = ou; + } + Api.OnOrderUpdateCall(ar); + } + + private void UpdateHistoryOrders(InBuf buf) { + Log.trace("UpdateHistoryOrders"); + } + + private void UpdateOrders(InBuf buf) { + Log.trace("UpdateOrders"); + int num = buf.Int(); + OrderUpdate[] ar = new OrderUpdate[num]; + for (int i = 0; i < num; i++) { + if (Api.Connection.TradeBuild <= 1891) { + throw new UnsupportedOperationException("TradeBuild <= 1891"); + } + OrderUpdate ou = new OrderUpdate(); + ou.Trans = buf.Struct(new TransactionInfo()); + ou.OrderInternal = buf.Struct(new OrderInternal()); + ar[i] = ou; + } + Api.OnOrderUpdateCall(ar); + } + + private void UpdateAccount(InBuf buf) { + Log.trace("UpdateAccount"); + } + + private void UpdateTickers(InBuf buf) { + Log.trace("UpdateTickers"); + } + + private void UpdateSymbolsBase(InBuf buf) { + Log.trace("UpdateSymbolsBase"); + } + + private void UpdateSymbolGroup(InBuf buf) { + Log.trace("UpdateSymbolGroup"); + } + + private void UpdateSymbols(InBuf buf) { + Log.trace("UpdateSymbols"); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Order.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Order.java new file mode 100644 index 00000000000..bd6be2aeeae --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Order.java @@ -0,0 +1,317 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Pending, market or history order */ +public class Order { + public long Ticket; + public double Profit; + public double Swap; + public double Commission; + public double ClosePrice; + public java.time.LocalDateTime CloseTime = java.time.LocalDateTime.MIN; + public double CloseVolume; + + public double OpenPrice; + public java.time.LocalDateTime OpenTime = java.time.LocalDateTime.MIN; + public double Lots; + public double ContractSize; + public long ExpertId; + public PlacedType PlacedType; + public OrderType OrderType; + public DealType DealType; + public String Symbol; + public String Comment; + public OrderState State = OrderState.values()[0]; + public double StopLoss; + public double TakeProfit; + public int RequestId; + public int Digits; + public double ProfitRate; + public DealInternal DealInternalIn; + public DealInternal DealInternalOut; + public OrderInternal OrderInternal; + double CloseProfit; + + public Order() {} + + public final void Update(Order order) { + Ticket = order.Ticket; + Profit = order.Profit; + Swap = order.Swap; + Commission = order.Commission; + ClosePrice = order.ClosePrice; + CloseTime = order.CloseTime; + CloseVolume = order.CloseVolume; + OpenPrice = order.OpenPrice; + OpenTime = order.OpenTime; + Lots = order.Lots; + ContractSize = order.ContractSize; + ExpertId = order.ExpertId; + PlacedType = order.PlacedType; + OrderType = order.OrderType; + DealType = order.DealType; + Symbol = order.Symbol; + Comment = order.Comment; + State = order.State; + StopLoss = order.StopLoss; + TakeProfit = order.TakeProfit; + if (order.RequestId != 0) { + RequestId = order.RequestId; + } + Digits = order.Digits; + ProfitRate = order.ProfitRate; + DealInternalIn = order.DealInternalIn; + DealInternalOut = order.DealInternalOut; + OrderInternal = order.OrderInternal; + } + + public Order(DealInternal[] deals, int requestId) { + RequestId = requestId; + // Console.WriteLine(deals.Length); + for (DealInternal item : deals) { + if (item.Direction == Direction.In) { + DealInternalIn = item; + OpenTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + OpenPrice = item.OpenPrice; + Lots = item.Lots; + ContractSize = item.ContractSize; + PlacedType = item.PlacedType; + Ticket = item.PositionTicket; + DealType = item.Type; + if (DealType == DealType.DealSell) OrderType = OrderType.Sell; + else OrderType = OrderType.Buy; + ExpertId = item.ExpertId; + Symbol = item.Symbol; + Commission = item.Commission; + Swap = item.Swap; + Comment = item.Comment; + StopLoss = item.StopLoss; + TakeProfit = item.TakeProfit; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + if (item.Direction == Direction.Out) { + DealInternalOut = item; + CloseTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + ClosePrice = item.OpenPrice; + CloseVolume += item.Lots; + Commission += item.Commission; + Profit += item.Profit; + Swap += item.Swap; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + } + } + + public final void UpdateOnStop(DealInternal item, boolean unusual) { + DealInternalOut = item; + CloseTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + if (unusual) ClosePrice = item.Price; + else ClosePrice = item.OpenPrice; + ClosePrice = item.OpenPrice; + CloseVolume = item.Lots; + Commission = item.Commission; + Profit = item.Profit; + Swap = item.Swap; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + + public Order(DealInternal[] deals) { + // Console.WriteLine(deals.Length); + for (DealInternal item : deals) { + if (item.Direction == Direction.In) { + DealInternalIn = item; + OpenTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + OpenPrice = item.OpenPrice; + Lots = item.Lots; + ContractSize = item.ContractSize; + PlacedType = item.PlacedType; + if (item.PositionTicket != 0) { + Ticket = item.PositionTicket; + } + DealType = item.Type; + if (DealType == DealType.DealSell) OrderType = OrderType.Sell; + else OrderType = OrderType.Buy; + ExpertId = item.ExpertId; + Symbol = item.Symbol; + Commission = item.Commission; + Swap = item.Swap; + Comment = item.Comment; + StopLoss = item.StopLoss; + TakeProfit = item.TakeProfit; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + if (item.Direction == Direction.Out || item.Direction == Direction.OutBy) { + DealInternalOut = item; + CloseTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + ClosePrice = item.OpenPrice; + CloseVolume = item.Lots; + Commission += item.Commission; + Profit += item.Profit; + Swap += item.Swap; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + if (item.PositionTicket != 0) { + Ticket = item.PositionTicket; + } + } + } + } + + void Update(DealInternal item) { + if (item.Direction == Direction.In) { + DealInternalIn = item; + OpenTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + OpenPrice = item.OpenPrice; + Lots = item.Lots; + ContractSize = item.ContractSize; + PlacedType = (PlacedType) item.PlacedType; + Ticket = item.PositionTicket; + DealType = item.Type; + if (DealType == DealType.DealSell) OrderType = OrderType.Sell; + ExpertId = item.ExpertId; + Symbol = item.Symbol; + Commission = item.Commission; + Swap = item.Swap; + Comment = item.Comment; + StopLoss = item.StopLoss; + TakeProfit = item.TakeProfit; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } else if (item.Direction == Direction.Out) { + DealInternalOut = item; + CloseTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + ClosePrice = item.OpenPrice; + CloseVolume = item.Lots; + CloseProfit += item.Profit; + Commission += item.Commission; + Swap += item.Swap; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + } + + public Order(DealInternal item) { + DealInternalIn = item; + OpenTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + OpenPrice = item.OpenPrice; + Lots = item.Lots; + ContractSize = item.ContractSize; + PlacedType = item.PlacedType; + Ticket = item.PositionTicket; + if (Ticket == 0) { + Ticket = item.PositionTicket; + } + DealType = item.Type; + if (DealType == DealType.DealSell) OrderType = OrderType.Sell; + else OrderType = OrderType.Buy; + ExpertId = item.ExpertId; + Symbol = item.Symbol; + Commission = item.Commission; + Swap = item.Swap; + Comment = item.Comment; + Profit = item.Profit; + StopLoss = item.StopLoss; + TakeProfit = item.TakeProfit; + State = OrderState.Filled; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + + public Order(OrderInternal item) { + OrderInternal = item; + OpenTime = ConvertTo.DateTimeMs(item.OpenTimeMs); + OpenPrice = item.OpenPrice; + Lots = item.Lots; + ContractSize = item.ContractSize; + PlacedType = item.PlacedType; + Ticket = item.TicketNumber; + OrderType = item.Type; + ExpertId = item.ExpertId; + Symbol = item.Symbol; + Comment = item.Comment; + State = item.State; + StopLoss = item.StopLoss; + TakeProfit = item.TakeProfit; + Digits = item.Digits; + ProfitRate = item.ProfitRate; + } + + public Order( + OrderProgress progr, java.time.LocalDateTime serverTime) // deal executed - OrderSend market + { + OpenTime = serverTime; + OpenPrice = progr.TradeResult.OpenPrice; + Lots = (double) progr.TradeResult.Volume / 100000000; + PlacedType = progr.TradeRequest.PlacedType; + Ticket = progr.TradeResult.TicketNumber; + OrderType = progr.TradeRequest.OrderType; + if (progr.TradeRequest.OrderType.toString().startsWith("Buy")) { + DealType = DealType.DealBuy; + } + if (progr.TradeRequest.OrderType.toString().startsWith("Sell")) { + DealType = DealType.DealSell; + } + ExpertId = progr.TradeRequest.ExpertId; + Symbol = progr.TradeRequest.Currency; + // Commission = + // Swap = item.Swap; + Comment = progr.TradeResult.Comment; + StopLoss = progr.TradeRequest.StopLoss; + TakeProfit = progr.TradeRequest.TakeProfit; + State = OrderState.Filled; + RequestId = progr.TradeRequest.RequestId; + Digits = progr.TradeRequest.Digits; + } + + public Order( + OrderProgress progr, + java.time.LocalDateTime serverTime, + Order order) // deal executed - OrderClose market + { + CloseTime = serverTime; + ClosePrice = progr.TradeResult.OpenPrice; + CloseVolume = (double) progr.TradeResult.Volume / 100000000; + // PlacedType = (PlacedType)progr.TradeRequest.PlacedType; + Ticket = progr.TradeResult.TicketNumber; + // OrderType = progr.TradeRequest.OrderType; + // if (progr.TradeRequest.OrderType.ToString().StartsWith("Buy")) + // DealType = DealType.DealBuy; + // if (progr.TradeRequest.OrderType.ToString().StartsWith("Sell")) + // DealType = DealType.DealSell; + // ExpertId = progr.TradeRequest.ExpertId; + Symbol = progr.TradeRequest.Currency; + // Commission = + // Swap = item.Swap; + Comment = progr.TradeResult.Comment; + // StopLoss = progr.TradeRequest.StopLoss; + // TakeProfit = progr.TradeRequest.TakeProfit; + State = OrderState.Filled; + RequestId = progr.TradeRequest.RequestId; + Digits = progr.TradeRequest.Digits; + if (order != null) { + OrderInternal = order.OrderInternal; + DealInternalIn = order.DealInternalIn; + DealInternalOut = order.DealInternalOut; + OrderType = order.OrderType; + DealType = order.DealType; + ExpertId = order.ExpertId; + Commission = order.Commission; + Swap = order.Swap; + OpenTime = order.OpenTime; + Lots = order.Lots; + TakeProfit = order.TakeProfit; + StopLoss = order.StopLoss; + PlacedType = order.PlacedType; + ProfitRate = order.ProfitRate; + } + } + + public Order Clone() { + Order res = new Order(); + res.Update(this); + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderDirection.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderDirection.java new file mode 100644 index 00000000000..5ac1ac8e1a1 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderDirection.java @@ -0,0 +1,36 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum OrderDirection { + In(0), + Out(1), + InOut(2); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (OrderDirection.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private OrderDirection(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static OrderDirection forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistory.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistory.java new file mode 100644 index 00000000000..7c3c674cd4f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistory.java @@ -0,0 +1,230 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.*; + +class OrderHistory { + private Logger Log; + private MT5API Api; + + public OrderHistory(MT5API api, Logger log) { + Log = log; + Api = api; + } + + public final void Request( + java.time.LocalDateTime from, + java.time.LocalDateTime to, + int existCount, + long historyTime, + boolean deals) + throws IOException { + OutBuf buf = new OutBuf(); + if (deals) buf.ByteToBuffer((byte) 0x21); + else buf.ByteToBuffer((byte) 0x20); + buf.LongLongToBuffer(ConvertTo.Long(from)); + buf.LongLongToBuffer(ConvertTo.Long(to)); + if (existCount > 0) { + buf.LongToBuffer(1); + buf.LongLongToBuffer(ConvertTo.Long(from)); + buf.LongLongToBuffer(ConvertTo.Long(to)); + buf.LongLongToBuffer(historyTime); + buf.LongToBuffer(existCount); + buf.LongToBuffer(0); + buf.LongToBuffer(0); + buf.LongToBuffer(0); + } else buf.LongToBuffer(0); + Api.Connection.SendPacket((byte) 0x65, buf); + } + + public final void Parse(InBuf buf) { + byte cmd = buf.Byte(); + if (cmd == 0x20) { + int[] action = new int[1]; + List res = ParseOrders(buf, action); + ArrayList list = new ArrayList(); + for (int i = 0; i < res.size(); i++) { + Order order = new Order(res.get(i)); + list.add(order); + } + OrderHistoryEventArgs args = new OrderHistoryEventArgs(); + args.Action = action[0]; + args.Orders = list; + args.InternalOrders = res; + Api.OnOrderHisotyCall(args); + } else if (cmd == 0x21) { + int[] action = new int[1]; + HashMap> res = ParseDeals(buf, action); + List list = new LinkedList(); + for (long key : res.keySet()) { + List value = res.get(key); + if (value.size() > 1) { + if (key == 0) // balance + for (DealInternal deal : value) list.add(new Order(deal)); + else list.add(new Order(value.toArray(new DealInternal[0]))); + } + if (res.get(key).size() == 1) + if (value.get(0).Type != DealType.DealBuy && value.get(0).Type != DealType.DealSell) + list.add(new Order(value.get(0))); + } + List deals = new LinkedList<>(); + for (long key : res.keySet()) deals.addAll(res.get(key)); + OrderHistoryEventArgs args = new OrderHistoryEventArgs(); + args.Action = action[0]; + args.Orders = list; + args.InternalDeals = deals; + Api.OnOrderHisotyCall(args); + } else + throw new RuntimeException("Unknown Trade Parse Cmd = 0x" + String.format("%X", cmd & 0xFF)); + } + + private HashMap> ParseDeals(InBuf buf, int[] action) { + int updId = buf.Int(); + int num = buf.Int(); + HashMap> res = new HashMap>(); + for (int i = 0; i < num; i++) { + LocalDateTime time = ConvertTo.DateTime(buf.Long()); + action[0] = buf.Int(); + if (action[0] == 1) continue; + if (action[0] == 4) { + // RemoveItem(time); + // continue; + } + if (res == null) res = ParseReceivedDeals(action[0], buf); + else { + HashMap> map = ParseReceivedDeals(action[0], buf); + for (long key : map.keySet()) + if (!res.containsKey(key)) res.put(key, map.get(key)); + else res.get(key).addAll(map.get(key)); + } + } + return res; + } + + private List ParseOrders(InBuf buf, int[] action) { + int updId = buf.Int(); + int num = buf.Int(); + List res = new LinkedList<>(); + for (int i = 0; i < num; i++) { + LocalDateTime time = ConvertTo.DateTime(buf.Long()); + action[0] = buf.Int(); + if (action[0] == 1) continue; + if (action[0] == 4) { + // RemoveItem(time); + continue; + } + res.addAll(Arrays.asList(ParseReceivedOrders(action[0], buf))); + } + return res; + } + + private HashMap> ParseReceivedDeals(int action, InBuf buf) { + if (action == 0) { + int num = buf.Int(); + long[] tickets = new long[num]; + for (int i = 0; i < num; i++) tickets[i] = buf.Long(); + if (Api.Connection.TradeBuild <= 1892) throw new RuntimeException("TradeBuild <= 1892"); + HashMap> res = buf.ArrayDeal(); + buf.Bytes(16); + return res; + } else { + Msg status = Msg.forValue(buf.Int()); + HashMap> res = buf.ArrayDeal(); + return res; + } + } + + private OrderInternal[] ParseReceivedOrders(int action, InBuf buf) { + if (action == 0) { + int num = buf.Int(); + long[] tickets = new long[num]; + for (int i = 0; i < num; i++) tickets[i] = buf.Long(); + if (Api.Connection.TradeBuild <= 1892) throw new RuntimeException("TradeBuild <= 1892"); + OrderInternal[] res = buf.Array(new OrderInternal()); + buf.Bytes(16); + return res; + } else { + Msg status = Msg.forValue(buf.Int()); + OrderInternal[] res = buf.Array(new OrderInternal()); + return res; + } + } + + private ArrayList ParseBuf(InBuf buf, T t) // extends FromBufReader + { + // buf.Int(); + int updId = buf.Int(); + int num = buf.Int(); + ArrayList res = new ArrayList(); + for (int i = 0; i < num; i++) { + java.time.LocalDateTime time = ConvertTo.DateTime(buf.Long()); + int action = buf.Int(); + if (action == 1) { + continue; + } + if (action == 4) { + // RemoveItem(time); + continue; + } + // if ((action != 0) && (action != 0xE)) + // { + long[] tickets = null; + RefObject tempRef_tickets = new RefObject(tickets); + for (Object item : ParseReceivedData(action, buf, tempRef_tickets, t)) res.add((T) item); + tickets = tempRef_tickets.argValue; + // } + } + return res; + } + + // private void ParseOrders(InBuf buf) + // { + // int updId = buf.Int(); + // int num = buf.Int(); + // for (int i = 0; i < num; i++) + // { + // var time = ConvertTo.DateTime(buf.Long()); + // int action = buf.Int(); + // if (action == 1) + // continue; + // if (action == 4) + // { + // //RemoveItem(time); + // continue; + // } + // if ((action != 0) && (action != 0xE)) + // { + // ParseReceivedData(action, buf); + // } + // } + // } + + private T[] ParseReceivedData( + int action, InBuf buf, RefObject tickets, T t) { + int num = buf.Int(); + tickets.argValue = new long[num]; + for (int i = 0; i < num; i++) { + tickets.argValue[i] = buf.Long(); + } + if (Api.Connection.TradeBuild <= 1892) { + throw new UnsupportedOperationException("TradeBuild <= 1892"); + } + T[] res = buf.Array(t); + // DealInternal[] res = new DealInternal[num]; + // for (int i = 0; i < num; i++) + // res[i] = LoadDeal(buf); + if (action != 0) { + return res; + } + buf.Bytes(16); + return res; + } + + private DealInternal LoadDeal(InBuf buf) { + if (Api.Connection.TradeBuild <= 1892) { + throw new UnsupportedOperationException("TradeBuild <= 1892"); + } + return buf.Struct(new DealInternal()); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistoryEventArgs.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistoryEventArgs.java new file mode 100644 index 00000000000..61397209602 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderHistoryEventArgs.java @@ -0,0 +1,10 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.List; + +public class OrderHistoryEventArgs { + public List Orders; + public List InternalDeals; + public List InternalOrders; + public int Action; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderInternal.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderInternal.java new file mode 100644 index 00000000000..db248b884ef --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderInternal.java @@ -0,0 +1,225 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x27C, CharSet = CharSet.Unicode)]*/ +public class OrderInternal extends FromBufReader { + /** Ticket number */ + /*[FieldOffset(0)]*/ public long TicketNumber; + + /** Text id */ + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Id; + + /** Account login */ + /*[FieldOffset(72)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Login; + public long Login; + + /*[FieldOffset(80)]*/ public long s50; + + /** Symbol currency */ + /*[FieldOffset(88)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Symbol; + + /** History time (as FileTime format) */ + /*[FieldOffset(152)]*/ public long HistoryTime; + + /** Open time */ + /*[FieldOffset(160)]*/ public long OpenTime; + + /** Expiration time */ + /*[FieldOffset(168)]*/ public long ExpirationTime; + + /** Execution time */ + /*[FieldOffset(176)]*/ public long ExecutionTime; + + /** Order type */ + /*[FieldOffset(184)]*/ public OrderType Type = OrderType.values()[0]; + + /** Fill policy */ + /*[FieldOffset(188)]*/ public FillPolicy FillPolicy; + + /** Expiration type */ + /*[FieldOffset(192)]*/ public ExpirationDate ExpirationType = ExpirationDate.values()[0]; + + /*[FieldOffset(196)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sC4; + public byte[] sC4; + + /** Placed type */ + /*[FieldOffset(204)]*/ public PlacedType PlacedType; + + /*[FieldOffset(208)]*/ public int sD0; + + /** Open price */ + /*[FieldOffset(212)]*/ public double OpenPrice; + + /** Order price */ + /*[FieldOffset(220)]*/ public double OrderPrice; + + /** Price */ + /*[FieldOffset(228)]*/ public double Price; + + /** Stop loss */ + /*[FieldOffset(236)]*/ public double StopLoss; + + /** Take profit */ + /*[FieldOffset(244)]*/ public double TakeProfit; + + /** Cover volume */ + /*[FieldOffset(252)]*/ public double Lots; + + /** Request volume */ + /*[FieldOffset(260)]*/ public long RequestVolume; + + /** Order state */ + /*[FieldOffset(268)]*/ public OrderState State = OrderState.values()[0]; + + /** Expert id */ + /*[FieldOffset(272)]*/ public long ExpertId; + + /** Associative deal ticket */ + /*[FieldOffset(280)]*/ public long DealTicket; + + /** Comment text */ + /*[FieldOffset(288)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Comment; + + /** Lots */ + /*[FieldOffset(352)]*/ public double ContractSize; + + /** Significant digits */ + /*[FieldOffset(360)]*/ public int Digits; + + /** Symbols base significant digits */ + /*[FieldOffset(364)]*/ public int BaseDigits; + + /*[FieldOffset(368)]*/ public double s170; + /*[FieldOffset(376)]*/ public double s178; + /*[FieldOffset(384)]*/ public long s180; + + /** Profit rate */ + /*[FieldOffset(392)]*/ public double ProfitRate; + + /** Open time (ms) */ + /*[FieldOffset(400)]*/ public long OpenTimeMs; + + /*[FieldOffset(408)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 48)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s198; + public byte[] s198; + /*[FieldOffset(456)]*/ public int s1C8; + /*[FieldOffset(460)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 176)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s1CC; + public byte[] s1CC; + + /** Volume */ + private long Volume; + + /** Request lots */ + private double RequestLots; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 636; + OrderInternal st = new OrderInternal(); + st.TicketNumber = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Id = GetString(buf.Bytes(64)); + st.Login = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.s50 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Symbol = GetString(buf.Bytes(64)); + st.HistoryTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.OpenTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ExpirationTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ExecutionTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Type = OrderType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.FillPolicy = FillPolicy.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.ExpirationType = ExpirationDate.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sC4 = new byte[8]; + st.sC4 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.sC4[i] = buf.Byte(); + } + st.PlacedType = PlacedType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.sD0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.OpenPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.OrderPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Price = BitConverter.ToDouble(buf.Bytes(8), 0); + st.StopLoss = BitConverter.ToDouble(buf.Bytes(8), 0); + st.TakeProfit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Volume = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.Lots = BigDecimal.valueOf(st.Volume).divide(BigDecimal.valueOf(100000000)).doubleValue(); + st.RequestVolume = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.RequestLots = (double) st.RequestVolume / 100000000; + st.State = OrderState.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.ExpertId = BitConverter.ToInt64(buf.Bytes(8), 0); + st.DealTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Comment = GetString(buf.Bytes(64)); + st.ContractSize = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Digits = BitConverter.ToInt32(buf.Bytes(4), 0); + st.BaseDigits = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s170 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s178 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s180 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ProfitRate = BitConverter.ToDouble(buf.Bytes(8), 0); + st.OpenTimeMs = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s198 = new byte[48]; + st.s198 = new byte[48]; + for (int i = 0; i < 48; i++) { + st.s198[i] = buf.Byte(); + } + st.s1C8 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s1CC = new byte[176]; + st.s1CC = new byte[176]; + for (int i = 0; i < 176; i++) { + st.s1CC[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } + + public boolean IsAssociativeDealOrder() { + return IsTradeOrder() && DealTicket != 0 && (TicketNumber == 0 || (DealTicket != TicketNumber)); + } + + public boolean IsTradeOrder() { + return (Type == OrderType.Buy) || (Type == OrderType.Sell); + } + + public boolean IsLimitOrder() { + return (Type == OrderType.BuyLimit) || (Type == OrderType.SellLimit); + } + + public boolean IsStopOrder() { + return (Type == OrderType.BuyStop) || (Type == OrderType.SellStop); + } + + public boolean IsBuyOrder() { + return (Type == OrderType.Buy) + || (Type == OrderType.BuyStop) + || (Type == OrderType.BuyLimit) + || (Type == OrderType.BuyStopLimit); + } + + public boolean IsStopLimitOrder() { + return (Type == OrderType.BuyStopLimit) || (Type == OrderType.SellStopLimit); + } + + public LocalDateTime OpenTimeAsDateTime() { + return ConvertTo.DateTime(OpenTime); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderMargin.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderMargin.java new file mode 100644 index 00000000000..51a1214b3db --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderMargin.java @@ -0,0 +1,475 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; + +public class OrderMargin { + public class TradesInfo // sizeof 0x328 d + { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong m_nLogin; + public long m_nLogin; // 0 + public String s8 = new String(new char[16]); + public int m_nDigits; // 18 + public String s1C = new String(new char[36]); + public double m_dBalance; // 40 + public double m_dCredit; // 48 + public double m_dCommission; // 50 + public double m_dBlocked; // 58 + public String s60 = new String(new char[24]); + public double m_dMargin; // 78 + public double m_dMarginFree; // 80 + public double m_dMarginLevel; // 88 + public int m_nLeverage; // 90 + public int s94; + public double m_dMarginInitial; // 98 + public double m_dMarginMaintenance; // A0 + public String sA8 = new String(new char[16]); + public double m_dOrderProfit; // B8 + public double m_dSwap; // C0 + public double m_dOrderCommission; // C8 + public double m_dProfit; // D0 + public double m_dEquity; // D8 + public double m_dAssets; // E0 + public double m_dLiabilities; // E8 + public double m_dCollateral; // F0 + public String sF8 = new String(new char[136]); + public int s180; + public String s184 = new String(new char[316]); + public double s2C0; + public int s2C8; + public String s2CC = new String(new char[88]); + } + + private MT5API Api; + + public OrderMargin(MT5API api) { + Api = api; + } + + protected final double GetBidRate(String pCurrency1, String pCurrency2) throws IOException { + String cur = pCurrency1 + pCurrency2; + if (Api.Symbols.Exist(cur)) { + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(cur).Bid; + } + cur = pCurrency2 + pCurrency1; + if (Api.Symbols.Exist(cur)) { + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return 1 / Api.GetQuote(cur).Ask; + } + return 0; + } + + public final double GetBidProfitRate(String pCurrency1, String pCurrency2) throws IOException { + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency1)) { + throw new RuntimeException("pCurrency1 is null or empty"); + } + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency2)) { + throw new RuntimeException("pCurrency2 is null or empty"); + } + if (pCurrency1.compareTo(pCurrency2) != 0 || IsRubleCurrency(pCurrency1, pCurrency2)) { + return 1.0; + } + double rate = GetBidRate(pCurrency1, pCurrency2); + if ((int) rate != 0) { + return rate; + } + double toUSD = GetBidRate(pCurrency1, "USD"); + if (toUSD == 0) { + return 0; + } + double fromUsd = GetBidRate("USD", pCurrency2); + if (fromUsd == 0) { + return 0; + } + return toUSD * fromUsd; + } + + public static boolean IsRubleCurrency(String pCurrency1, String pCurrency2) { + String[] sCurrency = {"RUB", "RUR"}; + for (int i = 0; i < 1; i++) { + if (pCurrency1.compareTo(sCurrency[i]) != 0 && pCurrency2.compareTo(sCurrency[i + 1]) != 0) { + return true; + } + if (pCurrency2.compareTo(sCurrency[i]) != 0 && pCurrency1.compareTo(sCurrency[i + 1]) != 0) { + return true; + } + } + return false; + } + + protected final double GetAskRate(String pCurrency1, String pCurrency2) throws IOException { + String cur = pCurrency1 + pCurrency2; + if (Api.Symbols.Exist(cur)) { + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(cur).Ask; + } + cur = pCurrency2 + pCurrency1; + if (Api.Symbols.Exist(cur)) { + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return 1 / Api.GetQuote(cur).Bid; + } + return 0; + } + + public final double GetAskProfitRate(String pCurrency1, String pCurrency2) throws IOException { + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency1)) { + throw new RuntimeException("pCurrency1 is null or empty"); + } + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency2)) { + throw new RuntimeException("pCurrency2 is null or empty"); + } + if (pCurrency1.compareTo(pCurrency2) != 0 || IsRubleCurrency(pCurrency1, pCurrency2)) { + return 1.0; + } + double rate = GetAskRate(pCurrency1, pCurrency2); + if ((int) rate != 0) { + return rate; + } + double toUSD = GetAskRate(pCurrency1, "USD"); + if (toUSD == 0) { + return 0; + } + double fromUSD = GetAskRate("USD", pCurrency2); + if (fromUSD == 0) { + return 0; + } + return toUSD * fromUSD; + } + + private double RoundAdd(double value1, double value2, int digits) { + return Math.round((value1 + value2) * Math.pow(10, digits)) / Math.pow(10, digits); + } + + protected final double GetPrice(Order order) throws IOException { + while (Api.GetQuote(order.Symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + if (order.OrderType == OrderType.Buy) { + return Api.GetQuote(order.Symbol).Bid; + } + if (order.OrderType == OrderType.Sell) { + return Api.GetQuote(order.Symbol).Ask; + } + return 0; + } + + private double m_dContractSize; + private double m_dMaintenanceMargin; + private double m_dInitialMargin; + + private double IntToDouble(int value) { + double _DP2to32 = 4.294967296e9; + double res = (double) value; + if (value < 0) { + res += _DP2to32; + } + return res; + } + + private double m_dTickSize; + private double m_dTickValue; + private double m_dSettlementPrice; + private double m_dUpperLimit; + private double m_dLowerLimit; + private double s1F0; + private double m_dFaceValue; + private int sE8; + + public final double CalcDefMargin( + int nLeverage, + OrderType type, + boolean bInitialMargin, + boolean bPrice, + double lots, + double price, + SymbolInfo sym) { + double initialMargin = m_dInitialMargin; + if (!bInitialMargin && m_dMaintenanceMargin != 0) { + initialMargin = m_dMaintenanceMargin; + } + double leverage = IntToDouble(nLeverage); + double margin = 0; + switch (sym.CalcMode) { + case Forex: + if (initialMargin > 0) { + margin = (lots * initialMargin) / (m_dContractSize * leverage); + } else { + margin = lots / leverage; + } + break; + case Futures: + case ExchangeFutures: + case ExchangeMarginOption: + margin = lots * initialMargin / m_dContractSize; + break; + case CFD: + case ExchangeStocks: + if (initialMargin > 0) { + margin = lots * initialMargin / m_dContractSize; + } else { + margin = lots * price; + } + break; + case CFDIndex: + if (initialMargin > 0) { + margin = lots * initialMargin / m_dContractSize; + } else if (m_dTickSize != 0) { + margin = lots * price / m_dTickSize * m_dTickValue; + } + break; + case CFDLeverage: + if (initialMargin > 0) { + margin = (lots * initialMargin) / (m_dContractSize * leverage); + } else { + margin = lots * price / leverage; + } + break; + case CalcMode5: + if (initialMargin > 0) { + margin = lots * initialMargin / m_dContractSize; + } else { + margin = lots; + } + break; + case FORTSFutures: + if (m_dTickSize > 0) { + double rate = (m_dTickValue / m_dTickSize) * (s1F0 * 0.01 + 1.0); + switch (type) { + case Buy: + if (bPrice) { + margin = m_dInitialMargin + (price - m_dSettlementPrice) * rate; + } else { + margin = m_dInitialMargin + (m_dUpperLimit - m_dSettlementPrice) * rate; + } + margin *= lots / m_dContractSize; + break; + case Sell: + if (bPrice) { + margin = m_dMaintenanceMargin + (m_dSettlementPrice - price) * rate; + } else { + margin = m_dMaintenanceMargin + (m_dSettlementPrice - m_dLowerLimit) * rate; + } + margin *= lots / m_dContractSize; + break; + case BuyLimit: + case BuyStopLimit: + margin = m_dInitialMargin + (price - m_dSettlementPrice) * rate; + margin *= lots / m_dContractSize; + break; + case SellLimit: + case SellStopLimit: + margin = m_dMaintenanceMargin + (m_dSettlementPrice - price) * rate; + margin *= lots / m_dContractSize; + break; + case BuyStop: + margin = m_dInitialMargin + (m_dUpperLimit - m_dSettlementPrice) * rate; + margin *= lots / m_dContractSize; + break; + case SellStop: + margin = m_dInitialMargin + (m_dSettlementPrice - m_dLowerLimit) * rate; + margin *= lots / m_dContractSize; + break; + default: + margin = lots * initialMargin / m_dContractSize; + break; + } + } + break; + case ExchangeBounds: + if (initialMargin > 0) { + margin = lots * initialMargin / m_dContractSize; + } else { + margin = + Math.round((lots * price * m_dFaceValue * 0.01) * Math.pow(10, sE8)) + / Math.pow(10, sE8); + } + break; + case Collateral: + break; + } + return margin; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private ulong m_lVolume; + private long m_lVolume; // 218 + private double m_dLots; // 220 + private double m_dOpenPrice; // 228 + private double m_dPrice; // 230 + private double m_dMargin; // 238 + private double m_dBuyMargin; // 240 + private double m_dSellMargin; // 248 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private ulong m_lBuyVolume; + private long m_lBuyVolume; // 250 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private ulong m_lSellVolume; + private long m_lSellVolume; // 258 + private boolean m_bOrder; // 260 + private double m_dTradeMargin; // 268 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private ulong m_lTradeVolume; + private long m_lTradeVolume; // 270 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private ulong m_lSpreadVolume; + private long m_lSpreadVolume; // 278 + private OrderType m_TradeType = OrderType.values()[0]; // 280 + private double m_dMarginRate; // 288 + private double s1E8; + + public final double GetTradeMargin(SymbolInfo sym, Order order, TradesInfo trdInfo) + throws IOException { + double buyMargin = m_dBuyMargin; + double sellMargin = m_dSellMargin; + String m_sProfitCurrency = Api.Symbols.Base.Currency; + OrderType m_Type = order.OrderType; + + m_dTradeMargin = 0; + if (sym.CalcMode == CalculationMode.Collateral) { + m_lTradeVolume = 0; + m_TradeType = OrderType.Buy; + m_dMarginRate = 0; + if (order.OrderType == OrderType.Buy) { + double profitRate = GetAskProfitRate(m_sProfitCurrency, sym.Name); + double price = m_dPrice; + if (price == 0) { + price = GetPrice(order); + } + double volume = + Math.round((m_dLots * price) * Math.pow(10, sym.Digits)) / Math.pow(10, sym.Digits); + buyMargin = + Math.round((volume * profitRate * s1E8) * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits); + trdInfo.m_dAssets = RoundAdd(trdInfo.m_dAssets, buyMargin, sym.Digits); + trdInfo.m_dCollateral = RoundAdd(trdInfo.m_dCollateral, buyMargin, sym.Digits); + } + if (order.OrderType == OrderType.Sell) { + double profitRate = GetBidProfitRate(m_sProfitCurrency, sym.Name); + double price = m_dPrice; + if (price == 0) { + price = GetPrice(order); + } + double volume = + Math.round((m_dLots * price) * Math.pow(10, sym.Digits)) / Math.pow(10, sym.Digits); + sellMargin = + -Math.round((volume * profitRate) * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits); + trdInfo.m_dLiabilities = RoundAdd(trdInfo.m_dLiabilities, sellMargin, sym.Digits); + trdInfo.m_dCollateral = RoundAdd(trdInfo.m_dCollateral, sellMargin, sym.Digits); + } + return m_dTradeMargin; + } + + if (m_Type == OrderType.Buy) { + buyMargin += m_dMargin; + if (sym.CalcMode == CalculationMode.FORTSFutures) { + sellMargin -= + Math.round( + (CalcDefMargin( + Api.Account.Leverage, + OrderType.Sell, + false, + true, + m_dLots, + m_dOpenPrice, + sym)) + * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits); + } else if (m_dSellMargin != 0 && m_lSellVolume != 0) { + sellMargin -= + Math.round( + (m_dSellMargin / ULL2DBL(m_lSellVolume) * ULL2DBL(m_lVolume)) + * Math.pow(10, trdInfo.m_nDigits)) + / Math.pow(10, trdInfo.m_nDigits); + } + } + if (m_Type == OrderType.Sell) { + sellMargin += m_dMargin; + if (sym.CalcMode == CalculationMode.FORTSFutures) { + buyMargin -= + Math.round( + (CalcDefMargin( + Api.Account.Leverage, + OrderType.Buy, + false, + true, + m_dLots, + m_dOpenPrice, + sym)) + * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits); + } else if (m_dBuyMargin != 0 && m_lBuyVolume != 0) { + buyMargin -= + Math.round( + (m_dBuyMargin / ULL2DBL(m_lBuyVolume) * ULL2DBL(m_lVolume)) + * Math.pow(10, trdInfo.m_nDigits)) + / Math.pow(10, trdInfo.m_nDigits); + } + } + + double margin = Math.max(buyMargin, sellMargin); + m_dTradeMargin = margin; + m_lTradeVolume = 0; + m_dMarginRate = 0; + m_TradeType = OrderType.Buy; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong buyVolume = m_lBuyVolume; + long buyVolume = m_lBuyVolume; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong sellVolume = m_lSellVolume; + long sellVolume = m_lSellVolume; + if (m_lVolume != 0) { + if (m_Type == OrderType.Buy) { + buyVolume += m_lVolume; + } + if (m_Type == OrderType.Sell) { + sellVolume += m_lVolume; + } + } + if (buyVolume > sellVolume) { + m_TradeType = OrderType.Buy; + m_lTradeVolume = buyVolume; + } else if (buyVolume < sellVolume) { + m_TradeType = OrderType.Sell; + m_lTradeVolume = sellVolume; + } else { + m_TradeType = m_Type; + m_lTradeVolume = buyVolume; + } + if (m_lTradeVolume != 0) { + m_dMarginRate = margin / ULL2DBL(m_lTradeVolume); + } + trdInfo.m_dMargin = RoundAdd(trdInfo.m_dMargin, margin, sym.Digits); + return m_dTradeMargin; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: double ULL2DBL(ulong value) + private double ULL2DBL(long value) { + return (double) value; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProfit.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProfit.java new file mode 100644 index 00000000000..de7c35a226c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProfit.java @@ -0,0 +1,297 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; + +class OrderProfit { + private MT5API QC; + + public OrderProfit(MT5API qc) { + QC = qc; + } + + public final void Update(Order order, double bid, double ask) throws IOException { + SymbolInfo sym = QC.Symbols.GetInfo(order.Symbol); + UpdateSymbolTick(sym, bid, ask); + if (sym.CalcMode == CalculationMode.Futures + || sym.CalcMode == CalculationMode.ExchangeFutures + || sym.CalcMode == CalculationMode.FORTSFutures) { + double price = 0.0; + if (order.OrderType == OrderType.Buy) { + price = + (QC.GetQuote(order.Symbol).Bid - order.OpenPrice) * sym.bid_tickvalue / sym.tick_size; + } else if (order.OrderType == OrderType.Sell) { + price = + (order.OpenPrice - QC.GetQuote(order.Symbol).Ask) * sym.ask_tickvalue / sym.tick_size; + } + order.Profit = price * order.Lots; + } else { + if (order.OrderType == OrderType.Buy) { + order.Profit = + (Math.round((QC.GetQuote(order.Symbol).Bid) * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits) + - Math.round((order.OpenPrice) * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits)) + * order.Lots + * (0.0 == sym.ask_tickvalue ? 1.0 : sym.ask_tickvalue); + } else if (order.OrderType == OrderType.Sell) { + order.Profit = + (Math.round((order.OpenPrice) * Math.pow(10, sym.Digits)) / Math.pow(10, sym.Digits) + - Math.round((QC.GetQuote(order.Symbol).Ask) * Math.pow(10, sym.Digits)) + / Math.pow(10, sym.Digits)) + * order.Lots + * (0.0 == sym.ask_tickvalue ? 1.0 : sym.ask_tickvalue); + } + } + order.Profit = Math.round(order.Profit * Math.pow(10, sym.Digits)) / Math.pow(10, sym.Digits); + } + + public final boolean GetTickRate(String symbol, Quote rate) throws IOException { + if (!QC.Symbols.Exist(symbol)) { + return false; + } + while (QC.GetQuote(symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + Quote q = QC.GetQuote(symbol); + rate.Bid = q.Bid; + rate.Ask = q.Ask; + return true; + } + + private boolean memcmp(String str1, int ind, String str2, int count) { + return memcmp(str1.substring(3), str2, count); + } + + private boolean memcmp(String str1, String str2, int count) { + for (int i = 0; i < count; i++) { + if (i == str1.length()) { + if (str1.length() == str2.length()) { + return true; + } else { + return false; + } + } + if (i == str2.length()) { + if (str1.length() == str2.length()) { + return true; + } else { + return false; + } + } + if (str1.charAt(i) != str2.charAt(i)) { + return false; + } + } + return true; + } + + private void memcpy(RefObject dst, int dstind, String src, int srcind, int count) { + String res = dst.argValue.substring(0, dstind); + for (int i = 0; i < count; i++) { + if (i + srcind >= src.length()) { + break; + } else { + res += src.charAt(i + srcind); + } + } + for (int i = dstind + count; i < dst.argValue.length(); i++) { + res += dst.argValue.charAt(i); + } + dst.argValue = res; + } + + private void memcpy(RefObject dst, String src, int count) { + String res = ""; + for (int i = 0; i < count; i++) { + res += src.charAt(i); + } + for (int i = count; i < dst.argValue.length(); i++) { + res += dst.argValue.charAt(i); + } + dst.argValue = res; + } + + boolean UpdateSymbolTick(SymbolInfo sym, double bid, double ask) throws IOException { + Quote rate = new Quote(); + ; + String sym_symbol = sym.Name; + String cur1 = sym_symbol; + String cur2 = ""; + if (sym.CalcMode == CalculationMode.Forex) { + if (memcmp(cur1, QC.Symbols.Base.Currency, 3)) { + sym.bid_tickvalue = sym.ContractSize / bid; + sym.ask_tickvalue = sym.ContractSize / ask; + } else if (memcmp(cur1, 3, QC.Symbols.Base.Currency, 3)) { + sym.bid_tickvalue = sym.ContractSize; + sym.ask_tickvalue = sym.ContractSize; + } else { + RefObject tempRef_cur2 = new RefObject(cur2); + memcpy(tempRef_cur2, QC.Symbols.Base.Currency, 3); + cur2 = tempRef_cur2.argValue; + RefObject tempRef_cur22 = new RefObject(cur2); + memcpy(tempRef_cur22, 3, cur1, 3, 3); + cur2 = tempRef_cur22.argValue; + RefObject tempRef_cur23 = new RefObject(cur2); + memcpy(tempRef_cur23, 6, sym_symbol, 6, 6); + cur2 = tempRef_cur23.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue = sym.ContractSize / rate.Bid; + sym.ask_tickvalue = sym.ContractSize / rate.Ask; + } else { + RefObject tempRef_cur24 = new RefObject(cur2); + memcpy(tempRef_cur24, 0, cur1, 3, 3); + cur2 = tempRef_cur24.argValue; + RefObject tempRef_cur25 = new RefObject(cur2); + memcpy(tempRef_cur25, 3, QC.Symbols.Base.Currency, 0, 3); + cur2 = tempRef_cur25.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue = sym.ContractSize * rate.Bid; + sym.ask_tickvalue = sym.ContractSize * rate.Ask; + } else { + RefObject tempRef_cur26 = new RefObject(cur2); + memcpy(tempRef_cur26, "USD", 3); + cur2 = tempRef_cur26.argValue; + RefObject tempRef_cur27 = new RefObject(cur2); + memcpy(tempRef_cur27, 3, cur1, 3, 3); + cur2 = tempRef_cur27.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue = sym.ContractSize / rate.Bid; + sym.ask_tickvalue = sym.ContractSize / rate.Ask; + } else { + RefObject tempRef_cur28 = new RefObject(cur2); + memcpy(tempRef_cur28, 0, cur1, 3, 3); + cur2 = tempRef_cur28.argValue; + RefObject tempRef_cur29 = new RefObject(cur2); + memcpy(tempRef_cur29, 3, "USD", 0, 3); + cur2 = tempRef_cur29.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue = sym.ContractSize * rate.Bid; + sym.ask_tickvalue = sym.ContractSize * rate.Ask; + } + } + RefObject tempRef_cur210 = new RefObject(cur2); + memcpy(tempRef_cur210, "USD", 3); + cur2 = tempRef_cur210.argValue; + RefObject tempRef_cur211 = new RefObject(cur2); + memcpy(tempRef_cur211, 3, QC.Symbols.Base.Currency, 0, 3); + cur2 = tempRef_cur211.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue *= rate.Bid; + sym.ask_tickvalue *= rate.Ask; + } else { + RefObject tempRef_cur212 = new RefObject(cur2); + memcpy(tempRef_cur212, QC.Symbols.Base.Currency, 3); + cur2 = tempRef_cur212.argValue; + RefObject tempRef_cur213 = new RefObject(cur2); + memcpy(tempRef_cur213, 3, "USD", 0, 3); + cur2 = tempRef_cur213.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue /= rate.Bid; + sym.ask_tickvalue /= rate.Ask; + } + } + } + } + } + return (sym.bid_tickvalue > 0) + && (sym.bid_tickvalue < Double.MAX_VALUE) + && (sym.ask_tickvalue > 0) + && (sym.ask_tickvalue < Double.MAX_VALUE); + } + double dTick = (sym.CalcMode == CalculationMode.Futures) ? sym.TickValue : sym.ContractSize; + sym.bid_tickvalue = dTick; + sym.ask_tickvalue = dTick; + if (!DotNetToJavaStringHelper.stringsEqual(sym.ProfitCurrency, QC.Symbols.Base.Currency)) { + RefObject tempRef_cur214 = new RefObject(cur2); + memcpy(tempRef_cur214, QC.Symbols.Base.Currency, 3); + cur2 = tempRef_cur214.argValue; + RefObject tempRef_cur215 = new RefObject(cur2); + memcpy(tempRef_cur215, 3, sym.ProfitCurrency, 0, 3); + cur2 = tempRef_cur215.argValue; + if (cur2.length() > 6) { + cur2 = cur2.substring(0, 6); // cur2[6] = 0; + } + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue /= rate.Bid; + sym.ask_tickvalue = sym.bid_tickvalue; + } else { + RefObject tempRef_cur23 = new RefObject(cur2); + memcpy(tempRef_cur23, 6, sym_symbol, 6, 6); + cur2 = tempRef_cur23.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue /= rate.Bid; + sym.ask_tickvalue = sym.bid_tickvalue; + } else { + RefObject tempRef_cur216 = new RefObject(cur2); + memcpy(tempRef_cur216, sym.ProfitCurrency, 3); + cur2 = tempRef_cur216.argValue; + RefObject tempRef_cur217 = new RefObject(cur2); + memcpy(tempRef_cur217, 3, QC.Symbols.Base.Currency, 0, 3); + cur2 = tempRef_cur217.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue *= rate.Bid; + sym.ask_tickvalue = sym.bid_tickvalue; + } + RefObject tempRef_cur218 = new RefObject(cur2); + memcpy(tempRef_cur218, "USD", 3); + cur2 = tempRef_cur218.argValue; + RefObject tempRef_cur219 = new RefObject(cur2); + memcpy(tempRef_cur219, 3, sym.ProfitCurrency, 0, 3); + cur2 = tempRef_cur219.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue /= rate.Bid; + sym.ask_tickvalue = sym.bid_tickvalue; + } else { + RefObject tempRef_cur220 = new RefObject(cur2); + memcpy(tempRef_cur220, sym.ProfitCurrency, 3); + cur2 = tempRef_cur220.argValue; + RefObject tempRef_cur221 = new RefObject(cur2); + memcpy(tempRef_cur221, 3, "USD", 0, 3); + cur2 = tempRef_cur221.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue *= rate.Bid; + sym.ask_tickvalue *= rate.Ask; + // ->94111D + } else { + sym.bid_tickvalue = 0; + sym.ask_tickvalue = 0; + return false; + } + } + RefObject tempRef_cur222 = new RefObject(cur2); + memcpy(tempRef_cur222, "USD", 3); + cur2 = tempRef_cur222.argValue; + RefObject tempRef_cur223 = new RefObject(cur2); + memcpy(tempRef_cur223, 3, QC.Symbols.Base.Currency, 0, 3); + cur2 = tempRef_cur223.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue *= rate.Bid; + sym.ask_tickvalue *= rate.Ask; + } else { + RefObject tempRef_cur224 = new RefObject(cur2); + memcpy(tempRef_cur224, QC.Symbols.Base.Currency, 3); + cur2 = tempRef_cur224.argValue; + RefObject tempRef_cur225 = new RefObject(cur2); + memcpy(tempRef_cur225, 3, "USD", 0, 3); + cur2 = tempRef_cur225.argValue; + if (GetTickRate(cur2, rate)) { + sym.bid_tickvalue /= rate.Bid; + sym.ask_tickvalue /= rate.Ask; + } else { + sym.bid_tickvalue = 0; + sym.ask_tickvalue = 0; + return false; + } + } + } + } + } + return (sym.bid_tickvalue > 0) + && (sym.bid_tickvalue < Double.MAX_VALUE) + && (sym.ask_tickvalue > 0) + && (sym.ask_tickvalue < Double.MAX_VALUE); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgress.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgress.java new file mode 100644 index 00000000000..ff319ff9e87 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgress.java @@ -0,0 +1,19 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class OrderProgress extends FromBufReader { + public TransactionInfo OrderUpdate; + public TradeRequest TradeRequest; + public TradeResult TradeResult; + public DealsResult DealsResult; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 0; + OrderProgress st = new OrderProgress(); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgressEventArgs.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgressEventArgs.java new file mode 100644 index 00000000000..525b2a18844 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderProgressEventArgs.java @@ -0,0 +1,42 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Order progress event arguments. */ +// C# TO JAVA CONVERTER WARNING: Java does not allow user-defined value types. The behavior of this +// class will differ from the original: +// ORIGINAL LINE: public struct OrderProgressEventArgs +public final class OrderProgressEventArgs { + /** Temporary ID. Useful until server assign ticket number. */ + public int TempID; + + /** Stage of order processing by server. */ + public ProgressType Type = ProgressType.values()[0]; + + /** Opened/closed order. */ + // public Order Order; + /** Exception during processing of the order. Could be ServerException or RequoteException. */ + public RuntimeException Exception; + + /** + * Converts to string. + * + * @return "TempID Type Exception" + */ + @Override + public String toString() { + String res = TempID + " " + Type + " " + Exception; + if (Exception != null) { + res += " " + Exception.getMessage(); + } + return res; + } + + public OrderProgressEventArgs clone() { + OrderProgressEventArgs varCopy = new OrderProgressEventArgs(); + + varCopy.TempID = this.TempID; + varCopy.Type = this.Type; + varCopy.Exception = this.Exception; + + return varCopy; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderSender.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderSender.java new file mode 100644 index 00000000000..427f87ef2e4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderSender.java @@ -0,0 +1,136 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.*; +import java.util.Enumeration; +import javax.net.ssl.KeyManagerFactory; + +public class OrderSender { + private Connection Connection; + + public OrderSender(Connection connection) { + Connection = connection; + } + + public final void Send(TradeRequest req) throws IOException { + OutBuf buf = new OutBuf(); + buf.Add((byte) (0 & 0xFF)); + WriteRequest(buf, req, 96, Connection.TradeBuild); + if (Connection.TradeBuild <= 1891) { + req.Lots /= 10000; + req.s174 /= 10000; + } + ProtectTradeRequest(req, buf); + Connection.SendCompress((byte) 0x6C, buf); + } + + private void ProtectTradeRequest(TradeRequest req, OutBuf buf) { + if (Connection.TradeBuild > 1349) { + CryptRequestProtectKey(req, buf); + } else { + CryptRequestAccountLogon(req, buf); + } + } + + private void CryptRequestProtectKey(TradeRequest req, OutBuf buf) { + if (Connection.ProtectKey == null) { + if ((Connection.QC.Account.TradeFlags & 8) != 0) throw new RuntimeException("Investor mode"); + else { + try { + KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance("SunX509"); + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load( + new ByteArrayInputStream(Connection.QC.PfxFile), + Connection.QC.PfxFilePassword.toCharArray()); + kmf.init(keystore, Connection.QC.PfxFilePassword.toCharArray()); + Enumeration aliases = keystore.aliases(); + PrivateKey key = null; + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + key = (PrivateKey) keystore.getKey(alias, Connection.QC.PfxFilePassword.toCharArray()); + } + if (key == null) throw new RuntimeException("RSA private key not found"); + Signature privateSignature = Signature.getInstance("SHA1withRSA"); + privateSignature.initSign(key); + privateSignature.update(UDT.GetBytes(req)); + byte[] sign = privateSignature.sign(); + Connection.reverse(sign); + buf.ByteToBuffer((byte) 0x2C); + buf.LongToBuffer(sign.length); + buf.DataToBuffer(sign); + return; + } catch (Exception ex) { + throw new RuntimeException("Invalid cerificate: " + ex.getMessage()); + } + } + } + // byte[] key = {(byte) 0xd1, 0x68, 0x5a, 0x59, (byte) 0xde, 0x51, (byte) 0xf9, (byte) 0xdd, + // 0x65, (byte) 0x83, (byte) 0xd7, 0x1b, (byte) 0x89, 0x18, (byte) 0xbe, (byte) 0xa9, 0x4c, + // 0x0a, 0x6b, 0x7d, (byte) 0xfb, 0x66, (byte) 0xab, (byte) 0x8a, 0x53, 0x0a, (byte) 0xeb, 0x7e, + // 0x6b, 0x68, 0x31, 0x50}; + byte[] key = new byte[32]; + System.arraycopy(Connection.ProtectKey, 0, key, 0, Connection.ProtectKey.length); + byte[] mod1 = new byte[64]; + System.arraycopy(key, 0, mod1, 0, key.length); + byte[] mod2 = new byte[64]; + System.arraycopy(key, 0, mod2, 0, key.length); + for (int i = 0; i < 64; i++) { + mod1[i] ^= 0x36; + mod2[i] ^= 0x5C; + } + byte[] data = UDT.GetBytes(req); + byte[] hash1 = new byte[32]; + Sha256 sha = new Sha256(); + sha.AddData(mod1); + sha.AddData(data); + copyTo(sha.GetHash(), hash1); + sha = new Sha256(); + sha.AddData(mod2); + sha.AddData(hash1); + byte[] hash2 = new byte[32]; + copyTo(sha.GetHash(), hash2); + buf.Add((byte) (0x55 & 0xFF)); + buf.Add((int) 32); + buf.Add(hash2); + } + + void copyTo(byte[] src, byte[] dst) { + System.arraycopy(src, 0, dst, 0, src.length); + } + + private void CryptRequestAccountLogon(TradeRequest req, OutBuf buf) { + throw new UnsupportedOperationException(); + } + + private void WriteRequest(OutBuf buf, TradeRequest req, int mode, int tradeBuild) { + switch (mode) { + case 0: + case 4: + case 5: + case 7: + case 8: + case 9: + case 32: + case 33: + case 34: + case 35: + case 36: + case 66: + case 67: + case 69: + case 73: + case 96: + case 97: + case 98: + case 99: + case 100: + case 101: + if (tradeBuild > 1891) { + break; + } + throw new UnsupportedOperationException(); + } + buf.Add(UDT.GetBytes(req)); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderState.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderState.java new file mode 100644 index 00000000000..a9c2f9805e4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderState.java @@ -0,0 +1,43 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum OrderState { + Started(0), + Placed(1), + Cancelled(2), + Partial(3), + Filled(4), + Rejected(5), + Expired(6), + RequestAdding(7), + RequestModifying(8), + RequestCancelling(9); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (OrderState.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private OrderState(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static OrderState forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderType.java new file mode 100644 index 00000000000..624ca526cbf --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderType.java @@ -0,0 +1,42 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum OrderType { + Buy(0), + Sell(1), + BuyLimit(2), + SellLimit(3), + BuyStop(4), + SellStop(5), + BuyStopLimit(6), + SellStopLimit(7), + CloseBy(8); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (OrderType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private OrderType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static OrderType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderUpdate.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderUpdate.java new file mode 100644 index 00000000000..7e2e3bb1cff --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OrderUpdate.java @@ -0,0 +1,10 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class OrderUpdate { + public TransactionInfo Trans; + public OrderInternal OrderInternal; + public DealInternal Deal; + public DealInternal OppositeDeal; + public UpdateType Type; + public Order Order; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OutBuf.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OutBuf.java new file mode 100644 index 00000000000..cb4b02f066d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/OutBuf.java @@ -0,0 +1,146 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; + +class OutBuf { + + public ArrayList List; + + public OutBuf() { + List = new ArrayList(); + } + + public OutBuf(byte[] bytes) { + List = convertBytesToList(bytes); + } + + private static ArrayList convertBytesToList(byte[] bytes) { + final ArrayList list = new ArrayList<>(); + for (byte b : bytes) { + list.add(b); + } + return list; + } + + public final void CreateHeader(byte type, int id, boolean compressed) { + byte[] hdr = new byte[9]; + hdr[0] = type; // type + System.arraycopy( + BitConverter.GetBytes(List.size()), + 0, + hdr, + 1, + BitConverter.GetBytes(List.size()).length); // size + System.arraycopy( + BitConverter.GetBytes((short) id), + 0, + hdr, + 5, + BitConverter.GetBytes((short) id).length); // ID + if (compressed) { + System.arraycopy( + BitConverter.GetBytes((short) 3), 0, hdr, 7, BitConverter.GetBytes((short) 3).length); + } else { + System.arraycopy( + BitConverter.GetBytes((short) 2), + 0, + hdr, + 7, + BitConverter.GetBytes((short) 2).length); // Flags PHF_COMPLETE + } + ByteLists.addPrimitiveArrayToList(0, hdr, List); + } + + public final byte[] ToArray() { + return ByteLists.toArray(List); + } + + public final void Add(byte b) { + List.add(b); + } + + public final void Add(long l) { + Add(BitConverter.GetBytes(l)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void Add(ushort l) + public final void Add(short l) { + Add(BitConverter.GetBytes(l)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void Add(uint l) + public final void Add(int l) { + Add(BitConverter.GetBytes(l)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void Add(byte[] bytes) + public final void Add(byte[] bytes) { + ByteLists.addPrimitiveArrayToList(bytes, List); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void ByteToBuffer(byte v) + public final void ByteToBuffer(byte v) { + Add(v); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void LongToBuffer(uint v) + public final void LongToBuffer(int v) { + Add(v); + } + + public final void IntToBuffer(int v) { + Add(v); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void WordToBuffer(ushort v) + public final void WordToBuffer(short v) { + Add(v); + } + + public final void LongLongToBuffer(long v) { + Add(v); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void DataToBuffer(byte[] v) + public final void DataToBuffer(byte[] v) { + Add(v); + } + + public final void Add(String str, int len) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var res = new byte[len*4]; + byte[] res = new byte[len * 4]; + if (str != null) { + System.arraycopy( + str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE), + 0, + res, + 0, + str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE).length); + } + Add(res); + } + + public final void Add(double price) { + Add(BitConverter.GetBytes(price)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal void Add(byte[] ar, int len) + public final void Add(byte[] ar, int len) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var res = new byte[len]; + byte[] res = new byte[len]; + if (ar != null) { + System.arraycopy(ar, 0, res, 0, ar.length); + } + Add(res); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackDecrypt.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackDecrypt.java new file mode 100644 index 00000000000..3ba8fe80717 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackDecrypt.java @@ -0,0 +1,30 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class PackDecrypt { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte DecryptByte = 0; + private byte DecryptByte = 0; + private int DecryptIndex = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] CryptKey; + private byte[] CryptKey; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal PackDecrypt(byte[] cryptKey) + public PackDecrypt(byte[] cryptKey) { + CryptKey = cryptKey; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal byte[] Decrypt(byte[] bytes) + public final byte[] Decrypt(byte[] bytes) { + for (int i = 0; i < bytes.length; i++) { + bytes[i] ^= + (byte) + (((DecryptByte & 0xFF) + (CryptKey[DecryptIndex % CryptKey.length] & 0xFF)) & 0xFF); + DecryptIndex++; + DecryptByte = bytes[i]; + } + return bytes; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackEncrypt.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackEncrypt.java new file mode 100644 index 00000000000..0ace2c4a38a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PackEncrypt.java @@ -0,0 +1,32 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class PackEncrypt { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte EncryptByte = 0; + private byte EncryptByte = 0; + private int EncryptIndex = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal byte[] CryptKey; + public byte[] CryptKey; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: internal PackEncrypt(byte[] cryptKey) + public PackEncrypt(byte[] cryptKey) { + CryptKey = cryptKey; + } + + public final void EncryptPacket(OutBuf buf) { + for (int i = 9; i < buf.List.size(); i++) { + byte b = buf.List.get(i); + buf.List.set( + i, + (byte) + ((byte) buf.List.get(i) + ^ (byte) + (((EncryptByte & 0xFF) + (CryptKey[EncryptIndex % CryptKey.length] & 0xFF)) + & 0xFF))); + EncryptIndex++; + EncryptByte = b; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdr.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdr.java new file mode 100644 index 00000000000..5f17d718676 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdr.java @@ -0,0 +1,34 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Size = 0x09, CharSet = CharSet.Unicode)]*/ +public class PacketHdr extends FromBufReader // sizeof 0x09 c +{ + /*[FieldOffset(0)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte Type; + public byte Type; // 0 + /*[FieldOffset(1)]*/ public int PacketSize; // 1 + /*[FieldOffset(5)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ushort Id; + public short Id; // 5 + /*[FieldOffset(7)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ushort Flags; + public short Flags; // 7 + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 9; + PacketHdr st = new PacketHdr(); // sizeof 0x09 c(); + st.Type = buf.Byte(); + st.PacketSize = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Id = BitConverter.ToInt16(buf.Bytes(2), 0); + st.Flags = BitConverter.ToInt16(buf.Bytes(2), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdrEx.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdrEx.java new file mode 100644 index 00000000000..64f64300f68 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PacketHdrEx.java @@ -0,0 +1,43 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Size = 0x11, CharSet = CharSet.Unicode)]*/ +public class PacketHdrEx extends FromBufReader { // sizeof 0x11 c + /*[FieldOffset(0)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte Type; + public byte Type; // 0 + /*[FieldOffset(1)]*/ public int PacketSize; // 1 + /*[FieldOffset(5)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ushort Id; + public short Id; // 5 + /*[FieldOffset(7)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ushort Flags; + public short Flags; // 7 + /*[FieldOffset(9)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint m_nOriginalSize; + public int m_nOriginalSize; // 9 + /*[FieldOffset(13)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint m_nCompressSize; + public int m_nCompressSize; // D + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 17; + PacketHdrEx st = new PacketHdrEx(); // sizeof 0x11 c(); + st.Type = buf.Byte(); + st.PacketSize = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Id = BitConverter.ToUInt16(buf.Bytes(2), 0); + st.Flags = BitConverter.ToUInt16(buf.Bytes(2), 0); + st.m_nOriginalSize = (int) BitConverter.ToUInt32(buf.Bytes(4), 0); + st.m_nCompressSize = (int) BitConverter.ToUInt32(buf.Bytes(4), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingCloseWaiter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingCloseWaiter.java new file mode 100644 index 00000000000..bfdf651a7d0 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingCloseWaiter.java @@ -0,0 +1,63 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; + +class PendingCloseWaiter implements OnOrderProgress, OnOrderUpdate { + private MT5API Client; + private int Id; + private int Timeout; + private OrderProgress Progr; + private Order Order; + private long Ticket; + + public PendingCloseWaiter(MT5API client, int id, int timeout, long ticket) { + Client = client; + Id = id; + Timeout = timeout; + Ticket = ticket; + Client.ProgressWaiters.add(this); + Client.UpdateWaiters.add(this); + } + + public void invoke(MT5API sender, OrderUpdate update) { + if (update.OrderInternal != null) + if (update.OrderInternal.TicketNumber == Ticket) + if (update.OrderInternal.State == OrderState.Cancelled) + Order = new Order(update.OrderInternal); + } + + public void invoke(MT5API sender, OrderProgress progress) { + if (progress.TradeRequest.RequestId == Id) Progr = progress; + } + + public Order Wait() { + try { + return WaitInternal(); + } finally { + Client.ProgressWaiters.remove(this); + Client.UpdateWaiters.remove(this); + } + } + + public Order WaitInternal() { + LocalDateTime start = LocalDateTime.now(); + while (true) { + if (Duration.between(start, LocalDateTime.now()).toMillis() > Timeout) + throw new RuntimeException("Trade timeout"); + if (Progr != null) { + Msg status = Progr.TradeResult.Status; + if (status != Msg.REQUEST_ACCEPTED + && status != Msg.REQUEST_ON_WAY + && status != Msg.REQUEST_EXECUTED + && status != Msg.DONE + && status != Msg.ORDER_PLACED) throw new RuntimeException(status.toString()); + } + if (Order != null) return Order; + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingOpenWaiter.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingOpenWaiter.java new file mode 100644 index 00000000000..3a091657582 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PendingOpenWaiter.java @@ -0,0 +1,70 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.concurrent.ConcurrentLinkedQueue; + +class PendingOpenWaiter implements OnOrderProgress, OnOrderUpdate { + private MT5API Client; + private int Id; + private int Timeout; + private OrderProgress Progr; + private Order Order; + private long Ticket; + private ConcurrentLinkedQueue Orders = new ConcurrentLinkedQueue(); + + public PendingOpenWaiter(MT5API client, int id, int timeout) { + Client = client; + Id = id; + Timeout = timeout; + Client.ProgressWaiters.add(this); + Client.UpdateWaiters.add(this); + } + + public void invoke(MT5API sender, OrderUpdate update) { + if (update.OrderInternal != null) + if (Ticket == 0) Orders.add(new Order(update.OrderInternal)); + else if (update.OrderInternal.TicketNumber == Ticket) Order = new Order(update.OrderInternal); + } + + public void invoke(MT5API sender, OrderProgress progress) { + if (progress.TradeRequest.RequestId == Id) Progr = progress; + } + + public Order Wait() { + try { + return WaitInternal(); + } finally { + Client.ProgressWaiters.remove(this); + Client.UpdateWaiters.remove(this); + } + } + + Order WaitInternal() { + java.time.LocalDateTime start = java.time.LocalDateTime.now(); + while (true) { + if (Duration.between(start, LocalDateTime.now()).toMillis() > Timeout) { + throw new RuntimeException("Trade timeout"); + } + if (Progr != null) { + Msg status = Progr.TradeResult.Status; + if (status != Msg.REQUEST_ACCEPTED + && status != Msg.REQUEST_ON_WAY + && status != Msg.REQUEST_EXECUTED + && status != Msg.DONE + && status != Msg.ORDER_PLACED) throw new RuntimeException(status.toString()); + if (Progr != null) { + if (Progr.TradeResult.TicketNumber != 0) { + Ticket = Progr.TradeResult.TicketNumber; + for (Order order : Orders) if (order.Ticket == Ticket) Order = order; + } + } + } + if (Order != null) return Order; + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PlacedType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PlacedType.java new file mode 100644 index 00000000000..e49afecf083 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PlacedType.java @@ -0,0 +1,58 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum PlacedType { + // Manually(0), + // ByExpert(1), + // ByDealer(2), + // OnSL(3), + // OnTP(4), + // OnStopOut(5), + // OnRollover(6), + // Mobile(16), + // Web(17); + Manually(0), // The deal was executed as a result of activation of an order placed from a desktop + // terminal + Mobile(1), // The deal was executed as a result of activation of an order placed from a mobile + // application + Web(2), // The deal was executed as a result of activation of an order placed from the web + // platform + ByExpert(3), // The deal was executed as a result of activation of an order placed from an MQL5 + // program, i.e. an Expert Advisor or a script + OnSL(4), // The deal was executed as a result of Stop Loss activation + OnTP(5), // The deal was executed as a result of Take Profit activation + OnStopOut(6), // The deal was executed as a result of the Stop Out event + OnRollover(7), // The deal was executed due to a rollover + OnVmargin(8), // The deal was executed after charging the variation margin + OnSplit( + 18); // The deal was executed after the split (price reduction) of an instrument, which had an + // open position during split announcement + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (PlacedType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private PlacedType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static PlacedType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProcessEvents.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProcessEvents.java new file mode 100644 index 00000000000..8349a08f990 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProcessEvents.java @@ -0,0 +1,7 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ProcessEvents { + SingleThread, + ThreadPool, + NewThread +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Profit.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Profit.java new file mode 100644 index 00000000000..eea08c0b78f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Profit.java @@ -0,0 +1,407 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; + +class Profit { + private MT5API Api; + + public Profit(MT5API api) { + Api = api; + } + + public final void Calculate(Quote quote) { + for (Order item : Api.GetOpenedOrders()) { + if (item.OrderType == OrderType.Buy || item.OrderType == OrderType.Sell) { + if (DotNetToJavaStringHelper.stringsEqual(item.Symbol, quote.Symbol)) { + try { + double closeprice = item.OrderType == OrderType.Buy ? quote.Ask : quote.Bid; + SymbolInfo sym = Api.Symbols.GetInfo(item.Symbol); + DealInternal deal = item.DealInternalIn; + CalculateProfit( + sym, + deal.Type == DealType.DealBuy, + deal.Lots, + deal.ContractSize, + deal.OpenPrice, + closeprice, + deal); + } catch (RuntimeException | IOException ex) { + Api.Log.exception(ex); + } + } + } + } + } + + private double AsSize(double value, double lots) { + return Math.round((lots * value) * Math.pow(10, 8)) / Math.pow(10, 8); + } + + private double AsLots(double value) { + return Math.round(((double) value * 1.0e-8) * Math.pow(10, 8)) / Math.pow(10, 8); + } + + public final void CalculateProfit( + SymbolInfo sym, + boolean buy, + double volume, + double lots, + double openPrice, + double price, + DealInternal deal) + throws IOException { + double profit; + switch (sym.CalcMode) { + case Forex: + case CalcMode5: + profit = AsSize(volume, lots); + if (buy) { + profit = + Math.round((profit * price) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision) + - Math.round((profit * openPrice) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision); + } else { + profit = + Math.round((profit * openPrice) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision) + - Math.round((profit * price) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision); + } + break; + case Futures: + case ExchangeFutures: + case ExchangeOption: + case ExchangeMarginOption: + profit = AsLots(volume); + if (buy) { + profit *= price - openPrice; + } else { + profit *= openPrice - price; + } + profit *= sym.TickValue; + if (sym.TickSize > 0) { + profit /= sym.TickSize; + } + break; + case FORTSFutures: + { + profit = AsLots(volume); + double v1 = sym.TickValue * openPrice; + double v2 = sym.TickValue * price; + if (sym.TickSize > 0) { + v1 /= sym.TickSize; + v2 /= sym.TickSize; + } + v1 = Math.round(v1 * Math.pow(10, sym.Precision)) / Math.pow(10, sym.Precision); + v2 = Math.round(v2 * Math.pow(10, sym.Precision)) / Math.pow(10, sym.Precision); + if (buy) { + profit *= v2 - v1; + } else { + profit *= v1 - v2; + } + break; + } + case ExchangeBounds: + profit = AsSize(volume, lots); + if (buy) { + profit = + Math.round((profit * price * sym.FaceValue / 100.0) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision) + - Math.round( + (profit * openPrice * sym.FaceValue / 100.0) + * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision); + } else { + profit = + Math.round((profit * openPrice * sym.FaceValue / 100.0) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision) + - Math.round( + (profit * price * sym.FaceValue / 100.0) * Math.pow(10, sym.Precision)) + / Math.pow(10, sym.Precision); + } + break; + case Collateral: + profit = 0; + break; + default: + profit = AsSize(volume, lots); + if (buy) { + profit *= price - openPrice; + } else { + profit *= openPrice - price; + } + break; + } + if ((profit > 1.0e11) || (profit < -1.0e11)) { + profit = 0; + } + // if ((price < 0) || ((sym.CalcMode != CalculationMode.Collateral) && price > 0)) + // profit = 0; + double bidRate = 0, askRate = 0; + RefObject tempRef_bidRate = new RefObject(bidRate); + RefObject tempRef_askRate = new RefObject(askRate); + GetAskBidProfitRate( + sym.ProfitCurrency, + Api.Symbols.Base.Currency, + tempRef_bidRate, + tempRef_askRate, + sym, + price); + askRate = tempRef_askRate.argValue; + bidRate = tempRef_bidRate.argValue; + double rate; + if (((sym.CalcMode == CalculationMode.Forex) || (sym.CalcMode == CalculationMode.CalcMode5)) + && (sym.s550 & 1) == 0) { + rate = buy ? askRate : bidRate; + } else { + rate = (profit > 0) ? bidRate : askRate; + } + profit /= rate; + deal.ProfitRate = rate; + deal.Profit = profit; + // return profit; + // return Math.Round(profit, sym.Precision); + } + + private void GetAskBidProfitRate( + String cur1, + String cur2, + RefObject askpr, + RefObject bidpr, + SymbolInfo sym, + double price) + throws IOException { + if (GetBidAskRate(cur1, cur2, askpr, bidpr, sym, price)) { + return; + } + double toUSDAsk = 0; + double toUSDBid = 0; + double fromUSDAsk = 0; + double fromUSDBid = 0; + RefObject tempRef_toUSDAsk = new RefObject(toUSDAsk); + RefObject tempRef_toUSDBid = new RefObject(toUSDBid); + RefObject tempRef_fromUSDAsk = new RefObject(fromUSDAsk); + RefObject tempRef_fromUSDBid = new RefObject(fromUSDBid); + if (!GetBidAskRate(cur1, "USD", tempRef_toUSDAsk, tempRef_toUSDBid, sym, price) + || !GetBidAskRate("USD", cur2, tempRef_fromUSDAsk, tempRef_fromUSDBid, sym, price)) { + fromUSDBid = tempRef_fromUSDBid.argValue; + fromUSDAsk = tempRef_fromUSDAsk.argValue; + toUSDBid = tempRef_toUSDBid.argValue; + toUSDAsk = tempRef_toUSDAsk.argValue; + bidpr.argValue = 0D; + askpr.argValue = 0D; + return; + } else { + fromUSDBid = tempRef_fromUSDBid.argValue; + fromUSDAsk = tempRef_fromUSDAsk.argValue; + toUSDBid = tempRef_toUSDBid.argValue; + toUSDAsk = tempRef_toUSDAsk.argValue; + } + askpr.argValue = toUSDAsk * fromUSDAsk; + bidpr.argValue = toUSDBid * fromUSDBid; + } + + private boolean GetBidAskRate( + String cur1, + String cur2, + RefObject ask, + RefObject bid, + SymbolInfo sym, + double price) + throws IOException { + String cur = Api.Symbols.ExistStartsWith(cur1 + cur2); + if (cur != null) { + if (price != 0 + && ((sym.CalcMode == CalculationMode.Forex) + || (sym.CalcMode == CalculationMode.CalcMode5)) + && ((sym.s550 & 1) == 0) + && DotNetToJavaStringHelper.stringsEqual(sym.Name, cur)) { + bid.argValue = price; + ask.argValue = price; + return true; + } + Api.Subscribe(cur); + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + bid.argValue = Api.GetQuote(cur).Bid; + ask.argValue = Api.GetQuote(cur).Ask; + return true; + } + cur = Api.Symbols.ExistStartsWith(cur2 + cur1); + if (cur != null) { + if (price != 0 + && ((sym.CalcMode == CalculationMode.Forex) + || (sym.CalcMode == CalculationMode.CalcMode5)) + && ((sym.s550 & 1) == 0) + && DotNetToJavaStringHelper.stringsEqual(sym.Name, cur)) { + bid.argValue = 1.0 / price; + ask.argValue = 1.0 / price; + return true; + } + Api.Subscribe(cur); + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + bid.argValue = Api.GetQuote(cur).Bid; + ask.argValue = Api.GetQuote(cur).Ask; + return true; + } + bid.argValue = 0D; + ask.argValue = 0D; + return false; + } +} +/* + double CalcProfit(SymBaseInfo symBase, SymbolInfo sym, OrderType type, + double openPrice, double price, double volume, double lots, double volumeRate) + { + if (symBase.AccMethod == PosAccMethod.Netting) + { + switch (sym.CalcMode) + { + case CalculationMode.ExchangeStocks: + case CalculationMode.ExchangeBounds: + return CalcExchangeProfit(symBase, sym, type, price, volume, volumeRate); + case CalculationMode.Collateral: + return CalcCollateralProfit(symBase, sym, price, volume); + } + } + else + { + switch (sym.CalcMode) + { + case CalculationMode.Forex: + case CalculationMode.CalcMode5: + CalcForexProfit(symBase, sym, openPrice, volume, lots, volumeRate); + break; + case CalculationMode.Collateral: + return 0; + } + } + return CalcBaseProfit(symBase, sym, type, openPrice, volume, volumeRate); + } + + private double CalcBaseProfit(SymBaseInfo symBase, SymbolInfo sym, OrderType type, double openPrice, double volume, double volumeRate) + { + throw new NotImplementedException(); + } + + private double CalcCollateralProfit(SymBaseInfo symBase, SymbolInfo sym, double price, double volume) + { + throw new NotImplementedException(); + } + + private double CalcExchangeProfit(SymBaseInfo symBase, SymbolInfo sym, OrderType type, double price, double volume, double volumeRate) + { + throw new NotImplementedException(); + } + + struct ProfitStruct //sizeof 0x66 d + { + public string Currency; //0 + public double Lots; //40 + public int SymDigits; //48 + public int BaseDigits; //49 + public double Profit; //4A + public double ProfitRate; //52 + public double Margin; //5A + public int s62; + } + + Dictionary Profits = new Dictionary(); + + void CalcForexProfit(SymBaseInfo symBase, SymbolInfo sym, double price, double volume, double lots, double volumeRate) + { + var baseCurrency = sym.Currency; + baseCurrency = baseCurrency.Substring(0, 3); + ProfitStruct profit; + if (Profits.ContainsKey(baseCurrency)) + profit = Profits[baseCurrency]; + else + { + profit = new ProfitStruct(); + profit.SymDigits = sym.Digits; + profit.BaseDigits = symBase.Digits; + } + if (volume>0) + { + double vl = Math.Round(volume * lots, 8); + double nlots = profit.Lots + vl; + if (nlots > 0) + { + profit.ProfitRate = (profit.Lots * profit.ProfitRate + vl * volumeRate) / nlots; + profit.Lots = nlots; + } + else + { + profit.ProfitRate = 0; + profit.Lots = 0; + } + } + profit.Profit = Math.Round(profit.ProfitRate * profit.Lots / Api.Account.Leverage, sym.Digits); + var quoteCurrency = sym.Currency.Substring(3, 3); + if (Profits.ContainsKey(quoteCurrency)) + profit = Profits[quoteCurrency]; + else + { + profit = new ProfitStruct(); + profit.SymDigits = sym.Digits; + profit.BaseDigits = symBase.Digits; + } + if (volume > 0) + { + double vl = Math.Round(-(volume * lots * price), 8); + double nlots = Math.Round(profit.Lots + vl, 8); + if (nlots > 0) + { + profit.ProfitRate = (profit.Lots * profit.ProfitRate + vl * volumeRate / price) / nlots; + profit.Lots = nlots; + } + else + { + profit.ProfitRate = 0; + profit.Lots = 0; + } + } + profit.Profit = Math.Round(profit.Lots * profit.ProfitRate / Api.Account.Leverage, sym.Digits); + } + +for (int i = 0; i < deals.Count; i++) + { + var deal = deals[i]; + SymbolInfo sym; + try + { + sym = Api.Symbols.GetInfo(deal.Symbol); + } + catch (Exception ex) + { + continue; + } + //double dir; + //if (deal.Type == OrderType.Buy) + // dir = 1.0; + //else if (deal.Type == OrderType.Sell) + // dir = -1.0; + //else + // continue; + while (Api.GetQuote(deal.Symbol) == null) + Thread.Sleep(1); + double price = Api.GetQuote(deal.Symbol).Ask; + if (deal.Type == OrderType.Buy) + price = Api.GetQuote(deal.Symbol).Bid; + //CalcProfit(symBase, sym, deal.Type, deal.OpenPrice, price, + // deal.Lots * dir, deal.ContractSize, deal.VolumeRate); + CalculateProfit(sym, deal.Type == OrderType.Buy, deal.Lots, deal.ContractSize, deal.OpenPrice, price, deal); + } + //vProfitBase::UpdateProfit(symBase, trdInfo); + +*/ diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProgressType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProgressType.java new file mode 100644 index 00000000000..c3739294bf8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ProgressType.java @@ -0,0 +1,39 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Stage of order processing by server. */ +public enum ProgressType { + /** Order was rejected. */ + Rejected, + /** Order was accepted by server. */ + Accepted, + /** Server started to execute the order. */ + InProcess, + /** Order was opened. */ + Opened, + /** Order was closed. */ + Closed, + /** Order was modified. */ + Modified, + /** Pending order was deleted. */ + PendingDeleted, + /** Closed of pair of opposite orders. */ + ClosedBy, + /** Closed of multiple orders. */ + MultipleClosedBy, + /** Trade timeout. */ + Timeout, + /** Price data. */ + Price, + /** Exception. */ + Exception; + + public static final int SIZE = java.lang.Integer.SIZE; + + public int getValue() { + return this.ordinal(); + } + + public static ProgressType forValue(int value) { + return values()[value]; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals5D8.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals5D8.java new file mode 100644 index 00000000000..28ee146d49f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals5D8.java @@ -0,0 +1,70 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xC0, CharSet = CharSet.Unicode)]*/ +public class PumpDeals5D8 extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s0; + public byte[] s0; + /*[FieldOffset(8)]*/ public double Balance; + /*[FieldOffset(16)]*/ public double Credit; + /*[FieldOffset(24)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s18; + public byte[] s18; + /*[FieldOffset(32)]*/ public double s20; + /*[FieldOffset(40)]*/ public double s28; + /*[FieldOffset(48)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 52)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s30; + public byte[] s30; + /*[FieldOffset(100)]*/ public double Blocked; + /*[FieldOffset(108)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 84)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s6C; + public byte[] s6C; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 192; + PumpDeals5D8 st = new PumpDeals5D8(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s0 = new byte[8]; + st.s0 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s0[i] = buf.Byte(); + } + st.Balance = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Credit = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s18 = new byte[8]; + st.s18 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s18[i] = buf.Byte(); + } + st.s20 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s28 = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s30 = new byte[52]; + st.s30 = new byte[52]; + for (int i = 0; i < 52; i++) { + st.s30[i] = buf.Byte(); + } + st.Blocked = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s6C = new byte[84]; + st.s6C = new byte[84]; + for (int i = 0; i < 84; i++) { + st.s6C[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals698.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals698.java new file mode 100644 index 00000000000..1af7431cfa4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/PumpDeals698.java @@ -0,0 +1,27 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x40, CharSet = CharSet.Unicode)]*/ +public class PumpDeals698 extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s0; + public byte[] s0; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 64; + PumpDeals698 st = new PumpDeals698(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s0 = new byte[64]; + st.s0 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s0[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Quote.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Quote.java new file mode 100644 index 00000000000..6f557221625 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Quote.java @@ -0,0 +1,44 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** New quote event arguments. */ +public class Quote { + /** Trading instrument. */ + public String Symbol; + + /** Bid. */ + public double Bid; + + /** Ask. */ + public double Ask; + + /** Server time. */ + public java.time.LocalDateTime Time = java.time.LocalDateTime.MIN; + + /** Last deal price. */ + public double Last; + + /** Volume */ + public long Volume; + + /** Spread */ + public int Spread; + + public long UpdateMask; + public short BankId; + + public Quote() {} + + public Quote(String symbol) { + Symbol = symbol; + } + + /** + * Convert to string. + * + * @return "Symbol Bid Ask" + */ + @Override + public String toString() { + return Symbol + " " + Bid + " " + Ask; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistory.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistory.java new file mode 100644 index 00000000000..d02ca61ab6f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistory.java @@ -0,0 +1,264 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.util.*; + +public class QuoteHistory { + private MT5API QuoteClient; + + public QuoteHistory(MT5API qc) { + QuoteClient = qc; + } + + public static void ReqProcess(Connection connection, String symbol) throws IOException { + OutBuf buf = new OutBuf(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((byte)0xE); + buf.Add((byte) (0xE & 0xFF)); + // C# TO JAVA CONVERTER TODO TASK: There is no equivalent to implicit typing in Java: + byte[] bytes = symbol.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + buf.Add(bytes); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add(new byte[32 * 2 - bytes.Length]); + buf.Add(new byte[32 * 2 - bytes.length]); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((ushort)1); + buf.Add((short) (1 & 0xFFFF)); // size + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((byte)0); + buf.Add((byte) (0 & 0xFF)); // year + buf.Add((int) 0); // time + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((ushort)0); + buf.Add((short) (0 & 0xFFFF)); // CheckDate + connection.SendPacket((byte) 0x66, buf); + } + + public static void ReqStart(Connection connection, String symbol, short date) throws IOException { + OutBuf buf; + buf = new OutBuf(); + buf.Add((byte) 0xE); + byte[] bytes = symbol.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + buf.Add(bytes); + buf.Add(new byte[32 * 2 - bytes.length]); + buf.Add((short) 0); // size + // buf.Add((byte)0); //year + // buf.Add((int)0); //time + buf.Add(date); // CheckDate + connection.SendPacket((byte) 0x66, buf); + } + + public static void ReqSend(Connection connection, String symbol, short a, short b) + throws IOException { + OutBuf buf = new OutBuf(); + buf.Add((byte) 9); + byte[] bytes = symbol.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + buf.Add(bytes); + buf.Add(new byte[32 * 2 - bytes.length]); + buf.Add((short) a); // begin + buf.Add((short) b); // firstDate + connection.SendPacket((byte) 0x66, buf); + } + + public static void ReqSelect(Connection connection, String symbol) throws IOException { + OutBuf buf = new OutBuf(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((byte)9); + buf.Add((byte) (9 & 0xFF)); + // C# TO JAVA CONVERTER TODO TASK: There is no equivalent to implicit typing in Java: + byte[] bytes = symbol.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + buf.Add(bytes); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add(new byte[32 * 2 - bytes.Length]); + buf.Add(new byte[32 * 2 - bytes.length]); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((ushort)1); + buf.Add((short) (1 & 0xFFFF)); // begin + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: buf.Add((ushort)1); + buf.Add((short) (1 & 0xFFFF)); // firstDate + connection.SendPacket((byte) 0x66, buf); + } + + public final void Parse(InBuf buf) { + byte cmd = buf.Byte(); + String symbol = ConvertBytes.ToUnicode(buf.Bytes(64)); + switch ((cmd & 0xFF)) { + case 0x0E: + ParseStart(buf, symbol); + break; + case 9: + QuoteClient.OnQuoteHistory(symbol, ParseSelect(buf, symbol)); + break; + default: + throw new UnsupportedOperationException("Parse quote hist cmd = " + (cmd & 0xFF)); + } + } + + public final List ParseSelect(InBuf buf, String symbol) { + int status = buf.Int(); + if (status != 0) { + throw new RuntimeException((Msg.forValue(status)).toString()); + } + short num = buf.UShort(); + LinkedList bars = new LinkedList<>(); + for (int i = 0; i < (num & 0xFFFF); i++) bars.addAll(0, ParseContainer(buf, symbol)); + return bars; + } + + public final void ParseStart(InBuf buf, String symbol) { + int status = buf.Int(); + if (status != 0) { + throw new RuntimeException((Msg.forValue(status)).toString()); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var day = buf.UShort(); + short day = buf.UShort(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var num = buf.UShort(); + short num = buf.UShort(); + if ((num & 0xFFFF) == 0) { + ParseContainer(buf, symbol); + } else { + throw new UnsupportedOperationException("num != 0"); + } + } + + private LinkedList ParseContainer(InBuf buf, String symbol) { + HistHeader hdr = UDT.ReadStruct(buf.Bytes(0x81), 0, 0x81, new HistHeader()); + if ((hdr.Flags & 1) != 0) { + throw new RuntimeException("Compressed"); + } + byte[] data = buf.Bytes(hdr.DataSize); + return ReadBarRecords(new BitReader(data, hdr), hdr, symbol); + } + + private LinkedList ReadBarRecords(BitReader btr, HistHeader hdr, String symbol) { + long flags = 0; + boolean bSpread = false; + boolean bVolume = false; + int numBars = 0; + LinkedList bars = new LinkedList(); + BarRecord rec = new BarRecord(); + while ((btr.BitPos <= btr.BitSize) && (numBars < hdr.NumberBars)) { + long type = BitConverter.ToInt64(btr.GetRecord(8), 0); + if (type == 0) { + flags = BitConverter.ToUInt64(btr.GetRecord(8), 0); + rec.Time = BitConverter.ToInt64(btr.GetRecord(8), 0); + rec.OpenPrice = BitConverter.ToInt32(btr.GetSignRecord(8), 0); + rec.High = BitConverter.ToInt32(btr.GetRecord(4), 0); + rec.Low = BitConverter.ToInt32(btr.GetRecord(4), 0); + rec.Close = BitConverter.ToInt32(btr.GetSignRecord(4), 0); + rec.TickVolume = BitConverter.ToUInt64(btr.GetRecord(8), 0); + if ((flags & 1) != 0) { + bSpread = true; + rec.Spread = BitConverter.ToInt32(btr.GetSignRecord(4), 0); + } + if ((flags & 2) != 0) { + bVolume = true; + rec.Volume = BitConverter.ToUInt64(btr.GetRecord(8), 0); + } + btr.SkipRecords(flags, 4); + bars.add(RecordToBar(rec.clone(), hdr.Digits).clone()); + numBars++; + } else if (type == 1) { + long num = btr.GetLong(); + for (long i = 0; i < num; i++) { + rec.Time += 60; + long value = btr.GetSignLong(); + rec.OpenPrice += (hdr.LimitPoints & 0xFFFFFFFFL) * value + rec.Close; + int data = btr.GetInt(); + rec.High = (int) ((hdr.LimitPoints & 0xFFFFFFFFL) * data); + data = btr.GetInt(); + rec.Low = (int) ((hdr.LimitPoints & 0xFFFFFFFFL) * data); + value = btr.GetSignLong(); + rec.Close = (int) ((hdr.LimitPoints & 0xFFFFFFFFL) * (int) value); + rec.TickVolume = btr.GetULong(); + if (bSpread) { + rec.Spread = btr.GetSignInt(); + } + if (bVolume) { + rec.Volume = btr.GetULong(); + } + btr.SkipRecords(flags, 4); + bars.add(RecordToBar(rec.clone(), hdr.Digits).clone()); + numBars++; + } + } else if (type == 2) { + long value = btr.GetLong(); + rec.Time += value * 60; + } + } + return bars; + // QuoteClient.OnQuoteHistory(symbol, bars.toArray(new Bar[0])); + // m_Hdr.m_nBitSize = btr.m_nBitPos; + // if (!numBars) + // return true; + // int i = firstPos; + // while ((TimeToDate(arrBar[i].m_lTime) != m_Hdr.m_Date) && (i < arrBar.GetSize())) + // i++; + // int removed = 0; + // if (i != firstPos) + // { + // removed = i - firstPos + 1; + // arrBar.ShrinkTo(firstPos, i); + // } + // if (arrBar.GetSize()) + // { + // i = arrBar.GetSize() - 1; + // while ((TimeToDate(arrBar[i].m_lTime) != m_Hdr.m_Date) && (i > firstPos)) + // i--; + // if (i != arrBar.GetSize() - 1) + // { + // removed = arrBar.GetSize() - i; + // arrBar.SetSize(i); + // } + // } + } + + private Bar RecordToBar(BarRecord rec, int digits) { + Bar bar = new Bar(); + bar.Time = ConvertTo.DateTime(rec.Time); + bar.OpenPrice = ConvertTo.LongLongToDouble(digits, rec.OpenPrice); + bar.HighPrice = ConvertTo.LongLongToDouble(digits, rec.OpenPrice + rec.High); + bar.LowPrice = ConvertTo.LongLongToDouble(digits, rec.OpenPrice - rec.Low); + bar.ClosePrice = ConvertTo.LongLongToDouble(digits, rec.OpenPrice + rec.Close); + bar.Volume = rec.Volume; + bar.TickVolume = rec.TickVolume; + bar.Spread = rec.Spread; + return bar; + } +} +/* + * bool vHistorySymbol::SendRequest() +{ + if (m_bAbsentSymbol || (m_pClient->GetServerStatus() != vAcceptAccount) || + (m_History.m_Mode == vMode_StartRequest) || m_History.m_BeginHistory.IsEmpty()) + return false; + vDate firstDate = m_History.m_BeginDay; + if (firstDate != vDate(0, 1, 1)) + firstDate = TimeToDate(DateToTime(firstDate) - 24 * 3600); + vDate begin = vDate(0, 1, 1); + if ((m_History.m_BeginHistory != vDate(0, 1, 1)) && (firstDate != vDate(0, 1, 1))) + { + begin = m_History.m_BeginHistory; + if (firstDate < begin) + return false; + } + vSockBufManager bufMan(0); + bufMan.ByteToBuffer(9); + bufMan.DataToBuffer(m_Symbol.m_SymInfo.m_sCurrency, 32 * sizeof(wchar_t)); + bufMan.DataToBuffer(&begin, 2); + bufMan.DataToBuffer(&firstDate, 2); +#ifdef DEBUG_QH + vString<256> sDate1, sDate2; + s_pLogger->LogMessage(vMessage, L"vHistory", L"SendRequest: 9 %s, Date1 %s, Date2 %s", m_Symbol.m_SymInfo.m_sCurrency, + sDate1.DateToString(DateToTime(begin)), sDate2.DateToString(DateToTime(firstDate))); +#endif + if (m_pClient->SendPacket(0x66, &bufMan)) + return true; + s_pLogger->LogMessage(vFatal, L"History", L"'%s' request sending failed [%u]", m_Symbol.m_SymInfo.m_sCurrency, GetLastError()); + return false; +} + + */ diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistoryEventArgs.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistoryEventArgs.java new file mode 100644 index 00000000000..1c9a5b29737 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/QuoteHistoryEventArgs.java @@ -0,0 +1,15 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.List; + +/** Quote history event args. */ +// C# TO JAVA CONVERTER WARNING: Java does not allow user-defined value types. The behavior of this +// class will differ from the original: +// ORIGINAL LINE: public struct QuoteHistoryEventArgs +public final class QuoteHistoryEventArgs { + /** Instrument. */ + public String Symbol; + + /** History bars. */ + public List Bars; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/RefObject.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/RefObject.java new file mode 100644 index 00000000000..4c0bc4ea98b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/RefObject.java @@ -0,0 +1,17 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// ---------------------------------------------------------------------------------------- +// Copyright © 2007 - 2017 Tangible Software Solutions Inc. +// This class can be used by anyone provided that the copyright notice remains intact. +// +// This class is used to simulate the ability to pass arguments by reference in Java. +// ---------------------------------------------------------------------------------------- +public final class RefObject { + public T argValue; + + public RefObject(T refArg) { + argValue = refArg; + } + + public RefObject() {} +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SHA256Native.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SHA256Native.java new file mode 100644 index 00000000000..1f7a15f71ed --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SHA256Native.java @@ -0,0 +1,19 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +class SHA256Native { + MessageDigest MD; + + SHA256Native() { + try { + MD = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + } + } + + byte[] ComputeHash(byte[] bytes) { + return MD.digest(bytes); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SecureSocket.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SecureSocket.java new file mode 100644 index 00000000000..2b1f9d6fe98 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SecureSocket.java @@ -0,0 +1,187 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class SecureSocket { + private Decoder Decoder; + private Encoder Encoder; + private Logger Log; + Socket Sock; + DataOutputStream Output; + DataInputStream Input; + + public SecureSocket() { + Log = new Logger(this); + } + + // public SecureSocket(byte[] key) + // { + // Decoder = new Decoder(key); + // Encoder = new Encoder(key); + // Log = new Logger(this); + // } + + // public final void SetNewKey(byte[] key) + // { + // if (Decoder == null) + // { + // Decoder = new Decoder(key); + // } + // else + // { + // Decoder.ChangeKey(key); + // } + // if (Encoder == null) + // { + // Encoder = new Encoder(key); + // } + // else + // { + // Encoder.ChangeKey(key); + // } + // } + // + // public final byte[] GetKey() + // { + // if (Decoder != null) + // { + // return Decoder.GetKey(); + // } + // if (Encoder != null) + // { + // return Encoder.GetKey(); + // } + // return null; + // } + + // public final void ResetDecoder() + // { + // Decoder.Reset(); + // } + // + // public final void ResetEncoder() + // { + // Encoder.Reset(); + // } + + public final void Send(byte[] buf) throws IOException { + Output.write(buf); + Output.flush(); + } + + public final byte[] Receive(int count) throws IOException { + byte[] buf = new byte[count]; + int rest = buf.length; + while (rest > 0) { + int len = Input.read(buf, buf.length - rest, rest); + if (len == 0) { + throw new RuntimeException("Server disconnected"); + } else if (len == -1) { + throw new RuntimeException("Server closed the stream"); + } else { + rest -= len; + } + } + return buf; + } + + // public final int SendEnrypt(byte[] hdr, byte[] buf) + // { + // byte value = 0; + // for (int i = 1; i < buf.length; i++) + // { + // value = (byte)((((value & 0xFF) + (Crypt.CryptKey[(i - 1) & 0xF] & 0xFF)) ^ (buf[i] & 0xFF)) + // & 0xFF); + // buf[i] = value; + // } + // return Sock.Send(buf); + // } + // + // public final int SendEnrypt(byte[] buf) + // { + // byte value = 0; + // for (int i = 1; i < buf.length; i++) + // { + // value = (byte)((((value & 0xFF) + (Crypt.CryptKey[(i - 1) & 0xF] & 0xFF)) ^ (buf[i] & 0xFF)) + // & 0xFF); + // buf[i] = value; + // } + // return Sock.Send(buf); + // } + // + // public final byte[] ReceiveDecrypt(int count) + // { + // byte[] buf = Receive(count); + // byte prev = 0; + // for (int i = 0; i < buf.length; i++) + // { + // byte value = (byte)(((prev & 0xFF) + (Crypt.CryptKey[i & 0xF] & 0xFF)) & 0xFF); + // prev = buf[i]; + // (buf[i] & 0xFF) ^= (value & 0xFF); + // } + // Log.trace("RECV " + ConvertBytes.ToHex(buf)); + // return buf; + // } + // + // public final int SendEncode(byte[] buf) + // { + // return Sock.Send(Encoder.Encode(buf)); + // } + + public final byte[] ReceiveDecode(int count) throws IOException { + byte[] buf = Receive(count); + return Decoder.Decode(buf); + } + + public final byte[] ReceiveCopmressed() throws IOException { + byte[] buf = ReceiveDecode(4); + int dstlen = BitConverter.ToInt32(buf, 0); + buf = ReceiveDecode(4); + int len = BitConverter.ToInt32(buf, 0); + buf = ReceiveDecode(len); + return Decompressor.decompress(buf, dstlen); + } + + final void Connect(String host, int port) throws IOException { + if (Decoder != null) { + Decoder.Reset(); + } + if (Encoder != null) { + Encoder.Reset(); + } + Sock = new Socket(); + Sock.connect(new InetSocketAddress(host, port), 30000); + Sock.setSoTimeout(30000); + Output = new DataOutputStream(Sock.getOutputStream()); + Input = new DataInputStream(Sock.getInputStream()); + } + + final void Close() { + try { + if (Sock != null) { + Sock.close(); + } + } catch (Exception ex) { + } + try { + if (Input != null) { + Input.close(); + } + } catch (Exception ex) { + } + try { + if (Output != null) { + Output.close(); + } + } catch (Exception ex) { + } + } + + int avaliable() throws IOException { + return Input.available(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Server.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Server.java new file mode 100644 index 00000000000..47b378acc69 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Server.java @@ -0,0 +1,8 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class Server { + public ServerInfoEx ServerInfoEx; + public ServerInfo ServerInfo; + public Access[] Accesses; + public AccessEx[] AccessesEx; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerException.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerException.java new file mode 100644 index 00000000000..bce319dafcc --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerException.java @@ -0,0 +1,17 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Error reply from server on login and order opening/closing/modifying. */ +public class ServerException extends RuntimeException { + /** Error code. */ + public Msg Code = Msg.values()[0]; + + /** + * Initialize ServerException. + * + * @param code Exception code. + */ + public ServerException(Msg code) { + super(code.toString()); + Code = code; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerGroup.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerGroup.java new file mode 100644 index 00000000000..f323be59615 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerGroup.java @@ -0,0 +1,7 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class ServerGroup { + public ServerInfoEx ServerInfo; + public Access[] Accesses; + public AccessEx[] AccessesEx; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfo.java new file mode 100644 index 00000000000..e0114394589 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfo.java @@ -0,0 +1,63 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public class ServerInfo extends FromBufReader { + public static final int Size = 0x294; + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String ServerName; + /*[FieldOffset(128)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ + public String CompanyName; + /*[FieldOffset(384)]*/ + public int s180; + /*[FieldOffset(388)]*/ + public int s184; + /*[FieldOffset(392)]*/ + public int DST; + /*[FieldOffset(396)]*/ + public int TimeZone; + /*[FieldOffset(400)]*/ + public int s190; + /*[FieldOffset(404)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String Address; + /*[FieldOffset(532)]*/ + public int PingTime; + /*[FieldOffset(536)]*/ + public int s218; + /*[FieldOffset(540)]*/ + public int s21C; + /*[FieldOffset(544)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 116)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s220; + public byte[] s220; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + Size; + ServerInfo st = new ServerInfo(); + st.ServerName = GetString(buf.Bytes(128)); + st.CompanyName = GetString(buf.Bytes(256)); + st.s180 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s184 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.DST = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TimeZone = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s190 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Address = GetString(buf.Bytes(128)); + st.PingTime = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s218 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s21C = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s220 = new byte[116]; + st.s220 = new byte[116]; + for (int i = 0; i < 116; i++) { + st.s220[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfoEx.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfoEx.java new file mode 100644 index 00000000000..4b459dc0a94 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerInfoEx.java @@ -0,0 +1,87 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x6B4, CharSet = CharSet.Unicode)]*/ +public class ServerInfoEx extends FromBufReader { + public static final int Size = 1716; + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String ServerName; + /*[FieldOffset(128)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ + public String CompanyName; + /*[FieldOffset(384)]*/ + public int s180; + /*[FieldOffset(388)]*/ + public int s184; + /*[FieldOffset(392)]*/ + public int DST; + /*[FieldOffset(396)]*/ + public int TimeZone; + /*[FieldOffset(400)]*/ + public int s190; + /*[FieldOffset(404)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ + public String Address; + /*[FieldOffset(532)]*/ + public int PingTime; + /*[FieldOffset(536)]*/ + public int s218; + /*[FieldOffset(540)]*/ + public int s21C; + /*[FieldOffset(544)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 116)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s220; + public byte[] s220; + /*[FieldOffset(660)]*/ + public int s294; + /*[FieldOffset(664)]*/ + public int s298; + /*[FieldOffset(668)]*/ + public long s29C; + /*[FieldOffset(676)]*/ + public long s2A4; + /*[FieldOffset(684)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ + public String CompanyLink; + /*[FieldOffset(1196)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ + public String s4AC; + /*[FieldOffset(1708)]*/ + public long s6AC; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 1716; + ServerInfoEx st = new ServerInfoEx(); + st.ServerName = GetString(buf.Bytes(128)); + st.CompanyName = GetString(buf.Bytes(256)); + st.s180 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s184 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.DST = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TimeZone = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s190 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Address = GetString(buf.Bytes(128)); + st.PingTime = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s218 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s21C = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s220 = new byte[116]; + st.s220 = new byte[116]; + for (int i = 0; i < 116; i++) { + st.s220[i] = buf.Byte(); + } + st.s294 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s298 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s29C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s2A4 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.CompanyLink = GetString(buf.Bytes(512)); + st.s4AC = GetString(buf.Bytes(512)); + st.s6AC = BitConverter.ToInt64(buf.Bytes(8), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerRec.java new file mode 100644 index 00000000000..50acfe7f0d7 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServerRec.java @@ -0,0 +1,43 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x214, CharSet = CharSet.Unicode)]*/ +public class ServerRec extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String ServerName; + /*[FieldOffset(128)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String CompanyName; + /*[FieldOffset(384)]*/ public int s180; + /*[FieldOffset(388)]*/ public int s184; + /*[FieldOffset(392)]*/ public int DST; + /*[FieldOffset(396)]*/ public int TimeZone; + /*[FieldOffset(400)]*/ public int s190; + /*[FieldOffset(404)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s194; + public byte[] s194; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 532; + ServerRec st = new ServerRec(); + st.ServerName = GetString(buf.Bytes(128)); + st.CompanyName = GetString(buf.Bytes(256)); + st.s180 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s184 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.DST = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TimeZone = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s190 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s194 = new byte[128]; + st.s194 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s194[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServersDatLoader.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServersDatLoader.java new file mode 100644 index 00000000000..29d668cc097 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ServersDatLoader.java @@ -0,0 +1,54 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; + +public class ServersDatLoader { + public final Server[] Load(String path) throws IOException { + InBuf buf = new InBuf(Files.readAllBytes(Paths.get(path)), 0); + DatHeader hdr = buf.Struct(new DatHeader()); + ArrayList lst = new ArrayList(); + while (buf.gethasData()) { + Server res = new Server(); + if ((hdr.Id & 0xFFFFFFFFL) == 0x1F9 || (hdr.Id & 0xFFFFFFFFL) == 0x1FA) { + res.ServerInfoEx = buf.CryptStruct(ServerInfoEx.Size, new ServerInfoEx()); + int num = buf.Int(); + if (num < 0 || num > 128) break; + res.Accesses = new Access[num]; + for (int i = 0; i < num; i++) { + res.Accesses[i] = new Access(); + res.Accesses[i].AccessRec = buf.CryptStruct(AccessRec.Size, new AccessRec()); + res.Accesses[i].Addresses = buf.CryptArray(AddressRec.Size, new AddressRec()); + } + num = buf.Int(); + if (num < 0 || num > 128) break; + res.AccessesEx = new AccessEx[num]; + for (int i = 0; i < num; i++) { + res.AccessesEx[i] = new AccessEx(); + res.AccessesEx[i].AccessRec = + buf.CryptStruct(AccessRecEx.Size, new AccessRecEx()); + res.AccessesEx[i].Addresses = + buf.CryptArray(AddressRecEx.Size, new AddressRecEx()); + } + } else if ((hdr.Id & 0xFFFFFFFFL) == 0x1F7 || (hdr.Id & 0xFFFFFFFFL) == 0x1F8) { + res.ServerInfo = buf.CryptStruct(ServerInfo.Size, new ServerInfo()); + // buf.Bytes(8); + int num = buf.Int(); + if (num < 0 || num > 128) break; + res.Accesses = new Access[num]; + for (int i = 0; i < num; i++) { + res.Accesses[i] = new Access(); + res.Accesses[i].AccessRec = buf.CryptStruct(AccessRec.Size, new AccessRec()); + res.Accesses[i].Addresses = buf.CryptArray(AddressRec.Size, new AddressRec()); + } + } else { + throw new UnsupportedOperationException( + "hdr.Id = 0x" + String.format("%X", hdr.Id & 0xFFFFFFFFL)); + } + lst.add(res); + } + return lst.toArray(new Server[0]); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Session.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Session.java new file mode 100644 index 00000000000..c9b2b14547d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Session.java @@ -0,0 +1,40 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x28, CharSet = CharSet.Unicode)]*/ +public class Session extends FromBufReader { + /** Start time (in minites) */ + /*[FieldOffset(0)]*/ public int StartTime; + + /** End time (in minutes) */ + /*[FieldOffset(4)]*/ public int EndTime; + + /*[FieldOffset(8)]*/ public int s8; + /*[FieldOffset(12)]*/ public int sC; + /*[FieldOffset(16)]*/ public int s10; + /*[FieldOffset(20)]*/ public int s14; + /*[FieldOffset(24)]*/ public int s18; + /*[FieldOffset(28)]*/ public int s1C; + /*[FieldOffset(32)]*/ public int s20; + /*[FieldOffset(36)]*/ public int s24; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 40; + Session st = new Session(); + st.StartTime = BitConverter.ToInt32(buf.Bytes(4), 0); + st.EndTime = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s10 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s14 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s18 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s20 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s24 = BitConverter.ToInt32(buf.Bytes(4), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256.java new file mode 100644 index 00000000000..d8890107170 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256.java @@ -0,0 +1,29 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +class Sha256 { + + MessageDigest MD; + + public Sha256() { + try { + MD = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public void AddData(byte[] data, int of, int len) { + MD.update(data, of, len); + } + + public void AddData(byte[] data) { + MD.update(data); + } + + public byte[] GetHash() { + return MD.digest(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256Managed.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256Managed.java new file mode 100644 index 00000000000..66f7fe82be5 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Sha256Managed.java @@ -0,0 +1,455 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.util.List; + +/* + * Copyright (c) 2010 Yuri K. Schlesner + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +class Sha256Managed { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static readonly UInt32[] K = new UInt32[64] { 0x428A2F98, 0x71374491, + // 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, + // 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, + // 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, + // 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, + // 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, + // 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, + // 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, + // 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 }; + private static final int[] K = + new int[] { + 0x428A2F98, + 0x71374491, + 0xB5C0FBCF, + 0xE9B5DBA5, + 0x3956C25B, + 0x59F111F1, + 0x923F82A4, + 0xAB1C5ED5, + 0xD807AA98, + 0x12835B01, + 0x243185BE, + 0x550C7DC3, + 0x72BE5D74, + 0x80DEB1FE, + 0x9BDC06A7, + 0xC19BF174, + 0xE49B69C1, + 0xEFBE4786, + 0x0FC19DC6, + 0x240CA1CC, + 0x2DE92C6F, + 0x4A7484AA, + 0x5CB0A9DC, + 0x76F988DA, + 0x983E5152, + 0xA831C66D, + 0xB00327C8, + 0xBF597FC7, + 0xC6E00BF3, + 0xD5A79147, + 0x06CA6351, + 0x14292967, + 0x27B70A85, + 0x2E1B2138, + 0x4D2C6DFC, + 0x53380D13, + 0x650A7354, + 0x766A0ABB, + 0x81C2C92E, + 0x92722C85, + 0xA2BFE8A1, + 0xA81A664B, + 0xC24B8B70, + 0xC76C51A3, + 0xD192E819, + 0xD6990624, + 0xF40E3585, + 0x106AA070, + 0x19A4C116, + 0x1E376C08, + 0x2748774C, + 0x34B0BCB5, + 0x391C0CB3, + 0x4ED8AA4A, + 0x5B9CCA4F, + 0x682E6FF3, + 0x748F82EE, + 0x78A5636F, + 0x84C87814, + 0x8CC70208, + 0x90BEFFFA, + 0xA4506CEB, + 0xBEF9A3F7, + 0xC67178F2 + }; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 ROTL(UInt32 x, byte n) + private static int ROTL(int x, byte n) { + assert n < 32; + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + return (x << n) | (x >>> (32 - n)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 ROTR(UInt32 x, byte n) + private static int ROTR(int x, byte n) { + assert n < 32; + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + return (x >>> n) | (x << (32 - n)); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 Ch(UInt32 x, UInt32 y, UInt32 z) + private static int Ch(int x, int y, int z) { + return (x & y) ^ ((~x) & z); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 Maj(UInt32 x, UInt32 y, UInt32 z) + private static int Maj(int x, int y, int z) { + return (x & y) ^ (x & z) ^ (y & z); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 Sigma0(UInt32 x) + private static int Sigma0(int x) { + return ROTR(x, (byte) 2) ^ ROTR(x, (byte) 13) ^ ROTR(x, (byte) 22); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 Sigma1(UInt32 x) + private static int Sigma1(int x) { + return ROTR(x, (byte) 6) ^ ROTR(x, (byte) 11) ^ ROTR(x, (byte) 25); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 sigma0(UInt32 x) + private static int sigma0(int x) { + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + return ROTR(x, (byte) 7) ^ ROTR(x, (byte) 18) ^ (x >>> 3); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static UInt32 sigma1(UInt32 x) + private static int sigma1(int x) { + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + return ROTR(x, (byte) 17) ^ ROTR(x, (byte) 19) ^ (x >>> 10); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private UInt32[] H = new UInt32[8] { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, + // 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 }; + private int[] H = + new int[] { + 0x6A09E667, + 0xBB67AE85, + 0x3C6EF372, + 0xA54FF53A, + 0x510E527F, + 0x9B05688C, + 0x1F83D9AB, + 0x5BE0CD19 + }; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private byte[] pending_block = new byte[64]; + private byte[] pending_block = new byte[64]; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private uint pending_block_off = 0; + private int pending_block_off = 0; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private UInt32[] uint_buffer = new UInt32[16]; + private int[] uint_buffer = new int[16]; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private UInt64 bits_processed = 0; + private long bits_processed = 0; + + private boolean closed = false; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private void processBlock(UInt32[] M) + private void processBlock(int[] M) { + assert M.length == 16; + + // 1. Prepare the message schedule (W[t]): + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: UInt32[] W = new UInt32[64]; + int[] W = new int[64]; + for (int t = 0; t < 16; ++t) { + W[t] = M[t]; + } + + for (int t = 16; t < 64; ++t) { + W[t] = sigma1(W[t - 2]) + W[t - 7] + sigma0(W[t - 15]) + W[t - 16]; + } + + // 2. Initialize the eight working variables with the (i-1)-st hash value: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: UInt32 a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h + // = H[7]; + int a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7]; + + // 3. For t=0 to 63: + for (int t = 0; t < 64; ++t) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: UInt32 T1 = h + Sigma1(e) + Ch(e, f, g) + K[t] + W[t]; + int T1 = h + Sigma1(e) + Ch(e, f, g) + K[t] + W[t]; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: UInt32 T2 = Sigma0(a) + Maj(a, b, c); + int T2 = Sigma0(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + } + + // 4. Compute the intermediate hash value H: + H[0] = a + H[0]; + H[1] = b + H[1]; + H[2] = c + H[2]; + H[3] = d + H[3]; + H[4] = e + H[4]; + H[5] = f + H[5]; + H[6] = g + H[6]; + H[7] = h + H[7]; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public void AddData(byte[] data, uint offset, uint len) + public final void AddData(byte[] data, int offset, int len) { + if (closed) { + throw new IllegalStateException("Adding data to a closed hasher."); + } + + if (len == 0) { + return; + } + + bits_processed += len * 8; + + while (len > 0) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: uint amount_to_copy; + int amount_to_copy; + + if (len < 64) { + if (pending_block_off + len > 64) { + amount_to_copy = 64 - pending_block_off; + } else { + amount_to_copy = len; + } + } else { + amount_to_copy = 64 - pending_block_off; + } + + System.arraycopy(data, offset, pending_block, pending_block_off, amount_to_copy); + len -= amount_to_copy; + offset += amount_to_copy; + pending_block_off += amount_to_copy; + + if (pending_block_off == 64) { + toUintArray(pending_block, uint_buffer); + processBlock(uint_buffer); + pending_block_off = 0; + } + } + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ReadOnlyCollection GetHash() + public final byte[] GetHash() { + return toByteArray(GetHashUInt32()); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ReadOnlyCollection GetHashUInt32() + public final int[] GetHashUInt32() { + if (!closed) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: UInt64 size_temp = bits_processed; + long size_temp = bits_processed; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: AddData(new byte[1] { 0x80 }, 0, 1); + AddData(new byte[] {(byte) 0x80}, 0, 1); + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: uint available_space = 64 - pending_block_off; + int available_space = 64 - pending_block_off; + + if (available_space < 8) { + available_space += 64; + } + + // 0-initialized + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] padding = new byte[available_space]; + byte[] padding = new byte[available_space]; + // Insert lenght uint64 + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: for (uint i = 1; i <= 8; ++i) + for (int i = 1; i <= 8; ++i) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: padding[padding.Length - i] = (byte)size_temp; + padding[padding.length - i] = (byte) size_temp; + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical + // right shift operator since the left operand was originally of an unsigned type, but you + // should confirm this replacement: + size_temp >>>= 8; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: AddData(padding, 0u, (uint)padding.Length); + AddData(padding, 0, (int) padding.length); + + assert pending_block_off == 0; + + closed = true; + } + return H; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static void toUintArray(byte[] src, UInt32[] dest) + private static void toUintArray(byte[] src, int[] dest) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: for (uint i = 0, j = 0; i < dest.Length; ++i, j += 4) + for (int i = 0, j = 0; i < dest.length; ++i, j += 4) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[i] = ((UInt32)src[j + 0] << 24) | ((UInt32)src[j + 1] << 16) | + // ((UInt32)src[j + 2] << 8) | ((UInt32)src[j + 3]); + dest[i] = + ((int) src[j + 0] << 24) + | ((int) src[j + 1] << 16) + | ((int) src[j + 2] << 8) + | ((int) src[j + 3]); + } + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: private static ReadOnlyCollection toByteArray(ReadOnlyCollection + // src) + private static byte[] toByteArray(List src) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] dest = new byte[src.Count * 4]; + byte[] dest = new byte[src.size() * 4]; + int pos = 0; + + for (int i = 0; i < src.size(); ++i) { + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 24); + dest[pos++] = (byte) (src.get(i) >>> 24); + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 16); + dest[pos++] = (byte) (src.get(i) >>> 16); + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 8); + dest[pos++] = (byte) (src.get(i) >>> 8); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i]); + dest[pos++] = (byte) (int) src.get(i); + } + return dest; + } + + private static byte[] toByteArray(int[] src) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] dest = new byte[src.Count * 4]; + byte[] dest = new byte[src.length * 4]; + int pos = 0; + + for (int i = 0; i < src.length; ++i) { + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 24); + dest[pos++] = (byte) (src[i] >>> 24); + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 16); + dest[pos++] = (byte) (src[i] >>> 16); + // C# TO JAVA CONVERTER WARNING: The right shift operator was replaced by Java's logical right + // shift operator since the left operand was originally of an unsigned type, but you should + // confirm this replacement: + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i] >> 8); + dest[pos++] = (byte) (src[i] >>> 8); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: dest[pos++] = (byte)(src[i]); + dest[pos++] = (byte) (int) src[i]; + } + return dest; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static ReadOnlyCollection HashFile(Stream fs) + public static byte[] HashFile(java.io.InputStream fs) throws IOException { + Sha256Managed sha = new Sha256Managed(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] buf = new byte[8196]; + byte[] buf = new byte[8196]; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: uint bytes_read; + int bytes_read; + do { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: bytes_read = (uint)fs.Read(buf, 0, buf.Length); + bytes_read = (int) fs.read(buf, 0, buf.length); + if (bytes_read == 0) { + break; + } + + sha.AddData(buf, 0, bytes_read); + } while (bytes_read == 8196); + + return sha.GetHash(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ShortContainer.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ShortContainer.java new file mode 100644 index 00000000000..b6366e70a9f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ShortContainer.java @@ -0,0 +1,9 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class ShortContainer { + public static ShortContainer from(String8 string8) { + throw new RuntimeException(); + } + + public void set(short i) {} +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadData.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadData.java new file mode 100644 index 00000000000..e396acf295d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadData.java @@ -0,0 +1,40 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x80, CharSet = CharSet.Unicode)]*/ +public class SpreadData extends FromBufReader { + /*[FieldOffset(0)]*/ public int s0; + /*[FieldOffset(4)]*/ public int s4; + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Currency; + /*[FieldOffset(72)]*/ public long s48; + /*[FieldOffset(80)]*/ public long s50; + /*[FieldOffset(88)]*/ public double s58; + /*[FieldOffset(96)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s60; + public byte[] s60; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 128; + SpreadData st = new SpreadData(); + st.s0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Currency = GetString(buf.Bytes(64)); + st.s48 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s50 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s58 = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s60 = new byte[32]; + st.s60 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s60[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadInfo.java new file mode 100644 index 00000000000..53dc99f6d8f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SpreadInfo.java @@ -0,0 +1,39 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x64, CharSet = CharSet.Unicode)]*/ +public class SpreadInfo extends FromBufReader { + /*[FieldOffset(0)]*/ public long Time; + /*[FieldOffset(8)]*/ public int Id; + /*[FieldOffset(12)]*/ public int sC; + /*[FieldOffset(16)]*/ public double s10; + /*[FieldOffset(24)]*/ public double s18; + /*[FieldOffset(32)]*/ public int s20; + /*[FieldOffset(36)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s24; + public byte[] s24; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 100; + SpreadInfo st = new SpreadInfo(); + st.Time = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Id = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s10 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s18 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s20 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s24 = new byte[64]; + st.s24 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s24[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/String8.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/String8.java new file mode 100644 index 00000000000..bfd9c8fa3ff --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/String8.java @@ -0,0 +1,87 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class String8 { + byte[] Data; + int Index; + + public String8(int sLen) { + Data = new byte[sLen]; + Index = 0; + } + + public String toString() { + if (Index < Data.length) + return "Byte = " + + String.format("%02x", Data[Index] & 0xff) + + " Index = " + + Index + + " DataLen = " + + Data.length; + else return "Byte = out Index = " + Index + " DataLen = " + Data.length; + } + + public String8(byte[] src) { + Data = src; + Index = 0; + } + + public String8() {} + + static String8 nnc(String8 src) { + String8 dst = new String8(); + dst.Data = src.Data; + dst.Index = src.Index; + return dst; + } + + static int dataDistance(String8 a, String8 b) { + return b.Index - a.Index; + } + + static String8 fromData(byte[] src) { + String8 s = new String8(); + s.Data = src; + s.Index = 0; + return s; + } + + public static String8 from(Container mPBuffer_u) { + throw new RuntimeException(); + } + + public static String8 from(String8 src) { + String8 s = new String8(); + s.Data = src.Data; + s.Index = src.Index; + return s; + } + + public static String8 from(String8 src, int index) { + String8 s = new String8(); + s.Data = src.Data; + s.Index = index; + return s; + } + + String8 shift(int i) { + String8 s = new String8(); + s.Data = Data; + s.Index = Index + i; + return s; + } + + byte get(int i) { + return Data[Index + i]; + } + + byte set(int i, byte value) { + Data[Index + i] = value; + return value; + } + + byte get() { + return Data[Index]; + } + + public void fill(int i, int sz_u, byte b) {} +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Subscriber.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Subscriber.java new file mode 100644 index 00000000000..61252190d6a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Subscriber.java @@ -0,0 +1,453 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +class Subscriber { + private MT5API QuoteClient; + private final ConcurrentHashMap Quotes = new ConcurrentHashMap(); + private Logger Log; + + public Subscriber(MT5API quoteClient, Logger log) { + Log = log; + QuoteClient = quoteClient; + } + + public final Quote GetQuote(String symbol) throws IOException { + if (!Quotes.containsKey(symbol)) { + Quotes.put(symbol, new Quote(symbol)); + Request(); + } + Quote res = Quotes.get(symbol); + if (res.Ask == 0 || res.Bid == 0) return null; + else return res; + } + + public final boolean Subscribed(String symbol) { + return Quotes.containsKey(symbol); + } + + public final void Subscribe(String symbol) throws IOException { + if (!Quotes.containsKey(symbol)) { + Quotes.put(symbol, new Quote(symbol)); + Request(); + } + } + + public String[] Subscriptions() { + return Quotes.keySet().toArray(new String[0]); + } + + public final void Subscribe(String[] symbols) throws IOException { + boolean needRequest = false; + for (String symbol : symbols) { + if (!Quotes.containsKey(symbol)) { + Quotes.put(symbol, new Quote(symbol)); + needRequest = true; + } + } + if (needRequest) Request(); + } + + public final void Unsubscribe(String symbol) throws IOException { + boolean found = false; + for (Order order : QuoteClient.GetOpenedOrders()) { + if ((order.OrderType == OrderType.Buy || order.OrderType == OrderType.Sell) + && order.Symbol == symbol) found = true; + } + if (!found) + if (Quotes.containsKey(symbol)) { + Quotes.remove(symbol); + Request(); + } + } + + private void Request() throws IOException { + OutBuf buf = new OutBuf(); + buf.ByteToBuffer((byte) 9); + buf.IntToBuffer(Quotes.keySet().size()); + // C# TO JAVA CONVERTER TODO TASK: There is no equivalent to implicit typing in Java: + for (String item : Quotes.keySet()) { + SymbolInfo si = QuoteClient.Symbols.GetInfo(item); + buf.IntToBuffer(si.Id); + } + QuoteClient.Connection.SendPacket((byte) 0x69, buf); + } + + public final void Parse(InBuf buf) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: var bytes = buf.ToBytes(); + byte[] bytes = buf.ToBytes(); + BitReaderQuotes br = new BitReaderQuotes(bytes, bytes.length * 8); + br.Initialize((byte) 2, (byte) 3); + while (true) { + TickRec rec = new TickRec(); + RefObject tempRef_Id = new RefObject(rec.Id); + if (!br.GetInt(tempRef_Id)) { + rec.Id = tempRef_Id.argValue; + break; + } else { + rec.Id = tempRef_Id.argValue; + } + RefObject tempRef_Time = new RefObject(rec.Time); + if (!br.GetLong(tempRef_Time)) { + rec.Time = tempRef_Time.argValue; + break; + } else { + rec.Time = tempRef_Time.argValue; + } + RefObject tempRef_UpdateMask = new RefObject(rec.UpdateMask); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!br.GetULong(out rec.UpdateMask)) + if (!br.GetULong(tempRef_UpdateMask)) { + rec.UpdateMask = tempRef_UpdateMask.argValue; + break; + } else { + rec.UpdateMask = tempRef_UpdateMask.argValue; + } + if ((rec.UpdateMask & 1) != 0) { + RefObject tempRef_Bid = new RefObject(rec.Bid); + if (!br.GetLong(tempRef_Bid)) { + rec.Bid = tempRef_Bid.argValue; + break; + } else { + rec.Bid = tempRef_Bid.argValue; + } + } + if ((rec.UpdateMask & 2) != 0) { + RefObject tempRef_Ask = new RefObject(rec.Ask); + if (!br.GetLong(tempRef_Ask)) { + rec.Ask = tempRef_Ask.argValue; + break; + } else { + rec.Ask = tempRef_Ask.argValue; + } + } + if ((rec.UpdateMask & 4) != 0) { + RefObject tempRef_Last = new RefObject(rec.Last); + if (!br.GetLong(tempRef_Last)) { + rec.Last = tempRef_Last.argValue; + break; + } else { + rec.Last = tempRef_Last.argValue; + } + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong volume = 0; + long volume = 0; + if ((rec.UpdateMask & 8) != 0) { + RefObject tempRef_volume = new RefObject(volume); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!br.GetULong(out volume)) + if (!br.GetULong(tempRef_volume)) { + volume = tempRef_volume.argValue; + break; + } else { + volume = tempRef_volume.argValue; + } + } + if ((rec.UpdateMask & 0x10) != 0) { + RefObject tempRef_s3C = new RefObject(rec.s3C); + if (!br.GetLong(tempRef_s3C)) { + rec.s3C = tempRef_s3C.argValue; + break; + } else { + rec.s3C = tempRef_s3C.argValue; + } + } + if ((rec.UpdateMask & 0x20) != 0) { + RefObject tempRef_BankId = new RefObject(rec.BankId); + if (!br.GetShort(tempRef_BankId)) { + rec.BankId = tempRef_BankId.argValue; + break; + } else { + rec.BankId = tempRef_BankId.argValue; + rec.BankId = -1; + } + } + if ((rec.UpdateMask & 0x40) != 0) { + RefObject tempRef_TimeMs = new RefObject(rec.TimeMs); + if (!br.GetLong(tempRef_TimeMs)) { + rec.TimeMs = tempRef_TimeMs.argValue; + break; + } else { + rec.TimeMs = tempRef_TimeMs.argValue; + } + rec.TimeMs += rec.Time * 1000; + } else { + rec.TimeMs = rec.Time * 1000; + } + if ((rec.UpdateMask & 0x80) != 0) { + RefObject tempRef_s44 = new RefObject(rec.s44); + if (!br.GetLong(tempRef_s44)) { + rec.s44 = tempRef_s44.argValue; + break; + } else { + rec.s44 = tempRef_s44.argValue; + } + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong volumeEx = 0; + long volumeEx = 0; + if ((rec.UpdateMask & 0x100) != 0) { + RefObject tempRef_volumeEx = new RefObject(volumeEx); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: if (!br.GetULong(out volumeEx)) + if (!br.GetULong(tempRef_volumeEx)) { + volumeEx = tempRef_volumeEx.argValue; + break; + } else { + volumeEx = tempRef_volumeEx.argValue; + } + } + if ((rec.UpdateMask & 0x108) != 0) { + rec.Volume = volume * 100000000 + volumeEx; + } + br.SkipRecords(rec.UpdateMask, 0x100); + br.AlignBitPosition(1); + SymbolInfo info = QuoteClient.Symbols.GetInfo(rec.Id); + Quote quote; + if (!Quotes.containsKey(info.Name)) { + continue; + } else { + quote = Quotes.get(info.Name); + if (quote == null) { + quote = new Quote(); + quote.Symbol = info.Name; + Quotes.put(quote.Symbol, quote); + } + } + RecToQuote(rec, quote); + if (quote.Bid != 0) { + if (quote.Ask != 0) { + if (QuoteClient.ServerDetails.getKey().ServerName.startsWith("LandFX")) { + double dif = 2 * Math.pow(10, -QuoteClient.Symbols.GetInfo(quote.Symbol).Digits); + quote.Bid += dif; + quote.Ask -= dif; + } + QuoteClient.OnQuoteCall(quote); + } + } + } + } + + private void RecToQuote(TickRec rec, Quote quote) { + SymbolInfo info = QuoteClient.Symbols.GetInfo(rec.Id); + quote.Time = ConvertTo.DateTimeMs(rec.TimeMs); + quote.BankId = rec.BankId; + quote.UpdateMask = rec.UpdateMask; + // if ((rec.UpdateMask & 1)!=0) + if (rec.Bid != 0) { + quote.Bid = ConvertTo.LongLongToDouble(info.Digits, rec.Bid); + } + // if ((rec.UpdateMask & 2)!=0) + if (rec.Ask != 0) { + quote.Ask = ConvertTo.LongLongToDouble(info.Digits, rec.Ask); + } + // if ((rec.UpdateMask & 4) != 0) + if (rec.Last != 0) { + quote.Last = ConvertTo.LongLongToDouble(info.Digits, rec.Last); + } + // if ((rec.UpdateMask & 8) != 0) + if (rec.Volume != 0) { + quote.Volume = rec.Volume; + } + // if ((rec.UpdateMask & 8) != 0x80) + // quote.s44 = rec.s44; + } + + private void RecToQuote(SymbolMarketData rec, Quote quote) { + SymbolInfo info = QuoteClient.Symbols.GetInfo(rec.Id); + quote.Time = ConvertTo.DateTimeMs(rec.TimeMs); + quote.UpdateMask = rec.UpdateMask; + // if ((rec.UpdateMask & 1)!=0) + if (rec.Bid != 0) { + quote.Bid = ConvertTo.LongLongToDouble(info.Digits, rec.Bid); + } + // if ((rec.UpdateMask & 2)!=0) + if (rec.Ask != 0) { + quote.Ask = ConvertTo.LongLongToDouble(info.Digits, rec.Ask); + } + // if ((rec.UpdateMask & 4) != 0) + if (rec.Last != 0) { + quote.Last = ConvertTo.LongLongToDouble(info.Digits, rec.Last); + } + // if ((rec.UpdateMask & 8) != 0) + if (rec.Volume != 0) { + quote.Volume = rec.Volume; + } + // if ((rec.UpdateMask & 8) != 0x80) + // quote.s44 = rec.s44; + } + + public void ParseSymbolData(InBuf buf) { + byte[] bytes = buf.ToBytes(); + BitReaderQuotes br = new BitReaderQuotes(bytes, bytes.length * 8); + br.Initialize((byte) 2, (byte) 3); + while (true) { + SymbolMarketData rec = new SymbolMarketData(); + RefObject ir = new RefObject(); + if (!br.GetInt(ir)) break; + else rec.Id = ir.argValue; + RefObject lr = new RefObject(); + if (!br.GetULong(lr)) break; + else rec.UpdateMask = lr.argValue; + if (!br.GetLong(lr)) break; + else rec.Time = lr.argValue; + if ((rec.UpdateMask & 1) != 0) + if (!br.GetLong(lr)) break; + else rec.Bid = lr.argValue; + if ((rec.UpdateMask & 2) != 0) + if (!br.GetLong(lr)) break; + else rec.BidHigh = lr.argValue; + if ((rec.UpdateMask & 4) != 0) + if (!br.GetLong(lr)) break; + else rec.BidLow = lr.argValue; + if ((rec.UpdateMask & 8) != 0) + if (!br.GetLong(lr)) break; + else rec.Ask = lr.argValue; + if ((rec.UpdateMask & 0x10) != 0) + if (!br.GetLong(lr)) break; + else rec.AskHigh = lr.argValue; + if ((rec.UpdateMask & 0x20) != 0) + if (!br.GetLong(lr)) break; + else rec.AskLow = lr.argValue; + if ((rec.UpdateMask & 0x40) != 0) + if (!br.GetLong(lr)) break; + else rec.Last = lr.argValue; + if ((rec.UpdateMask & 0x80) != 0) + if (!br.GetLong(lr)) break; + else rec.LastHigh = lr.argValue; + if ((rec.UpdateMask & 0x100) != 0) + if (!br.GetLong(lr)) break; + else rec.LastLow = lr.argValue; + if ((rec.UpdateMask & 0x200) != 0) + if (!br.GetULong(lr)) break; + else rec.Volume = lr.argValue; + if ((rec.UpdateMask & 0x400) != 0) + if (!br.GetULong(lr)) break; + else rec.VolumeHigh = lr.argValue; + if ((rec.UpdateMask & 0x800) != 0) + if (!br.GetULong(lr)) break; + else rec.VolumeLow = lr.argValue; + if ((rec.UpdateMask & 0x1000) != 0) + if (!br.GetULong(lr)) break; + else rec.BuyVolume = lr.argValue; + if ((rec.UpdateMask & 0x2000) != 0) + if (!br.GetULong(lr)) break; + else rec.SellVolume = lr.argValue; + if ((rec.UpdateMask & 0x4000) != 0) + if (!br.GetULong(lr)) break; + else rec.BuyOrders = lr.argValue; + if ((rec.UpdateMask & 0x8000) != 0) + if (!br.GetULong(lr)) break; + else rec.SellOrders = lr.argValue; + if ((rec.UpdateMask & 0x10000) != 0) + if (!br.GetLong(lr)) break; + else rec.Deals = lr.argValue; + if ((rec.UpdateMask & 0x20000) != 0) + if (!br.GetULong(lr)) break; + else rec.DealsVolume = lr.argValue; + if ((rec.UpdateMask & 0x40000) != 0) + if (!br.GetLong(lr)) break; + else rec.Turnover = lr.argValue; + if ((rec.UpdateMask & 0x80000) != 0) + if (!br.GetLong(lr)) break; + else rec.OpenInterest = lr.argValue; + if ((rec.UpdateMask & 0x100000) != 0) + if (!br.GetLong(lr)) break; + else rec.OpenPrice = lr.argValue; + if ((rec.UpdateMask & 0x200000) != 0) + if (!br.GetLong(lr)) break; + else rec.ClosePrice = lr.argValue; + if ((rec.UpdateMask & 0x400000) != 0) + if (!br.GetLong(lr)) break; + else rec.AverageWeightPrice = lr.argValue; + if ((rec.UpdateMask & 0x800000) != 0) + if (!br.GetLong(lr)) break; + else rec.PriceChange = lr.argValue; + if ((rec.UpdateMask & 0x1000000) != 0) + if (!br.GetLong(lr)) break; + else rec.PriceVolatility = lr.argValue; + if ((rec.UpdateMask & 0x2000000) != 0) + if (!br.GetLong(lr)) break; + else rec.PriceTheoretical = lr.argValue; + if ((rec.UpdateMask & 0x8000000) != 0) + if (!br.GetLong(lr)) break; + else rec.PriceChange = lr.argValue; + if ((rec.UpdateMask & 0x4000000) != 0) + if (!br.GetLong(lr)) { + rec.TimeMs += rec.Time * 1000; + break; + } else rec.TimeMs = rec.Time * 1000; + if ((rec.UpdateMask & 0x10000000) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x20000000) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x40000000) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x80000000) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x100000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x200000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x400000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x800000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x1000000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x2000000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x4000000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x8000000000L) != 0) if (!br.GetLong(lr)) break; + + if ((rec.UpdateMask & 0x10000000000L) != 0) if (!br.GetLong(lr)) break; + + br.SkipRecords(rec.UpdateMask, 0x10000000000L); + if ((rec.UpdateMask & 0x4000000000000000L) != 0) { + long mask; + if (!br.GetULong(lr)) break; + br.SkipRecords(lr.argValue, 0); + } + if ((rec.UpdateMask & 0x8000000000000000L) != 0) { + long mask; + if (!br.GetULong(lr)) break; + br.SkipRecords(lr.argValue, 0); + } + br.AlignBitPosition(1); + SymbolInfo info; + try { + info = QuoteClient.Symbols.GetInfo(rec.Id); + } catch (Exception ex) { + continue; + } + + Quote quote; + + if (!Quotes.containsKey(info.Name)) { + continue; + } else { + quote = Quotes.get(info.Name); + if (quote == null) { + quote = new Quote(); + quote.Symbol = info.Name; + Quotes.put(quote.Symbol, quote); + } + } + RecToQuote(rec, quote); + if (quote.Bid != 0) { + if (quote.Ask != 0) { + if (QuoteClient.ServerDetails.getKey().ServerName.startsWith("LandFX")) { + double dif = 2 * Math.pow(10, -QuoteClient.Symbols.GetInfo(quote.Symbol).Digits); + quote.Bid += dif; + quote.Ask -= dif; + } + QuoteClient.OnQuoteCall(quote); + } + } + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SwapType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SwapType.java new file mode 100644 index 00000000000..4a38c67e49f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SwapType.java @@ -0,0 +1,42 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum SwapType { + SwapNone(0), + InPoints(1), + SymInfo_s408(2), // ??? + MarginCurrency(3), + Currency(4), + PercCurPrice(5), // In percentage terms, using current price + PercOpenPrice(6), // In percentage terms, using open price + PointClosePrice(7), // In points, reopen position by close price + PointBidPrice(8); // In points, reopen position by bid price + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (SwapType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private SwapType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static SwapType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymBaseInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymBaseInfo.java new file mode 100644 index 00000000000..115bd75bbfb --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymBaseInfo.java @@ -0,0 +1,286 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0xE84, CharSet = CharSet.Unicode)]*/ +public class SymBaseInfo extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s0; + public byte[] s0; + /*[FieldOffset(16)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s10; + /*[FieldOffset(144)]*/ public int s90; + /*[FieldOffset(148)]*/ public long s94; + /*[FieldOffset(156)]*/ public long s9C; + /*[FieldOffset(164)]*/ public long sA4; + /*[FieldOffset(172)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sAC; + public byte[] sAC; + /*[FieldOffset(236)]*/ public int sEC; + /*[FieldOffset(240)]*/ public long sF0; + /*[FieldOffset(248)]*/ public int sF8; + /*[FieldOffset(252)]*/ public int sFC; + /*[FieldOffset(256)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 60)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s100; + public byte[] s100; + /*[FieldOffset(316)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String CompanyName; + /*[FieldOffset(572)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ public String s23C; + /*[FieldOffset(1084)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s43C; + /*[FieldOffset(1212)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ public String s4BC; + /*[FieldOffset(1724)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s6BC; + /*[FieldOffset(1852)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s73C; + /*[FieldOffset(1980)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s7BC; + /*[FieldOffset(2108)]*/ public int s83C; + /*[FieldOffset(2112)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s840; + /*[FieldOffset(2240)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s8C0; + /*[FieldOffset(2368)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s940; + /*[FieldOffset(2496)]*/ public long s9C0; + /*[FieldOffset(2504)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s9C8; + public byte[] s9C8; + /*[FieldOffset(2568)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Currency; + /*[FieldOffset(2632)]*/ public int Digits; + /*[FieldOffset(2636)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sA4C; + public byte[] sA4C; + /*[FieldOffset(2764)]*/ public int sACC; + /*[FieldOffset(2768)]*/ public int sAD0; + /*[FieldOffset(2772)]*/ public int sAD4; + /*[FieldOffset(2776)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String sAD8; + /*[FieldOffset(2904)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ public int[] sB58; + /*[FieldOffset(2936)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sB78; + public byte[] sB78; + /*[FieldOffset(2968)]*/ public int ReceiveUserMsg; + /*[FieldOffset(2972)]*/ public int sB9C; + /*[FieldOffset(2976)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sBA0; + public byte[] sBA0; + /*[FieldOffset(3040)]*/ public io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.AccMethod + AccMethod; + /*[FieldOffset(3044)]*/ public int sBE4; + /*[FieldOffset(3048)]*/ public int sBE8; + /*[FieldOffset(3052)]*/ public MarginMode MarginMode; + /*[FieldOffset(3056)]*/ public double sBF0; + /*[FieldOffset(3064)]*/ public double sBF8; + /*[FieldOffset(3072)]*/ public int sC00; + /*[FieldOffset(3076)]*/ public double sC04; + /*[FieldOffset(3084)]*/ public int sC0C; + /*[FieldOffset(3088)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 60)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sC10; + public byte[] sC10; + /*[FieldOffset(3148)]*/ public int sC4C; + /*[FieldOffset(3152)]*/ public double sC50; + /*[FieldOffset(3160)]*/ public int sC58; + /*[FieldOffset(3164)]*/ public int sC5C; + /*[FieldOffset(3168)]*/ public int sC60; + /*[FieldOffset(3172)]*/ public double sC64; + /*[FieldOffset(3180)]*/ public long sC6C; + /*[FieldOffset(3188)]*/ public int sC74; + /*[FieldOffset(3192)]*/ public int sC78; + /*[FieldOffset(3196)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 248)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sC7C; + public byte[] sC7C; + /*[FieldOffset(3444)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] SymbolsHash; + public byte[] SymbolsHash; + /*[FieldOffset(3460)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sD84; + public byte[] sD84; + /*[FieldOffset(3492)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] SpreadsHash; + public byte[] SpreadsHash; + /*[FieldOffset(3508)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 208)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] sDB4; + public byte[] sDB4; + private String s8BC; + private String s9BC; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd; + if (buf.SymBuild <= 2097) endInd = buf.Ind + 3716; + else endInd = buf.Ind + 4228; + SymBaseInfo st = new SymBaseInfo(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s0 = new byte[16]; + st.s0 = new byte[16]; + for (int i = 0; i < 16; i++) { + st.s0[i] = buf.Byte(); + } + st.s10 = GetString(buf.Bytes(128)); + st.s90 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s94 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s9C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.sA4 = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sAC = new byte[64]; + st.sAC = new byte[64]; + for (int i = 0; i < 64; i++) { + st.sAC[i] = buf.Byte(); + } + st.sEC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sF0 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.sF8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sFC = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s100 = new byte[60]; + st.s100 = new byte[60]; + for (int i = 0; i < 60; i++) { + st.s100[i] = buf.Byte(); + } + st.CompanyName = GetString(buf.Bytes(256)); + st.s23C = GetString(buf.Bytes(512)); + st.s43C = GetString(buf.Bytes(128)); + st.s4BC = GetString(buf.Bytes(512)); + st.s6BC = GetString(buf.Bytes(128)); + st.s73C = GetString(buf.Bytes(128)); + if (buf.SymBuild <= 2097) { + st.s7BC = GetString(buf.Bytes(128)); + } else { + st.s7BC = GetString(buf.Bytes(256)); + st.s8BC = GetString(buf.Bytes(256)); + st.s9BC = GetString(buf.Bytes(128)); + } + st.s83C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s840 = GetString(buf.Bytes(128)); + st.s8C0 = GetString(buf.Bytes(128)); + st.s940 = GetString(buf.Bytes(128)); + st.s9C0 = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s9C8 = new byte[64]; + st.s9C8 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s9C8[i] = buf.Byte(); + } + st.Currency = GetString(buf.Bytes(64)); + if (st.Currency == "") throw new RuntimeException("Account currency is not correct"); + st.Digits = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sA4C = new byte[128]; + st.sA4C = new byte[128]; + for (int i = 0; i < 128; i++) { + st.sA4C[i] = buf.Byte(); + } + st.sACC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sAD0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sAD4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sAD8 = GetString(buf.Bytes(128)); + st.sB58 = new int[8]; + for (int i = 0; i < 8; i++) { + st.sB58[i] = BitConverter.ToInt32(buf.Bytes(4), 0); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sB78 = new byte[32]; + st.sB78 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.sB78[i] = buf.Byte(); + } + st.ReceiveUserMsg = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sB9C = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sBA0 = new byte[64]; + st.sBA0 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.sBA0[i] = buf.Byte(); + } + st.AccMethod = + io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.AccMethod.forValue( + BitConverter.ToInt32(buf.Bytes(4), 0)); + st.sBE4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sBE8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.MarginMode = MarginMode.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.sBF0 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.sBF8 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.sC00 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC04 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.sC0C = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sC10 = new byte[60]; + st.sC10 = new byte[60]; + for (int i = 0; i < 60; i++) { + st.sC10[i] = buf.Byte(); + } + st.sC4C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC50 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.sC58 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC5C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC60 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC64 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.sC6C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.sC74 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.sC78 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sC7C = new byte[248]; + st.sC7C = new byte[248]; + for (int i = 0; i < 248; i++) { + st.sC7C[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.SymbolsHash = new byte[16]; + st.SymbolsHash = new byte[16]; + for (int i = 0; i < 16; i++) { + st.SymbolsHash[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sD84 = new byte[32]; + st.sD84 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.sD84[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.SpreadsHash = new byte[16]; + st.SpreadsHash = new byte[16]; + for (int i = 0; i < 16; i++) { + st.SpreadsHash[i] = buf.Byte(); + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.sDB4 = new byte[208]; + st.sDB4 = new byte[208]; + for (int i = 0; i < 208; i++) { + st.sDB4[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymGroup.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymGroup.java new file mode 100644 index 00000000000..f027129eec8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymGroup.java @@ -0,0 +1,306 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x4CC, CharSet = CharSet.Unicode)]*/ +public class SymGroup extends FromBufReader { + public double MaxLots() { + return (double) MaxVolume / 100000000; + } + + public double MinLots() { + return (double) MinVolume / 100000000; + } + + public double LotsStep() { + return (double) VolumeStep / 100000000; + } + + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s0; + public byte[] s0; + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String GroupName; + /*[FieldOffset(264)]*/ public int DeviationRate; + /*[FieldOffset(268)]*/ public int RoundRate; + /*[FieldOffset(272)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s110; + public byte[] s110; + /*[FieldOffset(304)]*/ public TradeMode TradeMode; + /*[FieldOffset(308)]*/ public int SL; + /*[FieldOffset(312)]*/ public int TP; + /*[FieldOffset(316)]*/ public ExecutionType TradeType = ExecutionType.values()[0]; + /*[FieldOffset(320)]*/ public int FillPolicy; + /*[FieldOffset(324)]*/ public int Expiration; + /*[FieldOffset(328)]*/ public int OrderFlags; + /*[FieldOffset(332)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 52)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s14C; + public byte[] s14C; + /*[FieldOffset(384)]*/ public int s180; + /*[FieldOffset(388)]*/ public int PriceTimeout; + /*[FieldOffset(392)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s188; + public byte[] s188; + /*[FieldOffset(424)]*/ public int s1A8; + /*[FieldOffset(428)]*/ public int RequoteTimeout; + /*[FieldOffset(432)]*/ public int s1B0; + /*[FieldOffset(436)]*/ public int s1B4; + /*[FieldOffset(440)]*/ public int s1B8; + /*[FieldOffset(444)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint RequestLots; + public int RequestLots; + /*[FieldOffset(448)]*/ public int s1C4; + /*[FieldOffset(452)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 24)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s1C8; + public byte[] s1C8; + /*[FieldOffset(476)]*/ public int s1E0; + /*[FieldOffset(480)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s1E4; + public byte[] s1E4; + /*[FieldOffset(608)]*/ public int s264; + /*[FieldOffset(612)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s268; + public byte[] s268; + /*[FieldOffset(740)]*/ public int tmp1; + /*[FieldOffset(744)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint MinVolume; + public long MinVolume; + /*[FieldOffset(748)]*/ + // public int tmp2; + /*[FieldOffset(752)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public uint MaxVolume; + public long MaxVolume; + /*[FieldOffset(756)]*/ + // public int tmp3; + /*[FieldOffset(760)]*/ public long VolumeStep; + /*[FieldOffset(768)]*/ public long s300; + /*[FieldOffset(776)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 56)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s308; + public byte[] s308; + /*[FieldOffset(832)]*/ public int s340; + /*[FieldOffset(836)]*/ public double InitialMargin; + /*[FieldOffset(844)]*/ public double MaintenanceMargin; + /*[FieldOffset(852)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ public double[] InitMarginRate; + /*[FieldOffset(916)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 8)]*/ public double[] MntnMarginRate; + /*[FieldOffset(980)]*/ public double HedgedMargin; + /*[FieldOffset(988)]*/ public double s3DC; + /*[FieldOffset(996)]*/ public double s3E4; + /*[FieldOffset(1004)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 40)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s3EC; + public byte[] s3EC; + /*[FieldOffset(1044)]*/ public SwapType SwapType; + /*[FieldOffset(1048)]*/ public double SwapLong; + /*[FieldOffset(1056)]*/ public double SwapShort; + /*[FieldOffset(1064)]*/ public V3DaysSwap ThreeDaysSwap = V3DaysSwap.values()[0]; + /*[FieldOffset(1068)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s42C; + public byte[] s42C; + /*[FieldOffset(1100)]*/ public int s44C; + /*[FieldOffset(1104)]*/ public int s450; + /*[FieldOffset(1108)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 120)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s454; + public byte[] s454; + + @Override + public Object ReadFromBuf(InBuf buf) { + int startInd = buf.Ind; + int endInd = buf.Ind + 1228; + SymGroup st = new SymGroup(); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s0 = new byte[8]; + st.s0 = new byte[8]; + for (int i = 0; i < 8; i++) { + st.s0[i] = buf.Byte(); + } + st.GroupName = GetString(buf.Bytes(256)); + st.DeviationRate = BitConverter.ToInt32(buf.Bytes(4), 0); + st.RoundRate = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s110 = new byte[32]; + st.s110 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s110[i] = buf.Byte(); + } + st.TradeMode = TradeMode.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.SL = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TP = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TradeType = ExecutionType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.FillPolicy = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Expiration = BitConverter.ToInt32(buf.Bytes(4), 0); + st.OrderFlags = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s14C = new byte[52]; + st.s14C = new byte[52]; + for (int i = 0; i < 52; i++) { + st.s14C[i] = buf.Byte(); + } + st.s180 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.PriceTimeout = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s188 = new byte[32]; + st.s188 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s188[i] = buf.Byte(); + } + st.s1A8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.RequoteTimeout = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1B0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1B4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s1B8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.RequestLots = (int) BitConverter.ToUInt32(buf.Bytes(4), 0); + st.s1C4 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s1C8 = new byte[24]; + st.s1C8 = new byte[24]; + for (int i = 0; i < 24; i++) { + st.s1C8[i] = buf.Byte(); + } + st.s1E0 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s1E4 = new byte[128]; + st.s1E4 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s1E4[i] = buf.Byte(); + } + st.s264 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s268 = new byte[128]; + st.s268 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s268[i] = buf.Byte(); + } + st.tmp1 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.MinVolume = BitConverter.ToInt64(buf.Bytes(8), 0); + // st.tmp2 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.MaxVolume = BitConverter.ToInt64(buf.Bytes(8), 0); + // st.tmp3 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.VolumeStep = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s300 = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s308 = new byte[56]; + st.s308 = new byte[56]; + for (int i = 0; i < 56; i++) { + st.s308[i] = buf.Byte(); + } + st.s340 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.InitialMargin = BitConverter.ToDouble(buf.Bytes(8), 0); + st.MaintenanceMargin = BitConverter.ToDouble(buf.Bytes(8), 0); + st.InitMarginRate = new double[8]; + for (int i = 0; i < 8; i++) { + st.InitMarginRate[i] = BitConverter.ToDouble(buf.Bytes(8), 0); + } + st.MntnMarginRate = new double[8]; + for (int i = 0; i < 8; i++) { + st.MntnMarginRate[i] = BitConverter.ToDouble(buf.Bytes(8), 0); + } + st.HedgedMargin = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s3DC = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s3E4 = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s3EC = new byte[40]; + st.s3EC = new byte[40]; + for (int i = 0; i < 40; i++) { + st.s3EC[i] = buf.Byte(); + } + st.SwapType = SwapType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.SwapLong = BitConverter.ToDouble(buf.Bytes(8), 0); + st.SwapShort = BitConverter.ToDouble(buf.Bytes(8), 0); + st.ThreeDaysSwap = V3DaysSwap.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s42C = new byte[32]; + st.s42C = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s42C[i] = buf.Byte(); + } + st.s44C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s450 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s454 = new byte[120]; + st.s454 = new byte[120]; + for (int i = 0; i < 120; i++) { + st.s454[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } + + public void CopyValues(SymGroup grp) { + if (grp.DeviationRate != Integer.MAX_VALUE) DeviationRate = grp.DeviationRate; + if (grp.RoundRate != Integer.MAX_VALUE) RoundRate = grp.RoundRate; + if (grp.TradeMode != null) TradeMode = grp.TradeMode; + if (grp.SL != Integer.MAX_VALUE) SL = grp.SL; + if (grp.TP != Integer.MAX_VALUE) TP = grp.TP; + if (grp.TradeType != null) TradeType = grp.TradeType; + if ((int) grp.FillPolicy != -1) FillPolicy = grp.FillPolicy; + if (grp.Expiration != -1) Expiration = grp.Expiration; + if (grp.OrderFlags != -1) OrderFlags = grp.OrderFlags; + if (grp.s180 != -1) s180 = grp.s180; + if (grp.PriceTimeout != -1) PriceTimeout = grp.PriceTimeout; + if (grp.s1A8 != -1) s1A8 = grp.s1A8; + if (grp.RequoteTimeout != -1) RequoteTimeout = grp.RequoteTimeout; + if (grp.s1B0 != -1) s1B0 = grp.s1B0; + if (grp.s1B4 != -1) s1B4 = grp.s1B4; + if (grp.RequestLots != -1) RequestLots = grp.RequestLots; + if (grp.s1C4 != -1) s1C4 = grp.s1C4; + if (grp.MinVolume != -1) MinVolume = grp.MinVolume; + if (grp.MaxVolume != -1) MaxVolume = grp.MaxVolume; + if (grp.VolumeStep != -1) VolumeStep = grp.VolumeStep; + if (grp.s300 != -1) s300 = grp.s300; + if (grp.s340 != -1) s340 = grp.s340; + if (grp.InitialMargin != Double.MAX_VALUE) InitialMargin = grp.InitialMargin; + if (grp.MaintenanceMargin != Double.MAX_VALUE) MaintenanceMargin = grp.MaintenanceMargin; + if (grp.InitMarginRate[0] != Double.MAX_VALUE) InitMarginRate[0] = grp.InitMarginRate[0]; + if (grp.InitMarginRate[1] != Double.MAX_VALUE) InitMarginRate[1] = grp.InitMarginRate[1]; + if (grp.InitMarginRate[2] != Double.MAX_VALUE) InitMarginRate[2] = grp.InitMarginRate[2]; + if (grp.InitMarginRate[3] != Double.MAX_VALUE) InitMarginRate[3] = grp.InitMarginRate[3]; + if (grp.InitMarginRate[4] != Double.MAX_VALUE) InitMarginRate[4] = grp.InitMarginRate[4]; + if (grp.InitMarginRate[5] != Double.MAX_VALUE) InitMarginRate[5] = grp.InitMarginRate[5]; + if (grp.InitMarginRate[6] != Double.MAX_VALUE) InitMarginRate[6] = grp.InitMarginRate[6]; + if (grp.InitMarginRate[7] != Double.MAX_VALUE) InitMarginRate[7] = grp.InitMarginRate[7]; + if (grp.MntnMarginRate[0] != Double.MAX_VALUE) MntnMarginRate[0] = grp.MntnMarginRate[0]; + if (grp.MntnMarginRate[1] != Double.MAX_VALUE) MntnMarginRate[1] = grp.MntnMarginRate[1]; + if (grp.MntnMarginRate[2] != Double.MAX_VALUE) MntnMarginRate[2] = grp.MntnMarginRate[2]; + if (grp.MntnMarginRate[3] != Double.MAX_VALUE) MntnMarginRate[3] = grp.MntnMarginRate[3]; + if (grp.MntnMarginRate[4] != Double.MAX_VALUE) MntnMarginRate[4] = grp.MntnMarginRate[4]; + if (grp.MntnMarginRate[5] != Double.MAX_VALUE) MntnMarginRate[5] = grp.MntnMarginRate[5]; + if (grp.MntnMarginRate[6] != Double.MAX_VALUE) MntnMarginRate[6] = grp.MntnMarginRate[6]; + if (grp.MntnMarginRate[7] != Double.MAX_VALUE) MntnMarginRate[7] = grp.MntnMarginRate[7]; + if (grp.HedgedMargin != Double.MAX_VALUE) HedgedMargin = grp.HedgedMargin; + if (grp.s3DC != Double.MAX_VALUE) s3DC = grp.s3DC; + if (grp.s3E4 != Double.MAX_VALUE) s3E4 = grp.s3E4; + if (grp.SwapType != null) SwapType = grp.SwapType; + if (grp.SwapLong != Double.MAX_VALUE) SwapLong = grp.SwapLong; + if (grp.SwapShort != Double.MAX_VALUE) SwapShort = grp.SwapShort; + if ((int) grp.ThreeDaysSwap.getValue() != Integer.MAX_VALUE) ThreeDaysSwap = grp.ThreeDaysSwap; + s44C = grp.s44C; + s450 = grp.s450; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolInfo.java new file mode 100644 index 00000000000..c9164f9441b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolInfo.java @@ -0,0 +1,289 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x6A0, CharSet = CharSet.Unicode)]*/ +public class SymbolInfo extends FromBufReader { + /** Update time */ + /*[FieldOffset(0)]*/ public long UpdateTime; + + /** Symbol currency */ + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Name; + + /** Symbol ISIN */ + /*[FieldOffset(72)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String ISIN; + + /** Description */ + /*[FieldOffset(104)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String Description; + + /*[FieldOffset(232)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String sE8; + /*[FieldOffset(360)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s168; + /*[FieldOffset(424)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String s1A8; + + /** Reference to site */ + /*[FieldOffset(488)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]*/ public String RefToSite; + + /*[FieldOffset(1000)]*/ public int Custom; + /*[FieldOffset(1004)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 28)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s3EC; + public byte[] s3EC; + /*[FieldOffset(1032)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String s408; + + /** Currency for profit */ + /*[FieldOffset(1064)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String ProfitCurrency; + + /** Currency for margin */ + /*[FieldOffset(1096)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String MarginCurrency; + + /*[FieldOffset(1128)]*/ public int s468; + /*[FieldOffset(1132)]*/ public int Precision; + /*[FieldOffset(1136)]*/ public int s470; + /*[FieldOffset(1140)]*/ public int s474; + /*[FieldOffset(1144)]*/ public int s478; + + /** Background color */ + /*[FieldOffset(1148)]*/ public int BkgndColor; + + /*[FieldOffset(1152)]*/ public int s480; + /*[FieldOffset(1156)]*/ public int s484; + /*[FieldOffset(1160)]*/ public int s488; + /*[FieldOffset(1164)]*/ public int s48C; + + /** Significant digits */ + /*[FieldOffset(1168)]*/ public int Digits; + + /** Symbol points */ + /*[FieldOffset(1172)]*/ public double Points; + + /** Symbol limit points */ + /*[FieldOffset(1180)]*/ public double LimitPoints; + + /** Symbol id */ + /*[FieldOffset(1188)]*/ public int Id; + + /*[FieldOffset(1192)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s4A8; + public byte[] s4A8; + /*[FieldOffset(1224)]*/ public long s4C8; + + /** Depth of market */ + /*[FieldOffset(1232)]*/ public int DepthOfMarket; + + /*[FieldOffset(1236)]*/ public int s4D4; + /*[FieldOffset(1240)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 36)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s4D8; + public byte[] s4D8; + /*[FieldOffset(1276)]*/ public int s4FC; + /*[FieldOffset(1280)]*/ public int s500; + /*[FieldOffset(1284)]*/ public int s504; + /*[FieldOffset(1288)]*/ public int s508; + /*[FieldOffset(1292)]*/ public int s50C; + /*[FieldOffset(1296)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s510; + public byte[] s510; + /*[FieldOffset(1360)]*/ public long s550; + + /** Spread */ + /*[FieldOffset(1368)]*/ public int Spread; + + /*[FieldOffset(1372)]*/ public int s55C; + + /** Tick value */ + /*[FieldOffset(1376)]*/ public double TickValue; + + /** Tick size */ + /*[FieldOffset(1384)]*/ public double TickSize; + + /** Contract size */ + /*[FieldOffset(1392)]*/ public double ContractSize; + + /** Good till canceled mode */ + /*[FieldOffset(1400)]*/ public GTCMode GTCMode; + + /** Calculation mode */ + /*[FieldOffset(1404)]*/ public CalculationMode CalcMode = CalculationMode.values()[0]; + + /*[FieldOffset(1408)]*/ public int s580; + /*[FieldOffset(1412)]*/ public int s584; + + /** Settlement price */ + /*[FieldOffset(1416)]*/ public double SettlementPrice; + + /** Lower limit */ + /*[FieldOffset(1424)]*/ public double LowerLimit; + + /** Upper limit */ + /*[FieldOffset(1432)]*/ public double UpperLimit; + + /*[FieldOffset(1440)]*/ public double s5A0; + /*[FieldOffset(1448)]*/ public int s5A8; + + /** Face value */ + /*[FieldOffset(1452)]*/ public double FaceValue; + + /** Accuired interest */ + /*[FieldOffset(1460)]*/ public double AccruedInterest; + + /*[FieldOffset(1468)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 12)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s5BC; + public byte[] s5BC; + /*[FieldOffset(1480)]*/ public long s5C8; + + /** First trade time */ + /*[FieldOffset(1488)]*/ public long FirstTradeTime; + + /** Last trade time */ + /*[FieldOffset(1496)]*/ public long LastTradeTime; + + /*[FieldOffset(1504)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s5E0; + public byte[] s5E0; + /*[FieldOffset(1568)]*/ public int s620; + /*[FieldOffset(1572)]*/ public int s624; + /*[FieldOffset(1576)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 120)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s628; + public byte[] s628; + + public double bid_tickvalue; + public double ask_tickvalue; + public double tick_value; + public double tick_size; + private String s68; + private String _sE8; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 1696; + if (buf.SymBuild > 2097) endInd += 256; + SymbolInfo st = new SymbolInfo(); + st.UpdateTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Name = GetString(buf.Bytes(64)); + st.ISIN = GetString(buf.Bytes(32)); + if (buf.SymBuild > 2097) { + st.s68 = GetString(buf.Bytes(128)); + st._sE8 = GetString(buf.Bytes(128)); + } + st.Description = GetString(buf.Bytes(128)); + st.sE8 = GetString(buf.Bytes(128)); + st.s168 = GetString(buf.Bytes(64)); + st.s1A8 = GetString(buf.Bytes(64)); + st.RefToSite = GetString(buf.Bytes(512)); + st.Custom = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s3EC = new byte[28]; + st.s3EC = new byte[28]; + for (int i = 0; i < 28; i++) { + st.s3EC[i] = buf.Byte(); + } + st.s408 = GetString(buf.Bytes(32)); + st.ProfitCurrency = GetString(buf.Bytes(32)); + st.MarginCurrency = GetString(buf.Bytes(32)); + st.s468 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Precision = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s470 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s474 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s478 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.BkgndColor = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s480 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s484 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s488 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s48C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Digits = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Points = BitConverter.ToDouble(buf.Bytes(8), 0); + st.LimitPoints = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Id = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s4A8 = new byte[32]; + st.s4A8 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s4A8[i] = buf.Byte(); + } + st.s4C8 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.DepthOfMarket = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s4D4 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s4D8 = new byte[36]; + st.s4D8 = new byte[36]; + for (int i = 0; i < 36; i++) { + st.s4D8[i] = buf.Byte(); + } + st.s4FC = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s500 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s504 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s508 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s50C = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s510 = new byte[64]; + st.s510 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s510[i] = buf.Byte(); + } + st.s550 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Spread = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s55C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TickValue = BitConverter.ToDouble(buf.Bytes(8), 0); + st.TickSize = BitConverter.ToDouble(buf.Bytes(8), 0); + st.ContractSize = BitConverter.ToDouble(buf.Bytes(8), 0); + st.GTCMode = GTCMode.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.CalcMode = CalculationMode.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.s580 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s584 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.SettlementPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.LowerLimit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.UpperLimit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s5A0 = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s5A8 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.FaceValue = BitConverter.ToDouble(buf.Bytes(8), 0); + st.AccruedInterest = BitConverter.ToDouble(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s5BC = new byte[12]; + st.s5BC = new byte[12]; + for (int i = 0; i < 12; i++) { + st.s5BC[i] = buf.Byte(); + } + st.s5C8 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.FirstTradeTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.LastTradeTime = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s5E0 = new byte[64]; + st.s5E0 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s5E0[i] = buf.Byte(); + } + st.s620 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s624 = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s628 = new byte[120]; + st.s628 = new byte[120]; + for (int i = 0; i < 120; i++) { + st.s628[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMargin.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMargin.java new file mode 100644 index 00000000000..8ead5922f0a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMargin.java @@ -0,0 +1,785 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.IOException; + +class SymbolMargin { + class HedStatInfo // sizeof 0x30 d + { + public long m_lVolume; // 0 + public long m_lOrderVolume; // 8 + public double m_dLots; // 10 + public double m_dPrice; // 18 + public double m_dVolume; // 20 + public double m_dMarginRate; // 28 + } + + class TradesInfo // sizeof 0x328 d + { + public long m_nLogin; // 0 + public String s8 = new String(new char[16]); + public int m_nDigits; // 18 + public String s1C = new String(new char[36]); + public double m_dBalance; // 40 + public double m_dCredit; // 48 + public double m_dCommission; // 50 + public double m_dBlocked; // 58 + public String s60 = new String(new char[24]); + public double m_dMargin; // 78 + public double m_dMarginFree; // 80 + public double m_dMarginLevel; // 88 + public int m_nLeverage; // 90 + public int s94; + public double m_dMarginInitial; // 98 + public double m_dMarginMaintenance; // A0 + public String sA8 = new String(new char[16]); + public double m_dOrderProfit; // B8 + public double m_dSwap; // C0 + public double m_dOrderCommission; // C8 + public double m_dProfit; // D0 + public double m_dEquity; // D8 + public double m_dAssets; // E0 + public double m_dLiabilities; // E8 + public double m_dCollateral; // F0 + public String sF8 = new String(new char[136]); + public int s180; + public String s184 = new String(new char[316]); + public double s2C0; + public int s2C8; + public String s2CC = new String(new char[88]); + } + + private MT5API Api; + private String Symbol; + private HedStatInfo[] m_Deal = new HedStatInfo[2]; + private HedStatInfo[] m_Order = new HedStatInfo[8]; + private double[] m_dMntnMarginRate; + private double[] m_dInitMarginRate; + private double m_dInitialMargin; + private double m_dMaintenanceMargin; + private TradesInfo m_pTradesInfo = new TradesInfo(); + private double m_dContractSize; + private double s160; + private double s1E8; + private double s1F0; + private int sE8; + private int sEC; + private int sF0; + private int sF4; + private CalculationMode m_CalcMode = CalculationMode.values()[0]; + private double m_dTickSize; + private double m_dTickValue; + private double m_dFaceValue; + private double m_dTradeMargin; + private String m_sProfitCurrency; + private SymbolInfo m_SymInfo; + private String m_sCurrency; + private int s148; + private String m_sMarginCurrency; + + public SymbolMargin(MT5API api, String symbol) { + Api = api; + Symbol = symbol; + SymGroup gr = api.Symbols.GetGroup(symbol); + SymbolInfo sym = api.Symbols.GetInfo(symbol); + m_dContractSize = sym.ContractSize; + m_dInitMarginRate = gr.InitMarginRate; + m_dMntnMarginRate = gr.MntnMarginRate; + m_dInitialMargin = gr.InitialMargin; + m_dMaintenanceMargin = gr.MaintenanceMargin; + s160 = gr.s3DC; + s1E8 = gr.HedgedMargin; + s1F0 = gr.s3E4; + sE8 = sym.s468; + sEC = sym.Precision; + sF0 = gr.DeviationRate; + sF4 = gr.RoundRate; + m_CalcMode = sym.CalcMode; + m_dTickSize = sym.TickSize; + m_dTickValue = sym.TickValue; + m_dFaceValue = sym.FaceValue; + m_Deal[0] = new HedStatInfo(); + m_Deal[1] = new HedStatInfo(); + for (int i = 0; i < 8; i++) { + m_Order[i] = new HedStatInfo(); + } + m_pTradesInfo.m_nLeverage = api.Account.Leverage; + m_sProfitCurrency = api.Symbols.Base.Currency; + m_SymInfo = sym; + m_sCurrency = symbol; + s148 = gr.s340; + m_sMarginCurrency = sym.MarginCurrency; + } + + public final boolean AcceptDeal(DealInternal deal) { + if (deal.Type == null) + throw new RuntimeException( + "Deal.Type is null for " + deal.PositionTicket + ". TypeInt = " + deal.TypeInt); + if (deal.Type == DealType.DealBuy) { + m_Deal[0].m_lVolume += deal.Volume; + m_Deal[0].m_dLots += ULL2DBL(deal.Volume) * deal.OpenPrice; + m_Deal[0].m_dVolume += ULL2DBL(deal.Volume) * deal.VolumeRate; + m_Deal[0].m_dPrice = deal.Price; + m_Deal[0].m_dMarginRate = + m_dMntnMarginRate[0] != 0 ? m_dMntnMarginRate[0] : m_dInitMarginRate[0]; + return true; + } + if (deal.Type == DealType.DealSell) { + m_Deal[1].m_lVolume += deal.Volume; + m_Deal[1].m_dLots += ULL2DBL(deal.Volume) * deal.OpenPrice; + m_Deal[1].m_dVolume += ULL2DBL(deal.Volume) * deal.VolumeRate; + m_Deal[1].m_dPrice = deal.Price; + m_Deal[1].m_dMarginRate = + m_dMntnMarginRate[1] != 0 ? m_dMntnMarginRate[1] : m_dInitMarginRate[1]; + return true; + } + return false; + } + + public final boolean AcceptOrder(OrderInternal order, boolean bOrder) throws IOException { + if (m_CalcMode == CalculationMode.Collateral) { + return false; + } + OrderType type = order.Type; + if (order.IsAssociativeDealOrder() || (type == OrderType.CloseBy)) { + return true; + } + if (bOrder) { + if ((order.s1C8 == 1) && (order.IsLimitOrder() || order.IsStopOrder())) { + type = order.IsBuyOrder() ? OrderType.Buy : OrderType.Sell; + } else if ((order.s1C8 == 2) && order.IsStopLimitOrder()) { + type = order.IsBuyOrder() ? OrderType.BuyLimit : OrderType.SellLimit; + } + } + double mr = GetMarginRate(type, true, m_sCurrency); + if (mr <= 0) { + return true; + } + double price = order.OpenPrice; + if (price == 0 && ((type == OrderType.Buy) || (type == OrderType.Sell))) { + price = order.Price; + } + if ((type == OrderType.BuyStopLimit) || (type == OrderType.SellStopLimit)) { + price = order.OrderPrice; + } + double profitRate; + if ((type == OrderType.Buy) + || (type == OrderType.BuyStop) + || (type == OrderType.BuyLimit) + || (type == OrderType.BuyStopLimit)) { + profitRate = order.ProfitRate; + if (profitRate == 0) { + profitRate = GetAskProfitRate(m_sMarginCurrency, m_sCurrency, price); + } + if (profitRate <= 0) { + return true; + } + } else if ((type == OrderType.Sell) + || (type == OrderType.SellStop) + || (type == OrderType.SellLimit) + || (type == OrderType.SellStopLimit)) { + profitRate = order.ProfitRate; + if (profitRate == 0) { + profitRate = GetBidProfitRate(m_sMarginCurrency, m_sCurrency, price); + } + if (profitRate <= 0) { + return true; + } + } else { + return false; + } + m_Order[type.getValue()].m_lVolume += order.RequestVolume; + m_Order[type.getValue()].m_dLots += ULL2DBL(order.RequestVolume) * price; + m_Order[type.getValue()].m_dVolume += ULL2DBL(order.RequestVolume) * profitRate; + m_Order[type.getValue()].m_dMarginRate = mr; + return true; + } + + public final double GetTradeMargin() throws IOException { + TradesInfo trdInfo = m_pTradesInfo; + m_dTradeMargin = 0; + if (m_Deal[0].m_lVolume != 0) { + double vr = 1.0 / ULL2DBL(m_Deal[0].m_lVolume); + m_Deal[0].m_dLots *= vr; + m_Deal[0].m_dVolume *= vr; + } + if (m_Deal[1].m_lVolume != 0) { + double vr = 1.0 / ULL2DBL(m_Deal[1].m_lVolume); + m_Deal[1].m_dLots *= vr; + m_Deal[1].m_dVolume *= vr; + } + if (m_CalcMode == CalculationMode.Collateral) { + // vTickRate rate = new vTickRate(); + if (m_Deal[0].m_lVolume != 0) { + double profit = GetAskProfitRate(m_sProfitCurrency, m_SymInfo.Name); + double price = m_Deal[0].m_dPrice; + CalculationMode calcMode = CalculationMode.Forex; + if (price == 0) { + price = GetBid(m_sCurrency); + } + double volume = AsLots(m_Deal[0].m_lVolume, m_dContractSize); + double margin = + Math.round( + (Math.round((volume * price) * Math.pow(10, m_SymInfo.Digits)) + / Math.pow(10, m_SymInfo.Digits) + * profit + * s1E8) + * Math.pow(10, m_SymInfo.Digits)) + / Math.pow(10, m_SymInfo.Digits); + trdInfo.m_dAssets = RoundAdd(trdInfo.m_dAssets, margin, m_SymInfo.Digits); + trdInfo.m_dCollateral = RoundAdd(trdInfo.m_dCollateral, margin, m_SymInfo.Digits); + } + if (m_Deal[1].m_lVolume != 0) { + double profit = GetBidProfitRate(m_sProfitCurrency, m_SymInfo.Name); + double price = m_Deal[1].m_dPrice; + CalculationMode calcMode = CalculationMode.Forex; + if (price == 0) { + price = GetAsk(m_sCurrency); + } + double volume = AsLots(m_Deal[1].m_lVolume, m_dContractSize); + double margin = + -Math.round( + (Math.round((volume * price) * Math.pow(10, m_SymInfo.Digits)) + / Math.pow(10, m_SymInfo.Digits) + * profit) + * Math.pow(10, m_SymInfo.Digits)) + / Math.pow(10, m_SymInfo.Digits); + trdInfo.m_dLiabilities = RoundAdd(trdInfo.m_dLiabilities, margin, m_SymInfo.Digits); + trdInfo.m_dCollateral = RoundAdd(trdInfo.m_dCollateral, margin, m_SymInfo.Digits); + } + return m_dTradeMargin; + } + double buyMargin = 0; + double sellMargin = 0; + if ((s148 & 4) != 0) { + for (int i = 0; i < 8; i++) { + HedStatInfo stat = m_Order[i]; + if (stat.m_lVolume == 0) { + continue; + } + double vr = 1.0 / ULL2DBL(stat.m_lVolume); + stat.m_dLots *= vr; + stat.m_dVolume *= vr; + if ((i == OrderType.Buy.getValue()) + || (i == OrderType.BuyStop.getValue()) + || (i == OrderType.BuyLimit.getValue()) + || (i == OrderType.BuyStopLimit.getValue())) { + buyMargin += + CalcHedMargin(m_pTradesInfo, stat.m_lVolume, true, false, stat.m_dLots) + * stat.m_dVolume + * stat.m_dMarginRate; + } + if ((i == OrderType.Sell.getValue()) + || (i == OrderType.SellStop.getValue()) + || (i == OrderType.SellLimit.getValue()) + || (i == OrderType.SellStopLimit.getValue())) { + sellMargin += + CalcHedMargin(m_pTradesInfo, stat.m_lVolume, true, false, stat.m_dLots) + * stat.m_dVolume + * stat.m_dMarginRate; + } + } + double buyDealMargin = 0; + if (m_Deal[0].m_lVolume != 0) { + buyDealMargin = + CalcHedMargin(m_pTradesInfo, m_Deal[0].m_lVolume, false, false, m_Deal[0].m_dLots) + * m_Deal[0].m_dVolume + * m_Deal[0].m_dMarginRate; + } + double sellDealMargin = 0; + if (m_Deal[1].m_lVolume != 0) { + sellDealMargin = + CalcHedMargin(m_pTradesInfo, m_Deal[1].m_lVolume, false, false, m_Deal[1].m_dLots) + * m_Deal[1].m_dVolume + * m_Deal[1].m_dMarginRate; + } + buyMargin += buyDealMargin; + sellMargin += sellDealMargin; + m_dTradeMargin = Math.max(buyMargin, sellMargin); + } else { + double dealMargin = CalculateDealMargin(); + for (int i = 0; i < 8; i++) { + HedStatInfo stat = m_Order[i]; + if (stat.m_lVolume == 0) { + continue; + } + double vr = 1.0 / ULL2DBL(stat.m_lVolume); + stat.m_dLots *= vr; + stat.m_dVolume *= vr; + if ((i == OrderType.BuyStop.getValue()) + || (i == OrderType.BuyLimit.getValue()) + || (i == OrderType.BuyStopLimit.getValue())) { + buyMargin += + CalcHedMargin(m_pTradesInfo, stat.m_lVolume, true, false, stat.m_dLots) + * stat.m_dVolume + * stat.m_dMarginRate; + } + if ((i == OrderType.SellStop.getValue()) + || (i == OrderType.SellLimit.getValue()) + || (i == OrderType.SellStopLimit.getValue())) { + sellMargin += + CalcHedMargin(m_pTradesInfo, stat.m_lVolume, true, false, stat.m_dLots) + * stat.m_dVolume + * stat.m_dMarginRate; + } + } + if (m_Order[0].m_lVolume != 0) { + if (m_Deal[0].m_dMarginRate == 0) { + m_Deal[0].m_dMarginRate = m_Order[0].m_dMarginRate; + } + double vr = 1.0 / ULL2DBL(m_Order[0].m_lVolume + m_Deal[0].m_lVolume); + double orderAmount = ULL2DBL(m_Order[0].m_lVolume) * m_Order[0].m_dLots; + double dealAmount = ULL2DBL(m_Deal[0].m_lVolume) * m_Deal[0].m_dLots; + m_Deal[0].m_dLots = (orderAmount + dealAmount) * vr; + orderAmount = ULL2DBL(m_Order[0].m_lVolume) * m_Order[0].m_dVolume; + dealAmount = ULL2DBL(m_Deal[0].m_lVolume) * m_Deal[0].m_dVolume; + m_Deal[0].m_dVolume = (orderAmount + dealAmount) * vr; + m_Deal[0].m_lVolume += m_Order[0].m_lVolume; + m_Deal[0].m_lOrderVolume = m_Order[0].m_lVolume; + } + if (m_Order[1].m_lVolume != 0) { + if (m_Deal[1].m_dMarginRate == 0) { + m_Deal[1].m_dMarginRate = m_Order[1].m_dMarginRate; + } + double vr = 1.0 / ULL2DBL(m_Order[1].m_lVolume + m_Deal[1].m_lVolume); + double orderAmount = ULL2DBL(m_Order[1].m_lVolume) * m_Order[1].m_dLots; + double dealAmount = ULL2DBL(m_Deal[1].m_lVolume) * m_Deal[1].m_dLots; + m_Deal[1].m_dLots = (orderAmount + dealAmount) * vr; + orderAmount = ULL2DBL(m_Order[1].m_lVolume) * m_Order[1].m_dVolume; + dealAmount = ULL2DBL(m_Deal[1].m_lVolume) * m_Deal[1].m_dVolume; + m_Deal[1].m_dVolume = (orderAmount + dealAmount) * vr; + m_Deal[1].m_lVolume += m_Order[1].m_lVolume; + m_Deal[1].m_lOrderVolume = m_Order[1].m_lVolume; + } + double margin = dealMargin; + if (m_Order[0].m_lVolume != 0 || m_Order[1].m_lVolume != 0) { + dealMargin = CalculateDealMargin(); + margin = Math.max(margin, dealMargin); + } + m_dTradeMargin = margin + buyMargin + sellMargin; + } + trdInfo.m_dMargin = RoundAdd(trdInfo.m_dMargin, m_dTradeMargin, m_SymInfo.Digits); + return m_dTradeMargin; + } + + public final double CalculateDealMargin() { + double margin = 0; + if (m_Deal[0].m_lVolume > m_Deal[1].m_lVolume) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong tv = m_Deal[0].m_lVolume - m_Deal[1].m_lVolume; + long tv = m_Deal[0].m_lVolume - m_Deal[1].m_lVolume; + if (m_dInitialMargin == 0 + || m_dMaintenanceMargin == 0 + || (m_dInitialMargin == m_dMaintenanceMargin) + || m_Deal[0].m_lOrderVolume == 0) { + margin = CalcHedMargin(m_pTradesInfo, tv, false, false, m_Deal[0].m_dLots); + } else { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong dv = tv; + long dv = tv; + if (dv > m_Deal[0].m_lOrderVolume) { + dv -= m_Deal[0].m_lOrderVolume; + } + tv -= dv; + margin = CalcHedMargin(m_pTradesInfo, dv, true, false, m_Deal[0].m_dLots); + if (tv != 0) { + margin += CalcHedMargin(m_pTradesInfo, tv, false, false, m_Deal[0].m_dLots); + } + } + margin *= m_Deal[0].m_dVolume * m_Deal[0].m_dMarginRate; + } + if (m_Deal[0].m_lVolume < m_Deal[1].m_lVolume) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong tv = m_Deal[1].m_lVolume - m_Deal[0].m_lVolume; + long tv = m_Deal[1].m_lVolume - m_Deal[0].m_lVolume; + if (m_dInitialMargin == 0 + || m_dMaintenanceMargin == 0 + || (m_dInitialMargin == m_dMaintenanceMargin) + || m_Deal[1].m_lOrderVolume == 0) { + margin = CalcHedMargin(m_pTradesInfo, tv, false, false, m_Deal[1].m_dLots); + } else { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong dv = tv; + long dv = tv; + if (dv > m_Deal[1].m_lOrderVolume) { + dv -= m_Deal[1].m_lOrderVolume; + } + tv -= dv; + margin = CalcHedMargin(m_pTradesInfo, dv, true, false, m_Deal[1].m_dLots); + if (tv != 0) { + margin += CalcHedMargin(m_pTradesInfo, tv, false, false, m_Deal[1].m_dLots); + } + } + margin *= m_Deal[1].m_dVolume * m_Deal[1].m_dMarginRate; + } + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: ulong volume = Math.Min(m_Deal[0].m_lVolume, m_Deal[1].m_lVolume); + long volume = Math.min(m_Deal[0].m_lVolume, m_Deal[1].m_lVolume); + if (volume == 0) { + return margin; + } + double vr = 1.0 / ULL2DBL(m_Deal[0].m_lVolume + m_Deal[1].m_lVolume); + double buyVolume = ULL2DBL(m_Deal[0].m_lVolume); + double sellVolume = ULL2DBL(m_Deal[1].m_lVolume); + double lots = ((m_Deal[0].m_dLots * buyVolume) + (m_Deal[1].m_dLots * sellVolume)) * vr; + double amount = ((m_Deal[0].m_dVolume * buyVolume) + (m_Deal[1].m_dVolume * sellVolume)) * vr; + double rate = (m_Deal[0].m_dMarginRate + m_Deal[1].m_dMarginRate) * 0.5; + return margin + CalcHedMargin(m_pTradesInfo, volume, false, true, lots) * amount * rate; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public double CalcHedMargin(TradesInfo trdInfo, ulong volume, bool + // bInitialMargin, bool bPrice, double lots) + public final double CalcHedMargin( + TradesInfo trdInfo, long volume, boolean bInitialMargin, boolean bPrice, double lots) { + double price = AsLots(volume, m_dContractSize); + double amount = AsAmounts(volume); + double initialMargin = m_dInitialMargin; + if (!bInitialMargin && m_dMaintenanceMargin != 0) { + initialMargin = m_dMaintenanceMargin; + } + if (bPrice) { + if ((int) initialMargin != 0) { + initialMargin = s160; + } + price = AsLots(volume, s160); + } + double margin = 0; + double leveage = IntToDouble(trdInfo.m_nLeverage); + switch (m_CalcMode) { + case Forex: + if (initialMargin > 0) { + margin = amount * initialMargin / leveage; + } else { + margin = price / leveage; + } + break; + case Futures: + case ExchangeFutures: + case FORTSFutures: + case ExchangeMarginOption: + margin = amount * initialMargin; + break; + case CFD: + case ExchangeStocks: + if (initialMargin > 0) { + margin = amount * initialMargin; + } else { + margin = price * lots; + } + break; + case CFDIndex: + if (initialMargin > 0) { + margin = amount * initialMargin; + } else if (m_dTickSize != 0) { + margin = price * lots / m_dTickSize * m_dTickValue; + } + break; + case CFDLeverage: + if (initialMargin > 0) { + margin = amount * initialMargin / leveage; + } else { + margin = price * lots / leveage; + } + break; + case CalcMode5: + if (initialMargin > 0) { + margin = amount * initialMargin; + } else { + margin = price; + } + break; + case ExchangeBounds: + if (initialMargin > 0) { + margin = amount * initialMargin; + } else { + margin = + Math.round((price * lots * m_dFaceValue * 0.01) * Math.pow(10, sE8)) + / Math.pow(10, sE8); + } + break; + case Collateral: + break; + } + return margin; + } + + protected final double GetBidRate(String pCurrency1, String pCurrency2) throws IOException { + return GetBidRate(pCurrency1, pCurrency2, 0); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: protected double GetBidRate(string pCurrency1, string pCurrency2, double price = + // 0) + protected final double GetBidRate(String pCurrency1, String pCurrency2, double price) + throws IOException { + String cur = pCurrency1 + pCurrency2; + if (Api.Symbols.Exist(cur)) { + if (price != 0 + && ((m_SymInfo.CalcMode == CalculationMode.Forex) + || (m_SymInfo.CalcMode == CalculationMode.CalcMode5)) + && cur.compareTo(m_SymInfo.Name) != 0) { + return price; + } + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(cur).Bid; + } + cur = pCurrency2 + pCurrency1; + if (Api.Symbols.Exist(cur)) { + if (price != 0 + && ((m_SymInfo.CalcMode == CalculationMode.Forex) + || (m_SymInfo.CalcMode == CalculationMode.CalcMode5)) + && cur.compareTo(m_SymInfo.Name) != 0) { + return 1 / price; + } + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return 1 / Api.GetQuote(cur).Ask; + } + return 0; + } + + public final double GetBidProfitRate(String pCurrency1, String pCurrency2) throws IOException { + return GetBidProfitRate(pCurrency1, pCurrency2, 0); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: public double GetBidProfitRate(string pCurrency1, string pCurrency2, double + // price = 0) + public final double GetBidProfitRate(String pCurrency1, String pCurrency2, double price) + throws IOException { + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency1)) { + throw new RuntimeException("pCurrency1 is null or empty"); + } + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency2)) { + throw new RuntimeException("pCurrency2 is null or empty"); + } + if (pCurrency1.compareTo(pCurrency2) != 0 || IsRubleCurrency(pCurrency1, pCurrency2)) { + return 1.0; + } + double rate = GetBidRate(pCurrency1, pCurrency2, price); + if ((int) rate != 0) { + return rate; + } + double toUSD = GetBidRate(pCurrency1, "USD", price); + if (toUSD == 0) { + return 0; + } + double fromUsd = GetBidRate("USD", pCurrency2, price); + if (fromUsd == 0) { + return 0; + } + return toUSD * fromUsd; + } + + public static boolean IsRubleCurrency(String pCurrency1, String pCurrency2) { + String[] sCurrency = {"RUB", "RUR"}; + for (int i = 0; i < 1; i++) { + if (pCurrency1.compareTo(sCurrency[i]) != 0 && pCurrency2.compareTo(sCurrency[i + 1]) != 0) { + return true; + } + if (pCurrency2.compareTo(sCurrency[i]) != 0 && pCurrency1.compareTo(sCurrency[i + 1]) != 0) { + return true; + } + } + return false; + } + + protected final double GetAskRate(String pCurrency1, String pCurrency2) throws IOException { + return GetAskRate(pCurrency1, pCurrency2, 0); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: protected double GetAskRate(string pCurrency1, string pCurrency2, double price = + // 0) + protected final double GetAskRate(String pCurrency1, String pCurrency2, double price) + throws IOException { + String cur = pCurrency1 + pCurrency2; + if (Api.Symbols.Exist(cur)) { + if (price != 0 + && ((m_SymInfo.CalcMode == CalculationMode.Forex) + || (m_SymInfo.CalcMode == CalculationMode.CalcMode5)) + && cur.compareTo(m_SymInfo.Name) != 0) { + return price; + } + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(cur).Ask; + } + cur = pCurrency2 + pCurrency1; + if (Api.Symbols.Exist(cur)) { + if (price != 0 + && ((m_SymInfo.CalcMode == CalculationMode.Forex) + || (m_SymInfo.CalcMode == CalculationMode.CalcMode5)) + && cur.compareTo(m_SymInfo.Name) != 0) { + return 1 / price; + } + while (Api.GetQuote(cur) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return 1 / Api.GetQuote(cur).Bid; + } + return 0; + } + + public final double GetAskProfitRate(String pCurrency1, String pCurrency2) throws IOException { + return GetAskProfitRate(pCurrency1, pCurrency2, 0); + } + + // C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are + // created above: + // ORIGINAL LINE: public double GetAskProfitRate(string pCurrency1, string pCurrency2, double + // price = 0) + public final double GetAskProfitRate(String pCurrency1, String pCurrency2, double price) + throws IOException { + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency1)) { + throw new RuntimeException("pCurrency1 is null or empty"); + } + if (DotNetToJavaStringHelper.isNullOrWhiteSpace(pCurrency2)) { + throw new RuntimeException("pCurrency2 is null or empty"); + } + if (pCurrency1.compareTo(pCurrency2) != 0 || IsRubleCurrency(pCurrency1, pCurrency2)) { + return 1.0; + } + double rate = GetAskRate(pCurrency1, pCurrency2, price); + if ((int) rate != 0) { + return rate; + } + double toUSD = GetAskRate(pCurrency1, "USD", price); + if (toUSD == 0) { + return 0; + } + double fromUSD = GetAskRate("USD", pCurrency2, price); + if (fromUSD == 0) { + return 0; + } + return toUSD * fromUSD; + } + + protected final double GetPrice(Order order) throws IOException { + while (Api.GetQuote(order.Symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + if (order.OrderType == OrderType.Buy) { + return Api.GetQuote(order.Symbol).Bid; + } + if (order.OrderType == OrderType.Sell) { + return Api.GetQuote(order.Symbol).Ask; + } + return 0; + } + + protected final double GetBid(String symbol) throws IOException { + while (Api.GetQuote(symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(symbol).Bid; + } + + protected final double GetAsk(String symbol) throws IOException { + while (Api.GetQuote(symbol) == null) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + } + } + return Api.GetQuote(symbol).Ask; + } + + private double GetMarginRate(OrderType type, boolean init, String symbol) { + double[] initMarginRate = Api.Symbols.GetGroup(symbol).InitMarginRate; + double[] maintainceMarginRate = Api.Symbols.GetGroup(symbol).MntnMarginRate; + double im = 0; + double mm = 0; + switch (type) { + case Buy: + im = initMarginRate[0]; + mm = maintainceMarginRate[0]; + break; + case Sell: + im = initMarginRate[1]; + mm = maintainceMarginRate[1]; + break; + case BuyLimit: + im = initMarginRate[2]; + mm = maintainceMarginRate[2]; + break; + case SellLimit: + im = initMarginRate[3]; + mm = maintainceMarginRate[3]; + break; + case BuyStop: + im = initMarginRate[4]; + mm = maintainceMarginRate[4]; + break; + case SellStop: + im = initMarginRate[5]; + mm = maintainceMarginRate[5]; + break; + case BuyStopLimit: + im = initMarginRate[6]; + mm = maintainceMarginRate[6]; + break; + case SellStopLimit: + im = initMarginRate[7]; + mm = maintainceMarginRate[7]; + break; + } + return init ? im : mm; + } + + private double RoundAdd(double value1, double value2, int digits) { + return Math.round((value1 + value2) * Math.pow(10, digits)) / Math.pow(10, digits); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: double ULL2DBL(ulong value) + private double ULL2DBL(long value) { + return (double) value; + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: double AsLots(ulong value, double lots) + private double AsLots(long value, double lots) { + return Math.round((ULL2DBL(value) * 1.0e-8 * lots) * Math.pow(10, 8)) / Math.pow(10, 8); + } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: double AsAmounts(ulong value) + private double AsAmounts(long value) { + return Math.round((ULL2DBL(value) * 1.0e-8) * Math.pow(10, 8)) / Math.pow(10, 8); + } + + private double IntToDouble(int value) { + double _DP2to32 = 4.294967296e9; + double res = (double) value; + if (value < 0) { + res += _DP2to32; + } + return res; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMarketData.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMarketData.java new file mode 100644 index 00000000000..360de878faf --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolMarketData.java @@ -0,0 +1,42 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class SymbolMarketData { + public int Id; + public long UpdateMask; + public long Time; + // private int s14; + public long Bid; + public long BidHigh; + public long BidLow; + public long Ask; + public long AskHigh; + public long AskLow; + public long Last; + public long LastHigh; + public long LastLow; + public long Volume; + public long VolumeHigh; + public long VolumeLow; + public long Deals; + public long DealsVolume; + public long Turnover; + public long OpenInterest; + public long BuyOrders; + public long BuyVolume; + public long SellOrders; + public long SellVolume; + public long OpenPrice; + public long ClosePrice; + public long AverageWeightPrice; + public long PriceChange; + public long PriceVolatility; + public long PriceTheoretical; + public long TimeMs; + public long PriceDelta; + public long PriceTheta; + public long PriceGamma; + public long PriceVega; + public long PriceRho; + public long PriceOmega; + public long PriceSensitivity; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSessions.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSessions.java new file mode 100644 index 00000000000..4338b51c46a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSessions.java @@ -0,0 +1,8 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; + +public class SymbolSessions { + public ArrayList> Quotes; + public ArrayList> Trades; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSet.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSet.java new file mode 100644 index 00000000000..b51a2fd77aa --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/SymbolSet.java @@ -0,0 +1,24 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x188, CharSet = CharSet.Unicode)]*/ +public class SymbolSet extends FromBufReader { + /*[FieldOffset(0)]*/ public long UpdateTime; + /*[FieldOffset(8)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)]*/ public String GroupNames; + /*[FieldOffset(264)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 64)]*/ public String s108; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 392; + SymbolSet st = new SymbolSet(); + st.UpdateTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.GroupNames = GetString(buf.Bytes(256)); + st.s108 = GetString(buf.Bytes(128)); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Symbols.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Symbols.java new file mode 100644 index 00000000000..b853d2de885 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Symbols.java @@ -0,0 +1,63 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Symbols { + public SymBaseInfo Base; + public SymGroup[] SymGroups; + public SymbolInfo[] Infos; + public HashMap Sessions = new HashMap(); + public HashMap Groups = new HashMap(); + + public final SymbolInfo GetInfo(String symbol) { + for (SymbolInfo item : Infos) { + if (DotNetToJavaStringHelper.stringsEqual(item.Name, symbol)) { + return item; + } + } + throw new RuntimeException("Symbol not found: " + symbol); + } + + public final SymGroup GetGroup(String symbol) { + if (!Groups.containsKey(symbol)) { + throw new RuntimeException("Symbol not found: " + symbol); + } + SymGroup res = Groups.get(symbol); + for (SymGroup slave : SymGroups) { + String regex = slave.GroupName.replace("\\", "\\\\").replace("*", ".*"); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(res.GroupName); + if (matcher.matches()) res.CopyValues(slave); + } + return res; + } + + public final SymbolInfo GetInfo(int id) { + for (SymbolInfo item : Infos) { + if (item.Id == id) { + return item; + } + } + throw new RuntimeException("Symbol not found: " + id); + } + + public final boolean Exist(String symbol) { + for (SymbolInfo item : Infos) { + if (DotNetToJavaStringHelper.stringsEqual(item.Name, symbol)) { + return true; + } + } + return false; + } + + public final String ExistStartsWith(String symbol) { + for (SymbolInfo item : Infos) { + if (item.Name.startsWith(symbol)) { + return item.Name; + } + } + return null; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreadPool.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreadPool.java new file mode 100644 index 00000000000..9ca0b53da7c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreadPool.java @@ -0,0 +1,21 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +public class ThreadPool { + static Logger Log = new Logger("ThreadPool"); + + // public static ExecutorService Executor = Executors.newFixedThreadPool(10); + + public static void QueueUserWorkItem(Runnable worker) { + // Executor.execute(worker); + CompletableFuture.runAsync(worker) + .orTimeout(300, TimeUnit.SECONDS) + .exceptionally( + throwable -> { + Log.error(throwable.getMessage()); + return null; + }); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreeDaysSwap.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreeDaysSwap.java new file mode 100644 index 00000000000..74d5346ce99 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/ThreeDaysSwap.java @@ -0,0 +1,40 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum ThreeDaysSwap { + vSunday(0), + vMonday(1), + vTuesday(2), + vWednesday(3), + vThursday(4), + vFriday(5), + vSaturday(6); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (ThreeDaysSwap.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private ThreeDaysSwap(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static ThreeDaysSwap forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TickRec.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TickRec.java new file mode 100644 index 00000000000..8bc17c64f61 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TickRec.java @@ -0,0 +1,44 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x4E, CharSet = CharSet.Unicode)]*/ +public class TickRec extends FromBufReader { + /*[FieldOffset(0)]*/ public int Id; + /*[FieldOffset(4)]*/ public long Time; + /*[FieldOffset(12)]*/ public long TimeMs; + /*[FieldOffset(20)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong UpdateMask; + public long UpdateMask; + /*[FieldOffset(28)]*/ public long Bid; + /*[FieldOffset(36)]*/ public long Ask; + /*[FieldOffset(44)]*/ public long Last; + /*[FieldOffset(52)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Volume; + public long Volume; + /*[FieldOffset(60)]*/ public long s3C; + /*[FieldOffset(68)]*/ public long s44; + /*[FieldOffset(76)]*/ public short BankId; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 78; + TickRec st = new TickRec(); + st.Id = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Time = BitConverter.ToInt64(buf.Bytes(8), 0); + st.TimeMs = BitConverter.ToInt64(buf.Bytes(8), 0); + st.UpdateMask = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.Bid = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Ask = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Last = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Volume = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.s3C = BitConverter.ToInt64(buf.Bytes(8), 0); + st.s44 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.BankId = BitConverter.ToInt16(buf.Bytes(2), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Ticker.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Ticker.java new file mode 100644 index 00000000000..1d7b796364c --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Ticker.java @@ -0,0 +1,32 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x5A, CharSet = CharSet.Unicode)]*/ +public class Ticker extends FromBufReader { + /*[FieldOffset(0)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Name; + /*[FieldOffset(64)]*/ public short BankId; + /*[FieldOffset(66)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 24)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s42; + public byte[] s42; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 90; + Ticker st = new Ticker(); + st.Name = GetString(buf.Bytes(64)); + st.BankId = BitConverter.ToInt16(buf.Bytes(2), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s42 = new byte[24]; + st.s42 = new byte[24]; + for (int i = 0; i < 24; i++) { + st.s42[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TimeoutException.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TimeoutException.java new file mode 100644 index 00000000000..eaa39ab101b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TimeoutException.java @@ -0,0 +1,17 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Trade timeout exception. */ +public class TimeoutException extends Exception { + /** */ + private static final long serialVersionUID = 1L; + + /** + * Initialize TimeoutException. + * + * @param message Exception message. + */ + public TimeoutException(String message, Logger log) { + super(message); + log.warn(getMessage()); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeMode.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeMode.java new file mode 100644 index 00000000000..fd32a2b5821 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeMode.java @@ -0,0 +1,38 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum TradeMode { + Disabled(0), + LongOnly(1), + ShortOnly(2), + CloseOnly(3), + FullAccess(4); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (TradeMode.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private TradeMode(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static TradeMode forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequest.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequest.java new file mode 100644 index 00000000000..0768d138284 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequest.java @@ -0,0 +1,271 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Size = 0x320, CharSet = CharSet.Unicode)]*/ +public class TradeRequest extends FromBufReader { + // + /** Request id */ + /*[FieldOffset(0)]*/ public int RequestId; + + /*[FieldOffset(4)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s4; + public byte[] s4; + + /** Trade type */ + /*[FieldOffset(68)]*/ public TradeType TradeType; + + /** Account login */ + /*[FieldOffset(72)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Login; + public long Login; + + /*[FieldOffset(80)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 72)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s50; + public byte[] s50; + + /** Transfer login */ + /*[FieldOffset(152)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong TransferLogin; + public long TransferLogin; + + /*[FieldOffset(160)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String sA0; + + /** Symbol currency */ + /*[FieldOffset(288)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String Currency; + + /** Lots */ + /*[FieldOffset(352)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Lots; + public long Lots; + + /*[FieldOffset(360)]*/ public long s168; + + /** Significant digits */ + /*[FieldOffset(368)]*/ public int Digits; + + /*[FieldOffset(372)]*/ public long s174; + /*[FieldOffset(380)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s17C; + public byte[] s17C; + + /** Order ticket */ + /*[FieldOffset(400)]*/ public long OrderTicket; + + /*[FieldOffset(408)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s198; + public byte[] s198; + /*[FieldOffset(472)]*/ public long s1D8; + + /** Expiration time */ + /*[FieldOffset(480)]*/ public long ExpirationTime; + + /** Order type */ + /*[FieldOffset(488)]*/ public OrderType OrderType; + + /** Fill policy */ + /*[FieldOffset(492)]*/ public FillPolicy FillPolicy; + + /** Expiration type */ + /*[FieldOffset(496)]*/ public ExpirationType ExpirationType = + io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.ExpirationType.values()[0]; + + /** Request flags */ + /*[FieldOffset(500)]*/ public long Flags; + + /** Placed type */ + /*[FieldOffset(508)]*/ public PlacedType PlacedType; + + /*[FieldOffset(512)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s200; + public byte[] s200; + + /** Price */ + /*[FieldOffset(528)]*/ public double Price; + + /** Order price */ + /*[FieldOffset(536)]*/ public double OrderPrice; + + /** Stop loss */ + /*[FieldOffset(544)]*/ public double StopLoss; + + /** Take profit */ + /*[FieldOffset(552)]*/ public double TakeProfit; + + /** Deviation */ + /*[FieldOffset(560)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Deviation; + public long Deviation; + + /*[FieldOffset(568)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s238; + public byte[] s238; + + /** Expert id */ + /*[FieldOffset(600)]*/ public long ExpertId; + + /** Text comment */ + /*[FieldOffset(608)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]*/ public String Comment; + + /** Deal ticket */ + /*[FieldOffset(672)]*/ public long DealTicket; + + /** By close deal ticket */ + /*[FieldOffset(680)]*/ public long ByCloseTicket; + + /*[FieldOffset(688)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 112)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s2B0; + public byte[] s2B0; + + // new internal string GetString(byte[] buf) + // { + // int count = 0; + // for (int i = 0; i < buf.Length; i += 2) + // { + // if (buf[i] == 0 && buf[i + 1] == 0) + // break; + // count++; + // } + // byte[] res = new byte[count * 2]; + // for (int i = 0; i < count * 2; i++) + // res[i] = buf[i]; + // string result = Encoding.Unicode.GetString(res); + // return result; + // } + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 800; + TradeRequest st = new TradeRequest(); + st.RequestId = BitConverter.ToInt32(buf.Bytes(4), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s4 = new byte[64]; + st.s4 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s4[i] = buf.Byte(); + } + st.TradeType = TradeType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.Login = BitConverter.ToUInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s50 = new byte[72]; + st.s50 = new byte[72]; + for (int i = 0; i < 72; i++) { + st.s50[i] = buf.Byte(); + } + st.TransferLogin = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.sA0 = GetString(buf.Bytes(128)); + st.Currency = GetString(buf.Bytes(64)); + st.Lots = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.s168 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Digits = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s174 = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s17C = new byte[20]; + st.s17C = new byte[20]; + for (int i = 0; i < 20; i++) { + st.s17C[i] = buf.Byte(); + } + st.OrderTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s198 = new byte[64]; + st.s198 = new byte[64]; + for (int i = 0; i < 64; i++) { + st.s198[i] = buf.Byte(); + } + st.s1D8 = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ExpirationTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.OrderType = OrderType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.FillPolicy = FillPolicy.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.ExpirationType = ExpirationType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.Flags = BitConverter.ToInt64(buf.Bytes(8), 0); + st.PlacedType = PlacedType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s200 = new byte[16]; + st.s200 = new byte[16]; + for (int i = 0; i < 16; i++) { + st.s200[i] = buf.Byte(); + } + st.Price = BitConverter.ToDouble(buf.Bytes(8), 0); + st.OrderPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.StopLoss = BitConverter.ToDouble(buf.Bytes(8), 0); + st.TakeProfit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Deviation = BitConverter.ToUInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s238 = new byte[32]; + st.s238 = new byte[32]; + for (int i = 0; i < 32; i++) { + st.s238[i] = buf.Byte(); + } + st.ExpertId = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Comment = GetString(buf.Bytes(64)); + st.DealTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + st.ByCloseTicket = BitConverter.ToInt64(buf.Bytes(8), 0); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s2B0 = new byte[112]; + st.s2B0 = new byte[112]; + for (int i = 0; i < 112; i++) { + st.s2B0[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } + + public final void WriteToBuf(OutBuf buf) { + buf.Add(RequestId); + buf.Add(s4, 64); + buf.Add(TradeType.getValue()); + buf.Add(Login); + buf.Add(s50, 72); + buf.Add(TransferLogin); + buf.Add(sA0, 32); + buf.Add(Currency, 16); + buf.Add(Lots); + buf.Add(s168); + buf.Add(Digits); + buf.Add(s174); + buf.Add(s17C, 20); + buf.Add(OrderTicket); + buf.Add(s198, 64); + buf.Add(s1D8); + buf.Add(ExpirationTime); + buf.Add(OrderType.getValue()); + buf.Add(FillPolicy.getValue()); + buf.Add(ExpirationType.getValue()); + buf.Add(Flags); + buf.Add(PlacedType.getValue()); + buf.Add(s200, 16); + buf.Add(Price); + buf.Add(OrderPrice); + buf.Add(StopLoss); + buf.Add(TakeProfit); + buf.Add(Deviation); + buf.Add(s238, 32); + buf.Add(ExpertId); + buf.Add(Comment, 16); + buf.Add(DealTicket); + buf.Add(ByCloseTicket); + buf.Add(s2B0, 112); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequestInternal.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequestInternal.java new file mode 100644 index 00000000000..0d6db21c14e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeRequestInternal.java @@ -0,0 +1,185 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: +// ORIGINAL LINE: [StructLayout(LayoutKind.Explicit, Size = 0x320, CharSet = CharSet.Unicode)] +// public class TradeRequestInternal +public class TradeRequestInternal { + // + /** Request id */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(0)] public int RequestId; + public int RequestId; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(4)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)] + // public byte[] s4; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s4; + + /** Trade type */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(68)] public TradeType TradeType; + public TradeType TradeType; + + /** Account login */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(72)] public ulong Login; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public long Login; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(80)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 72)] + // public byte[] s50; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s50; + + /** Transfer login */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(152)] public ulong TransferLogin; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public long TransferLogin; + + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(160)][MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)] + // public string sA0; + public String sA0; + + /** Symbol currency */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(288)][MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)] + // public string Currency; + public String Currency; + + /** Lots */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(352)] public ulong Lots; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public long Lots; + + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(360)] public long s168; + public long s168; + + /** Significant digits */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(368)] public int Digits; + public int Digits; + + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(372)] public long s174; + public long s174; + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(380)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 20)] + // public byte[] s17C; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s17C; + + /** Order ticket */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(400)] public long OrderTicket; + public long OrderTicket; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(408)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 64)] + // public byte[] s198; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s198; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(472)] public long s1D8; + public long s1D8; + + /** Expiration time */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(480)] public long ExpirationTime; + public long ExpirationTime; + + /** Order type */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(488)] public OrderType OrderType; + public OrderType OrderType; + + /** Fill policy */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(492)] public FillPolicy FillPolicy; + public FillPolicy FillPolicy; + + /** Expiration type */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(496)] public ExpirationDate ExpirationType; + public ExpirationDate ExpirationType = ExpirationDate.values()[0]; + + /** Request flags */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(500)] public long Flags; + public long Flags; + + /** Placed type */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(508)] public PlacedType PlacedType; + public PlacedType PlacedType; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(512)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)] + // public byte[] s200; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s200; + + /** Price */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(528)] public double Price; + public double Price; + + /** Order price */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(536)] public double OrderPrice; + public double OrderPrice; + + /** Stop loss */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(544)] public double StopLoss; + public double StopLoss; + + /** Take profit */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(552)] public double TakeProfit; + public double TakeProfit; + + /** Deviation */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(560)] public ulong Deviation; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public long Deviation; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(568)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)] + // public byte[] s238; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s238; + + /** Expert id */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(600)] public long ExpertId; + public long ExpertId; + + /** Text comment */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(608)][MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)] + // public string Comment; + public String Comment; + + /** Deal ticket */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(672)] public long DealTicket; + public long DealTicket; + + /** By close deal ticket */ + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + // ORIGINAL LINE: [FieldOffset(680)] public long ByCloseTicket; + public long ByCloseTicket; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: [FieldOffset(688)][MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = + // 112)] public byte[] s2B0; + // C# TO JAVA CONVERTER TODO TASK: Java annotations will not correspond to .NET attributes: + public byte[] s2B0; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeResult.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeResult.java new file mode 100644 index 00000000000..b3613aab50b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeResult.java @@ -0,0 +1,53 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x104, CharSet = CharSet.Unicode)]*/ +public class TradeResult extends FromBufReader { + /*[FieldOffset(0)]*/ public Msg Status = Msg.values()[0]; + /*[FieldOffset(4)]*/ public long PositionId; + /*[FieldOffset(12)]*/ public long TicketNumber; + /*[FieldOffset(20)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Volume; + public long Volume; + /*[FieldOffset(28)]*/ public double OpenPrice; + /*[FieldOffset(36)]*/ public int s0; + /*[FieldOffset(40)]*/ public int s4; + /*[FieldOffset(44)]*/ public double Bid; + /*[FieldOffset(52)]*/ public double Ask; + /*[FieldOffset(60)]*/ public double Last; + /*[FieldOffset(68)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Comment; + /*[FieldOffset(132)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public byte[] s84; + public byte[] s84; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 260; + TradeResult st = new TradeResult(); + st.Status = Msg.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.PositionId = BitConverter.ToInt64(buf.Bytes(8), 0); + st.TicketNumber = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Volume = BitConverter.ToUInt64(buf.Bytes(8), 0); + st.OpenPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.s0 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s4 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Bid = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Ask = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Last = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Comment = GetString(buf.Bytes(64)); + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: st.s84 = new byte[128]; + st.s84 = new byte[128]; + for (int i = 0; i < 128; i++) { + st.s84[i] = buf.Byte(); + } + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeType.java new file mode 100644 index 00000000000..5044ce03bd8 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TradeType.java @@ -0,0 +1,60 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum TradeType { + TradePrice(0), + RequestExecution(1), + InstantExecution(2), + MarketExecution(3), + ExchangeExecution(4), + SetOrder(5), + ModifyDeal(6), + ModifyOrder(7), + CancelOrder(8), + Transfer(9), + ClosePosition(10), + ActivateOrder(100), + ActivateStopLoss(101), + ActivateTakeProfit(102), + ActivateStopLimitOrder(103), + ActivateStopOutOrder(104), + ActivateStopOutPosition(105), + ExpireOrder(106), + ForSetOrder(200), + ForOrderPrice(201), + ForModifyDeal(202), + ForModifyOrder(203), + ForCancelOrder(204), + ForActivateOrder(205), + ForBalance(206), + ForActivateStopLimitOrder(207), + ForClosePosition(208); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (TradeType.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private TradeType(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static TradeType forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TransactionInfo.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TransactionInfo.java new file mode 100644 index 00000000000..10201f1ab4f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TransactionInfo.java @@ -0,0 +1,82 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/*[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x98, CharSet = CharSet.Unicode)]*/ +public class TransactionInfo extends FromBufReader { + /** Transaction ticket */ + /*[FieldOffset(0)]*/ public int UpdateId; + + /** Order ticket */ + /*[FieldOffset(4)]*/ public int Action; + + /** Deal ticket */ + /*[FieldOffset(8)]*/ public long TicketNumber; + + /*[FieldOffset(16)]*/ + /*[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 32)]*/ public String Currency; + + /** Symbol currency */ + /*[FieldOffset(80)]*/ public int Id; + + /** Significant digits */ + /*[FieldOffset(84)]*/ public int s54; + + /** Transaction id */ + /*[FieldOffset(88)]*/ public OrderType s58 = OrderType.values()[0]; + + /** Transaction type */ + /*[FieldOffset(92)]*/ public int s5C; + + /** Order type */ + /*[FieldOffset(96)]*/ public OrderState OrderState; + + /** Order state */ + /*[FieldOffset(100)]*/ public ExpirationType ExpirationType; + + /** Order placed type */ + /*[FieldOffset(104)]*/ public long ExpirationTime; + + /** Deal type */ + /*[FieldOffset(112)]*/ public double OpenPrice; + + /** Deal placed type */ + /*[FieldOffset(120)]*/ public double OrderPrice; + + /** Expiration type */ + /*[FieldOffset(128)]*/ public double StopLoss; + + /** Expiration time */ + /*[FieldOffset(136)]*/ public double TakeProfit; + + /** Open price */ + /*[FieldOffset(144)]*/ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public ulong Volume; + public long Volume; + + @Override + public Object ReadFromBuf(InBuf buf) { + int endInd = buf.Ind + 152; + TransactionInfo st = new TransactionInfo(); + st.UpdateId = BitConverter.ToInt32(buf.Bytes(4), 0); + st.Action = BitConverter.ToInt32(buf.Bytes(4), 0); + st.TicketNumber = BitConverter.ToInt64(buf.Bytes(8), 0); + st.Currency = GetString(buf.Bytes(64)); + st.Id = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s54 = BitConverter.ToInt32(buf.Bytes(4), 0); + st.s58 = OrderType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.s5C = BitConverter.ToInt32(buf.Bytes(4), 0); + st.OrderState = OrderState.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.ExpirationType = ExpirationType.forValue(BitConverter.ToInt32(buf.Bytes(4), 0)); + st.ExpirationTime = BitConverter.ToInt64(buf.Bytes(8), 0); + st.OpenPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.OrderPrice = BitConverter.ToDouble(buf.Bytes(8), 0); + st.StopLoss = BitConverter.ToDouble(buf.Bytes(8), 0); + st.TakeProfit = BitConverter.ToDouble(buf.Bytes(8), 0); + st.Volume = BitConverter.ToUInt64(buf.Bytes(8), 0); + if (buf.Ind != endInd) { + throw new RuntimeException( + "Wrong reading from buffer(buf.Ind != endInd): " + buf.Ind + " != " + endInd); + } + return st; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Trial.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Trial.java new file mode 100644 index 00000000000..9ef8c878d27 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/Trial.java @@ -0,0 +1,38 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.*; +import java.net.*; +import java.security.*; +import java.util.*; +import javax.crypto.*; + +class Trial { + private static String Res = null; + + static void check(String guid) { + try { + if (true) return; + if (Res == null) { + URL obj = new URL("https://trial.mtapi.io/NewCheckMT5Java?guid=" + guid); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + con.setRequestMethod("POST"); + con.setDoOutput(true); + PrintWriter wr = new PrintWriter(con.getOutputStream()); + wr.flush(); + wr.close(); + int responseCode = con.getResponseCode(); + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + Res = response.toString(); + } + if (!Res.startsWith("OK")) throw new Exception(Res); + } catch (Exception ex) { + throw new RuntimeException("Trial check exception: " + ex.getMessage()); + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TryParseHelper.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TryParseHelper.java new file mode 100644 index 00000000000..79132ec4a07 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/TryParseHelper.java @@ -0,0 +1,72 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +// ---------------------------------------------------------------------------------------- +// Copyright © 2007 - 2017 Tangible Software Solutions Inc. +// This class can be used by anyone provided that the copyright notice remains intact. +// +// This class is used to convert some of the C# TryParse methods to Java. +// ---------------------------------------------------------------------------------------- +public final class TryParseHelper { + public static boolean tryParseInt(String s, RefObject result) { + try { + result.argValue = Integer.parseInt(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseShort(String s, RefObject result) { + try { + result.argValue = Short.parseShort(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseLong(String s, RefObject result) { + try { + result.argValue = Long.parseLong(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseByte(String s, RefObject result) { + try { + result.argValue = Byte.parseByte(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseDouble(String s, RefObject result) { + try { + result.argValue = Double.parseDouble(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseFloat(String s, RefObject result) { + try { + result.argValue = Float.parseFloat(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } + + public static boolean tryParseBoolean(String s, RefObject result) { + try { + result.argValue = Boolean.parseBoolean(s); + return true; + } catch (NumberFormatException e) { + return false; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UDT.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UDT.java new file mode 100644 index 00000000000..8dd146f5a0f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UDT.java @@ -0,0 +1,100 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.ArrayList; + +class UDT { + // public static T ReadStruct(byte[] data, int offset) + // { + // int size = Marshal.SizeOf(typeof(T)); + // IntPtr ptr = Marshal.AllocHGlobal(size); + // Marshal.Copy(data, offset, ptr, size); + // T temp = (T)Marshal.PtrToStructure(ptr, typeof(T)); + // Marshal.FreeHGlobal(ptr); + // return temp; + // } + + // public static T ReadStruct(byte[] data, int offset, int size) + // { + // IntPtr ptr = Marshal.AllocHGlobal(size); + // Marshal.Copy(data, offset, ptr, size); + // T temp = (T)Marshal.PtrToStructure(ptr, typeof(T)); + // Marshal.FreeHGlobal(ptr); + // return temp; + // } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public static T ReadStruct(InBuf buf) where T : FromBufReader, new() + public static T ReadStruct(InBuf buf, T t) { + return (T) t.ReadFromBuf(buf); + } + + // C# TO JAVA CONVERTER TODO TASK: The C# 'new()' constraint has no equivalent in Java: + // ORIGINAL LINE: public static T ReadStruct(byte[] data, int offset, int size) where T : + // FromBufReader, new() + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + public static T ReadStruct(byte[] data, int offset, int size, T t) { + InBuf buf = new InBuf(data, offset); + return (T) t.ReadFromBuf(buf); + } + + // public static byte[] GetBytes(object obj) + // { + // int size; + // if (obj is TradeRequest) + // size = 800; + // else + // throw new Exception("Unknown type"); + // byte[] buffer = new byte[size]; + // IntPtr ptr = Marshal.AllocHGlobal(size); + // Marshal.StructureToPtr(obj, ptr, false); + // Marshal.Copy(ptr, buffer, 0, size); + // Marshal.FreeHGlobal(ptr); + // return buffer; + // } + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] GetBytes(TradeRequest req) + public static byte[] GetBytes(TradeRequest req) { + OutBuf buf = new OutBuf(); + req.WriteToBuf(buf); + return ByteLists.toArray(buf.List); + } + + static String readString(byte[] buf, int of, int len) { + ArrayList res = new ArrayList(); + for (int i = 0; i < len; i += 2) { + if (buf[of + i] == 0 && buf[of + i + 1] == 0) break; + res.add(buf[of + i]); + res.add(buf[of + i + 1]); + } + byte[] array = new byte[res.size()]; + for (int i = 0; i < res.size(); i++) array[i] = res.get(i); + try { + return new String(array, java.nio.charset.StandardCharsets.UTF_16LE.toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + static String readStringASCII(byte[] buf, int of, int len) { + ArrayList res = new ArrayList(); + for (int i = 0; i < len; i += 1) { + if (buf[of + i] == 0) // && buf[of + i + 1] == 0 + if (i > 0) break; + res.add(buf[of + i]); + // res.Add(buf[of + i + 1]); + } + if (res.size() > 0) if (res.get(0) == 0) res.remove(0); + byte[] array = new byte[res.size()]; + for (int i = 0; i < res.size(); i++) array[i] = res.get(i); + String r = null; + try { + r = new String(array, Charset.forName("ASCII").toString()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + return r; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UpdateType.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UpdateType.java new file mode 100644 index 00000000000..3ecd377c0e4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/UpdateType.java @@ -0,0 +1,31 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum UpdateType { + Unknown, + PendingClose, + MarketOpen, + PendingOpen, + MarketClose, + PartialClose, + Started, + Filled, + Cancelling, + MarketModify, + PendingModify, + OnStopLoss, + OnTakeProfit, + OnStopOut, + Balance, + Expired, + Rejected; + + public static final int SIZE = java.lang.Integer.SIZE; + + public int getValue() { + return this.ordinal(); + } + + public static UpdateType forValue(int value) { + return values()[value]; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/V3DaysSwap.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/V3DaysSwap.java new file mode 100644 index 00000000000..1e4ddd517f4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/V3DaysSwap.java @@ -0,0 +1,40 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +public enum V3DaysSwap { + Sunday(0), + Monday(1), + Tuesday(2), + Wednesday(3), + Thursday(4), + Friday(5), + Saturday(6); + + public static final int SIZE = java.lang.Integer.SIZE; + + private int intValue; + private static java.util.HashMap mappings; + + private static java.util.HashMap getMappings() { + if (mappings == null) { + synchronized (V3DaysSwap.class) { + if (mappings == null) { + mappings = new java.util.HashMap(); + } + } + } + return mappings; + } + + private V3DaysSwap(int value) { + intValue = value; + getMappings().put(value, this); + } + + public int getValue() { + return intValue; + } + + public static V3DaysSwap forValue(int value) { + return getMappings().get(value); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vAES.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vAES.java new file mode 100644 index 00000000000..44c7f034186 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vAES.java @@ -0,0 +1,689 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +import java.util.Arrays; + +class vAES { + private int m_nCipherRnd; + private int[] m_Ks = new int[64]; // KeySchedule + private int[] m_Ke = new int[64]; // KeyEncoded + private int[][] s_tabIT = new int[4][256]; + private int[][] s_tabFT = new int[4][256]; + private int[] InvSBox = new int[256]; + private int[] SBox = new int[256]; + private int[] s_tabIB = new int[256]; + + vAES() { + m_nCipherRnd = 0; + Arrays.fill(m_Ks, 0); + Arrays.fill(m_Ke, 0); + + // Arrays.fill(m_Ks, 0, 64); + // Array.Clear(m_Ke, 0, 64); + } + + private byte[] EncryptBlock(byte[] data) { + int[] ind = new int[4]; + long[] w = new long[4]; + ind[0] = m_Ks[0] ^ BitConverter.ToInt32(data, 0); + + ind[1] = m_Ks[1] ^ BitConverter.ToInt32(data, 4); + + ind[2] = m_Ks[2] ^ BitConverter.ToInt32(data, 8); + + ind[3] = m_Ks[3] ^ BitConverter.ToInt32(data, 12); + + w[0] = + s_tabFT[0][ind[0] & 0xFF] + ^ s_tabFT[1][(ind[1] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[2] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[3] >>> 24) & 0xFF] + ^ m_Ks[4]; + w[0] &= 0xFFFFFFFF; + w[1] = + s_tabFT[0][ind[1] & 0xFF] + ^ s_tabFT[1][(ind[2] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[3] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[0] >>> 24) & 0xFF] + ^ m_Ks[5]; + w[1] &= 0xFFFFFFFF; + w[2] = + s_tabFT[0][ind[2] & 0xFF] + ^ s_tabFT[1][(ind[3] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[0] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[1] >>> 24) & 0xFF] + ^ m_Ks[6]; + w[2] &= 0xFFFFFFFF; + w[3] = + s_tabFT[0][ind[3] & 0xFF] + ^ s_tabFT[1][(ind[0] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[1] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[2] >>> 24) & 0xFF] + ^ m_Ks[7]; + w[3] &= 0xFFFFFFFF; + for (int i = 0; i < w.length; i++) ind[i] = (int) w[i]; + // System.arraycopy(w, 0, ind, 0, w.length); + int i; + for (i = 0; i < m_nCipherRnd - 2; i += 2) { + w[0] = + s_tabFT[0][ind[0] & 0xFF] + ^ s_tabFT[1][(ind[1] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[2] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[3] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 8]; + + w[1] = + s_tabFT[0][ind[1] & 0xFF] + ^ s_tabFT[1][(ind[2] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[3] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[0] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 9]; + + w[2] = + s_tabFT[0][ind[2] & 0xFF] + ^ s_tabFT[1][(ind[3] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[0] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[1] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 10]; + + w[3] = + s_tabFT[0][ind[3] & 0xFF] + ^ s_tabFT[1][(ind[0] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[1] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[2] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 11]; + for (int j = 0; j < w.length; j++) ind[j] = (int) w[j]; + // System.arraycopy(w, 0, ind, 0, w.length); + w[0] = + s_tabFT[0][ind[0] & 0xFF] + ^ s_tabFT[1][(ind[1] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[2] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[3] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 12]; + + w[1] = + s_tabFT[0][ind[1] & 0xFF] + ^ s_tabFT[1][(ind[2] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[3] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[0] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 13]; + + w[2] = + s_tabFT[0][ind[2] & 0xFF] + ^ s_tabFT[1][(ind[3] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[0] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[1] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 14]; + + w[3] = + s_tabFT[0][ind[3] & 0xFF] + ^ s_tabFT[1][(ind[0] >>> 8) & 0xFF] + ^ s_tabFT[2][(ind[1] >>> 16) & 0xFF] + ^ s_tabFT[3][(ind[2] >>> 24) & 0xFF] + ^ m_Ks[i * 4 + 15]; + + for (int j = 0; j < w.length; j++) ind[j] = (int) w[j]; + // System.arraycopy(w, 0, ind, 0, w.length); + } + byte[] crypt = new byte[16]; + System.arraycopy( + BitConverter.GetBytes( + SBox[ind[0] & 0xFF] + ^ (SBox[(ind[1] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[2] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[3] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 8]), + 0, + crypt, + 0, + BitConverter.GetBytes( + SBox[ind[0] & 0xFF] + ^ (SBox[(ind[1] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[2] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[3] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 8]) + .length); + System.arraycopy( + BitConverter.GetBytes( + SBox[ind[1] & 0xFF] + ^ (SBox[(ind[2] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[3] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[0] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 9]), + 0, + crypt, + 4, + BitConverter.GetBytes( + SBox[ind[1] & 0xFF] + ^ (SBox[(ind[2] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[3] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[0] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 9]) + .length); + System.arraycopy( + BitConverter.GetBytes( + SBox[ind[2] & 0xFF] + ^ (SBox[(ind[3] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[0] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[1] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 10]), + 0, + crypt, + 8, + BitConverter.GetBytes( + SBox[ind[2] & 0xFF] + ^ (SBox[(ind[3] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[0] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[1] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 10]) + .length); + System.arraycopy( + BitConverter.GetBytes( + SBox[ind[3] & 0xFF] + ^ (SBox[(ind[0] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[1] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[2] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 11]), + 0, + crypt, + 12, + BitConverter.GetBytes( + SBox[ind[3] & 0xFF] + ^ (SBox[(ind[0] >>> 8) & 0xFF] << 8) + ^ (SBox[(ind[1] >>> 16) & 0xFF] << 16) + ^ (SBox[(ind[2] >>> 24) & 0xFF] << 24) + ^ m_Ks[i * 4 + 11]) + .length); + return crypt; + } + + private long upr(long x) { + return ((x << 8) | (x >>> (32 - 8))) & 0xFFFFFFFF; + } + + final void GenerateTables() { + short[] log = new short[256]; + short[] pow = new short[256]; + log[0] = 0; + short w = 1; + for (int i = 0; i < 256; i++) { + int v = w; + log[v] = (short) i; + pow[i] = w; + w ^= (short) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + // if(w>256) + // w-=256; + } + pow[255] = 0; + for (int i = 0; i < 256; i++) { + short v = pow[255 - log[i]]; + w = + (short) + ((((((((v >>> 1) ^ v) >> 1) ^ v) >> 1) ^ v) >> 4) + ^ (((((((v << 1) ^ v) << 1) ^ v) << 1) ^ v) << 1) + ^ v + ^ 0x63); + w &= 0xFF; + SBox[i] = w; + InvSBox[w] = i; + } + for (int i = 0; i < 256; i++) { + short v1 = (short) SBox[i]; + v1 &= 0xFF; + short v2 = (short) (v1 << 1); + v2 &= 0xFF; + if ((v1 & 0x80) != 0) { + v2 ^= 0x1B; + v2 &= 0xFF; + } + long wt = ((((long) v1 ^ v2) << 24) | (v1 << 16) | (v1 << 8) | v2); + wt &= 0xFFFFFFFF; + s_tabFT[0][i] = (int) wt; + wt = upr(wt); + s_tabFT[1][i] = (int) wt; + wt = upr(wt); + s_tabFT[2][i] = (int) wt; + wt = upr(wt); + s_tabFT[3][i] = (int) wt; + wt = 0; + short v = (short) InvSBox[i]; + v &= 0xFF; + if (v != 0) { + wt = + (((long) pow[(log[v] + 0x68) % 255] << 24) + ^ (pow[(log[v] + 0xEE) % 255] << 16) + ^ (pow[(log[v] + 0xC7) % 255] << 8) + ^ pow[(log[v] + 0xDF) % 255]); + wt &= 0xFFFFFFFF; + } + s_tabIT[0][i] = (int) wt; + wt = upr(wt); + s_tabIT[1][i] = (int) wt; + wt = upr(wt); + s_tabIT[2][i] = (int) wt; + wt = upr(wt); + s_tabIT[3][i] = (int) wt; + } + s_tabIT.toString(); + } + + private int bKs(int index) { + long ks = m_Ks[index / 4]; + switch (index % 4) { + case 0: + return (int) (ks & 0xFF); + case 1: + return (int) ((ks >>> 8) & 0xFF); + case 2: + return (int) ((ks >>> 16) & 0xFF); + case 3: + return (int) ((ks >>> 24) & 0xFF); + } + return 0; + } + + private void EncodeKey(byte[] key, int szKey) { + if (szKey > 256) { + return; + } + if (SBox[0] == 0) { + GenerateTables(); + } + for (int i = 0; i < szKey / 32; i++) { + m_Ks[i] = BitConverter.ToInt32(key, i * 4); + } + short v; + short w = 1; + int indKs; + if (szKey == 128) { + for (int i = 0; i < 2; i++) { + m_Ks[i * 20 + 4] = + (((((SBox[bKs(i * 80 + 12)] << 8) ^ SBox[bKs(i * 80 + 15)]) << 8) + ^ SBox[bKs(i * 80 + 14)]) + << 8) + ^ SBox[bKs(i * 80 + 13)] + ^ m_Ks[i * 20] + ^ w; + + m_Ks[i * 20 + 5] = m_Ks[i * 20 + 1] ^ m_Ks[i * 20 + 4]; + + m_Ks[i * 20 + 6] = m_Ks[i * 20 + 1] ^ m_Ks[i * 20 + 2] ^ m_Ks[i * 20 + 4]; + + m_Ks[i * 20 + 7] = m_Ks[i * 20 + 3] ^ m_Ks[i * 20 + 6]; + + v = w; + w = (byte) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + m_Ks[i * 20 + 8] = + (((((SBox[bKs(i * 80 + 28)] << 8) ^ SBox[bKs(i * 80 + 31)]) << 8) + ^ SBox[bKs(i * 80 + 30)]) + << 8) + ^ SBox[bKs(i * 80 + 29)] + ^ m_Ks[i * 20 + 4] + ^ w; + + m_Ks[i * 20 + 9] = m_Ks[i * 20 + 5] ^ m_Ks[i * 20 + 8]; + + m_Ks[i * 20 + 10] = m_Ks[i * 20 + 5] ^ m_Ks[i * 20 + 6] ^ m_Ks[i * 20 + 8]; + + m_Ks[i * 20 + 11] = m_Ks[i * 20 + 7] ^ m_Ks[i * 20 + 10]; + + v = w; + w = (byte) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + m_Ks[i * 20 + 12] = + (((((SBox[bKs(i * 80 + 44)] << 8) ^ SBox[bKs(i * 80 + 47)]) << 8) + ^ SBox[bKs(i * 80 + 46)]) + << 8) + ^ SBox[bKs(i * 80 + 45)] + ^ m_Ks[i * 20 + 8] + ^ w; + m_Ks[i * 20 + 13] = m_Ks[i * 20 + 9] ^ m_Ks[i * 20 + 12]; + m_Ks[i * 20 + 14] = m_Ks[i * 20 + 9] ^ m_Ks[i * 20 + 10] ^ m_Ks[i * 20 + 12]; + m_Ks[i * 20 + 15] = m_Ks[i * 20 + 11] ^ m_Ks[i * 20 + 14]; + v = w; + w = (byte) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + m_Ks[i * 20 + 16] = + (((((SBox[bKs(i * 80 + 60)] << 8) ^ SBox[bKs(i * 80 + 63)]) << 8) + ^ SBox[bKs(i * 80 + 62)]) + << 8) + ^ SBox[bKs(i * 80 + 61)] + ^ m_Ks[i * 20 + 12] + ^ w; + m_Ks[i * 20 + 17] = m_Ks[i * 20 + 13] ^ m_Ks[i * 20 + 16]; + m_Ks[i * 20 + 18] = m_Ks[i * 20 + 13] ^ m_Ks[i * 20 + 14] ^ m_Ks[i * 20 + 16]; + m_Ks[i * 20 + 19] = m_Ks[i * 20 + 15] ^ m_Ks[i * 20 + 18]; + v = w; + w = (byte) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + m_Ks[i * 20 + 20] = + (((((SBox[bKs(i * 80 + 76)] << 8) ^ SBox[bKs(i * 80 + 79)]) << 8) + ^ SBox[bKs(i * 80 + 78)]) + << 8) + ^ SBox[bKs(i * 80 + 77)] + ^ m_Ks[i * 20 + 16] + ^ w; + m_Ks[i * 20 + 21] = m_Ks[i * 20 + 17] ^ m_Ks[i * 20 + 20]; + m_Ks[i * 20 + 22] = m_Ks[i * 20 + 17] ^ m_Ks[i * 20 + 18] ^ m_Ks[i * 20 + 20]; + m_Ks[i * 20 + 23] = m_Ks[i * 20 + 19] ^ m_Ks[i * 20 + 22]; + v = w; + w = (byte) ((v << 1) ^ (((w & 0x80) != 0) ? 0x1B : 0)); + w &= 0xFF; + } + m_nCipherRnd = 10; + indKs = 80 * 2; + } else if (szKey == 196) { + for (int i = 0; i < 2; i++) { + m_Ks[i * 24 + 6] = + (((((SBox[bKs(i * 96 + 20)] << 8) ^ SBox[bKs(i * 96 + 23)]) << 8) + ^ SBox[bKs(i * 96 + 22)]) + << 8) + ^ SBox[bKs(i * 96 + 21)] + ^ m_Ks[i * 24] + ^ w; + m_Ks[i * 24 + 7] = m_Ks[i * 24 + 1] ^ m_Ks[i * 24 + 6]; + m_Ks[i * 24 + 8] = m_Ks[i * 24 + 1] ^ m_Ks[i * 24 + 2] ^ m_Ks[i * 24 + 6]; + m_Ks[i * 24 + 9] = m_Ks[i * 24 + 3] ^ m_Ks[i * 24 + 8]; + m_Ks[i * 24 + 10] = m_Ks[i * 24 + 3] ^ m_Ks[i * 24 + 4] ^ m_Ks[i * 24 + 8]; + m_Ks[i * 24 + 11] = m_Ks[i * 24 + 5] ^ m_Ks[i * 24 + 10]; + w <<= 1; + w &= 0xFF; + m_Ks[i * 24 + 12] = + (((((SBox[bKs(i * 96 + 44)] << 8) ^ SBox[bKs(i * 96 + 47)]) << 8) + ^ SBox[bKs(i * 96 + 46)]) + << 8) + ^ SBox[bKs(i * 96 + 45)] + ^ m_Ks[i * 24 + 6] + ^ w; + m_Ks[i * 24 + 13] = m_Ks[i * 24 + 7] ^ m_Ks[i * 24 + 12]; + m_Ks[i * 24 + 14] = m_Ks[i * 24 + 7] ^ m_Ks[i * 24 + 8] ^ m_Ks[i * 24 + 12]; + m_Ks[i * 24 + 15] = m_Ks[i * 24 + 9] ^ m_Ks[i * 24 + 14]; + m_Ks[i * 24 + 16] = m_Ks[i * 24 + 9] ^ m_Ks[i * 24 + 10] ^ m_Ks[i * 24 + 14]; + m_Ks[i * 24 + 17] = m_Ks[i * 24 + 11] ^ m_Ks[i * 24 + 16]; + w <<= 1; + w &= 0xFF; + m_Ks[i * 24 + 18] = + (((((SBox[bKs(i * 96 + 68)] << 8) ^ SBox[bKs(i * 96 + 71)]) << 8) + ^ SBox[bKs(i * 96 + 70)]) + << 8) + ^ SBox[bKs(i * 96 + 69)] + ^ m_Ks[i * 24 + 12] + ^ w; + m_Ks[i * 24 + 19] = m_Ks[i * 24 + 13] ^ m_Ks[i * 24 + 18]; + m_Ks[i * 24 + 20] = m_Ks[i * 24 + 13] ^ m_Ks[i * 24 + 14] ^ m_Ks[i * 24 + 18]; + m_Ks[i * 24 + 21] = m_Ks[i * 24 + 15] ^ m_Ks[i * 24 + 20]; + m_Ks[i * 24 + 22] = m_Ks[i * 24 + 15] ^ m_Ks[i * 24 + 16] ^ m_Ks[i * 24 + 20]; + m_Ks[i * 24 + 23] = m_Ks[i * 24 + 17] ^ m_Ks[i * 24 + 22]; + w <<= 1; + w &= 0xFF; + m_Ks[i * 24 + 24] = + (((((SBox[bKs(i * 96 + 92)] << 8) ^ SBox[bKs(i * 96 + 95)]) << 8) + ^ SBox[bKs(i * 96 + 94)]) + << 8) + ^ SBox[bKs(i * 96 + 93)] + ^ m_Ks[i * 24 + 18] + ^ w; + m_Ks[i * 24 + 25] = m_Ks[i * 24 + 19] ^ m_Ks[i * 24 + 18]; + m_Ks[i * 24 + 26] = m_Ks[i * 24 + 19] ^ m_Ks[i * 24 + 20] ^ m_Ks[i * 24 + 18]; + m_Ks[i * 24 + 27] = m_Ks[i * 24 + 21] ^ m_Ks[i * 24 + 26]; + m_Ks[i * 24 + 28] = m_Ks[i * 24 + 21] ^ m_Ks[i * 24 + 22] ^ m_Ks[i * 24 + 26]; + m_Ks[i * 24 + 29] = m_Ks[i * 24 + 23] ^ m_Ks[i * 24 + 28]; + w <<= 1; + w &= 0xFF; + } + m_nCipherRnd = 12; + indKs = 96 * 2; + } else if (szKey == 256) { + for (int i = 0; i < 7; i++) { + m_Ks[i * 8 + 8] = + (((((SBox[bKs(i * 32 + 28)] << 8) ^ SBox[bKs(i * 32 + 31)]) << 8) + ^ SBox[bKs(i * 32 + 30)]) + << 8) + ^ SBox[bKs(i * 32 + 29)] + ^ m_Ks[i * 8] + ^ w; + m_Ks[i * 8 + 9] = m_Ks[i * 8 + 1] ^ m_Ks[i * 8 + 8]; + m_Ks[i * 8 + 10] = m_Ks[i * 8 + 1] ^ m_Ks[i * 8 + 2] ^ m_Ks[i * 8 + 8]; + m_Ks[i * 8 + 11] = m_Ks[i * 8 + 3] ^ m_Ks[i * 8 + 10]; + w <<= 1; + w &= 0xFF; + m_Ks[i * 8 + 12] = + (((((SBox[bKs(i * 32 + 44)] << 8) ^ SBox[bKs(i * 32 + 47)]) << 8) + ^ SBox[bKs(i * 32 + 46)]) + << 8) + ^ SBox[bKs(i * 32 + 45)] + ^ m_Ks[i * 8 + 4]; + m_Ks[i * 8 + 13] = m_Ks[i * 8 + 5] ^ m_Ks[i * 8 + 12]; + m_Ks[i * 8 + 14] = m_Ks[i * 8 + 5] ^ m_Ks[i * 8 + 6] ^ m_Ks[i * 8 + 12]; + m_Ks[i * 8 + 15] = m_Ks[i * 8 + 7] ^ m_Ks[i * 8 + 14]; + } + m_nCipherRnd = 14; + indKs = 32 * 7; + } else { + m_nCipherRnd = 0; + return; + } + m_Ke[0] = m_Ks[indKs / 4]; + m_Ke[1] = m_Ks[indKs / 4 + 1]; + m_Ke[2] = m_Ks[indKs / 4 + 2]; + m_Ke[3] = m_Ks[indKs / 4 + 3]; + int ind = 0; + for (int i = (m_nCipherRnd - 1) * 4; i > 0; i--, ind++) { + indKs += (((i & 3) != 0) ? 1 : -7) * 4; + long l = + (long) s_tabIT[3][SBox[bKs(indKs + 15)]] + ^ s_tabIT[2][SBox[bKs(indKs + 14)]] + ^ s_tabIT[1][SBox[bKs(indKs + 13)]] + ^ s_tabIT[0][SBox[bKs(indKs + 12)]]; + l &= 0xFFFFFFFF; + m_Ke[ind + 4] = (int) l; + } + m_Ke[ind + 4] = m_Ks[indKs / 4 - 4]; + m_Ke[ind + 5] = m_Ks[indKs / 4 - 3]; + m_Ke[ind + 6] = m_Ks[indKs / 4 - 2]; + m_Ke[ind + 7] = m_Ks[indKs / 4 - 1]; + } + + final byte[] EncryptData(byte[] data, byte[] key) { + EncodeKey(key, key.length * 8); + byte[] crypt = new byte[(data.length + 15) & ~15]; + byte[] block = new byte[16]; + int ib = 0; + for (int i = 0; i < data.length / 16; i++, ib += 16) { + for (int k = 0; k < 16; k++) { + short b = block[k]; + b &= 0xFF; + b ^= data[ib + k]; + b &= 0xFF; + block[k] = (byte) b; + } + block = EncryptBlock(block); + System.arraycopy(block, 0, crypt, ib, 16); + } + if ((data.length & 0xF) != 0) { + for (int i = 0; i < (data.length & 0xF); i++) { + short b = block[i]; + b &= 0xFF; + b ^= data[ib + i]; + b &= 0xFF; + block[i] = (byte) b; + } + block = EncryptBlock(block); + System.arraycopy(block, 0, crypt, ib, 16); + } + return crypt; + } + + byte[] DecryptData(byte[] data, byte[] key) { + EncodeKey(key, key.length * 8); + byte[] buf = new byte[data.length]; + byte[] block0 = new byte[16]; + byte[] block1 = new byte[16]; + byte[] block2 = new byte[16]; + byte[] block3 = new byte[16]; + byte[] res = new byte[16]; + int ib = 0; + for (int i = data.length / 16; i > 0; i--, ib += 16) { + if ((i & 1) == 0) { + System.arraycopy(data, ib, block0, 0, 16); + System.arraycopy(data, ib, block2, 0, 16); + block2 = DecryptBlock(block2); + for (int k = 0; k < 16; k++) res[k] = (byte) (block1[k] ^ block2[k]); + } else { + System.arraycopy(data, ib, block1, 0, 16); + System.arraycopy(data, ib, block3, 0, 16); + block3 = DecryptBlock(block3); + for (int k = 0; k < 16; k++) res[k] = (byte) (block0[k] ^ block3[k]); + } + System.arraycopy(res, 0, buf, ib, 16); + } + return buf; + } + + private byte[] DecryptBlock(byte[] data) { + long[] ind = new long[4]; + long[] w = new long[4]; + ind[0] = m_Ke[0] ^ BitConverter.ToUInt32(data, 0); + ind[1] = m_Ke[1] ^ BitConverter.ToUInt32(data, 4); + ind[2] = m_Ke[2] ^ BitConverter.ToUInt32(data, 8); + ind[3] = m_Ke[3] ^ BitConverter.ToUInt32(data, 12); + w[0] = + s_tabIT[0][(int) (ind[0] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[3] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[2] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[1] >> 24) & 0xFF)] + ^ m_Ke[4]; + w[0] &= 0xFFFFFFFF; + w[1] = + s_tabIT[0][(int) (ind[1] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[0] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[3] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[2] >> 24) & 0xFF)] + ^ m_Ke[5]; + w[1] &= 0xFFFFFFFF; + w[2] = + s_tabIT[0][(int) (ind[2] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[1] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[0] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[3] >> 24) & 0xFF)] + ^ m_Ke[6]; + w[2] &= 0xFFFFFFFF; + w[3] = + s_tabIT[0][(int) (ind[3] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[2] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[1] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[0] >> 24) & 0xFF)] + ^ m_Ke[7]; + w[3] &= 0xFFFFFFFF; + ind = Arrays.copyOf(w, w.length); + + int i; + for (i = 0; i < m_nCipherRnd - 2; i += 2) { + w[0] = + s_tabIT[0][(int) (ind[0] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[3] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[2] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[1] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 8]; + w[0] &= 0xFFFFFFFF; + w[1] = + s_tabIT[0][(int) (ind[1] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[0] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[3] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[2] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 9]; + w[1] &= 0xFFFFFFFF; + w[2] = + s_tabIT[0][(int) (ind[2] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[1] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[0] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[3] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 10]; + w[2] &= 0xFFFFFFFF; + w[3] = + s_tabIT[0][(int) (ind[3] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[2] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[1] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[0] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 11]; + w[3] &= 0xFFFFFFFF; + ind = Arrays.copyOf(w, w.length); + w[0] = + s_tabIT[0][(int) (ind[0] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[3] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[2] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[1] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 12]; + w[0] &= 0xFFFFFFFF; + w[1] = + s_tabIT[0][(int) (ind[1] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[0] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[3] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[2] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 13]; + w[1] &= 0xFFFFFFFF; + w[2] = + s_tabIT[0][(int) (ind[2] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[1] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[0] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[3] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 14]; + w[2] &= 0xFFFFFFFF; + w[3] = + s_tabIT[0][(int) (ind[3] & 0xFF)] + ^ s_tabIT[1][(int) ((ind[2] >> 8) & 0xFF)] + ^ s_tabIT[2][(int) ((ind[1] >> 16) & 0xFF)] + ^ s_tabIT[3][(int) ((ind[0] >> 24) & 0xFF)] + ^ m_Ke[i * 4 + 15]; + w[3] &= 0xFFFFFFFF; + ind = Arrays.copyOf(w, w.length); + } + byte[] crypt = new byte[16]; + System.arraycopy( + BitConverter.GetBytes( + InvSBox[(int) (ind[0] & 0xFF)] + ^ (InvSBox[(int) ((ind[3] >> 8) & 0xFF)] << 8) + ^ (InvSBox[(int) ((ind[2] >> 16) & 0xFF)] << 16) + ^ (InvSBox[(int) ((ind[1] >> 24) & 0xFF)] << 24) + ^ m_Ke[i * 4 + 8]), + 0, + crypt, + 0, + 4); + System.arraycopy( + BitConverter.GetBytes( + InvSBox[(int) (ind[1] & 0xFF)] + ^ (InvSBox[(int) ((ind[0] >> 8) & 0xFF)] << 8) + ^ (InvSBox[(int) ((ind[3] >> 16) & 0xFF)] << 16) + ^ (InvSBox[(int) ((ind[2] >> 24) & 0xFF)] << 24) + ^ m_Ke[i * 4 + 9]), + 0, + crypt, + 4, + 4); + System.arraycopy( + BitConverter.GetBytes( + InvSBox[(int) (ind[2] & 0xFF)] + ^ (InvSBox[(int) ((ind[1] >> 8) & 0xFF)] << 8) + ^ (InvSBox[(int) ((ind[0] >> 16) & 0xFF)] << 16) + ^ (InvSBox[(int) ((ind[3] >> 24) & 0xFF)] << 24) + ^ m_Ke[i * 4 + 10]), + 0, + crypt, + 8, + 4); + System.arraycopy( + BitConverter.GetBytes( + InvSBox[(int) (ind[3] & 0xFF)] + ^ (InvSBox[(int) ((ind[2] >> 8) & 0xFF)] << 8) + ^ (InvSBox[(int) ((ind[1] >> 16) & 0xFF)] << 16) + ^ (InvSBox[(int) ((ind[0] >> 24) & 0xFF)] << 24) + ^ m_Ke[i * 4 + 11]), + 0, + crypt, + 12, + 4); + return crypt; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vCRC32.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vCRC32.java new file mode 100644 index 00000000000..681cfb26f10 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vCRC32.java @@ -0,0 +1,315 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class vCRC32 { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: static uint[] CRCTable = new uint[] { 0x00000000, 0x77073096, 0xee0e612c, + // 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, + // 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, + // 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, + // 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + // 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, + // 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, + // 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, + // 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, + // 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + // 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, + // 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, + // 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, + // 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, + // 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + // 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, + // 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, + // 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, + // 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, + // 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + // 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, + // 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, + // 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, + // 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, + // 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + // 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, + // 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, + // 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, + // 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, + // 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + // 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, + // 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, + // 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; + private static int[] CRCTable = + new int[] { + 0x00000000, + 0x77073096, + 0xee0e612c, + 0x990951ba, + 0x076dc419, + 0x706af48f, + 0xe963a535, + 0x9e6495a3, + 0x0edb8832, + 0x79dcb8a4, + 0xe0d5e91e, + 0x97d2d988, + 0x09b64c2b, + 0x7eb17cbd, + 0xe7b82d07, + 0x90bf1d91, + 0x1db71064, + 0x6ab020f2, + 0xf3b97148, + 0x84be41de, + 0x1adad47d, + 0x6ddde4eb, + 0xf4d4b551, + 0x83d385c7, + 0x136c9856, + 0x646ba8c0, + 0xfd62f97a, + 0x8a65c9ec, + 0x14015c4f, + 0x63066cd9, + 0xfa0f3d63, + 0x8d080df5, + 0x3b6e20c8, + 0x4c69105e, + 0xd56041e4, + 0xa2677172, + 0x3c03e4d1, + 0x4b04d447, + 0xd20d85fd, + 0xa50ab56b, + 0x35b5a8fa, + 0x42b2986c, + 0xdbbbc9d6, + 0xacbcf940, + 0x32d86ce3, + 0x45df5c75, + 0xdcd60dcf, + 0xabd13d59, + 0x26d930ac, + 0x51de003a, + 0xc8d75180, + 0xbfd06116, + 0x21b4f4b5, + 0x56b3c423, + 0xcfba9599, + 0xb8bda50f, + 0x2802b89e, + 0x5f058808, + 0xc60cd9b2, + 0xb10be924, + 0x2f6f7c87, + 0x58684c11, + 0xc1611dab, + 0xb6662d3d, + 0x76dc4190, + 0x01db7106, + 0x98d220bc, + 0xefd5102a, + 0x71b18589, + 0x06b6b51f, + 0x9fbfe4a5, + 0xe8b8d433, + 0x7807c9a2, + 0x0f00f934, + 0x9609a88e, + 0xe10e9818, + 0x7f6a0dbb, + 0x086d3d2d, + 0x91646c97, + 0xe6635c01, + 0x6b6b51f4, + 0x1c6c6162, + 0x856530d8, + 0xf262004e, + 0x6c0695ed, + 0x1b01a57b, + 0x8208f4c1, + 0xf50fc457, + 0x65b0d9c6, + 0x12b7e950, + 0x8bbeb8ea, + 0xfcb9887c, + 0x62dd1ddf, + 0x15da2d49, + 0x8cd37cf3, + 0xfbd44c65, + 0x4db26158, + 0x3ab551ce, + 0xa3bc0074, + 0xd4bb30e2, + 0x4adfa541, + 0x3dd895d7, + 0xa4d1c46d, + 0xd3d6f4fb, + 0x4369e96a, + 0x346ed9fc, + 0xad678846, + 0xda60b8d0, + 0x44042d73, + 0x33031de5, + 0xaa0a4c5f, + 0xdd0d7cc9, + 0x5005713c, + 0x270241aa, + 0xbe0b1010, + 0xc90c2086, + 0x5768b525, + 0x206f85b3, + 0xb966d409, + 0xce61e49f, + 0x5edef90e, + 0x29d9c998, + 0xb0d09822, + 0xc7d7a8b4, + 0x59b33d17, + 0x2eb40d81, + 0xb7bd5c3b, + 0xc0ba6cad, + 0xedb88320, + 0x9abfb3b6, + 0x03b6e20c, + 0x74b1d29a, + 0xead54739, + 0x9dd277af, + 0x04db2615, + 0x73dc1683, + 0xe3630b12, + 0x94643b84, + 0x0d6d6a3e, + 0x7a6a5aa8, + 0xe40ecf0b, + 0x9309ff9d, + 0x0a00ae27, + 0x7d079eb1, + 0xf00f9344, + 0x8708a3d2, + 0x1e01f268, + 0x6906c2fe, + 0xf762575d, + 0x806567cb, + 0x196c3671, + 0x6e6b06e7, + 0xfed41b76, + 0x89d32be0, + 0x10da7a5a, + 0x67dd4acc, + 0xf9b9df6f, + 0x8ebeeff9, + 0x17b7be43, + 0x60b08ed5, + 0xd6d6a3e8, + 0xa1d1937e, + 0x38d8c2c4, + 0x4fdff252, + 0xd1bb67f1, + 0xa6bc5767, + 0x3fb506dd, + 0x48b2364b, + 0xd80d2bda, + 0xaf0a1b4c, + 0x36034af6, + 0x41047a60, + 0xdf60efc3, + 0xa867df55, + 0x316e8eef, + 0x4669be79, + 0xcb61b38c, + 0xbc66831a, + 0x256fd2a0, + 0x5268e236, + 0xcc0c7795, + 0xbb0b4703, + 0x220216b9, + 0x5505262f, + 0xc5ba3bbe, + 0xb2bd0b28, + 0x2bb45a92, + 0x5cb36a04, + 0xc2d7ffa7, + 0xb5d0cf31, + 0x2cd99e8b, + 0x5bdeae1d, + 0x9b64c2b0, + 0xec63f226, + 0x756aa39c, + 0x026d930a, + 0x9c0906a9, + 0xeb0e363f, + 0x72076785, + 0x05005713, + 0x95bf4a82, + 0xe2b87a14, + 0x7bb12bae, + 0x0cb61b38, + 0x92d28e9b, + 0xe5d5be0d, + 0x7cdcefb7, + 0x0bdbdf21, + 0x86d3d2d4, + 0xf1d4e242, + 0x68ddb3f8, + 0x1fda836e, + 0x81be16cd, + 0xf6b9265b, + 0x6fb077e1, + 0x18b74777, + 0x88085ae6, + 0xff0f6a70, + 0x66063bca, + 0x11010b5c, + 0x8f659eff, + 0xf862ae69, + 0x616bffd3, + 0x166ccf45, + 0xa00ae278, + 0xd70dd2ee, + 0x4e048354, + 0x3903b3c2, + 0xa7672661, + 0xd06016f7, + 0x4969474d, + 0x3e6e77db, + 0xaed16a4a, + 0xd9d65adc, + 0x40df0b66, + 0x37d83bf0, + 0xa9bcae53, + 0xdebb9ec5, + 0x47b2cf7f, + 0x30b5ffe9, + 0xbdbdf21c, + 0xcabac28a, + 0x53b39330, + 0x24b4a3a6, + 0xbad03605, + 0xcdd70693, + 0x54de5729, + 0x23d967bf, + 0xb3667a2e, + 0xc4614ab8, + 0x5d681b02, + 0x2a6f2b94, + 0xb40bbe37, + 0xc30c8ea1, + 0x5a05df1b, + 0x2d02ef8d + }; + + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static uint Calculate(byte[] data, uint crc) + public static int Calculate(byte[] data, int crc) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: uint CRCVal = ~crc; + int CRCVal = (int) ~(crc & 0xFFFFFFFFL); + for (int i = 0; i < data.length; i++) { + // C# TO JAVA CONVERTER WARNING: The right shift operator was not replaced by Java's logical + // right shift operator since the left operand was not confirmed to be of an unsigned type, + // but you should review whether the logical right shift operator (>>>) is more appropriate: + CRCVal = + (int) + (((CRCVal & 0xFFFFFFFFL) >> 8) + ^ (CRCTable[(CRCVal & 0xff) ^ (data[i] & 0xFF)] & 0xFFFFFFFFL)); + } + return (int) ~(CRCVal & 0xFFFFFFFFL); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vRSA.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vRSA.java new file mode 100644 index 00000000000..d7cced50c77 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vRSA.java @@ -0,0 +1,114 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class vRSA { + private long M, N, D, Y; + + // private long P, Q; + + vRSA(long p) { + // P = p ^ 0x151D8255; + // Q = p ^ 0x274ECC00; + M = 0x67789ED4559AF79L; + N = 0xCCCCCCCCCCCCCCCCL; + D = 0x1DE7FED38081L; + // E = 0x5D405B5; + Y = 0x53290744C4D541L; + } + + final long ComputePacketKey(byte[] data) { + return ExpMod64(PrepareKey(data) % N, N, M); + } + + final long ComputeFileKey(byte[] data) { + return ExpMod64(PrepareKey(data) % M, D, M); + } + + final boolean CheckKey(byte[] data) { + if (data.length < 8) { + return false; + } + int szData = data.length - 8; + byte[] buf = new byte[szData]; + System.arraycopy(data, 0, buf, 0, szData); + long dataKey = PrepareKey(buf); + long origKey = BitConverter.ToInt64(data, szData); + return ExpMod64(origKey, Y, M) == (dataKey % M); + } + + private long PrepareKey(byte[] data) { + if (data.length < 1) { + return 0; + } + long h = 0; + long pm = 0x123456789L; + for (int i = 0; i < data.length / 8; i++) { + long w = BitConverter.ToInt64(data, i * 8); + h ^= w; + int lw = (int) w; + int hw = (int) (w >>> 32); + long sign = ((hw & 0x80000000) != 0) ? 0xFFFFFFFF00000000L : 0; + long t = ((long) (hw ^ lw) << 32) | (long) (lw ^ hw); + t += sign; + t += (((long) lw << 32) | hw) | sign; + t += pm; + t += w; + pm ^= t; + } + int rem = data.length & 7; + if (rem != 0) { + long ls = 0; + long rs = 0; + long mdc = 0x11F71FB04CBL; + int last = 0; + if (rem >= 2) { + int it = (rem - 2) / 2 + 1; + last = it * 2; + int off = data.length - rem; + int sh = 0; + for (int i = 0; i < it; i++, sh += 16) { + ls += (long) ((byte) data[i * 2 + off]) << sh; + rs += (long) ((byte) data[i * 2 + 1 + off]) << (sh + 8); + } + } + if (last < rem) { + mdc += (long) ((byte) data[data.length - 1]) << (last << 8); + } + long w = (ls + rs + mdc); + h ^= w; + int lw = (int) w; + int hw = (int) (w >>> 32); + long sign = ((hw & 0x80000000) != 0) ? 0xFFFFFFFF00000000L : 0; + long t = ((long) (hw ^ lw) << 32) | (long) (lw ^ hw); + t += sign; + t += (((long) lw << 32) | hw) | sign; + t += pm; + t += w; + pm ^= t; + } + return (((long) data.length * 0x100000001L) ^ pm ^ h) & 0xFFFFFFFFFFFL; + } + + private long ExpMod64(long rem, long n, long m) { + long key = 1; + long prv = rem; + for (int i = 0; i < 64; i++) { + if (((n >>> i) & 1) != 0) { + key = MulMod64(key, prv, m); + } + prv = MulMod64(prv, prv, m); + } + return key; + } + + private long MulMod64(long k, long n, long m) { + long key = 0; + long prv = k; + for (int i = 0; i < 64; i++) { + if (((n >>> i) & 1) != 0) { + key = (key + prv) % m; + } + prv = prv * 2 % m; + } + return key; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vSHA1.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vSHA1.java new file mode 100644 index 00000000000..095e8558608 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vSHA1.java @@ -0,0 +1,214 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +class vSHA1 { + private int[] Regs = new int[5]; + private int nBitCount = 0; + private int dwData = 0; + private byte[] dwBlock = new byte[64]; + private int dwCount = 0; + private int dbCount = 0; + + vSHA1() { + nBitCount = 0; + dwData = 0; + dwCount = 0; + dbCount = 0; + Regs[0] = 0x67452301; + Regs[1] = 0xEFCDAB89; + Regs[2] = 0x98BADCFE; + Regs[3] = 0x10325476; + Regs[4] = 0xC3D2E1F0; + // Array.Clear(dwBlock, 0, 16); + for (int i = 0; i < 16; i++) dwBlock[i] = 0; + } + + final void HashData(byte[] data) { + byte[] tmp = new byte[0]; + for (int i = 0; i < data.length; i++) { + dwData = (dwData << 8) + ((int) data[i] & 0xFF); + nBitCount += 8; + if (++dbCount >= 4) { + dbCount = 0; + System.arraycopy( + BitConverter.GetBytes(dwData), + 0, + dwBlock, + dwCount * 4, + BitConverter.GetBytes(dwData).length); + if (++dwCount >= 16) { + dwCount = 0; + Transform(dwBlock); + } + dwData = 0; + } + tmp = dwBlock; + } + tmp.toString(); + } + + final byte[] FinalizeHash() { + int bitCnt = nBitCount; + dwData = (dwData << 8) + 0x80; + while (true) { + nBitCount += 8; + if (++dbCount >= 4) { + dbCount = 0; + System.arraycopy( + BitConverter.GetBytes(dwData), + 0, + dwBlock, + dwCount * 4, + BitConverter.GetBytes(dwData).length); + if (++dwCount >= 16) { + dwCount = 0; + Transform(dwBlock); + } + dwData = 0; + } + if ((dbCount == 0) && (dwCount == 14)) { + break; + } + dwData <<= 8; + } + System.arraycopy( + BitConverter.GetBytes(0), 0, dwBlock, dwCount * 4, BitConverter.GetBytes(0).length); + if (++dwCount >= 16) { + dwCount = 0; + Transform(dwBlock); + } + System.arraycopy( + BitConverter.GetBytes(bitCnt), + 0, + dwBlock, + dwCount * 4, + BitConverter.GetBytes(bitCnt).length); + if (++dwCount >= 16) { + dwCount = 0; + Transform(dwBlock); + } + return new byte[] { + (byte) (Regs[0] >>> 24), + (byte) (Regs[0] >>> 16), + (byte) (Regs[0] >>> 8), + (byte) (Regs[0] >>> 0), + (byte) (Regs[1] >>> 24), + (byte) (Regs[1] >>> 16), + (byte) (Regs[1] >>> 8), + (byte) (Regs[1] >>> 0), + (byte) (Regs[2] >>> 24), + (byte) (Regs[2] >>> 16), + (byte) (Regs[2] >>> 8), + (byte) (Regs[2] >>> 0), + (byte) (Regs[3] >>> 24), + (byte) (Regs[3] >>> 16), + (byte) (Regs[3] >>> 8), + (byte) (Regs[3] >>> 0), + (byte) (Regs[4] >>> 24), + (byte) (Regs[4] >>> 16), + (byte) (Regs[4] >>> 8), + (byte) (Regs[4] >>> 0) + }; + } + + final byte[] ComputeHash(byte[] data) { + int len = data.length; + int left = 0; + if (len >= 64) { + byte[] block = new byte[64]; + for (int i = 0; i < len / 64; i++) { + for (int k = 0; k < 64; k++) { + block[k] = data[i * 64 + k]; + } + Transform(block); + left += 64; + } + } + int rem = len % 64; + if (rem > 0) { + byte[] block = new byte[64]; + for (int k = 0; k < rem; k++) { + block[k] = data[left + k]; + } + Transform(block); + } + return new byte[] { + (byte) (Regs[0] >>> 0), + (byte) (Regs[0] >>> 8), + (byte) (Regs[0] >>> 16), + (byte) (Regs[0] >>> 24), + (byte) (Regs[1] >>> 0), + (byte) (Regs[1] >>> 8), + (byte) (Regs[1] >>> 16), + (byte) (Regs[1] >>> 24), + (byte) (Regs[2] >>> 0), + (byte) (Regs[2] >>> 8), + (byte) (Regs[2] >>> 16), + (byte) (Regs[2] >>> 24), + (byte) (Regs[3] >>> 0), + (byte) (Regs[3] >>> 8), + (byte) (Regs[3] >>> 16), + (byte) (Regs[3] >>> 24), + (byte) (Regs[4] >>> 0), + (byte) (Regs[4] >>> 8), + (byte) (Regs[4] >>> 16), + (byte) (Regs[4] >>> 24) + }; + } + + private int SHA1Shift(int bits, int word) { + return ((word << bits) | (word >>> (32 - bits))); + } + + private void Transform(byte[] data) { + int temp; + int[] W = new int[80]; + for (int i = 0; i < 16; i++) { + W[i] = BitConverter.ToInt32(data, i * 4); + } + for (int i = 16; i < 80; i++) { + W[i] = SHA1Shift(1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]); + } + int A = Regs[0]; + int B = Regs[1]; + int C = Regs[2]; + int D = Regs[3]; + int E = Regs[4]; + for (int i = 0; i < 20; i++) { + temp = SHA1Shift(5, A) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999; + E = D; + D = C; + C = SHA1Shift(30, B); + B = A; + A = temp; + } + for (int i = 20; i < 40; i++) { + temp = SHA1Shift(5, A) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1; + E = D; + D = C; + C = SHA1Shift(30, B); + B = A; + A = temp; + } + for (int i = 40; i < 60; i++) { + temp = SHA1Shift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC; + E = D; + D = C; + C = SHA1Shift(30, B); + B = A; + A = temp; + } + for (int i = 60; i < 80; i++) { + temp = SHA1Shift(5, A) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6; + E = D; + D = C; + C = SHA1Shift(30, B); + B = A; + A = temp; + } + Regs[0] += A; + Regs[1] += B; + Regs[2] += C; + Regs[3] += D; + Regs[4] += E; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vUTF.java b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vUTF.java new file mode 100644 index 00000000000..514e97006c7 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/mtapi/mt5/vUTF.java @@ -0,0 +1,33 @@ +package io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5; + +/** Convert string to byte array. */ +class vUTF { + /** Convert UNICODE string to byte array. */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static byte[] toByte(string str) + public static byte[] toByte(String str) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: byte[] res = new byte[str.Length]; + byte[] res = new byte[str.length()]; + for (int i = 0; i < str.length(); i++) { + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: res[i] = (byte)(str[i] & 0xFF); + res[i] = (byte) (str.charAt(i) & 0xFF); + } + return res; + } + + /** Convert byte array to UNICODE string. */ + // C# TO JAVA CONVERTER WARNING: Unsigned integer types have no direct equivalent in Java: + // ORIGINAL LINE: public static string toString(byte[] bytes, int offset) + public static String toString(byte[] bytes, int offset) { + String str = ""; + for (int i = offset; i < bytes.length; i++) { + if ((bytes[i] & 0xFF) == 0) { + break; + } + str += (char) (bytes[i] & 0xFF); + } + return str; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/GlobalDataFeed.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/GlobalDataFeed.java new file mode 100644 index 00000000000..ae92bc41b79 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/GlobalDataFeed.java @@ -0,0 +1,61 @@ +package io.quantum.trading.brokers.indian; + +import io.quantum.trading.brokers.*; +import io.quantum.trading.entities.BrokerConfigDetails.*; +import io.quantum.trading.entities.NseScriptMetadata; + +public class GlobalDataFeed extends NseBroker { + + private final GlobalDataFeedConfigDetails globalDataFeedConfigDetails; + + public GlobalDataFeed(GlobalDataFeedConfigDetails globalDataFeedConfigDetails) { + this.globalDataFeedConfigDetails = globalDataFeedConfigDetails; + // TODO establish connect + } + + @Override + public double getBalance() throws BrokerException { + return 0; + } + + @Override + public boolean testConnection() throws BrokerException { + return false; + } + + @Override + public void disConnect() throws BrokerException {} + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + return null; + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + return null; + } + + @Override + public void cancelOrder(String orderId) throws BrokerException {} + + @Override + public void closeOrder(String orderId) throws BrokerException {} + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + return null; + } + + @Override + public String getBrokerToken(NseScriptMetadata nseScriptMetadata) { + return null; + } + + @Override + public String getBrokerScriptName(NseScriptMetadata nseScriptMetadata) { + return null; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/IndianDemoBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/IndianDemoBroker.java new file mode 100644 index 00000000000..3ad7ec7227a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/IndianDemoBroker.java @@ -0,0 +1,239 @@ +package io.quantum.trading.brokers.indian; + +import io.quantum.trading.brokers.*; +import io.quantum.trading.entities.*; +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.redis.RedisCrudService; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; + +@Slf4j +public class IndianDemoBroker extends NseBroker implements AllowBacktesting { + + private final RedisCrudService redisCrudService; + private final Map brokerOrderDetails; + private final Map> ordersByScript; + private final DemoBrokerRepository demoBrokerRepository; + private final BrokerConfig brokerConfig; + private final boolean backtestMode; + private final boolean utMode; + + public IndianDemoBroker( + BrokerConfig brokerConfig, + RedisCrudService redisCrudService, + DemoBrokerRepository demoBrokerRepository) { + this.redisCrudService = redisCrudService; + this.demoBrokerRepository = demoBrokerRepository; + this.brokerConfig = brokerConfig; + backtestMode = + brokerConfig.getBrokerConfigDetails() instanceof BrokerConfigDetails.ZerodhaBackTestBroker; + this.brokerOrderDetails = new ConcurrentHashMap<>(); + ordersByScript = new ConcurrentHashMap<>(); + utMode = false; + } + + public IndianDemoBroker( + BrokerConfig brokerConfig, + RedisCrudService redisCrudService, + DemoBrokerRepository demoBrokerRepository, + boolean utMode) { + this.redisCrudService = redisCrudService; + this.demoBrokerRepository = demoBrokerRepository; + this.brokerConfig = brokerConfig; + backtestMode = + brokerConfig.getBrokerConfigDetails() instanceof BrokerConfigDetails.ZerodhaBackTestBroker; + this.brokerOrderDetails = new ConcurrentHashMap<>(); + ordersByScript = new ConcurrentHashMap<>(); + this.utMode = utMode; + } + + private Candle getCurrentCandle(String exchangeScript) { + if (backtestMode) { + return redisCrudService.getKeyValue(exchangeScript, Candle.class).orElseThrow(); + } else { + var liveTick = redisCrudService.getKeyValue(exchangeScript, LiveTick.class).orElseThrow(); + return liveTick.toCandle(); + } + } + + @Override + public double getBalance() throws BrokerException { + return 0; + } + + @Override + public boolean testConnection() throws BrokerException { + return false; + } + + @Override + public void disConnect() throws BrokerException {} + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + var currentCandle = getCurrentCandle(brokerPlaceOrderRequest.getExchangeScript()); + var orderTime = + backtestMode + ? new DateTime(currentCandle.getEndTs()).plusMillis(1).getMillis() + : System.currentTimeMillis(); + var brokerOrderDetail = + switch (brokerPlaceOrderRequest.getType()) { + case BrokerOrderType.REGULAR_BUY_NSE -> { + var orderRequest = (BrokerPlaceOrderRequest.RegularBuyNse) brokerPlaceOrderRequest; + yield BrokerOrderDetail.RegularBuyNse.builder() + .price(orderRequest.getPrice()) + .productType(orderRequest.getProductType()) + .type(orderRequest.getType()) + .exchangeScript(orderRequest.getExchangeScript()) + .quantity(orderRequest.getQuantity()) + .filledQuantity(orderRequest.getQuantity()) + .stopLoss(0) + .takeProfit(0) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.COMPLETE) + .tag(orderRequest.getTag()) + .openTs(orderTime) + .closeTs(orderTime) + .orderUpdateTs(0) + .avgOpenPrice(currentCandle.getClose()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .build(); + } + + case BrokerOrderType.REGULAR_SELL_NSE -> { + var orderRequest = (BrokerPlaceOrderRequest.RegularSellNse) brokerPlaceOrderRequest; + yield BrokerOrderDetail.RegularSellNse.builder() + .price(orderRequest.getPrice()) + .productType(orderRequest.getProductType()) + .type(orderRequest.getType()) + .exchangeScript(orderRequest.getExchangeScript()) + .quantity(orderRequest.getQuantity()) + .filledQuantity(orderRequest.getQuantity()) + .stopLoss(0) + .takeProfit(0) + .orderId(UUID.randomUUID().toString()) + .orderStatus(BrokerOrderStatus.COMPLETE) + .tag(orderRequest.getTag()) + .openTs(orderTime) + .closeTs(orderTime) + .orderUpdateTs(0) + .avgOpenPrice(currentCandle.getClose()) + .avgClosePrice(0.0) + .profitAndLoss(BigDecimal.valueOf(0.0)) + .build(); + } + default -> + throw new IllegalArgumentException( + brokerPlaceOrderRequest.getType() + " orderType not available"); + }; + + var orderId = brokerOrderDetail.getOrderId(); + var exchangeScript = brokerPlaceOrderRequest.getExchangeScript(); + brokerOrderDetails.put(orderId, brokerOrderDetail); + var demoBrokerOrderDetail = + new DemoBrokerOrderDetails( + new DemoBrokerOrderDetails.DemoBrokerOrderId(orderId, brokerConfig.getId()), + brokerOrderDetail); + if (!backtestMode && !utMode) { + demoBrokerRepository.save(demoBrokerOrderDetail); + } + if (!ordersByScript.containsKey(exchangeScript)) { + ordersByScript.put(exchangeScript, new ConcurrentSkipListSet<>()); + } + ordersByScript.get(exchangeScript).add(orderId); + + return brokerOrderDetail; + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + var orderId = brokerModifyOrderRequest.getOrderId(); + DemoBrokerOrderDetails dbOrderDetail = null; + if (!backtestMode && !utMode) { + dbOrderDetail = demoBrokerRepository.findById_OrderId(orderId).get(); + } + var orderDetail = + backtestMode ? brokerOrderDetails.get(orderId) : dbOrderDetail.getBrokerOrderDetail(); + if (orderDetail.getOrderStatus() != BrokerOrderStatus.COMPLETE + || orderDetail.getOrderStatus() != BrokerOrderStatus.CANCELLED + || orderDetail.getOrderStatus() != BrokerOrderStatus.REJECTED) { + brokerOrderDetails.put(orderId, orderDetail); + if (!backtestMode && !utMode) { + demoBrokerRepository.save(dbOrderDetail); + } + return orderDetail; + } else { + throw new BrokerException(ApiError.BROKER_MODIFY_ORDER, orderDetail.getOrderStatus()); + } + } + + @Override + public void cancelOrder(String orderId) throws BrokerException { + DemoBrokerOrderDetails dbOrderDetail = null; + if (!backtestMode && !utMode) { + dbOrderDetail = demoBrokerRepository.findById_OrderId(orderId).get(); + } + var orderDetail = + backtestMode ? brokerOrderDetails.get(orderId) : dbOrderDetail.getBrokerOrderDetail(); + if (orderDetail.getOrderStatus() == BrokerOrderStatus.PLACED) { + orderDetail.setOrderStatus(BrokerOrderStatus.CANCELLED); + if (!backtestMode && !utMode) { + demoBrokerRepository.save(dbOrderDetail); + } + brokerOrderDetails.remove(orderId); + ordersByScript.get(orderDetail.getExchangeScript()).remove(orderId); + } else { + throw new BrokerException( + ApiError.ORDER_CANNOT_CANCEL, orderId, orderDetail.getOrderStatus()); + } + } + + @Override + public void closeOrder(String orderId) throws BrokerException {} + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + if (brokerOrderDetails.containsKey(orderId)) { + return brokerOrderDetails.get(orderId); + } else { + var brokerOrder = demoBrokerRepository.findById_OrderId(orderId); + if (!backtestMode && !utMode && brokerOrder.isPresent()) { + brokerOrderDetails.put(orderId, brokerOrder.get().getBrokerOrderDetail()); + return brokerOrder.get().getBrokerOrderDetail(); + } else { + throw new BrokerException(ApiError.ORDER_NOT_FOUND, orderId); + } + } + } + + @Override + public String getBrokerToken(NseScriptMetadata nseScriptMetadata) { + return null; + } + + @Override + public String getBrokerScriptName(NseScriptMetadata nseScriptMetadata) { + return null; + } + + @Override + public void synchronizeOrders() {} + + @Override + public boolean isInBacktestMode() { + return backtestMode; + } + + @Override + public boolean isUtMode() { + return this.utMode; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/NseBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/NseBroker.java new file mode 100644 index 00000000000..03988604976 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/NseBroker.java @@ -0,0 +1,131 @@ +package io.quantum.trading.brokers.indian; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quantum.trading.brokers.Broker; +import io.quantum.trading.brokers.BrokerException; +import io.quantum.trading.brokers.Currency; +import io.quantum.trading.brokers.Exchange; +import io.quantum.trading.entities.NseScriptMetadata; +import io.quantum.trading.util.OmsUtil; +import java.io.IOException; +import java.net.*; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public abstract class NseBroker extends Broker { + + public static final String MIS = "MIS"; + public static final String CNC = "CNC"; + public static final String NRML = "NRML"; + + private Map freezeLimits = new HashMap<>(); + + @Getter protected Map scriptMetadataMap; + protected Map> scriptsTokenSymbolMap; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @Override + public Currency getCurrency() throws BrokerException { + return Currency.INR; + } + + public final int getLotSize(String exchangeScript) { + var script = OmsUtil.getScript(exchangeScript); + var exchange = OmsUtil.getExchange(exchangeScript); + return scriptMetadataMap + .get(new NseScriptMetadata.NseMetadataId(script, Exchange.valueOf(exchange))) + .getLotSize(); + } + + public final int getFreezeLimitForScript(String exchangeScript) { + var script = OmsUtil.getScript(exchangeScript); + var exchange = OmsUtil.getExchange(exchangeScript); + return scriptMetadataMap + .get(new NseScriptMetadata.NseMetadataId(script, Exchange.valueOf(exchange))) + .getFreezeLimit(); + } + + public abstract String getBrokerToken(NseScriptMetadata nseScriptMetadata); + + public abstract String getBrokerScriptName(NseScriptMetadata nseScriptMetadata); + + public final void updateScriptMetadata( + Map scriptMetadataMap, + Map> scriptTokenSymbolMap) { + this.scriptMetadataMap = scriptMetadataMap; + this.scriptsTokenSymbolMap = scriptTokenSymbolMap; + } + + // public Instrument getOptionsChildInstrumentWithAtm( + // double atm, double roundOff, int factor, TradeType optionsType, String instrumentName) { + // var type = optionsType == TradeType.OPTIONS_CE ? "CE" : "PE"; + // String regex; + // int strikePrice; + // if (atm % roundOff <= roundOff / 2) { + // strikePrice = (((int) (Math.floor(atm / roundOff)) * + // Double.valueOf(roundOff).intValue())); + // } else { + // strikePrice = (((int) (Math.ceil(atm / roundOff)) * Double.valueOf(roundOff).intValue())); + // } + // + // strikePrice = strikePrice + factor; + // regex = strikePrice + type; + // + // // var instruments = getInstruments(); + // // var filtered = instruments.stream() + // // .filter(instrument -> instrument.name != null && + // // instrument.name.equals(instrumentName)) + // // .filter(instrument -> instrument.tradingsymbol.endsWith(regex)) + // // .sorted(Comparator.comparing(o -> o.expiry)) + // // .toList(); + // // return filtered.get(0); + // + // return null; + // } + + protected final int getFreezeLimit(String name) { + if (freezeLimits.isEmpty()) { + var fyersUrls = + List.of( + "https://public.fyers.in/sym_details/NSE_CM_sym_master.json", + "https://public.fyers.in/sym_details/BSE_FO_sym_master.json", + "https://public.fyers.in/sym_details/NSE_FO_sym_master.json"); + fyersUrls.forEach( + (fyersUrl) -> { + try (var client = HttpClient.newHttpClient()) { + var req = + HttpRequest.newBuilder() + .uri(new URI(fyersUrl)) + .timeout(Duration.of(10, SECONDS.toChronoUnit())) + .GET() + .build(); + var res = client.send(req, HttpResponse.BodyHandlers.ofString()); + Map> scriptMap = + OBJECT_MAPPER.readValue(res.body(), Map.class); + scriptMap.forEach( + (symbol, details) -> { + var freezeLimit = String.valueOf(details.get("qtyFreeze")).trim(); + if (!freezeLimit.isEmpty()) { + freezeLimits.put( + details.get("underSym").toString(), Integer.parseInt(freezeLimit)); + } + }); + + } catch (URISyntaxException | IOException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + } + return freezeLimits.getOrDefault(name, 0); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/ZerodhaBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/ZerodhaBroker.java new file mode 100644 index 00000000000..d4d61f59e5f --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/ZerodhaBroker.java @@ -0,0 +1,1099 @@ +package io.quantum.trading.brokers.indian; + +import static com.zerodhatech.kiteconnect.utils.Constants.*; +import static java.lang.Double.parseDouble; + +import com.neovisionaries.ws.client.OpeningHandshakeException; +import com.zerodhatech.kiteconnect.KiteConnect; +import com.zerodhatech.kiteconnect.kitehttp.exceptions.KiteException; +import com.zerodhatech.kiteconnect.kitehttp.exceptions.PermissionException; +import com.zerodhatech.kiteconnect.kitehttp.exceptions.TokenException; +import com.zerodhatech.models.*; +import com.zerodhatech.ticker.KiteTicker; +import com.zerodhatech.ticker.OnError; +import com.zerodhatech.ticker.OnOrderUpdate; +import io.quantum.trading.brokers.*; +import io.quantum.trading.entities.BrokerConfig; +import io.quantum.trading.entities.BrokerConfigDetails.ZerodhaBrokerConfigDetails; +import io.quantum.trading.entities.NseScriptMetadata; +import io.quantum.trading.entities.NseScriptMetadata.Segment; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.exception.ApiError; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.provider.DataProvidable; +import io.quantum.trading.provider.HistoricalDataProvidable; +import io.quantum.trading.provider.NseMetadataProvidable; +import io.quantum.trading.util.OmsUtil; +import io.quantum.trading.util.RetryUtils; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.math.BigDecimal; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; + +/*When websocket is disconnected it will reconnect automatically & subscribe to the scripts + *, a method will fetch balance for period of every minute to check if the connection is alive + *& reconnect on need basis*/ +@Slf4j +public class ZerodhaBroker extends NseBroker + implements NseMetadataProvidable, DataProvidable, HistoricalDataProvidable { + private static final String TAG_PREFIX = "QUANTUM_BOT"; + private static final String DUMMY_TRADE = "NO_TRADE"; + + // TODO Track this for random AOT issue https://github.com/oracle/graal/issues/6456 + public static final int TOO_MANY_REQUESTS_WAIT_TIME = + 1000; // getDefault().create().nextInt(100, 1000); + private KiteConnect kite; + private KiteTicker tickerProvider; + private final Supplier brokerConfigSupplier; + private BrokerConfig brokerConfig; + private OnOrderUpdate onOrderUpdateHandler; + private final ScheduledExecutorService reconnectTimer = + Executors.newSingleThreadScheduledExecutor(Thread.ofPlatform().factory()); + private List instruments; + private Map> exchangeTokenBrokerMetadataCache; + private Map subscribedScripts = new HashMap<>(); + private Function, Void> liveTickConsumer; + private final String orderTypeSuffix = "Nse"; + private boolean kiteConnected = false; + private final BlockingQueue> liveTicksHolder = new LinkedBlockingQueue<>(1000); + private Thread liveTicksAsyncPublisher; + private static final String LIVE_FEED_LOCK = "LIVE_FEED_LOCK"; + private static final String LIVE_FEED_SUBSCRIBE_LOCK = "LIVE_FEED_SUBSCRIBE_LOCK"; + + public ZerodhaBroker(Supplier brokerConfigSupplier) { + this.brokerConfigSupplier = brokerConfigSupplier; + init(); + initReconnect(); + } + + public static String generateAccessToken(String requestToken, String apiKey, String apiSecret) + throws IOException, KiteException { + var kiteConnect = new KiteConnect(apiKey); + return kiteConnect.generateSession(requestToken, apiSecret).accessToken; + } + + private void init() { + brokerConfig = brokerConfigSupplier.get(); + var brokerConfigDetails = (ZerodhaBrokerConfigDetails) brokerConfig.getBrokerConfigDetails(); + kite = new KiteConnect(brokerConfigDetails.getApiKey()); + kite.setAccessToken(brokerConfigDetails.getAccessToken()); + + var connectionInProgress = new AtomicBoolean(true); + // Set session expiry callback. + kite.setSessionExpiryHook( + () -> { + if (!connectionInProgress.get()) { + log.warn( + "[{} - {}] Zerodha session has expired. Please update the database and login again", + brokerConfig.getDisplayName(), + brokerConfigDetails.getUserId()); + kiteConnected = false; + init(); + } + }); + checkReconnectTask(false); + connectionInProgress.set(false); + } + + private void initReconnect() { + reconnectTimer.scheduleAtFixedRate( + () -> checkReconnectTask(true), 0, 60_000, TimeUnit.MILLISECONDS); + } + + private void checkReconnectTask(boolean autoRetry) { + if (kite == null) { + return; + } + + while (true) { + try { + getBalance(); + if (!kiteConnected) { + kiteConnected = true; + log.info( + "[{} - {}] Kite connected", + brokerConfig.getDisplayName(), + ((ZerodhaBrokerConfigDetails) brokerConfig.getBrokerConfigDetails()).getUserId()); + } + return; + } catch (Throwable t) { + if (t instanceof BrokerException brokerException) { + if ((brokerException.getCause() instanceof TokenException tokenException + && tokenException.code == 403) + || (brokerException.getCause() instanceof PermissionException permissionException + && permissionException.code == 403)) { + kiteConnected = false; + if (autoRetry) { + init(); + } + return; + } else { + log.error("", t); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + continue; + } + } + } + } + } + + private List getInstruments() { + if (instruments == null) { + try { + instruments = kite.getInstruments(); + } catch (KiteException | IOException e) { + throw new RuntimeException(e); + } + } + return instruments; + } + + @Override + public List getMetadata() { + return getInstruments().stream().map(this::getNseScriptMetadata).toList(); + } + + private NseScriptMetadata getNseScriptMetadata(Instrument instrument) { + var segment = getSegment(instrument); + return new NseScriptMetadata( + instrument.getName(), + instrument.getTradingsymbol(), + instrument.getExchange(), + segment, + Double.parseDouble(instrument.getStrike()), + instrument.getExpiry() == null + ? null + : new DateTime(instrument.getExpiry()).minusHours(5).minusMinutes(30).toDate(), + String.valueOf(instrument.getExchange_token()), + instrument.getLot_size(), + getFreezeLimit(instrument.getName())); + } + + private Segment getSegment(Instrument instrument) { + var segment = instrument.getSegment(); + var instrumentType = instrument.getInstrument_type(); + + return switch (segment) { + case "INDICES" -> Segment.INDICES; + case "BSE" -> Segment.BSE; + case "NSE" -> Segment.NSE; + case "NFO-FUT" -> Segment.NFO_FUT; + case "NFO-OPT" -> instrumentType.equals("CE") ? Segment.NFO_CE : Segment.NFO_PE; + case "BCD-OPT" -> instrumentType.equals("CE") ? Segment.BCD_CE : Segment.BCD_PE; + case "BCD-FUT" -> Segment.BCD_FUT; + case "BFO-OPT" -> instrumentType.equals("CE") ? Segment.BFO_CE : Segment.BFO_PE; + case "BFO-FUT" -> Segment.BFO_FUT; + case "CDS-FUT" -> Segment.CDS_FUT; + case "CDS-OPT" -> instrumentType.equals("CE") ? Segment.CDS_CE : Segment.CDS_PE; + case "MCX-FUT" -> Segment.MCX_FUT; + case "MCX-OPT" -> instrumentType.equals("CE") ? Segment.MCX_CE : Segment.MCX_PE; + case "NCO" -> Segment.NCO; + case "NCO-FUT" -> Segment.NCO_FUT; + case "NCO-OPT" -> instrumentType.equals("CE") ? Segment.NCO_CE : Segment.NCO_PE; + default -> + throw new IllegalStateException( + String.format( + "Unexpected value: %s for script, %s", segment, instrument.getTradingsymbol())); + }; + } + + @Override + public String getBrokerToken(NseScriptMetadata nseScriptMetadata) { + loadBrokerMetadata(); + var segment = nseScriptMetadata.getSegment(); + var exchange = nseScriptMetadata.getId().getExchange(); + + var metadataMapperKey = nseScriptMetadata.getExchangeToken(); + if (segment.equals(Segment.INDICES)) metadataMapperKey = metadataMapperKey + "_" + segment; + else metadataMapperKey = metadataMapperKey + "_" + exchange; + + if (!exchangeTokenBrokerMetadataCache.containsKey(metadataMapperKey)) { + throw new IllegalArgumentException("Token not found for script, " + metadataMapperKey); + } + return exchangeTokenBrokerMetadataCache.get(metadataMapperKey).get("token"); + } + + private void loadBrokerMetadata() { + if (exchangeTokenBrokerMetadataCache == null || exchangeTokenBrokerMetadataCache.isEmpty()) { + exchangeTokenBrokerMetadataCache = new HashMap<>(); + getInstruments() + .forEach( + instrument -> { + Map brokerConfigsMap = new HashMap<>(); + brokerConfigsMap.put("token", String.valueOf(instrument.getInstrument_token())); + brokerConfigsMap.put("script_name", instrument.getTradingsymbol()); + if (instrument.segment.equals(Segment.INDICES.name())) + exchangeTokenBrokerMetadataCache.put( + instrument.getExchange_token() + "_" + Segment.INDICES, brokerConfigsMap); + else + exchangeTokenBrokerMetadataCache.put( + String.valueOf(instrument.getExchange_token()) + "_" + instrument.exchange, + brokerConfigsMap); + }); + } + } + + @Override + public String getBrokerScriptName(NseScriptMetadata nseScriptMetadata) { + loadBrokerMetadata(); + var segment = nseScriptMetadata.getSegment(); + var exchange = nseScriptMetadata.getId().getExchange(); + + var metadataMapperKey = nseScriptMetadata.getExchangeToken(); + if (segment.equals(Segment.INDICES)) metadataMapperKey = metadataMapperKey + "_" + segment; + else metadataMapperKey = metadataMapperKey + "_" + exchange; + if (!exchangeTokenBrokerMetadataCache.containsKey(metadataMapperKey)) { + throw new IllegalArgumentException("ScriptName not found for script, " + metadataMapperKey); + } + return exchangeTokenBrokerMetadataCache.get(metadataMapperKey).get("script_name"); + } + + @Override + public void startFeed(Function, Void> liveTickConsumer) { + this.liveTickConsumer = liveTickConsumer; + // Kite Ticker class uses e.printstacktrace() which writes to stderr. Yet we catch these + // exceptions properly. So, skipping the noises + System.setErr( + new PrintStream( + new OutputStream() { + public void write(int b) {} + })); + initLiveFeed(); + } + + @Override + public void disconnectFeed() { + if (tickerProvider != null) { + tickerProvider.setOnConnectedListener(() -> tickerProvider.disconnect()); + tickerProvider.setOnTickerArrivalListener(ticks -> {}); + tickerProvider.setOnDisconnectedListener( + () -> log.info("Websocket disconnected and won't re-connect automatically")); + tickerProvider.setOnErrorListener( + new OnError() { + @Override + public void onError(Exception exception) { + log.error("Exception. But already disconnected", exception); + } + + @Override + public void onError(KiteException kiteException) { + log.error("KiteException. But already disconnected", kiteException); + } + + @Override + public void onError(String error) { + log.error("Error - {}. But already disconnected", error); + } + }); + tickerProvider.disconnect(); + } + } + + private void initLiveFeed() { + synchronized (LIVE_FEED_LOCK) { + var connectionInProgress = new AtomicBoolean(false); + if (tickerProvider == null || !tickerProvider.isConnectionOpen()) { + // Websocket + tickerProvider = new KiteTicker(kite.getAccessToken(), kite.getApiKey()); + try { + tickerProvider.setMaximumRetries(Integer.MAX_VALUE); + tickerProvider.setMaximumRetryInterval(5); + tickerProvider.setTryReconnection(true); + if (onOrderUpdateHandler != null) { + tickerProvider.setOnOrderUpdateListener(onOrderUpdateHandler); + } + + setupLiveTickListener(); + tickerProvider.setOnConnectedListener( + () -> { + synchronized (LIVE_FEED_SUBSCRIBE_LOCK) { + log.info("Websocket connected"); + var tokens = new ArrayList<>(subscribedScripts.values()); + tickerProvider.unsubscribe(tokens); + tickerProvider.subscribe(tokens); + tickerProvider.setMode(tokens, KiteTicker.modeFull); + } + }); + tickerProvider.setOnDisconnectedListener( + () -> { + log.info("Websocket disconnected. Re-connecting..."); + disconnectFeed(); + initLiveFeed(); + }); + tickerProvider.setOnErrorListener( + new OnError() { + @Override + public void onError(Exception e) { + if (e instanceof OpeningHandshakeException openingHandshakeException) { + if (openingHandshakeException.getStatusLine().getStatusCode() == 403) { + if (connectionInProgress.get()) { + // KiteTicker.connect() method calls onError() and the Timer thread calls + // onError(). We have to ignore the KiteTicker.connect() onError event. + log.debug("Received 403 on websocket. Ignoring as connection in progress"); + return; + } + if (kiteConnected) { + log.error("Received 403 on websocket. Re-connecting..."); + disconnectFeed(); + initLiveFeed(); + } else { + log.error( + "Received 403 on websocket. Since Kite api is not connected, ignoring"); + } + } else { + log.error("Received openingHandshakeException", e); + } + } else { + log.error("Exception in socket listener", e); + } + } + + @Override + public void onError(KiteException kiteException) { + log.error( + "Kite Exception in socket listener - {} with code - {}", + kiteException.message, + kiteException.code); + } + + @Override + public void onError(String error) { + log.error("Error in socket listener: {}", error); + } + }); + connectionInProgress.set(true); + tickerProvider.connect(); + connectionInProgress.set(false); + DataProvidable.super.reconnectFeed(); + } catch (KiteException e) { + log.error(getErrMsg(e), e); + } + } + } + } + + private void setupLiveTickListener() { + tickerProvider.setOnTickerArrivalListener( + ticks -> { + try { + if (!ticks.isEmpty()) { + liveTicksHolder.put(ticks); + } else { + log.debug("Ticks is empty"); + } + } catch (InterruptedException e) { + // ignore + log.error("", e); + } + }); + + if (liveTicksAsyncPublisher == null) { + liveTicksAsyncPublisher = + Thread.ofPlatform() + .name("live-ticks-async-publisher") + .start( + () -> { + while (true) { + try { + var currLiveTicks = liveTicksHolder.take(); + var liveTicks = + currLiveTicks.stream() + .filter(tick -> tick.getTickTimestamp() != null) + .map( + tick -> { + try { + var ltp = tick.getLastTradedPrice(); + var exchangeScripts = + scriptsTokenSymbolMap.get(tick.getInstrumentToken()); + return exchangeScripts.stream() + .map( + exchangeScript -> + new LiveTick( + exchangeScript.getExchange() + + ":" + + exchangeScript.getScript(), + tick.getTickTimestamp().getTime(), + new Date(), + ltp, + ltp, + ltp, + tick.getVolumeTradedToday())) + .toList(); + } catch (Throwable t) { + log.error("", t); + throw t; + } + }) + .flatMap(Collection::stream) + .toList(); + liveTickConsumer.apply(liveTicks); + } catch (InterruptedException e) { + log.error("live-ticks-async-publisher thread interrupted"); + } + } + }); + } + } + + @Override + public void subscribe(String exchangeScript) { + synchronized (LIVE_FEED_SUBSCRIBE_LOCK) { + var script = OmsUtil.getScript(exchangeScript); + var exchange = OmsUtil.getExchange(exchangeScript); + if (!scriptMetadataMap.containsKey( + new NseScriptMetadata.NseMetadataId(script, Exchange.valueOf(exchange)))) { + log.error("{} is not found in metadata", exchangeScript); + return; + } + var scriptToken = + Long.parseLong( + scriptMetadataMap + .get(new NseScriptMetadata.NseMetadataId(script, Exchange.valueOf(exchange))) + .getBrokerScriptIdMap() + .get(BrokerConfig.BrokerType.ZERODHA)); + var tokensToSubscribe = new ArrayList(); + tokensToSubscribe.add(scriptToken); + if (tickerProvider.isConnectionOpen()) { + tickerProvider.subscribe(tokensToSubscribe); + tickerProvider.setMode(tokensToSubscribe, KiteTicker.modeFull); + } + subscribedScripts.put(exchangeScript, scriptToken); + } + } + + @Override + public void unSubscribe(String script) { + synchronized (LIVE_FEED_SUBSCRIBE_LOCK) { + if (subscribedScripts.containsKey(script)) { + // Do not get from scriptMetadataMap bcoz in case the metadata changes, the old scripts and + // it's tokens will not be available + var scriptToken = subscribedScripts.get(script); + var tokensToUnSubscribe = new ArrayList(); + tokensToUnSubscribe.add(scriptToken); + if (tickerProvider.isConnectionOpen()) { + tickerProvider.unsubscribe(tokensToUnSubscribe); + } + subscribedScripts.remove(script); + } + } + } + + @Override + public void reconnectFeed() { + init(); + initLiveFeed(); + } + + @Override + public boolean testConnection() throws BrokerException { + try { + getBalance(); + return true; + } catch (Throwable t) { + log.warn( + "Getting balance failed with exception. Concluding that the broker is not logged in", t); + return false; + } + } + + @Override + public void closeOrder(String orderId) throws BrokerException {} + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + return getBrokerOrderDetail(getOrderDetail(orderId), 0); + } + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + switch (brokerPlaceOrderRequest.getType()) { + case BrokerOrderType.REGULAR_BUY_NSE -> { + var orderRequest = (BrokerPlaceOrderRequest.RegularBuyNse) brokerPlaceOrderRequest; + var script = OmsUtil.getScript(orderRequest.getExchangeScript()); + var exchange = OmsUtil.getExchange(orderRequest.getExchangeScript()); + var res = + placeOrder( + script, + orderRequest.getQuantity(), + "BUY", + orderRequest.getPrice(), + exchange, + orderRequest.getProductType()); + return getBrokerOrderDetail(getOrderDetail(res), orderRequest.getPrice()); + } + case BrokerOrderType.REGULAR_SELL_NSE -> { + var orderRequest = (BrokerPlaceOrderRequest.RegularSellNse) brokerPlaceOrderRequest; + var script = OmsUtil.getScript(orderRequest.getExchangeScript()); + var exchange = OmsUtil.getExchange(orderRequest.getExchangeScript()); + // if(!orderRequest.getProductType().equals("CNC")){ + var res = + placeOrder( + script, + orderRequest.getQuantity(), + "SELL", + orderRequest.getPrice(), + exchange, + orderRequest.getProductType()); + return getBrokerOrderDetail(getOrderDetail(res), orderRequest.getPrice()); + // } + // else throw new BrokerException(ApiError.INVALID_PLACE_REQUEST); + } + default -> + throw new IllegalArgumentException( + brokerPlaceOrderRequest.getType() + " orderType not available"); + } + } + + private BrokerOrderDetail getBrokerOrderDetail(Order order, double requestedPrice) { + try { + switch (order.transactionType) { + case "BUY" -> { + return BrokerOrderDetail.RegularBuyNse.builder() + .quantity(Double.parseDouble(order.quantity)) + .stopLoss(0) + .takeProfit(0) + .openTs(new DateTime(order.orderTimestamp).minusHours(5).minusMinutes(30).getMillis()) + .closeTs( + (order.orderTimestamp != null) + ? new DateTime(order.orderTimestamp) + .minusHours(5) + .minusMinutes(30) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue("RegularBuy" + orderTypeSuffix)) + .exchangeScript(order.exchange + ":" + order.tradingSymbol) + .orderId(order.orderId) + .orderStatus(BrokerOrderStatus.findEnumByValue(order.status)) + .avgOpenPrice(parseDouble(order.averagePrice)) + .price(requestedPrice) + .productType(order.product) + .avgClosePrice(0) + .profitAndLoss(BigDecimal.valueOf(0)) + .filledQuantity(Double.parseDouble(order.filledQuantity)) + .tag(order.tag) + .build(); + } + case "SELL" -> { + return BrokerOrderDetail.RegularSellNse.builder() + .quantity(Double.parseDouble(order.quantity)) + .stopLoss(0) + .takeProfit(0) + .openTs(new DateTime(order.orderTimestamp).minusHours(5).minusMinutes(30).getMillis()) + .closeTs( + (order.orderTimestamp != null) + ? new DateTime(order.orderTimestamp) + .minusHours(5) + .minusMinutes(30) + .getMillis() + : 0) + .orderUpdateTs(0) + .type(BrokerOrderType.findEnumByValue("RegularSell" + orderTypeSuffix)) + .exchangeScript(order.exchange + ":" + order.tradingSymbol) + .orderId(order.orderId) + .orderStatus(BrokerOrderStatus.findEnumByValue(order.status)) + .avgOpenPrice(parseDouble(order.averagePrice)) + .price(requestedPrice) + .productType(order.product) + .avgClosePrice(0) + .profitAndLoss(BigDecimal.valueOf(0)) + .filledQuantity(Double.parseDouble(order.filledQuantity)) + .tag(order.tag) + .build(); + } + default -> throw new RuntimeException("Error in mapping.." + order.transactionType); + } + } catch (Exception e) { + throw new RuntimeException("Error in formatting - " + order.orderId, e); + } + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + return null; + } + + private List getHistoricalData( + String instrumentToken, DateTime from, DateTime to, TimeFrame timeFrame) { + var interval = + switch (timeFrame) { + case TimeFrame.MINUTE_1 -> "minute"; + case TimeFrame.MINUTE_3 -> "3minute"; + case TimeFrame.MINUTE_5 -> "5minute"; + case TimeFrame.MINUTE_10 -> "10minute"; + case TimeFrame.MINUTE_15 -> "15minute"; + case TimeFrame.MINUTE_30 -> "30minute"; + case TimeFrame.HOUR_1 -> "60minute"; + default -> throw new RuntimeException("Timeframe, " + timeFrame + " is not supported"); + }; + HistoricalData todayData; + while (true) { + try { + log.debug("Getting data from, {} to, {}", from, to); + todayData = + kite.getHistoricalData( + from.toDate(), to.toDate(), instrumentToken, interval, false, true); + return todayData.dataArrayList.stream() + .map(historicalData -> createCandleFromHistoricalData(historicalData, timeFrame)) + .collect(Collectors.toList()); + } catch (Throwable e) { + if (e instanceof KiteException k) { + log.error( + "Failed to get historical data for token - {}; from - {}; to - {} with error, {}", + instrumentToken, + from, + to, + k.message, + e); + } else { + log.error( + "Failed to get historical data for token - {}; from - {}; to - {}", + instrumentToken, + from, + to, + e); + } + } + } + } + + @Override + public double getBalance() { + try { + var marginVal = + kite.getMargins().values().stream().mapToDouble(margin -> parseDouble(margin.net)).sum(); + var posVal = kite.getPositions().get("net").stream().mapToDouble(pos -> pos.unrealised).sum(); + var holdingsVal = + kite.getHoldings().stream().mapToDouble(holding -> holding.averagePrice).sum(); + return marginVal + posVal + holdingsVal; + } catch (Throwable e) { + throw new BrokerException(e, ApiError.BROKER_GET_BALANCE_FAILURE, getErrMsg(e)); + } + } + + public String buyShares( + String tradingSymbol, + double quantity, + double limitPrice, + String exchange, + String productType) { + if (quantity == 0) return DUMMY_TRADE; + return placeOrder( + tradingSymbol, quantity, TRANSACTION_TYPE_BUY, limitPrice, exchange, productType); + } + + public String sellShares( + String tradingSymbol, + double quantity, + double limitPrice, + String exchange, + String productType) { + if (quantity == 0) return DUMMY_TRADE; + return placeOrder( + tradingSymbol, quantity, TRANSACTION_TYPE_SELL, limitPrice, exchange, productType); + } + + public String modifyOrder( + String orderId, double limitPrice, double triggerPrice, String orderType, double qty) { + var orderDetail = getOrderDetail(orderId); + var orderStatus = BrokerOrderStatus.findEnumByValue(orderDetail.status); + if (orderStatus == BrokerOrderStatus.CANCELLED + || orderStatus == BrokerOrderStatus.REJECTED + || orderStatus == BrokerOrderStatus.COMPLETE) { + throw new BrokerException(ApiError.ORDER_CANNOT_MODIFY, orderId, orderStatus); + } + OrderParams orderParams = new OrderParams(); + orderParams.price = limitPrice; + orderParams.triggerPrice = triggerPrice; + orderParams.orderType = orderType; + orderParams.quantity = (int) qty; + Function> modifyOrderFunc = + (unused) -> { + try { + kite.modifyOrder(orderId, orderParams, VARIETY_REGULAR); + return new RetryUtils.Output<>(false, orderId); + } catch (Throwable t) { + if (t instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + // ignore + } + log.warn( + "Modify order received 429 from Zerodha. Cancelling and placing the order again"); + cancelOrder(orderId); + return new RetryUtils.Output<>( + false, + placeOrder( + orderDetail.tradingSymbol, + qty, + orderDetail.transactionType, + limitPrice, + orderDetail.exchange, + orderDetail.product)); + } + throw new BrokerException(t, ApiError.BROKER_MODIFY_ORDER, getErrMsg(t)); + } + }; + return RetryUtils.runWithRetries( + modifyOrderFunc, null, 10, this::getErrMsg, "Zerodha modify order"); + } + + /** + * Always send a tag with a unique identifier + * + * @param tradingSymbol This is the script name + * @param quantity Quantity of shares to buy + * @param transactionType Buy/Sell + * @param tag Some random comments for future reference + * @param limitPrice Value of limit price + * @param exchange NSE/BSE + * @param productType MIS/CNC/LONG TERM + * @return orderId + */ + public Order placeOrderBroker( + String tradingSymbol, + double quantity, + String transactionType, + String tag, + double limitPrice, + String exchange, + String productType) { + OrderParams orderParams = new OrderParams(); + orderParams.exchange = exchange; + orderParams.tradingsymbol = tradingSymbol; + orderParams.transactionType = transactionType; + orderParams.quantity = (int) quantity; + orderParams.product = productType; + orderParams.tag = tag; + orderParams.price = limitPrice; + var orderType = limitPrice == 0.0 ? ORDER_TYPE_MARKET : ORDER_TYPE_LIMIT; + orderParams.orderType = orderType; + var variety = VARIETY_REGULAR; + + Function> placeOrderFunction = + (unused) -> { + try { + log.info( + "Placing order for tradingSymbol - {}; transactionType - {}; quantity - {}; tag - {}", + tradingSymbol, + transactionType, + quantity, + tag); + return new RetryUtils.Output<>(false, kite.placeOrder(orderParams, variety)); + } catch (Throwable t) { + if (t instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(1000); + } catch (InterruptedException ex) { + // ignore + } + log.warn( + "Place order received 429 from Zerodha. Cancelling and placing the order again"); + return new RetryUtils.Output<>(true, null); + } + var errMsg = getErrMsg(t); + try { + var orderOpt = + getOrders().stream() + .filter( + order -> + order.exchange.equals(exchange) + && order.tradingSymbol.equals(tradingSymbol) + && order.transactionType.equals(transactionType) + && parseDouble(order.quantity) == quantity + && order.product.equals(productType) + && (order.tag == null || order.tag.equals(tag)) + && parseDouble(order.price) == limitPrice + && order.orderType.equals(orderType) + && order.orderVariety.equals(variety)) + .findAny(); + if (orderOpt.isPresent()) { + log.error( + "Failed while placing order. But found similar order in the broker. ErrMsg - {}", + errMsg, + t); + return new RetryUtils.Output<>(false, orderOpt.get()); + } else { + // This is thrown when order is not found in broker. RetryUtils will retry to place + // the order again + throw new BrokerException(t, ApiError.ORDER_CANNOT_PLACE, errMsg); + } + } catch (BrokerException ex) { + throw new BrokerException(ex, ex.getApiError(), false); + } + } + }; + + return RetryUtils.runWithRetries( + placeOrderFunction, + null, + 10, + this::getErrMsg, + "Zerodha place order", + (t) -> { + if (t instanceof BrokerException be) { + return be.isRetryable(); + } else return true; + }); + } + + /** + * Always send a tag with a unique identifier + * + * @param tradingSymbol This is the script name + * @param quantity Quantity of shares to buy + * @param transactionType Buy/Sell + * @param limitPrice Value of limit price + * @param exchange NSE/BSE + * @param productType MIS/CNC/LONG TERM + * @return orderId + */ + public String placeOrder( + String tradingSymbol, + double quantity, + String transactionType, + double limitPrice, + String exchange, + String productType) { + var tag = UUID.randomUUID().toString().replace("-", "").substring(0, 15); + OrderParams orderParams = new OrderParams(); + orderParams.exchange = exchange; + orderParams.tradingsymbol = tradingSymbol; + orderParams.transactionType = transactionType; + orderParams.quantity = (int) quantity; + orderParams.product = productType; + orderParams.tag = tag; + orderParams.price = limitPrice; + orderParams.disclosedQuantity = 0; + var orderType = limitPrice == 0.0 ? ORDER_TYPE_MARKET : ORDER_TYPE_LIMIT; + orderParams.orderType = orderType; + var variety = VARIETY_REGULAR; + + Function> placeOrderFunction = + (unused) -> { + try { + log.info( + "Placing order for tradingSymbol - {}; transactionType - {}; quantity - {}; tag - {}", + tradingSymbol, + transactionType, + quantity, + tag); + return new RetryUtils.Output<>(false, kite.placeOrder(orderParams, variety).orderId); + } catch (Throwable t) { + if (t instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(TOO_MANY_REQUESTS_WAIT_TIME); + } catch (InterruptedException ex) { + // ignore + } + log.warn( + "Place order received 429 from Zerodha. Cancelling and placing the order again"); + return new RetryUtils.Output<>(true, null); + } + var errMsg = getErrMsg(t); + try { + var orderOpt = + getOrders().stream() + .filter( + order -> + order.exchange.equals(exchange) + && order.tradingSymbol.equals(tradingSymbol) + && order.transactionType.equals(transactionType) + && parseDouble(order.quantity) == quantity + && order.product.equals(productType) + && (order.tag != null && order.tag.equals(tag)) + && parseDouble(order.price) == limitPrice + && order.orderType.equals(orderType) + && order.orderVariety.equals(variety)) + .findAny(); + if (orderOpt.isPresent()) { + log.error( + "Failed while placing order. But found similar order in the broker. ErrMsg - {}", + errMsg, + t); + return new RetryUtils.Output<>(false, orderOpt.get().orderId); + } else { + // This is thrown when order is not found in broker. RetryUtils will retry to place + // the order again + throw new BrokerException(t, ApiError.ORDER_CANNOT_PLACE, errMsg); + } + } catch (BrokerException ex) { + throw new BrokerException(false, ex); + } + } + }; + + return RetryUtils.runWithRetries( + placeOrderFunction, + null, + 10, + this::getErrMsg, + "Zerodha place order", + (t) -> { + if (t instanceof BrokerException be) { + return be.isRetryable(); + } else return true; + }); + } + + private String getErrMsg(Throwable t) { + if (t instanceof KiteException ke) { + return String.format( + "{\"class\":\"%s\",\"err\":\"%s\",\"code\":\"%s\",\"reference\":\"https://kite.trade/docs/connect/v3/exceptions/#common-http-error-codes\"}", + ke.getClass().getName(), ke.message, ke.code); + } else { + return t.getMessage(); + } + } + + public Order getOrderDetail(String orderId) { + log.info("Getting details for orderId, {}", orderId); + Function> getOrderFunc = + (unused) -> { + try { + var orders = kite.getOrderHistory(orderId); + return new RetryUtils.Output<>(false, orders.get(orders.size() - 1)); + } catch (Throwable e) { + if (e instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(TOO_MANY_REQUESTS_WAIT_TIME); + } catch (InterruptedException ex) { + // ignore + } + log.warn("Get order received 429 from Zerodha."); + return new RetryUtils.Output<>(true, null); + } + throw new BrokerException(e, ApiError.BROKER_GET_ORDER_DETAILS_FAILURE, getErrMsg(e)); + } + }; + return RetryUtils.runWithRetries(getOrderFunc, null, 10, this::getErrMsg, "Zerodha get order"); + } + + @Override + public void cancelOrder(String orderId) { + if (orderId.equals(DUMMY_TRADE)) return; + log.info("Cancelling order, {}", orderId); + var orderDetail = getOrderDetail(orderId); + var orderStatus = BrokerOrderStatus.findEnumByValue(orderDetail.status); + if (orderStatus == BrokerOrderStatus.CANCELLED) { + log.warn("Order, {} is already cancelled", orderId); + return; + } else if (orderStatus == BrokerOrderStatus.REJECTED + || orderStatus == BrokerOrderStatus.COMPLETE) { + throw new BrokerException(ApiError.ORDER_CANNOT_CANCEL, orderId, orderStatus); + } + Function> cancelOrder = + (unused) -> { + try { + kite.cancelOrder(orderId, VARIETY_REGULAR); + return null; + } catch (Throwable t) { + if (t instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(TOO_MANY_REQUESTS_WAIT_TIME); + } catch (InterruptedException ex) { + // ignore + } + log.warn( + "Cancel order received 429 from Zerodha. Cancelling and placing the order again"); + return new RetryUtils.Output<>(true, null); + } + throw new BrokerException( + t, ApiError.BROKER_CANCEL_ORDER, orderId + ". " + getErrMsg(t)); + } + }; + + RetryUtils.runWithRetries(cancelOrder, null, 10, this::getErrMsg, "Zerodha cancel order"); + } + + public List getOrders() { + Function>> getOrdersFunc = + (unused) -> { + try { + return new RetryUtils.Output<>(false, kite.getOrders()); + } catch (KiteException | IOException e) { + if (e instanceof KiteException ke && ke.code == 429) { + try { + Thread.sleep(TOO_MANY_REQUESTS_WAIT_TIME); + } catch (InterruptedException ex) { + // ignore + } + log.warn("Get orders received 429 from Zerodha."); + return new RetryUtils.Output<>(true, null); + } + throw new BrokerException(e, ApiError.BROKER_GET_ORDER_DETAILS_FAILURE, getErrMsg(e)); + } + }; + + return RetryUtils.runWithRetries( + getOrdersFunc, null, 10, this::getErrMsg, "Zerodha get orders"); + } + + @Override + public void disConnect() { + this.reconnectTimer.shutdown(); + this.reconnectTimer.close(); + this.kite = null; + disconnectFeed(); + } + + private static Candle createCandleFromHistoricalData(HistoricalData candle, TimeFrame timeFrame) { + return new Candle( + DateTime.parse(candle.timeStamp).toDate(), + candle.open, + candle.high, + candle.low, + candle.close, + candle.volume, + candle.oi, + timeFrame); + } + + /** + * This is used only by Shruthi Copy trading Poc feature + * + * @param handler This lambda is invoked on any order update + */ + public void setOnOrderUpdateHandler(OnOrderUpdate handler) { + onOrderUpdateHandler = handler; + initLiveFeed(); + tickerProvider.setOnOrderUpdateListener(onOrderUpdateHandler); + } + + @Override + public List getHistoricalDataFromProvider( + NseScriptMetadata nseScriptMetadata, DateTime from, DateTime to, TimeFrame timeFrame) { + String token = nseScriptMetadata.getBrokerScriptIdMap().get(BrokerConfig.BrokerType.ZERODHA); + if (token == null || token.isEmpty()) { + return new ArrayList<>(); + } else { + var candles = new ArrayList(); + var start = from; + while (start.isBefore(to)) { + if (start.plusDays(30).isBefore(to)) { + candles.addAll(getHistoricalData(token, start, start.plusDays(30), timeFrame)); + start = start.plusDays(31); + } else { + candles.addAll(getHistoricalData(token, start, to, timeFrame)); + start = to; + } + } + return candles; + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateRequest.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateRequest.java new file mode 100644 index 00000000000..881242d11be --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateRequest.java @@ -0,0 +1,13 @@ +package io.quantum.trading.brokers.indian.fyers; + +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; + +@Data +@Builder +public class FyersAuthCodeValidateRequest { + @NonNull private String grant_type; + @NonNull private String appIdHash; + @NonNull private String code; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateResponse.java new file mode 100644 index 00000000000..c6f35179222 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersAuthCodeValidateResponse.java @@ -0,0 +1,15 @@ +package io.quantum.trading.brokers.indian.fyers; + +import lombok.Data; + +@Data +public class FyersAuthCodeValidateResponse { + private String s; + private String code; + + private String message; + + private String access_token; + + private String refresh_token; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersBroker.java new file mode 100644 index 00000000000..96c134d06df --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersBroker.java @@ -0,0 +1,310 @@ +package io.quantum.trading.brokers.indian.fyers; + +import static io.quantum.trading.brokers.Exchange.BFO; +import static io.quantum.trading.brokers.Exchange.NFO; +import static io.quantum.trading.entities.BrokerConfig.BrokerType.FYERS; +import static io.quantum.trading.entities.NseScriptMetadata.Segment.INDICES; +import static java.util.concurrent.TimeUnit.SECONDS; + +import io.quantum.trading.brokers.*; +import io.quantum.trading.brokers.indian.NseBroker; +import io.quantum.trading.entities.BrokerConfig; +import io.quantum.trading.entities.BrokerConfigDetails; +import io.quantum.trading.entities.NseScriptMetadata; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.patterns.Candle; +import io.quantum.trading.provider.HistoricalDataProvidable; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.joda.time.DateTime; +import org.joda.time.Instant; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; + +@Slf4j +public class FyersBroker extends NseBroker implements HistoricalDataProvidable { + + private final BrokerConfigDetails.FyersBrokerConfigDetails fyersBrokerConfigDetails; + + private final RestTemplate restTemplate = new RestTemplate(); + + private Map> exchangeTokenBrokerMetadataCache; + + private final String[] FYERS_EXCHANGE_TOKEN_URLS = { + "https://public.fyers.in/sym_details/NSE_CD.csv", + "https://public.fyers.in/sym_details/NSE_FO.csv", + "https://public.fyers.in/sym_details/NSE_CM.csv", + "https://public.fyers.in/sym_details/BSE_CM.csv", + "https://public.fyers.in/sym_details/BSE_FO.csv", + "https://public.fyers.in/sym_details/MCX_COM.csv" + }; + + public FyersBroker(BrokerConfigDetails.FyersBrokerConfigDetails fyersBrokerConfigDetails) { + this.fyersBrokerConfigDetails = fyersBrokerConfigDetails; + } + + @Override + public double getBalance() throws BrokerException { + + return 0; + } + + @Override + public boolean testConnection() throws BrokerException { + return false; + } + + @Override + public void disConnect() throws BrokerException {} + + @Override + public BrokerOrderDetail placeOrder(BrokerPlaceOrderRequest brokerPlaceOrderRequest) + throws BrokerException { + return null; + } + + @Override + public BrokerOrderDetail modifyOrder(BrokerModifyOrderRequest brokerModifyOrderRequest) + throws BrokerException { + return null; + } + + @Override + public void cancelOrder(String orderId) throws BrokerException {} + + @Override + public void closeOrder(String orderId) throws BrokerException {} + + @Override + public BrokerOrderDetail getOrderDetails(String orderId) throws BrokerException { + return null; + } + + @Override + public String getBrokerToken(NseScriptMetadata nseScriptMetadata) { + loadBrokerMetadata(); + var segment = nseScriptMetadata.getSegment(); + var exchange = nseScriptMetadata.getId().getExchange(); + var scriptName = nseScriptMetadata.getId().getScript().trim().replaceAll(" ", ""); + + var metadataMapperKey = nseScriptMetadata.getExchangeToken(); + if (segment.equals(INDICES)) metadataMapperKey = metadataMapperKey + "_" + segment; + else metadataMapperKey = metadataMapperKey + "_" + exchange; + if (!exchangeTokenBrokerMetadataCache.containsKey(metadataMapperKey)) { + List matchingKeys = + exchangeTokenBrokerMetadataCache.entrySet().stream() + .filter(entry -> entry.getKey().contains("_INDICES")) + .filter( + entry -> + entry + .getValue() + .get("script_name") + .trim() + .replaceAll(" ", "") + .contains(scriptName + "-")) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (matchingKeys.isEmpty()) { + log.info("Token not found for script {}, so returning empty string ", metadataMapperKey); + return ""; + } else if (matchingKeys.size() > 1) { + log.info( + "More than one match found from broker metadata with ScriptName mapping {} ,{} ", + scriptName, + matchingKeys); + return ""; + } + return exchangeTokenBrokerMetadataCache.get(matchingKeys.get(0)).get("token"); + } + return exchangeTokenBrokerMetadataCache.get(metadataMapperKey).get("token"); + } + + private void loadBrokerMetadata() { + if (exchangeTokenBrokerMetadataCache == null || exchangeTokenBrokerMetadataCache.isEmpty()) { + exchangeTokenBrokerMetadataCache = new HashMap<>(); + Arrays.stream(FYERS_EXCHANGE_TOKEN_URLS) + .toList() + .forEach( + fyersExchangeTokenUrl -> { + log.info("Fetching broker tokens from , {}", fyersExchangeTokenUrl); + try (var client = HttpClient.newHttpClient()) { + var req = + HttpRequest.newBuilder() + .uri(new URI(fyersExchangeTokenUrl)) + .timeout(Duration.of(10, SECONDS.toChronoUnit())) + .GET() + .build(); + var res = client.send(req, HttpResponse.BodyHandlers.ofString()); + Stream.of(res.body().split("\n")) + .forEach( + row -> { + var fields = row.split(","); + var scriptName = fields[9].trim(); + var exchangeToken = fields[12].trim(); + Map brokerMetadata = new HashMap<>(); + brokerMetadata.put("token", fields[0].trim()); + brokerMetadata.put("script_name", scriptName); + if (scriptName.endsWith("-INDEX")) + exchangeTokenBrokerMetadataCache.put( + exchangeToken + "_" + INDICES, brokerMetadata); + else if (scriptName.startsWith("NSE") + && (scriptName.endsWith("CE") + || scriptName.endsWith("PE") + || scriptName.endsWith("FUT"))) + exchangeTokenBrokerMetadataCache.put( + exchangeToken + "_" + NFO, brokerMetadata); + else if (scriptName.startsWith("BSE") + && (scriptName.endsWith("CE") + || scriptName.endsWith("PE") + || scriptName.endsWith("FUT"))) + exchangeTokenBrokerMetadataCache.put( + exchangeToken + "_" + BFO, brokerMetadata); + else + exchangeTokenBrokerMetadataCache.put( + exchangeToken + "_" + scriptName.split(":")[0], brokerMetadata); + }); + } catch (URISyntaxException | IOException | InterruptedException e) { + throw new RuntimeException(e); + } + }); + } + } + + @Override + public String getBrokerScriptName(NseScriptMetadata nseScriptMetadata) { + loadBrokerMetadata(); + var segment = nseScriptMetadata.getSegment(); + var scriptName = nseScriptMetadata.getId().getScript().trim().replaceAll(" ", ""); + var exchange = nseScriptMetadata.getId().getExchange(); + + var metadataMapperKey = nseScriptMetadata.getExchangeToken(); + if (segment.equals(INDICES)) metadataMapperKey = metadataMapperKey + "_" + segment; + else metadataMapperKey = metadataMapperKey + "_" + exchange; + if (!exchangeTokenBrokerMetadataCache.containsKey(metadataMapperKey)) { + List matchingKeys = + exchangeTokenBrokerMetadataCache.entrySet().stream() + .filter(entry -> entry.getKey().contains("_INDICES")) + .filter( + entry -> + entry + .getValue() + .get("script_name") + .trim() + .replaceAll(" ", "") + .contains(scriptName + "-")) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (matchingKeys.isEmpty()) { + log.info( + "ScriptName not found for script {}, so returning empty string ", metadataMapperKey); + return ""; + } else if (matchingKeys.size() > 1) { + log.info( + "More than one match found from broker metadata with ScriptName mapping {} ,{} ", + scriptName, + matchingKeys); + return ""; + } + return exchangeTokenBrokerMetadataCache.get(matchingKeys.get(0)).get("script_name"); + } + return exchangeTokenBrokerMetadataCache.get(metadataMapperKey).get("script_name"); + } + + @Override + public List getHistoricalDataFromProvider( + NseScriptMetadata nseScriptMetadata, DateTime from, DateTime to, TimeFrame timeFrame) { + String token = nseScriptMetadata.getBrokerScriptIdMap().get(FYERS); + if (token == null || token.isEmpty()) { + return new ArrayList<>(); + } else { + var candles = new ArrayList(); + var start = from; + while (start.isBefore(to)) { + if (start.plusDays(99).isBefore(to)) { + candles.addAll( + getHistoricalData(nseScriptMetadata, start, start.plusDays(99), timeFrame)); + start = start.plusDays(100); + } else { + candles.addAll(getHistoricalData(nseScriptMetadata, start, to, timeFrame)); + start = to; + } + } + return candles; + } + } + + @NotNull + private List getHistoricalData( + NseScriptMetadata nseScriptMetadata, DateTime from, DateTime to, TimeFrame timeFrame) { + var resolution = + switch (timeFrame) { + case MINUTE_1 -> "1"; + case MINUTE_3 -> "3"; + case MINUTE_5 -> "5"; + case MINUTE_10 -> "10"; + case MINUTE_15 -> "15"; + case MINUTE_30 -> "30"; + case HOUR_1 -> "60"; + default -> throw new RuntimeException("Timeframe, " + timeFrame + " is not supported"); + }; + String formattedFrom = from.toLocalDate().toString(); + String formattedTo = to.toLocalDate().toString(); + String symbol = nseScriptMetadata.getBrokerScriptNameMap().get(BrokerConfig.BrokerType.FYERS); + String continuousFlag = + (nseScriptMetadata.getId().getScript().endsWith("1!") + || nseScriptMetadata.getId().getScript().endsWith("2!") + || nseScriptMetadata.getId().getScript().endsWith("3!")) + ? "1" + : ""; + + final String url = + "https://api.fyers.in/data-rest/v2/history/?resolution={resolution}&date_format=1&range_from={formattedFrom}&range_to={formattedTo}&cont_flag={continuousFlag}&symbol={symbol}"; + Map params = new HashMap<>(); + params.put("resolution", resolution); + params.put("formattedFrom", formattedFrom); + params.put("formattedTo", formattedTo); + params.put("symbol", symbol); + params.put("continuousFlag", continuousFlag); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.put( + "Authorization", + Collections.singletonList( + fyersBrokerConfigDetails.getAppId() + ":" + fyersBrokerConfigDetails.getAccessToken())); + HttpEntity request = new HttpEntity(headers); + try { + ResponseEntity fyersHistoricalResponse = + restTemplate.exchange( + url, HttpMethod.GET, request, FyersHistoricalResponse.class, params); + return fyersHistoricalResponse.getBody().getCandles().stream() + .map(singleCandle -> createCandleFromHistoricalData(singleCandle, timeFrame)) + .collect(Collectors.toList()); + } catch (Exception e) { + log.error("Cannot get data for {}", symbol, e); + return new ArrayList<>(); + } + } + + private static Candle createCandleFromHistoricalData( + List singleCandle, TimeFrame timeFrame) { + return new Candle( + new Date(Instant.ofEpochSecond(Long.parseLong(singleCandle.get(0))).getMillis()), + Double.parseDouble(singleCandle.get(1)), + Double.parseDouble(singleCandle.get(2)), + Double.parseDouble(singleCandle.get(3)), + Double.parseDouble(singleCandle.get(4)), + Double.parseDouble(singleCandle.get(5)), + 0, + timeFrame); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersHistoricalResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersHistoricalResponse.java new file mode 100644 index 00000000000..226a3c2668e --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/fyers/FyersHistoricalResponse.java @@ -0,0 +1,20 @@ +package io.quantum.trading.brokers.indian.fyers; + +import java.util.List; +import lombok.Data; + +@Data +public class FyersHistoricalResponse { + private String s; + private List> candles; +} + +@Data +class FyerHistoricalCandles { + private long beginTs; + private double open; + private double high; + private double low; + private double close; + private double volume; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/JSON.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/JSON.java new file mode 100644 index 00000000000..c5c02c2e6e1 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/JSON.java @@ -0,0 +1,373 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.bind.util.ISO8601Utils; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.gsonfire.GsonFireBuilder; +import java.io.IOException; +import java.io.StringReader; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import okio.ByteString; +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; +import org.threeten.bp.format.DateTimeFormatter; + +public class JSON { + private Gson gson; + private boolean isLenientOnJson = false; + private DateTypeAdapter dateTypeAdapter = new DateTypeAdapter(); + private SqlDateTypeAdapter sqlDateTypeAdapter = new SqlDateTypeAdapter(); + private OffsetDateTimeTypeAdapter offsetDateTimeTypeAdapter = new OffsetDateTimeTypeAdapter(); + private LocalDateTypeAdapter localDateTypeAdapter = new LocalDateTypeAdapter(); + private ByteArrayAdapter byteArrayAdapter = new ByteArrayAdapter(); + + public static GsonBuilder createGson() { + GsonFireBuilder fireBuilder = new GsonFireBuilder(); + GsonBuilder builder = fireBuilder.createGsonBuilder(); + return builder; + } + + private static String getDiscriminatorValue(JsonElement readElement, String discriminatorField) { + JsonElement element = readElement.getAsJsonObject().get(discriminatorField); + if (null == element) { + throw new IllegalArgumentException( + "missing discriminator field: <" + discriminatorField + ">"); + } + return element.getAsString(); + } + + private static Class getClassByDiscriminator( + Map classByDiscriminatorValue, String discriminatorValue) { + Class clazz = + (Class) classByDiscriminatorValue.get(discriminatorValue.toUpperCase(Locale.ROOT)); + if (null == clazz) { + throw new IllegalArgumentException( + "cannot determine model class of name: <" + discriminatorValue + ">"); + } + return clazz; + } + + public JSON() { + gson = + createGson() + .registerTypeAdapter(Date.class, dateTypeAdapter) + .registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter) + .registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter) + .registerTypeAdapter(LocalDate.class, localDateTypeAdapter) + .registerTypeAdapter(byte[].class, byteArrayAdapter) + .create(); + } + + /** + * Get Gson. + * + * @return Gson + */ + public Gson getGson() { + return gson; + } + + /** + * Set Gson. + * + * @param gson Gson + * @return JSON + */ + public JSON setGson(Gson gson) { + this.gson = gson; + return this; + } + + public JSON setLenientOnJson(boolean lenientOnJson) { + isLenientOnJson = lenientOnJson; + return this; + } + + /** + * Serialize the given Java object into JSON string. + * + * @param obj Object + * @return String representation of the JSON + */ + public String serialize(Object obj) { + return gson.toJson(obj); + } + + /** + * Deserialize the given JSON string to Java object. + * + * @param Type + * @param body The JSON string + * @param returnType The type to deserialize into + * @return The deserialized Java object + */ + @SuppressWarnings("unchecked") + public T deserialize(String body, Type returnType) { + try { + if (isLenientOnJson) { + JsonReader jsonReader = new JsonReader(new StringReader(body)); + // see + // https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/stream/JsonReader.html#setLenient(boolean) + jsonReader.setLenient(true); + return gson.fromJson(jsonReader, returnType); + } else { + return gson.fromJson(body, returnType); + } + } catch (JsonParseException e) { + // Fallback processing when failed to parse JSON form response body: + // return the response body string directly for the String return type; + if (returnType.equals(String.class)) { + return (T) body; + } else { + throw (e); + } + } + } + + /** Gson TypeAdapter for Byte Array type */ + public class ByteArrayAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, byte[] value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + out.value(ByteString.of(value).base64()); + } + } + + @Override + public byte[] read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String bytesAsBase64 = in.nextString(); + ByteString byteString = ByteString.decodeBase64(bytesAsBase64); + return byteString.toByteArray(); + } + } + } + + /** Gson TypeAdapter for JSR310 OffsetDateTime type */ + public static class OffsetDateTimeTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public OffsetDateTimeTypeAdapter() { + this(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + } + + public OffsetDateTimeTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, OffsetDateTime date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public OffsetDateTime read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + if (date.endsWith("+0000")) { + date = date.substring(0, date.length() - 5) + "Z"; + } + return OffsetDateTime.parse(date, formatter); + } + } + } + + /** Gson TypeAdapter for JSR310 LocalDate type */ + public class LocalDateTypeAdapter extends TypeAdapter { + + private DateTimeFormatter formatter; + + public LocalDateTypeAdapter() { + this(DateTimeFormatter.ISO_LOCAL_DATE); + } + + public LocalDateTypeAdapter(DateTimeFormatter formatter) { + this.formatter = formatter; + } + + public void setFormat(DateTimeFormatter dateFormat) { + this.formatter = dateFormat; + } + + @Override + public void write(JsonWriter out, LocalDate date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + out.value(formatter.format(date)); + } + } + + @Override + public LocalDate read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + return LocalDate.parse(date, formatter); + } + } + } + + public JSON setOffsetDateTimeFormat(DateTimeFormatter dateFormat) { + offsetDateTimeTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setLocalDateFormat(DateTimeFormatter dateFormat) { + localDateTypeAdapter.setFormat(dateFormat); + return this; + } + + /** + * Gson TypeAdapter for java.sql.Date type If the dateFormat is null, a simple "yyyy-MM-dd" format + * will be used (more efficient than SimpleDateFormat). + */ + public static class SqlDateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public SqlDateTypeAdapter() {} + + public SqlDateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, java.sql.Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = date.toString(); + } + out.value(value); + } + } + + @Override + public java.sql.Date read(JsonReader in) throws IOException { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return new java.sql.Date(dateFormat.parse(date).getTime()); + } + return new java.sql.Date(ISO8601Utils.parse(date, new ParsePosition(0)).getTime()); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } + } + + /** + * Gson TypeAdapter for java.util.Date type If the dateFormat is null, ISO8601Utils will be used. + */ + public static class DateTypeAdapter extends TypeAdapter { + + private DateFormat dateFormat; + + public DateTypeAdapter() {} + + public DateTypeAdapter(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + public void setFormat(DateFormat dateFormat) { + this.dateFormat = dateFormat; + } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value; + if (dateFormat != null) { + value = dateFormat.format(date); + } else { + value = ISO8601Utils.format(date, true); + } + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + try { + if (dateFormat != null) { + return dateFormat.parse(date); + } + return ISO8601Utils.parse(date, new ParsePosition(0)); + } catch (ParseException e) { + throw new JsonParseException(e); + } + } + } catch (IllegalArgumentException e) { + throw new JsonParseException(e); + } + } + } + + public JSON setDateFormat(DateFormat dateFormat) { + dateTypeAdapter.setFormat(dateFormat); + return this; + } + + public JSON setSqlDateFormat(DateFormat dateFormat) { + sqlDateTypeAdapter.setFormat(dateFormat); + return this; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakApiClient.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakApiClient.java new file mode 100644 index 00000000000..35eddf24f21 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakApiClient.java @@ -0,0 +1,450 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.google.gson.JsonObject; +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j2; +import okhttp3.*; +import okhttp3.internal.http.HttpMethod; +import okhttp3.logging.HttpLoggingInterceptor; +import org.threeten.bp.LocalDate; +import org.threeten.bp.OffsetDateTime; + +@Log4j2 +public class KotakApiClient { + + private OkHttpClient httpClient; + private Map defaultHeaders = new HashMap(); + private Map defaultQueryParams = new HashMap(); + private String apiAuthorizationHeader = ""; + private String consumerKey = ""; + private boolean debug; + @Getter private String ip = "127.0.0.1"; + private HttpLoggingInterceptor loggingInterceptor; + private JSON json = new JSON(); + private KotakTokenResponse token = null; + private static String LOCK_OBJ = "dummy"; + @Setter private AuthHandler authHandler = null; + + public KotakApiClient(String consumerKey, String consumerSecret) { + httpClient = new OkHttpClient.Builder().build(); + + var consumerCombined = consumerKey + ":" + consumerSecret; + this.consumerKey = consumerKey; + apiAuthorizationHeader = + "Basic " + + Base64.getEncoder().encodeToString(consumerCombined.getBytes(StandardCharsets.UTF_8)); + addDefaultHeader("User-Agent", "Java"); + + try { + Socket socket = new Socket(); + socket.connect(new InetSocketAddress("google.com", 80)); + ip = socket.getLocalAddress().getHostAddress(); + socket.close(); + } catch (IOException e) { + log.error("Cant find device IP, using localhost", e); + } + } + + public void setDefaultHeaders(Map defaultHeaders) { + synchronized (LOCK_OBJ) { + this.defaultHeaders = defaultHeaders; + } + } + + public void addDefaultHeader(String key, String value) { + defaultHeaders.put(key, value); + } + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + if (debug != this.debug) { + if (debug) { + loggingInterceptor = new HttpLoggingInterceptor(); + loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + httpClient = httpClient.newBuilder().addInterceptor(loggingInterceptor).build(); + } else { + httpClient.interceptors().remove(loggingInterceptor); + loggingInterceptor = null; + } + } + this.debug = debug; + } + + public String login(KotakLoginPayload payload, Map headers) throws Exception { + var res = executeRequest(Method.POST, ClientBase.LOGIN_USERID, null, headers, null, payload); + var loginRes = (KotakLoginResponse) deserializeIfSuccess(res, KotakLoginResponse.class); + return getSessionToken(payload, loginRes, headers); + } + + private String getSessionToken( + KotakLoginPayload payload, KotakLoginResponse kotakLoginResponse, Map headers) + throws Exception { + headers.put("oneTimeToken", kotakLoginResponse.getOneTimeToken()); + var res = + executeRequest( + Method.POST, ClientBase.ONE_TIME_TOKEN_VALIDATE, null, headers, null, payload); + var sessionResObj = (JsonObject) deserializeIfSuccess(res, JsonObject.class); + if (!sessionResObj.has("sessionToken")) { + throw new Exception( + "Seesion Token not part of response, Response json: " + + json.getGson().toJson(sessionResObj)); + } + return sessionResObj.get("sessionToken").getAsString(); + } + + public KotakTokenResponse getAccessToken( + String apiUsername, String apiPassword, String refreshToken) throws Exception { + var formParams = new HashMap(); + formParams.put("grant_type", "password"); + if (apiUsername != null) { + formParams.put("username", apiUsername); + } + if (apiPassword != null) { + formParams.put("password", apiPassword); + } + if (refreshToken != null) { + formParams.put("refresh_token", refreshToken); + } + + var headers = new HashMap(); + headers.put("Content-Type", "application/x-www-form-urlencoded"); + headers.put("Authorization", apiAuthorizationHeader); + + var res = executeRequest(Method.POST, ClientBase.TOKEN, null, headers, formParams, null); + var resString = res.body().string(); + var resToken = (KotakTokenResponse) json.deserialize(resString, KotakTokenResponse.class); + if (resToken.getAccessToken() != null) { + token = resToken; + } + return resToken; + } + + public KotakInstrumentLinkResponse getInstrumentLinks() { + Response res = null; + try { + res = + executeRequest( + Method.GET, ClientBase.SCRIPT_MASTER_API_BASE + "/filename", null, null, null, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + return deserializeIfSuccess(res, KotakInstrumentLinkResponse.class); + } + + public double getMargin() { + Response res = null; + try { + res = + executeRequest( + Method.GET, ClientBase.MARGIN_API_BASE + "/margin", null, null, null, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + var resData = deserializeIfSuccess(res, Map.class); + double margin = + (double) + ((Map) (((Map) (((ArrayList) (resData.get("equity"))).get(0))).get("cash"))) + .get("totalMargin"); + return margin; + } + + public String placeOrder(KotakPlaceOrderPayload payload) { + Response res = null; + try { + res = executeRequest(Method.POST, ClientBase.ORDER_API_BASE, null, null, null, payload); + } catch (IOException e) { + throw new RuntimeException(e); + } + var placeOrderResponse = deserializeIfSuccess(res, KotakPlaceOrderResponse.class); + return String.valueOf(placeOrderResponse.getExchangeResponse().getOrderId()); + } + + public void cancelOrder(String orderId) { + Response res = null; + try { + res = + executeRequest( + Method.DELETE, ClientBase.ORDER_API_BASE + "/" + orderId, null, null, null, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + try { + log.info("Cancel Order response - {}", res.body().string()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public List getOrderDetails(String orderId) { + Response res = null; + try { + res = + executeRequest( + Method.GET, ClientBase.REPORTS_API_BASE + "/orders/", null, null, null, null); + } catch (IOException e) { + throw new RuntimeException(e); + } + var orderDetails = Arrays.asList(deserializeIfSuccess(res, KotakOrderDetailsResponse[].class)); + orderDetails = + orderDetails.stream() + .filter(orderDetail -> orderDetail.getOrderId() == Long.valueOf(orderId)) + .collect(Collectors.toList()); + if (orderDetails.isEmpty()) { + throw new RuntimeException("OrderId, " + orderId + " is not found"); + } else return orderDetails; + } + + public T deserializeIfSuccess(Response response, Class returnType) { + String resString = null; + try { + resString = response.body().string(); + } catch (IOException e) { + throw new RuntimeException(e); + } + var jsonObj = (JsonObject) json.deserialize(resString, JsonObject.class); + if (jsonObj.has("Success")) { + var payloadString = json.getGson().toJson(jsonObj.get("Success")); + return json.deserialize(payloadString, returnType); + } else if (jsonObj.has("success")) { + var payloadString = json.getGson().toJson(jsonObj.get("success")); + return json.deserialize(payloadString, returnType); + } else { + throw new RuntimeException("Login Failed, Response json: " + resString); + } + } + + private Response executeRequest( + Method method, + String path, + Map queryParams, + Map headers, + Map formParams, + Object body) + throws IOException { + Request request = null; + Request.Builder reqBuilder = null; + RequestBody reqBody; + synchronized (LOCK_OBJ) { + var pathWithDefaultParams = frameUrlWithQueryParams(path, defaultQueryParams); + var pathWithParams = frameUrlWithQueryParams(pathWithDefaultParams, queryParams); + + reqBuilder = new Request.Builder().url(pathWithParams); + + for (var header : defaultHeaders.entrySet()) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + + String contentType = "application/json"; + if (headers != null) { + for (var param : headers.entrySet()) { + reqBuilder.header(param.getKey(), parameterToString(param.getValue())); + } + + if (headers.get("Content-Type") != null) { + contentType = headers.get("Content-Type").toString(); + } + } + + if (!HttpMethod.permitsRequestBody(method.method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentType)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentType)) { + reqBody = buildRequestBodyMultipart(formParams); + } else if (body == null) { + if (method == Method.DELETE) { + reqBody = null; + } else { + reqBody = RequestBody.create(MediaType.parse(contentType), ""); + } + } else { + reqBody = serialize(body, contentType); + } + + request = reqBuilder.method(method.method, reqBody).build(); + } + var response = httpClient.newCall(request).execute(); + var resBodyStr = Objects.requireNonNull(response.body()).string(); + if (isUnAuthorizedRes(response, resBodyStr)) { + if (authHandler != null) { + authHandler.login(); + for (var header : defaultHeaders.entrySet()) { + reqBuilder.header(header.getKey(), parameterToString(header.getValue())); + } + request = reqBuilder.method(method.method, reqBody).build(); + response = httpClient.newCall(request).execute(); + resBodyStr = Objects.requireNonNull(response.body()).string(); + if (isUnAuthorizedRes(response, resBodyStr)) { + throw new RuntimeException("Authentication Failed Second time can't proceed"); + } + } + } + var resContentType = Objects.requireNonNull(response.body()).contentType(); + var resBody = ResponseBody.create(resBodyStr, resContentType); + return response.newBuilder().body(resBody).build(); + } + + private boolean isUnAuthorizedRes(Response response, String resBodyStr) { + return response.code() == 401 + || resBodyStr.toLowerCase().contains("Invalid Credentials".toLowerCase()) + || resBodyStr + .toLowerCase() + .contains("Session token is either expired or not correct".toLowerCase()); + } + + private RequestBody buildRequestBodyFormEncoding(Map formParams) { + var formBuilder = new okhttp3.FormBody.Builder(); + for (Map.Entry param : formParams.entrySet()) { + formBuilder.add(param.getKey(), parameterToString(param.getValue())); + } + return formBuilder.build(); + } + + private RequestBody buildRequestBodyMultipart(Map formParams) { + MultipartBody.Builder mpBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + for (Map.Entry param : formParams.entrySet()) { + if (param.getValue() instanceof File) { + File file = (File) param.getValue(); + Headers partHeaders = + Headers.of( + "Content-Disposition", + "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\""); + MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file)); + mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file)); + } else { + Headers partHeaders = + Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""); + mpBuilder.addPart( + partHeaders, RequestBody.create(null, parameterToString(param.getValue()))); + } + } + return mpBuilder.build(); + } + + private String guessContentTypeFromFile(File file) { + String contentType = URLConnection.guessContentTypeFromName(file.getName()); + if (contentType == null) { + return "application/octet-stream"; + } else { + return contentType; + } + } + + private String frameUrlWithQueryParams(String path, Map queryParams) { + final StringBuilder url = new StringBuilder(); + url.append(path); + + if (queryParams != null && !queryParams.isEmpty()) { + String prefix = path.contains("?") ? "&" : "?"; + for (var param : queryParams.entrySet()) { + if (param.getValue() != null) { + if (prefix != null) { + url.append(prefix); + prefix = null; + } else { + url.append("&"); + } + url.append(param.getKey()).append("=").append(parameterToString(param.getValue())); + } + } + } + return url.toString(); + } + + public String parameterToString(Object param) { + if (param == null) { + return ""; + } else if (param instanceof Date + || param instanceof OffsetDateTime + || param instanceof LocalDate) { + String jsonStr = json.serialize(param); + return jsonStr.substring(1, jsonStr.length() - 1); + } else if (param instanceof Collection) { + StringBuilder b = new StringBuilder(); + for (Object o : (Collection) param) { + if (b.length() > 0) { + b.append(","); + } + b.append(String.valueOf(o)); + } + return b.toString(); + } else { + return String.valueOf(param); + } + } + + private RequestBody serialize(Object obj, String contentType) { + if (obj instanceof byte[]) { + return RequestBody.create(MediaType.parse(contentType), (byte[]) obj); + } else if (obj instanceof File) { + return RequestBody.create(MediaType.parse(contentType), (File) obj); + } else if (isJsonMime(contentType)) { + String content; + if (obj != null) { + content = json.serialize(obj); + } else { + content = null; + } + return RequestBody.create(MediaType.parse(contentType), content); + } else { + throw new RuntimeException("Content type \"" + contentType + "\" is not supported"); + } + } + + private boolean isJsonMime(String mime) { + String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; + return mime != null && (mime.matches(jsonMime) || mime.equals("*/*")); + } + + public static final class ClientBase { + public static final String TOKEN_API_BASE = "https://tradeapi.kotaksecurities.com"; + public static final String TOKEN = TOKEN_API_BASE + "/token"; + + public static final String SESSION_API_BASE = + "https://tradeapi.kotaksecurities.com/apim/session/1.0/session"; + + public static final String LOGIN_USERID = SESSION_API_BASE + "/login/userid"; + public static final String ONE_TIME_TOKEN_VALIDATE = SESSION_API_BASE + "/2FA/oneTimeToken"; + public static final String ORDER_API_BASE = + "https://tradeapi.kotaksecurities.com/apim/orders/1.0/orders"; + public static final String REPORTS_API_BASE = + "https://tradeapi.kotaksecurities.com/apim/reports/1.0"; + public static final String SCRIPT_MASTER_API_BASE = + "https://tradeapi.kotaksecurities.com/apim/scripmaster/1.1"; + public static final String MARGIN_API_BASE = + "https://tradeapi.kotaksecurities.com/apim/margin/1.0"; + } + + public enum Method { + GET("GET"), + POST("POST"), + DELETE("DELETE"); + + private final String method; + + Method(final String method) { + this.method = method; + } + + @Override + public String toString() { + return method; + } + } + + public interface AuthHandler { + void login(); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakBroker.java new file mode 100644 index 00000000000..9996771ef5a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakBroker.java @@ -0,0 +1,253 @@ +// package io.quantum.trading.brokers.kotak; +// +// import com.fasterxml.jackson.databind.MappingIterator; +// import com.fasterxml.jackson.dataformat.csv.CsvMapper; +// import com.fasterxml.jackson.dataformat.csv.CsvSchema; +// import io.quantum.trading.brokers.OrderDetail; +// import io.quantum.trading.brokers.OrderDetail.BrokerOrderStatus; +// import io.quantum.trading.brokers.zerodha.ZerodhaBroker; +// import io.quantum.trading.entities.BrokerConfigDetails.KotakBrokerConfigDetails; +// import io.quantum.trading.entities.BrokerConfigDetails.ZerodhaBrokerConfigDetails; +// import lombok.SneakyThrows; +// import lombok.extern.slf4j.Slf4j; +// import org.joda.time.DateTime; +// +// import java.net.URI; +// import java.net.http.HttpClient; +// import java.net.http.HttpRequest; +// import java.net.http.HttpResponse; +// import java.util.ArrayList; +// import java.util.HashMap; +// import java.util.List; +// import java.util.Map; +// +// @Slf4j +// public class KotakBroker extends ZerodhaBroker { +// private KotakApiClient kotakApiClient; +// private List kotakInstruments; +// private boolean dryRun; +// +// public KotakBroker(KotakBrokerConfigDetails brokerConfigDetails, +// ZerodhaBrokerConfigDetails zerodhaBrokerConfigDetails, boolean dryRun) { +// super(zerodhaBrokerConfigDetails, dryRun); +// this.dryRun = dryRun; +// +// kotakApiClient = new KotakApiClient(brokerConfigDetails.getConsumerKey(), +// brokerConfigDetails.getConsumerSecret()); +// kotakApiClient.setDebug(false); +// kotakApiClient.setAuthHandler(() -> kotakLogin(brokerConfigDetails)); +// refreshKotakInstruments(); +// } +// +// @SneakyThrows +// private void kotakLogin(KotakBrokerConfigDetails brokerConfigDetails) { +// var apiUsername = brokerConfigDetails.getUserId(); +// var apiPassword = brokerConfigDetails.getTradeApiPassword(); +// var consumerKey = brokerConfigDetails.getConsumerKey(); +// var accPassword = brokerConfigDetails.getAccPassword(); +// var tokenRes = kotakApiClient.getAccessToken(apiUsername, apiPassword, null); +// Map defaultHeaders = new HashMap<>(); +// defaultHeaders.put("Authorization", "Bearer " + tokenRes.getAccessToken()); +// defaultHeaders.put("consumerKey", consumerKey); +// defaultHeaders.put("ip", kotakApiClient.getIp()); +// +// var payload = new KotakLoginPayload(); +// payload.setUserid(apiUsername); +// payload.setPassword(accPassword); +// var sessionToken = kotakApiClient.login(payload, defaultHeaders); +// defaultHeaders.put("sessionToken", sessionToken); +// +// kotakApiClient.setDefaultHeaders(defaultHeaders); +// } +// +// @SneakyThrows +// private void refreshKotakInstruments() { +// var instrumentLinks = kotakApiClient.getInstrumentLinks(); +// var fnoUrl = instrumentLinks.getFno(); +// var cashUrl = instrumentLinks.getCash(); +// kotakInstruments = new ArrayList<>(); +// var client = HttpClient.newBuilder().build(); +// HttpRequest request = HttpRequest.newBuilder() +// .uri(new URI(fnoUrl)) +// .GET() +// .build(); +// HttpResponse response = client.send(request, +// HttpResponse.BodyHandlers.ofString()); +// +// CsvSchema bootstrap = CsvSchema.emptySchema().withHeader().withColumnSeparator('|'); +// CsvMapper csvMapper = new CsvMapper(); +// MappingIterator mappingIterator = +// csvMapper.readerFor(KotakInstrument.class) +// .with(bootstrap) +// .readValues(response.body()); +// var fnoInstruments = mappingIterator.readAll(); +// kotakInstruments.addAll(fnoInstruments); +// +// // Cash instruments +// request = HttpRequest.newBuilder() +// .uri(new URI(cashUrl)) +// .GET() +// .build(); +// response = client.send(request, HttpResponse.BodyHandlers.ofString()); +// mappingIterator = csvMapper.readerFor(KotakInstrument.class) +// .with(bootstrap) +// .readValues(response.body()); +//// var cashInstruments = mappingIterator.readAll(); +//// kotakInstruments.addAll(cashInstruments); +// +// log.info("Refreshed the instruments. Total count is {}", kotakInstruments.size()); +// } +// +// @Override +// public double getBalance() { +// return kotakApiClient.getMargin(); +// } +// +// @Override +// public boolean isLoggedIn() { +// try { +// getBalance(); +// return true; +// } catch (Throwable t) { +// log.warn("Getting balance failed with exception. Concluding that the broker is not +// logged in", t); +// return false; +// } +// } +// +// @Override +// public String placeOrder(String tradingSymbol, double quantity, String transactionType, String +// tag, double limitPrice) { +// var zerodhaInstrument = getInstruments().stream() +// .filter(instrument -> instrument.tradingsymbol.equals(tradingSymbol)) +// .findFirst() +// .get(); +// var kotakInstrument = kotakInstruments.stream() +// .filter(instrument -> +// instrument.getExchangeToken().equals(String.valueOf(zerodhaInstrument.getExchange_token()))) +// .findFirst() +// .get(); +// log.info("Zerodha tradingSymbol - {}; Kotak instrumrntToken - {}", tradingSymbol, +// kotakInstrument.getInstrumentToken()); +// // Place Kotak order +// var placeOrderPayload = KotakPlaceOrderPayload.builder() +// .instrumentToken(Integer.parseInt(kotakInstrument.getInstrumentToken())) +// .transactionType(transactionType) +// .quantity((int) quantity) +// .price(limitPrice) // limit order +// .product("MIS") +// .validity("GFD") +// .variety("REGULAR") +// .disclosedQuantity(0) +// .triggerPrice(0) +// .tag(tag) +// .build(); +// return kotakApiClient.placeOrder(placeOrderPayload); +// } +// +// @Override +// public OrderDetail getOrderDetail(String orderId) { +// log.info("Getting details for orderId, {}", orderId); +// while (true) { +// try { +// var orders = kotakApiClient.getOrderDetails(orderId); +// var finalOrder = orders.get(orders.size() - 1); +// return OrderDetail.builder() +// .orderId(String.valueOf(finalOrder.getOrderId())) +// .openTimestamp(new DateTime(finalOrder.getOrderTimestamp())) +// .avgPrice(finalOrder.getPrice()) +// .status(fromStatus(finalOrder.getStatus())) +// .build(); +// } catch (Exception e) { +// log.error("Error while checking order, {}. Checking the order details again", +// orderId, e); +// } +// } +// } +// +// protected BrokerOrderStatus fromStatus(String status) { +// BrokerOrderStatus orderStatus; +// switch (status) { +// case "TRAD": +// orderStatus = BrokerOrderStatus.COMPLETE; +// break; +// case "CAN": +// orderStatus = BrokerOrderStatus.CANCELLED; +// break; +//// case "REJECTED": +//// orderStatus = BrokerOrderStatus.REJECTED; +//// break; +// default: +// orderStatus = BrokerOrderStatus.OPEN; +// break; +// } +// return orderStatus; +// } +// +// @Override +// public void cancelOrder(String orderId) { +// if (orderId.equals(DUMMY_TRADE)) return; +// log.info("Cancelling order, {}", orderId); +// kotakApiClient.cancelOrder(orderId); +// } +// +// @Override +// public double pollUntilOrderCancel(String orderId) { +// if (orderId.equals(DUMMY_TRADE)) return -1; +// while (true) { +// log.info("Polling to verify cancel order status for orderId, {}", orderId); +// try { +// var orders = kotakApiClient.getOrderDetails(orderId); +// if (orders.size() == 0) { +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// // ignore +// } +// continue; +// } +// var finalOrder = orders.get(orders.size() - 1); +// if (fromStatus(finalOrder.getStatus()) == BrokerOrderStatus.CANCELLED) { +// return finalOrder.getPrice(); +// } +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// // ignore +// } +// } catch (Exception e) { +// throw new RuntimeException("Error while checking order, " + orderId, e); +// } +// } +// } +// +// @Override +// public double pollUntilOrderCompletion(String orderId) { +// if (orderId.equals(DUMMY_TRADE)) return -1; +// while (true) { +// log.info("Polling to verify order status for orderId, {}", orderId); +// try { +// var orders = kotakApiClient.getOrderDetails(orderId); +// if (orders.size() == 0) { +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// // ignore +// } +// continue; +// } +// var finalOrder = orders.get(orders.size() - 1); +// if (fromStatus(finalOrder.getStatus()) == BrokerOrderStatus.COMPLETE) { +// return finalOrder.getPrice(); +// } +// try { +// Thread.sleep(100); +// } catch (InterruptedException e) { +// // ignore +// } +// } catch (Exception e) { +// throw new RuntimeException("Error while checking order, " + orderId, e); +// } +// } +// } +// } diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrument.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrument.java new file mode 100644 index 00000000000..6189092d9eb --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrument.java @@ -0,0 +1,27 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.Date; +import lombok.Data; + +@Data +public class KotakInstrument { + private String instrumentToken; + private String instrumentName; + private String name; + private double lastPrice; + + @JsonFormat(pattern = "ddMMMyy", timezone = "Asia/Kolkata") + private Date expiry; + + private double strike; + private double tickSize; + private int lotSize; + private String instrumentType; + private String segment; + private String exchange; + private String isin; + private String multiplier; + private String exchangeToken; + private String optionType; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrumentLinkResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrumentLinkResponse.java new file mode 100644 index 00000000000..21cf87aea26 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakInstrumentLinkResponse.java @@ -0,0 +1,9 @@ +package io.quantum.trading.brokers.indian.kotak; + +import lombok.Data; + +@Data +public class KotakInstrumentLinkResponse { + private String cash; + private String fno; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginPayload.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginPayload.java new file mode 100644 index 00000000000..678344ad869 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginPayload.java @@ -0,0 +1,9 @@ +package io.quantum.trading.brokers.indian.kotak; + +import lombok.Data; + +@Data +public class KotakLoginPayload { + private String userid; + private String password; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginResponse.java new file mode 100644 index 00000000000..c76e73f5f1d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakLoginResponse.java @@ -0,0 +1,22 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class KotakLoginResponse { + @SerializedName("Status") + private String status; + + private String userid; + private String accessCodeTime; + private int authLevel; + private String biometric; + private String clientType; + private String emailId; + private String message; + private String mpin; + private int nomineeFlag; + private String oneTimeToken; + private String phoneNo; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakOrderDetailsResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakOrderDetailsResponse.java new file mode 100644 index 00000000000..0a8004a036d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakOrderDetailsResponse.java @@ -0,0 +1,11 @@ +package io.quantum.trading.brokers.indian.kotak; + +import lombok.Data; + +@Data +public class KotakOrderDetailsResponse { + private String orderTimestamp; + private long orderId; + private double price; + private String status; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderPayload.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderPayload.java new file mode 100644 index 00000000000..88aec4d5ed1 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderPayload.java @@ -0,0 +1,23 @@ +package io.quantum.trading.brokers.indian.kotak; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder(toBuilder = true) +@AllArgsConstructor +@NoArgsConstructor +@Data +public class KotakPlaceOrderPayload { + private int instrumentToken; + private String transactionType; + private int quantity; + private double price; // limit order + private String product; // NORMAL,MIS + private String validity; // GFD,IOC + private String variety; // REGULAR + private int disclosedQuantity; + private double triggerPrice; + private String tag; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderResponse.java new file mode 100644 index 00000000000..9d49d96f95b --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakPlaceOrderResponse.java @@ -0,0 +1,19 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class KotakPlaceOrderResponse { + @SerializedName("NSE") + private ExchangeResponse exchangeResponse; + + @Data + public static class ExchangeResponse { + private String message; + private long orderId; + private double price; + private int quantity; + private String tag; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakTokenResponse.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakTokenResponse.java new file mode 100644 index 00000000000..ed896815a42 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/kotak/KotakTokenResponse.java @@ -0,0 +1,21 @@ +package io.quantum.trading.brokers.indian.kotak; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class KotakTokenResponse { + @SerializedName("access_token") + private String accessToken; + + @SerializedName("refresh_token") + private String refreshToken; + + private String scope; + + @SerializedName("token_type") + private String tokenType; + + @SerializedName("expires_in") + private int expiresIn; +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenApiJava.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenApiJava.java new file mode 100644 index 00000000000..5ffc7d3b05d --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenApiJava.java @@ -0,0 +1,236 @@ +package io.quantum.trading.brokers.indian.prostocks; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.boot.json.GsonJsonParser; + +@Slf4j +public class NorenApiJava { + String _host; + NorenRequests _api; + private String _userid; + private String _actid; + private String _key; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final GsonJsonParser GSON_JSON_PARSER = new GsonJsonParser(); + + public NorenApiJava(String host) { + this._host = host; + this._api = new NorenRequests(host); + } + + public String login( + String userid, + String password, + String twoFA, + String vendor_code, + String api_secret, + String imei) { + String url = this._api.routes.get("authorize"); + JSONObject jsonObject = new JSONObject(); + String passwordsha = this._api.sha256(password); + String appkey = userid + "|" + api_secret; + String appkeysha = this._api.sha256(appkey); + jsonObject.put("source", "API"); + jsonObject.put("apkversion", "1.0.0"); + jsonObject.put("uid", userid); + jsonObject.put("pwd", passwordsha); + jsonObject.put("factor2", twoFA); + jsonObject.put("vc", vendor_code); + jsonObject.put("appkey", appkeysha); + jsonObject.put("imei", imei); + String response = this._api.post(url, jsonObject); + JSONObject jsonResp = new JSONObject(response); + String stat = jsonResp.getString("stat").toString(); + if ("Ok".equals(stat)) { + this._userid = userid; + this._actid = userid; + this._key = jsonResp.getString("susertoken").toString(); + } + + return response; + } + + public JSONObject search(String exchange, String searchtext) { + String url = this._api.routes.get("searchscrip"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("exch", exchange); + jsonObject.put("stext", this._api.encodeValue(searchtext)); + String response = this._api.post(url, this._key, jsonObject); + JSONObject jsonResp = new JSONObject(response); + return jsonResp; + } + + public JSONArray get_order_book() { + String url = this._api.routes.get("orderbook"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + String response = this._api.post(url, this._key, jsonObject); + System.out.println(response); + if (response.charAt(0) == '[') { + JSONArray jsonResp = new JSONArray(response); + return jsonResp; + } else { + return null; + } + } + + public JSONArray get_order_history(String norenorderno) { + String url = this._api.routes.get("singleorderhistory"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("norenordno", norenorderno); + String response = this._api.post(url, this._key, jsonObject); + log.info("Get order history response - {}", response); + if (response.charAt(0) == '[') { + response = removeDuplicates(response); + JSONArray jsonResp = new JSONArray(response); + return jsonResp; + } else { + return null; + } + } + + @SneakyThrows + public static String removeDuplicates(String inputJson) { + var list = GSON_JSON_PARSER.parseList(inputJson); + return OBJECT_MAPPER.writeValueAsString(list); + } + + public JSONArray get_trade_book() { + String url = this._api.routes.get("tradebook"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("actid", this._actid); + String response = this._api.post(url, this._key, jsonObject); + System.out.println(response); + if (response.charAt(0) == '[') { + JSONArray jsonResp = new JSONArray(response); + return jsonResp; + } else { + return null; + } + } + + public JSONObject get_quotes(String exchange, String token) { + String url = this._api.routes.get("getquotes"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("exch", exchange); + jsonObject.put("token", token); + + String response = this._api.post(url, this._key, jsonObject); + JSONObject jsonResp = new JSONObject(response); + return jsonResp; + } + + public JSONObject place_order( + String buy_or_sell, + String product_type, + String exchange, + String tradingsymbol, + Integer quantity, + Integer discloseqty, + String price_type, + Double price, + String remarks, + Double trigger_price, + String retention, + String amo, + Double bookloss_price, + Double bookprofit_price, + Double trail_price) { + String url = this._api.routes.get("placeorder"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("ordersource", "API"); + jsonObject.put("actid", this._actid); + jsonObject.put("trantype", buy_or_sell); + jsonObject.put("prd", product_type); + jsonObject.put("exch", exchange); + jsonObject.put("tsym", this._api.encodeValue(tradingsymbol)); + jsonObject.put("qty", Integer.toString(quantity)); + jsonObject.put("dscqty", Integer.toString(discloseqty)); + jsonObject.put("prctyp", price_type); + jsonObject.put("prc", Double.toString(price)); + if (null != trigger_price) { + jsonObject.put("trgprc", Double.toString(trigger_price)); + } + + if (null == retention) { + retention = "DAY"; + } + + jsonObject.put("ret", retention); + jsonObject.put("remarks", remarks); + if (null != amo) { + jsonObject.put("amo", amo); + } + + String response = this._api.post(url, this._key, jsonObject); + JSONObject jsonResp = new JSONObject(response); + return jsonResp; + } + + public JSONObject modify_order( + String orderno, + String exchange, + String tradingsymbol, + Integer newquantity, + String newprice_type, + Double newprice, + Double newtrigger_price, + Double bookloss_price, + Double bookprofit_price, + Double trail_price) { + String url = this._api.routes.get("modifyorder"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("ordersource", "API"); + jsonObject.put("actid", this._actid); + jsonObject.put("norenordno", orderno); + jsonObject.put("exch", exchange); + jsonObject.put("tsym", this._api.encodeValue(tradingsymbol)); + jsonObject.put("qty", Integer.toString(newquantity)); + jsonObject.put("prctyp", newprice_type); + jsonObject.put("prc", Double.toString(newprice)); + if (newprice_type.equals("SL-LMT") || "SL-MKT".equals(newprice_type)) { + if (newtrigger_price == null) { + return null; + } + + jsonObject.put("trgprc", Double.toString(newtrigger_price)); + } + + if (newtrigger_price != null) { + jsonObject.put("blprc", Double.toString(bookloss_price)); + } + + if (trail_price != null) { + jsonObject.put("trailprc", Double.toString(trail_price)); + } + + if (bookprofit_price != null) { + jsonObject.put("bpprc", Double.toString(bookprofit_price)); + } + + String response = this._api.post(url, this._key, jsonObject); + JSONObject jsonResp = new JSONObject(response); + return jsonResp; + } + + public JSONObject cancel_order(String orderno) { + String url = this._api.routes.get("cancelorder"); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("uid", this._userid); + jsonObject.put("ordersource", "API"); + jsonObject.put("norenordno", orderno); + String response = this._api.post(url, this._key, jsonObject); + JSONObject jsonResp = new JSONObject(response); + return jsonResp; + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRequests.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRequests.java new file mode 100644 index 00000000000..e584f89ecfe --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRequests.java @@ -0,0 +1,100 @@ +package io.quantum.trading.brokers.indian.prostocks; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Request.Builder; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.json.JSONObject; + +public class NorenRequests { + OkHttpClient client = new OkHttpClient(); + NorenRoutes routes = new NorenRoutes(); + public static final MediaType JSON = MediaType.get("application/json; charset=utf-8"); + + public NorenRequests(String host) { + NorenRoutes._host = host; + } + + public String encodeValue(String value) { + try { + return URLEncoder.encode(value, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException var3) { + return var3.toString(); + } + } + + private static String bytesToHex(byte[] hash) { + StringBuilder hexString = new StringBuilder(2 * hash.length); + + for (int i = 0; i < hash.length; ++i) { + String hex = Integer.toHexString(255 & hash[i]); + if (hex.length() == 1) { + hexString.append('0'); + } + + hexString.append(hex); + } + + return hexString.toString(); + } + + String sha256(String input) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] encodedhash = digest.digest(input.getBytes(StandardCharsets.UTF_8)); + return bytesToHex(encodedhash); + } catch (NoSuchAlgorithmException var4) { + return input; + } + } + + String run(String url) { + try { + Request request = (new Builder()).url(url).build(); + Response response = this.client.newCall(request).execute(); + return response.body().string(); + } catch (IOException var4) { + var4.printStackTrace(); + return var4.toString(); + } + } + + String post(String url, JSONObject jsnObj) { + String json = "jData=" + jsnObj.toString(); + System.out.println(url + " " + json); + RequestBody body = RequestBody.create(json, JSON); + Request request = (new Builder()).url(url).post(body).build(); + + try { + Response response = this.client.newCall(request).execute(); + return response.body().string(); + } catch (IOException var7) { + var7.printStackTrace(); + return var7.toString(); + } + } + + String post(String url, String key, JSONObject jsnObj) { + String var10000 = jsnObj.toString(); + String json = "jData=" + var10000 + "&jKey=" + key; + System.out.println(url + " " + json); + RequestBody body = RequestBody.create(json, JSON); + Request request = (new Builder()).url(url).post(body).build(); + + try { + Response response = this.client.newCall(request).execute(); + return response.body().string(); + } catch (IOException var8) { + var8.printStackTrace(); + return var8.toString(); + } + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRoutes.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRoutes.java new file mode 100644 index 00000000000..3d6c2fd2aea --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/NorenRoutes.java @@ -0,0 +1,30 @@ +package io.quantum.trading.brokers.indian.prostocks; + +import java.util.HashMap; +import java.util.Map; + +public class NorenRoutes { + public Map routes = + new HashMap() { + { + this.put("authorize", "/QuickAuth"); + this.put("logout", "/Logout"); + this.put("searchscrip", "/SearchScrip"); + this.put("orderbook", "/OrderBook"); + this.put("tradebook", "/TradeBook"); + this.put("placeorder", "/PlaceOrder"); + this.put("modifyorder", "/ModifyOrder"); + this.put("cancelorder", "/CancelOrder"); + this.put("getquotes", "/GetQuotes"); + this.put("singleorderhistory", "/SingleOrdHist"); + } + }; + public static String _host = "http://kurma.kambala.co.in:9959/NorenWClient/"; + + public NorenRoutes() {} + + public String get(String key) { + String var10000 = _host; + return var10000 + (String) this.routes.get(key); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/ProStocksBroker.java b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/ProStocksBroker.java new file mode 100644 index 00000000000..94188f89486 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/brokers/indian/prostocks/ProStocksBroker.java @@ -0,0 +1,116 @@ +// package io.quantum.trading.brokers.prostocks; +// +// import com.noren.javaapi.NorenApiJava; +// import io.quantum.trading.brokers.BrokerException; +// import io.quantum.trading.brokers.OrderDetail; +// import io.quantum.trading.brokers.zerodha.ZerodhaBroker; +// import io.quantum.trading.entities.BrokerConfigDetails; +// import io.quantum.trading.util.ThreadFactoryHelper; +// import lombok.extern.slf4j.Slf4j; +// import org.joda.time.DateTime; +// import org.joda.time.format.DateTimeFormat; +// import org.joda.time.format.DateTimeFormatter; +// import org.json.JSONObject; +// +// import java.text.SimpleDateFormat; +// import java.util.concurrent.Executors; +// import java.util.concurrent.ScheduledExecutorService; +// import java.util.concurrent.TimeUnit; +// +// @Slf4j +// public class ProStocksBroker extends ZerodhaBroker { +// private static NorenApiJava norenClient; +// private static String identifier = "Dotconn-Java-SDK"; +// +// private static final ScheduledExecutorService LOGIN_POOL = +// Executors.newSingleThreadScheduledExecutor(new ThreadFactoryHelper("prostocks-login-")); +// +// private BrokerConfigDetails.ProStocksBrokerConfigDetails brokerConfigDetails; +// +// public ProStocksBroker(BrokerConfigDetails.ProStocksBrokerConfigDetails +// proStocksBrokerConfigDetails, +// BrokerConfigDetails.ZerodhaBrokerConfigDetails zerodhaBrokerConfigDetails, boolean +// dryRun) { +// super(zerodhaBrokerConfigDetails, dryRun); +// this.brokerConfigDetails = proStocksBrokerConfigDetails; +// +// norenClient = new NorenApiJava(proStocksBrokerConfigDetails.getBrokerUrl()); +// this.login(); +// LOGIN_POOL.scheduleAtFixedRate(this::login, 10, 10, TimeUnit.MINUTES); +// } +// +// private void login() { +// var res = norenClient.login(brokerConfigDetails.getUserId(), +// brokerConfigDetails.getPassword(), +// brokerConfigDetails.getTwoFactor(), brokerConfigDetails.getVendorCode(), +// brokerConfigDetails.getApiKey(), +// identifier); +// var resObj = new JSONObject(res); +// if (!resObj.getString("stat").equalsIgnoreCase("ok")) { +// throw new RuntimeException("Login failed response: " + res); +// } +// } +// +// private String getProStocksTradingSymbol(String tradingSymbol) { +// var ins = getInstruments().stream() +// .filter(instrument -> instrument.getTradingsymbol().equals(tradingSymbol)) +// .findFirst().get(); +// var sf = new SimpleDateFormat("ddMMMyy"); +// var type = ins.getTradingsymbol().endsWith("PE") ? "P" : "C" ; +// var tsym = ""; +// if (ins.getInstrument_type().equals("EQ")) { +// tsym = tradingSymbol + "-EQ"; +// } else { +// tsym = ins.getName() + sf.format(ins.getExpiry()) + type + ins.getStrike(); +// } +// log.info("Matched Prostock's tsym {} for Zerodha's trading symbol {}", tsym.toUpperCase(), +// tradingSymbol); +// return tsym.toUpperCase(); +// } +// +// @Override +// public String placeOrder(String tradingSymbol, double quantity, String transactionType, String +// tag, double limitPrice) { +// var tempTransactionType = transactionType.equals("BUY") ? "B" : "S"; +// try { +// tradingSymbol = getProStocksTradingSymbol(tradingSymbol); +// log.info("Placing order for tradingSymbol - {}; transactionType - {}; quantity - {}; +// tag - {}", +// tradingSymbol, tempTransactionType, quantity, tag); +// var res = norenClient.place_order(tempTransactionType, "I", "NFO", tradingSymbol, +// (int) quantity, (int) quantity, "MKT", 0.0, tag, null, null, +// null, null, null, null); +// if (!res.getString("stat").equalsIgnoreCase("ok")) { +// throw new RuntimeException("Placing order failed response: " + res); +// } +// return res.getString("norenordno"); +// } catch (Exception e) { +// throw new BrokerException("Error while placing order", e); +// } +// +// } +// +// @Override +// public OrderDetail getOrderDetail(String orderId) { +// while (true) { +// try { +// var orderHistory = norenClient.get_order_history(orderId); +// var finalOrder = (JSONObject) orderHistory.get(0); +// log.info("Order History - {}", orderHistory); +// log.info("Final Order Detail - {}", finalOrder); +// DateTimeFormatter dtf = DateTimeFormat.forPattern("HH:mm:ss dd-MM-yyyy"); +// var avgPrice = finalOrder.has("avgprc") ? +// Double.parseDouble(finalOrder.getString("avgprc")) : -1; +// return OrderDetail.builder() +// .orderId(finalOrder.getString("norenordno")) +// .openTimestamp(dtf.parseDateTime(finalOrder.getString("norentm"))) +// .avgPrice(avgPrice) +// .status(fromStatus(finalOrder.getString("status"))) +// .build(); +// } catch (Throwable e) { +// log.error("Error while checking order, {}. Checking the order details again", +// orderId, e); +// } +// } +// } +// } diff --git a/brokers/src/main/java/io/quantum/trading/provider/DataProvidable.java b/brokers/src/main/java/io/quantum/trading/provider/DataProvidable.java new file mode 100644 index 00000000000..f87927ed3e4 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/provider/DataProvidable.java @@ -0,0 +1,38 @@ +package io.quantum.trading.provider; + +import io.quantum.trading.brokers.LiveTick; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +public interface DataProvidable { + Set WATCH_LIST = new HashSet<>(); + String WATCH_LIST_LOCK = "WATCH_LIST_LOCK"; + + default void addToWatchList(String exchangeScript) { + synchronized (WATCH_LIST_LOCK) { + WATCH_LIST.add(exchangeScript); + subscribe(exchangeScript); + } + } + + default void removeFromWatchList(String exchangeScript) { + synchronized (WATCH_LIST_LOCK) { + WATCH_LIST.remove(exchangeScript); + unSubscribe(exchangeScript); + } + } + + void startFeed(Function, Void> liveTickConsumer); + + void disconnectFeed(); + + void subscribe(String script); + + void unSubscribe(String script); + + default void reconnectFeed() { + WATCH_LIST.forEach(this::subscribe); + } +} diff --git a/brokers/src/main/java/io/quantum/trading/provider/ForexMetadataProvidable.java b/brokers/src/main/java/io/quantum/trading/provider/ForexMetadataProvidable.java new file mode 100644 index 00000000000..0b0b619a071 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/provider/ForexMetadataProvidable.java @@ -0,0 +1,10 @@ +package io.quantum.trading.provider; + +import io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5.SymbolInfo; +import java.util.List; + +public interface ForexMetadataProvidable { + List getSymbols(); + + String getGroupName(String symbol); +} diff --git a/brokers/src/main/java/io/quantum/trading/provider/HistoricalDataProvidable.java b/brokers/src/main/java/io/quantum/trading/provider/HistoricalDataProvidable.java new file mode 100644 index 00000000000..23d24072d8a --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/provider/HistoricalDataProvidable.java @@ -0,0 +1,12 @@ +package io.quantum.trading.provider; + +import io.quantum.trading.entities.NseScriptMetadata; +import io.quantum.trading.enums.TimeFrame; +import io.quantum.trading.patterns.Candle; +import java.util.List; +import org.joda.time.DateTime; + +public interface HistoricalDataProvidable { + List getHistoricalDataFromProvider( + NseScriptMetadata nseScriptMetadata, DateTime from, DateTime to, TimeFrame timeFrame); +} diff --git a/brokers/src/main/java/io/quantum/trading/provider/NseMetadataProvidable.java b/brokers/src/main/java/io/quantum/trading/provider/NseMetadataProvidable.java new file mode 100644 index 00000000000..e46ca43bb25 --- /dev/null +++ b/brokers/src/main/java/io/quantum/trading/provider/NseMetadataProvidable.java @@ -0,0 +1,8 @@ +package io.quantum.trading.provider; + +import io.quantum.trading.entities.NseScriptMetadata; +import java.util.List; + +public interface NseMetadataProvidable { + List getMetadata(); +} From 0b460f735b587095429916bea2a89e1f41697979 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 19:48:18 +0530 Subject: [PATCH 02/47] sync.yml --- .github/workflows/main.yml | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000000..223f4c624e4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,51 @@ +name: Sync from GitLab and Create Pull Request + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to sync from GitLab' + required: true + default: 'master' + schedule: + - cron: '0 12 * * *' # Runs every day at 12:00 UTC + +jobs: + sync: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + # Step 1: Checkout GitHub repository + - name: Checkout GitHub repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 # Required to pull all history and branches + token: ${{ secrets.GITHUB_TOKEN }} + + # Step 2: Set Git identity + - name: Set git identity + run: |- + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + # Step 3: Pull code from GitLab repository and check for changes + - id: find_mutations + name: Find mutations + run: |- + mkdir -p gitlab-repo + cd gitlab-repo + git init + git remote add origin https://oauth2:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/root/mt5-java-api.git + git fetch origin + git pull origin master + ls + rm -rf .git + rm -rf .github + rm -rf META-INF + rm -rf .gitlab-ci.yml + rm -rf README.md + rm pg.txt + cd .. # Navigate back to the root directory From 3a957ff2eb79376fb4b41597073a46855f7dfe9a Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 19:50:25 +0530 Subject: [PATCH 03/47] Update main.yml --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 223f4c624e4..312986f21a5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,3 +49,6 @@ jobs: rm -rf README.md rm pg.txt cd .. # Navigate back to the root directory + git add gitlab-repo/** + git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT + git clean -df From 2c96f535804a9b3d96eacd68d9edf36bdb2688d3 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 19:52:48 +0530 Subject: [PATCH 04/47] Update main.yml --- .github/workflows/main.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 312986f21a5..1b59f0d7391 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,3 +52,24 @@ jobs: git add gitlab-repo/** git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT git clean -df + + # Step 4: Create Pull Request if changes are found + - name: Create Pull Request in GitHub + if: ${{ steps.find_mutations.outputs.patch_created == 'true' }} + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + gitlab-repo/** + commit-message: |- + feat: Adding/Updating gitlab-files + [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + branch: test + title: "feat: Add a new commited files from GitLap" + body: |- + This pull request is created automatically by GitHub Actions to sync changes from GitLab. + + [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + author: github-actions + committer: github-actions + signoff: true From 2d1c5e1b8d662ab9d6e6778124493c44b4c61102 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:07:53 +0530 Subject: [PATCH 05/47] Update main.yml --- .github/workflows/main.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1b59f0d7391..223f4c624e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,27 +49,3 @@ jobs: rm -rf README.md rm pg.txt cd .. # Navigate back to the root directory - git add gitlab-repo/** - git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT - git clean -df - - # Step 4: Create Pull Request if changes are found - - name: Create Pull Request in GitHub - if: ${{ steps.find_mutations.outputs.patch_created == 'true' }} - uses: peter-evans/create-pull-request@v5 - with: - token: ${{ secrets.GITHUB_TOKEN }} - add-paths: | - gitlab-repo/** - commit-message: |- - feat: Adding/Updating gitlab-files - [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - branch: test - title: "feat: Add a new commited files from GitLap" - body: |- - This pull request is created automatically by GitHub Actions to sync changes from GitLab. - - [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - author: github-actions - committer: github-actions - signoff: true From fc976a8199a732bbe6dff11c9b357f6334dfca8d Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:12:10 +0530 Subject: [PATCH 06/47] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 223f4c624e4..41af2ba6e42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ on: branch: description: 'Branch to sync from GitLab' required: true - default: 'master' + default: 'main' schedule: - cron: '0 12 * * *' # Runs every day at 12:00 UTC @@ -40,7 +40,7 @@ jobs: git init git remote add origin https://oauth2:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - git pull origin master + git pull origin main ls rm -rf .git rm -rf .github From 899af8315f221c343cde8ee6fd28952e9c721439 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:15:11 +0530 Subject: [PATCH 07/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41af2ba6e42..e3b5f491bc5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ on: branch: description: 'Branch to sync from GitLab' required: true - default: 'main' + default: 'master' schedule: - cron: '0 12 * * *' # Runs every day at 12:00 UTC From 7d380386419f929f0ecf1cf13dac37670e7b9382 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:31:02 +0530 Subject: [PATCH 08/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3b5f491bc5..41af2ba6e42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ on: branch: description: 'Branch to sync from GitLab' required: true - default: 'master' + default: 'main' schedule: - cron: '0 12 * * *' # Runs every day at 12:00 UTC From 1ac44f38e059fc53d6336274c23686097d76ca2b Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:43:54 +0530 Subject: [PATCH 09/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41af2ba6e42..844db7f2118 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://oauth2:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/root/mt5-java-api.git + git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main ls From ab20721dd705860fffb305a85835db4911efb2a7 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:48:41 +0530 Subject: [PATCH 10/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 844db7f2118..3a7b7e99fb4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git + git remote add origin https://oauth2:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/gtskaushik/mt5-java-api.git git fetch origin git pull origin main ls From f52a3e6d632e087464554c407e3082b95dc36dc2 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:50:38 +0530 Subject: [PATCH 11/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3a7b7e99fb4..f77f97ef548 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://oauth2:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/gtskaushik/mt5-java-api.git + git remote add origin http://git.mtapi.be:88/root/mt5-java-api.git git fetch origin git pull origin main ls From c61976f0d3503b3a4001f3e2ec8b7bf43329c260 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 20:52:40 +0530 Subject: [PATCH 12/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f77f97ef548..a25aca8ec82 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin http://git.mtapi.be:88/root/mt5-java-api.git + git remote add origin https://git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main ls From 3a5ad5a2a70b164c1c7bc263fa14afa144ab23fc Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:00:43 +0530 Subject: [PATCH 13/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a25aca8ec82..8bd1dc91ce0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://git.mtapi.io/root/mt5-java-api.git + git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main ls From ae98f119535fe58ace6e5b29f6889a4d098335a9 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:14:55 +0530 Subject: [PATCH 14/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8bd1dc91ce0..14c7f63ac26 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_SYNC_TOKEN }}@git.mtapi.io/root/mt5-java-api.git + git remote add origin https://gitlab-ci-token:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main ls From 689fe710159bccfa17828b6fe0b453deb39a590f Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:16:11 +0530 Subject: [PATCH 15/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14c7f63ac26..953739a2720 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://gitlab-ci-token:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git + git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main ls From 3ad9dc7b30519a2ca735898ee138398f3cfc2b82 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:25:56 +0530 Subject: [PATCH 16/47] Update main.yml --- .github/workflows/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 953739a2720..2539e9b4ff9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,13 @@ jobs: git config user.name "github-actions" git config user.email "github-actions@github.com" - # Step 3: Pull code from GitLab repository and check for changes + - name: Setup SSH + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -t rsa git.mtapi.io >> ~/.ssh/known_hosts + - id: find_mutations name: Find mutations run: |- From fa960432ce3827b9488abf0b765c96c3040473b0 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:28:27 +0530 Subject: [PATCH 17/47] Update main.yml --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2539e9b4ff9..24e2dabee9a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,20 +31,22 @@ jobs: git config user.name "github-actions" git config user.email "github-actions@github.com" + # Step 3: Setup SSH - name: Setup SSH - run: | + run: |- mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -t rsa git.mtapi.io >> ~/.ssh/known_hosts + # Step 4: Sync from GitLab - id: find_mutations name: Find mutations run: |- mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git + git remote add origin git@git.mtapi.io:root/mt5-java-api.git git fetch origin git pull origin main ls From c893141e4718259f9c0e6c7ed2a0d52c2e385a34 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:36:04 +0530 Subject: [PATCH 18/47] Update main.yml --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 24e2dabee9a..457d98b8a1e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,10 +35,12 @@ jobs: - name: Setup SSH run: |- mkdir -p ~/.ssh - echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa + echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -t rsa git.mtapi.io >> ~/.ssh/known_hosts - + - name: Test SSH Connection + run: | + ssh -T git@git.mtapi.io # Step 4: Sync from GitLab - id: find_mutations name: Find mutations From 2423ab8ffe8623210a8b00c15f53d55c42d44880 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 21:38:10 +0530 Subject: [PATCH 19/47] Update main.yml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 457d98b8a1e..d78af572ea2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,6 +38,7 @@ jobs: echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -t rsa git.mtapi.io >> ~/.ssh/known_hosts + - name: Test SSH Connection run: | ssh -T git@git.mtapi.io From 1628297290d207e31542a8bf8def33873d9f623c Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 22:22:29 +0530 Subject: [PATCH 20/47] Update main.yml --- .github/workflows/main.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d78af572ea2..af960bc0658 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,26 +30,13 @@ jobs: run: |- git config user.name "github-actions" git config user.email "github-actions@github.com" - - # Step 3: Setup SSH - - name: Setup SSH - run: |- - mkdir -p ~/.ssh - echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ssh-keyscan -t rsa git.mtapi.io >> ~/.ssh/known_hosts - - - name: Test SSH Connection - run: | - ssh -T git@git.mtapi.io - # Step 4: Sync from GitLab - id: find_mutations name: Find mutations run: |- mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin git@git.mtapi.io:root/mt5-java-api.git + git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@gitlab.com/gtskaushik/mt5-java-api.git git fetch origin git pull origin main ls From c72fcea3385f106def06bf8eb3fd92b80f3bf260 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 11 Sep 2024 22:24:46 +0530 Subject: [PATCH 21/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index af960bc0658..fe1a2c49dcf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@gitlab.com/gtskaushik/mt5-java-api.git + git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/gtskaushik/mt5-java-api.git git fetch origin git pull origin main ls From 7217134b33139e463b37db19e0b217b0c9cb2ea3 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:22:07 +0530 Subject: [PATCH 22/47] Update main.yml --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fe1a2c49dcf..bfa0ccd2c7a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,9 +36,9 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://oauth2:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/gtskaushik/mt5-java-api.git - git fetch origin - git pull origin main + git remote add origin https://git.mtapi.io/root/mt5-java-api.git + git fetch origin https://{{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git + git pull origin https://{{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git main ls rm -rf .git rm -rf .github From cda0b14615626c25821d7ceb9f113d5ea2de422d Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:23:40 +0530 Subject: [PATCH 23/47] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bfa0ccd2c7a..1616c472544 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,8 +37,8 @@ jobs: cd gitlab-repo git init git remote add origin https://git.mtapi.io/root/mt5-java-api.git - git fetch origin https://{{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git - git pull origin https://{{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git main + git fetch origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git + git pull origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git main ls rm -rf .git rm -rf .github From eb30ac8858a738a5ab08e66f287175f4de870411 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:29:59 +0530 Subject: [PATCH 24/47] Update main.yml --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1616c472544..0174f4b661e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,9 +37,8 @@ jobs: cd gitlab-repo git init git remote add origin https://git.mtapi.io/root/mt5-java-api.git - git fetch origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git - git pull origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}git.mtapi.io/root/mt5-java-api.git main - ls + git fetch origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git + git pull https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git main ls rm -rf .git rm -rf .github rm -rf META-INF From 3730734c36b29d4f38e1712e5d4a2a20abb55171 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:42:32 +0530 Subject: [PATCH 25/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0174f4b661e..46ffb7ff6f9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -38,7 +38,7 @@ jobs: git init git remote add origin https://git.mtapi.io/root/mt5-java-api.git git fetch origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git - git pull https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git main ls + git pull https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git main rm -rf .git rm -rf .github rm -rf META-INF From 1b063138905ef531ffbf2502671974add4f062ca Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:49:14 +0530 Subject: [PATCH 26/47] Update main.yml --- .github/workflows/main.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 46ffb7ff6f9..77f8e6056eb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,12 +37,8 @@ jobs: cd gitlab-repo git init git remote add origin https://git.mtapi.io/root/mt5-java-api.git - git fetch origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git - git pull https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git main - rm -rf .git - rm -rf .github - rm -rf META-INF - rm -rf .gitlab-ci.yml - rm -rf README.md - rm pg.txt - cd .. # Navigate back to the root directory + git fetch origin + git pull origin main + env: + GIT_USERNAME: ${{ secrets.GITLAB_USERNAME }} + GIT_PASSWORD: ${{ secrets.GITLAB_PASSWORD }} From 606c19e25ed8e746ab8f3d7fc4b32c165081c2cf Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:52:27 +0530 Subject: [PATCH 27/47] Update main.yml --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 77f8e6056eb..da9dadd7ad6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,8 @@ jobs: run: |- git config user.name "github-actions" git config user.email "github-actions@github.com" + git config --global credential.helper store + echo "https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git" > ~/.git-credentials - id: find_mutations name: Find mutations run: |- From 7cb6bb210c4c667ddd0900e2ea54e5dde839efe8 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:53:58 +0530 Subject: [PATCH 28/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index da9dadd7ad6..42da58d4656 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,7 @@ jobs: git config user.name "github-actions" git config user.email "github-actions@github.com" git config --global credential.helper store - echo "https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_PASSWORD }}@git.mtapi.io/root/mt5-java-api.git" > ~/.git-credentials + echo "https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git" > ~/.git-credentials - id: find_mutations name: Find mutations run: |- From 478714662ae3ea6141f1ded5ff54541bd34d0516 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 09:56:35 +0530 Subject: [PATCH 29/47] Update main.yml --- .github/workflows/main.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 42da58d4656..32b1c867c6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,8 +30,7 @@ jobs: run: |- git config user.name "github-actions" git config user.email "github-actions@github.com" - git config --global credential.helper store - echo "https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git" > ~/.git-credentials + - id: find_mutations name: Find mutations run: |- @@ -43,4 +42,4 @@ jobs: git pull origin main env: GIT_USERNAME: ${{ secrets.GITLAB_USERNAME }} - GIT_PASSWORD: ${{ secrets.GITLAB_PASSWORD }} + GIT_PASSWORD: ${{ secrets.GITLAB_TOKEN }} From 59197b2fc9607ae5a2ec234d6f6c44fe01f4fc8b Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:00:04 +0530 Subject: [PATCH 30/47] Update main.yml --- .github/workflows/main.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32b1c867c6f..eebc3158a9e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,8 @@ jobs: git init git remote add origin https://git.mtapi.io/root/mt5-java-api.git git fetch origin + gtskaushik + DotConn@1234 git pull origin main - env: - GIT_USERNAME: ${{ secrets.GITLAB_USERNAME }} - GIT_PASSWORD: ${{ secrets.GITLAB_TOKEN }} + gtskaushik + DotConn@1234 From 3374ffaaf59ee8f2257d73e3f744fab8537a65d9 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:16:49 +0530 Subject: [PATCH 31/47] Update main.yml --- .github/workflows/main.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index eebc3158a9e..094a604de49 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -37,10 +37,6 @@ jobs: mkdir -p gitlab-repo cd gitlab-repo git init - git remote add origin https://git.mtapi.io/root/mt5-java-api.git + git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - gtskaushik - DotConn@1234 git pull origin main - gtskaushik - DotConn@1234 From 71def3f0d0c589d4acd6d317867127ce1c0f2ac7 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:22:06 +0530 Subject: [PATCH 32/47] Update main.yml --- .github/workflows/main.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 094a604de49..1b7065ae5af 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,3 +40,11 @@ jobs: git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin git pull origin main + ls + rm -rf .git + rm -rf .github + rm -rf META-INF + rm -rf .gitlab-ci.yml + rm -rf README.md + rm -rf pg.txt + cd .. # Navigate back to the root directory From f15c342425e39ae739b5f13ee4e4909d87ffb4e0 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:28:06 +0530 Subject: [PATCH 33/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1b7065ae5af..c9525b0a58c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,7 @@ jobs: git init git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - git pull origin main + git pull origin master ls rm -rf .git rm -rf .github From a87ca58690eb3a225a0dc98fd1ff1a2c5a5e001b Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:30:35 +0530 Subject: [PATCH 34/47] Update main.yml --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9525b0a58c..40690033f8d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,8 @@ jobs: git init git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - git pull origin master + git branch -M main + git pull -u origin master ls rm -rf .git rm -rf .github From f7f1ed0e1e1f9418d237ea3b19935aa57e43eb56 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:39:25 +0530 Subject: [PATCH 35/47] Update main.yml --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 40690033f8d..c9525b0a58c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,8 +39,7 @@ jobs: git init git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - git branch -M main - git pull -u origin master + git pull origin master ls rm -rf .git rm -rf .github From 8936525fda67b66757daef433127e4503318b768 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 10:40:28 +0530 Subject: [PATCH 36/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c9525b0a58c..1b7065ae5af 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,7 +39,7 @@ jobs: git init git remote add origin https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mtapi.io/root/mt5-java-api.git git fetch origin - git pull origin master + git pull origin main ls rm -rf .git rm -rf .github From 0a3f34c1cd8f1f7e41be7298ea1b1ce4df1ce4e1 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:37:36 +0530 Subject: [PATCH 37/47] Update main.yml --- .github/workflows/main.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1b7065ae5af..6406dcccc46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,4 +47,28 @@ jobs: rm -rf .gitlab-ci.yml rm -rf README.md rm -rf pg.txt - cd .. # Navigate back to the root directory + grep -rl 'mtapi.mt5' . + grep -rl 'mtapi.mt5' . | xargs sed -i 's/mtapi.mt5/io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5/g' + git add . + git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT + git clean -df + # Step 4: Create Pull Request if changes are found + - name: Create Pull Request in GitHub + if: ${{ steps.find_mutations.outputs.patch_created == 'true' }} + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + add-paths: | + /my-app/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/** + commit-message: |- + feat: Adding/Updating gitlab-files + [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + branch: gitlab-sync + title: "feat: Add a new commited files from GitLap" + body: |- + This pull request is created automatically by GitHub Actions to sync changes from GitLab. + + [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + author: github-actions + committer: github-actions + signoff: true From 1e048e47fb2e172b27ffb2ba57a59ce5f3a08247 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:39:17 +0530 Subject: [PATCH 38/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6406dcccc46..89be448a9b4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,7 +59,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | - /my-app/brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/** + brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/** commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} From d184e13503c24f13514747de722697850d88afc0 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:44:47 +0530 Subject: [PATCH 39/47] Update main.yml From 63bcceee4b5097f75d38c645ee62a2c9be6f5944 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:46:07 +0530 Subject: [PATCH 40/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 89be448a9b4..be7f3ffcec1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,7 +49,7 @@ jobs: rm -rf pg.txt grep -rl 'mtapi.mt5' . grep -rl 'mtapi.mt5' . | xargs sed -i 's/mtapi.mt5/io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5/g' - git add . + git add gitlab-repo/** git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT git clean -df # Step 4: Create Pull Request if changes are found From fd6021fab32610a849d89d1639a0d327a763b9de Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:48:00 +0530 Subject: [PATCH 41/47] Update main.yml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be7f3ffcec1..048d1a55e0c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,6 +49,7 @@ jobs: rm -rf pg.txt grep -rl 'mtapi.mt5' . grep -rl 'mtapi.mt5' . | xargs sed -i 's/mtapi.mt5/io.quantum.trading.brokers.forex.mt5protocol.mtapi.mt5/g' + cd .. git add gitlab-repo/** git diff --staged --patch --exit-code > .repo.patch || echo "patch_created=true" >> $GITHUB_OUTPUT git clean -df From 3750e38ecd4361d76d07f815167343058582e28c Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:56:25 +0530 Subject: [PATCH 42/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 048d1a55e0c..3745eeadd84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,7 +64,7 @@ jobs: commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - branch: gitlab-sync + branch: sathish title: "feat: Add a new commited files from GitLap" body: |- This pull request is created automatically by GitHub Actions to sync changes from GitLab. From 632d10d34b378fd07816c9d94105381b4d3c51fc Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 11:59:02 +0530 Subject: [PATCH 43/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3745eeadd84..f6bdc97f417 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | - brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/** + gitlab-repo/** commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} From 34f375edefeef54431a3cbec0cea3118d913acf4 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 12:06:45 +0530 Subject: [PATCH 44/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f6bdc97f417..26e78521185 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | - gitlab-repo/** + brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} From 560b99d81f17d01549930f5bd3d57eb3b5c1b4b5 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 12:08:38 +0530 Subject: [PATCH 45/47] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 26e78521185..3745eeadd84 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} add-paths: | - brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol + brokers/src/main/java/io/quantum/trading/brokers/forex/mt5protocol/** commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} From e0627610a6148bea5663b5463fcce80e00b79bc5 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Thu, 12 Sep 2024 12:25:58 +0530 Subject: [PATCH 46/47] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3745eeadd84..20316ba926e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,8 +64,8 @@ jobs: commit-message: |- feat: Adding/Updating gitlab-files [Workflow Run]: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - branch: sathish - title: "feat: Add a new commited files from GitLap" + branch: kumar + title: "feat: Add a new commited files from GitLab" body: |- This pull request is created automatically by GitHub Actions to sync changes from GitLab. From 2d0ba079fc5eb39c6dd7a7dd2e3cd4c54e675b68 Mon Sep 17 00:00:00 2001 From: sathishadhirav Date: Wed, 25 Sep 2024 17:39:48 +0530 Subject: [PATCH 47/47] Create merge.yml --- .github/workflows/merge.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/merge.yml diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 00000000000..8647f32eebd --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,18 @@ +name: Build and Create Docker Image on Merge + +on: + pull_request: + types: [closed] + branches: + - main + +jobs: + build: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2