public async Task EvaluateRateAsync(Currency from, Currency to) { var now = DateTime.UtcNow; if (now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday) { return; } var rate = await _rateFatcher.GetRateAsync(from, to); var reversedRate = 1f / rate; _logger.LogInformation($"Evaluating {from} -> {to} with: {rate} [{reversedRate}]"); var closeOportunities = await FindCloseOportunitiesAsync(from, to, rate); _logger.LogInformation($"Found opportunities for {from} -> {to}: {string.Join(",", closeOportunities.Select(p => p.Id))}"); var reverseOpportunities = await FindCloseOportunitiesAsync(to, from, reversedRate); _logger.LogInformation($"Found opportunities for {to} -> {from}: {string.Join(",", reverseOpportunities.Select(p => p.Id))}"); closeOportunities.AddRange(reverseOpportunities); var notification = string.Join("<br>", closeOportunities.Select(p => $"<a href=\"{_options.CloseUIUrl}id={p.Id}\">[{p.Id}]</a> {p.FromCurrency} -> {p.ToCurrency} : {p.OpenAmount} for {p.OpenRate}. Proposal: {p.OpenAmount*p.OpenRate} [{rate} - {reversedRate}]")); if (!string.IsNullOrEmpty(notification)) { NotificationManager.Notify("Opportunities", notification, NotificationTarget); } if (now.Hour == _options.OpenHour) { var reversedAmount = _options.OpenAmount / reversedRate; var openedId = await _dataStore.Position.OpenAsync(from.ToString(), to.ToString(), _options.OpenAmount, rate); var reverseOpenedId = await _dataStore.Position.OpenAsync(to.ToString(), from.ToString(), reversedAmount, reversedRate); _logger.LogInformation($"Opened: {from} -> {to} for {rate}"); _logger.LogInformation($"Opened: {to} -> {from} for {reversedRate}"); NotificationManager.Notify("Opened", $"[{openedId}/{reverseOpenedId}] {from} -> {to} : {rate} [{reversedRate}]", NotificationTarget); } }
public async Task OffsetIsDynamicalyComputed() { _rateFetcher.GetRateAsync(Currency.EUR, Currency.USD).Returns(Task.FromResult(1.195f)); _position.FindOpenPositionsAsync(Arg.Any <string>(), Arg.Any <string>(), Arg.Any <float>()).Returns(Task.FromResult(new List <Position>())); float offsetOfReverted = float.NaN; float offset = float.NaN; await _position.FindOpenPositionsAsync("EUR", "USD", Arg.Do <float>(a => offset = a)); await _position.FindOpenPositionsAsync("USD", "EUR", Arg.Do <float>(a => offsetOfReverted = a)); await _underTest.EvaluateRateAsync(Currency.EUR, Currency.USD); offset.Should().NotBe(float.NaN, "offset needs to be evaluated"); // this is needed because of precision (1.195f + 0.003f = 1.1980000007) offset.Should().BeInRange(1.198f, 1.19811f, "offset is equal to configured one"); offsetOfReverted.Should().NotBe(float.NaN, "reverted offset needs to be evaluated"); offsetOfReverted.Should().BeInRange(0.8369f, 0.8398f, "reverted offset is less than non-reverted"); }
public void Setup() { _options = new RateEvaluatorOptions { CloseOffsetPercentage = 0.26f, NotificationTarget = TargetEmail, OpenAmount = 10, OpenHour = DateTime.UtcNow.Hour }; var logger = Substitute.For <ILogger <DefaultRateEvaluator> >(); _notificationManager = Substitute.For <INotificationManager>(); _rateFetcher = Substitute.For <IRates>(); _rateFetcher.GetRateAsync(Currency.EUR, Currency.USD).Returns(Task.FromResult(2f)); _position = Substitute.For <IPosition>(); var store = Substitute.For <IDataStore>(); store.Position.Returns(_position); _underTest = new DefaultRateEvaluator(_options, logger, _notificationManager, _rateFetcher, store); }