예제 #1
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * {
             * "result": true,
             * "orders": [
             * {
             * "amount": 0.1,
             * "avg_price": 0,
             * "create_date": 1418008467000,
             * "deal_amount": 0,
             * "order_id": 10000591,
             * "orders_id": 10000591,
             * "price": 500,
             * "status": 0,
             * "symbol": "btc_usd",
             * "type": "sell"
             * },
             *
             *
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["amount"].ConvertInvariant <decimal>(),
                AmountFilled = token["deal_amount"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                AveragePrice = token["avg_price"].ConvertInvariant <decimal>(),
                IsBuy        = token["type"].ToStringInvariant().StartsWith("buy"),
                OrderDate    = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["create_date"].ConvertInvariant <long>()),
                OrderId      = token["order_id"].ToStringInvariant(),
                Symbol       = token["symbol"].ToStringInvariant(),
                Result       = ParseOrderStatus(token["status"].ConvertInvariant <int>()),
            };

            return(result);
        }
        protected override async Task <ExchangeOrderResult> OnGetOrderDetailsAsync(string orderId, string marketSymbol = null)
        {
            Dictionary <string, object> payload = await GetNoncePayloadAsync();

            if (string.IsNullOrEmpty(marketSymbol))
            {
                throw new InvalidOperationException("Binance single order details request requires symbol");
            }
            payload["symbol"]  = marketSymbol;
            payload["orderId"] = orderId;
            JToken token = await MakeJsonRequestAsync <JToken>("/order", BaseUrlPrivate, payload);

            ExchangeOrderResult result = ParseOrder(token);

            // Add up the fees from each trade in the order
            Dictionary <string, object> feesPayload = await GetNoncePayloadAsync();

            feesPayload["symbol"] = marketSymbol;
            JToken feesToken = await MakeJsonRequestAsync <JToken>("/myTrades", BaseUrlPrivate, feesPayload);

            ParseFees(feesToken, result);

            return(result);
        }
        private ExchangeOrderResult ParseClientOrder(JToken token)
        {
            //  "data": [{"id": 4910,"currencyPair": "BTC/USD","goodUntilTime": 0,"type": "MARKET_SELL","orderStatus": "EXECUTED","issueTime": 1409920636701,"price": null,"quantity": 2.85714285,"remainingQuantity": 0,"commission": null,"commissionRate": 0.005, "lastModificationTime": 1409920636701 }, .. ]
            ExchangeOrderResult order = new ExchangeOrderResult()
            {
                OrderId   = token["id"].ToStringInvariant(),
                Symbol    = token["currencyPair"].ToStringInvariant(),
                OrderDate = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["issueTime"].ConvertInvariant <long>()),
                IsBuy     = token["type"].ToStringInvariant().Contains("BUY"),
                Price     = token["price"].ConvertInvariant <decimal>(),
                Amount    = token["quantity"].ConvertInvariant <decimal>(),
                Fees      = token["commission"].ConvertInvariant <decimal>(),
            };

            order.AmountFilled = order.Amount - token["remainingQuantity"].ConvertInvariant <decimal>();
            switch (token["status"].ToStringInvariant())
            {
            case "CANCELLED": order.Result = ExchangeAPIOrderResult.Canceled; break;
            }



            return(order);
        }
예제 #4
0
        private ExchangeOrderResult ParseFill(JToken result)
        {
            decimal amount = result["size"].ConvertInvariant <decimal>();
            decimal price  = result["price"].ConvertInvariant <decimal>();
            string  symbol = result["product_id"].ToStringInvariant();

            decimal fees = result["fee"].ConvertInvariant <decimal>();

            ExchangeOrderResult order = new ExchangeOrderResult
            {
                TradeId      = result["trade_id"].ToStringInvariant(),
                Amount       = amount,
                AmountFilled = amount,
                Price        = price,
                Fees         = fees,
                AveragePrice = price,
                IsBuy        = (result["side"].ToStringInvariant() == "buy"),
                OrderDate    = result["created_at"].ToDateTimeInvariant(),
                MarketSymbol = symbol,
                OrderId      = result["order_id"].ToStringInvariant(),
            };

            return(order);
        }
예제 #5
0
        protected override async Task <ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrderRequest order)
        {
            var payload = GetNoncePayload();

            payload.Add("method", "Trade");
            payload.Add("pair", order.Symbol);
            payload.Add("type", order.IsBuy ? "buy" : "sell");
            payload.Add("rate", order.Price);
            payload.Add("amount", order.Amount);
            // "return":{"received":0.1,"remains":0,"order_id":12345,"funds":{"btc":15,"ltc":51.82,	"nvc":0, ... }}
            JToken token = await MakeJsonRequestAsync <JToken>("/", PrivateURL, payload, "POST");

            token = CheckError(token);
            ExchangeOrderResult result = new ExchangeOrderResult()
            {
                OrderId      = token["order_id"].ToStringInvariant(),
                OrderDate    = DateTime.UtcNow,                     // since they don't pass it back
                AmountFilled = token["received"].ConvertInvariant <decimal>(),
            };

            result.Amount = token["remains"].ConvertInvariant <decimal>() + result.AmountFilled;
            if (result.Amount == result.AmountFilled)
            {
                result.Result = ExchangeAPIOrderResult.Filled;
            }
            else if (result.AmountFilled == 0m)
            {
                result.Result = ExchangeAPIOrderResult.Pending;
            }
            else
            {
                result.Result = ExchangeAPIOrderResult.FilledPartially;
            }

            return(result);
        }
