Java sorting by spread between two BigDecimal - java

Please help with sorting in Java. I have a simple example (looks like this).
I need to sort list by the difference between two BigDecimals.
My Data class (I cut getters)
public class Quotation {
private final long id;
private final BigDecimal askPrice;
private final BigDecimal bidPrice;
public Quotation(long id, BigDecimal bidPrice, BigDecimal askPrice) {
this.id = id;
this.askPrice = askPrice;
this.bidPrice = bidPrice;
}
}
Here we store our records.
storeQuotation(new Quotation(1000,new BigDecimal("99"), new BigDecimal("104")));
storeQuotation(new Quotation(1001,new BigDecimal("69"), new BigDecimal("72")));
storeQuotation(new Quotation(1002,new BigDecimal("65"), new BigDecimal("69")));
storeQuotation(new Quotation(1003,new BigDecimal("70"), new BigDecimal("71")));
storeQuotation(new Quotation(1004,new BigDecimal("71"), new BigDecimal("73")));
storeQuotation(new Quotation(1005,new BigDecimal("90"), new BigDecimal("95")));
storeQuotation(new Quotation(1006,new BigDecimal("92"), new BigDecimal("93")));
storeQuotation(new Quotation(1007,new BigDecimal("94"), new BigDecimal("98")));
storeQuotation(new Quotation(1008,new BigDecimal("90"), new BigDecimal("92")));
storeQuotation(new Quotation(1009,new BigDecimal("92"), new BigDecimal("95")));
out - Not Sorted
id - 1000. Bid - 99. Ask - 104. Spread = 5
id - 1001. Bid - 69. Ask - 72. Spread = 3
id - 1002. Bid - 65. Ask - 69. Spread = 4
id - 1003. Bid - 70. Ask - 71. Spread = 1
id - 1004. Bid - 71. Ask - 73. Spread = 2
id - 1005. Bid - 90. Ask - 95. Spread = 5
id - 1006. Bid - 92. Ask - 93. Spread = 1
id - 1007. Bid - 94. Ask - 98. Spread = 4
id - 1008. Bid - 90. Ask - 92. Spread = 2
id - 1009. Bid - 92. Ask - 95. Spread = 3
And I just need to sort this list by the difference between bidPrice and askPrice.
I tried this method...
public static List<Quotation> getSpreadsList(boolean decreasing) {
List<Quotation> sortedBySpread = QuotationsStoreImpl.quotationList;
Collections.sort(sortedBySpread, (a, b) ->
(a.getBidPrice().intValue() - b.getAskPrice().intValue()));
// sortedBySpread.sort((a, b) ->
// (a.getBidPrice().intValue() - b.getAskPrice().intValue()));
if (decreasing) {
Collections.reverse(sortedBySpread);
}
return sortedBySpread;
}
}
But without success...
out - Sorted
id - 1002. Bid - 65. Ask - 69. Spread = 4
id - 1003. Bid - 70. Ask - 71. Spread = 1
id - 1004. Bid - 71. Ask - 73. Spread = 2
id - 1001. Bid - 69. Ask - 72. Spread = 3
id - 1008. Bid - 90. Ask - 92. Spread = 2
id - 1009. Bid - 92. Ask - 95. Spread = 3
id - 1006. Bid - 92. Ask - 93. Spread = 1
id - 1007. Bid - 94. Ask - 98. Spread = 4
id - 1005. Bid - 90. Ask - 95. Spread = 5
id - 1000. Bid - 99. Ask - 104. Spread = 5
The list is mixed but not sorted according to my criteria !
Spread not sorted !
How can I sort this list correct, by spread ?
I don't have much experience in java.
And all my attempts have come to nothing.

