Calculate Running Total Using LINQ to SQL - c#

I have a WPF C# Application which is accessing data through a SQL-LINQ connection and putting it on a Datagrid. I have added a Running Pips column and have been trying to figure out how in to get this column populated correctly. I have found several methods through searching the web, but none of these are appropriate for my particular setup. Most of the time I end up in an endless loop. I would like the Running Pips column to be cumulative and the order of the calculation to start from the earliest 'Close Time'.
Close Time Profit Running Profit
10.09.11 $10 $10
10.10.11 $20 $30
10.11.11 $15 $45
Here is a summary of the code. I hope someone can suggest how to fix this code with any relevant information needed to aid me in implementing this:
public void RefreshClose() {
if (CloseTradeCollection == null) return;
var i = 0;
if (StaticTool.SelectedAccount == null) {
ExistCloseTrade = false;
CloseTradeCollection.Clear();
return;
}
Account = StaticTool.SelectedAccount;
foreach (var trade in GetClosedTradesFromDb()) {
if (CloseTradeCollection.Count <= i) {
ExistCloseTrade = true;
var tradeDetails = new Trade {
Id = trade.id,
Ticket = trade.ticket,
OpenTime = trade.opentime,
CloseTime = trade.closetime,
Symbol = trade.symbol,
OpenPrice = trade.openprice,
ClosePrice = trade.closeprice,
Profit = trade.profit,
Comment = trade.comment.Trim(),
};
tradeDetails.History = tradeDetails.CloseTime - tradeDetails.OpenTime;
CloseTradeCollection.Add(tradeDetails);
}
else {
var tradeDetails = CloseTradeCollection[i];
tradeDetails.Id = trade.id;
tradeDetails.Ticket = trade.ticket;
tradeDetails.OpenTime = trade.opentime;
tradeDetails.CloseTime = trade.closetime;
tradeDetails.Symbol = trade.symbol;
tradeDetails.OpenPrice = trade.openprice;
tradeDetails.ClosePrice = trade.closeprice;
tradeDetails.Profit = trade.profit;
tradeDetails.Comment = trade.comment.Trim();
tradeDetails.History = DateTime.Now - tradeDetails.OpenTime;
//tradeDetails.RunningProfit = ????????
}
i++;
}
}

You can create a local variable to keep track of running profit, making use of the fact that the assignment operation returns the value being assigned.
tradeDetails.RunningProfit = (runningProfit = runningProfit + trade.profit);

Here is a general idea - you should be able to adopt it to your needs:
class Trade {
public DateTime CloseTime { get; set; }
public decimal Profit { get; set; }
}
class TradeWithRunningProfit : Trade {
public decimal RunningProfit { get; set; }
}
class Program {
static IEnumerable<TradeWithRunningProfit> GetRunningProfits(IEnumerable<Trade> rows) {
decimal running_profit = 0;
return
from row in rows
select new TradeWithRunningProfit {
CloseTime = row.CloseTime,
Profit = row.Profit,
RunningProfit = (running_profit += row.Profit)
};
}
static void Main(string[] args) {
var rows = new[] {
new Trade { CloseTime = new DateTime(11,10,09), Profit = 10},
new Trade { CloseTime = new DateTime(11,10,10), Profit = 20},
new Trade { CloseTime = new DateTime(11,10,11), Profit = 15},
};
foreach (var row_with_running_profit in GetRunningProfits(rows)) {
Console.WriteLine(
"{0}\t{1}\t{2}",
row_with_running_profit.CloseTime,
row_with_running_profit.Profit,
row_with_running_profit.RunningProfit
);
}
}
}

Related

Acumatica Update Revenue Budget Fields from Tasks on Project level