예제 #6
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            ExchangeOrderResult order        = new ExchangeOrderResult();
            decimal             amount       = token.Value <decimal>("Quantity");
            decimal             remaining    = token.Value <decimal>("QuantityRemaining");
            decimal             amountFilled = amount - remaining;

            order.Amount       = amount;
            order.AmountFilled = amountFilled;
            order.AveragePrice = token["PricePerUnit"] == null ? token["Price"].Value <decimal>() : token.Value <decimal>("PricePerUnit");
            order.Message      = string.Empty;
            order.OrderId      = token.Value <string>("OrderUuid");
            order.Result       = (amountFilled == amount ? ExchangeAPIOrderResult.Filled : (amountFilled == 0 ? ExchangeAPIOrderResult.Pending : ExchangeAPIOrderResult.FilledPartially));
            order.OrderDate    = token["Opened"] == null ? token["TimeStamp"].Value <DateTime>() : token["Opened"].Value <DateTime>();
            order.Symbol       = token["Exchange"].Value <string>();
            string type = (string)token["OrderType"];

            if (string.IsNullOrWhiteSpace(type))
            {
                type = (string)token["Type"] ?? string.Empty;
            }
            order.IsBuy = type.IndexOf("BUY", StringComparison.OrdinalIgnoreCase) >= 0;
            return(order);
        }
예제 #7
0
        public override ExchangeOrderResult PlaceOrder(ExchangeOrderRequest order)
        {
            string symbol = NormalizeSymbol(order.Symbol);
            Dictionary <string, object> payload = new Dictionary <string, object>(StringComparer.OrdinalIgnoreCase)
            {
                { "pair", symbol },
                { "type", (order.IsBuy ? "buy" : "sell") },
                { "ordertype", "limit" },
                { "price", order.Price.ToString(CultureInfo.InvariantCulture.NumberFormat) },
                { "volume", order.RoundAmount().ToString(CultureInfo.InvariantCulture.NumberFormat) },
                { "nonce", GenerateNonce() }
            };

            JObject             obj    = MakeJsonRequest <JObject>("/0/private/AddOrder", null, payload);
            JToken              token  = CheckError(obj);
            ExchangeOrderResult result = new ExchangeOrderResult();

            result.OrderDate = DateTime.UtcNow;
            if (token["txid"] is JArray array)
            {
                result.OrderId = (string)array[0];
            }
            return(result);
        }
