Example #1
0
        public bool CanBuy(BuyOptions options, out string message)
        {
            IPairConfig pairConfig = GetPairConfig(options.Pair);

            if (!options.ManualOrder && !options.Swap && IsTradingSuspended)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: trading suspended";
                return(false);
            }
            else if (!options.ManualOrder && !options.Swap && !pairConfig.BuyEnabled)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: buying not enabled";
                return(false);
            }
            else if (!options.ManualOrder && Config.ExcludedPairs.Contains(options.Pair))
            {
                message = $"Cancel buy request for {options.Pair}. Reason: exluded pair";
                return(false);
            }
            else if (!options.ManualOrder && !options.IgnoreExisting && Account.HasTradingPair(options.Pair))
            {
                message = $"Cancel buy request for {options.Pair}. Reason: pair already exists";
                return(false);
            }
            else if (!options.ManualOrder && !options.Swap && Config.MaxPairs != 0 && Account.GetTradingPairs().Count() >= Config.MaxPairs && !Account.HasTradingPair(options.Pair))
            {
                message = $"Cancel buy request for {options.Pair}. Reason: maximum pairs reached";
                return(false);
            }
            else if (!options.ManualOrder && !options.Swap && pairConfig.BuyMinBalance != 0 && (Account.GetBalance() - options.MaxCost) < pairConfig.BuyMinBalance)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: minimum balance reached";
                return(false);
            }
            else if (GetCurrentPrice(options.Pair) <= 0)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: invalid price";
                return(false);
            }
            else if (Account.GetBalance() < options.MaxCost)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: not enough balance";
                return(false);
            }
            else if (options.Amount == null && options.MaxCost == null || options.Amount != null && options.MaxCost != null)
            {
                message = $"Cancel buy request for {options.Pair}. Reason: either max cost or amount needs to be specified (not both)";
            }
            else if (!options.ManualOrder && !options.Swap && pairConfig.BuySamePairTimeout > 0 && OrderHistory.Any(h => h.Side == OrderSide.Buy && h.Pair == options.Pair) &&
                     (DateTimeOffset.Now - OrderHistory.Where(h => h.Pair == options.Pair).Max(h => h.Date)).TotalSeconds < pairConfig.BuySamePairTimeout)
            {
                var elapsedSeconds = (DateTimeOffset.Now - OrderHistory.Where(h => h.Pair == options.Pair).Max(h => h.Date)).TotalSeconds;
                message = $"Cancel buy request for {options.Pair}. Reason: buy same pair timeout (elapsed: {elapsedSeconds:0.#}, timeout: {pairConfig.BuySamePairTimeout:0.#})";
                return(false);
            }

            message = null;
            return(true);
        }
Example #2
0
        public void Buy(BuyOptions options)
        {
            lock (syncRoot)
            {
                PauseTasks();
                try
                {
                    IRule       rule       = signalsService.Rules.Entries.FirstOrDefault(r => r.Name == options.Metadata.SignalRule);
                    RuleAction  ruleAction = rule?.Action ?? RuleAction.Default;
                    IPairConfig pairConfig = GetPairConfig(options.Pair);

                    bool arbitragePair = pairConfig.ArbitrageEnabled && pairConfig.ArbitrageSignalRules.Contains(options.Metadata.SignalRule);
                    if (arbitragePair)
                    {
                        Arbitrage arbitrage = Exchange.GetArbitrage(options.Pair, Config.Market, pairConfig.ArbitrageMarkets, pairConfig.ArbitrageType);
                        if (arbitrage.IsAssigned)
                        {
                            Arbitrage(new ArbitrageOptions(options.Pair, arbitrage, options.Metadata));
                        }
                    }
                    else
                    {
                        ITradingPair swappedPair = Account.GetTradingPairs().OrderBy(p => p.CurrentMargin).FirstOrDefault(tradingPair =>
                        {
                            IPairConfig tradingPairConfig = GetPairConfig(tradingPair.Pair);
                            return(tradingPairConfig.SellEnabled && tradingPairConfig.SwapEnabled && tradingPairConfig.SwapSignalRules != null &&
                                   tradingPairConfig.SwapSignalRules.Contains(options.Metadata.SignalRule) &&
                                   tradingPairConfig.SwapTimeout < (DateTimeOffset.Now - tradingPair.OrderDates.DefaultIfEmpty().Max()).TotalSeconds);
                        });

                        if (swappedPair != null)
                        {
                            Swap(new SwapOptions(swappedPair.Pair, options.Pair, options.Metadata));
                        }
                        else if (ruleAction == RuleAction.Default)
                        {
                            if (CanBuy(options, out string message))
                            {
                                tradingTimedTask.InitiateBuy(options);
                            }
                            else
                            {
                                loggingService.Debug(message);
                            }
                        }
                    }
                }
                finally
                {
                    ContinueTasks();
                }
            }
        }