Collections.sort(sortedBySpread, (a, b) -> (a.getBidPrice().intValue() - b.getAskPrice().intValue())); does some math that makes little since since it subtracts the ask from the bid of two different quotes.
Instead you should calculate the spread of a and then subtract the spread of b:
Collections.sort(sortedBySpread, (a, b) -> (a.getBidPrice().intValue() - a.getAskPrice().intValue()) - (b.getBidPrice().intValue() - b.getAskPrice().intValue()));
Generally this could be expanded into the following to make it more clear what is going on:
Collections.sort(sortedBySpread, (Quotation a, Quotation b) -> {
int spreadA = a.getBidPrice().intValue() - a.getAskPrice().intValue();
int spreadB = b.getBidPrice().intValue() - b.getAskPrice().intValue();
return spreadA - spreadB;
});
But starting with the first snippet IntelliJ suggest the arguably far cleaner solution
Collections.sort(sortedBySpread, Comparator.comparingInt(a -> (a.getBidPrice().intValue() - a.getAskPrice().intValue())));
And going from there it might make sense to have a getSpread on Quotation:
public int getSpread() {
return bidPrice.intValue() - askPrice.intValue();
}
which would then allow
Collections.sort(sortedBySpread, Comparator.comparingInt(Quotation::getSpread));
And finally
sortedBySpread.sort(Comparator.comparingInt(Quotation::getSpread));
without the need for Collections.sort.

For demo, I put your values in a list. I also added the following to your class.
public BigDecimal getSpread() {
return askPrice.subtract(bidPrice);
}
public String toString() {
return "id - %d bid - %6.2f ask - %6.2f spread - %6.2f".formatted(id, bidPrice, askPrice,getSpread());
}
The data
List<Quotation> quotes = new ArrayList<>(List.of(
(new Quotation(1000,new BigDecimal("99"), new BigDecimal("104"))),
(new Quotation(1001,new BigDecimal("69"), new BigDecimal("72"))),
(new Quotation(1002,new BigDecimal("65"), new BigDecimal("69"))),
(new Quotation(1003,new BigDecimal("70"), new BigDecimal("71"))),
(new Quotation(1004,new BigDecimal("71"), new BigDecimal("73"))),
(new Quotation(1005,new BigDecimal("90"), new BigDecimal("95"))),
(new Quotation(1006,new BigDecimal("92"), new BigDecimal("93"))),
(new Quotation(1007,new BigDecimal("94"), new BigDecimal("98"))),
(new Quotation(1008,new BigDecimal("90"), new BigDecimal("92"))),
(new Quotation(1009,new BigDecimal("92"), new BigDecimal("95")))));
The sorting part is simple. Just use a comparator, referencing the spread.
quotes.sort(Comparator.comparing(Quotation::getSpread));
And print
for(Quotation q : quotes) {
System.out.println(q);
}
Prints
id - 1003 bid - 70.00 ask - 71.00 spread - 1.00
id - 1006 bid - 92.00 ask - 93.00 spread - 1.00
id - 1004 bid - 71.00 ask - 73.00 spread - 2.00
id - 1008 bid - 90.00 ask - 92.00 spread - 2.00
id - 1001 bid - 69.00 ask - 72.00 spread - 3.00
id - 1009 bid - 92.00 ask - 95.00 spread - 3.00
id - 1002 bid - 65.00 ask - 69.00 spread - 4.00
id - 1007 bid - 94.00 ask - 98.00 spread - 4.00
id - 1000 bid - 99.00 ask - 104.00 spread - 5.00
id - 1005 bid - 90.00 ask - 95.00 spread - 5.00

Related

How to solve this logically