예제 #8
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * "symbol": "IOTABTC",
             * "orderId": 1,
             * "clientOrderId": "12345",
             * "transactTime": 1510629334993,
             * "price": "1.00000000",
             * "origQty": "1.00000000",
             * "executedQty": "0.00000000",
             * "status": "NEW",
             * "timeInForce": "GTC",
             * "type": "LIMIT",
             * "side": "SELL",
             * "fills": [
             *    {
             *      "price": "4000.00000000",
             *      "qty": "1.00000000",
             *      "commission": "4.00000000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3999.00000000",
             *      "qty": "5.00000000",
             *      "commission": "19.99500000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3998.00000000",
             *      "qty": "2.00000000",
             *      "commission": "7.99600000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3997.00000000",
             *      "qty": "1.00000000",
             *      "commission": "3.99700000",
             *      "commissionAsset": "USDT"
             *    },
             *    {
             *      "price": "3995.00000000",
             *      "qty": "1.00000000",
             *      "commission": "3.99500000",
             *      "commissionAsset": "USDT"
             *    }
             *  ]
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["origQty"].ConvertInvariant <decimal>(),
                AmountFilled = token["executedQty"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                IsBuy        = token["side"].ToStringInvariant() == "BUY",
                OrderDate    = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(token["time"].ConvertInvariant <long>(token["transactTime"].ConvertInvariant <long>())),
                OrderId      = token["orderId"].ToStringInvariant(),
                MarketSymbol = token["symbol"].ToStringInvariant()
            };

            switch (token["status"].ToStringInvariant())
            {
            case "NEW":
                result.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "PARTIALLY_FILLED":
                result.Result = ExchangeAPIOrderResult.FilledPartially;
                break;

            case "FILLED":
                result.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "CANCELED":
            case "PENDING_CANCEL":
            case "EXPIRED":
            case "REJECTED":
                result.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                result.Result = ExchangeAPIOrderResult.Error;
                break;
            }

            ParseAveragePriceAndFeesFromFills(result, token["fills"]);

            return(result);
        }
        protected override async Task <IEnumerable <ExchangeOrderResult> > OnGetCompletedOrderDetailsAsync(string marketSymbol = null, DateTime?afterDate = null)
        {
            // TODO: Bitstamp bug: bad request if url contains symbol, so temporarily using url for all symbols
            // string url = string.IsNullOrWhiteSpace(symbol) ? "/user_transactions/" : "/user_transactions/" + symbol;
            string url     = "/user_transactions/";
            var    payload = await GetNoncePayloadAsync();

            payload["limit"] = 1000;
            JToken result = await MakeJsonRequestAsync <JToken>(url, null, payload, "POST");

            List <ExchangeOrderResult> orders = new List <ExchangeOrderResult>();

            foreach (var transaction in result as JArray)
            {
                int type = transaction["type"].ConvertInvariant <int>();
                // only type 2 is order transaction type, so we discard all other transactions
                if (type != 2)
                {
                    continue;
                }

                string tradingPair = ((JObject)transaction).Properties().FirstOrDefault(p =>
                                                                                        !p.Name.Equals("order_id", StringComparison.InvariantCultureIgnoreCase) &&
                                                                                        p.Name.Contains("_"))?.Name.Replace("_", "-");
                if (!string.IsNullOrWhiteSpace(tradingPair) && !string.IsNullOrWhiteSpace(marketSymbol) && !NormalizeMarketSymbol(tradingPair).Equals(marketSymbol))
                {
                    continue;
                }

                var quoteCurrency = tradingPair.Trim().Substring(tradingPair.Length - 3).ToLowerInvariant();
                var baseCurrency  = tradingPair.Trim().ToLowerInvariant().Replace(quoteCurrency, "").Replace("-", "").Replace("_", "");

                decimal             resultBaseCurrency = transaction[baseCurrency].ConvertInvariant <decimal>();
                ExchangeOrderResult order = new ExchangeOrderResult()
                {
                    OrderId      = transaction["order_id"].ToStringInvariant(),
                    IsBuy        = resultBaseCurrency > 0,
                    Fees         = transaction["fee"].ConvertInvariant <decimal>(),
                    FeesCurrency = quoteCurrency.ToStringUpperInvariant(),
                    MarketSymbol = NormalizeMarketSymbol(tradingPair),
                    OrderDate    = transaction["datetime"].ToDateTimeInvariant(),
                    AmountFilled = Math.Abs(resultBaseCurrency),
                    AveragePrice = transaction[$"{baseCurrency}_{quoteCurrency}"].ConvertInvariant <decimal>()
                };
                orders.Add(order);
            }
            // at this point one transaction transformed into one order, we need to consolidate parts into order
            // group by order id
            var groupings = orders.GroupBy(o => o.OrderId);
            List <ExchangeOrderResult> orders2 = new List <ExchangeOrderResult>();

            foreach (var group in groupings)
            {
                decimal             spentQuoteCurrency = group.Sum(o => o.AveragePrice * o.AmountFilled);
                ExchangeOrderResult order = group.First();
                order.AmountFilled = group.Sum(o => o.AmountFilled);
                order.AveragePrice = spentQuoteCurrency / order.AmountFilled;
                order.Price        = order.AveragePrice;
                orders2.Add(order);
            }

            return(orders2);
        }
예제 #10
0
        /// <summary>
        /// Place a limit order by first querying the order book and then placing the order for a threshold below the bid or above the ask that would fully fulfill the amount.
        /// The order book is scanned until an amount of bids or asks that will fulfill the order is found and then the order is placed at the lowest bid or highest ask price multiplied
        /// by priceThreshold.
        /// </summary>
        /// <param name="symbol">Symbol to sell</param>
        /// <param name="amount">Amount to sell</param>
        /// <param name="isBuy">True for buy, false for sell</param>
        /// <param name="orderBookCount">Amount of bids/asks to request in the order book</param>
        /// <param name="priceThreshold">Threshold below the lowest bid or above the highest ask to set the limit order price at. For buys, this is converted to 1 / priceThreshold.
        /// This can be set to 0 if you want to set the price like a market order.</param>
        /// <param name="thresholdToAbort">If the lowest bid/highest ask price divided by the highest bid/lowest ask price is below this threshold, throw an exception.
        /// This ensures that your order does not buy or sell at an extreme margin.</param>
        /// <param name="abortIfOrderBookTooSmall">Whether to abort if the order book does not have enough bids or ask amounts to fulfill the order.</param>
        /// <returns>Order result</returns>
        public virtual async Task <ExchangeOrderResult> PlaceSafeMarketOrderAsync(string symbol, decimal amount, bool isBuy, int orderBookCount = 100, decimal priceThreshold = 0.9m,
                                                                                  decimal thresholdToAbort = 0.75m, bool abortIfOrderBookTooSmall = false)
        {
            if (priceThreshold > 0.9m)
            {
                throw new APIException("You cannot specify a price threshold above 0.9m, otherwise there is a chance your order will never be fulfilled. For buys, this is " +
                                       "converted to 1.0m / priceThreshold, so always specify the value below 0.9m");
            }
            else if (priceThreshold <= 0m)
            {
                priceThreshold = 1m;
            }
            else if (isBuy && priceThreshold > 0m)
            {
                priceThreshold = 1.0m / priceThreshold;
            }
            ExchangeOrderBook book = await GetOrderBookAsync(symbol, orderBookCount);

            if (book == null || (isBuy && book.Asks.Count == 0) || (!isBuy && book.Bids.Count == 0))
            {
                throw new APIException($"Error getting order book for {symbol}");
            }
            decimal counter   = 0m;
            decimal highPrice = decimal.MinValue;
            decimal lowPrice  = decimal.MaxValue;

            if (isBuy)
            {
                foreach (ExchangeOrderPrice ask in book.Asks.Values)
                {
                    counter  += ask.Amount;
                    highPrice = Math.Max(highPrice, ask.Price);
                    lowPrice  = Math.Min(lowPrice, ask.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }
            else
            {
                foreach (ExchangeOrderPrice bid in book.Bids.Values)
                {
                    counter  += bid.Amount;
                    highPrice = Math.Max(highPrice, bid.Price);
                    lowPrice  = Math.Min(lowPrice, bid.Price);
                    if (counter >= amount)
                    {
                        break;
                    }
                }
            }
            if (abortIfOrderBookTooSmall && counter < amount)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell") } order for {symbol} and amount {amount} cannot be fulfilled because the order book is too thin.");
            }
            else if (lowPrice / highPrice < thresholdToAbort)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} would place for a price below threshold of {thresholdToAbort}, aborting.");
            }
            ExchangeOrderRequest request = new ExchangeOrderRequest
            {
                Amount            = amount,
                OrderType         = OrderType.Limit,
                Price             = CryptoUtility.RoundAmount((isBuy ? highPrice : lowPrice) * priceThreshold),
                ShouldRoundAmount = true,
                Symbol            = symbol
            };
            ExchangeOrderResult result = await PlaceOrderAsync(request);

            // wait about 10 seconds until the order is fulfilled
            int       i        = 0;
            const int maxTries = 20; // 500 ms for each try

            for (; i < maxTries; i++)
            {
                await System.Threading.Tasks.Task.Delay(500);

                result = await GetOrderDetailsAsync(result.OrderId, symbol);

                switch (result.Result)
                {
                case ExchangeAPIOrderResult.Filled:
                case ExchangeAPIOrderResult.Canceled:
                case ExchangeAPIOrderResult.Error:
                    break;
                }
            }

            if (i == maxTries)
            {
                throw new APIException($"{(isBuy ? "Buy" : "Sell")} order for {symbol} and amount {amount} timed out and may not have been fulfilled");
            }

            return(result);
        }
