【问题标题】:How to iterate through a list and match transactions如何遍历列表并匹配交易
【发布时间】:2021-06-14 02:58:16
【问题描述】:

我正在开展一个项目,通过使用我的经纪人生成的 CSV 文件,将我的交易直接上传到我正在构建的应用程序中,而不必手动在日志中输入交易或付费。

我的问题是数据表示为交易而不是交易,因此我必须匹配交易(买入/卖出)并从中创建另一个对象。我想创建一个“交易”对象的原因是将它们的列表存储在数据库中,并将这些对象传递给其他方法来计算东西。

以下是我的经纪人提供的数据:

这是 CSV 文件的标题:

帐户、T/D、S/D、货币、类型、侧面、符号、数量、价格、执行时间、Comm、SEC、TAF、NSCC、纳斯达克、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,灯,, FAKEACCOUNT,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,, FAKEACCOUNT,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,灯,e, FAKEACCOUNT,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,灯,X, FAKEACCOUNT,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,灯,, FAKEACCOUNT,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, , FAKEACCOUNT,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,自愿,, FAKEACCOUNT,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,, FAKEACCOUNT,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,自愿的,, FAKEACCOUNT,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, 自愿的,, FAKEACCOUNT,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,自愿的,, FAKEACCOUNT,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 数量,使交易的总数量为零,这意味着交易已完成(关闭)。

我已经创建了一个包含所有必需字段的 Transaction 类、一个构造函数、getter 和 setter,以及一个 Trade 类。

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()
}

贸易类:

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 笔交易中卖出(多笔交易才能平仓),这意味着我必须匹配多笔交易。
  • 在通过的列表中仍有可能部分打开交易。我不知道如何处理这种可能性。
  • 符号可能是“股票代码”符号或期权符号,不确定是否相关。

我的尝试

从使用的文件中,我得到一个交易对象列表,但我会按符号、边(买/卖)和数量来匹配交易。这种方法的问题在于它可能不是同一个行业。

 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; 
}

我对编程和处理数据非常陌生,我想把这件事做好。任何帮助都将不胜感激,因为我无法解决匹配交易。

谢谢!

【问题讨论】:

    标签: java trading


    【解决方案1】:

    如果我正确理解您的逻辑,您的程序中需要 2 个数据结构:

    1. 一个动态数组(建议使用ArrayList)来保存您收集的所有已平仓交易。

    2. 一个字典/地图(建议使用HashMap)来保存所有未平仓交易并通过它们的符号快速访问它们。

    你的算法应该是这样的:

    遍历所有事务。
    对于每笔交易,检查其交易品种是否在未平仓交易地图中。
    --> 如果是,则将该交易添加到现有交易中。检查它是否将符号数量减少到 0。
    ----> 如果符号 == 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 类中再添加两个函数。

    首先,向您的Trade 类添加一个构造函数,该构造函数直接从第一个事务初始化它,而不是单独传递每个参数。

    public Trade(Transaction first) {
        symbol = first.getSymbol();
        // ... all other fields initialization ...
    }
    

    其次,将添加更多交易的逻辑移到交易类中:

    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 文件中存在错误,您可能会得到某个符号的负数。

    如果您在添加此类代码时遇到问题,您应该提出一个单独的问题。

    【讨论】:

    • 这非常有用。通读代码后想到的一个问题是,我应该在我的数据库中存储未平仓交易和已平仓交易吗?您知道,如果程序在有未平仓交易时关闭,或者如果我上传了不完整的文件,或者当我仍有未平仓头寸时关闭。无论如何,一旦功能解锁,我会接受您的回答并奖励您的声誉积分。再次感谢
    • @ADSquared 你问了一个很好的设计问题!我假设该程序将在相对较小的 CSV 上快速运行,并且在完成后没有未平仓头寸,但是如果您的程序需要大量时间来处理(超过几分钟)或者 Trade 对象可以在之后保留在打开的地图中处理(空缺职位)然后是的 - 您绝对应该存储内部状态以便能够恢复。假设您使用的是 SQL 类型 DB,我会将未平仓头寸放在单独的表中。
    • @ADSquared 遗憾的是,SO 不适合设计问题,但我想给出一些一般性建议:任何程序都可以用几种不同的方式设计,其中一些相同,一些更好或更差适合目的。做某事几乎没有“一个正确的解决方案”。由于您是编程新手,请尝试添加您认为对您的程序有用的内容,看看它是如何工作的。不要太担心“这是正确的做法吗”。您将学习并到达那里!祝你好运!
    • @Lev_M 非常感谢您的帮助,非常感谢!
    【解决方案2】:

    对于你的第一个问题,

    1. 有时我扩大头寸意味着我不会在 1 笔交易中卖出(多笔交易才能平仓),这意味着我必须匹配多笔交易。

    使用 groupingBy 方法:

    按“符号”对您的交易进行分组并收集为地图。

    Map postsPerType = transactions.stream() .collect(groupingBy(Transactions::getSymbol));

    这样您就可以将所有交易组合在一起。

    1. 在通过的列表中仍有可能部分打开交易。我不知道如何处理这种可能性。

    通过过滤掉Buy和Sell交易来迭代上面收集的交易并匹配数量,以确保交易是否完成。

    【讨论】:

    • 我现在正在探索地图,不知道那个数据结构。我马上就发现了一个问题,您可以在多个场合多次交易同一个交易品种,打开和关闭交易。也许一个事务打开了 Map,一旦数量达到 0,Map 应该被重置?所以其他事务不使用相同的密钥编译?
    • 无需重置。 Map 的工作方式就像所有交易都按符号分组一样。例如 - Symbol1 - transaction1 transaction2 Symbol2 transactoin1 transaction2 transaction3
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    • 1970-01-01
    • 2022-11-20
    • 2021-03-18
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多