public static string TradeSignalSetsAreCorrect(AutoTradeSettings sets, SUBSCRIPTION_SIGNAL setsStored) { var errors = new List<string>(); if ((int?)sets.VolumeRound != setsStored.VolumeRound) errors.Add("VolumeRound"); if (sets.HedgingOrdersEnabled != setsStored.HedgingOrdersEnabled) errors.Add("HedgingOrdersEnabled"); if (sets.PercentLeverage != setsStored.PercentLeverage) errors.Add("PercentLeverage"); if (sets.MinVolume != setsStored.MinVolume) errors.Add("MinVolume"); if (sets.StepVolume != setsStored.StepVolume) errors.Add("StepVolume"); if (sets.MaxLeverage != setsStored.MaxLeverage) errors.Add("MaxLeverage"); if (sets.MaxVolume != setsStored.MaxVolume) errors.Add("MaxVolume"); if (sets.TargetAccount != setsStored.TargetAccount) errors.Add("TargetAccount"); if (sets.TradeAuto != setsStored.AutoTrade) errors.Add("TradeAuto"); if (sets.FixedVolume != setsStored.FixedVolume) errors.Add("FixedVolume"); return string.Join(", ", errors); }
public static AutoTradeSettings DecorateAutoTradeSettings(SUBSCRIPTION_SIGNAL cat) { return(new AutoTradeSettings { FixedVolume = cat.FixedVolume, HedgingOrdersEnabled = cat.HedgingOrdersEnabled, MaxLeverage = cat.MaxLeverage, MaxVolume = cat.MaxVolume, MinVolume = cat.MinVolume, PercentLeverage = cat.PercentLeverage ?? 100, StepVolume = cat.StepVolume, TradeAuto = cat.AutoTrade ?? false, VolumeRound = (VolumeRoundType?)cat.VolumeRound, TargetAccount = cat.TargetAccount }); }
public void TestCalcVolume() { var action = new TradeSignalActionTrade { Leverage = 1, OrderId = 9999999, Price = 1.3820f, Side = -1, ServiceId = serviceId, Ticker = "EURUSD" }; var tradeSets = new SUBSCRIPTION_SIGNAL { TargetAccount = testAccountId, AutoTrade = true, MaxLeverage = 10, MaxVolume = 100000, PercentLeverage = 120, HedgingOrdersEnabled = true, User = 0 }; // расчет объема обычного ордера var volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.Greater(volume, 0, "TradeSignalDealer - объем больше 0"); // превышение плеча action.Leverage = 4.1M; tradeSets.MaxLeverage = 5; volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.AreEqual(volume, 0, "TradeSignalDealer - объем 0 (превышение плеча)"); // просто сделка по йене action.Ticker = "USDJPY"; action.Price = 92.1f; action.Leverage = 2; tradeSets.MaxLeverage = 7.6f; volume = Dealer.GetNewOrderVolume(action, connection, tradeSets); Assert.AreEqual(70000, volume, "TradeSignalDealer - объем USDJPY больше 0"); }
public static int GetNewOrderVolume(TradeSignalActionTrade action, TradeSharpConnection ctx, SUBSCRIPTION_SIGNAL tradeSets) { // получить ордера по счету, посчитать актуальный баланс (средства) счета try { var account = ctx.ACCOUNT.FirstOrDefault(a => a.ID == tradeSets.TargetAccount.Value); if (account == null) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: счет не найден", tradeSets.TargetAccount, action.OrderId); return 0; } var equity = (double)account.Balance; // ордера var orders = ctx.POSITION.Where(p => p.AccountID == tradeSets.TargetAccount && p.State == (int)PositionState.Opened).ToList().Select(o => LinqToEntity.DecorateOrder(o)).ToList(); // проверить - нет ли противоположного ордера? var quotes = Contract.Util.BL.QuoteStorage.Instance.ReceiveAllData(); if (orders.Count > 0) { if (!(tradeSets.HedgingOrdersEnabled ?? false)) { var hasOrdersCounter = orders.Any(p => p.Symbol == action.Ticker && p.Side != action.Side); if (hasOrdersCounter) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Info, LogMsgContraOrders, 1000 * 60 * 90, "Ордер {0} {1}, счет {2} - есть встречные ордера", action.Side > 0 ? "BUY" : "SELL", action.Ticker, tradeSets.TargetAccount); return 0; } } // посчитать профит по позам bool noQuoteError; var openResult = DalSpot.Instance.CalculateOpenedPositionsCurrentResult( orders, quotes, account.Currency, out noQuoteError); if (noQuoteError) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmCalcNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - нет котировки"); return 0; } equity += openResult; } if (equity <= 0) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgEquityIsNil, 1000 * 60 * 90, "Баланс счета {0}: {1}", account.ID, equity); return 0; } // посчитать объем var volumeDepo = (tradeSets.PercentLeverage ?? 100) / 100f * equity * (double)action.Leverage; // пересчитать объем в базовую валюту string errorStr; var volumeBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!volumeBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmToBase, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать объем по сделке {0}, валюта счета {1}: {2}", action.Ticker, account.Currency, errorStr); return 0; } // округлить объем var volume = MarketOrder.RoundDealVolume((int)volumeBase, (VolumeRoundType)(tradeSets.VolumeRound ?? (int)VolumeRoundType.Ближайшее), tradeSets.MinVolume ?? 10000, tradeSets.StepVolume ?? 10000); if (volume == 0) { var msg = string.Format("Полученный объем входа ({0}) округлен до 0", (int)volumeBase); logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgVolmRoundToZero, 1000 * 60 * 90, msg); return 0; } // проверить на превышение плеча if ((tradeSets.MaxLeverage ?? 0) > 0) { var totalExposure = 0M; var ordersBySignaller = orders.Where(position => { int orderSignalCatId, parentDealId; if (MarketOrder.GetTradeSignalFromDeal(position, out orderSignalCatId, out parentDealId)) return orderSignalCatId == action.ServiceId; return false; }).GroupBy(o => o.Symbol).ToDictionary(o => o.Key, o => o.ToList()); foreach (var orderByTicker in ordersBySignaller) { // суммарная экспозиция var exp = orderByTicker.Value.Sum(o => o.Side * o.Volume); if (orderByTicker.Key == action.Ticker) exp += action.Side * volume; if (exp == 0) continue; // перевести экспозицию в валюту депозита var expBase = DalSpot.Instance.ConvertToTargetCurrency(action.Ticker, true, account.Currency, volumeDepo, quotes, out errorStr); if (!expBase.HasValue) { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgExposureNoQuote, 1000 * 60 * 90, "GetNewOrderVolume - невозможно посчитать экспозицию по сделке " + "{0}, тикер {1}: {2}", action.Ticker, orderByTicker.Key, errorStr); return 0; } totalExposure += Math.Abs(expBase.Value); } var totalLeverage = (double)totalExposure / equity; // ReSharper disable PossibleInvalidOperationException if (totalLeverage > tradeSets.MaxLeverage.Value) // ReSharper restore PossibleInvalidOperationException { logNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMsgMaxLevExceeded, 1000 * 60 * 90, "GetNewOrderVolume - макс плечо ({0}) превышено, " + "тикер {1}: плечо составит {2}", tradeSets.MaxLeverage.Value, action.Ticker, totalLeverage); return 0; } } return volume; } catch (Exception ex) { Logger.ErrorFormat("Ошибка при расчете объема по счету {0}, ордер {1}: {2}", tradeSets.TargetAccount, action.OrderId, ex); } return 0; }
public bool SubscribeOnService(TradeSharpConnection ctx, int userId, int serviceId, bool renewAuto, bool unsubscribe, AutoTradeSettings tradeSets, out WalletError error) { // имеющаяся подписка var subs = ctx.SUBSCRIPTION.FirstOrDefault(s => s.Service == serviceId && s.User == userId); // просто отписаться от сервиса if (unsubscribe) { error = WalletError.OK; if (subs == null) return true; ctx.SUBSCRIPTION.Remove(subs); try { ctx.SaveChanges(); } catch (Exception ex) { Logger.Error("Ошибка удаления подписки (SubscribeOnService)", ex); error = WalletError.ServerError; return false; } return true; } var paidService = ctx.SERVICE.FirstOrDefault(s => s.ID == serviceId); if (paidService == null) { error = WalletError.InvalidData; return false; } // проверить - не подписывается ли пользователь сам на себя? if (paidService.User == userId) { error = WalletError.InvalidData; return false; } // провести списание денежных средств // содрать денежку var feeError = ChargeFeeOnSubscription(ctx, serviceId, userId, false); if (feeError != WalletError.OK) { error = feeError; return false; } // продлить или обновить подписку var subExists = subs != null; if (subs == null) subs = new SUBSCRIPTION(); subs.RenewAuto = renewAuto; subs.TimeEnd = DateTime.Now.Date.AddDays(1); subs.TimeStarted = DateTime.Now.Date; subs.User = userId; subs.Service = serviceId; if (!subExists) ctx.SUBSCRIPTION.Add(subs); // обновить или создать настройки торговли var signalTradeSets = ctx.SUBSCRIPTION_SIGNAL.FirstOrDefault(s => s.Service == serviceId && s.User == userId); var setsExists = signalTradeSets != null; if (signalTradeSets == null) signalTradeSets = new SUBSCRIPTION_SIGNAL(); signalTradeSets.AutoTrade = tradeSets.TradeAuto; signalTradeSets.FixedVolume = tradeSets.FixedVolume; signalTradeSets.HedgingOrdersEnabled = tradeSets.HedgingOrdersEnabled; signalTradeSets.MaxLeverage = tradeSets.MaxLeverage; signalTradeSets.MaxVolume = tradeSets.MaxVolume; signalTradeSets.MinVolume = tradeSets.MinVolume; signalTradeSets.PercentLeverage = tradeSets.PercentLeverage; signalTradeSets.Service = serviceId; signalTradeSets.StepVolume = tradeSets.StepVolume; signalTradeSets.User = userId; signalTradeSets.TargetAccount = tradeSets.TargetAccount; signalTradeSets.VolumeRound = (int?)tradeSets.VolumeRound; if (!setsExists) ctx.SUBSCRIPTION_SIGNAL.Add(signalTradeSets); try { ctx.SaveChanges(); } catch (Exception ex) { Logger.Error("Ошибка сохранения подписки (SubscribeOnService)", ex); error = WalletError.ServerError; return false; } error = WalletError.OK; return true; }
public void TestSubscribeOnCustomPortfolio() { var managerTrade = ManagerTrade.Instance; var portfolio = new TopPortfolio { Criteria = "P", DescendingOrder = true, ParticipantCount = 6, Name = "UserGeniusExclusiveTop", }; var tradeSets = AutoTradeSettingsSampler.MakeSampleTradeSettings(); tradeSets.TargetAccount = conn.PLATFORM_USER_ACCOUNT.First(pa => pa.PlatformUser == subscriber.ID).Account; var status = managerTrade.SubscribeOnPortfolio(ProtectedOperationContext.MakeServerSideContext(), subscriber.Login, portfolio, null, tradeSets); Assert.AreEqual(RequestStatus.OK, status, "SubscribeOnPortfolio - удалось подписаться"); // проверить количество подписок var userSubsCount = conn.SUBSCRIPTION.Count(s => s.User == subscriber.ID); Assert.AreEqual(portfolio.ParticipantCount, userSubsCount, "SubscribeOnPortfolio - подписался на всех в портфеле"); // проверить настройки авто-торговли var subSettings = conn.SUBSCRIPTION_SIGNAL.First(ss => ss.User == subscriber.ID); var wrongFields = AutoTradeSettingsSampler.TradeSignalSetsAreCorrect(tradeSets, subSettings); if (!string.IsNullOrEmpty(wrongFields)) Assert.Fail("SubscribeOnPortfolio - настройки авто-торговли не сохранены в подписке: " + wrongFields); // настройки авто-торговли для самого портфеля var portfolioTradeSets = (from upf in conn.USER_TOP_PORTFOLIO join pf in conn.TOP_PORTFOLIO on upf.Portfolio equals pf.Id where upf.User == subscriber.ID select upf).FirstOrDefault(); Assert.IsNotNull(portfolioTradeSets, "SubscribeOnPortfolio - портфель создан"); var portfolioSignalSets = new SUBSCRIPTION_SIGNAL { AutoTrade = portfolioTradeSets.AutoTrade, MaxLeverage = portfolioTradeSets.MaxLeverage, PercentLeverage = portfolioTradeSets.PercentLeverage, HedgingOrdersEnabled = portfolioTradeSets.HedgingOrdersEnabled, FixedVolume = portfolioTradeSets.FixedVolume, MinVolume = portfolioTradeSets.MinVolume, MaxVolume = portfolioTradeSets.MaxVolume, VolumeRound = portfolioTradeSets.VolumeRound, StepVolume = portfolioTradeSets.StepVolume, TargetAccount = portfolioTradeSets.TargetAccount }; wrongFields = AutoTradeSettingsSampler.TradeSignalSetsAreCorrect(tradeSets, portfolioSignalSets); if (!string.IsNullOrEmpty(wrongFields)) Assert.Fail("SubscribeOnPortfolio - настройки авто-торговли не сохранены в портфеле: " + wrongFields); // отписаться от портфеля status = managerTrade.UnsubscribePortfolio(ProtectedOperationContext.MakeServerSideContext(), subscriber.Login, true, true); Assert.AreEqual(RequestStatus.OK, status, "SubscribeOnPortfolio - удалось отписаться"); userSubsCount = conn.SUBSCRIPTION.Count(s => s.User == subscriber.ID); Assert.AreEqual(0, userSubsCount, "SubscribeOnPortfolio - отписался ото всех в портфеле"); }
public static AutoTradeSettings DecorateAutoTradeSettings(SUBSCRIPTION_SIGNAL cat) { return new AutoTradeSettings { FixedVolume = cat.FixedVolume, HedgingOrdersEnabled = cat.HedgingOrdersEnabled, MaxLeverage = cat.MaxLeverage, MaxVolume = cat.MaxVolume, MinVolume = cat.MinVolume, PercentLeverage = cat.PercentLeverage ?? 100, StepVolume = cat.StepVolume, TradeAuto = cat.AutoTrade ?? false, VolumeRound = (VolumeRoundType?)cat.VolumeRound, TargetAccount = cat.TargetAccount }; }