aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/edu/brown/cs/student/term/parsing/Transaction.java
blob: 1f502b1f892d7f7ce03f7d282312ee0d6af598d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package edu.brown.cs.student.term.parsing;

import edu.brown.cs.student.term.hub.Holder;
import edu.brown.cs.student.term.trade.Trade;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.List;


/**
 * This class represents the transaction data from the xml file.
 */
public class Transaction {
  private Document doc;
  private final List<Trade> trades;
  private final String personName;
  private final int id;
  private final String ticker;

  /**
   * Constructor that represents the transaction from the document.
   * @param document The document parsed from the xml file.
   */
  public Transaction(Document document) {
    // TODO: add log comments
    System.err.println("LOG: Entered constructor for: " + getClass());
    doc = document;
    trades = new ArrayList<>();

    personName = personName();
    id = id();
    ticker = ticker();

    // There are two types of transactions within the xml - derivative and non derivative.
    // We only look at non-derivative
    NodeList nonDerivative = document.getElementsByTagName("nonDerivativeTransaction");
    System.err.println("LOG: Called processTransaction in constructor for: " + getClass());
    processTransactions(nonDerivative);
  }

  /**
   * Takes a transaction as a node list, then processes and stores them into trades.
   * @param tradesAsNodes The trades within the transaction as a nodelist.
   */
  private void processTransactions(NodeList tradesAsNodes) {
    int numTrades = tradesAsNodes.getLength();
    for(int i = 0; i < numTrades; i++) {
      // Convert to an element to extract specific info.
      // I know there is a cast, but this how it is done and is checked with the assertion.
      Node trade = tradesAsNodes.item(i);
      assert trade.getNodeType() == Node.ELEMENT_NODE;
      Element tradeValues = (Element) trade;

      // This stages the params for the trade.
      // TODO: update with real timestamp
      double ts = getDate(tradeValues).hashCode();
      int isBuy = getIsBuy(tradeValues);
      int numShares = getNumShares(tradeValues);
      // TODO: update id? unique for each holder?
      Holder holder = new Holder(id, personName);
      double pricePerShare = getPriceShares(tradeValues);

      if (pricePerShare != 0 && numShares != -1 && pricePerShare != -1 && isBuy != -1) {
        trades.add(new Trade(id, ticker, ts, isBuy, numShares, holder, pricePerShare));
      }
    }

    doc = null;
  }

  /**
   * Extracts the id of the transaction from the document.
   * @return The id.
   */
  private int id() {
    // Id of transaction
    NodeList idNode = doc.getElementsByTagName("rptOwnerCik");
    // TODO: add error parsing
    return Integer.parseInt(idNode.item(0).getTextContent());
  }

  /**
   * Extracts the person's name of the transaction from the document.
   * @return The person's name.
   */
  private String personName() {
    // Name of person
    NodeList nameNode = doc.getElementsByTagName("rptOwnerName");
    return nameNode.item(0).getTextContent();
  }

  /**
   * Extracts the security ticker of the transaction from the document
   * @return The ticker.
   */
  private String ticker() {
    // Ticker of security
    NodeList idNode = doc.getElementsByTagName("issuerTradingSymbol");
    return idNode.item(0).getTextContent();
  }


  public String getValueFromNode(NodeList dataHolder) {
    // Convert to an element to extract the value form.
    // I know there is a cast, but this how it is done and is checked with the assertion.
    Node dataNode = dataHolder.item(0);
    assert dataNode.getNodeType() == Node.ELEMENT_NODE;
    Element data = (Element) dataNode;

    // Only one field within the value holding the value...
    NodeList value = data.getElementsByTagName("value");
    if (value.getLength() == 0) {
      // If market trade, some values don't have fields...
      return "";
    }
    return value.item(0).getTextContent();
  }

  /**
   * Extracts the trade date from a trade node.
   * @param values The value array representing the trade.
   * @return The date in mm-dd-yyyy format as a string.
   */
  public String getDate(Element values) {
    NodeList date = values.getElementsByTagName("transactionDate");
    return getValueFromNode(date);
  }

  /**
   * Extracts the number of shares moved from the trade.
   * @param values The value array representing the trade.
   * @return The number of shares.
   */
  public int getNumShares(Element values) {
    NodeList numShares = values.getElementsByTagName("transactionShares");
    String num = getValueFromNode(numShares);
    num = num.equals("") ? "<empty>" : num;
    try {
      // Some ints have training zeros, so need to parse Double then truncate.
      return (int) Double.parseDouble(num);
    } catch (NumberFormatException e) {
      System.err.println("WARNING: Num shares in XML bad format: " + num);
      return -1;
    }
  }

  /**
   * Extracts the price each share cost from the trade.
   * @param values The value array representing the trade.
   * @return The price of each share.
   */
  public double getPriceShares(Element values) {
    NodeList priceShares = values.getElementsByTagName("transactionPricePerShare");
    String num = getValueFromNode(priceShares);
    num = num.equals("") ? "<empty>" : num;
    try {
      return Double.parseDouble(num);
    } catch (NumberFormatException e) {
      System.err.println("WARNING: Price shares in XML bad format: " + num);
      return -1;
    }
  }

  /**
   * Determines whether the shares in the trade were bought or sold.
   * @param values The value array representing the trade.
   * @return True if they were bought (acquired). False, if sold.
   */
  public int getIsBuy(Element values) {
    NodeList isBuy = values.getElementsByTagName("transactionAcquiredDisposedCode");
    String value = getValueFromNode(isBuy);
    if (value.equals("A")) {
      return 1;
    } else if (value.equals("D")) {
      return 0;
    } else {
      System.err.println("WARNING: Acquire code not recognized: " + value);
      return -1;
    }
  }

  /**
   * Returns the list of trades that occurred within the transaction.
   * @return A list of trade objects.
   */
  public List<Trade> getTrades() {
    return trades;
  }

  /**
   * Accessor method that returns the id of the transaction.
   * @return The id.
   */
  public int getId() {
    return id;
  }

  /**
   * Accessor method that returns the person's name who did the transaction.
   * @return The name of whom did the transaction.
   */
  public String getPersonName() {
    return personName;
  }
}