如何遍历列表并匹配事务
How to iterate through a list and match transactions
我正在做一个项目,通过使用我的经纪人生成的 CSV 文件,将我的交易直接上传到我正在构建的应用程序,而不必在日志中手动输入交易或支付交易费用。
我的问题是数据表示为交易而不是交易,因此我必须匹配交易 (Buys/sells) 并从中创建另一个 object。我想创建一个“交易”Object 的原因是将它们的列表存储在数据库中并将这些 object 传递给其他方法来计算东西。
这是来自我的经纪人的数据:
这是 CSV 文件的 Header:
账户,T/D,S/D,货币,类型,边,符号,数量,价格,执行时间,Comm,SEC,TAF,NSCC,Nasdaq,ECN删除,ECN 添加,总收益,净收益,Clr Broker,Liq,Note
包含多个交易示例的CSV文件示例数据:
FAKEACCOUNT,12/22/2020,12/23/2020,USD,2,B,MSFT201224P00222500,1,0.77,09:50:45,0.59,0,0,0.033, 0.09,0,0,-77,-77.713,LAMP,
假账户,12/23/2020,12/24/2020,USD,2,S,MSFT201224P00222500,7,1.3,09:47:32,4.13,0.03,0.01,0.033,0.63,0,0,910,905.167,VOLANT,
假账户,12/24/2020,12/29/2020,USD,2,B,COCP,450,1.7,07:31:58,2.25,0,0,0.033,0.007065,0,0,-765,- 767.290065,LAMP,e,
假账户,12/24/2020,12/29/2020,USD,2,B,COCP,75,1.65,08:08:06,0.99,0,0,0.033,0.0011775,0,0,-123.75,- 124.7741775,LAMP,X,
假账户,12/24/2020,12/29/2020,USD,2,B,COCP,15,1.63,09:29:23,0.99,0,0,0.033,0.0002355,0,0,-24.45,- 25.4732355,LAMP,
假账户,12/28/2020,12/30/2020,USD,2,S,COCP,540,1.4709,10:30:36,2.7,0.02,0.07,0.033,0.008478,0,0,794.286,791.454522,MNGD, ,
假账户,12/29/2020,12/30/2020,USD,2,B,PYPL210108P00235000,1,5.35,09:34:21,0.59,0,0,0.033,0.09,0,0,-535,- 535.713,飞翔者,
假账户,12/29/2020,12/30/2020,USD,2,S,PYPL210108P00235000,1,5.95,09:36:47,0.59,0.02,0.01,0.033,0.09,0,0,595,594.257,VOLANT,
假账户,12/29/2020,12/30/2020,USD,2,B,NFLX201231P00535000,1,5.68,11:58:17,0.59,0,0,0.033,0.09,0,0,-568,- 568.713,飞翔者,
假账户,12/29/2020,12/30/2020,USD,2,B,SPY201230P00372000,1,0.91,12:01:26,0.59,0,0,0.033,0.09,0,0,-91,- 91.713,飞翔者,
假账户,12/29/2020,12/30/2020,USD,2,S,SPY201230P00372000,1,0.97,12:07:18,0.59,0.01,0.01,0.033,0.09,0,0,97,96.267,飞翔,
假账户,12/29/2020,12/30/2020,USD,2,S,NFLX201231P00535000,1,6.02,12:21:55,0.59,0.02,0.01,0.033,0.09,0,0,602,601.257,VOLANT,
在这里,我为每种颜色匹配了相同的交易以更好地解释这个概念。黄色是形成 1 笔交易的两笔交易。开仓交易是“买入”(B),因此要平仓,匹配交易应该是“卖出”(S)。
相同的概念,绿色稍微复杂一些。开仓交易是数量为 450 的“买入”。随后的交易也使用相同的代码“买入”,因此增加仓位(450 + 75 + 15 = 540 数量)。关闭交易的匹配交易应该是“卖出”,但也可以是增量的。所以我应该在交易初始化后跟踪数量。查看最后一笔绿色交易是如何卖出相同符号的540数量,使交易总量为零,意味着交易完成(已关闭)。
我已经进行了交易 class,其中包含所有必填字段、构造函数、getter 和 setter,以及交易 class。
public class Transaction {
private String account;
private LocalDate transactionDate;
private LocalDate settledDate;
private String currency;
private int type;
private char side;
private String symbol;
private int quantity;
private double price;
private LocalTime executionTime;
private double commission;
private double secFee;
private double tafFee;
private double nsccFee;
private double nasdaqFee;
private double ecnRemove;
private double ecnAdd;
private double grossProceeds;
private double netProceeds;
public Transaction(String account, LocalDate transactionDate, LocalDate settledDate, String currency,
int type, char side, String symbol, int quantity, double price, LocalTime executionTime,
double commission, double secFee, double tafFee, double nsccFee, double nasdaqFee,
double ecnRemove, double ecnAdd, double grossProceeds, double netProceeds) {
this.account = account;
this.transactionDate = transactionDate;
this.settledDate = settledDate;
this.currency = currency;
this.type = type;
this.side = side;
this.symbol = symbol;
this.quantity = quantity;
this.price = price;
this.executionTime = executionTime;
this.commission = commission;
this.secFee = secFee;
this.tafFee = tafFee;
this.nsccFee = nsccFee;
this.nasdaqFee = nasdaqFee;
this.ecnRemove = ecnRemove;
this.ecnAdd = ecnAdd;
this.grossProceeds = grossProceeds;
this.netProceeds = netProceeds;
}
// Getters, setters and toString()
}
贸易Class:
public Trade(String symbol, String side, LocalDate openDate, LocalTime openTime, LocalDate closeDate,
LocalTime closeTime,
double averageOpenPrice, int shares, double averageClosingPrice, double risk, String setup,
String comments) {
//Geting unique ID based on time
Date date = Calendar.getInstance().getTime();
this.id = date.getTime();
this.symbol = symbol;
this.side = side;
this.openDate = openDate;
this.openTime = openTime;
this.closeDate = closeDate;
this.closeTime = closeTime;
this.averageOpenPrice = averageOpenPrice;
this.shares = shares;
this.averageClosingPrice = averageClosingPrice;
this.risk = risk;
this.setup = setup;
this.comments = comments;
pnl = calculatePnL(averageOpenPrice, averageClosingPrice, shares, side);
percentGain = calculatePercentGain(averageOpenPrice, averageClosingPrice, side);
}
}
我的问题:由于两个原因,我无法迭代交易列表并匹配它们:
- 有时我会平仓,这意味着我不会在 1 笔交易中卖出(多笔交易平仓),这意味着我必须匹配多笔交易。
- 在通过的列表中,有可能交易仍处于部分打开状态。我不知道如何处理这种可能性。
- 符号可以是“代码”符号或期权符号,不确定是否相关。
我试过的:
从消耗的文件中,我得到了交易 object 的列表,虽然我会按符号、面 (Buy/sell) 和数量来匹配交易。这种方法的问题在于它可能不是同一笔交易。
public ObservableList<Trade> parseTradesFromTransactions(ObservableList<Transaction> list) {
for(Transaction transaction : list) {
int closedTradecount = 0;
// Iterating through the list
String symbol = transaction.getSymbol();
LocalDate transactionDate = transaction.getTransactionDate();
int quantity = transaction.getQuantity();
char side = transaction.getSide();
// iterate through the rest and match
for(int i = 0; i < list.size(); i ++) {
if(symbol.equals(list.get(i).getSymbol())){
if(transaction.getSide() == 'B' && list.get(i).getSide() == 'S' && transaction.getQuantity() == list.get(i).getQuantity()){
closedTradecount++;
}
}
}
}
return tradeList;
}
我对编程和处理数据还很陌生,我想做好这件事。任何帮助将不胜感激,因为我无法全神贯注于匹配交易。
谢谢!
对于你的第一个问题,
- 有时我会平仓,这意味着我不会在 1 笔交易中卖出(多笔交易平仓),这意味着我必须匹配多笔交易。
使用分组方法:
按 'symbol' 对您的交易进行分组并收集为地图。
Map postsPerType = transactions.stream()
.collect(groupingBy(Transactions::getSymbol));
这样您就可以将所有交易组合在一起。
- 在通过的列表中,有可能交易仍处于部分打开状态。我不知道如何处理这种可能性。
迭代以上收集到的交易,通过筛选买卖交易来匹配数量,确保交易是否完成。
如果我对你的逻辑理解正确,你的程序中将需要 2 个数据结构:
一个动态数组(建议使用ArrayList
)来保存您收集的所有平仓交易。
一个字典/地图(建议使用 HashMap
)来保存所有未结交易并通过它们的符号快速访问它们。
您的算法将如下所示:
迭代所有事务。
对于每笔交易,检查其代码是否在开放交易地图中。
--> 如果是,将交易添加到现有交易中。检查它是否将符号数量减少到 0.
----> 如果 symobol == 0,关闭交易,并将其从地图移至关闭的交易列表。
----> 如果交易品种 > 0,继续下一笔交易。
--> 如果是不是,为符号创建一个新交易并将其添加到地图。
处理完所有交易后,地图应该是空的,您可以将列表存储在您的数据库中。
代码看起来像这样:
public class TransactionProcessor {
private ArrayList<Trade> mClosedTrades = new ArrayList<>();
private HashMap<String, Trade> mOpenTrades = new HashMap<>();
public void processTransaction(Transaction transaction) {
Trade curTrade; //for convinience
if (!mOpenTrades.containsKey(transaction.getSymbol())) {
curTrade = new Trade(transaction);
mOpenTrades.add(transaction.getSymbol(), curTrade);
} else {
curTrade = mOpenTrades.get(transaction.getSymbol());
//shortcut: this function returns true if current transaction closes the trade:
if (curTrade.addTransaction(transaction)) {
mClosedTrades.add(curTrade);
mOpenTrades.remove(curTrade.getSymbol());
}
}
}
}
要使此代码正常工作,您需要向 Trade
class 添加两个函数。
首先,向您的 Trade
class 添加一个构造函数,直接从第一个事务对其进行初始化,而不是单独传递每个参数。
public Trade(Transaction first) {
symbol = first.getSymbol();
// ... all other fields initialization ...
}
其次,将添加更多交易的逻辑移动到交易 class:
public boolean addTransaction(Transaction newTrans) {
//optional: add code that makes sure this transaction belongs to this trade by checking the symbol
if (newTrans.getSide() == 'B') {
quantity += newTrans.getQuantity();
} else {
quantity -= newTrans.getQuantity();
}
return quantity == 0; //this is same as if q == 0 return true; else return false;
}
此代码假定您的 CSV 文件中的交易是有序的,因此您永远不需要在您仍然拥有任何特定交易品种的数量时开立新交易。
此外,没有错误检查。
如果 CSV 文件中有错误,您最终可能会得到一些符号的负数量。
如果您在添加此类代码时遇到问题,您应该单独提出一个问题。
我正在做一个项目,通过使用我的经纪人生成的 CSV 文件,将我的交易直接上传到我正在构建的应用程序,而不必在日志中手动输入交易或支付交易费用。
我的问题是数据表示为交易而不是交易,因此我必须匹配交易 (Buys/sells) 并从中创建另一个 object。我想创建一个“交易”Object 的原因是将它们的列表存储在数据库中并将这些 object 传递给其他方法来计算东西。
这是来自我的经纪人的数据:
这是 CSV 文件的 Header:
账户,T/D,S/D,货币,类型,边,符号,数量,价格,执行时间,Comm,SEC,TAF,NSCC,Nasdaq,ECN删除,ECN 添加,总收益,净收益,Clr Broker,Liq,Note
包含多个交易示例的CSV文件示例数据:
FAKEACCOUNT,12/22/2020,12/23/2020,USD,2,B,MSFT201224P00222500,1,0.77,09:50:45,0.59,0,0,0.033, 0.09,0,0,-77,-77.713,LAMP, 假账户,12/23/2020,12/24/2020,USD,2,S,MSFT201224P00222500,7,1.3,09:47:32,4.13,0.03,0.01,0.033,0.63,0,0,910,905.167,VOLANT, 假账户,12/24/2020,12/29/2020,USD,2,B,COCP,450,1.7,07:31:58,2.25,0,0,0.033,0.007065,0,0,-765,- 767.290065,LAMP,e, 假账户,12/24/2020,12/29/2020,USD,2,B,COCP,75,1.65,08:08:06,0.99,0,0,0.033,0.0011775,0,0,-123.75,- 124.7741775,LAMP,X, 假账户,12/24/2020,12/29/2020,USD,2,B,COCP,15,1.63,09:29:23,0.99,0,0,0.033,0.0002355,0,0,-24.45,- 25.4732355,LAMP, 假账户,12/28/2020,12/30/2020,USD,2,S,COCP,540,1.4709,10:30:36,2.7,0.02,0.07,0.033,0.008478,0,0,794.286,791.454522,MNGD, , 假账户,12/29/2020,12/30/2020,USD,2,B,PYPL210108P00235000,1,5.35,09:34:21,0.59,0,0,0.033,0.09,0,0,-535,- 535.713,飞翔者, 假账户,12/29/2020,12/30/2020,USD,2,S,PYPL210108P00235000,1,5.95,09:36:47,0.59,0.02,0.01,0.033,0.09,0,0,595,594.257,VOLANT, 假账户,12/29/2020,12/30/2020,USD,2,B,NFLX201231P00535000,1,5.68,11:58:17,0.59,0,0,0.033,0.09,0,0,-568,- 568.713,飞翔者, 假账户,12/29/2020,12/30/2020,USD,2,B,SPY201230P00372000,1,0.91,12:01:26,0.59,0,0,0.033,0.09,0,0,-91,- 91.713,飞翔者, 假账户,12/29/2020,12/30/2020,USD,2,S,SPY201230P00372000,1,0.97,12:07:18,0.59,0.01,0.01,0.033,0.09,0,0,97,96.267,飞翔, 假账户,12/29/2020,12/30/2020,USD,2,S,NFLX201231P00535000,1,6.02,12:21:55,0.59,0.02,0.01,0.033,0.09,0,0,602,601.257,VOLANT,
在这里,我为每种颜色匹配了相同的交易以更好地解释这个概念。黄色是形成 1 笔交易的两笔交易。开仓交易是“买入”(B),因此要平仓,匹配交易应该是“卖出”(S)。
相同的概念,绿色稍微复杂一些。开仓交易是数量为 450 的“买入”。随后的交易也使用相同的代码“买入”,因此增加仓位(450 + 75 + 15 = 540 数量)。关闭交易的匹配交易应该是“卖出”,但也可以是增量的。所以我应该在交易初始化后跟踪数量。查看最后一笔绿色交易是如何卖出相同符号的540数量,使交易总量为零,意味着交易完成(已关闭)。
我已经进行了交易 class,其中包含所有必填字段、构造函数、getter 和 setter,以及交易 class。
public class Transaction {
private String account;
private LocalDate transactionDate;
private LocalDate settledDate;
private String currency;
private int type;
private char side;
private String symbol;
private int quantity;
private double price;
private LocalTime executionTime;
private double commission;
private double secFee;
private double tafFee;
private double nsccFee;
private double nasdaqFee;
private double ecnRemove;
private double ecnAdd;
private double grossProceeds;
private double netProceeds;
public Transaction(String account, LocalDate transactionDate, LocalDate settledDate, String currency,
int type, char side, String symbol, int quantity, double price, LocalTime executionTime,
double commission, double secFee, double tafFee, double nsccFee, double nasdaqFee,
double ecnRemove, double ecnAdd, double grossProceeds, double netProceeds) {
this.account = account;
this.transactionDate = transactionDate;
this.settledDate = settledDate;
this.currency = currency;
this.type = type;
this.side = side;
this.symbol = symbol;
this.quantity = quantity;
this.price = price;
this.executionTime = executionTime;
this.commission = commission;
this.secFee = secFee;
this.tafFee = tafFee;
this.nsccFee = nsccFee;
this.nasdaqFee = nasdaqFee;
this.ecnRemove = ecnRemove;
this.ecnAdd = ecnAdd;
this.grossProceeds = grossProceeds;
this.netProceeds = netProceeds;
}
// Getters, setters and toString()
}
贸易Class:
public Trade(String symbol, String side, LocalDate openDate, LocalTime openTime, LocalDate closeDate,
LocalTime closeTime,
double averageOpenPrice, int shares, double averageClosingPrice, double risk, String setup,
String comments) {
//Geting unique ID based on time
Date date = Calendar.getInstance().getTime();
this.id = date.getTime();
this.symbol = symbol;
this.side = side;
this.openDate = openDate;
this.openTime = openTime;
this.closeDate = closeDate;
this.closeTime = closeTime;
this.averageOpenPrice = averageOpenPrice;
this.shares = shares;
this.averageClosingPrice = averageClosingPrice;
this.risk = risk;
this.setup = setup;
this.comments = comments;
pnl = calculatePnL(averageOpenPrice, averageClosingPrice, shares, side);
percentGain = calculatePercentGain(averageOpenPrice, averageClosingPrice, side);
}
}
我的问题:由于两个原因,我无法迭代交易列表并匹配它们:
- 有时我会平仓,这意味着我不会在 1 笔交易中卖出(多笔交易平仓),这意味着我必须匹配多笔交易。
- 在通过的列表中,有可能交易仍处于部分打开状态。我不知道如何处理这种可能性。
- 符号可以是“代码”符号或期权符号,不确定是否相关。
我试过的:
从消耗的文件中,我得到了交易 object 的列表,虽然我会按符号、面 (Buy/sell) 和数量来匹配交易。这种方法的问题在于它可能不是同一笔交易。
public ObservableList<Trade> parseTradesFromTransactions(ObservableList<Transaction> list) {
for(Transaction transaction : list) {
int closedTradecount = 0;
// Iterating through the list
String symbol = transaction.getSymbol();
LocalDate transactionDate = transaction.getTransactionDate();
int quantity = transaction.getQuantity();
char side = transaction.getSide();
// iterate through the rest and match
for(int i = 0; i < list.size(); i ++) {
if(symbol.equals(list.get(i).getSymbol())){
if(transaction.getSide() == 'B' && list.get(i).getSide() == 'S' && transaction.getQuantity() == list.get(i).getQuantity()){
closedTradecount++;
}
}
}
}
return tradeList;
}
我对编程和处理数据还很陌生,我想做好这件事。任何帮助将不胜感激,因为我无法全神贯注于匹配交易。
谢谢!
对于你的第一个问题,
- 有时我会平仓,这意味着我不会在 1 笔交易中卖出(多笔交易平仓),这意味着我必须匹配多笔交易。
使用分组方法:
按 'symbol' 对您的交易进行分组并收集为地图。
Map
这样您就可以将所有交易组合在一起。
- 在通过的列表中,有可能交易仍处于部分打开状态。我不知道如何处理这种可能性。
迭代以上收集到的交易,通过筛选买卖交易来匹配数量,确保交易是否完成。
如果我对你的逻辑理解正确,你的程序中将需要 2 个数据结构:
一个动态数组(建议使用
ArrayList
)来保存您收集的所有平仓交易。一个字典/地图(建议使用
HashMap
)来保存所有未结交易并通过它们的符号快速访问它们。
您的算法将如下所示:
迭代所有事务。
对于每笔交易,检查其代码是否在开放交易地图中。
--> 如果是,将交易添加到现有交易中。检查它是否将符号数量减少到 0.
----> 如果 symobol == 0,关闭交易,并将其从地图移至关闭的交易列表。
----> 如果交易品种 > 0,继续下一笔交易。
--> 如果是不是,为符号创建一个新交易并将其添加到地图。
处理完所有交易后,地图应该是空的,您可以将列表存储在您的数据库中。
代码看起来像这样:
public class TransactionProcessor {
private ArrayList<Trade> mClosedTrades = new ArrayList<>();
private HashMap<String, Trade> mOpenTrades = new HashMap<>();
public void processTransaction(Transaction transaction) {
Trade curTrade; //for convinience
if (!mOpenTrades.containsKey(transaction.getSymbol())) {
curTrade = new Trade(transaction);
mOpenTrades.add(transaction.getSymbol(), curTrade);
} else {
curTrade = mOpenTrades.get(transaction.getSymbol());
//shortcut: this function returns true if current transaction closes the trade:
if (curTrade.addTransaction(transaction)) {
mClosedTrades.add(curTrade);
mOpenTrades.remove(curTrade.getSymbol());
}
}
}
}
要使此代码正常工作,您需要向 Trade
class 添加两个函数。
首先,向您的 Trade
class 添加一个构造函数,直接从第一个事务对其进行初始化,而不是单独传递每个参数。
public Trade(Transaction first) {
symbol = first.getSymbol();
// ... all other fields initialization ...
}
其次,将添加更多交易的逻辑移动到交易 class:
public boolean addTransaction(Transaction newTrans) {
//optional: add code that makes sure this transaction belongs to this trade by checking the symbol
if (newTrans.getSide() == 'B') {
quantity += newTrans.getQuantity();
} else {
quantity -= newTrans.getQuantity();
}
return quantity == 0; //this is same as if q == 0 return true; else return false;
}
此代码假定您的 CSV 文件中的交易是有序的,因此您永远不需要在您仍然拥有任何特定交易品种的数量时开立新交易。
此外,没有错误检查。 如果 CSV 文件中有错误,您最终可能会得到一些符号的负数量。
如果您在添加此类代码时遇到问题,您应该单独提出一个问题。