I have set and a sub-set for each id. i need to accumulate the total
ex: employeeIdSet is the outer set which has all the employeeIds
Now each employee - may be combined or not combined and they will be added credits
empa - credit 10
empb linked with empc, empd - credit would be 15, overall for the 3 employees.
similalrly
empe linked with empz, emps - credit would be 7, over all for the 3 employees and linked with empq where the credit is 9
similarly
empr linked with empo - credit would be 6, overall for the 2 employees
Now i want to have a list of employee id with respective credits
ex:
empa-10
emp-15,
empc-15,
empd-15,
empe - 7+9,
empz - 7+9,
emps- 7+9,
empr - 6,
empo - 6
the problem we get employee id in the outer loop and inner loop we can get the subsequent employees. however all addition leads to problem
code
final Set<Long> combinedEmployeeIdSet = new HashSet<>();
final Set<CombinedEmployee> combinedEmployees = employee.getCombinedEmployees();
for(final CombinedEmployee combinedEmployee: combinedEmployees) {
combinedEmployeeIdSet.add(combinedEmployee.getId());
}
for(final OtherEmployee otherEmployee: otherEmployees) {
if(!combinedEmployeeIdSet.contains(otherEmployee.getId())) {
employeeCredit += otherEmployee.getCredit();
}
}
expectation is get the total credits of the given employeeId where when there under same group, it should be added as one single unit, else the credit should be added
empe - 7+9, displays 15
empz - 7+9, displays 15
emps- 7+9, displays 15
thanks
Very confused by your description.
Do you mean you have some "emp"s, say: emp-a,emp-b ... emp-x, and each emp have a credit, say: emp-a:10, emp-b:5, emp-c:7... emp-x:6. Some emps have links with other emps, say: emp-a (emp-b, empc). Now you want to get the credit for each emp, if the emp has links, its credit should be a sumarize of itself and all its links.
So you may get
emp-a 10+5+7
emp-b 5
emp-c 7
...
emp-x 6

need ideas for the minRow method

I have java worksheet, which uses arraylist. It is making a excel sheet. There are functions like total, count, getters and setters which i could solve it easily but I cannot seem to do the min method below, the requirements are as followed
The data looks something like this:
A(0) B(1) C(2) D(3) E(4) F(5)
0 - - - - - -
1 - - 8.8 - 0.0 -0.1
2 - - - -6.5 - -
3 - - - - - -
4 - - - 1.8 -1.4 -
5 - - - - - -
6 0 1.9 - - - -
---------------------------
value of the first row for which there is a EntryData object
in the list data
return null if there is no data
public Integer minRow() {
return null; //to be completed
}
The test data is here to check if the function works:
public void testMinRow() {
assertNotNull(sheet.minRow());
assertTrue(sheet.minRow()==1);
assertNull(empty.minRow());
}
I got this far, but only the first test works here:
public Integer minRow() {
int firstRow = 0;
for(DataEntry item: data) {
if(item.getRow() > firstRow)
firstRow = item.getRow();
}
return firstRow; //to be completed
}
Why not using foreach to go throug the single lines, than save the value of the line and compare to the next value in this line and so on. at every compare, only save the smaller (min) value and at the end print this value out

retrieve histogram from mssql table using java