Example #3
0
        public bool CanSell(SellOptions options, out string message)
        {
            IPairConfig pairConfig = GetPairConfig(options.Pair);

            if (!options.ManualOrder && !options.Arbitrage && IsTradingSuspended)
            {
                message = $"Cancel sell request for {options.Pair}. Reason: trading suspended";
                return(false);
            }
            else if (!options.ManualOrder && !options.Arbitrage && !pairConfig.SellEnabled)
            {
                message = $"Cancel sell request for {options.Pair}. Reason: selling not enabled";
                return(false);
            }
            else if (!options.ManualOrder && !options.Arbitrage && Config.ExcludedPairs.Contains(options.Pair))
            {
                message = $"Cancel sell request for {options.Pair}. Reason: excluded pair";
                return(false);
            }
            else if (!Account.HasTradingPair(options.Pair, includeDust: true) && !Account.HasTradingPair(NormalizePair(options.Pair), includeDust: true))
            {
                message = $"Cancel sell request for {options.Pair}. Reason: pair does not exist";
                return(false);
            }
            else if (options.Price != null && options.Price <= 0)
            {
                message = $"Cancel sell request for {options.Pair}. Reason: invalid price";
                return(false);
            }
            else if (options.Amount != null && options.Amount <= 0)
            {
                message = $"Cancel sell request for {options.Pair}. Reason: invalid amount";
                return(false);
            }
            else if (options.Amount != null && options.Price != null && (options.Amount * options.Price) < Config.MinCost)
            {
                message = $"Cancel sell request for {options.Pair}. Reason: dust";
                return(false);
            }
            else if (!options.ManualOrder && !options.Arbitrage && (DateTimeOffset.Now - Account.GetTradingPair(options.Pair, includeDust: true).OrderDates.DefaultIfEmpty().Max()).
                     TotalMilliseconds < (MIN_INTERVAL_BETWEEN_BUY_AND_SELL / Application.Speed))
            {
                message = $"Cancel sell request for {options.Pair}. Reason: pair just bought";
                return(false);
            }
            message = null;
            return(true);
        }
        public void InitiateBuy(BuyOptions options)
        {
            IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);

            if (!options.ManualOrder && !options.Swap && pairConfig.BuyTrailing != 0)
            {
                if (!trailingBuys.ContainsKey(options.Pair))
                {
                    trailingSells.TryRemove(options.Pair, out SellTrailingInfo sellTrailingInfo);

                    ITradingPair tradingPair   = tradingService.Account.GetTradingPair(options.Pair);
                    decimal      currentPrice  = tradingService.GetCurrentPrice(options.Pair);
                    decimal      currentMargin = 0;

                    var trailingInfo = new BuyTrailingInfo
                    {
                        BuyOptions         = options,
                        Trailing           = pairConfig.BuyTrailing,
                        TrailingStopMargin = pairConfig.BuyTrailingStopMargin,
                        TrailingStopAction = pairConfig.BuyTrailingStopAction,
                        InitialPrice       = currentPrice,
                        LastTrailingMargin = currentMargin,
                        BestTrailingMargin = currentMargin
                    };

                    if (trailingBuys.TryAdd(options.Pair, trailingInfo))
                    {
                        if (LoggingEnabled)
                        {
                            loggingService.Info($"Start trailing buy {tradingPair?.FormattedName ?? options.Pair}. Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}");
                        }
                    }
                }
                else
                {
                    if (LoggingEnabled)
                    {
                        //loggingService.Info($"Cancel trailing buy {tradingPair?.FormattedName ?? pair}. Reason: already trailing");
                    }
                }
            }
            else
            {
                PlaceBuyOrder(options);
            }
        }
        public void InitiateSell(SellOptions options)
        {
            IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);

            if (!options.ManualOrder && !options.Swap && pairConfig.SellTrailing != 0)
            {
                if (!trailingSells.ContainsKey(options.Pair))
                {
                    trailingBuys.TryRemove(options.Pair, out BuyTrailingInfo buyTrailingInfo);

                    ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                    tradingPair.SetCurrentValues(tradingService.GetCurrentPrice(options.Pair), tradingService.GetCurrentSpread(options.Pair));

                    var trailingInfo = new SellTrailingInfo
                    {
                        SellOptions        = options,
                        SellMargin         = pairConfig.SellMargin,
                        Trailing           = pairConfig.SellTrailing,
                        TrailingStopMargin = pairConfig.SellTrailingStopMargin,
                        TrailingStopAction = pairConfig.SellTrailingStopAction,
                        InitialPrice       = tradingPair.CurrentPrice,
                        LastTrailingMargin = tradingPair.CurrentMargin,
                        BestTrailingMargin = tradingPair.CurrentMargin
                    };

                    if (trailingSells.TryAdd(options.Pair, trailingInfo))
                    {
                        if (LoggingEnabled)
                        {
                            loggingService.Info($"Start trailing sell {tradingPair.FormattedName}. Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}");
                        }
                    }
                }
                else
                {
                    if (LoggingEnabled)
                    {
                        //loggingService.Info($"Cancel trailing sell {tradingPair.FormattedName}. Reason: already trailing");
                    }
                }
            }
            else
            {
                PlaceSellOrder(options);
            }
        }
        public void InitiateSell(SellOptions options)
        {
            if (tradingService.Account.HasTradingPair(options.Pair))
            {
                IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);
                if (!options.ManualOrder && pairConfig.SellTrailing != 0)
                {
                    if (!trailingSells.ContainsKey(options.Pair))
                    {
                        StopTrailingBuy(options.Pair);
                        ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                        tradingPair.SetCurrentValues(tradingService.GetPrice(options.Pair), tradingService.Exchange.GetPriceSpread(options.Pair));

                        var trailingInfo = new SellTrailingInfo
                        {
                            SellOptions        = options,
                            SellMargin         = pairConfig.SellMargin,
                            Trailing           = pairConfig.SellTrailing,
                            TrailingStopMargin = pairConfig.SellTrailingStopMargin,
                            TrailingStopAction = pairConfig.SellTrailingStopAction,
                            InitialPrice       = tradingPair.CurrentPrice,
                            LastTrailingMargin = tradingPair.CurrentMargin,
                            BestTrailingMargin = tradingPair.CurrentMargin
                        };

                        if (trailingSells.TryAdd(options.Pair, trailingInfo))
                        {
                            if (LoggingEnabled)
                            {
                                loggingService.Info($"Start trailing sell {tradingPair.FormattedName}. " +
                                                    $"Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}");
                            }
                        }
                    }
                }
                else
                {
                    orderingService.PlaceSellOrder(options);
                }
            }
            else
            {
                loggingService.Info($"Cancel initiate sell for {options.Pair}. Reason: pair does not exist");
            }
        }
        public void InitiateBuy(BuyOptions options)
        {
            IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);

            if (!options.ManualOrder && pairConfig.BuyTrailing != 0)
            {
                if (!trailingBuys.ContainsKey(options.Pair))
                {
                    StopTrailingSell(options.Pair);
                    decimal currentPrice  = tradingService.GetPrice(options.Pair);
                    decimal currentMargin = 0;

                    var trailingInfo = new BuyTrailingInfo
                    {
                        BuyOptions         = options,
                        Trailing           = pairConfig.BuyTrailing,
                        TrailingStopMargin = pairConfig.BuyTrailingStopMargin,
                        TrailingStopAction = pairConfig.BuyTrailingStopAction,
                        InitialPrice       = currentPrice,
                        LastTrailingMargin = currentMargin,
                        BestTrailingMargin = currentMargin
                    };

                    if (trailingBuys.TryAdd(options.Pair, trailingInfo))
                    {
                        if (LoggingEnabled)
                        {
                            ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                            loggingService.Info($"Start trailing buy {tradingPair?.FormattedName ?? options.Pair}. " +
                                                $"Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}");
                        }
                    }
                }
            }
            else
            {
                orderingService.PlaceBuyOrder(options);
            }
        }
