예제 #1
0
        public void CanParseRateRules()
        {
            // Check happy path
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("// Some cool comments");
            builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
            builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
            builder.AppendLine("// Some other cool comments");
            builder.AppendLine("BTC_usd = kraken(BTC_USD)");
            builder.AppendLine("BTC_X = Coinbase(BTC_X);");
            builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");

            Assert.False(RateRules.TryParse("DPW*&W&#hdi&#&3JJD", out var rules));
            Assert.True(RateRules.TryParse(builder.ToString(), out rules));
            Assert.Equal(
                "// Some cool comments\n" +
                "DOGE_X = DOGE_BTC * BTC_X * 1.1;\n" +
                "DOGE_BTC = bittrex(DOGE_BTC);\n" +
                "// Some other cool comments\n" +
                "BTC_USD = kraken(BTC_USD);\n" +
                "BTC_X = coinbase(BTC_X);\n" +
                "X_X = coinaverage(X_X) * 1.02;",
                rules.ToString());
            var tests = new[]
예제 #2
0
        public static RateRule CreateFromExpression(string expression, CurrencyPair currencyPair)
        {
            var ex = RateRules.CreateExpression(expression);

            RateRules.TryParse("", out var rules);
            return(new RateRule(rules, currencyPair, ex));
        }
예제 #3
0
        public void SecondDuplicatedRuleIsIgnored()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("DOGE_X = 1.1");
            builder.AppendLine("DOGE_X = 1.2");
            Assert.True(RateRules.TryParse(builder.ToString(), out var rules));
            var rule = rules.GetRuleFor(new CurrencyPair("DOGE", "BTC"));

            rule.Reevaluate();
            Assert.True(!rule.HasError);
            Assert.Equal(1.1m, rule.BidAsk.Ask);
        }
        public async Task <IActionResult> Rates(RatesViewModel model, string command = null, string storeId = null, CancellationToken cancellationToken = default)
        {
            model.SetExchangeRates(GetSupportedExchanges(), model.PreferredExchange);
            model.StoreId = storeId ?? model.StoreId;
            CurrencyPair[] currencyPairs = null;
            try
            {
                currencyPairs = model.DefaultCurrencyPairs?
                                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                .Select(p => CurrencyPair.Parse(p))
                                .ToArray();
            }
            catch
            {
                ModelState.AddModelError(nameof(model.DefaultCurrencyPairs), "Invalid currency pairs (should be for example: BTC_USD,BTC_CAD,BTC_JPY)");
            }
            if (!ModelState.IsValid)
            {
                return(View(model));
            }
            if (model.PreferredExchange != null)
            {
                model.PreferredExchange = model.PreferredExchange.Trim().ToLowerInvariant();
            }

            var blob = StoreData.GetStoreBlob();

            model.DefaultScript      = blob.GetDefaultRateRules(_NetworkProvider).ToString();
            model.AvailableExchanges = GetSupportedExchanges();

            blob.PreferredExchange    = model.PreferredExchange;
            blob.Spread               = (decimal)model.Spread / 100.0m;
            blob.DefaultCurrencyPairs = currencyPairs;
            if (!model.ShowScripting)
            {
                if (!GetSupportedExchanges().Select(c => c.Name).Contains(blob.PreferredExchange, StringComparer.OrdinalIgnoreCase))
                {
                    ModelState.AddModelError(nameof(model.PreferredExchange), $"Unsupported exchange ({model.RateSource})");
                    return(View(model));
                }
            }
            RateRules rules = null;

            if (model.ShowScripting)
            {
                if (!RateRules.TryParse(model.Script, out rules, out var errors))
                {
                    errors = errors ?? new List <RateRulesErrors>();
                    var errorString = String.Join(", ", errors.ToArray());
                    ModelState.AddModelError(nameof(model.Script), $"Parsing error ({errorString})");
                    return(View(model));
                }
                else
                {
                    blob.RateScript = rules.ToString();
                    ModelState.Remove(nameof(model.Script));
                    model.Script = blob.RateScript;
                }
            }
            rules = blob.GetRateRules(_NetworkProvider);

            if (command == "Test")
            {
                if (string.IsNullOrWhiteSpace(model.ScriptTest))
                {
                    ModelState.AddModelError(nameof(model.ScriptTest), "Fill out currency pair to test for (like BTC_USD,BTC_CAD)");
                    return(View(model));
                }
                var splitted = model.ScriptTest.Split(',', StringSplitOptions.RemoveEmptyEntries);

                var pairs = new List <CurrencyPair>();
                foreach (var pair in splitted)
                {
                    if (!CurrencyPair.TryParse(pair, out var currencyPair))
                    {
                        ModelState.AddModelError(nameof(model.ScriptTest), $"Invalid currency pair '{pair}' (it should be formatted like BTC_USD,BTC_CAD)");
                        return(View(model));
                    }
                    pairs.Add(currencyPair);
                }

                var fetchs      = _RateFactory.FetchRates(pairs.ToHashSet(), rules, cancellationToken);
                var testResults = new List <RatesViewModel.TestResultViewModel>();
                foreach (var fetch in fetchs)
                {
                    var testResult = await(fetch.Value);
                    testResults.Add(new RatesViewModel.TestResultViewModel()
                    {
                        CurrencyPair = fetch.Key.ToString(),
                        Error        = testResult.Errors.Count != 0,
                        Rule         = testResult.Errors.Count == 0 ? testResult.Rule + " = " + testResult.BidAsk.Bid.ToString(CultureInfo.InvariantCulture)
                                                            : testResult.EvaluatedRule
                    });
                }
                model.TestRateRules = testResults;
                return(View(model));
            }
            else // command == Save
            {
                if (StoreData.SetStoreBlob(blob))
                {
                    await _Repo.UpdateStore(StoreData);

                    StatusMessage = "Rate settings updated";
                }
                return(RedirectToAction(nameof(Rates), new
                {
                    storeId = StoreData.Id
                }));
            }
        }
