444 lines
18 KiB
C#
444 lines
18 KiB
C#
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
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
/// <param name="commonSettings"></param>
|
|
/// <param name="settings"></param>
|
|
/// <param name="knokDao"></param>
|
|
/// <param name="tagDao"></param>
|
|
/// <param name="imageManager"></param>
|
|
/// <param name="generalManager"></param>
|
|
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<string, string> 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<Knok> 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<Knok> 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<KnokFullData> 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<bool> Validate(CreateKnokArgs create)
|
|
{
|
|
var knok = create;
|
|
if (knok == null)
|
|
{
|
|
throw new ArgumentNullException("Please provide data");
|
|
}
|
|
Dictionary<string, string> errors = new Dictionary<string, string>();
|
|
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<IEnumerable<KnokFeed>> 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<IEnumerable<KnokOngoing>> 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<IEnumerable<KnokEnded>> 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<IEnumerable<KnokOngoing>> 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<IEnumerable<KnokEnded>> 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<KnokOwnData> 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<float> GetKnokPrice(long userId)
|
|
{
|
|
return knokDao.GetKnokPrice(userId);
|
|
}
|
|
|
|
public async Task<Knok> 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<KnokserProfile> 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
|
|
}
|
|
}
|