Example #8
0
        public IOrderDetails PlaceBuyOrder(BuyOptions options)
        {
            OrderDetails orderDetails = new OrderDetails();

            tradingService.StopTrailingBuy(options.Pair);
            tradingService.StopTrailingSell(options.Pair);

            try
            {
                ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair, includeDust: true);
                options.Price  = tradingService.GetPrice(options.Pair, TradePriceType.Ask, normalize: false);
                options.Amount = options.Amount ?? (options.MaxCost.Value / (options.Pair.EndsWith(Constants.Markets.USDT) ? 1 : options.Price));
                options.Price  = tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value);
                options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value);

                if (tradingService.CanBuy(options, out string message))
                {
                    IPairConfig pairConfig = tradingService.GetPairConfig(options.Pair);
                    BuyOrder    buyOrder   = new BuyOrder
                    {
                        Type   = pairConfig.BuyType,
                        Date   = DateTimeOffset.Now,
                        Pair   = options.Pair,
                        Price  = options.Price.Value,
                        Amount = options.Amount.Value
                    };

                    lock (tradingService.Account.SyncRoot)
                    {
                        loggingService.Info($"Place buy order for {tradingPair?.FormattedName ?? options.Pair}. " +
                                            $"Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: " + (options.Metadata.SignalRule ?? "N/A"));

                        if (!tradingService.Config.VirtualTrading)
                        {
                            orderDetails = tradingService.Exchange.PlaceOrder(buyOrder) as OrderDetails;
                        }
                        else
                        {
                            string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair);
                            orderDetails = new OrderDetails
                            {
                                OrderId      = DateTime.Now.ToFileTimeUtc().ToString(),
                                Side         = OrderSide.Buy,
                                Result       = OrderResult.Filled,
                                Date         = buyOrder.Date,
                                Pair         = buyOrder.Pair,
                                Amount       = buyOrder.Amount,
                                AmountFilled = buyOrder.Amount,
                                Price        = buyOrder.Price,
                                AveragePrice = buyOrder.Price,
                                Fees         = buyOrder.Amount * buyOrder.Price * tradingService.Config.VirtualTradingFees,
                                FeesCurrency = pairMarket
                            };
                        }

                        NormalizeOrder(orderDetails, TradePriceType.Ask);
                        options.Metadata.TradingRules  = pairConfig.Rules.ToList();
                        options.Metadata.LastBuyMargin = options.Metadata.LastBuyMargin ?? tradingPair?.CurrentMargin ?? null;
                        orderDetails.Metadata          = options.Metadata;
                        tradingService.Account.AddBuyOrder(orderDetails);
                        tradingService.Account.Save();
                        tradingService.LogOrder(orderDetails);

                        decimal fees = tradingService.CalculateOrderFees(orderDetails);
                        tradingPair = tradingService.Account.GetTradingPair(orderDetails.Pair, includeDust: true);
                        loggingService.Info("{@Trade}", orderDetails);
                        loggingService.Info($"Buy order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). " +
                                            $"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, " +
                                            $"Filled: {orderDetails.AmountFilled:0.########}, Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}");
                        notificationService.Notify($"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, " +
                                                   $"Price: {orderDetails.AveragePrice:0.00000000}, Cost: {(orderDetails.Cost + fees):0.00000000}");
                    }

                    tradingService.ReapplyTradingRules();
                }
                else
                {
                    loggingService.Info(message);
                }
            }
            catch (Exception ex)
            {
                loggingService.Error($"Unable to place buy order for {options.Pair}", ex);
                notificationService.Notify($"Unable to buy {options.Pair}: {ex.Message}");
            }
            return(orderDetails);
        }
