private void InitiateBuy(string pair, IRule rule, IEnumerable <ISignal> ruleSignals)
        {
            trailingSignals.TryRemove(pair, out List <SignalTrailingInfo> trailingInfo);

            IPairConfig         pairConfig    = tradingService.GetPairConfig(pair);
            SignalRuleModifiers ruleModifiers = rule.GetModifiers <SignalRuleModifiers>();
            string ruleSignalsList            = String.Join(", ", ruleSignals.Select(s => s.Name));

            if (LoggingEnabled)
            {
                loggingService.Info($"Initiate buy request for {pair}. Rule: {rule.Name}, Signals: {ruleSignalsList}");
            }

            var buyOptions = new BuyOptions(pair)
            {
                MaxCost  = pairConfig.BuyMaxCost * pairConfig.BuyMultiplier * (ruleModifiers?.CostMultiplier ?? 1),
                Metadata = new OrderMetadata
                {
                    SignalRule         = rule.Name,
                    Signals            = ruleSignals.Select(s => s.Name).ToList(),
                    BoughtRating       = ruleSignals.Any(s => s.Rating.HasValue) ? ruleSignals.Where(s => s.Rating.HasValue).Average(s => s.Rating) : null,
                    BoughtGlobalRating = signalsService.GetGlobalRating()
                }
            };

            tradingService.Buy(buyOptions);
        }
예제 #2
0
        public void Buy(BuyOptions options)
        {
            lock (SyncRoot)
            {
                IRule rule = signalsService.Rules.Entries.FirstOrDefault(r => r.Name == options.Metadata.SignalRule);

                ITradingPair swappedPair = Account.GetTradingPairs().OrderBy(p => p.CurrentMargin).FirstOrDefault(tradingPair =>
                {
                    IPairConfig pairConfig = GetPairConfig(tradingPair.Pair);
                    return(pairConfig.SellEnabled && pairConfig.SwapEnabled && pairConfig.SwapSignalRules != null && pairConfig.SwapSignalRules.Contains(options.Metadata.SignalRule) &&
                           pairConfig.SwapTimeout < (DateTimeOffset.Now - tradingPair.OrderDates.Max()).TotalSeconds);
                });

                if (swappedPair != null)
                {
                    Swap(new SwapOptions(swappedPair.Pair, options.Pair, options.Metadata));
                }
                else if (rule?.Action != Constants.SignalRuleActions.Swap)
                {
                    if (CanBuy(options, out string message))
                    {
                        tradingTimedTask.InitiateBuy(options);
                    }
                    else
                    {
                        loggingService.Debug(message);
                    }
                }
            }
        }
예제 #3
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);
        }
예제 #4
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();
                }
            }
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        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);
            }
        }
예제 #7
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);
        }
예제 #8
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}");
            }
        }
예제 #9
0
        public void Swap(SwapOptions options)
        {
            lock (syncRoot)
            {
                PauseTasks();
                try
                {
                    if (CanSwap(options, out string message))
                    {
                        ITradingPair oldTradingPair = Account.GetTradingPair(options.OldPair);
                        var          sellOptions    = new SellOptions(options.OldPair)
                        {
                            Swap        = true,
                            ManualOrder = options.ManualOrder,
                            Metadata    = new OrderMetadata {
                                SwapPair = options.NewPair
                            }
                        };

                        if (CanSell(sellOptions, out message))
                        {
                            decimal currentMargin       = oldTradingPair.CurrentMargin;
                            decimal additionalCosts     = oldTradingPair.Cost - oldTradingPair.CurrentCost + (oldTradingPair.Metadata.AdditionalCosts ?? 0);
                            int     additionalDCALevels = oldTradingPair.DCALevel;

                            IOrderDetails sellOrderDetails = orderingService.PlaceSellOrder(sellOptions);
                            if (!Account.HasTradingPair(options.OldPair))
                            {
                                var buyOptions = new BuyOptions(options.NewPair)
                                {
                                    Swap        = true,
                                    ManualOrder = options.ManualOrder,
                                    MaxCost     = sellOrderDetails.Cost,
                                    Metadata    = options.Metadata
                                };
                                buyOptions.Metadata.LastBuyMargin       = currentMargin;
                                buyOptions.Metadata.SwapPair            = options.OldPair;
                                buyOptions.Metadata.AdditionalDCALevels = additionalDCALevels;
                                buyOptions.Metadata.AdditionalCosts     = additionalCosts;
                                IOrderDetails buyOrderDetails = orderingService.PlaceBuyOrder(buyOptions);

                                var newTradingPair = Account.GetTradingPair(options.NewPair) as TradingPair;
                                if (newTradingPair != null)
                                {
                                    newTradingPair.Metadata.AdditionalCosts += CalculateOrderFees(sellOrderDetails);
                                    loggingService.Info($"Swap {oldTradingPair.FormattedName} for {newTradingPair.FormattedName}. " +
                                                        $"Old margin: {oldTradingPair.CurrentMargin:0.00}, new margin: {newTradingPair.CurrentMargin:0.00}");
                                }
                                else
                                {
                                    loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to buy {options.NewPair}");
                                    notificationService.Notify($"Unable to swap {options.OldPair} for {options.NewPair}: Failed to buy {options.NewPair}");
                                }
                            }
                            else
                            {
                                loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to sell {options.OldPair}");
                            }
                        }
                        else
                        {
                            loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}: {message}");
                        }
                    }
                    else
                    {
                        loggingService.Info(message);
                    }
                }
                finally
                {
                    ContinueTasks();
                }
            }
        }