예제 #11
0
        // DONE
        protected override async Task <ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrderRequest order)
        {
            // In Aquanow market order, when buying crypto the amount of crypto that is bought is the receiveQuantity
            // and when selling the amount of crypto that is sold is the deliverQuantity
            string amountParameter = order.IsBuy ? "receiveQuantity" : "deliverQuantity";
            string amountReceived  = order.IsBuy ? "deliverQuantity" : "receiveQuantity";
            string feesCurrency    = order.IsBuy ? order.MarketSymbol.Substring(0, order.MarketSymbol.IndexOf('-')) : order.MarketSymbol.Substring(order.MarketSymbol.IndexOf('-') + 1);
            var    payload         = await GetNoncePayloadAsync();

            payload["ticker"]        = order.MarketSymbol;
            payload["tradeSide"]     = order.IsBuy ? "buy" : "sell";
            payload[amountParameter] = order.Amount;
            order.ExtraParameters.CopyTo(payload);
            JToken token = await MakeJsonRequestAsync <JToken>("/trades/v1/market", null, payload, "POST");

            var orderDetailsPayload = await GetNoncePayloadAsync();

            //{
            //   "type": "marketOrderSubmitAck",
            //   "payload": {
            //     "orderId": "cfXXXXXX-56ce-4df8-9f1e-729e87bf54d8",
            //     "receiveCurrency": "BTC",
            //     "receiveQuantity": 0.00004,
            //     "deliverCurrency": "USD",
            //     "deliverQuantity": 0.369124,
            //     "fee": 0.000001
            //   }
            //}



            JToken result = await MakeJsonRequestAsync <JToken>($"/trades/v1/order?orderId={token["payload"]["orderId"].ToStringInvariant()}", null, orderDetailsPayload, "GET");

            // {
            //   "priceArrival": 9223.5,
            //   "orderId": "24cf77ad-7e93-44d7-86f8-b9d9a046b008",
            //   "remainingQtyBase": 0,
            //   "tradeSize": 0.0004,
            //   "exchangeOrderId": "-",
            //   "tradePriceAvg": 9223.5,
            //   "fillPct": 100,
            //   "finalizeReturnedQtyBase": 0,
            //   "tradeSide": "buy",
            //   "exchangeClientOrderId": "-",
            //   "tradeTime": 1594681810754,
            //   "childOrderCount": 0,
            //   "fillFeeQuote": 0,
            //   "itemDateTime": 1594681811719,
            //   "baseSymbol": "USD",
            //   "strategy": "MARKET",
            //   "fillQtyQuote": 0.0004,
            //   "usernameRef": "-",
            //   "fillQtyBase": 3.6894,
            //   "priceMarket": "-",
            //   "symbol": "BTC-USD",
            //   "tradeStatus": "COMPLETE",
            //   "commissionRate": 20,
            //   "createdAt": 1594681810756,
            //   "message": "-",
            //   "priceLimit": 9223.5,
            //   "quoteSymbol": "BTC",
            //   "remainingQtyQuote": 0,
            //   "orderIdParent": "24cf77ad-7e93-44d7-86f8-b9d9a046b008",
            //   "orderType": "parentOrder",
            //   "updatedAt": 1594681811941,
            //   "tradeDuration": 0,
            //   "username": "******",
            //   "fillFeeQuoteAqua": 0.0000001
            // }
            ExchangeOrderResult orderDetails = new ExchangeOrderResult
            {
                OrderId      = result["orderId"].ToStringInvariant(),
                AmountFilled = result["fillQtyQuote"].ToStringInvariant().ConvertInvariant <decimal>(),
                Amount       = payload[amountParameter].ConvertInvariant <decimal>(),
                OrderDate    = CryptoUtility.UnixTimeStampToDateTimeMilliseconds(result["tradeTime"].ConvertInvariant <double>()),
                Message      = result["message"].ToStringInvariant(),
                IsBuy        = order.IsBuy,
                Fees         = token["payload"]["fee"].ConvertInvariant <decimal>(),
                FeesCurrency = feesCurrency,
                MarketSymbol = order.MarketSymbol,
                Price        = result["priceArrival"].ToStringInvariant().ConvertInvariant <decimal>(),
            };

            switch (result["tradeStatus"].ToStringInvariant())
            {
            case "COMPLETE":
                orderDetails.AveragePrice = result["tradePriceAvg"].ToStringInvariant().ConvertInvariant <decimal>();
                orderDetails.Result       = ExchangeAPIOrderResult.Filled;
                break;

            default:
                orderDetails.Result = ExchangeAPIOrderResult.Error;
                break;
            }
            return(orderDetails);
        }
