Skip to content

Commit 5074538

Browse files
authored
Poloniex OnPlaceOrderAsync improvement. (#612)
Added proper order status. Handled exception when order not filled using FOK. Small fixes in ParseOrderTrades. Updated tests.
1 parent 947446d commit 5074538

File tree

2 files changed

+108
-64
lines changed

2 files changed

+108
-64
lines changed

src/ExchangeSharp/API/Exchanges/Poloniex/ExchangePoloniexAPI.cs

Lines changed: 92 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -84,29 +84,40 @@ protected override Task OnInitializeAsync()
8484
return Task.CompletedTask;
8585
}
8686

87-
public ExchangeOrderResult ParsePlacedOrder(JToken result)
88-
{
89-
ExchangeOrderResult order = new ExchangeOrderResult
90-
{
91-
OrderId = result["orderNumber"].ToStringInvariant(),
92-
};
87+
public ExchangeOrderResult ParsePlacedOrder(JToken result, ExchangeOrderRequest request)
88+
{
89+
ExchangeOrderResult order = new ExchangeOrderResult
90+
{
91+
OrderId = result["orderNumber"].ToStringInvariant(),
92+
Amount = request.Amount,
93+
Price = request.Price
94+
};
9395

94-
JToken trades = result["resultingTrades"];
95-
if (trades != null && trades.Children().Count() != 0)
96-
{
97-
ParseOrderTrades(trades, order);
98-
}
96+
JToken trades = result["resultingTrades"];
9997

100-
return order;
101-
}
98+
if (trades == null)
99+
{
100+
order.Result = ExchangeAPIOrderResult.Canceled;
101+
}
102+
else if (trades != null && trades.Count() == 0)
103+
{
104+
order.Result = ExchangeAPIOrderResult.Pending;
105+
}
106+
else if (trades != null && trades.Children().Count() != 0)
107+
{
108+
ParseOrderTrades(trades, order);
109+
}
102110

103-
/// <summary>
104-
/// Parses an order which has not been filled.
105-
/// </summary>
106-
/// <param name="result">The JToken to parse.</param>
107-
/// <param name="marketSymbol">Market symbol or null if it's in the result</param>
108-
/// <returns>ExchangeOrderResult with the open order and how much is remaining to fill</returns>
109-
public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = null)
111+
return order;
112+
}
113+
114+
/// <summary>
115+
/// Parses an order which has not been filled.
116+
/// </summary>
117+
/// <param name="result">The JToken to parse.</param>
118+
/// <param name="marketSymbol">Market symbol or null if it's in the result</param>
119+
/// <returns>ExchangeOrderResult with the open order and how much is remaining to fill</returns>
120+
public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = null)
110121
{
111122
ExchangeOrderResult order = new ExchangeOrderResult
112123
{
@@ -128,51 +139,58 @@ public ExchangeOrderResult ParseOpenOrder(JToken result, string marketSymbol = n
128139
return order;
129140
}
130141

131-
public void ParseOrderTrades(IEnumerable<JToken> trades, ExchangeOrderResult order)
132-
{
133-
bool orderMetadataSet = false;
134-
foreach (JToken trade in trades)
135-
{
136-
if (!orderMetadataSet)
137-
{
138-
order.IsBuy = trade["type"].ToStringLowerInvariant() != "sell";
139-
string parsedSymbol = trade["currencyPair"].ToStringInvariant();
140-
if (string.IsNullOrWhiteSpace(parsedSymbol) && trade.Parent != null)
141-
{
142-
parsedSymbol = trade.Parent.Path;
143-
}
144-
if (order.MarketSymbol == "all" || !string.IsNullOrWhiteSpace(parsedSymbol))
145-
{
146-
order.MarketSymbol = parsedSymbol;
147-
}
148-
if (!string.IsNullOrWhiteSpace(order.MarketSymbol))
149-
{
150-
order.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
151-
}
152-
orderMetadataSet = true;
153-
}
142+
public void ParseOrderTrades(IEnumerable<JToken> trades, ExchangeOrderResult order)
143+
{
144+
bool orderMetadataSet = false;
145+
foreach (JToken trade in trades)
146+
{
147+
if (!orderMetadataSet)
148+
{
149+
order.IsBuy = trade["type"].ToStringLowerInvariant() != "sell";
150+
string parsedSymbol = trade["currencyPair"].ToStringInvariant();
151+
if (string.IsNullOrWhiteSpace(parsedSymbol) && trade.Parent != null)
152+
{
153+
parsedSymbol = trade.Parent.Path;
154+
}
155+
if (order.MarketSymbol == "all" || !string.IsNullOrWhiteSpace(parsedSymbol))
156+
{
157+
order.MarketSymbol = parsedSymbol;
158+
}
159+
if (!string.IsNullOrWhiteSpace(order.MarketSymbol))
160+
{
161+
order.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
162+
}
163+
orderMetadataSet = true;
164+
}
154165

155-
decimal tradeAmt = trade["amount"].ConvertInvariant<decimal>();
156-
decimal tradeRate = trade["rate"].ConvertInvariant<decimal>();
166+
decimal tradeAmt = trade["amount"].ConvertInvariant<decimal>();
167+
decimal tradeRate = trade["rate"].ConvertInvariant<decimal>();
157168

158-
order.AveragePrice = (order.AveragePrice * order.AmountFilled + tradeAmt * tradeRate) / (order.AmountFilled + tradeAmt);
159-
order.Amount += tradeAmt;
160-
order.AmountFilled = order.Amount;
169+
order.AveragePrice = (order.AveragePrice * order.AmountFilled + tradeAmt * tradeRate) / (order.AmountFilled + tradeAmt);
170+
order.AmountFilled += tradeAmt;
161171

162-
if (order.OrderDate == DateTime.MinValue)
163-
{
164-
order.OrderDate = trade["date"].ToDateTimeInvariant();
165-
}
172+
if (order.OrderDate == DateTime.MinValue)
173+
{
174+
order.OrderDate = trade["date"].ToDateTimeInvariant();
175+
}
166176

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

171-
// Poloniex does not provide a way to get the original price
172-
order.Price = order.AveragePrice;
173-
}
181+
if (order.AmountFilled == order.Amount)
182+
{
183+
order.Result = ExchangeAPIOrderResult.Filled;
184+
}
185+
else
186+
{
187+
order.Result = ExchangeAPIOrderResult.FilledPartially;
188+
}
189+
// Poloniex does not provide a way to get the original price
190+
order.Price = order.AveragePrice;
191+
}
174192

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

768-
JToken result = await MakePrivateAPIRequestAsync(order.IsBuy ? (order.IsMargin ? "marginBuy" : "buy") : (order.IsMargin ? "marginSell" : "sell"), orderParams);
769-
ExchangeOrderResult exchangeOrderResult = ParsePlacedOrder(result);
770-
exchangeOrderResult.MarketSymbol = order.MarketSymbol;
786+
JToken result = null;
787+
try
788+
{
789+
result = await MakePrivateAPIRequestAsync(order.IsBuy ? (order.IsMargin ? "marginBuy" : "buy") : (order.IsMargin ? "marginSell" : "sell"), orderParams);
790+
}
791+
catch (Exception e)
792+
{
793+
if (!e.Message.Contains("Unable to fill order completely"))
794+
{
795+
throw;
796+
}
797+
result = JToken.FromObject(new { orderNumber = "0", currencyPair = order.MarketSymbol });
798+
}
799+
ExchangeOrderResult exchangeOrderResult = ParsePlacedOrder(result, order);
800+
exchangeOrderResult.MarketSymbol = order.MarketSymbol;
771801
exchangeOrderResult.FeesCurrency = ParseFeesCurrency(order.IsBuy, order.MarketSymbol);
772802
return exchangeOrderResult;
773803
}

tests/ExchangeSharpTests/ExchangePoloniexAPITests.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,14 @@ public void ParseBuyOrder_SingleTrade_HappyPath()
6060

6161
var singleOrder = JsonConvert.DeserializeObject<JToken>(BuyOrder);
6262
var polo = CreatePoloniexAPI();
63-
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder);
63+
var orderRequest = new ExchangeOrderRequest
64+
{
65+
MarketSymbol = "FOO_FOO",
66+
Amount = 338.8732m,
67+
Price = 0.00000173m,
68+
IsBuy = true
69+
};
70+
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder, orderRequest);
6471
order.OrderId.Should().Be("31226040");
6572
order.IsBuy.Should().BeTrue();
6673
order.Amount.Should().Be(338.8732m);
@@ -88,7 +95,14 @@ public void ParseSellOrder_SingleTrade_HappyPath()
8895

8996
var singleOrder = JsonConvert.DeserializeObject<JToken>(SellOrder);
9097
var polo = CreatePoloniexAPI();
91-
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder);
98+
var orderRequest = new ExchangeOrderRequest
99+
{
100+
MarketSymbol = "FOO_FOO",
101+
Amount = 338.8732m,
102+
Price = 0.00000173m,
103+
IsBuy = false
104+
};
105+
ExchangeOrderResult order = polo.ParsePlacedOrder(singleOrder, orderRequest);
92106
order.OrderId.Should().Be("31226040");
93107
order.IsBuy.Should().BeFalse();
94108
order.Amount.Should().Be(338.8732m);

0 commit comments

Comments
 (0)