/// <summary> /// посчитать профит по открытым позам в валюте депозита /// </summary> private float CalculateAccountOpenProfit(TradeSharpConnection ctx, ACCOUNT account, Dictionary<string, CandleData> candles) { var profitOpen = 0f; foreach (var pos in ctx.POSITION.Where(p => p.AccountID == account.ID)) { // найти последнюю котировку CandleData candle; if (!candles.TryGetValue(pos.Symbol, out candle)) { Errors.Add("CalculateAccountOpenProfit - " + Resource.ErrorMessageNoExitPriceFor + " " + pos.Symbol); continue; } // получить профит по позиции var priceExit = pos.Side > 0 ? candle.close : DalSpot.Instance.GetAskPriceWithDefaultSpread(pos.Symbol, candle.close); var profitCounter = pos.Volume * pos.Side * (priceExit - (float)pos.PriceEnter); // перевести профит в валюту депо if (!ConvertBaseOrCounterDepo(false, pos.Symbol, account.Currency, candles, ref profitCounter)) continue; profitOpen += profitCounter; } return profitOpen; }
public string EditSpotItem(TradeSharpConnection context, SpotModel model) { var result = string.Empty; var newContext = EnsureContext(ref context); try { var itemToEdit = context.SPOT.Single(x => x.Title == model.Title); itemToEdit.ComBase = model.ComBase; itemToEdit.ComCounter = model.ComCounter; itemToEdit.Title = model.Title; itemToEdit.CodeFXI = model.CodeFXI; itemToEdit.MinVolume = model.MinVolume; itemToEdit.MinStepVolume = model.MinStepVolume; itemToEdit.Precise = model.Precise; itemToEdit.SwapBuy = model.SwapBuy; itemToEdit.SwapSell = model.SwapSell; itemToEdit.Description = model.Description; context.SaveChanges(); } catch (Exception ex) { Logger.Error("EditSpotItem()", ex); result = ex.Message; } finally { if (newContext != null) newContext.Dispose(); } return result; }
public string AddNewSpotItem(TradeSharpConnection context, SpotModel model) { var result = string.Empty; var newContext = EnsureContext(ref context); try { var spot = new SPOT { ComBase = model.ComBase, ComCounter = model.ComCounter, Title = model.Title, CodeFXI = model.CodeFXI, MinVolume = model.MinVolume, MinStepVolume = model.MinStepVolume, Precise = model.Precise, SwapBuy = model.SwapBuy, SwapSell = model.SwapSell, Description = model.Description }; context.SPOT.Add(spot); context.SaveChanges(); } catch (Exception ex) { Logger.Error("AddNewSpotItem()", ex); result = ex.Message; } finally { if (newContext != null) newContext.Dispose(); } return result; }
/// <summary> /// добавить маркапы и прочее шкурилово /// </summary> public static void ProcessOrderClosing(MarketOrder order, ACCOUNT account, ORDER_BILL bill, TradeSharpConnection ctx, Dictionary<string, QuoteData> quotes, string brokerCurrency) { //Logger.Info("BillingManager.ProcessOrderClosing()"); try { // перевести маркапы в валюту брокера var markupSum = bill.MarkupExit + bill.MarkupEnter; // в контрвалюте... if (markupSum != 0) markupSum = order.Volume * markupSum; Logger.Info("BillingManager: markupSum is " + markupSum); // перевести в валюту брокера var resultCounter = order.Side * order.Volume * (order.PriceExit.Value - order.PriceEnter); var rateCounterDepo = resultCounter == 0 ? 1 : order.ResultDepo / resultCounter; // валюта брокера к контрвалюте var rateCounterBroker = rateCounterDepo; Logger.Info("BillingManager: rateCounterDepo is " + rateCounterDepo); if (account.Currency != brokerCurrency) { // пример: позиция EURCAD, валюта брокера USD // надо получить курс CADUSD var markupBroker = (float) markupSum; string errorStr; var resultedMarkup = DalSpot.Instance.ConvertToTargetCurrency(order.Symbol, false, brokerCurrency, markupBroker, quotes, out errorStr, true); if (!resultedMarkup.HasValue) { Logger.ErrorFormat("BillingManager.ProcessOrderClosing - невозможно перевести профит по {0} в {1}: {2}", order.Symbol, brokerCurrency, errorStr); return; } markupBroker = (float)resultedMarkup.Value; rateCounterBroker = (float)(markupBroker / markupSum); markupSum = markupBroker; } else markupSum *= rateCounterBroker; bill.MarkupBroker = markupSum; bill.ProfitBroker = resultCounter * rateCounterBroker; Logger.InfoFormat("BillingManager: MarkupBroker: {0}, ProfitBroker: {1}", bill.MarkupBroker, bill.ProfitBroker); // сохранить билль ctx.Entry(bill).State = EntityState.Modified; Logger.InfoFormat("BillingManager:OK"); } catch (Exception ex) { Logger.Error("BillingManager.ProcessOrderClosing() - ошибка редактирования счета", ex); } }
public ACCOUNT_SHARE_HISTORY CalculateAccountShare(TradeSharpConnection ctx, ACCOUNT_SHARE share, ACCOUNT_SHARE ownerShare, ref decimal ownersMoney, decimal equity, PaidService serviceWithFeeScale, out bool feeWasTaken) { feeWasTaken = false; var shareMoney = equity*share.Share/100M; var hist = new ACCOUNT_SHARE_HISTORY { Account = share.Account, Date = DateTime.Now, OldShare = share.Share, OldHWM = share.HWM, NewShare = share.Share, NewHWM = Math.Max(share.HWM ?? 0, shareMoney), ShareOwner = share.ShareOwner, ShareAmount = shareMoney }; share.HWM = hist.NewHWM; if ((hist.OldHWM ?? 0) == 0) return hist; if (shareMoney <= hist.OldHWM) return hist; if (serviceWithFeeScale == null) return hist; // был установлен новый HWM? var aboveHwm = shareMoney - hist.OldHWM.Value; // посчитать сумму комиссии var fee = serviceWithFeeScale.CalculateFee(shareMoney, aboveHwm); if (fee < 0.01M) return hist; feeWasTaken = true; // этот самый кусочек приписать владельцу и списать его с пайщика ownersMoney += fee; shareMoney -= fee; ownerShare.Share = ownersMoney*100M/equity; share.Share = shareMoney*100M/equity; var newHwm = shareMoney; if (newHwm > share.HWM) { share.HWM = newHwm; hist.NewHWM = newHwm; } return hist; }
public WalletError ChargeFeeOnSubscription(TradeSharpConnection ctx, int serviceId, int usr, bool renewSubscription) { SERVICE service; try { service = ctx.SERVICE.First(s => s.ID == serviceId); } catch (Exception ex) { Logger.Error("Ошибка в ChargeFeeOnSubscription() - сервис не найден", ex); return WalletError.InvalidData; } return ChargeFeeOnSubscription(ctx, service, usr, renewSubscription); }
/// <summary> /// Получить с сервера описание всех дилеров, используя уже имеющийся контекст (что бы не пересоздавать его лишний раз) /// </summary> /// <param name="ctx">контекст</param> /// <returns>перечисление диллеров</returns> public IEnumerable<DealerDescription> GetAllDealerDescription(TradeSharpConnection ctx) { Logger.Info(string.Format("попытка запроса описания всех дилеров и их мапинг в класс {0}", typeof(DealerDescription).Name)); var dealerDescription = new List<DealerDescription>(); try { foreach (var dealer in ctx.DEALER) dealerDescription.Add(LinqToEntity.DecorateDealerDescription(dealer)); } catch (Exception ex) { Logger.Error("GetAllDealerDescription()", ex); return new List<DealerDescription>(); } return dealerDescription; }
private static decimal AmendDeal(POSITION_CLOSED deal, TradeSharpConnection conn, decimal deltaAmount) { // "поправить" сделку deal.Side = - deal.Side; deal.ResultDepo = - deal.ResultDepo; var bc = conn.BALANCE_CHANGE.FirstOrDefault(b => b.Position == deal.ID); if (bc != null) { bc.ChangeType = (int) BalanceChangeType.Profit; var trans = conn.TRANSFER.FirstOrDefault(t => t.BalanceChange == bc.ID); deltaAmount += (bc.Amount*2); if (trans != null) { trans.Amount = -trans.Amount; trans.TargetAmount = -trans.TargetAmount; } } return deltaAmount; }
public string DeleteSpotItem(TradeSharpConnection context, string title) { var result = string.Empty; var newContext = EnsureContext(ref context); try { var itemToDel = context.SPOT.Single(x => x.Title == title); context.SPOT.Remove(itemToDel); context.SaveChanges(); } catch (Exception ex) { Logger.Error("DeleteSpotItem()", ex); result = ex.Message; } finally { if (newContext != null) newContext.Dispose(); } return result; }
/// <summary> /// вызывается для нового, только что созданного ордера /// создает запись по ордеру /// </summary> public static void SaveNewOrderBill(OrderBill bill, int positionId, TradeSharpConnection ctx) { bill.Position = positionId; ctx.ORDER_BILL.Add(LinqToEntity.UndecorateOrderBill(bill)); }
public bool UpdateAccountBalance(TradeSharpConnection ctx, ACCOUNT account, decimal amount, BalanceChangeType changeType, string description, DateTime valueDate, int? positionId) { var bc = new BALANCE_CHANGE { AccountID = account.ID, Amount = amount, ChangeType = (int)changeType, Description = description, ValueDate = valueDate, Position = positionId }; try { ctx.BALANCE_CHANGE.Add(bc); account.Balance += ((changeType == BalanceChangeType.Deposit || changeType == BalanceChangeType.Profit) ? amount : -amount); ctx.SaveChanges(); return true; } catch (Exception ex) { Logger.ErrorFormat("Ошибка обновления баланса счета {0} на сумму {1}: {2}", account.ID, amount, ex); return false; } }
public static ORDER_BILL ProcessPriceForOrderClosing(MarketOrder order, Account account, TradeSharpConnection ctx) { AccountGroup.MarkupType markType; var spreadDelta = AcccountMarkupDictionary.GetMarkupAbs(account.Group, order.Symbol, out markType); var markupExit = 0f; if (spreadDelta > 0 && markType != AccountGroup.MarkupType.NoMarkup) { markupExit = spreadDelta; if (markType == AccountGroup.MarkupType.Markup) order.PriceExit -= spreadDelta * order.Side; } var bill = ctx.ORDER_BILL.FirstOrDefault(b => b.Position == order.ID); if (bill != null) bill.MarkupExit = markupExit; //else // Logger.Debug("Markup: ProcessPriceForOrderClosing - can not find the bill for pos " + order.ID); return bill; }
/// <summary> /// Пересчёт баланса для указанного счёта. Применяется, например, после отмены уже закрытых сделок /// </summary> /// <param name="ctx"></param> /// <param name="accountId">уникаьлный идентификатор счёта, для которого пересчитываем баланс</param> public bool ReCalculateAccountBalance(TradeSharpConnection ctx, int accountId) { try { Logger.Info(string.Format("Пытыемся пересчитать баланс для счёта {0}", accountId)); var bal = ctx.BALANCE_CHANGE.Where(b => b.AccountID == accountId).Sum(c => (c.ChangeType ==(int)BalanceChangeType.Deposit || c.ChangeType ==(int)BalanceChangeType.Profit || c.ChangeType ==(int) BalanceChangeType.Swap ? 1: -1)*c.Amount); var acc = ctx.ACCOUNT.Single(a => a.ID == accountId); acc.Balance = bal; ctx.SaveChanges(); return true; } catch (InvalidOperationException ex) { Logger.Error(string.Format( "ReCalculateAccountBalance(). Не удалось пересчитать балланс {0}. Возможно нет ни одной проводки по счёту.",accountId), ex); } catch (Exception ex) { Logger.Error(string.Format("ReCalculateAccountBalance(). Не удалось пересчитать балланс счёта {0}.", accountId),ex); } return false; }
/// <summary> /// Метод пересчитывает баланс во всех операциях, кроме закрытия или отмены сделок /// </summary> public void UpdateBalanceChange(TradeSharpConnection ctx, MarketOrder closedPos, bool deleteTransferOnly) { // поправить трансфер по счету и баланс var dealDescr = string.Format("{1} #{0}", closedPos.ID, Resource.TitleMarketOrderResult); var trans = ctx.BALANCE_CHANGE.FirstOrDefault(c => c.AccountID == closedPos.AccountID && (c.ChangeType == (int)BalanceChangeType.Profit || c.ChangeType == (int)BalanceChangeType.Loss) && c.Description.Equals(dealDescr, StringComparison.OrdinalIgnoreCase)); if (deleteTransferOnly) { if (trans == null) return; ctx.BALANCE_CHANGE.Remove(trans); } else { // изменить или добавить перевод if (trans == null) { trans = new BALANCE_CHANGE { Description = dealDescr, AccountID = closedPos.AccountID }; ctx.BALANCE_CHANGE.Add(trans); } trans.Amount = (decimal)Math.Abs(closedPos.ResultDepo); trans.ChangeType = closedPos.ResultDepo > 0 ? (int)BalanceChangeType.Profit : (int)BalanceChangeType.Loss; if (closedPos.TimeExit != null) trans.ValueDate = closedPos.TimeExit.Value; } //ctx.BALANCE_CHANGE.ApplyCurrentValues(trans); ctx.SaveChanges(); }
private static void CorrectBalance(int acId, decimal deltaAmount, TradeSharpConnection conn) { // поправить депозит var bc = new BALANCE_CHANGE { AccountID = acId, ValueDate = DateTime.Now, Amount = deltaAmount, ChangeType = (int) BalanceChangeType.Withdrawal, Description = "public offering" }; conn.BALANCE_CHANGE.Add(bc); conn.SaveChanges(); var userId = conn.PLATFORM_USER_ACCOUNT.First(a => a.Account == acId).PlatformUser; var tr = new TRANSFER { ValueDate = bc.ValueDate, BalanceChange = bc.ID, Amount = -deltaAmount, Comment = "public offering", User = userId, TargetAmount = - deltaAmount }; conn.TRANSFER.Add(tr); }
private RequestStatus CheckCredentials(string hash, string userLogin, long localTime, int accountId, bool checkTradeRights, TradeSharpConnection connection = null) { try { var ctx = connection ?? DatabaseContext.Instance.Make(); try { var user = ctx.PLATFORM_USER.FirstOrDefault(u => u.Login == userLogin); if (user == null) return RequestStatus.IncorrectData; var userHash = CredentialsHash.MakeCredentialsHash(userLogin, user.Password, localTime); if (userHash != hash) return RequestStatus.IncorrectData; if (!ctx.PLATFORM_USER_ACCOUNT.Any(pa => pa.PlatformUser == user.ID && pa.Account == accountId && (!checkTradeRights || pa.RightsMask == (int) AccountRights.Управление))) return RequestStatus.Unauthorized; } finally { if (connection == null) ctx.Dispose(); } } catch (Exception ex) { Logger.Error("Ошибка в CheckCredentials()", ex); return RequestStatus.ServerError; } return RequestStatus.OK; }
private void PerformStopout(ACCOUNT ac, Dictionary<string, QuoteData> quotes, TradeSharpConnection ctx) { try { var sb = new StringBuilder(); var posReq = from pos in ctx.POSITION where pos.AccountID == ac.ID && pos.State == (int)PositionState.Opened select pos; var totalCount = 0; var closedCount = 0; foreach (var pos in posReq) { totalCount++; // закрыть ордер QuoteData quote; quotes.TryGetValue(pos.Symbol, out quote); var price = quote == null ? 0 : quote.GetPrice(pos.Side > 0 ? QuoteType.Bid : QuoteType.Ask); if (price == 0) { sb.AppendFormat("Невозможно закрыть ордер {0} ({1}): нет цены", pos.ID, pos.Symbol); continue; } if (orderRepository.CloseOrder(LinqToEntity.DecorateOrder(pos), (decimal) price, PositionExitReason.Stopout)) closedCount++; sb.AppendFormat("позиция {0} закрыта по цене {1:f4} - стопаут. ", pos.ID, price); } if (ShouldLogStopout(ac.ID)) Logger.Info(string.Format("Счет {0}. Закрыто {1} из {2} позиций. ", ac.ID, closedCount, totalCount) + sb); } catch (Exception ex) { loggerNoFlood.LogMessageFormatCheckFlood(LogEntryType.Error, LogMagicStopout, 60 * 1000, "Ошибка при выполнении стопаута {0}: {1}", ac.ID, ex); } }
private PaidService GetPaidServiceProgressiveFeeScaleDetail(TradeSharpConnection ctx, SERVICE srv) { var fees = ctx.SERVICE_RATE.Where(r => r.Service == srv.ID).OrderBy(r => r.UserBalance).ToList(); if (fees.Count == 0) return null; var service = LinqToEntity.DecoratePaidService(srv); service.serviceRates = fees.Select(f => new PaidServiceRate { Amount = f.Amount, RateType = PaidServiceRate.ServiceRateType.Percent, UserBalance = f.UserBalance }).ToList().ToList(); return service; }
public WalletError ChargeFeeOnSubscription(TradeSharpConnection ctx, SERVICE service, int usr, bool renewSubscription) { try { // создать платеж с кошелька usr на кошелек владельца сервиса (service) if (service.FixedPrice == 0) return WalletError.OK; // все на шарку! var price = service.FixedPrice > 0 ? service.FixedPrice : GetFeeByUserBalance(ctx, service); if (price == 0) return WalletError.OK; var usrWallet = ctx.WALLET.FirstOrDefault(w => w.User == usr); if (usrWallet == null) { Logger.Error("ChargeFeeOnSubscription(usrId=" + usr + ") - wallet is not found"); return WalletError.ServerError; } if (HasUserPaidTheService(ctx, service, usr)) return WalletError.OK; // посчитать в валюте пользователя var usrAmount = price; if (usrWallet.Currency != service.Currency) { string strError; var targetAmount = DalSpot.Instance.ConvertSourceCurrencyToTargetCurrency(usrWallet.Currency, service.Currency, (double)usrAmount, QuoteStorage.Instance.ReceiveAllData(), out strError); if (!targetAmount.HasValue) { Logger.ErrorFormat("ChargeFeeOnSubscription(usrId={0}) - currency rate is not found ({1}/{2})", usr, usrWallet.Currency, service.Currency); return WalletError.CurrencyExchangeFailed; } usrAmount = targetAmount.Value; } // если объем превышает возможности пользователя... if (usrWallet.Balance < usrAmount) { Logger.InfoFormat("ChargeFeeOnSubscription(usrId={0}) - not enough money ({1}, needed {2})", usr, usrAmount, usrWallet.Balance); return WalletError.InsufficientFunds; } usrWallet.Balance -= usrAmount; var trans = ctx.TRANSFER.Add(new TRANSFER { Amount = usrAmount, TargetAmount = price, Comment = "Fee on srv " + service.ID, RefWallet = service.User, User = usr, ValueDate = DateTime.Now, Subscription = service.ID }); Logger.InfoFormat("Добавляется трансфер: usr={0}, comment={1}", usr, trans.Comment); // добавить денег владельцу ctx.TRANSFER.Add(new TRANSFER { Amount = price, TargetAmount = usrAmount, Comment = "Paid for srv " + service.ID, RefWallet = usr, User = service.User, ValueDate = DateTime.Now, Subscription = service.ID }); var ownerWallet = ctx.WALLET.First(w => w.User == service.User); ownerWallet.Balance += price; // обновить подписку if (renewSubscription) { var sub = ctx.SUBSCRIPTION.FirstOrDefault(s => s.Service == service.ID); if (sub != null && sub.RenewAuto) { sub.TimeStarted = DateTime.Now.Date; sub.TimeEnd = sub.TimeStarted.AddDays(1); } } Logger.InfoFormat("ChargeFeeOnSubscription() - сохранение"); ctx.SaveChanges(); } catch (Exception ex) { Logger.Error("Error in ChargeFeeOnSubscription()", ex); return WalletError.ServerError; } return WalletError.OK; }
public static void InitializeFake(TradeSharpConnection connection) { fakeDbConnection = connection; }
public RequestStatus SubscribeUserOnPortfolio( TradeSharpConnection ctx, string subscriberLogin, TopPortfolio portfolio, decimal? maxFee, AutoTradeSettings tradeAutoSettings) { var user = ctx.PLATFORM_USER.FirstOrDefault(u => u.Login == subscriberLogin); if (user == null) return RequestStatus.Unauthorized; TOP_PORTFOLIO targetPortfolio = null; if (portfolio.Id > 0) targetPortfolio = ctx.TOP_PORTFOLIO.FirstOrDefault(p => p.Id == portfolio.Id); if (targetPortfolio != null) portfolio.ManagedAccount = targetPortfolio.ManagedAccount; // если портфель - пользовательский - сохранить его // для пользователя или обновить его имеющийся портфель if (!portfolio.IsCompanyPortfolio) { Logger.Info("SubscribeOnPortfolio() - user portfolio"); try { var existPortfolio = ctx.TOP_PORTFOLIO.FirstOrDefault(p => p.OwnerUser == user.ID); if (existPortfolio != null) { if (!LinqToEntity.DecoratePortfolio(existPortfolio).AreSame(portfolio)) { // удалить старый портфель пользователя ctx.TOP_PORTFOLIO.Remove(existPortfolio); existPortfolio = null; } else targetPortfolio = existPortfolio; } // создать портфель пользователя if (existPortfolio == null) { targetPortfolio = LinqToEntity.UndecoratePortfolio(portfolio); targetPortfolio.OwnerUser = user.ID; targetPortfolio.ManagedAccount = null; ctx.TOP_PORTFOLIO.Add(targetPortfolio); ctx.SaveChanges(); } } catch (DbEntityValidationException dbEx) { Logger.Error("SubscribeUserOnPortfolio - ошибка сохранения портфеля"); foreach (var validationErrors in dbEx.EntityValidationErrors) { foreach (var validationError in validationErrors.ValidationErrors) { Logger.ErrorFormat("Свойство: {0}, ошибка: {1}", validationError.PropertyName, validationError.ErrorMessage); } } } catch (Exception ex) { Logger.Error("Ошибка в SubscribeOnPortfolio() - обновление портфеля", ex); return RequestStatus.ServerError; } } else {// портфель компании Logger.Info("SubscribeOnPortfolio() - company portfolio"); if (targetPortfolio == null) { Logger.Error("Пользователь запросил несуществующий портфель компании " + portfolio.Id); return RequestStatus.ServerError; } } // очистить подписки пользователя на портфели // и привязать его к целевому портфелю //Logger.Info("SubscribeOnPortfolio() - removing bindings"); var oldBinding = ctx.USER_TOP_PORTFOLIO.FirstOrDefault(u => u.User == user.ID); // "посмотреть" настройки портфельной торговли в имеющейся подписке if (tradeAutoSettings == null) { if (oldBinding == null) { Logger.ErrorFormat("Подписка пользователя {0} на портфель {1} - нет данных по автоматической торговле", user.Login, portfolio.Id > 0 ? portfolio.Id.ToString() : portfolio.Name); return RequestStatus.BadRequest; } tradeAutoSettings = new AutoTradeSettings { FixedVolume = oldBinding.FixedVolume, HedgingOrdersEnabled = oldBinding.HedgingOrdersEnabled, MaxLeverage = oldBinding.MaxLeverage, MaxVolume = oldBinding.MaxVolume, MinVolume = oldBinding.MinVolume, PercentLeverage = oldBinding.PercentLeverage ?? 100, StepVolume = oldBinding.StepVolume, TargetAccount = oldBinding.TargetAccount, TradeAuto = oldBinding.AutoTrade ?? false, VolumeRound = (VolumeRoundType?)oldBinding.VolumeRound }; } if (oldBinding != null) ctx.USER_TOP_PORTFOLIO.Remove(oldBinding); //Logger.Info("SubscribeOnPortfolio() - adding binding"); ctx.USER_TOP_PORTFOLIO.Add(new USER_TOP_PORTFOLIO { User = user.ID, Portfolio = targetPortfolio.Id, MaxFee = maxFee, AutoTrade = tradeAutoSettings.TradeAuto, MaxLeverage = tradeAutoSettings.MaxLeverage, PercentLeverage = tradeAutoSettings.PercentLeverage, HedgingOrdersEnabled = tradeAutoSettings.HedgingOrdersEnabled, FixedVolume = tradeAutoSettings.FixedVolume, MinVolume = tradeAutoSettings.MinVolume, MaxVolume = tradeAutoSettings.MaxVolume, VolumeRound = (int?)tradeAutoSettings.VolumeRound, StepVolume = tradeAutoSettings.StepVolume, TargetAccount = tradeAutoSettings.TargetAccount }); ctx.SaveChanges(); //Logger.Info("SubscribeOnPortfolio() - changes are saved"); // найти трейдеров, удовлетворяющих критерию List<PerformerStat> performers; try { try { performers = TradeSharpAccountStatistics.Instance.proxy.GetAllPerformersWithCriteria(true, targetPortfolio.Criteria, targetPortfolio.ParticipantCount, !targetPortfolio.DescendingOrder, (float?)targetPortfolio.MarginValue, 0); } catch (Exception ex) { Logger.Error( "Ошибка в SubscribeOnPortfolio() - получение перформеров (" + targetPortfolio.Criteria + ")", ex); return RequestStatus.ServerError; } if (performers == null) { Logger.Error("Ошибка в SubscribeOnPortfolio() - список перформеров не получен (" + targetPortfolio.Criteria + ")"); return RequestStatus.ServerError; } } catch (Exception ex) { Logger.Error("Ошибка в SubscribeOnPortfolio() - подписка", ex); return RequestStatus.ServerError; } // сравнить полученный список с текущими подписками заказчика ("инвестора") // сформировать список для "отписки" и список для подписки var performerAcs = performers.Select(p => p.Account).ToList(); var subsToRemove = ctx.SUBSCRIPTION.Where(s => s.User == user.ID && s.SERVICE1.ServiceType == (int)PaidServiceType.Signals && !performerAcs.Contains(s.SERVICE1.AccountId ?? 0)).ToList(); foreach (var sub in subsToRemove) { WalletError error; SubscribeOnService(ctx, user.ID, sub.Service, false, true, tradeAutoSettings, out error); if (error != WalletError.OK) { Logger.ErrorFormat("Portfolio - unsubscribe user {0} from srv {1}: error {2}", user.ID, sub.Service, error); } } // новоподписавшиеся foreach (var pf in performers) { WalletError error; SubscribeOnService(ctx, user.ID, pf.Service, true, false, tradeAutoSettings, out error); if (error != WalletError.OK) { Logger.DebugFormat("Подписка SubscribeOnPortfolio({0}), сигн. {1}: {2}", subscriberLogin, pf.Service, error); } } return RequestStatus.OK; }
private RequestStatus GetUserSubscribedCats(TradeSharpConnection ctx, string userLogin, string hash, long localTime, out List<Subscription> categories) { categories = new List<Subscription>(); try { var user = ctx.PLATFORM_USER.FirstOrDefault(u => u.Login == userLogin); if (user == null) return RequestStatus.Unauthorized; var userHash = CredentialsHash.MakeCredentialsHash(userLogin, user.Password, localTime); if (userHash != hash) return RequestStatus.Unauthorized; // получить подписку на торг. сигналы и собственные сигналы пользователя categories = (from uc in ctx.SUBSCRIPTION_V where uc.User == user.ID select uc).ToList().Select(LinqToEntity.DecorateSubscription).ToList(); return RequestStatus.OK; } catch (Exception ex) { Logger.Error("Error in GetUserSubscribedCats()", ex); return RequestStatus.ServerError; } }
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 bool HasUserPaidTheService(TradeSharpConnection ctx, SERVICE service, int usr) { var nowDate = DateTime.Now.Date; // проверить - совершался ли платеж за подключение к подписке в ближайшие N минут? // если да - не снимать денег var hasTodayTransfers = ctx.TRANSFER.Any(t => t.User == usr && EntityFunctions.TruncateTime(t.ValueDate) == nowDate && t.Subscription == service.ID); return hasTodayTransfers; }
private bool ModifyClosedPosition(MarketOrder order, TradeSharpConnection ctx, POSITION_CLOSED pos, out string errorString) { errorString = string.Empty; if (order.IsClosed) { var opened = new POSITION { AccountID = pos.AccountID, ID = pos.ID, Comment = order.Comment, Magic = order.Magic, ExpertComment = order.ExpertComment, PendingOrderID = order.PendingOrderID, PriceBest = (decimal?)order.PriceBest, PriceEnter = (decimal)order.PriceEnter, PriceWorst = (decimal?)order.PriceWorst, Side = order.Side, Stoploss = (decimal?)order.StopLoss, Symbol = order.Symbol, Takeprofit = (decimal?)order.TakeProfit, TimeEnter = order.TimeEnter, Volume = order.Volume, State = (int) order.State, MasterOrder = order.MasterOrder }; ctx.POSITION_CLOSED.Remove(pos); ctx.POSITION.Add(opened); ctx.SaveChanges(); Logger.InfoFormat("Closed order {0} {1} {2} {3} was reopened", pos.ID, pos.Side > 0 ? "BUY" : "SELL", pos.Volume, pos.Symbol); return true; } pos.Comment = order.Comment; pos.Magic = order.Magic; pos.ExpertComment = order.ExpertComment; pos.PendingOrderID = order.PendingOrderID; pos.PriceBest = (decimal?)order.PriceBest; pos.PriceEnter = (decimal)order.PriceEnter; pos.PriceWorst = (decimal?)order.PriceWorst; pos.Side = order.Side; pos.Stoploss = (decimal?)order.StopLoss; pos.Symbol = order.Symbol; pos.Takeprofit = (decimal?)order.TakeProfit; if (order.TimeEnter != default(DateTime)) pos.TimeEnter = order.TimeEnter; pos.Volume = order.Volume; pos.ExitReason = (int) order.ExitReason; pos.PriceExit = (decimal) (order.PriceExit ?? 0); pos.PriceWorst = (decimal?) order.PriceWorst; pos.ResultBase = (decimal)order.ResultBase; pos.ResultDepo = (decimal)order.ResultDepo; pos.ResultPoints = (decimal)order.ResultPoints; pos.TimeExit = order.TimeExit ?? default(DateTime); pos.Swap = (decimal) (order.Swap ?? 0); Logger.InfoFormat("Closed order {0} {1} {2} {3} was modified", pos.ID, pos.Side > 0 ? "BUY" : "SELL", pos.Volume, pos.Symbol); ctx.SaveChanges(); return true; }
private void DoFixtransactions(BackgroundWorkerTask taskParam) { var accountList = (List<int>) taskParam.DataParam; int accountNumber = 0; foreach (var id in accountList) { if (workerQuote.CancellationPending) break; var accountId = id; using (var db = new TradeSharpConnection()) { foreach (var order in db.POSITION_CLOSED.Where(p => p.AccountID == accountId)) { if (workerQuote.CancellationPending) break; var orderId = order.ID; if (db.BALANCE_CHANGE.Any(bc => bc.Position == orderId)) continue; var changeType = (int) (order.ResultDepo > 0 ? BalanceChangeType.Profit : BalanceChangeType.Loss); var closeTime = order.TimeExit; var orderResult = Math.Abs(order.ResultDepo); var missIdBc = db.BALANCE_CHANGE.FirstOrDefault(b => b.AccountID == accountId && b.ValueDate == closeTime && b.Amount == orderResult); if (missIdBc != null) { missIdBc.Position = orderId; missIdBc.ChangeType = changeType; } else db.BALANCE_CHANGE.Add(new BALANCE_CHANGE { AccountID = order.AccountID, Position = order.ID, Amount = order.ResultDepo, ChangeType = changeType, ValueDate = order.TimeExit }); } db.SaveChanges(); } workerQuote.ReportProgress(100*(accountNumber++)/accountList.Count); } }
public RequestStatus UnsubscribeUserFromPortfolio(TradeSharpConnection ctx, string subscriberLogin, bool deletePortfolio, bool deleteSubscriptions) { try { var user = ctx.PLATFORM_USER.FirstOrDefault(u => u.Login == subscriberLogin); if (user == null) return RequestStatus.Unauthorized; var subscriptions = ctx.USER_TOP_PORTFOLIO.Where(u => u.User == user.ID).ToList(); foreach (var portfolioSub in subscriptions) { // удалить подписку ctx.USER_TOP_PORTFOLIO.Remove(portfolioSub); Logger.InfoFormat("UnsubscribeUserFromPortfolio({0}) - отписан от портфеля", subscriberLogin); // если подписка была на пользовательский портфель - удалить пользовательский портфель if (deletePortfolio) { var portfolio = ctx.TOP_PORTFOLIO.Single(p => p.Id == portfolioSub.Portfolio); if (portfolio.OwnerUser == user.ID) ctx.TOP_PORTFOLIO.Remove(portfolio); } if (!deleteSubscriptions) continue; // удалить подписки на сервисы var subs = ctx.SUBSCRIPTION.Where(s => s.User == user.ID).ToList(); foreach (var sub in subs) ctx.SUBSCRIPTION.Remove(sub); } ctx.SaveChanges(); return RequestStatus.OK; } catch (Exception ex) { Logger.Error("Ошибка в UnsubscribePortfolio()", ex); return RequestStatus.ServerError; } }
/// <summary> /// возвращает - соклько раз была списана комиссия /// </summary> public int ReCalculateAccountShares(TradeSharpConnection ctx, SERVICE srv) { var feeTakenCount = 0; try { PaidService serviceWithFeeScale = null; // дольки var shares = ctx.ACCOUNT_SHARE.Where(s => s.Account == srv.AccountId && s.ShareOwner != srv.User).ToList(); if (shares.Count == 0) return feeTakenCount; var ownerShare = ctx.ACCOUNT_SHARE.FirstOrDefault(s => s.Account == srv.AccountId && s.ShareOwner == srv.User); if (ownerShare == null) { Logger.ErrorFormat( "ReCalculateAccountShares(service={0}, account={1}, owner={2}) - доля владельца не найдена", srv.ID, srv.AccountId, srv.User); return feeTakenCount; } var account = ctx.ACCOUNT.First(a => a.ID == srv.AccountId); // получить актуальный баланс (средства) счета var positions = ctx.POSITION.Where(p => p.AccountID == srv.AccountId && p.State == (int) PositionState.Opened) .ToList().Select(LinqToEntity.DecorateOrder).ToList(); var quotes = QuoteStorage.Instance.ReceiveAllData(); bool noQuoteError; var profit = DalSpot.Instance.CalculateOpenedPositionsCurrentResult(positions, quotes, account.Currency, out noQuoteError); var equity = account.Balance + (decimal) profit; if (noQuoteError) { Logger.Error("Ошибка в ReCalculateAccountShares - нет котировки для пересчета одного из тикеров (" + string.Join(", ", positions.Select(p => p.Symbol).Distinct())); return feeTakenCount; } // пересчитать долю каждого пайщика var ownersMoney = equity * ownerShare.Share / 100M; serviceWithFeeScale = GetPaidServiceProgressiveFeeScaleDetail(ctx, srv); foreach (var share in shares) { bool feeWasTaken; var record = CalculateAccountShare(ctx, share, ownerShare, ref ownersMoney, equity, serviceWithFeeScale, out feeWasTaken); ctx.ACCOUNT_SHARE_HISTORY.Add(record); if (feeWasTaken) feeTakenCount++; } } catch (Exception ex) { Logger.Error("Ошибка в ReCalculateAccountShares", ex); } return feeTakenCount; }
//, PLATFORM_USER usr) /// <summary> /// по средствам клиента определить сумму по прогрессивной шкале /// </summary> private decimal GetFeeByUserBalance(TradeSharpConnection ctx, SERVICE srv) { var rate = ctx.SERVICE_RATE.FirstOrDefault(r => r.Service == srv.ID); return rate == null ? 0 : rate.Amount; }
/// <summary> /// отключить подписчика /// заодно отрубить ему торговые сигналы /// </summary> public bool UnsubscribeSubscriber(TradeSharpConnection ctx, SUBSCRIPTION sub) { // отписать подписчика и сформировать для него уведомление try { var service = ctx.SERVICE.First(s => s.ID == sub.Service); Logger.InfoFormat("UnsubscribeSubscriber(srv={0}, user={1}, srvAccount={2})", sub.Service, sub.User, service.AccountId); // отписать от торг. сигналов? if (service.ServiceType == (int)PaidServiceType.Signals) { var signalService = (from srv in ctx.SERVICE where srv.ID == sub.Service select srv).FirstOrDefault(); if (signalService != null) { var signalId = signalService.ID; var query = (from us in ctx.SUBSCRIPTION where us.Service == signalId && us.User == sub.User select us); var userSignalToRemove = new List<SUBSCRIPTION>(); foreach (var userSubscribed in query) userSignalToRemove.Add(userSubscribed); Logger.InfoFormat("UnsubscribeSubscriber(): remove {0} signals", userSignalToRemove.Count); foreach (var userSubscribed in userSignalToRemove) ctx.SUBSCRIPTION.Remove(userSubscribed); } else { Logger.ErrorFormat("UnsubscribeSubscriber(#{0}) - владелец сигнала #{1} не найден", sub.User, sub.Service); } } // убрать саму подписку ctx.SUBSCRIPTION.Remove(sub); // !! с уведомлением пока что вопрос return true; } catch (Exception ex) { Logger.Error("Error in UnsubscribeSubscriber()", ex); return false; } }
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; }