예제 #12
0
        /// <inheritdoc />
        protected async override Task <ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrderRequest order)
        {
            //{
            //	"market": "XRP-PERP",
            //  "side": "sell",
            //  "price": 0.306525,
            //  "type": "limit",
            //  "size": 31431.0,
            //  "reduceOnly": false,
            //  "ioc": false,
            //  "postOnly": false,
            //  "clientId": null
            //}

            IEnumerable <ExchangeMarket> markets = await OnGetMarketSymbolsMetadataAsync();

            ExchangeMarket market = markets.Where(m => m.MarketSymbol == order.MarketSymbol).First();

            var payload = await GetNoncePayloadAsync();

            var parameters = new Dictionary <string, object>(StringComparer.OrdinalIgnoreCase)
            {
                { "market", market.MarketSymbol },
                { "side", order.IsBuy ? "buy" : "sell" },
                { "type", order.OrderType.ToStringLowerInvariant() },
                { "size", order.RoundAmount() }
            };

            if (!string.IsNullOrEmpty(order.ClientOrderId))
            {
                parameters.Add("clientId", order.ClientOrderId);
            }

            if (order.IsPostOnly != null)
            {
                parameters.Add("postOnly", order.IsPostOnly);
            }

            if (order.OrderType != OrderType.Market)
            {
                int precision = BitConverter.GetBytes(decimal.GetBits((decimal)market.PriceStepSize)[3])[2];

                if (order.Price == null)
                {
                    throw new ArgumentNullException(nameof(order.Price));
                }

                parameters.Add("price", Math.Round(order.Price.Value, precision));
            }
            else
            {
                parameters.Add("price", null);
            }

            parameters.CopyTo(payload);

            order.ExtraParameters.CopyTo(payload);

            var response = await MakeJsonRequestAsync <JToken>("/orders", null, payload, "POST");

            ExchangeOrderResult result = new ExchangeOrderResult
            {
                OrderId       = response["id"].ToStringInvariant(),
                ClientOrderId = response["clientId"].ToStringInvariant(),
                OrderDate     = CryptoUtility.ToDateTimeInvariant(response["createdAt"]),
                Price         = CryptoUtility.ConvertInvariant <decimal>(response["price"]),
                AmountFilled  = CryptoUtility.ConvertInvariant <decimal>(response["filledSize"]),
                AveragePrice  = CryptoUtility.ConvertInvariant <decimal>(response["avgFillPrice"]),
                Amount        = CryptoUtility.ConvertInvariant <decimal>(response["size"]),
                MarketSymbol  = response["market"].ToStringInvariant(),
                IsBuy         = response["side"].ToStringInvariant() == "buy"
            };

            return(result);
        }
예제 #13
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * [
             * {
             * "orderID": "GTFTA-BTC-37058-1566969620678",
             * "userId": "5d49a9fb946e8c6a29dd3e0f",
             * "side": "sell",
             * "type": "limit",
             * "subType": "gtc",
             * "price": "0.0002000000",
             * "averagePrice": "0.0000000000",
             * "quantity": "1.000000",
             * "cumQuantity": "0.000000",
             * "symbol": "GTFTA/BTC",
             * "status": "new",
             * "created": "2019-08-28T05:20:20.678Z",
             * "updated": "2019-08-28T05:20:20.678Z",
             * "email": "*****@*****.**"
             * },
             * {
             * "orderID": "GTFTA-BTC-30015-1566918271314",
             * "userId": "5d49a9fb946e8c6a29dd3e0f",
             * "side": "sell",
             * "type": "limit",
             * "subType": "gtc",
             * "price": "0.0002000000",
             * "averagePrice": "0.0000000000",
             * "quantity": "1.000000",
             * "cumQuantity": "0.000000",
             * "symbol": "GTFTA/BTC",
             * "status": "new",
             * "created": "2019-08-27T15:04:31.315Z",
             * "updated": "2019-08-27T15:04:31.315Z",
             * "email": "*****@*****.**"
             * }
             * ]
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["quantity"].ConvertInvariant <decimal>(),
                AmountFilled = token["cumQuantity"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                AveragePrice = token["averagePrice"].ConvertInvariant <decimal>(),
                IsBuy        = token["side"].ToStringInvariant().ToUpper() == "BUY",
                OrderDate    = token["created"].ToDateTimeInvariant(),
                OrderId      = token["orderID"].ToStringInvariant(),
                MarketSymbol = token["symbol"].ToStringInvariant()
            };

            switch (token["status"].ToStringUpperInvariant())
            {
            case "NEW":
                result.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "PARTIALLY_FILLED":
                result.Result = ExchangeAPIOrderResult.FilledPartially;
                break;

            case "FILLED":
                result.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "CANCELED":
            case "CANCELLED":
            case "PENDING_CANCEL":
            case "EXPIRED":
            case "REJECTED":
                result.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                result.Result = ExchangeAPIOrderResult.Error;
                break;
            }

            //ParseAveragePriceAndFeesFromFills(result, token["fills"]);

            return(result);
        }