Example #9
0
        public IOrderDetails PlaceSellOrder(SellOptions options)
        {
            OrderDetails orderDetails = new OrderDetails();

            tradingService.StopTrailingSell(options.Pair);
            tradingService.StopTrailingBuy(options.Pair);

            try
            {
                string       normalizedPair = tradingService.NormalizePair(options.Pair);
                ITradingPair tradingPair    = tradingService.Account.GetTradingPair(normalizedPair, includeDust: true);
                options.Price  = tradingService.GetPrice(options.Pair, TradePriceType.Bid);
                options.Amount = options.Amount ?? tradingPair?.Amount ?? 0;
                options.Price  = options.Price != 1 ? tradingService.Exchange.ClampOrderPrice(options.Pair, options.Price.Value) : 1; // 1 = USDT price
                options.Amount = tradingService.Exchange.ClampOrderAmount(options.Pair, options.Amount.Value);

                if (tradingService.CanSell(options, out string message))
                {
                    IPairConfig pairConfig = tradingService.GetPairConfig(normalizedPair);
                    SellOrder   sellOrder  = new SellOrder
                    {
                        Type   = pairConfig.SellType,
                        Date   = DateTimeOffset.Now,
                        Pair   = options.Pair,
                        Price  = options.Price.Value,
                        Amount = options.Amount.Value
                    };

                    lock (tradingService.Account.SyncRoot)
                    {
                        tradingPair.SetCurrentValues(tradingService.GetPrice(normalizedPair), tradingService.Exchange.GetPriceSpread(normalizedPair));
                        string sellPairName = normalizedPair != options.Pair ? options.Pair : tradingPair.FormattedName;
                        loggingService.Info($"Place sell order for {sellPairName}. " +
                                            $"Price: {sellOrder.Price:0.00000000}, Amount: {sellOrder.Amount:0.########}, Margin: {tradingPair.CurrentMargin:0.00}");

                        if (!tradingService.Config.VirtualTrading)
                        {
                            orderDetails = tradingService.Exchange.PlaceOrder(sellOrder) as OrderDetails;
                        }
                        else
                        {
                            string pairMarket = tradingService.Exchange.GetPairMarket(options.Pair);
                            orderDetails = new OrderDetails
                            {
                                OrderId      = DateTime.Now.ToFileTimeUtc().ToString(),
                                Side         = OrderSide.Sell,
                                Result       = OrderResult.Filled,
                                Date         = sellOrder.Date,
                                Pair         = sellOrder.Pair,
                                Amount       = sellOrder.Amount,
                                AmountFilled = sellOrder.Amount,
                                Price        = sellOrder.Price,
                                AveragePrice = sellOrder.Price,
                                Fees         = sellOrder.Amount * sellOrder.Price * tradingService.Config.VirtualTradingFees,
                                FeesCurrency = pairMarket
                            };
                        }

                        NormalizeOrder(orderDetails, TradePriceType.Bid);
                        tradingPair.SetMetadata(tradingPair.Metadata.MergeWith(options.Metadata));
                        orderDetails.Metadata = tradingPair.Metadata;
                        var tradeResult = tradingService.Account.AddSellOrder(orderDetails) as TradeResult;
                        tradeResult.IsSwap      = options.Swap;
                        tradeResult.IsArbitrage = options.Arbitrage;
                        tradingService.Account.Save();
                        tradingService.LogOrder(orderDetails);

                        decimal fees      = tradingService.CalculateOrderFees(orderDetails);
                        decimal margin    = (tradeResult.Profit / (tradeResult.Cost + (tradeResult.Metadata.AdditionalCosts ?? 0)) * 100);
                        string  swapPair  = options.Metadata.SwapPair != null ? $", Swap Pair: {options.Metadata.SwapPair}" : "";
                        string  arbitrage = options.Metadata.Arbitrage != null ? $", Arbitrage: {options.Metadata.Arbitrage} ({options.Metadata.ArbitragePercentage:0.00})" : "";
                        loggingService.Info("{@Trade}", orderDetails);
                        loggingService.Info("{@Trade}", tradeResult);
                        loggingService.Info($"Sell order result for {orderDetails.OriginalPair ?? tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). " +
                                            $"Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Filled: {orderDetails.AmountFilled:0.########}, " +
                                            $"Cost: {orderDetails.Cost:0.00000000}, Fees: {fees:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}");
                        notificationService.Notify($"Sold {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, " +
                                                   $"Price: {orderDetails.AveragePrice:0.00000000}, Margin: {margin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}{arbitrage}");
                    }

                    tradingService.ReapplyTradingRules();
                }
                else
                {
                    loggingService.Info(message);
                }
            }
            catch (Exception ex)
            {
                loggingService.Error($"Unable to place sell order for {options.Pair}", ex);
                notificationService.Notify($"Unable to sell {options.Pair}: {ex.Message}");
            }
            return(orderDetails);
        }
        private PairConfig CreatePairConfig(string pair, ITradingConfig modifiedTradingConfig, IPairConfig modifiedPairConfig, IEnumerable <IRule> appliedRules)
        {
            ITradingPair tradingPair     = tradingService.Account.GetTradingPair(pair);
            DCALevel     currentDCALevel = GetCurrentDCALevel(tradingPair, modifiedTradingConfig.DCALevels);
            DCALevel     nextDCALevel    = GetNextDCALevel(tradingPair, modifiedTradingConfig.DCALevels, modifiedTradingConfig.RepeatLastDCALevel);

            return(new PairConfig
            {
                Rules = appliedRules.Select(r => r.Name),

                MaxPairs = modifiedTradingConfig.MaxPairs,

                BuyEnabled = tradingPair == null ? modifiedTradingConfig.BuyEnabled : modifiedTradingConfig.BuyDCAEnabled,
                BuyType = modifiedTradingConfig.BuyType,
                BuyMaxCost = modifiedTradingConfig.BuyMaxCost,
                BuyMultiplier = tradingPair == null ? (modifiedTradingConfig.BuyMultiplier != 0 ? modifiedTradingConfig.BuyMultiplier : 1) : nextDCALevel?.BuyMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier,
                BuyMinBalance = tradingPair == null ? modifiedTradingConfig.BuyMinBalance : modifiedTradingConfig.BuyDCAMinBalance,
                BuySamePairTimeout = (tradingPair == null ? modifiedTradingConfig.BuySamePairTimeout : nextDCALevel?.BuySamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout) / Application.Speed,
                BuyTrailing = tradingPair == null ? modifiedTradingConfig.BuyTrailing : nextDCALevel?.BuyTrailing ?? modifiedTradingConfig.BuyDCATrailing,
                BuyTrailingStopMargin = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopMargin : nextDCALevel?.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin,
                BuyTrailingStopAction = tradingPair == null ? modifiedTradingConfig.BuyTrailingStopAction : nextDCALevel?.BuyTrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction,

                SellEnabled = modifiedTradingConfig.SellEnabled,
                SellType = modifiedTradingConfig.SellType,
                SellMargin = currentDCALevel == null ? modifiedTradingConfig.SellMargin : currentDCALevel?.SellMargin ?? modifiedTradingConfig.SellDCAMargin,
                SellTrailing = currentDCALevel == null ? modifiedTradingConfig.SellTrailing : currentDCALevel?.SellTrailing ?? modifiedTradingConfig.SellDCATrailing,
                SellTrailingStopMargin = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopMargin : currentDCALevel?.SellTrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin,
                SellTrailingStopAction = currentDCALevel == null ? modifiedTradingConfig.SellTrailingStopAction : currentDCALevel?.SellTrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction,
                SellStopLossEnabled = modifiedTradingConfig.SellStopLossEnabled,
                SellStopLossAfterDCA = modifiedTradingConfig.SellStopLossAfterDCA,
                SellStopLossMinAge = modifiedTradingConfig.SellStopLossMinAge / Application.Speed,
                SellStopLossMargin = modifiedTradingConfig.SellStopLossMargin,

                SwapEnabled = modifiedPairConfig.SwapEnabled,
                SwapSignalRules = modifiedPairConfig.SwapSignalRules,
                SwapTimeout = (int)Math.Round(modifiedPairConfig.SwapTimeout / Application.Speed),

                ArbitrageEnabled = modifiedPairConfig.ArbitrageEnabled,
                ArbitrageMarkets = modifiedPairConfig.ArbitrageMarkets,
                ArbitrageType = modifiedPairConfig.ArbitrageType,
                ArbitrageBuyMultiplier = modifiedPairConfig.ArbitrageBuyMultiplier,
                ArbitrageSellMultiplier = modifiedPairConfig.ArbitrageSellMultiplier,
                ArbitrageSignalRules = modifiedPairConfig.ArbitrageSignalRules,

                CurrentDCAMargin = currentDCALevel?.Margin,
                NextDCAMargin = nextDCALevel?.Margin
            });
        }
