2018-05-22 10:11:50 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using Core.Main ;
2024-01-15 15:12:46 +01:00
using System.Globalization ;
2018-05-22 10:11:50 +02:00
using Core.Main.DataObjects ;
using Core.Main.DataObjects.PTMagicData ;
2018-12-15 22:07:29 +01:00
namespace Monitor.Pages
{
public class SalesAnalyzer : _Internal . BasePageModelSecure
{
2018-05-22 10:11:50 +02:00
public ProfitTrailerData PTData = null ;
2024-01-15 15:12:46 +01:00
public MiscData MiscData { get ; set ; }
public PropertiesData PropertiesData { get ; set ; }
public StatsData StatsData { get ; set ; }
public List < DailyPNLData > DailyPNL { get ; set ; }
2024-01-16 17:56:52 +01:00
public List < DailyTCVData > DailyTCV { get ; set ; }
2024-01-17 18:13:09 +01:00
public List < ProfitablePairsData > ProfitablePairs { get ; set ; }
2024-01-16 17:56:52 +01:00
public int ProfitDays { get ; set ; }
public int TCVDays { get ; set ; }
2024-01-15 15:12:46 +01:00
public List < MonthlyStatsData > MonthlyStats { get ; set ; }
2018-05-22 10:11:50 +02:00
public string TradesChartDataJSON = "" ;
2024-01-16 17:56:52 +01:00
public string CumulativeProfitChartDataJSON = "" ;
public string TCVChartDataJSON = "" ;
2018-05-22 10:11:50 +02:00
public string ProfitChartDataJSON = "" ;
2019-03-19 17:13:41 +01:00
public string BalanceChartDataJSON = "" ;
2018-05-22 10:11:50 +02:00
public IEnumerable < KeyValuePair < string , double > > TopMarkets = null ;
public DateTime MinSellLogDate = Constants . confMinDate ;
public Dictionary < DateTime , double > DailyGains = new Dictionary < DateTime , double > ( ) ;
public Dictionary < DateTime , double > MonthlyGains = new Dictionary < DateTime , double > ( ) ;
public DateTimeOffset DateTimeNow = Constants . confMinDate ;
2020-07-22 09:48:40 +02:00
public double totalCurrentValue = 0 ;
2024-01-15 15:12:46 +01:00
2018-12-15 22:07:29 +01:00
public void OnGet ( )
{
2018-05-22 10:11:50 +02:00
base . Init ( ) ;
2018-12-15 22:07:29 +01:00
2018-05-22 10:11:50 +02:00
BindData ( ) ;
}
2024-01-15 15:12:46 +01:00
2018-12-15 22:07:29 +01:00
private void BindData ( )
{
2019-10-13 22:08:48 +02:00
PTData = this . PtDataObject ;
2024-01-15 15:12:46 +01:00
MiscData = this . PTData . Misc ;
PropertiesData = this . PTData . Properties ;
StatsData = this . PTData . Stats ;
MonthlyStats = this . PTData . MonthlyStats ;
DailyPNL = this . PTData . DailyPNL ;
2024-01-16 17:56:52 +01:00
DailyTCV = this . PTData . DailyTCV ;
2024-01-17 18:13:09 +01:00
ProfitablePairs = this . PTData . ProfitablePairs ;
2024-01-15 15:12:46 +01:00
2018-05-22 10:11:50 +02:00
// Convert local offset time to UTC
TimeSpan offsetTimeSpan = TimeSpan . Parse ( PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . Replace ( "+" , "" ) ) ;
DateTimeNow = DateTimeOffset . UtcNow . ToOffset ( offsetTimeSpan ) ;
BuildSalesChartData ( ) ;
2024-01-16 17:56:52 +01:00
BuildProfitChartData ( ) ;
BuildCumulativeProfitChartData ( ) ;
BuildTCVChartData ( ) ;
2024-01-15 15:12:46 +01:00
}
2024-01-16 17:56:52 +01:00
private void BuildTCVChartData ( )
{
List < object > TCVPerDayList = new List < object > ( ) ;
if ( PTData . DailyTCV . Count > 0 )
{
// Get timezone offset
TimeSpan offset ;
bool isNegative = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . StartsWith ( "-" ) ;
string offsetWithoutSign = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . TrimStart ( '+' , '-' ) ;
if ( ! TimeSpan . TryParse ( offsetWithoutSign , out offset ) )
{
offset = TimeSpan . Zero ; // If offset is invalid, set it to zero
}
DateTime endDate = DateTime . UtcNow . Add ( isNegative ? - offset : offset ) . Date ;
// Parse dates once and adjust them to the local timezone
Dictionary < DateTime , DailyTCVData > dailyTCVByDate = PTData . DailyTCV
. Select ( data = > {
DateTime dateUtc = DateTime . ParseExact ( data . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ;
DateTime dateLocal = dateUtc . Add ( isNegative ? - offset : offset ) ;
return new { Date = dateLocal . Date , Data = data } ;
} )
. ToDictionary (
item = > item . Date ,
item = > item . Data
) ;
DateTime earliestDataDate = dailyTCVByDate . Keys . Min ( ) ;
DateTime startDate = earliestDataDate ;
// Calculate the total days of data available
TCVDays = ( endDate - startDate ) . Days ;
for ( DateTime date = startDate ; date < = endDate ; date = date . AddDays ( 1 ) )
{
// Use the dictionary to find the Data for the date
if ( dailyTCVByDate . TryGetValue ( date , out DailyTCVData dailyTCV ) )
{
double TCV = dailyTCV . TCV ;
// Add the data point to the list
TCVPerDayList . Add ( new { x = new DateTimeOffset ( date ) . ToUnixTimeMilliseconds ( ) , y = TCV } ) ;
}
}
// Convert the list to a JSON string using Newtonsoft.Json
TCVChartDataJSON = Newtonsoft . Json . JsonConvert . SerializeObject ( new [ ] {
new {
key = "TCV in " + PTData . Misc . Market ,
color = Constants . ChartLineColors [ 1 ] ,
values = TCVPerDayList
}
} ) ;
}
}
private void BuildCumulativeProfitChartData ( )
{
List < object > profitPerDayList = new List < object > ( ) ;
if ( PTData . DailyPNL . Count > 0 )
{
// Get timezone offset
TimeSpan offset ;
bool isNegative = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . StartsWith ( "-" ) ;
string offsetWithoutSign = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . TrimStart ( '+' , '-' ) ;
if ( ! TimeSpan . TryParse ( offsetWithoutSign , out offset ) )
{
offset = TimeSpan . Zero ; // If offset is invalid, set it to zero
}
DateTime endDate = DateTime . UtcNow . Add ( isNegative ? - offset : offset ) . Date ;
// Parse dates once and adjust them to the local timezone
Dictionary < DateTime , DailyPNLData > dailyPNLByDate = PTData . DailyPNL
. Select ( data = > {
DateTime dateUtc = DateTime . ParseExact ( data . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ;
DateTime dateLocal = dateUtc . Add ( isNegative ? - offset : offset ) ;
return new { Date = dateLocal . Date , Data = data } ;
} )
. ToDictionary (
item = > item . Date ,
item = > item . Data
) ;
DateTime earliestDataDate = dailyPNLByDate . Keys . Min ( ) ;
DateTime startDate = earliestDataDate ;
// Calculate the total days of data available
ProfitDays = ( endDate - startDate ) . Days ;
double previousDayCumulativeProfit = 0 ;
for ( DateTime date = startDate ; date < = endDate ; date = date . AddDays ( 1 ) )
{
// Use the dictionary to find the DailyPNLData for the date
if ( dailyPNLByDate . TryGetValue ( date , out DailyPNLData dailyPNL ) )
{
// Use the CumulativeProfitCurrency directly
double profitFiat = dailyPNL . CumulativeProfitCurrency ;
// Add the data point to the list
profitPerDayList . Add ( new { x = new DateTimeOffset ( date ) . ToUnixTimeMilliseconds ( ) , y = profitFiat } ) ;
previousDayCumulativeProfit = dailyPNL . CumulativeProfitCurrency ;
}
}
// Convert the list to a JSON string using Newtonsoft.Json
CumulativeProfitChartDataJSON = Newtonsoft . Json . JsonConvert . SerializeObject ( new [ ] {
new {
key = "Profit in " + PTData . Misc . Market ,
color = Constants . ChartLineColors [ 1 ] ,
values = profitPerDayList
}
} ) ;
}
}
private void BuildProfitChartData ( )
{
List < object > profitPerDayList = new List < object > ( ) ;
if ( PTData . DailyPNL . Count > 0 )
{
// Get timezone offset
TimeSpan offset ;
bool isNegative = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . StartsWith ( "-" ) ;
string offsetWithoutSign = PTMagicConfiguration . GeneralSettings . Application . TimezoneOffset . TrimStart ( '+' , '-' ) ;
if ( ! TimeSpan . TryParse ( offsetWithoutSign , out offset ) )
{
offset = TimeSpan . Zero ; // If offset is invalid, set it to zero
}
DateTime endDate = DateTime . UtcNow . Add ( isNegative ? - offset : offset ) . Date ;
// Parse dates once and adjust them to the local timezone
Dictionary < DateTime , DailyPNLData > dailyPNLByDate = PTData . DailyPNL
. Select ( data = > {
DateTime dateUtc = DateTime . ParseExact ( data . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ;
DateTime dateLocal = dateUtc . Add ( isNegative ? - offset : offset ) ;
return new { Date = dateLocal . Date , Data = data } ;
} )
. ToDictionary (
item = > item . Date ,
item = > item . Data
) ;
DateTime earliestDataDate = dailyPNLByDate . Keys . Min ( ) ;
DateTime startDate = earliestDataDate ;
// Calculate the total days of data available
ProfitDays = ( endDate - startDate ) . Days ;
double previousDayCumulativeProfit = 0 ;
bool isFirstDay = true ;
for ( DateTime date = startDate ; date < = endDate ; date = date . AddDays ( 1 ) )
{
// Use the dictionary to find the DailyPNLData for the date
if ( dailyPNLByDate . TryGetValue ( date , out DailyPNLData dailyPNL ) )
{
if ( isFirstDay )
{
isFirstDay = false ;
}
else
{
// Calculate the profit for the current day
double profitFiat = Math . Round ( dailyPNL . CumulativeProfitCurrency - previousDayCumulativeProfit , 2 ) ;
// Add the data point to the list
profitPerDayList . Add ( new { x = new DateTimeOffset ( date ) . ToUnixTimeMilliseconds ( ) , y = profitFiat } ) ;
}
previousDayCumulativeProfit = dailyPNL . CumulativeProfitCurrency ;
}
}
// Convert the list to a JSON string using Newtonsoft.Json
ProfitChartDataJSON = Newtonsoft . Json . JsonConvert . SerializeObject ( new [ ] {
new {
key = "Profit in " + PTData . Misc . Market ,
color = Constants . ChartLineColors [ 1 ] ,
values = profitPerDayList
}
} ) ;
}
}
2024-01-15 15:12:46 +01:00
public ( double totalMonths , DateTime startDate , DateTime endDate ) MonthlyAverages ( List < MonthlyStatsData > monthlyStats , List < DailyPNLData > dailyPNL )
{
double totalMonths = 0 ;
// Get the exact start and end dates of sales data
DateTime startDate = dailyPNL . Min ( d = > DateTime . ParseExact ( d . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ) ;
DateTime endDate = dailyPNL . Max ( d = > DateTime . ParseExact ( d . Date , "d-M-yyyy" , CultureInfo . InvariantCulture ) ) ;
int daysInFirstMonth = DateTime . DaysInMonth ( startDate . Year , startDate . Month ) - startDate . Day + 1 ;
int daysInLastMonth = endDate . Day ;
//Console.WriteLine("Start Date: {0}, End Date: {1}, Days in first month: {2}, Days in last month: {3}", startDate, endDate, daysInFirstMonth, daysInLastMonth);
for ( int i = 0 ; i < monthlyStats . Count ; i + + )
{
var monthStat = monthlyStats [ i ] ;
double weight ;
// Parse the Month property into a DateTime object
DateTime monthDate = DateTime . ParseExact ( monthStat . Month , "M-yyyy" , CultureInfo . InvariantCulture ) ;
// If it's the first or last month in the dataset, calculate the weight based on the number of days
if ( i = = 0 )
{
// Calculate weight based on the number of days in the dataset for the first month
weight = daysInFirstMonth / 30.0 ;
}
else if ( i = = monthlyStats . Count - 1 )
{
// Calculate weight based on the number of days in the dataset for the last month
weight = ( daysInLastMonth / 30.0 ) ;
}
else
{
// Otherwise, assume it's a full month
weight = 1 ;
}
totalMonths + = weight ;
//Console.WriteLine("Month: {0}, Weight: {1}", monthStat.Month, weight);
}
return ( totalMonths , startDate , endDate ) ;
2018-05-22 10:11:50 +02:00
}
2018-12-15 22:07:29 +01:00
private void BuildTopMarkets ( )
{
2018-05-22 10:11:50 +02:00
var markets = PTData . SellLog . GroupBy ( m = > m . Market ) ;
Dictionary < string , double > topMarketsDic = new Dictionary < string , double > ( ) ;
2018-12-15 22:07:29 +01:00
foreach ( var market in markets )
{
2020-07-26 16:28:37 +02:00
double totalProfit = 0 ;
2021-08-24 05:48:50 +02:00
totalProfit = PTData . SellLog . FindAll ( m = > m . Market = = market . Key ) . Sum ( m = > m . Profit ) ;
2018-05-22 10:11:50 +02:00
topMarketsDic . Add ( market . Key , totalProfit ) ;
}
TopMarkets = new SortedDictionary < string , double > ( topMarketsDic ) . OrderByDescending ( m = > m . Value ) . Take ( PTMagicConfiguration . GeneralSettings . Monitor . MaxTopMarkets ) ;
}
2018-12-15 22:07:29 +01:00
private void BuildSalesChartData ( )
{
if ( PTData . SellLog . Count > 0 )
{
2018-05-22 10:11:50 +02:00
MinSellLogDate = PTData . SellLog . OrderBy ( sl = > sl . SoldDate ) . First ( ) . SoldDate . Date ;
2019-03-22 15:23:32 +01:00
DateTime graphStartDate = DateTimeNow . DateTime . Date . AddDays ( - 1850 ) ;
2018-05-22 10:11:50 +02:00
if ( MinSellLogDate > graphStartDate ) graphStartDate = MinSellLogDate ;
int tradeDayIndex = 0 ;
string tradesPerDayJSON = "" ;
2018-12-15 22:07:29 +01:00
for ( DateTime salesDate = graphStartDate ; salesDate < = DateTimeNow . DateTime . Date ; salesDate = salesDate . AddDays ( 1 ) )
{
if ( tradeDayIndex > 0 )
{
2018-05-22 10:11:50 +02:00
tradesPerDayJSON + = ",\n" ;
}
int trades = PTData . SellLog . FindAll ( t = > t . SoldDate . Date = = salesDate . Date ) . Count ;
tradesPerDayJSON + = "{x: new Date('" + salesDate . Date . ToString ( "yyyy-MM-dd" ) + "'), y: " + trades + "}" ;
tradeDayIndex + + ;
}
TradesChartDataJSON = "[" ;
TradesChartDataJSON + = "{" ;
TradesChartDataJSON + = "key: 'Sales'," ;
TradesChartDataJSON + = "color: '" + Constants . ChartLineColors [ 0 ] + "'," ;
TradesChartDataJSON + = "values: [" + tradesPerDayJSON + "]" ;
TradesChartDataJSON + = "}" ;
TradesChartDataJSON + = "]" ;
2020-07-26 16:28:37 +02:00
2018-05-22 10:11:50 +02:00
}
}
}
}