In Acumatica (Build 2020.5.2.368) we have done extensive customisations on the Project Task (DAC: PMTask) which saves correctly when we hit the Save button. When we want to push over some of the changes to the Revenue Budget tab (DAC: PMRevenueBudget) - we use the following code:
// Task updated
protected void PMTask_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e)
{
var row = (PMTask)e.Row;
PMTaskExt TaskExt = row.GetExtension<PMTaskExt>();
// Check Qty
if (TaskExt.UsrQuantity == 0)
{
TaskExt.UsrQuantity = 1;
}
if (TaskExt.UsrCostPrice != 0 && TaskExt.UsrMarginPerc != 0)
{
// do custom calculations here -- I removed the part of the code as it was useless for the purpose of posting here.
// Set custom fields
// These fields all have values greater than 0 --> Double Checked this
TaskExt.UsrMarkupPerc = TaskExt.UsrCostPrice * TaskExt.UsrQuantity / 100;
TaskExt.UsrLineCost = TaskExt.UsrCostPrice * TaskExt.UsrQuantity;
TaskExt.UsrSellPrice = TaskExt.UsrUnitPrice * TaskExt.UsrQuantity;
TaskExt.UsrTotalRevenue = TaskExt.UsrSellPrice - TaskExt.UsrLineCost;
TaskExt.UsrCostPriceCalculation = Convert.ToDecimal(12.00);
TaskExt.UsrUnitPriceCalculation = Convert.ToDecimal(88.99);
}
else
{
// More calculations
}
int revAccountID = 0;
int costAccountID = 0;
foreach (PMAccountGroup pmag in AccountInfo.Select())
{
if (pmag.GroupCD.Trim().Equals("INCOME"))
{
revAccountID = (int)pmag.GroupID; // 898
}
if (pmag.GroupCD.Trim().Equals("EXPENSES"))
{
costAccountID = (int)pmag.GroupID;
}
}
// Find the Inventory Type
InventoryItem inventory = InvetoryInfo.Search<InventoryItem.inventoryID>(TaskExt.UsrInventoryID);
string invUOM = "";
if (inventory != null)
{
invUOM = inventory.BaseUnit;
}
// Task --> Revenue Budget
PMRevenueBudget revbudgetItem = Base.RevenueBudget.Search<PMRevenueBudget.projectTaskID>(thisTask.TaskID);
if (revbudgetItem != null)
{
revbudgetItem.AccountGroupID = revAccountID;
revbudgetItem.InventoryID = thiskExt.UsrInventoryID;
revbudgetItem.CuryUnitRate = thiskExt.UsrUnitPrice;
revbudgetItem.Qty = thiskExt.UsrQuantity;
revbudgetItem.CuryAmount = thiskExt.UsrSellPrice;
revbudgetItem.UOM = invUOM;
revbudgetItem.RevisedQty = revbudgetItem.Qty;
revbudgetItem.CuryRevisedAmount = revbudgetItem.CuryAmount;
// --> Base.RevenueBudget.Update(revbudgetItem); <-- This works to update the cahce, but as soon as the user hits save, it reverts the changes back to 0.
}
}
I have tried it in the row persisting and persisted events but then get the
"Another process has added/updated record"
error which reverts everything. How can I get the values over and save it to the table when I hit the Save button?
Any help would be greatly appreciated.
So, the best solution to this we found so far is to update the Graph in the row persisted.
protected void PMProject_RowPersisted(PXCache cache, PXRowPersistedEventArgs e)
{
var row = (PMProject)e.Row;
if (e.TranStatus == PXTranStatus.Completed)
{
var target = PXGraph.CreateInstance<ProjectTaskEntry>();
var target2 = PXGraph.CreateInstance<ProjectEntry>();
//var target = PXGraph.CreateInstance<ProjectEntry>();
foreach (PMTask taskitem in Base.Tasks.Select())
{
PMTaskExt TaskExt = taskitem.GetExtension<PMTaskExt>();
target.Task.Update(taskitem);
foreach (PMRevenueBudget revbudgetItem in Base.RevenueBudget.Search<PMRevenueBudget.projectTaskID>(taskitem.TaskID))
{
revbudgetItem.Qty = TaskExt.UsrQuantity;
target2.RevenueBudget.Update(revbudgetItem);
}
}
target2.Save.PressButton();
}
}

Cannot add together different variables, even though values have changed