Example #11
0
        private void ArbitrageReverse(ArbitrageOptions options)
        {
            string       marketPair            = Exchange.GetArbitrageMarketPair(options.Arbitrage.Market);
            ITradingPair existingMarketPair    = Account.GetTradingPair(marketPair);
            IPairConfig  pairConfig            = GetPairConfig(options.Pair);
            bool         useExistingMarketPair = (existingMarketPair != null && existingMarketPair.CurrentCost > pairConfig.BuyMaxCost &&
                                                  existingMarketPair.AveragePrice <= existingMarketPair.CurrentPrice);

            var buyMarketPairOptions = new BuyOptions(marketPair)
            {
                Arbitrage     = true,
                MaxCost       = pairConfig.BuyMaxCost,
                ManualOrder   = options.ManualOrder,
                IgnoreBalance = useExistingMarketPair,
                Metadata      = options.Metadata
            };

            if (CanBuy(buyMarketPairOptions, out string message))
            {
                IOrderDetails buyMarketPairOrderDetails = null;
                if (useExistingMarketPair)
                {
                    buyMarketPairOrderDetails = Account.AddBlankOrder(buyMarketPairOptions.Pair,
                                                                      buyMarketPairOptions.MaxCost.Value / GetPrice(buyMarketPairOptions.Pair, TradePriceType.Ask),
                                                                      includeFees: false);
                    loggingService.Info($"Use existing market pair for arbitrage: {marketPair}. " +
                                        $"Average price: {existingMarketPair.AveragePrice}, Current price: {existingMarketPair.CurrentPrice}");
                }
                else
                {
                    buyMarketPairOrderDetails = orderingService.PlaceBuyOrder(buyMarketPairOptions);
                }

                if (buyMarketPairOrderDetails.Result == OrderResult.Filled)
                {
                    decimal buyArbitragePairMultiplier = pairConfig.ArbitrageBuyMultiplier ?? DEFAULT_ARBITRAGE_BUY_MULTIPLIER;
                    decimal buyMarketPairFees          = CalculateOrderFees(buyMarketPairOrderDetails);
                    string  arbitragePair          = Exchange.ChangeMarket(options.Pair, options.Arbitrage.Market.ToString());
                    decimal buyArbitragePairAmount = options.Arbitrage.Market == ArbitrageMarket.USDT ?
                                                     buyMarketPairOrderDetails.AmountFilled * GetPrice(buyMarketPairOrderDetails.Pair, TradePriceType.Ask, normalize: false) / GetPrice(arbitragePair, TradePriceType.Ask) :
                                                     buyMarketPairOrderDetails.AmountFilled / GetPrice(arbitragePair, TradePriceType.Ask);

                    var buyArbitragePairOptions = new BuyOptions(arbitragePair)
                    {
                        Arbitrage   = true,
                        ManualOrder = options.ManualOrder,
                        Amount      = buyArbitragePairAmount * buyArbitragePairMultiplier,
                        Metadata    = options.Metadata
                    };

                    IOrderDetails buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions);
                    if (buyArbitragePairOrderDetails.Result == OrderResult.Filled)
                    {
                        decimal buyArbitragePairFees = CalculateOrderFees(buyArbitragePairOrderDetails);
                        options.Metadata.FeesNonDeductible = buyMarketPairFees * buyArbitragePairMultiplier;
                        var sellArbitragePairOptions = new SellOptions(buyArbitragePairOrderDetails.Pair)
                        {
                            Arbitrage   = true,
                            Amount      = buyArbitragePairOrderDetails.AmountFilled,
                            ManualOrder = options.ManualOrder,
                            Metadata    = options.Metadata
                        };

                        TradingPair existingArbitragePair = Account.GetTradingPair(buyArbitragePairOrderDetails.Pair) as TradingPair;
                        existingArbitragePair.OverrideCost(buyArbitragePairOrderDetails.Cost + buyArbitragePairFees * 2);
                        IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions);
                        existingArbitragePair.OverrideCost(null);

                        if (sellArbitragePairOrderDetails.Result == OrderResult.Filled)
                        {
                            loggingService.Info($"{pairConfig.ArbitrageType} arbitrage successful: {marketPair} -> {arbitragePair} -> {existingArbitragePair.Pair}");
                        }
                        else
                        {
                            loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {arbitragePair}");
                            notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {arbitragePair}");
                        }
                    }
                    else
                    {
                        loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}");
                        notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to buy arbitrage pair {arbitragePair}");
                    }
                }
                else
                {
                    loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy market pair {marketPair}");
                }
            }
            else
            {
                loggingService.Info($"Unable to arbitrage {options.Pair}: {message}");
            }
        }
        public IOrderDetails PlaceSellOrder(SellOptions options)
        {
            IOrderDetails orderDetails = null;

            trailingSells.TryRemove(options.Pair, out SellTrailingInfo sellTrailingInfo);
            trailingBuys.TryRemove(options.Pair, out BuyTrailingInfo buyTrailingInfo);

            if (tradingService.CanSell(options, out string message))
            {
                IPairConfig  pairConfig  = tradingService.GetPairConfig(options.Pair);
                ITradingPair tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                tradingPair.SetCurrentValues(tradingService.GetCurrentPrice(options.Pair), tradingService.GetCurrentSpread(options.Pair));

                SellOrder sellOrder = new SellOrder
                {
                    Type   = pairConfig.SellType,
                    Date   = DateTimeOffset.Now,
                    Pair   = options.Pair,
                    Amount = options.Amount ?? tradingPair.TotalAmount,
                    Price  = tradingPair.CurrentPrice
                };

                if (!tradingService.Config.VirtualTrading)
                {
                    loggingService.Info($"Place sell order for {tradingPair.FormattedName}. Price: {sellOrder.Price:0.00000000}, Amount: {sellOrder.Amount:0.########}, Margin: {tradingPair.CurrentMargin:0.00}");

                    try
                    {
                        lock (tradingService.Account.SyncRoot)
                        {
                            orderDetails = tradingService.PlaceOrder(sellOrder);
                            tradingPair.Metadata.SwapPair = options.SwapPair;
                            orderDetails.SetMetadata(tradingPair.Metadata);
                            ITradeResult tradeResult = tradingService.Account.AddSellOrder(orderDetails);
                            tradeResult.SetSwap(options.Swap);
                            tradingService.Account.Save();
                            tradingService.LogOrder(orderDetails);

                            decimal soldMargin = (tradeResult.Profit / (tradeResult.AverageCost + (tradeResult.Metadata.AdditionalCosts ?? 0)) * 100);
                            string  swapPair   = options.SwapPair != null ? $", Swap Pair: {options.SwapPair}" : "";
                            loggingService.Info("{@Trade}", orderDetails);
                            loggingService.Info("{@Trade}", tradeResult);
                            loggingService.Info($"Sell order result for {tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Filled: {orderDetails.AmountFilled:0.########}, Margin: {soldMargin:0.00}, Profit: {tradeResult.Profit:0.00000000}");
                            notificationService.Notify($"Sold {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, Price: {orderDetails.AveragePrice:0.00000000}, Margin: {soldMargin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}");
                        }
                    }
                    catch (Exception ex)
                    {
                        loggingService.Error($"Unable to place sell order for {options.Pair}", ex);
                        notificationService.Notify($"Unable to sell {options.Pair}: {ex.Message}");
                    }
                }
                else
                {
                    loggingService.Info($"Place virtual sell order for {tradingPair.FormattedName}. Price: {sellOrder.Price:0.00000000}, Amount: {sellOrder.Amount:0.########}");

                    lock (tradingService.Account.SyncRoot)
                    {
                        orderDetails = new OrderDetails
                        {
                            Metadata     = tradingPair.Metadata,
                            OrderId      = DateTime.Now.ToFileTimeUtc().ToString(),
                            Side         = OrderSide.Sell,
                            Result       = OrderResult.Filled,
                            Date         = sellOrder.Date,
                            Pair         = sellOrder.Pair,
                            Amount       = sellOrder.Amount,
                            AmountFilled = sellOrder.Amount,
                            Price        = sellOrder.Price,
                            AveragePrice = sellOrder.Price
                        };
                        tradingPair.Metadata.SwapPair = options.SwapPair;
                        ITradeResult tradeResult = tradingService.Account.AddSellOrder(orderDetails);
                        tradeResult.SetSwap(options.Swap);
                        tradingService.Account.Save();
                        tradingService.LogOrder(orderDetails);

                        decimal soldMargin = (tradeResult.Profit / (tradeResult.AverageCost + (tradeResult.Metadata.AdditionalCosts ?? 0)) * 100);
                        string  swapPair   = options.SwapPair != null ? $", Swap Pair: {options.SwapPair}" : "";
                        loggingService.Info("{@Trade}", orderDetails);
                        loggingService.Info("{@Trade}", tradeResult);
                        loggingService.Info($"Virtual sell order result for {tradingPair.FormattedName}. Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Margin: {tradingPair.CurrentMargin:0.00}, Profit: {tradeResult.Profit:0.00000000}");
                        notificationService.Notify($"Sold {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, Price: {orderDetails.AveragePrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}, Profit: {tradeResult.Profit:0.00000000}{swapPair}");
                    }
                }

                tradingService.ReapplyTradingRules();
            }
            else
            {
                loggingService.Info(message);
            }
            return(orderDetails);
        }
        public IOrderDetails PlaceBuyOrder(BuyOptions options)
        {
            IOrderDetails orderDetails = null;

            trailingBuys.TryRemove(options.Pair, out BuyTrailingInfo buyTrailingInfo);
            trailingSells.TryRemove(options.Pair, out SellTrailingInfo sellTrailingInfo);

            if (tradingService.CanBuy(options, out string message))
            {
                IPairConfig  pairConfig   = tradingService.GetPairConfig(options.Pair);
                ITradingPair tradingPair  = tradingService.Account.GetTradingPair(options.Pair);
                decimal      currentPrice = tradingService.GetCurrentPrice(options.Pair);
                options.Metadata.TradingRules = pairConfig.Rules.ToList();
                if (options.Metadata.LastBuyMargin == null)
                {
                    options.Metadata.LastBuyMargin = tradingPair?.CurrentMargin ?? 0;
                }
                string signalRule = options.Metadata.SignalRule ?? "N/A";

                BuyOrder buyOrder = new BuyOrder
                {
                    Type   = pairConfig.BuyType,
                    Date   = DateTimeOffset.Now,
                    Pair   = options.Pair,
                    Amount = options.Amount ?? (options.MaxCost.Value / currentPrice),
                    Price  = currentPrice
                };

                if (!tradingService.Config.VirtualTrading)
                {
                    loggingService.Info($"Place buy order for {tradingPair?.FormattedName ?? options.Pair}. Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: {signalRule}");

                    try
                    {
                        lock (tradingService.Account.SyncRoot)
                        {
                            orderDetails = tradingService.PlaceOrder(buyOrder);
                            orderDetails.SetMetadata(options.Metadata);
                            tradingService.Account.AddBuyOrder(orderDetails);
                            tradingService.Account.Save();
                            tradingService.LogOrder(orderDetails);

                            tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                            loggingService.Info("{@Trade}", orderDetails);
                            loggingService.Info($"Buy order result for {tradingPair.FormattedName}: {orderDetails.Result} ({orderDetails.Message}). Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Filled: {orderDetails.AmountFilled:0.########}, Cost: {orderDetails.AverageCost:0.00000000}");
                            notificationService.Notify($"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, Price: {orderDetails.AveragePrice:0.00000000}, Cost: {orderDetails.AverageCost:0.00000000}");
                        }
                    }
                    catch (Exception ex)
                    {
                        loggingService.Error($"Unable to place buy order for {options.Pair}", ex);
                        notificationService.Notify($"Unable to buy {options.Pair}: {ex.Message}");
                    }
                }
                else
                {
                    loggingService.Info($"Place virtual buy order for {tradingPair?.FormattedName ?? options.Pair}. Price: {buyOrder.Price:0.00000000}, Amount: {buyOrder.Amount:0.########}, Signal Rule: {signalRule}");

                    lock (tradingService.Account.SyncRoot)
                    {
                        decimal roundedAmount = Math.Round(buyOrder.Amount, 4);
                        orderDetails = new OrderDetails
                        {
                            Metadata     = options.Metadata,
                            OrderId      = DateTime.Now.ToFileTimeUtc().ToString(),
                            Side         = OrderSide.Buy,
                            Result       = OrderResult.Filled,
                            Date         = buyOrder.Date,
                            Pair         = buyOrder.Pair,
                            Amount       = roundedAmount,
                            AmountFilled = roundedAmount,
                            Price        = buyOrder.Price,
                            AveragePrice = buyOrder.Price
                        };
                        tradingService.Account.AddBuyOrder(orderDetails);
                        tradingService.Account.Save();
                        tradingService.LogOrder(orderDetails);

                        tradingPair = tradingService.Account.GetTradingPair(options.Pair);
                        loggingService.Info("{@Trade}", orderDetails);
                        loggingService.Info($"Virtual buy order result for {tradingPair.FormattedName}. Price: {orderDetails.AveragePrice:0.00000000}, Amount: {orderDetails.Amount:0.########}, Cost: {orderDetails.AverageCost:0.00000000}");
                        notificationService.Notify($"Bought {tradingPair.FormattedName}. Amount: {orderDetails.AmountFilled:0.########}, Price: {orderDetails.AveragePrice:0.00000000}, Cost: {orderDetails.AverageCost:0.00000000}");
                    }
                }

                tradingService.ReapplyTradingRules();
            }
            else
            {
                loggingService.Info(message);
            }
            return(orderDetails);
        }
        private void ProcessTradingPairs()
        {
            int traidingPairsCount = 0;

            foreach (var tradingPair in tradingService.Account.GetTradingPairs())
            {
                IPairConfig pairConfig = tradingService.GetPairConfig(tradingPair.Pair);
                tradingPair.SetCurrentValues(tradingService.GetCurrentPrice(tradingPair.Pair), tradingService.GetCurrentSpread(tradingPair.Pair));
                tradingPair.Metadata.TradingRules  = pairConfig.Rules.ToList();
                tradingPair.Metadata.CurrentRating = tradingPair.Metadata.Signals != null?signalsService.GetRating(tradingPair.Pair, tradingPair.Metadata.Signals) : null;

                tradingPair.Metadata.CurrentGlobalRating = signalsService.GetGlobalRating();

                if (trailingSells.TryGetValue(tradingPair.Pair, out SellTrailingInfo sellTrailingInfo))
                {
                    if (pairConfig.SellEnabled)
                    {
                        if (Math.Round(tradingPair.CurrentMargin, 1) != Math.Round(sellTrailingInfo.LastTrailingMargin, 1))
                        {
                            if (LoggingEnabled)
                            {
                                loggingService.Info($"Continue trailing sell {tradingPair.FormattedName}. Price: {tradingPair.CurrentPrice:0.00000000}, Margin: {tradingPair.CurrentMargin:0.00}");
                            }
                        }

                        if (tradingPair.CurrentMargin <= sellTrailingInfo.TrailingStopMargin || tradingPair.CurrentMargin < (sellTrailingInfo.BestTrailingMargin - sellTrailingInfo.Trailing))
                        {
                            trailingSells.TryRemove(tradingPair.Pair, out SellTrailingInfo p);

                            if (tradingPair.CurrentMargin > 0 || sellTrailingInfo.SellMargin < 0)
                            {
                                if (sellTrailingInfo.TrailingStopAction == SellTrailingStopAction.Sell || tradingPair.CurrentMargin > sellTrailingInfo.TrailingStopMargin)
                                {
                                    PlaceSellOrder(sellTrailingInfo.SellOptions);
                                }
                                else
                                {
                                    if (LoggingEnabled)
                                    {
                                        loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: stop margin reached");
                                    }
                                }
                            }
                            else
                            {
                                if (LoggingEnabled)
                                {
                                    loggingService.Info($"Stop trailing sell {tradingPair.FormattedName}. Reason: negative margin");
                                }
                            }
                        }
                        else
                        {
                            sellTrailingInfo.LastTrailingMargin = tradingPair.CurrentMargin;
                            if (tradingPair.CurrentMargin > sellTrailingInfo.BestTrailingMargin)
                            {
                                sellTrailingInfo.BestTrailingMargin = tradingPair.CurrentMargin;
                            }
                        }
                    }
                    else
                    {
                        trailingSells.TryRemove(tradingPair.Pair, out SellTrailingInfo p);
                    }
                }
                else
                {
                    if (pairConfig.SellEnabled && tradingPair.CurrentMargin >= pairConfig.SellMargin)
                    {
                        InitiateSell(new SellOptions(tradingPair.Pair));
                    }
                    else if (pairConfig.SellEnabled && pairConfig.SellStopLossEnabled && tradingPair.CurrentMargin <= pairConfig.SellStopLossMargin && tradingPair.CurrentAge >= pairConfig.SellStopLossMinAge &&
                             (pairConfig.NextDCAMargin == null || !pairConfig.SellStopLossAfterDCA))
                    {
                        if (LoggingEnabled)
                        {
                            loggingService.Info($"Stop loss triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}");
                        }
                        PlaceSellOrder(new SellOptions(tradingPair.Pair));
                    }
                    else if (pairConfig.NextDCAMargin != null && pairConfig.BuyEnabled && pairConfig.NextDCAMargin != null &&
                             !trailingBuys.ContainsKey(tradingPair.Pair) && !trailingSells.ContainsKey(tradingPair.Pair))
                    {
                        if (tradingPair.CurrentMargin <= pairConfig.NextDCAMargin)
                        {
                            var buyOptions = new BuyOptions(tradingPair.Pair)
                            {
                                MaxCost        = tradingPair.AverageCostPaid * pairConfig.BuyMultiplier,
                                IgnoreExisting = true
                            };

                            if (tradingService.CanBuy(buyOptions, message: out string message))
                            {
                                if (LoggingEnabled)
                                {
                                    loggingService.Info($"DCA triggered for {tradingPair.FormattedName}. Margin: {tradingPair.CurrentMargin:0.00}, Level: {pairConfig.NextDCAMargin:0.00}, Multiplier: {pairConfig.BuyMultiplier}");
                                }
                                InitiateBuy(buyOptions);
                            }
                        }
                    }
                }

                traidingPairsCount++;
            }

            foreach (var kvp in trailingBuys)
            {
                string          pair            = kvp.Key;
                BuyTrailingInfo buyTrailingInfo = kvp.Value;
                ITradingPair    tradingPair     = tradingService.Account.GetTradingPair(pair);
                IPairConfig     pairConfig      = tradingService.GetPairConfig(pair);
                decimal         currentPrice    = tradingService.GetCurrentPrice(pair);
                decimal         currentMargin   = Utils.CalculateMargin(buyTrailingInfo.InitialPrice, currentPrice);

                if (pairConfig.BuyEnabled)
                {
                    if (Math.Round(currentMargin, 1) != Math.Round(buyTrailingInfo.LastTrailingMargin, 1))
                    {
                        if (LoggingEnabled)
                        {
                            loggingService.Info($"Continue trailing buy {tradingPair?.FormattedName ?? pair}. Price: {currentPrice:0.00000000}, Margin: {currentMargin:0.00}");
                        }
                    }

                    if (currentMargin >= buyTrailingInfo.TrailingStopMargin || currentMargin > (buyTrailingInfo.BestTrailingMargin - buyTrailingInfo.Trailing))
                    {
                        trailingBuys.TryRemove(pair, out BuyTrailingInfo p);

                        if (buyTrailingInfo.TrailingStopAction == BuyTrailingStopAction.Buy || currentMargin < buyTrailingInfo.TrailingStopMargin)
                        {
                            PlaceBuyOrder(buyTrailingInfo.BuyOptions);
                        }
                        else
                        {
                            if (LoggingEnabled)
                            {
                                loggingService.Info($"Stop trailing buy {tradingPair?.FormattedName ?? pair}. Reason: stop margin reached");
                            }
                        }
                    }
                    else
                    {
                        buyTrailingInfo.LastTrailingMargin = currentMargin;
                        if (currentMargin < buyTrailingInfo.BestTrailingMargin)
                        {
                            buyTrailingInfo.BestTrailingMargin = currentMargin;
                        }
                    }
                }
                else
                {
                    trailingBuys.TryRemove(pair, out BuyTrailingInfo p);
                }
            }

            healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingPairsProcessed, $"Pairs: {traidingPairsCount}, Trailing buys: {trailingBuys.Count}, Trailing sells: {trailingSells.Count}");
        }