예제 #10
0
        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);
        }
예제 #11
0
        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}");
        }
예제 #12
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}");
            }
        }
예제 #13
0
        public void Swap(SwapOptions options)
        {
            lock (SyncRoot)
            {
                if (CanSwap(options, out string message))
                {
                    ITradingPair oldTradingPair = Account.GetTradingPair(options.OldPair);

                    var sellOptions = new SellOptions(options.OldPair)
                    {
                        Swap        = true,
                        SwapPair    = options.NewPair,
                        ManualOrder = options.ManualOrder
                    };

                    if (CanSell(sellOptions, out message))
                    {
                        decimal currentMargin       = oldTradingPair.CurrentMargin;
                        decimal additionalCosts     = oldTradingPair.AverageCostPaid - oldTradingPair.CurrentCost + (oldTradingPair.Metadata.AdditionalCosts ?? 0);
                        int     additionalDCALevels = oldTradingPair.DCALevel;

                        IOrderDetails sellOrderDetails = tradingTimedTask.PlaceSellOrder(sellOptions);
                        if (!Account.HasTradingPair(options.OldPair))
                        {
                            var buyOptions = new BuyOptions(options.NewPair)
                            {
                                Swap        = true,
                                ManualOrder = options.ManualOrder,
                                MaxCost     = sellOrderDetails.AverageCost,
                                Metadata    = options.Metadata
                            };
                            buyOptions.Metadata.LastBuyMargin       = currentMargin;
                            buyOptions.Metadata.SwapPair            = options.OldPair;
                            buyOptions.Metadata.AdditionalDCALevels = additionalDCALevels;
                            buyOptions.Metadata.AdditionalCosts     = additionalCosts;
                            IOrderDetails buyOrderDetails = tradingTimedTask.PlaceBuyOrder(buyOptions);

                            var newTradingPair = Account.GetTradingPair(options.NewPair) as TradingPair;
                            if (newTradingPair != null)
                            {
                                if (sellOrderDetails.Fees != 0 && sellOrderDetails.FeesCurrency != null)
                                {
                                    if (sellOrderDetails.FeesCurrency == Config.Market)
                                    {
                                        newTradingPair.Metadata.AdditionalCosts += sellOrderDetails.Fees;
                                    }
                                    else
                                    {
                                        string feesPair = sellOrderDetails.FeesCurrency + Config.Market;
                                        newTradingPair.Metadata.AdditionalCosts += GetCurrentPrice(feesPair) * sellOrderDetails.Fees;
                                    }
                                }
                                loggingService.Info($"Swap {oldTradingPair.FormattedName} for {newTradingPair.FormattedName}. Old margin: {oldTradingPair.CurrentMargin:0.00}, new margin: {newTradingPair.CurrentMargin:0.00}");
                            }
                            else
                            {
                                loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to buy {options.NewPair}");
                                notificationService.Notify($"Unable to swap {options.OldPair} for {options.NewPair}: Failed to buy {options.NewPair}");
                            }
                        }
                        else
                        {
                            loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}. Reason: failed to sell {options.OldPair}");
                        }
                    }
                    else
                    {
                        loggingService.Info($"Unable to swap {options.OldPair} for {options.NewPair}: {message}");
                    }
                }
                else
                {
                    loggingService.Info(message);
                }
            }
        }