Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 92 additions & 62 deletions src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,29 +84,40 @@ protected override Task OnInitializeAsync()
return Task.CompletedTask;
}

public ExchangeOrderResult ParsePlacedOrder(JToken result)
{
ExchangeOrderResult order = new ExchangeOrderResult
{
OrderId = result["orderNumber"].ToStringInvariant(),
};
public ExchangeOrderResult ParsePlacedOrder(JToken result, ExchangeOrderRequest request)
{
ExchangeOrderResult order = new ExchangeOrderResult
{
OrderId = result["orderNumber"].ToStringInvariant(),
Amount = request.Amount,
Price = request.Price
};

JToken trades = result["resultingTrades"];
if (trades != null && trades.Children().Count() != 0)
{
ParseOrderTrades(trades, order);
}
JToken trades = result["resultingTrades"];

return order;
}
if (trades == null)
{
order.Result = ExchangeAPIOrderResult.Canceled;
}
else if (trades != null && trades.Count() == 0)
{
order.Result = ExchangeAPIOrderResult.Pending;
}
else if (trades != null && trades.Children().Count() != 0)
{
ParseOrderTrades(trades, order);
}

/// <summary>
/// Parses an order which has not been filled.
/// </summary>
/// <param name="result">The JToken to parse.</param>
/// <param name="marketSymbol">Market symbol or null if it's in the result</param>
/// <returns>ExchangeOrderResult with the open order and how much is remaining to fill</returns>
public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = null)
return order;
}