Example #15
0
        private void ArbitrageDirect(ArbitrageOptions options)
        {
            string       arbitragePair            = options.Pair;
            ITradingPair existingArbitragePair    = this.Account.GetTradingPair(arbitragePair);
            IPairConfig  pairConfig               = this.GetPairConfig(options.Pair);
            bool         useExistingArbitragePair = (existingArbitragePair != null && existingArbitragePair.CurrentCost > pairConfig.BuyMaxCost &&
                                                     existingArbitragePair.AveragePrice <= existingArbitragePair.CurrentPrice);

            var buyArbitragePairOptions = new BuyOptions(arbitragePair)
            {
                Arbitrage     = true,
                MaxCost       = pairConfig.BuyMaxCost,
                ManualOrder   = options.ManualOrder,
                IgnoreBalance = useExistingArbitragePair,
                Metadata      = options.Metadata
            };

            if (this.CanBuy(buyArbitragePairOptions, out string message))
            {
                IOrderDetails buyArbitragePairOrderDetails = null;
                if (useExistingArbitragePair)
                {
                    buyArbitragePairOrderDetails = this.Account.AddBlankOrder(buyArbitragePairOptions.Pair,
                                                                              buyArbitragePairOptions.MaxCost.Value / this.GetPrice(buyArbitragePairOptions.Pair, TradePriceType.Ask),
                                                                              includeFees: false);
                    loggingService.Info($"Use existing arbitrage pair for arbitrage: {arbitragePair}. " +
                                        $"Average price: {existingArbitragePair.AveragePrice}, Current price: {existingArbitragePair.CurrentPrice}");
                }
                else
                {
                    buyArbitragePairOrderDetails = orderingService.PlaceBuyOrder(buyArbitragePairOptions);
                }

                if (buyArbitragePairOrderDetails.Result == OrderResult.Filled)
                {
                    decimal buyArbitragePairFees     = this.CalculateOrderFees(buyArbitragePairOrderDetails);
                    string  flippedArbitragePair     = this.Exchange.ChangeMarket(arbitragePair, options.Arbitrage.Market.ToString());
                    var     sellArbitragePairOptions = new SellOptions(flippedArbitragePair)
                    {
                        Arbitrage   = true,
                        Amount      = buyArbitragePairOrderDetails.AmountFilled,
                        ManualOrder = options.ManualOrder,
                        Metadata    = options.Metadata.MergeWith(new OrderMetadata
                        {
                            IsTransitional = true
                        })
                    };

                    IOrderDetails sellArbitragePairOrderDetails = orderingService.PlaceSellOrder(sellArbitragePairOptions);
                    if (sellArbitragePairOrderDetails.Result == OrderResult.Filled)
                    {
                        decimal sellArbitragePairMultiplier = pairConfig.ArbitrageSellMultiplier ?? DEFAULT_ARBITRAGE_SELL_MULTIPLIER;
                        decimal sellArbitragePairFees       = this.CalculateOrderFees(sellArbitragePairOrderDetails);
                        options.Metadata.FeesNonDeductible = buyArbitragePairFees * sellArbitragePairMultiplier;
                        decimal sellMarketPairAmount = sellArbitragePairOrderDetails.AmountFilled * this.GetPrice(flippedArbitragePair, TradePriceType.Bid, normalize: false) * sellArbitragePairMultiplier;
                        string  marketPair           = this.Exchange.GetArbitrageMarketPair(options.Arbitrage.Market);

                        var sellMarketPairOptions = new SellOptions(marketPair)
                        {
                            Arbitrage   = true,
                            Amount      = sellMarketPairAmount,
                            ManualOrder = options.ManualOrder,
                            Metadata    = options.Metadata.MergeWith(new OrderMetadata
                            {
                                IsTransitional = false,
                                OriginalPair   = arbitragePair
                            })
                        };

                        existingArbitragePair = this.Account.GetTradingPair(marketPair);
                        existingArbitragePair.OverrideCost((buyArbitragePairOrderDetails.Cost + sellArbitragePairFees * 2) * sellArbitragePairMultiplier);
                        IOrderDetails sellMarketPairOrderDetails = orderingService.PlaceSellOrder(sellMarketPairOptions);
                        existingArbitragePair.OverrideCost(null);

                        if (sellMarketPairOrderDetails.Result == OrderResult.Filled)
                        {
                            loggingService.Info($"{pairConfig.ArbitrageType} arbitrage successful: {arbitragePair} -> {flippedArbitragePair} -> {marketPair}");
                        }
                        else
                        {
                            loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell market pair {arbitragePair}");
                            notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell market pair {arbitragePair}");
                        }
                    }
                    else
                    {
                        loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to sell arbitrage pair {flippedArbitragePair}");
                        notificationService.Notify($"Unable to arbitrage {options.Pair}: Failed to sell arbitrage pair {flippedArbitragePair}");
                    }
                }
                else
                {
                    loggingService.Info($"Unable to arbitrage {options.Pair}. Reason: failed to buy arbitrage pair {arbitragePair}");
                }
            }
            else
            {
                loggingService.Info($"Unable to arbitrage {options.Pair}: {message}");
            }
        }