I want to implement java application that can connect to any sql server and load any table from it. For each table I want to create histogram based on some arbitrary columns.
For example if I have this table
name profit
------------
name1 12
name2 14
name3 18
name4 13
I can create histogram with bin size 4 based on min and max value of profit column and count number of records for each bin.
result is:
profit count
---------------
12-16 3
16-20 1
My solution for this problem is retrieving all the data based on required columns and after that construct the bins and group by the records using java stream Collectors.groupingBy.
I'm not sure if my solution is optimized and for this I want some help to find the better algorithm specially when I have big number of records.(for example use some benefits of sql server or other frameworks that can be used.)
Can I use better algorithm for this issue?
edit 1:
assume my sql result is in List data
private String mySimpleHash(Object[] row, int index) {
StringBuilder hash = new StringBuilder();
for (int i = 0; i < row.length; i++)
if (i != index)
hash.append(row[i]).append(":");
return hash.toString();
}
//index is index of column for histogram
List<Object[]> histogramData = new ArrayList<>();
final Map<String, List<Object[]>> map = data.stream().collect(
Collectors.groupingBy(row -> mySimpleHash(Arrays.copyOfRange(row, index))));
for (final Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
Object[] newRow = newData.get(rowNum);
double result = entry.getValue().stream()
.mapToDouble(row ->
Double.valueOf(row[index].toString())).count();
newRow[index] = result;
histogramData.add(newRow);
}
As you have considered, performing the aggregation after getting all the data out of SQL server is going to be very expensive if the number of rows in your tables increase. You can simply do the aggregation within SQL. Depending on how you are expressing your histogram bins, this is either trivial or requires some work. In your case, the requirement that the lowest bin start at min value requires a little bit of setup as opposed to binning starting from 0. See sample below. The inner query is mapping values to a bin number, the outer query is aggregating and computing the bin boundaries.
CREATE TABLE Test (
Name varchar(max) NOT NULL,
Profit int NOT NULL
)
INSERT Test(Name, Profit)
VALUES
('name1', 12),
('name2', 14),
('name3', 18),
('name4', 13)
DECLARE #minValue int = (SELECT MIN(Profit) FROM Test)
DECLARE #binSize int = 4
SELECT
(#minValue + #binSize * Bin) AS BinLow,
(#minValue + #binSize * Bin) + #binSize - 1 AS BinHigh,
COUNT(*) AS Count
FROM (
SELECT
((Profit - #minValue) / #binSize) AS Bin
FROM
Test
) AS t
GROUP BY Bin
| BinLow | BinHigh | Count |
|--------|---------|-------|
| 12 | 15 | 3 |
| 16 | 19 | 1 |
http://sqlfiddle.com/#!18/d093c/9

Division of teams on the basis of skill point

I am trying to put n players having different skill point(ranging from 100-3000) into r teams such that overall skill in each team is as close as possible to every other team.
I first sorted the players in descending order of skill points and top r players were put into each team. Now the team with lowest skill point(iterating and calculating sum) gets the top player remaining.
For eg.
A 600
B 550
C 400
D 250
E 220
F 200
G 150
H 140
For 2 teams, result will be:
Team A{600,250,220,150}= 1220
Team B{550,400,200,140}= 1290
In another approach each team gets a player from top and a player from bottom.
Team A{600,140,400,200}=1340
Team B{550,150,250,220}=1170
So here 1st approach was better, but for different set of data sometimes approach 2 is optimum and sometimes approach 1 is optimum.
Is there any specific algorithm to do this? I tried to read Microsoft's TrueSkill algorithm, but it was way too complex.
Looks like you want to score each combination of players. I'm going to cheat and use python here:
from itertools import combinations
players = [600, 550, 400, 250, 220, 150, 140]
scores = {}
for i in range(1, int(len(players)/2)):
for c in combinations(players, i):
scores[c] = abs(sum(c) - sum([p for p in players if p not in c]))
print sorted(scores.items(), key=lambda x: x[1])[0]
prints: ((600, 550), 10)
Edit: Didn't recognize this as a hard problem right away.
Like mcdowella mentioned in a comment, this problem, as stated, is np-hard. A classic approach would be Integer Programming.
The following implementation uses the Julia programming language and the JuMP library as modelling tool. The Integer/Mixed-Integer-Programming solver used is cbc, but a commercial solver as Gurobi may used too (only 2 lines of code-change needed!). Besides Gurobi, all the mentioned tools are open-source!
Code
using JuMP
using Cbc
# PARAMS
N_PLAYERS = 15
N_TEAMS = 3
LOWER_SKILL = 100
UPPER_SKILL = 3000
# RANDOM INSTANCE
SKILL = Int[]
for p = 1:N_PLAYERS
push!(SKILL, rand(LOWER_SKILL:UPPER_SKILL))
end
# MODEL
m = Model(solver=CbcSolver())
bigM = sum(SKILL)^2 # more tight bound possible
# VARS
#defVar(m, x[1:N_PLAYERS, 1:N_TEAMS], Bin) # player-team assignment vars
#defVar(m, 0 <= tsum_pos[1:N_TEAMS,1:N_TEAMS] <= bigM) # abs-linearization: pos-part
#defVar(m, 0 <= tsum_neg[1:N_TEAMS,1:N_TEAMS] <= bigM) # abs-linearization: neg-part
# CONSTRAINTS
# each player is assigned to exactly one team
for p = 1:N_PLAYERS
#addConstraint(m, sum{x[p,t], t=1:N_TEAMS} == 1)
end
# temporary team sum expresions
team_sums = AffExpr[]
for t = 1:N_TEAMS
#defExpr(y, SKILL[p] * sum{x[p,t], p=1:N_PLAYERS})
push!(team_sums, y)
end
# errors <-> splitted abs-vars equality
for t1 = 1:N_TEAMS
for t2 = 1:N_TEAMS
if t1 != t2
#addConstraint(m, (team_sums[t1] - team_sums[t2]) == (tsum_pos[t1,t2] - tsum_neg[t1,t2]))
end
end
end
# objective
#setObjective(m, Min, sum{tsum_pos[i,j] + tsum_neg[i,j], i=1:N_TEAMS, j=1:N_TEAMS}) # symmetry could be used
# SOLVE
tic()
status = solve(m)
toc()
# OUTPUT
println("Objective is: ", getObjectiveValue(m))
println("Solution: ")
println("Player skills: ", SKILL)
for p = 1:N_PLAYERS
for t = 1:N_TEAMS
if getValue(x[p,t]) > 0.5
println("player ", p, " in team ", t)
end
end
end
for t=1:N_TEAMS
sum_ = 0
for p=1:N_PLAYERS
if getValue(x[p,t]) > 0.5
sum_ += SKILL[p]
end
end
println("team: ", t, " -> ", sum_)
end
println(sum(SKILL))
This modelling uses some linearization-trick to model absolute values, as needed for a L1-norm-based error like you described in your post!
Output
elapsed time: 9.785739578 seconds
Objective is: 28.00000000000063 # REMARK: error is doubled because of symmetries which could be changed
Solution:
Player skills: [2919,1859,1183,1128,495,1436,2215,2045,651,540,2924,2367,1176,334,1300]
player 1 in team 3
player 2 in team 1
player 3 in team 3
player 4 in team 1
player 5 in team 3
player 6 in team 2
player 7 in team 2
player 8 in team 1
player 9 in team 1
player 10 in team 1
player 11 in team 3
player 12 in team 2
player 13 in team 2
player 14 in team 2
player 15 in team 1
team: 1 -> 7523
team: 2 -> 7528
team: 3 -> 7521
22572

FIFO Cost Calculation

Namaskaram,
I am developing a Retail POS application in Java Swing with H2 database. For calculating the cost, I need help.
FIFO Cost Calculation
I have the following tables
Purchase Table
Sale Table
FIFOCost Table
My requirement is to calculate Average Cost of each sold Item, into the FIFOCost Table.
Values to insert into FIFOCost table can be done, programmatically, whenever an insert happens in Sale Table. But, when any updates(like editing the Item or its Qty) happen in both Purchase and Sale table after few weeks or months (which requires FIFOCost Table, also to be updated, with huge volume of datas)
Currently, I am accomplishing through program itself. I am looking for a solution, where little programming and more work in database itself, using advanced tools like recursive.
You will need to add a QtySold column to your purchase table, and wade through the following algorithm on each sale:
qty_left = sale_qty
tot_cost = 0;
while qty_left > 0
fetch oldest row where qty_sold < qty
if qty_left + qty_sold < qty
then
set qty_sold = qty_sold + qty_left
tot_cost = tot_cost + (Purchase.Cost * qty_left)
qty_left = 0
else
qty_at_this_cost = qty - qty_sold
set qty_sold = qty
tot_cost = tot_cost + (Purchase.cost * qty_at_this_cost)
qty_left = qty_left - qty_at_this_cost
avg_cost = tot_cost / sale_qty
While it might be possible to do this with recursive SQL it would be neither efficient or readable.
You could possibly persuade your user to use the much more sensible and easier to calculate moving average cost.
i.e. everytime you purchase new stock you calculate
MVC = ((Current Stock * MVC) + (New Stock * New Cost)) / (Current Stock + New Stock)
This is similar but it averages out costs over all purchases.
You need to somewhere be keeping track of inventory on hand. When a sale occurs, you are going to do several things: Look at the number of items sold vs the amount of inventory on hand. For example, lets look at the second sale, 487. You sold 10 apples # $16.00. The first part of your function would query where the Item = Apple and OnHand >= 0. It would first come to item 101, which would have an OH of 5. Calculate the cost of goods sold using those 5 items at 11.00 each and update the inventory on hand to 0. You then again would have to query looking for Item = Apple, and onHand >= 0 and then use 5 from Item 195, adding that cost of goods sold to the number you calculated in the first part, and update the inventory on hand for that item.

Categories

Resources