So I'm REALLY new to coding, so I'm learning as I go. I'm doing an assignment for a course, where I have to create a party booking program for a company. The idea is simple, just selected some different options, add them up, and calculate the costs. I have labels that will change and display the integer that the variable is set to, and they work perfectly which would tell me that the variables themselves are indeed getting changed.
However, once I try to add the variables together, it doesn't work.
public partial class AdultPayment : Form
{
public static int ConvertNumberAdult = Convert.ToInt32(Adult.numberAdult);
public static int MealCost = Adult.mealPrice*ConvertNumberAdult;
private static int PrivateRoomCost;
public static int TotalCost = MealCost + CalculateWine + PrivateRoomCost;
public static string DisplayNumberAdult = Convert.ToString(Adult.numberAdult);
private static int CalculateWine { get; set; } = ConvertNumberAdult / 6 * 15;
public AdultPayment()
{
InitializeComponent();
lblAdultConfirmName.Text = Adult.adultContactName;
lblAdultConfirmNumber.Text = Adult.adultContactNumber;
lblAdultConfirmDate.Text = Adult.adultDate;
lblAdultConfirmTime.Text = Adult.adultTime;
lblConfirmNumberOfAdult.Text = Adult.numberAdult.ToString();
lblMealCostTotal.Text = "£"+ MealCost.ToString();
lblCostTotal.Text = "£" + TotalCost.ToString();
if (Adult.adultPrivateRoom == true)
{
PrivateRoomCost = 40;
lblAdultConfirmPrivateRoom.Text = "Yes";
lblPrivateRoomTotal.Text = "£"+ PrivateRoomCost;
}
else
{
PrivateRoomCost = 0;
lblAdultConfirmPrivateRoom.Text = "No";
lblPrivateRoomTotal.Text = "N/A";
}
if(Adult.menuOption == true)
{
lblConfirmMenu.Text = "2 Courses for £15 each";
}
if(Adult.menuOption == false)
{
lblConfirmMenu.Text = "3 Courses for £18 each";
}
if (Adult.addWine == false)
{
CalculateWine = 0;
lblConfirmWineAdded.Text = "No";
lblWineTotal.Text = "N/A";
}
else
{
lblConfirmWineAdded.Text = "Yes";
lblWineTotal.Text = "£"+ CalculateWine.ToString();
}
lblCostTotal.Text = "£" + TotalCost.ToString();
}
}
I have a different form, that allows the users to select their preferences, which works just as I need it to.
The variable that is supposed to add the other variables together is
public static int TotalCost = MealCost + CalculateWine + PrivateRoomCost;
I genuinely am so confused now, and because I'm so new to this I don't know how else to word it in order to find anything on the internet
Any help would be great!
Thanks!

Unable to convert Frame<int, string> to List<OHLCV>, so I can calculate indicators and loc its results

