internal int PlaceBuyOrder(double price, double amount, int orderToCancel = -1) { long amountXrpDrops = (long) Math.Round(amount * Const.DROPS_IN_NATIVE); double amountFiat = price * amount; int feeDrops = _lastPaidActionHadLowFee ? _feeProvider.EscalatedFeeInDrops : _feeProvider.BaseFeeInDrops; var command = new CreateBuyOrderRequest { tx_json = new CrOR_TxJson(_walletAddress, feeDrops) { TakerGets = new Take { currency = _fiatCurreny, value = amountFiat.ToString("0.00000"), issuer = _issuerAddress }, TakerPays = amountXrpDrops.ToString() } }; if (-1 < orderToCancel) { command.tx_json.OfferSequence = orderToCancel.ToString(); } _txSigner.Sign(command); ErrorResponse error = null; var delay = RETRY_DELAY; for (int i = 1; i <= RETRY_COUNT; i++) { string data = sendToRippleNet(Helpers.SerializeJson(command)); if (null == data) { return -1; } error = Helpers.DeserializeJSON<ErrorResponse>(data); if (!String.IsNullOrEmpty(error.error)) { _logger.Error("Error creating BUY order. Mesage=" + error.error_message); if (!error.IsCritical) { //The request might have been successfull even if server says there were problems _logger.Warning("Retry in " + delay + " ms..."); delay *= 2; Thread.Sleep(delay); continue; } throw new Exception(error.error + " " + error.error_message); } var response = Helpers.DeserializeJSON<NewOrderResponse>(data); if (ResponseKind.FatalError == response.result.ResponseKind) //TODO: this checking logic is to generic to be in 'PlaceBuyOrder'. { var message = String.Format("Error creating BUY order. Response={0} {1}", response.result.engine_result, response.result.engine_result_message); _logger.Warning(message); throw new Exception(message); } if (ResponseKind.NonCriticalError == response.result.ResponseKind) { _logger.Warning("Non-fatal error creating BUY order. Message=" + response.result.engine_result_message); _logger.Warning("Retry in " + delay + " ms..."); delay *= 2; Thread.Sleep(delay); continue; } if (ResponseKind.OldSequenceNumber == response.result.ResponseKind) { int expectedSeqNum = GetNextSequence(); _txSigner.UpdateSequenceNumber(expectedSeqNum); _logger.Warning(String.Format("Response: {0}. Synchronizing to suggested SeqNum {1}", response.result.engine_result_message, expectedSeqNum)); return orderToCancel; } if (ResponseKind.QueuedForLaterExecution == response.result.ResponseKind) { double feeXrp = (double)feeDrops / Const.DROPS_IN_NATIVE; _logger.Warning("Buy order queued due to high server load: " + response.result.engine_result_message + " (Fee was " + feeXrp + " XRP)"); _lastPaidActionHadLowFee = true; } else { _lastPaidActionHadLowFee = false; } return response.result.tx_json.Sequence; } throw new Exception(String.Format("Socket request failed {0} times in a row with error '{1}'. Giving up.", RETRY_COUNT, error.error_message)); }
/// <summary> /// Execute a market order to buy given amount of XRP. Used when XRP balance falls under reserver to get back /// to operational state /// </summary> /// <param name="xrpAmount">XRP amount to buy instantly</param> /// <returns>True for success, False in case of fail</returns> internal bool ExchangeXrp(double xrpAmount) { long amountXrpDrops = (long)Math.Round(xrpAmount * Const.DROPS_IN_NATIVE); var command = new CreateBuyOrderRequest { tx_json = new CrOR_TxJson(_walletAddress, _feeProvider.EscalatedFeeInDrops) { Flags = 262144, //tfFillOrKill TakerGets = new Take { currency = _fiatCurreny, value = "999", //No asset is so priceless to cost more that 999 XRP, right? issuer = _issuerAddress }, TakerPays = amountXrpDrops.ToString() } }; _txSigner.Sign(command); string data = sendToRippleNet(Helpers.SerializeJson(command)); if (null == data) { _logger.Warning("Server returned no data as a response to market buy order"); return false; } var response = Helpers.DeserializeJSON<NewOrderResponse>(data); if (ResponseKind.OldSequenceNumber == response.result.ResponseKind) { int expectedSeqNum = GetNextSequence(); _txSigner.UpdateSequenceNumber(expectedSeqNum); _logger.Warning(String.Format("Response: {0}. Synchronizing to suggested SeqNum {1}", response.result.engine_result_message, expectedSeqNum)); return false; } if (ResponseKind.Success != response.result.ResponseKind) { var message = String.Format("Response for market order to buy {0:0.0000} XRP not OK. Message: {1}", xrpAmount, response.result.engine_result_message); _logger.Warning(message); return false; } return true; }