예제 #5
0
        public async Task <IActionResult> NewAtomicSwap(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            NewViewModel newVM)
        {
            var fromWallet = await GetDerivationStrategy(walletId);

            var statusAsync = ExplorerClientProvider.GetExplorerClient(fromWallet.Network).GetStatusAsync();

            if (fromWallet == null)
            {
                return(NotFound());
            }
            var wallets = await GetNamedWallets(walletId.CryptoCode);

            newVM.SetWalletList(wallets, newVM.SelectedWallet);
            newVM.CryptoCode = fromWallet.Network.CryptoCode;
            if (!WalletId.TryParse(newVM.SelectedWallet, out var selectedWalletId))
            {
                ModelState.AddModelError(nameof(newVM.SelectedWallet), "Invalid wallet id");
                return(View(newVM));
            }
            var toWallet = await GetDerivationStrategy(selectedWalletId);

            if (toWallet == null)
            {
                ModelState.AddModelError(nameof(newVM.SelectedWallet), "Invalid wallet id");
                return(View(newVM));
            }

            var             id    = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
            AtomicSwapOffer offer = new AtomicSwapOffer();

            offer.MarketMakerUri = new Uri($"{this.Request.GetAbsoluteRoot().WithTrailingSlash()}api/xswap/{id}", UriKind.Absolute);
            offer.Offer          = new AtomicSwapOfferAsset()
            {
                Amount     = Money.Coins((decimal)newVM.Amount),
                CryptoCode = walletId.CryptoCode,
            };

            var minRelayFee   = (await statusAsync).BitcoinStatus.MinRelayTxFee;
            var minimumAmount = minRelayFee.GetFee(200); // Arbitrary but should cover the dust of any output

            if (offer.Offer.Amount <= minimumAmount)
            {
                ModelState.AddModelError(nameof(newVM.Amount), $"Amount must be above {minimumAmount}");
                return(View(newVM));
            }
            offer.Price = new AtomicSwapOfferAsset()
            {
                CryptoCode = toWallet.PaymentId.CryptoCode
            };
            var lockTimespan = TimeSpan.FromDays(2);

            offer.CreatedAt = DateTimeOffset.UtcNow;

            var storeData = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (ModelState.IsValid)
            {
                var pair = new CurrencyPair("AAA", "BBB");
                newVM.RateRule = $"{pair} = {newVM.RateRule}";

                if (RateRules.TryParse(newVM.RateRule, out var rules, out var rateRulesErrors))
                {
                    rules.Spread = (decimal)newVM.Spread / 100.0m;
                    var rateResult = await RateFetcher.FetchRate(pair, rules, CancellationToken.None);

                    if (rateResult.BidAsk == null)
                    {
                        string errorMessage = "Error when fetching rate";
                        if (rateResult.EvaluatedRule != null)
                        {
                            errorMessage += $" ({rateResult.EvaluatedRule})";
                        }
                        ModelState.AddModelError(nameof(newVM.RateRule), errorMessage);
                    }
                    else
                    {
                        offer.Price.Amount = Money.Coins(offer.Offer.Amount.ToDecimal(MoneyUnit.BTC) * rateResult.BidAsk.Ask);

                        rules.Spread = 0;
                        offer.Rule   = rules.GetRuleFor(pair).ToString();
                    }
                }
                else
                {
                    string errorDetails = "";
                    if (rateRulesErrors.Count > 0)
                    {
                        errorDetails = $" ({rateRulesErrors[0]})";
                    }
                    ModelState.AddModelError(nameof(newVM.RateRule), $"Impossible to parse rate rules{errorDetails}");
                }
            }