I'm trying to recreate python code which uses pandas (this one) in C# with Deedle. Basically, I'm struggling with two things.
I want to use the Rsi(this IReadOnlyList<OHLCV> source... overload while calculating my indicators. Right now I'm using List<decimal>.
How can I convert that loc into C#?
dataframe.loc[
(
qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) &
(dataframe['ha_close'] > dataframe['ema20']) &
(dataframe['ha_open'] < dataframe['ha_close']) # green bar
),
'buy'] = 1
The questioned method is called PopulateIndicators in my example below. I want to replace the foreach loop with something like the loc above as I mentioned above and make the code look a bit more like the python code (e.g. splitting it to three methods, etc.).
Fully testable code
using Binance.Net;
using Binance.Net.Enums;
using Deedle;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DeedleFrameTests
{
public class OHLCV
{
public DateTime Timestamp { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Volume { get; set; }
}
public static partial class IndicatorExtensions
{
private static IReadOnlyList<decimal?> FixIndicatorOrdering(IReadOnlyList<decimal> source, int outBegIdx, int outNbElement)
{
var outValues = new List<decimal?>();
var validItems = source.Take(outNbElement);
for (int i = 0; i < outBegIdx; i++)
outValues.Add(null);
foreach (var value in validItems)
outValues.Add(value);
return outValues;
}
public static IReadOnlyList<decimal?> Rsi(this IReadOnlyList<OHLCV> source, int period = 14)
{
var rsiValues = new decimal[source.Count];
var closes = source.Select(e => e.Close).ToArray();
var result = TALib.Core.Rsi(closes, 0, source.Count - 1, rsiValues, out int outBegIdx, out int outNbElement, period);
if (result == TALib.Core.RetCode.Success)
{
return FixIndicatorOrdering(rsiValues.ToList(), outBegIdx, outNbElement);
}
throw new Exception("Could not calculate RSI.");
}
public static IReadOnlyList<decimal?> Rsi(this List<decimal> source, int period = 14)
{
var rsiValues = new decimal[source.Count];
var result = TALib.Core.Rsi(source.ToArray(), 0, source.Count - 1, rsiValues, out int outBegIdx, out int outNbElement, period);
if (result == TALib.Core.RetCode.Success)
{
return FixIndicatorOrdering(rsiValues.ToList(), outBegIdx, outNbElement);
}
throw new Exception("Could not calculate RSI.");
}
}
public class TestStrategy
{
public enum TradeAdvice
{
Buy,
Sell,
Hold
}
public List<TradeAdvice> PopulateIndicatorsNoDeedle(List<OHLCV> candles)
{
var result = new List<TradeAdvice>();
var rsiPeriod = 2;
var rsi = candles.Rsi(rsiPeriod);
for (int i = 0; i < candles.Count; i++)
{
if (rsi[i] < 45 && rsi[i] > rsi[i - 1])
result.Add(TradeAdvice.Buy);
else if (rsi[i] > 70)
result.Add(TradeAdvice.Sell);
else
result.Add(TradeAdvice.Hold);
}
return result;
}
public List<TradeAdvice> PopulateIndicators(Frame<int, string> frame)
{
var closes = frame.GetColumn<decimal>("Close").Values.ToList();
var indicator = closes.Rsi(2);
frame.AddColumn("rsi", indicator);
var rsi = frame.GetColumn<decimal>("rsi").Realign(Enumerable.Range(0, closes.Count)).FillMissing(0m);
var rsiShifted = frame.GetColumn<decimal>("rsi").Shift(1).Realign(Enumerable.Range(0, closes.Count)).FillMissing(0m);
var result = new List<TradeAdvice>();
for (int i = 0; i < closes.Count; i++)
{
if (rsi[i] < 45 && rsi[i] > rsiShifted[i])
result.Add(TradeAdvice.Buy);
else if (rsi[i] > 70)
result.Add(TradeAdvice.Sell);
else
result.Add(TradeAdvice.Hold);
}
return result;
}
}
public class Program
{
public static void Main(string[] args)
{
var candles = new BinanceClient().Spot.Market.GetKlines("TRXUSDT", KlineInterval.OneMinute).Data?
.Select(x => new OHLCV
{
Timestamp = x.OpenTime,
Open = x.Open,
High = x.High,
Close = x.Close,
Low = x.Low,
Volume = x.BaseVolume
}).ToList();
var frame = Frame.FromRecords(candles);
var strategy = new TestStrategy();
var advices = strategy.PopulateIndicatorsNoDeedle(candles);
var advices2 = strategy.PopulateIndicators(frame);
var areEqual = Enumerable.SequenceEqual(advices, advices2);
Console.ReadLine();
}
}
}
Edit
I got working the first part of the question only (closes from List<decimal> to List<OHLCV>). This is the question that helped me Convert a Deedle Dataframe into a C# List of Custom Class.
The rest of the question remains still unsolved.
public List<TradeAdvice> PopulateIndicators(Frame<int, string> df)
{
var closes = df.Rows.Select(row =>
new OHLCV
{
Timestamp = row.Value.GetAs<DateTime>("Timestamp"),
Open = row.Value.GetAs<decimal>("Open"),
High = row.Value.GetAs<decimal>("High"),
Low = row.Value.GetAs<decimal>("Low"),
Close = row.Value.GetAs<decimal>("Close"),
Volume = row.Value.GetAs<decimal>("Volume")
}).Observations.Select(e => e.Value).ToList();
//var closes = df.GetColumn<decimal>("Close").Values.ToList();
var indicator = closes.Rsi(2);
df.AddColumn("rsi", indicator);
var rsi = df.GetColumn<decimal>("rsi").Realign(Enumerable.Range(0, closes.Count)).FillMissing(0m);
var rsiShifted = df.GetColumn<decimal>("rsi").Shift(1).Realign(Enumerable.Range(0, closes.Count)).FillMissing(0m);
var result = new List<TradeAdvice>();
for (int i = 0; i < closes.Count; i++)
{
if (rsi[i] < 45 && rsi[i] > rsiShifted[i])
result.Add(TradeAdvice.Buy);
else if (rsi[i] > 70)
result.Add(TradeAdvice.Sell);
else
result.Add(TradeAdvice.Hold);
}
return result;
}

EP Plus - Error Table range collides with table

I am building an export to excel functionality using EP plus and c# application. I am currently getting the error.
'Table range collides with table tblAllocations29'
In my code logic below, I am looping through a data structure that contains key and collection as a value.
I looping across each key and once again loop through each collection belonging to that key.
I basically need to print tabular information for each collection along with its totals.
In the current scenario, I am getting the error when it is trying to print
three arrays
The first array has 17 records
The second array has 29 records
The third array has 6 records
I have taken a note of the ranges it is creating while debugging
The ranges are
A1 G18
A20 G50
A51 G58
controller
[HttpGet]
[SkipTokenAuthorization]
public HttpResponseMessage DownloadFundAllocationDetails(int id, DateTime date)
{
var ms = GetStrategy(id);
DateTime d = new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1);
if (ms.FIRM_ID != null)
{
var firm = GetService<FIRM>().Get(ms.FIRM_ID.Value);
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationsGroup = null;
var allocationsGrouped = GetAllocationsGrouped(EntityType.Firm, firm.ID, d);
string fileName = string.Format("{0} as of {1}.xlsx", "test", date.ToString("MMM, yyyy"));
byte[] fileContents;
var newFile = new FileInfo(fileName);
using (var package = new OfficeOpenXml.ExcelPackage(newFile))
{
FundAllocationsPrinter.Print(package, allocationsGrouped);
fileContents = package.GetAsByteArray();
}
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(fileContents)
};
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return result;
}
return null;
#endregion
}
I have written the following utility that will try and export. It works sometimes when there are two array collections and it failed when processing three. Could somebody tell me what the problems are
FundsAllocationsPrinter.cs
public class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
foreach (var ag in allocation)
{
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + startposition + ":G" + (count + previouscount + 1)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
table.TableStyle = TableStyles.Dark10;
}
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + position].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + position].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + position].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + position].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + position].Value = ac.UsdEmv;
wsSheet1.Cells["F" + position].Value = Math.Round(ac.GroupPercent,2);
wsSheet1.Cells["G" + position].Value = Math.Round(ac.WEIGHT_WITH_EQ,2);
position++;
}
position++;
previouscount = position;
// position = position + 1;
startposition = position;
position++;
}
}
}
This is how the data looks when it is displayed successfully
Your issue is entirely in your Print method. You've been bitten by creating a slightly over-complicated row tracking mechanism and combining that with magic numbers. This causes you to position each table after the first one row higher than it should be. The header and subtotals are not part of the table, so you have a couple rows of leeway for the error. Tables can't overlap as you've seen, so EPPlus starts barking at you after you've exhausted your leeway.
All you need to do is keep track of the current row that you are writing to, and account for the space taken by your table header and footer (the subtotals) if you use them.
You declare these:
int count = 0;
int previouscount = 0;
var position = 2;
int startposition = 1;
But to write to the correct row, all you need is this:
var rowNumber = 1;
This will properly start writing your data in row one of the Excel sheet. As you write your table rows, you'll track and increment only the rowNumber. But what about the header and footer of each table? If you start writing at the first row of your table you'll overwrite the header, and if you don't account for both the header and footer you'll start having collisions like you've seen. So lets do this:
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
These are pretty self explanatory, you'll use the rowAdders to hop the header or footer when needed. rowNumber will always be your current row to create your table and write your data. You use the count when defining your table, but we've made it irrelevant for anything else, so we move it:
var allocationList = allocationGroup.ToList();
//Moved here
var count = allocationList.Count();
Your using statement becomes:
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
Next, it isn't mentioned in your post, but you are going to run into a problem with the following:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + count);
Your table names have to be unique, but you could very well wind up with multiple allocations having the same count, which will cause EPPlus to throw an exception at you for duplicating a table name. So you'll want to also track the index of your current table:
var rowNumber = 1;
var tableIndex = 0;
//...
foreach (var ag in allocation)
{
tableIndex += 1;
//...
}
And use it to ensure unique table names:
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
We use our format control variables:
// table.ShowHeader = true;
table.ShowFilter = true;
table.ShowTotal = true;
//Changes to
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
You have a small typo here:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,Allocations])";
//Should be:
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109,[Allocations])";
After you are done defining your table, you begin writing your data with a foreach loop. In order to prevent overwriting the table header if it exists, we'll have to advance one row. We also have to advance one row for each FIRMWIDE_MANAGER_ALLOCATION. If you are using the subtotals, we have to advance one row after the loop completes in order to properly position the next table:
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
//...
rowNumber += 1;
}
rowNumber += rowAdderForFooter;
And that's it. We now properly track our position using just one variable, and we modify the position as necessary if there is a header or footer on your table.
The following is a complete working example that can be run in LinqPad as long as you add the EPPlus package through Nuget. It creates a random number of allocation groups each with a random number of allocations, and then exports them. Change the output file path to something that works for you:
void Main()
{
var dataGenerator = new DataGenerator();
var allocations = dataGenerator.Generate();
var xlFile = new FileInfo(#"d:\so-test.xlsx");
if (xlFile.Exists)
{
xlFile.Delete();
}
using(var xl = new ExcelPackage(xlFile))
{
FundAllocationsPrinter.Print(xl, allocations);
xl.Save();
}
}
// Define other methods and classes here
public static class FundAllocationsPrinter
{
public static void Print(ExcelPackage package, ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> allocation)
{
ExcelWorksheet wsSheet1 = package.Workbook.Worksheets.Add("Sheet1");
wsSheet1.Protection.IsProtected = false;
IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> allocationGroup = null;
var rowNumber = 1;
int tableIndex = 0;
var showFilter = true;
var showHeader = true;
var showTotals = true;
var rowAdderForHeader = Convert.ToInt32(showHeader);
var rowAdderForFooter = Convert.ToInt32(showTotals);
foreach (var ag in allocation)
{
tableIndex += 1;
Console.WriteLine(tableIndex);
allocationGroup = ag.Select(a => a);
var allocationList = allocationGroup.ToList();
var count = allocationList.Count();
using (ExcelRange Rng = wsSheet1.Cells["A" + rowNumber + ":G" + (count + rowNumber)])
{
ExcelTableCollection tblcollection = wsSheet1.Tables;
ExcelTable table = tblcollection.Add(Rng, "tblAllocations" + tableIndex);
//Set Columns position & name
table.Columns[0].Name = "Manager Strategy";
table.Columns[1].Name = "Fund";
table.Columns[2].Name = "Portfolio";
table.Columns[3].Name = "As Of";
table.Columns[4].Name = "EMV (USD)";
table.Columns[5].Name = "Percent";
table.Columns[6].Name = "Allocations";
wsSheet1.Column(1).Width = 45;
wsSheet1.Column(2).Width = 45;
wsSheet1.Column(3).Width = 55;
wsSheet1.Column(4).Width = 15;
wsSheet1.Column(5).Width = 25;
wsSheet1.Column(6).Width = 20;
wsSheet1.Column(7).Width = 20;
table.ShowHeader = showHeader;
table.ShowFilter = showFilter;
table.ShowTotal = showTotals;
//Add TotalsRowFormula into Excel table Columns
table.Columns[0].TotalsRowLabel = "Total Rows";
table.Columns[4].TotalsRowFormula = "SUBTOTAL(109,[EMV (USD)])";
table.Columns[5].TotalsRowFormula = "SUBTOTAL(109,[Percent])";
table.Columns[6].TotalsRowFormula = "SUBTOTAL(109, [Allocations])";
table.TableStyle = TableStyles.Dark10;
}
//Account for the table header
rowNumber += rowAdderForHeader;
foreach (var ac in allocationGroup)
{
wsSheet1.Cells["A" + rowNumber].Value = ac.MANAGER_STRATEGY_NAME;
wsSheet1.Cells["B" + rowNumber].Value = ac.MANAGER_FUND_NAME;
wsSheet1.Cells["C" + rowNumber].Value = ac.PRODUCT_NAME;
wsSheet1.Cells["D" + rowNumber].Value = ac.EVAL_DATE.ToString("dd MMM, yyyy");
wsSheet1.Cells["E" + rowNumber].Value = ac.UsdEmv;
wsSheet1.Cells["F" + rowNumber].Value = Math.Round(ac.GroupPercent, 2);
wsSheet1.Cells["G" + rowNumber].Value = Math.Round(ac.WEIGHT_WITH_EQ, 2);
rowNumber++;
}
//Account for the table footer
rowNumber += rowAdderForFooter;
}
}
}
public class FIRMWIDE_MANAGER_ALLOCATION
{
public FIRMWIDE_MANAGER_ALLOCATION(string name, Random rnd)
{
Name = name;
MANAGER_STRATEGY_NAME = "strategy name";
MANAGER_FUND_NAME = "fund name";
PRODUCT_NAME = "product name";
EVAL_DATE = DateTime.Now;
UsdEmv = (decimal)rnd.NextDouble() * 100000000;
GroupPercent = (decimal)rnd.NextDouble() * 100;
WEIGHT_WITH_EQ = 0;
}
public string Name { get; set; }
public string MANAGER_STRATEGY_NAME { get; set; }
public string MANAGER_FUND_NAME { get; set; }
public string PRODUCT_NAME { get; set; }
public DateTime EVAL_DATE { get; set; }
public decimal UsdEmv { get; set; }
public decimal GroupPercent { get; set; }
public decimal WEIGHT_WITH_EQ { get; set; }
}
public class DataGenerator
{
public static Random rnd = new Random();
public ILookup<string, FIRMWIDE_MANAGER_ALLOCATION> Generate()
{
var data = new List<FIRMWIDE_MANAGER_ALLOCATION>();
var itemCount = rnd.Next(1, 100);
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
var name = Path.GetRandomFileName();
data.AddRange(GenerateItems(name));
}
return data.ToLookup(d => d.Name, d => d);
}
private IEnumerable<FIRMWIDE_MANAGER_ALLOCATION> GenerateItems(string name)
{
var itemCount = rnd.Next(1,100);
var items = new List<FIRMWIDE_MANAGER_ALLOCATION>();
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
items.Add(new FIRMWIDE_MANAGER_ALLOCATION(name, rnd));
}
return items;
}
}

