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 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 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 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); }