예제 #14
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * {[
             * {
             * "orderID": "b7b8518a-c0d8-028d-bb6e-d843f8f723a3",
             * "clOrdID": "",
             * "clOrdLinkID": "",
             * "account": 93592,
             * "symbol": "XBTUSD",
             * "side": "Buy",
             * "simpleOrderQty": null,
             * "orderQty": 1,
             * "price": 5500,
             * "displayQty": null,
             * "stopPx": null,
             * "pegOffsetValue": null,
             * "pegPriceType": "",
             * "currency": "USD",
             * "settlCurrency": "XBt",
             * "ordType": "Limit",
             * "timeInForce": "GoodTillCancel",
             * "execInst": "ParticipateDoNotInitiate",
             * "contingencyType": "",
             * "exDestination": "XBME",
             * "ordStatus": "Canceled",
             * "triggered": "",
             * "workingIndicator": false,
             * "ordRejReason": "",
             * "simpleLeavesQty": 0,
             * "leavesQty": 0,
             * "simpleCumQty": 0,
             * "cumQty": 0,
             * "avgPx": null,
             * "multiLegReportingType": "SingleSecurity",
             * "text": "Canceled: Canceled via API.\nSubmission from testnet.bitmex.com",
             * "transactTime": "2018-07-08T09:20:39.428Z",
             * "timestamp": "2018-07-08T11:35:05.334Z"
             * }
             * ]}
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["orderQty"].ConvertInvariant <decimal>(),
                AmountFilled = token["cumQty"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                IsBuy        = token["side"].ToStringInvariant().EqualsWithOption("Buy"),
                OrderDate    = token["transactTime"].ConvertInvariant <DateTime>(),
                OrderId      = token["orderID"].ToStringInvariant(),
                MarketSymbol = token["symbol"].ToStringInvariant()
            };

            // http://www.onixs.biz/fix-dictionary/5.0.SP2/tagNum_39.html
            switch (token["ordStatus"].ToStringInvariant())
            {
            case "New":
                result.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "PartiallyFilled":
                result.Result = ExchangeAPIOrderResult.FilledPartially;
                break;

            case "Filled":
                result.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "Canceled":
                result.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                result.Result = ExchangeAPIOrderResult.Error;
                break;
            }

            return(result);
        }
예제 #15
0
        private ExchangeOrderResult ParseOrder(JToken result)
        {
            decimal executedValue = result["executed_value"].ConvertInvariant <decimal>();
            decimal amountFilled  = result["filled_size"].ConvertInvariant <decimal>();
            decimal amount        = result["size"].ConvertInvariant <decimal>(amountFilled);
            decimal price         = result["price"].ConvertInvariant <decimal>();
            decimal averagePrice  = (amountFilled <= 0m ? 0m : executedValue / amountFilled);
            decimal fees          = result["fill_fees"].ConvertInvariant <decimal>();
            string  symbol        = result["id"].ToStringInvariant();

            ExchangeOrderResult order = new ExchangeOrderResult
            {
                Amount       = amount,
                AmountFilled = amountFilled,
                Price        = price,
                Fees         = fees,
                FeesCurrency = symbol.Substring(0, symbol.IndexOf('-')),
                AveragePrice = averagePrice,
                IsBuy        = (result["side"].ToStringInvariant() == "buy"),
                OrderDate    = ConvertDateTimeInvariant(result["created_at"]),
                Symbol       = symbol,
                OrderId      = result["id"].ToStringInvariant()
            };

            switch (result["status"].ToStringInvariant())
            {
            case "pending":
                order.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "active":
            case "open":
                if (order.Amount == order.AmountFilled)
                {
                    order.Result = ExchangeAPIOrderResult.Filled;
                }
                else if (order.AmountFilled > 0.0m)
                {
                    order.Result = ExchangeAPIOrderResult.FilledPartially;
                }
                else
                {
                    order.Result = ExchangeAPIOrderResult.Pending;
                }
                break;

            case "done":
            case "settled":
                order.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "cancelled":
            case "canceled":
                order.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                order.Result = ExchangeAPIOrderResult.Unknown;
                break;
            }
            return(order);
        }