/// <summary>
/// Parses an order which has not been filled.
/// </summary>
/// <param name="result">The JToken to parse.</param>
/// <param name="marketSymbol">Market symbol or null if it's in the result</param>
/// <returns>ExchangeOrderResult with the open order and how much is remaining to fill</returns>
public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = null)
{
ExchangeOrderResult order = new ExchangeOrderResult
{
Expand All @@ -128,51 +139,58 @@ public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = n
return order;
}

public void ParseOrderTrades(IEnumerable<JToken> trades, ExchangeOrderResult order)
{
bool orderMetadataSet = false;
foreach (JToken trade in trades)
{
if (!orderMetadataSet)
{
order.IsBuy = trade["type"].ToStringLowerInvariant() != "sell";
string parsedSymbol = trade["currencyPair"].ToStringInvariant();
if (string.IsNullOrWhiteSpace(parsedSymbol) && trade.Parent != null)
{
parsedSymbol = trade.Parent.Path;
}
if (order.MarketSymbol == "all" || !string.IsNullOrWhiteSpace(parsedSymbol))
{
order.MarketSymbol = parsedSymbol;
}
if (!string.IsNullOrWhiteSpace(order.MarketSymbol))
{
order.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
}
orderMetadataSet = true;
}
public void ParseOrderTrades(IEnumerable<JToken> trades, ExchangeOrderResult order)
{
bool orderMetadataSet = false;
foreach (JToken trade in trades)
{
if (!orderMetadataSet)
{
order.IsBuy = trade["type"].ToStringLowerInvariant() != "sell";
string parsedSymbol = trade["currencyPair"].ToStringInvariant();
if (string.IsNullOrWhiteSpace(parsedSymbol) && trade.Parent != null)
{
parsedSymbol = trade.Parent.Path;
}
if (order.MarketSymbol == "all" || !string.IsNullOrWhiteSpace(parsedSymbol))
{
order.MarketSymbol = parsedSymbol;
}
if (!string.IsNullOrWhiteSpace(order.MarketSymbol))
{
order.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
}
orderMetadataSet = true;
}

decimal tradeAmt = trade["amount"].ConvertInvariant<decimal>();
decimal tradeRate = trade["rate"].ConvertInvariant<decimal>();
decimal tradeAmt = trade["amount"].ConvertInvariant<decimal>();
decimal tradeRate = trade["rate"].ConvertInvariant<decimal>();

order.AveragePrice = (order.AveragePrice * order.AmountFilled + tradeAmt * tradeRate) / (order.AmountFilled + tradeAmt);
order.Amount += tradeAmt;
order.AmountFilled = order.Amount;
order.AveragePrice = (order.AveragePrice * order.AmountFilled + tradeAmt * tradeRate) / (order.AmountFilled + tradeAmt);
order.AmountFilled += tradeAmt;

if (order.OrderDate == DateTime.MinValue)
{
order.OrderDate = trade["date"].ToDateTimeInvariant();
}
if (order.OrderDate == DateTime.MinValue)
{
order.OrderDate = trade["date"].ToDateTimeInvariant();
}

// fee is a percentage taken from the traded amount rounded to 8 decimals
order.Fees += CalculateFees(tradeAmt, tradeRate, order.IsBuy, trade["fee"].ConvertInvariant<decimal>());
}
// fee is a percentage taken from the traded amount rounded to 8 decimals
order.Fees += CalculateFees(tradeAmt, tradeRate, order.IsBuy, trade["fee"].ConvertInvariant<decimal>());
}

// Poloniex does not provide a way to get the original price
order.Price = order.AveragePrice;
}
if (order.AmountFilled == order.Amount)
{
order.Result = ExchangeAPIOrderResult.Filled;
}
else
{
order.Result = ExchangeAPIOrderResult.FilledPartially;
}
// Poloniex does not provide a way to get the original price
order.Price = order.AveragePrice;
}

public void ParseClosePositionTrades(IEnumerable<JToken> trades, ExchangeCloseMarginPositionResult closePosition)
public void ParseClosePositionTrades(IEnumerable<JToken> trades, ExchangeCloseMarginPositionResult closePosition)
{
bool closePositionMetadataSet = false;
var tradeIds = new List<string>();
Expand Down Expand Up @@ -765,9 +783,21 @@ protected override async Task<ExchangeOrderResult> OnPlaceOrderAsync(ExchangeOrd
orderParams.Add(kv.Value);
}

JToken result = await MakePrivateAPIRequestAsync(order.IsBuy ? (order.IsMargin ? "marginBuy" : "buy") : (order.IsMargin ? "marginSell" : "sell"), orderParams);
ExchangeOrderResult exchangeOrderResult = ParsePlacedOrder(result);
exchangeOrderResult.MarketSymbol = order.MarketSymbol;
JToken result = null;
try
{
result = await MakePrivateAPIRequestAsync(order.IsBuy ? (order.IsMargin ? "marginBuy" : "buy") : (order.IsMargin ? "marginSell" : "sell"), orderParams);
}
catch (Exception e)
{
if (!e.Message.Contains("Unable to fill order completely"))
{
throw;
}
result = JToken.FromObject(new { orderNumber = "0", currencyPair = order.MarketSymbol });
}
ExchangeOrderResult exchangeOrderResult = ParsePlacedOrder(result, order);
exchangeOrderResult.MarketSymbol = order.MarketSymbol;
exchangeOrderResult.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
return exchangeOrderResult;
}
Expand Down
18 changes: 16 additions & 2 deletions tests/ExchangeSharpTests/ExchangePoloniexAPITests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@ public void ParseBuyOrder_SingleTrade_HappyPath()

var singleOrder = JsonConvert.DeserializeObject<JToken>(BuyOrder);
var polo = CreatePoloniexAPI();
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder);
var orderRequest = new ExchangeOrderRequest
{
MarketSymbol = "FOO_FOO",
Amount = 338.8732m,
Price = 0.00000173m,
IsBuy = true
};
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder, orderRequest);
order.OrderId.Should().Be("31226040");
order.IsBuy.Should().BeTrue();
order.Amount.Should().Be(338.8732m);
Expand Down Expand Up @@ -88,7 +95,14 @@ public void ParseSellOrder_SingleTrade_HappyPath()

var singleOrder = JsonConvert.DeserializeObject<JToken>(SellOrder);
var polo = CreatePoloniexAPI();
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder);
var orderRequest = new ExchangeOrderRequest
{
MarketSymbol = "FOO_FOO",
Amount = 338.8732m,
Price = 0.00000173m,
IsBuy = false
};
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder, orderRequest);
order.OrderId.Should().Be("31226040");
order.IsBuy.Should().BeFalse();
order.Amount.Should().Be(338.8732m);
Expand Down