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); } } } }
private DCALevel GetCurrentDCALevel(ITradingPair tradingPair, List <DCALevel> dcaLevels) { if (tradingPair != null && tradingPair.DCALevel > 0 && dcaLevels.Count >= tradingPair.DCALevel) { return(dcaLevels[tradingPair.DCALevel - 1]); } else { return(null); } }
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(); } } }
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 }); }
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 ProcessRule(IRule rule, Dictionary <string, ISignal> signals, string pair, List <string> excludedPairs, double?globalRating) { IEnumerable <IRuleCondition> conditions = rule.Trailing != null && rule.Trailing.Enabled ? rule.Trailing.StartConditions : rule.Conditions; ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair, includeDust: true); List <SignalTrailingInfo> trailingInfoList; if (!excludedPairs.Contains(pair) && (!trailingSignals.TryGetValue(pair, out trailingInfoList) || !trailingInfoList.Any(t => t.Rule == rule)) && (conditions == null || rulesService.CheckConditions(conditions, signals, globalRating, pair, tradingPair))) { IEnumerable <ISignal> ruleSignals = conditions != null?signals.Where(s => conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value) : new List <ISignal>(); if (rule.Trailing != null && rule.Trailing.Enabled) { if (trailingInfoList == null) { trailingInfoList = new List <SignalTrailingInfo>(); trailingSignals.TryAdd(pair, trailingInfoList); } trailingInfoList.Add(new SignalTrailingInfo { Rule = rule, StartTime = DateTimeOffset.Now }); if (LoggingEnabled) { loggingService.Info($"Start trailing signal for {pair}. Rule: {rule.Name}"); } } else { InitiateBuy(pair, rule, ruleSignals); } if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { excludedPairs.Add(pair); } } }
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); } }
private DCALevel GetNextDCALevel(ITradingPair tradingPair, List <DCALevel> dcaLevels, bool repeatLastDCALevel) { if (tradingPair != null && dcaLevels.Count > 0) { if (dcaLevels.Count >= tradingPair.DCALevel + 1) { return(dcaLevels[tradingPair.DCALevel]); } else if (repeatLastDCALevel) { return(dcaLevels[dcaLevels.Count - 1]); } else { return(null); } } else { return(null); } }
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); }
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); }
public void ProcessAllRules() { IEnumerable <IRule> enabledRules = tradingService.Rules?.Entries?.Where(r => r.Enabled) ?? new List <IRule>(); List <string> allPairs = tradingService.Exchange.GetMarketPairs(tradingService.Config.Market).ToList(); double?globalRating = signalsService.GetGlobalRating(); foreach (string pair in allPairs) { IEnumerable <ISignal> signalsByPair = signalsService.GetSignalsByPair(pair); if (signalsByPair != null) { Dictionary <string, ISignal> signals = signalsByPair.ToDictionary(s => s.Name, s => s); ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); TradingConfig modifiedTradingConfig = tradingService.Config.Clone() as TradingConfig; PairConfig modifiedPairConfig = new PairConfig(); pairConfigs.TryGetValue(pair, out PairConfig oldPairConfig); var appliedRules = new List <IRule>(); foreach (var rule in enabledRules) { if (rulesService.CheckConditions(rule.Conditions, signals, globalRating, pair, tradingPair)) { var modifiers = rule.GetModifiers <TradingRuleModifiers>(); if (modifiers != null) { // Base Trading Config modifiedTradingConfig.MaxPairs = modifiers.MaxPairs ?? modifiedTradingConfig.MaxPairs; modifiedTradingConfig.BuyEnabled = modifiers.BuyEnabled ?? modifiedTradingConfig.BuyEnabled; modifiedTradingConfig.BuyMaxCost = modifiers.BuyMaxCost ?? modifiedTradingConfig.BuyMaxCost; modifiedTradingConfig.BuyMultiplier = modifiers.BuyMultiplier ?? modifiedTradingConfig.BuyMultiplier; modifiedTradingConfig.BuyMinBalance = modifiers.BuyMinBalance ?? modifiedTradingConfig.BuyMinBalance; modifiedTradingConfig.BuySamePairTimeout = modifiers.BuySamePairTimeout ?? modifiedTradingConfig.BuySamePairTimeout; modifiedTradingConfig.BuyTrailing = modifiers.BuyTrailing ?? modifiedTradingConfig.BuyTrailing; modifiedTradingConfig.BuyTrailingStopMargin = modifiers.BuyTrailingStopMargin ?? modifiedTradingConfig.BuyTrailingStopMargin; modifiedTradingConfig.BuyTrailingStopAction = modifiers.BuyTrailingStopAction ?? modifiedTradingConfig.BuyTrailingStopAction; modifiedTradingConfig.BuyDCAEnabled = modifiers.BuyDCAEnabled ?? modifiedTradingConfig.BuyDCAEnabled; modifiedTradingConfig.BuyDCAMultiplier = modifiers.BuyDCAMultiplier ?? modifiedTradingConfig.BuyDCAMultiplier; modifiedTradingConfig.BuyDCAMinBalance = modifiers.BuyDCAMinBalance ?? modifiedTradingConfig.BuyDCAMinBalance; modifiedTradingConfig.BuyDCASamePairTimeout = modifiers.BuyDCASamePairTimeout ?? modifiedTradingConfig.BuyDCASamePairTimeout; modifiedTradingConfig.BuyDCATrailing = modifiers.BuyDCATrailing ?? modifiedTradingConfig.BuyDCATrailing; modifiedTradingConfig.BuyDCATrailingStopMargin = modifiers.BuyDCATrailingStopMargin ?? modifiedTradingConfig.BuyDCATrailingStopMargin; modifiedTradingConfig.BuyDCATrailingStopAction = modifiers.BuyDCATrailingStopAction ?? modifiedTradingConfig.BuyDCATrailingStopAction; modifiedTradingConfig.SellEnabled = modifiers.SellEnabled ?? modifiedTradingConfig.SellEnabled; modifiedTradingConfig.SellMargin = modifiers.SellMargin ?? modifiedTradingConfig.SellMargin; modifiedTradingConfig.SellTrailing = modifiers.SellTrailing ?? modifiedTradingConfig.SellTrailing; modifiedTradingConfig.SellTrailingStopMargin = modifiers.SellTrailingStopMargin ?? modifiedTradingConfig.SellTrailingStopMargin; modifiedTradingConfig.SellTrailingStopAction = modifiers.SellTrailingStopAction ?? modifiedTradingConfig.SellTrailingStopAction; modifiedTradingConfig.SellStopLossEnabled = modifiers.SellStopLossEnabled ?? modifiedTradingConfig.SellStopLossEnabled; modifiedTradingConfig.SellStopLossAfterDCA = modifiers.SellStopLossAfterDCA ?? modifiedTradingConfig.SellStopLossAfterDCA; modifiedTradingConfig.SellStopLossMinAge = modifiers.SellStopLossMinAge ?? modifiedTradingConfig.SellStopLossMinAge; modifiedTradingConfig.SellStopLossMargin = modifiers.SellStopLossMargin ?? modifiedTradingConfig.SellStopLossMargin; modifiedTradingConfig.SellDCAMargin = modifiers.SellDCAMargin ?? modifiedTradingConfig.SellDCAMargin; modifiedTradingConfig.SellDCATrailing = modifiers.SellDCATrailing ?? modifiedTradingConfig.SellDCATrailing; modifiedTradingConfig.SellDCATrailingStopMargin = modifiers.SellDCATrailingStopMargin ?? modifiedTradingConfig.SellDCATrailingStopMargin; modifiedTradingConfig.SellDCATrailingStopAction = modifiers.SellDCATrailingStopAction ?? modifiedTradingConfig.SellDCATrailingStopAction; modifiedTradingConfig.RepeatLastDCALevel = modifiers.RepeatLastDCALevel ?? modifiedTradingConfig.RepeatLastDCALevel; modifiedTradingConfig.DCALevels = modifiers.DCALevels ?? modifiedTradingConfig.DCALevels; // Base Pair Config modifiedPairConfig.SwapEnabled = modifiers.SwapEnabled ?? modifiedPairConfig.SwapEnabled; modifiedPairConfig.SwapSignalRules = modifiers.SwapSignalRules ?? modifiedPairConfig.SwapSignalRules; modifiedPairConfig.SwapTimeout = modifiers.SwapTimeout ?? modifiedPairConfig.SwapTimeout; modifiedPairConfig.ArbitrageEnabled = modifiers.ArbitrageEnabled ?? modifiedPairConfig.ArbitrageEnabled; modifiedPairConfig.ArbitrageMarkets = modifiers.ArbitrageMarkets ?? modifiedPairConfig.ArbitrageMarkets; modifiedPairConfig.ArbitrageType = modifiers.ArbitrageType ?? modifiedPairConfig.ArbitrageType; modifiedPairConfig.ArbitrageBuyMultiplier = modifiers.ArbitrageBuyMultiplier ?? modifiedPairConfig.ArbitrageBuyMultiplier; modifiedPairConfig.ArbitrageSellMultiplier = modifiers.ArbitrageSellMultiplier ?? modifiedPairConfig.ArbitrageSellMultiplier; modifiedPairConfig.ArbitrageSignalRules = modifiers.ArbitrageSignalRules ?? modifiedPairConfig.ArbitrageSignalRules; if (oldPairConfig != null && !oldPairConfig.ArbitrageEnabled && modifiedPairConfig.ArbitrageEnabled) { signalsService.ProcessPair(pair, signals); } } appliedRules.Add(rule); if (tradingService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { break; } } } pairConfigs[pair] = CreatePairConfig(pair, modifiedTradingConfig, modifiedPairConfig, appliedRules); } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.TradingRulesProcessed, $"Rules: {enabledRules.Count()}, Pairs: {allPairs.Count}"); }
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}"); } }
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}"); }
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(); } } }
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}"); } }
private void ProcessRules() { if (tradingService.Config.BuyEnabled) { var allSignals = signalsService.GetAllSignals(); if (allSignals != null) { var enabledRules = signalsService.Rules.Entries.Where(r => r.Enabled); if (enabledRules.Any()) { var groupedSignals = allSignals.Where(s => tradingService.GetPairConfig(s.Pair).BuyEnabled).GroupBy(s => s.Pair).ToDictionary(g => g.Key, g => g.ToDictionary(s => s.Name, s => s)); double?globalRating = signalsService.GetGlobalRating(); var excludedPairs = tradingService.Config.ExcludedPairs .Concat(tradingService.Account.GetTradingPairs().Select(p => p.Pair)) .Concat(tradingService.GetTrailingBuys()).ToList(); if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { excludedPairs.AddRange(trailingSignals.Keys); } foreach (var rule in enabledRules) { foreach (var group in groupedSignals) { string pair = group.Key; Dictionary <string, ISignal> signals = group.Value; ITradingPair tradingPair = tradingService.Account.GetTradingPair(pair); IEnumerable <IRuleCondition> conditions = rule.Trailing != null && rule.Trailing.Enabled ? rule.Trailing.StartConditions : rule.Conditions; List <SignalTrailingInfo> trailingInfoList; if (!excludedPairs.Contains(pair) && (!trailingSignals.TryGetValue(pair, out trailingInfoList) || !trailingInfoList.Any(t => t.Rule == rule)) && rulesService.CheckConditions(conditions, signals, globalRating, pair, tradingPair)) { IEnumerable <ISignal> ruleSignals = signals.Where(s => conditions.Any(c => c.Signal == s.Key)).Select(s => s.Value); if (rule.Trailing != null && rule.Trailing.Enabled) { if (trailingInfoList == null) { trailingInfoList = new List <SignalTrailingInfo>(); trailingSignals.TryAdd(pair, trailingInfoList); } trailingInfoList.Add(new SignalTrailingInfo { Rule = rule, StartTime = DateTimeOffset.Now }); string ruleSignalsList = String.Join(", ", ruleSignals.Select(s => s.Name)); if (LoggingEnabled) { loggingService.Info($"Start trailing signal for {pair}. Rule: {rule.Name}, Signals: {ruleSignalsList}"); } } else { InitiateBuy(pair, rule, ruleSignals); } if (signalsService.RulesConfig.ProcessingMode == RuleProcessingMode.FirstMatch) { excludedPairs.Add(pair); } } } } } healthCheckService.UpdateHealthCheck(Constants.HealthChecks.SignalRulesProcessed, $"Rules: {enabledRules.Count()}, Trailing signals: {trailingSignals.Count}"); } } }
public bool CheckConditions(IEnumerable <IRuleCondition> conditions, Dictionary <string, ISignal> signals, double?globalRating, string pair, ITradingPair tradingPair) { if (conditions != null) { foreach (var condition in conditions) { ISignal signal = null; if (condition.Signal != null && signals.TryGetValue(condition.Signal, out ISignal s)) { signal = s; } if (condition.MinPrice != null && (tradingService.GetPrice(pair) < condition.MinPrice) || condition.MaxPrice != null && (tradingService.GetPrice(pair) > condition.MaxPrice) || condition.MinSpread != null && (tradingService.Exchange.GetPriceSpread(pair) < condition.MinSpread) || condition.MaxSpread != null && (tradingService.Exchange.GetPriceSpread(pair) > condition.MaxSpread) || condition.MinArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List <ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage < condition.MinArbitrage || condition.MaxArbitrage != null && tradingService.Exchange.GetArbitrage(pair, tradingService.Config.Market, condition.ArbitrageMarket != null ? new List <ArbitrageMarket> { condition.ArbitrageMarket.Value } : null, condition.ArbitrageType).Percentage > condition.MaxArbitrage || condition.MinVolume != null && (signal == null || signal.Volume == null || signal.Volume < condition.MinVolume) || condition.MaxVolume != null && (signal == null || signal.Volume == null || signal.Volume > condition.MaxVolume) || condition.MinVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange < condition.MinVolumeChange) || condition.MaxVolumeChange != null && (signal == null || signal.VolumeChange == null || signal.VolumeChange > condition.MaxVolumeChange) || condition.MinPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange < condition.MinPriceChange) || condition.MaxPriceChange != null && (signal == null || signal.PriceChange == null || signal.PriceChange > condition.MaxPriceChange) || condition.MinRating != null && (signal == null || signal.Rating == null || signal.Rating < condition.MinRating) || condition.MaxRating != null && (signal == null || signal.Rating == null || signal.Rating > condition.MaxRating) || condition.MinRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange < condition.MinRatingChange) || condition.MaxRatingChange != null && (signal == null || signal.RatingChange == null || signal.RatingChange > condition.MaxRatingChange) || condition.MinVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility < condition.MinVolatility) || condition.MaxVolatility != null && (signal == null || signal.Volatility == null || signal.Volatility > condition.MaxVolatility) || condition.MinGlobalRating != null && (globalRating == null || globalRating < condition.MinGlobalRating) || condition.MaxGlobalRating != null && (globalRating == null || globalRating > condition.MaxGlobalRating) || condition.Pairs != null && (pair == null || !condition.Pairs.Contains(pair)) || condition.NotPairs != null && (pair == null || condition.NotPairs.Contains(pair)) || condition.MinAge != null && (tradingPair == null || tradingPair.CurrentAge < condition.MinAge / Application.Speed) || condition.MaxAge != null && (tradingPair == null || tradingPair.CurrentAge > condition.MaxAge / Application.Speed) || condition.MinLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge < condition.MinLastBuyAge / Application.Speed) || condition.MaxLastBuyAge != null && (tradingPair == null || tradingPair.LastBuyAge > condition.MaxLastBuyAge / Application.Speed) || condition.MinMargin != null && (tradingPair == null || tradingPair.CurrentMargin < condition.MinMargin) || condition.MaxMargin != null && (tradingPair == null || tradingPair.CurrentMargin > condition.MaxMargin) || condition.MinMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) < condition.MinMarginChange) || condition.MaxMarginChange != null && (tradingPair == null || tradingPair.Metadata.LastBuyMargin == null || (tradingPair.CurrentMargin - tradingPair.Metadata.LastBuyMargin) > condition.MaxMarginChange) || condition.MinAmount != null && (tradingPair == null || tradingPair.Amount < condition.MinAmount) || condition.MaxAmount != null && (tradingPair == null || tradingPair.Amount > condition.MaxAmount) || condition.MinCost != null && (tradingPair == null || tradingPair.CurrentCost < condition.MinCost) || condition.MaxCost != null && (tradingPair == null || tradingPair.CurrentCost > condition.MaxCost) || condition.MinDCALevel != null && (tradingPair == null || tradingPair.DCALevel < condition.MinDCALevel) || condition.MaxDCALevel != null && (tradingPair == null || tradingPair.DCALevel > condition.MaxDCALevel) || condition.SignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || !condition.SignalRules.Contains(tradingPair.Metadata.SignalRule)) || condition.NotSignalRules != null && (tradingPair == null || tradingPair.Metadata.SignalRule == null || condition.NotSignalRules.Contains(tradingPair.Metadata.SignalRule))) { return(false); } } } return(true); }
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); } } }
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); }