예제 #16
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * {
             * "quantity": "160.00",
             * "timestamp": 1569305973395,
             * "avgPrice": "0",
             * "dealQuantity": "0",
             * "orderId": 3261550990000011862,
             * "price": "0.00020250",
             * "orderState": 1,
             * "orderSide": "sell",
             * "triggerPrice": null,
             * "priceGap": null,
             * "orderMode": 0,
             * "orderProperty": 0
             * }
             *
             */
            ExchangeOrderResult result = new ExchangeOrderResult
            {
                Amount       = token["quantity"].ConvertInvariant <decimal>(),
                AmountFilled = token["dealQuantity"].ConvertInvariant <decimal>(),
                Price        = token["price"].ConvertInvariant <decimal>(),
                AveragePrice = token["avgPrice"].ConvertInvariant <decimal>(),
                IsBuy        = token["orderSide"].ToStringInvariant().ToUpper() == "BUY",
                OrderDate    = CryptoUtility.ParseTimestamp(token["timestamp"], TimestampType.UnixMilliseconds),
                OrderId      = token["orderId"].ToStringInvariant(),
                //MarketSymbol = token["symbol"].ToStringInvariant()
            };

            switch (token["orderState"].ToStringUpperInvariant())
            {
            case "NEW":
            case "1":
                result.Result = ExchangeAPIOrderResult.Pending;
                break;

            case "PARTIALLY_FILLED":
            case "2":
                result.Result = ExchangeAPIOrderResult.FilledPartially;
                break;

            case "FILLED":
            case "9":
                result.Result = ExchangeAPIOrderResult.Filled;
                break;

            case "CANCELED":
            case "CANCELLED":
            case "PENDING_CANCEL":
            case "EXPIRED":
            case "REJECTED":
            case "19":
                result.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                result.Result = ExchangeAPIOrderResult.Error;
                break;
            }

            //ParseAveragePriceAndFeesFromFills(result, token["fills"]);

            return(result);
        }
        private ExchangeOrderResult ParseOrder(JToken result)
        {
            //result = JToken.Parse("{\"orderNumber\":31226040,\"resultingTrades\":[{\"amount\":\"338.8732\",\"date\":\"2014-10-18 23:03:21\",\"rate\":\"0.00000173\",\"total\":\"0.00058625\",\"tradeID\":\"16164\",\"type\":\"buy\"}]}");
            // open order: { "orderNumber": "45549304213", "type": "sell", "rate": "0.01000000", "startingAmount": "1497.74185318", "amount": "1497.74185318", "total": "14.97741853", "date": "2018-01-28 17:07:39", "margin": 0 }
            ExchangeOrderResult order = new ExchangeOrderResult
            {
                OrderId = result["orderNumber"].ToStringInvariant()
            };
            JToken trades = result["resultingTrades"];

            if (trades != null && trades.Children().Count() != 0)
            {
                int tradeCount = trades.Children().Count();
                if (tradeCount != 0m)
                {
                    foreach (JToken token in trades)
                    {
                        decimal tradeAmt  = token["amount"].ConvertInvariant <decimal>();
                        decimal tradeRate = token["rate"].ConvertInvariant <decimal>();

                        order.Amount       += tradeAmt;
                        order.AmountFilled  = order.Amount;
                        order.AveragePrice += tradeRate;
                        if (token["type"].ToStringInvariant() == "buy")
                        {
                            order.IsBuy = true;
                        }
                        // fee is a percentage taken from the traded amount rounded to 8 decimals
                        if (order.IsBuy)
                        {
                            order.Fees += Math.Round(tradeAmt * token["fee"].ConvertInvariant <decimal>(), 8, MidpointRounding.AwayFromZero);
                        }
                        else
                        {
                            order.Fees += Math.Round(tradeAmt * tradeRate * token["fee"].ConvertInvariant <decimal>(), 8, MidpointRounding.AwayFromZero);
                        }
                        if (order.OrderDate == DateTime.MinValue)
                        {
                            order.OrderDate = token["date"].ConvertInvariant <DateTime>();
                        }
                    }
                    order.AveragePrice /= tradeCount;

                    // Poloniex does not provide a way to get the original price
                    order.Price = order.AveragePrice;
                }
            }
            else
            {
                if (result["rate"] != null)
                {
                    order.Price = result["rate"].ConvertInvariant <decimal>();
                }
                if (result["startingAmount"] != null)
                {
                    order.Amount = result["startingAmount"].ConvertInvariant <decimal>();
                }
                if (result["amount"] != null)
                {
                    order.AmountFilled = result["amount"].ConvertInvariant <decimal>() - order.Amount;
                }
                if (result["type"] != null)
                {
                    order.IsBuy = (result["type"].ToString() != "sell");
                }
                if (result["date"] != null)
                {
                    order.OrderDate = result["date"].ConvertInvariant <DateTime>();
                }
                // fee is a percentage taken from the traded amount rounded to 8 decimals
                if (result["type"] != null && result["amount"] != null && result["rate"] != null)
                {
                    if (order.IsBuy)
                    {
                        order.Fees += Math.Round(result["amount"].ConvertInvariant <decimal>() * result["fee"].ConvertInvariant <decimal>(), 8, MidpointRounding.AwayFromZero);
                    }
                    else
                    {
                        order.Fees += Math.Round(result["amount"].ConvertInvariant <decimal>() * result["rate"].ConvertInvariant <decimal>() * result["fee"].ConvertInvariant <decimal>(), 8, MidpointRounding.AwayFromZero);
                    }
                }
            }

            return(order);
        }