Print elements from a list in ascending order(date) whilst splitting the line by each day- C#

I am trying to print the following in ascending order of the date :
static void Main(string[] args)
{
string meter_id = "08002220";
string calc_constant = "0.1";
string interval = "00000100";
List<DateTime> readingDate = new List<DateTime>();
List<float> volume = new List<float>();
List<float> odometer = new List<float>();
var get_timestamp = DateTime.Now.ToString("yyyyMMddHHmm");
try
{
TextReader textReader = File.OpenText(#"C:\Users\umar\Documents\data format\test.csv");
var csv = new CsvReader(textReader);
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
readingDate.Add(DateTime.Parse(csv["Reading Date"]));
volume.Add(float.Parse(csv["Total Volume"]) / 1000);
odometer.Add(float.Parse(csv["Odometer"]) / 1000);
}
readingDate.Sort();
var printCMREG = readingDate.Zip(odometer, (first, second) => new { first, second });
var printCM = readingDate.Zip(volume, (first, second) => new { first, second });
Console.Write($" MEPMD01, 20080501, EDDYIQ, INSWT:053000,,,{get_timestamp},,OK,W,CMREG,{calc_constant},{interval},");
foreach (var print in printCMREG)
{
if (print.first.Hour == 0)
{
Console.Write($"{print.first.ToString("yyyyMMddHHmm")},R0,{print.second},");
}
}
Console.WriteLine("\n");
Console.Write($" MEPMD01, 20080501, EDDYIQ, INSWT:053000,,,{get_timestamp},,OK,W,CM,{calc_constant},{interval},");
foreach (var print in printCM)
{
Console.Write($"{print.first.ToString("yyyyMMddHHmm")},R0,{print.second},");
}
}
catch(System.IO.IOException e)
{
Console.WriteLine(e);
}
}
I have written the above code, which prints the date and time equivalent of a meter reading. What I need at the moment is the ability to sort the dates at the bottom in 24 hour format.
Console.Write($" MEPMD01, 20080501, EDDYIQ, INSWT:053000,,,{get_timestamp},,OK,W,CM,{calc_constant},{interval},");
foreach (var print in printCM)
{
Console.Write($"{print.first.ToString("yyyyMMddHHmm")},R0,{print.second},");
}
As can be seen in this line, this prints all the dates together, however, I want to split it up in 24 intervals.
Replace this loop:
foreach (var print in printCM)
{
Console.Write($"print.first.ToString("yyyyMMddHHmm")},R0,print.second},");
}
With this:
DateTime currentDay = null;
foreach (var print in printCM)
{
if(currentDay == null)
{
currentDay = print.first.Date;
}
else if(currentDay != print.first.Date)
{
Console.WriteLine();
currentDay = print.first.Date;
}
Console.Write($"{print.first.ToString("yyyyMMddHHmm")},R0,{print.second},");
}
It will see if the prints are on the same date. If not it will insert a newline and then update the currentDay variable.
This assumes your sort used earlier in your code is in fact sorting the list.
It would be better if you created a class and updated the variables in there, something like this, than you could just run a foreach each loop on the print them out.
public class CSVData
{
public DateTime ReadingDate { get; set; }
public float Volume { get; set; }
public float Odometer { get; set; }
}

Categories

Resources