diff --git a/GDAXClient.Specs/GDAXClient.Specs.csproj b/GDAXClient.Specs/GDAXClient.Specs.csproj index c98858b0..62a15380 100644 --- a/GDAXClient.Specs/GDAXClient.Specs.csproj +++ b/GDAXClient.Specs/GDAXClient.Specs.csproj @@ -92,6 +92,7 @@ + diff --git a/GDAXClient.Specs/JsonFixtures/Products/ProductHistoryFixture.cs b/GDAXClient.Specs/JsonFixtures/Products/ProductHistoryFixture.cs new file mode 100644 index 00000000..a0ab93e3 --- /dev/null +++ b/GDAXClient.Specs/JsonFixtures/Products/ProductHistoryFixture.cs @@ -0,0 +1,39 @@ +namespace GDAXClient.Specs.JsonFixtures.Products +{ + public static class ProductHistoryFixture + { + public static string Create() + { + + var json = @" +[ + [ + 1512691200, + 16777, + 17777.69, + 17390.01, + 17210.99, + 7650.386033540894 + ], + [ + 1512633600, + 14487.8, + 19697, + 14487.8, + 17390.01, + 65581.82529800163 + ], + [ + 1512576000, + 13500, + 14499.89, + 14056.78, + 14487.8, + 12303.76923928093 + ] +]"; + + return json; + } + } +} diff --git a/GDAXClient.Specs/Services/Orders/OrdersServiceSpecs.cs b/GDAXClient.Specs/Services/Orders/OrdersServiceSpecs.cs index 1a8da7e3..9f7b9815 100644 --- a/GDAXClient.Specs/Services/Orders/OrdersServiceSpecs.cs +++ b/GDAXClient.Specs/Services/Orders/OrdersServiceSpecs.cs @@ -81,7 +81,7 @@ class when_placing_a_limit_order }; Because of = () => - order_response_result = Subject.PlaceLimitOrderAsync(OrderSide.Buy, ProductType.BtcUsd, .01M, 0.1M).Result; + order_response_result = Subject.PlaceLimitOrderAsync(OrderSide.Buy, ProductType.BtcUsd, .01M, 0.1M, DateTime.Today.Date.ToUniversalTime()).Result; It should_have_correct_account_information = () => { diff --git a/GDAXClient.Specs/Services/Products/ProductsServiceSpecs.cs b/GDAXClient.Specs/Services/Products/ProductsServiceSpecs.cs index ad20df11..cdaa7dc7 100644 --- a/GDAXClient.Specs/Services/Products/ProductsServiceSpecs.cs +++ b/GDAXClient.Specs/Services/Products/ProductsServiceSpecs.cs @@ -1,4 +1,5 @@ -using GDAXClient.Authentication; +using System; +using GDAXClient.Authentication; using GDAXClient.HttpClient; using GDAXClient.Products; using GDAXClient.Services.HttpRequest; @@ -10,6 +11,7 @@ using Machine.Fakes; using Machine.Specifications; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -25,6 +27,8 @@ public class ProductsServiceSpecs : WithSubject static ProductsOrderBookResponse product_order_books_response; + static IEnumerable product_history_response; + static ProductTicker product_ticker_result; static ProductStats product_stats_result; @@ -146,5 +150,47 @@ class when_getting_product_stats product_stats_result.Volume.ShouldEqual(2.41000000M); }; } + + class when_getting_product_history + { + Establish context = () => + { + The().WhenToldTo(p => p.CreateHttpRequestMessage(Param.IsAny(), Param.IsAny(), Param.IsAny(), Param.IsAny())) + .Return(new HttpRequestMessage()); + + The().WhenToldTo(p => p.SendASync(Param.IsAny())) + .Return(Task.FromResult(new HttpResponseMessage())); + + The().WhenToldTo(p => p.ReadAsStringAsync(Param.IsAny())) + .Return(Task.FromResult(ProductHistoryFixture.Create())); + }; + + Because of = () => + product_history_response = Subject.GetProductHistoryAsync(ProductType.BtcUsd, DateTime.Now.AddDays(-1), DateTime.Now, 57600).Result; + + It should_have_correct_product_stats = () => + { + product_history_response.ToList()[0][0].ToString().ShouldEqual(1512691200.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[0][1].ToString().ShouldEqual(16777.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[0][2].ToString().ShouldEqual(17777.69.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[0][3].ToString().ShouldEqual(17390.01.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[0][4].ToString().ShouldEqual(17210.99.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[0][5].ToString().ShouldEqual(7650.386033540894.ToString(CultureInfo.InvariantCulture)); + + product_history_response.ToList()[1][0].ToString().ShouldEqual(1512633600.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[1][1].ToString().ShouldEqual(14487.8.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[1][2].ToString().ShouldEqual(19697.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[1][3].ToString().ShouldEqual(14487.8.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[1][4].ToString().ShouldEqual(17390.01.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[1][5].ToString().ShouldEqual(65581.82529800163.ToString(CultureInfo.InvariantCulture)); + + product_history_response.ToList()[2][0].ToString().ShouldEqual(1512576000.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[2][1].ToString().ShouldEqual(13500.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[2][2].ToString().ShouldEqual(14499.89.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[2][3].ToString().ShouldEqual(14056.78.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[2][4].ToString().ShouldEqual(14487.8.ToString(CultureInfo.InvariantCulture)); + product_history_response.ToList()[2][5].ToString().ShouldEqual(12303.76923928093.ToString(CultureInfo.InvariantCulture)); + }; + } } } diff --git a/GDAXClient/GDAXClient.csproj b/GDAXClient/GDAXClient.csproj index 7967de47..8a348c28 100644 --- a/GDAXClient/GDAXClient.csproj +++ b/GDAXClient/GDAXClient.csproj @@ -55,6 +55,7 @@ + diff --git a/GDAXClient/Services/Orders/OrdersService.cs b/GDAXClient/Services/Orders/OrdersService.cs index 5d7e08fc..5d69b934 100644 --- a/GDAXClient/Services/Orders/OrdersService.cs +++ b/GDAXClient/Services/Orders/OrdersService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Text; using System.Threading.Tasks; namespace GDAXClient.Services.Orders @@ -48,7 +49,7 @@ public async Task PlaceMarketOrderAsync(OrderSide side, ProductTy return orderResponse; } - public async Task PlaceLimitOrderAsync(OrderSide side, ProductType productId, decimal size, decimal price) + public async Task PlaceLimitOrderAsync(OrderSide side, ProductType productId, decimal size, decimal price, TimeInForce timeInForce = TimeInForce.Gtc, bool postOnly = true) { var newOrder = JsonConvert.SerializeObject(new Order { @@ -59,13 +60,47 @@ public async Task PlaceLimitOrderAsync(OrderSide side, ProductTyp size = size }); - var httpResponseMessage = await SendHttpRequestMessageAsync(HttpMethod.Post, authenticator, "/orders", newOrder); + var queryString = new StringBuilder("?"); + + queryString.Append("time_in_force="); + queryString.Append(timeInForce.ToString().ToUpperInvariant()); + + queryString.Append("&post_only="); + queryString.Append(postOnly.ToString().ToLower()); + + var httpResponseMessage = await SendHttpRequestMessageAsync(HttpMethod.Post, authenticator, "/orders" + queryString, newOrder).ConfigureAwait(false); var contentBody = await httpClient.ReadAsStringAsync(httpResponseMessage).ConfigureAwait(false); var orderResponse = JsonConvert.DeserializeObject(contentBody); return orderResponse; } + public async Task PlaceLimitOrderAsync(OrderSide side, ProductType productId, decimal size, decimal price, DateTime cancelAfter, bool postOnly = true) + { + var newOrder = JsonConvert.SerializeObject(new Order + { + side = side.ToString().ToLower(), + product_id = productId.ToDasherizedUpper(), + type = OrderType.Limit.ToString().ToLower(), + price = price, + size = size + }); + + var queryString = new StringBuilder("?"); + + queryString.Append("time_in_force=GTT"); + queryString.Append("&cancel_after="); + queryString.Append(cancelAfter.Minute + "," + cancelAfter.Hour + "," + cancelAfter.Day); + queryString.Append("&post_only="); + queryString.Append(postOnly.ToString().ToLower()); + + var httpResponseMessage = await SendHttpRequestMessageAsync(HttpMethod.Post, authenticator, "/orders" + queryString, newOrder); + var contentBody = await httpClient.ReadAsStringAsync(httpResponseMessage).ConfigureAwait(false); + var orderResponse = JsonConvert.DeserializeObject(contentBody); + + return orderResponse; + } + public async Task CancelAllOrdersAsync() { var httpResponseMessage = await SendHttpRequestMessageAsync(HttpMethod.Delete, authenticator, "/orders"); diff --git a/GDAXClient/Services/Orders/TimeInForce.cs b/GDAXClient/Services/Orders/TimeInForce.cs new file mode 100644 index 00000000..9c5f8e6b --- /dev/null +++ b/GDAXClient/Services/Orders/TimeInForce.cs @@ -0,0 +1,9 @@ +namespace GDAXClient.Services.Orders +{ + public enum TimeInForce + { + Gtc, + Ioc, + Fok + } +} diff --git a/GDAXClient/Services/Products/ProductsService.cs b/GDAXClient/Services/Products/ProductsService.cs index 2867c9b2..6393e079 100644 --- a/GDAXClient/Services/Products/ProductsService.cs +++ b/GDAXClient/Services/Products/ProductsService.cs @@ -1,4 +1,5 @@ -using GDAXClient.HttpClient; +using System; +using GDAXClient.HttpClient; using GDAXClient.Services; using GDAXClient.Services.Accounts; using GDAXClient.Services.HttpRequest; @@ -68,5 +69,18 @@ public async Task GetProductStatsAsync(ProductType productPair) return productStatsResponse; } + + + public async Task> GetProductHistoryAsync(ProductType productPair, DateTime start, DateTime end, int granularity) + { + var isoStart = start.ToString("s"); + var isoEnd = end.ToString("s"); + + var httpResponseMessage = await SendHttpRequestMessageAsync(HttpMethod.Get, authenticator, $"/products/{productPair.ToDasherizedUpper()}/candles?start={isoStart}&end={isoEnd}&granularity={granularity}"); + var contentBody = await httpClient.ReadAsStringAsync(httpResponseMessage).ConfigureAwait(false); + var productHistoryResponse = JsonConvert.DeserializeObject>(contentBody); + + return productHistoryResponse; + } } }