예제 #18
0
        private ExchangeOrderResult ParseOrder(JToken token)
        {
            /*
             * {
             *      "id": "string (uuid)",
             *      "marketSymbol": "string",
             *      "direction": "string",
             *      "type": "string",
             *      "quantity": "number (double)",
             *      "limit": "number (double)",
             *      "ceiling": "number (double)",
             *      "timeInForce": "string",
             *      "clientOrderId": "string (uuid)",
             *      "fillQuantity": "number (double)",
             *      "commission": "number (double)",
             *      "proceeds": "number (double)",
             *      "status": "string",
             *      "createdAt": "string (date-time)",
             *      "updatedAt": "string (date-time)",
             *      "closedAt": "string (date-time)",
             *      "orderToCancel": {
             *      "type": "string",
             *      "id": "string (uuid)"
             *                                              }
             * }
             */
            ExchangeOrderResult order        = new ExchangeOrderResult();
            decimal             amount       = token["quantity"].ConvertInvariant <decimal>();
            decimal             amountFilled = token["fillQuantity"].ConvertInvariant <decimal>();

            order.Amount       = amount;
            order.AmountFilled = amountFilled;
            order.Price        = token["limit"].ConvertInvariant <decimal>(order.AveragePrice);
            order.Message      = string.Empty;
            order.OrderId      = token["id"].ToStringInvariant();

            if (amountFilled >= amount)
            {
                order.Result = ExchangeAPIOrderResult.Filled;
            }
            else if (amountFilled == 0m)
            {
                order.Result = ExchangeAPIOrderResult.Pending;
            }
            else
            {
                order.Result = ExchangeAPIOrderResult.FilledPartially;
            }
            order.OrderDate    = token["createdAt"].ToDateTimeInvariant();
            order.MarketSymbol = token["marketSymbol"].ToStringInvariant();
            order.Fees         = token["commission"].ConvertInvariant <decimal>();    // This is always in the base pair (e.g. BTC, ETH, USDT)

            if (!string.IsNullOrWhiteSpace(order.MarketSymbol))
            {
                string[] pairs = order.MarketSymbol.Split('-');
                if (pairs.Length == 2)
                {
                    order.FeesCurrency = pairs[0];
                }
            }
            order.IsBuy = token["direction"].ToStringInvariant() == "BUY";
            return(order);
        }
예제 #19
0
        private ExchangeOrderResult ParseOrder(JToken result)
        {
            decimal executedValue = result["executed_value"].ConvertInvariant <decimal>();
            decimal amountFilled  = result["filled_size"].ConvertInvariant <decimal>();
            decimal amount        = result["size"].ConvertInvariant <decimal>(amountFilled);
            decimal price         = result["price"].ConvertInvariant <decimal>();
            decimal stop_price    = result["stop_price"].ConvertInvariant <decimal>();
            decimal?averagePrice  = (amountFilled <= 0m ? null : (decimal?)(executedValue / amountFilled));
            decimal fees          = result["fill_fees"].ConvertInvariant <decimal>();
            string  marketSymbol  = result["product_id"].ToStringInvariant(result["id"].ToStringInvariant());

            ExchangeOrderResult order = new ExchangeOrderResult
            {
                Amount        = amount,
                AmountFilled  = amountFilled,
                Price         = price <= 0m ? stop_price : price,
                Fees          = fees,
                FeesCurrency  = marketSymbol.Substring(0, marketSymbol.IndexOf('-')),
                AveragePrice  = averagePrice,
                IsBuy         = (result["side"].ToStringInvariant() == "buy"),
                OrderDate     = result["created_at"].ToDateTimeInvariant(),
                CompletedDate = result["done_at"].ToDateTimeInvariant(),
                MarketSymbol  = marketSymbol,
                OrderId       = result["id"].ToStringInvariant()
            };

            switch (result["status"].ToStringInvariant())
            {
            case "pending":
                order.Result = ExchangeAPIOrderResult.PendingOpen;
                break;

            case "active":
            case "open":
                if (order.Amount == order.AmountFilled)
                {
                    order.Result = ExchangeAPIOrderResult.Filled;
                }
                else if (order.AmountFilled > 0.0m)
                {
                    order.Result = ExchangeAPIOrderResult.FilledPartially;
                }
                else
                {
                    order.Result = ExchangeAPIOrderResult.Open;
                }
                break;

            case "done":
            case "settled":
                switch (result["done_reason"].ToStringInvariant())
                {
                case "cancelled":
                case "canceled":
                    order.Result = ExchangeAPIOrderResult.Canceled;
                    break;

                case "filled":
                    order.Result = ExchangeAPIOrderResult.Filled;
                    break;

                default:
                    order.Result = ExchangeAPIOrderResult.Unknown;
                    break;
                }
                break;

            case "rejected":
                order.Result = ExchangeAPIOrderResult.Rejected;
                break;

            case "cancelled":
            case "canceled":
                order.Result = ExchangeAPIOrderResult.Canceled;
                break;

            default:
                throw new NotImplementedException($"Unexpected status type: {result["status"].ToStringInvariant()}");
            }
            return(order);
        }