using Knoks.Core.Entities; using Knoks.Core.Entities.Args; using Knoks.Core.Logic.Interfaces; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using System.Linq; using Knoks.Core.Data.Interfaces; using Microsoft.AspNetCore.Http; using System.IO; using Knoks.Core.Data.Dao; namespace Knoks.Core.Logic.Managers { public class KnokManager : IKnokManager { #region Fields private readonly IKnokSettings settings; private readonly IKnokDao knokDao; private readonly IImageManager imageManager; private readonly ITagDao tagDao; private readonly Framework.Services.CommonSettings _commonSettings; private readonly IKnokPricelManager _priceManager; private readonly IGeneralManager _generalManager; #endregion /// /// ctor /// /// /// /// /// /// /// public KnokManager(Framework.Services.CommonSettings commonSettings, IKnokSettings settings, IKnokDao knokDao, ITagDao tagDao, IImageManager imageManager, IKnokPricelManager priceManager, IGeneralManager generalManager) { this.settings = settings; this._priceManager = priceManager; this.knokDao = knokDao; this.tagDao = tagDao; this.imageManager = imageManager; this._commonSettings = commonSettings; this._generalManager = generalManager; } public IDictionary Settings { get { var accessor = FastMember.TypeAccessor.Create(typeof(KnokSettings)); return accessor.GetMembers().ToDictionary(it => it.Name, it => accessor[settings, it.Name]?.ToString()); } } public async Task Process(CreateKnokArgs knok, decimal currentPrice) { if (await Validate(knok)) { var result = await knokDao.CreateKnok(knok, currentPrice); await UpdateTags(knok, result); await SaveImage(knok, result); InitializeImage(result.KnokId.Value, result?.Technical?.Image); return result; } return null; } public async Task GetKnok(long knokId, long? userId) { var result = await knokDao.GetKnokDetails(knokId, (settings as IKnokFeedsSettings).KnokFeedExpirationHours); if (result.CreatorUserId != userId) { throw new ArgumentException("Knok does not exists.", nameof(userId)); } await LoadTags(result); await knokDao.LoadImage(result.KnokId.Value, result); InitializeImage(knokId, result?.Technical?.Image); return result; } public async Task GetKnokFull(long knokId, long userId) { var result = await knokDao.GetKnokFullDetails(knokId, userId, (settings as IKnokFeedsSettings).KnokFeedExpirationHours, settings.DurationCRTAggrItems); if (result.Knoker.HasAvatar.HasValue) { result.Knoker.AvatarUrl = _generalManager.GetAvatarUrl(result.Knoker.UserId); } await LoadTags(result.Knok); await knokDao.LoadImage(result.Knok.KnokId.Value, result?.Knok); InitializeImage(knokId, result?.Knok.Technical?.Image); return result; } public KnokRate KnokRatingCalc(CreateKnokArgs create) { var knok = create; var result = new KnokRate(); if (knok == null) { throw new ArgumentNullException("Please provide data"); } result.Entry = (create.EntryPriceTo > 0 && create.EntryPriceFrom > 0) ? (1 - (create.EntryPriceTo - create.EntryPriceFrom) / (create.EntryPriceFrom * (settings.MAREntry / 100))) * 100 : 0; result.Exit = (create.ExitPriceTo > 0 && create.ExitPriceFrom > 0) ? (1 - (create.ExitPriceTo - create.ExitPriceFrom) / (create.ExitPriceFrom * (settings.MARExit / 100))) * 100 : 0; result.EntryToSL = (create.StopLoss > 0 && create.EntryPriceFrom > 0) ? (1 - (create.EntryPriceFrom - create.StopLoss) / (create.StopLoss * (settings.MaxSL / 100m))) * 100 : 0; result.Duration = create.Duration > 0 ? (1 - ((decimal)create.Duration / (decimal)settings.MAD)) * 100 : 0; var midEntry = (create.EntryPriceTo + create.EntryPriceFrom) / 2; var midExit = (create.ExitPriceTo + create.ExitPriceFrom) / 2; result.PotentialProfitValue = (midExit - midEntry) / midEntry * 100; if (result.PotentialProfitValue <= Constants.potentialProfitLow * 100) { result.PotentialProfit = PotentialProfit.Low; } else if (result.PotentialProfitValue <= Constants.potentialProfitMedium * 100) { result.PotentialProfit = PotentialProfit.Medium; } else if (result.PotentialProfitValue <= Constants.potentialProfitHigh * 100) { result.PotentialProfit = PotentialProfit.High; } else { result.PotentialProfit = PotentialProfit.VeryHigh; } //Calculation total depends on weights. 50% max result.Total = result.Entry * (Constants.entryWeight * 0.5m) + result.Exit * (Constants.exitWeight * 0.5m) + result.PotentialProfitValue * (Constants.entryToExitWeight * 0.5m) + result.EntryToSL * (Constants.entryToStopWeight * 0.5m) + result.Duration * (Constants.durationWeight * 0.5m); // Add additional 50% if (create.Technical != null && !string.IsNullOrEmpty( create.Technical.Header) && (create.Technical.Image != null && (create.Technical.Image.Content != null || !string.IsNullOrEmpty(create.Technical.Image.Url)))) { result.Total += Constants.analysisInfoRate; } if (create.Fundamental != null && !string.IsNullOrEmpty(create.Fundamental.Header) && !string.IsNullOrEmpty(create.Fundamental.References)) { result.Total += Constants.analysisInfoRate; } //TODO: That is not part of the knok rating calculating result.Price = _priceManager.TotalPrice(knok, create.UserId).GetAwaiter().GetResult(); result.PriceUSD = result.Price; return result; } public Task Validate(CreateKnokArgs create) { var knok = create; if (knok == null) { throw new ArgumentNullException("Please provide data"); } Dictionary errors = new Dictionary(); if (knok.EntryPriceFrom >= knok.EntryPriceTo) { errors.Add(nameof(knok.EntryPriceTo), $"To value should be greater than From"); } else { if (knok.EntryPriceFrom > 0 && (knok.EntryPriceTo - knok.EntryPriceFrom) / knok.EntryPriceFrom > settings.MAREntry * 0.01m) { errors.Add(nameof(knok.EntryPriceTo), $"Maximum allowed range between Entry From and To is {settings.MAREntry} percent"); } } if (knok.ExitPriceFrom >= knok.ExitPriceTo) { errors.Add(nameof(knok.ExitPriceTo), $"To value should be greater than From"); } else { if (knok.ExitPriceFrom > 0 && (knok.ExitPriceTo - knok.ExitPriceFrom) / knok.ExitPriceFrom > settings.MARExit * 0.01m) { errors.Add(nameof(knok.ExitPriceTo), $"Maximum allowed range between Exit From and To is {settings.MARExit} percent"); } } if (knok.StopLoss >= knok.EntryPriceFrom) { errors.Add(nameof(knok.StopLoss), $"Stop Loss value should be less than Entry From"); } if (errors.Any()) { throw new ValidationException(errors.First().Value, errors); } if (knok.StopLoss > 0 && (knok.EntryPriceFrom - knok.StopLoss) / knok.StopLoss > settings.MaxSL * 0.01m) { errors.Add(nameof(knok.StopLoss), $"Maximum allowed range between Stop Loss and Entry Price From is {settings.MaxSL} percent"); } if (knok.Duration > settings.MAD) { errors.Add(nameof(knok.Duration), $"Duration should be less than {settings.MAD} days"); } if (knok.EntryPriceTo > 0) { var potentialProfitValue = (knok.ExitPriceFrom - knok.EntryPriceTo) / knok.EntryPriceTo; if (Math.Round(potentialProfitValue, 2) < settings.MinAP * 0.01m) { errors.Add(nameof(knok.ExitPriceFrom), $"Minimum allowed profit is {settings.MinAP} percent"); } if (Math.Round(potentialProfitValue, 2) > settings.MaxAP * 0.01m) { errors.Add(nameof(knok.ExitPriceFrom), $"Maximum allowed profit is {settings.MaxAP} percent"); } ////Low: up to 30 % potential profit. //if (knok.PotentialProfitValue < 0.30m) //{ // knok.PotentialProfit = PotentialProfit.Low; //} ////Medium: 30 - 100 % potential profit. //else if (knok.PotentialProfitValue < 1m) //{ // knok.PotentialProfit = PotentialProfit.Medium; //} ////High: More than 100 % potential profit. //else //{ // knok.PotentialProfit = PotentialProfit.High; //} } if (errors.Any()) { throw new ValidationException(errors.First().Value, errors); } return Task.FromResult(true); } public string GetImageUrl(long knokId, string fileName) { string divider = fileName?.StartsWith("/") == true ? "" : "/"; return $"/knok/{knokId}/files{divider}{fileName}"; } public FileResponse GetFile(long knokId, string fileName) { var task = knokDao.GetKnokDetails(knokId, (settings as IKnokFeedsSettings).KnokFeedExpirationHours); task.Wait(); var knok = task.Result; var stream = imageManager.LoadKnokImage(knok.CreatorUserId.Value, knokId, fileName, out var modified); return new FileResponse() { File = stream, FileDownloadName = fileName, LastModified = modified }; } public async Task> GetPublicKnoks(KnokFeedArgs args) { var knoks = await knokDao.GetPublicKnoks(args, (settings as IKnokFeedsSettings).KnokFeedExpirationHours); return knoks.Select(it => { if (it.Knoker.HasAvatar) { it.Knoker.Href = _generalManager.GetAvatarUrl(it.Knoker.UserId.Value); } return it; }); } public async Task> GetAliveKnoks(KnokOngoingArgs args) { var knoks = await knokDao.GetAliveKnoks(args, settings.DurationCRTAggrItems); return knoks.Select(it => { if (it.Knokser.HasAvatar) { it.Knokser.Href = _generalManager.GetAvatarUrl(it.Knokser.UserId.Value); } return it; }); } public async Task> GetEndedKnoks(KnokEndedArgs args) { var knoks = await knokDao.GetEndedKnoks(args, settings.DurationCRTAggrItems); return knoks.Select(it => { if (it.Knokser.HasAvatar) { it.Knokser.Href = _generalManager.GetAvatarUrl(it.Knokser.UserId.Value); } return it; }); } public async Task> GetCurrentlyPublishedKnoks(KnokCurrentlyPublishedArgs args, long userId) { var knoks = await knokDao.GetCurrentlyPublishedKnoks(args, userId, (settings as IKnokFeedsSettings).KnokFeedExpirationHours, settings.DurationCRTAggrItems); return knoks.Select(it => { if (it.Knokser.HasAvatar) { it.Knokser.Href = _generalManager.GetAvatarUrl(it.Knokser.UserId.Value); } return it; }); } public async Task> GetPastPublishedKnoks(KnokPastPublishedArgs args, long userId) { var knoks = await knokDao.GetPastPublishedKnoks(args, userId, settings.DurationCRTAggrItems); return knoks.Select(it => { if (it.Knokser.HasAvatar) { it.Knokser.Href = _generalManager.GetAvatarUrl(it.Knokser.UserId.Value); } return it; }); } public async Task GetOwnData(long userId) { var data = await knokDao.GetOwnData(userId); if (data.Knoker.HasAvatar) { data.Knoker.Href = _generalManager.GetAvatarUrl(data.Knoker.UserId.Value); } return data; } public Task GetKnokPrice(long userId) { return knokDao.GetKnokPrice(userId); } public async Task PublishKnok(long knokId, User user, Account userAccount, decimal publishPrice, CreateKnokArgs args) // decimal publishPrice, decimal knokRating) { var knokRating = 0m; var knok = await knokDao.GetKnokDetails(knokId, (settings as IKnokFeedsSettings).KnokFeedExpirationHours); if (knok != null) { var knokargs = new CreateKnokArgs { Currency1 = knok.Currency1, Currency2 = knok.Currency2, Duration = knok.Duration, EntryPriceFrom = knok.EntryPriceFrom, EntryPriceTo = knok.EntryPriceTo, ExchangeId = knok.ExchangeId, ExitPriceFrom = knok.ExitPriceFrom, ExitPriceTo = knok.ExitPriceTo, Fundamental = knok.Fundamental, PotentialProfit = knok.PotentialProfit, PotentialProfitValue = knok.PotentialProfitValue, StopLoss = knok.StopLoss, UserId = knok.CreatorUserId.Value }; var rate = KnokRatingCalc(knokargs); knokRating = rate.Total; } else { knokRating = KnokRatingCalc(args).Total; } //var publishPrice = _priceManager.TotalPrice(args, user.UserId).GetAwaiter().GetResult(); return await knokDao.PublishKnok(knokId, user.UserId, userAccount.AccountId, publishPrice, knokRating); } public Task PurchaseKnok(long knokId, User user, Account account) { return knokDao.PurchaseKnok(knokId, user.UserId, account.AccountId); } public async Task GetKnokserProfile(long knokserId) { var knokserProfile = await knokDao.GetKnokserProfile(knokserId); knokserProfile.Href = knokserProfile.HasAvatar ? _generalManager.GetAvatarUrl(knokserProfile.UserId.Value) : null; return knokserProfile; } #region Private Methods private async Task SaveImage(CreateKnokArgs knok, Knok result) { var image = knok?.Technical?.Image; if (image != null) { Stream stream = image.ContentStream ?? (image.Content != null ? new MemoryStream(image.Content) : image.ContentBase64 != null ? Framework.Extentions.FileInfoExtentions.ParseImage(image.ContentBase64) : null); if (stream == null && !string.IsNullOrWhiteSpace(image.Url)) { try { image.FileName = image.FileName ?? new Uri(image.Url).AbsolutePath; } catch { } stream = await new System.Net.Http.HttpClient().GetStreamAsync(image.Url); } if (stream != null) { var fileName = await imageManager.SaveKnokImage(result.CreatorUserId.Value, result.KnokId.Value, image.FileName ?? "image.png", stream); image.FileName = fileName; } if (!string.IsNullOrWhiteSpace(image.FileName) || !string.IsNullOrWhiteSpace(image.Url)) { result.Technical.Image = await knokDao.CreateImage(result.KnokId.Value, image); } } } private async Task UpdateTags(CreateKnokArgs knok, Knok result) { if (knok?.Technical?.Tags != null) { result.Technical.Tags = await tagDao.SaveKnokTags((int)result.KnokId, knok?.Technical?.Tags, 1); } if (knok?.Fundamental?.Tags != null) { result.Fundamental.Tags = await tagDao.SaveKnokTags((int)result.KnokId, knok?.Fundamental?.Tags, 2); } } private async Task LoadTags(Knok knok) { if (knok?.Technical != null) { knok.Technical.Tags = await tagDao.GetKnokTags((int)knok.KnokId, 1); } if (knok?.Fundamental != null) { knok.Fundamental.Tags = await tagDao.GetKnokTags((int)knok.KnokId, 2); } } private void InitializeImage(long knokId, KnokImage image) { if (image != null) { image.Href = image.FileName != null ? GetImageUrl(knokId, image.FileName) : image.Url; } } #endregion } }