import java.io.;
import java.math.BigDecimal;
import java.util.;
public class PositionDisplay {
public static void displayPositions() {
System.out.println("保有ポジションを表示します。");
try {
// PositionCalculatorを使用してポジションデータを計算
List<PositionCalculator.PositionData> positions = PositionCalculator.calculateAllPositions();
if (positions.isEmpty()) {
System.out.println("保有ポジションがありません。");
return;
}
// 表形式で表示
displayPositionTable(positions);
} catch (FileNotFoundException e) {
System.out.println("エラー: 取引データファイルが見つかりません: transactions.csv");
} catch (Exception e) {
System.out.println("エラー: " + e.getMessage());
}
}
// 既存の取引データから保有数量を計算(publicメソッドとして公開)
public static Map<String, Integer> calculatePositions() throws IOException {
Map<String, Integer> positionMap = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader("transactions.csv"))) {
String headerLine = reader.readLine(); // ヘッダー行をスキップ
if (headerLine == null) {
return positionMap;
}
String line;
while ((line = reader.readLine()) != null) {
String[] fields = line.split(",");
if (fields.length >= 4) {
String ticker = fields[1].trim();
String side = fields[2].trim();
int quantity = Integer.parseInt(fields[3].trim());
// 現在の保有数量を取得(なければ0)
int currentQuantity = positionMap.getOrDefault(ticker, 0);
// B(買い)の場合は加算、S(売り)の場合は減算
if ("B".equals(side)) {
positionMap.put(ticker, currentQuantity + quantity);
} else if ("S".equals(side)) {
positionMap.put(ticker, currentQuantity - quantity);
}
}
}
}
return positionMap;
}
// 特定の銘柄の現在の保有数量を取得
public static int getCurrentPosition(String ticker) throws IOException {
Map<String, Integer> positionMap = calculatePositions();
return positionMap.getOrDefault(ticker, 0);
}
// 新しい取引を追加した場合の保有数量を計算
public static int calculatePositionAfterTransaction(String ticker, String side, int quantity) throws IOException {
int currentPosition = getCurrentPosition(ticker);
if ("B".equals(side)) {
return currentPosition + quantity;
} else if ("S".equals(side)) {
return currentPosition - quantity;
}
return currentPosition;
}
private static void displayPositionTable(List<PositionCalculator.PositionData> positions) {
System.out.println();
System.out.println("+------+----------------------+------------+------------+------------+------------+------------+");
System.out.println("| 銘柄コード | 銘柄名 | 保有数量 | 平均取得単価 | 実現損益 | 評価額 | 評価損益 |");
System.out.println("+------+----------------------+------------+------------+------------+------------+------------+");
for (PositionCalculator.PositionData position : positions) {
String productName = position.getProductName();
// 銘柄名が20文字以上の場合は省略
if (productName.length() > 20) {
productName = productName.substring(0, 17) + "...";
}
// 平均取得単価の表示
String avgPriceStr = formatPrice(position.getAverageUnitPrice());
// 実現損益の表示
String realizedProfitStr = formatPrice(position.getRealizedProfit());
// 評価額の表示
String valuationStr = formatPrice(position.getValuation());
// 評価損益の表示
String unrealizedProfitStr = formatPrice(position.getUnrealizedProfit());
System.out.printf("| %-6s | %-20s | %10s | %10s | %10s | %10s | %10s |%n",
position.getTicker(),
productName,
String.format("%,d", position.getQuantity()),
avgPriceStr,
realizedProfitStr,
valuationStr,
unrealizedProfitStr);
}
System.out.println("+------+----------------------+------------+------------+------------+------------+------------+");
}
// 価格をフォーマット(nullの場合はN/A、右詰め、3桁ごとのカンマ区切り、小数点第2位まで)
private static String formatPrice(BigDecimal price) {
if (price == null) {
return "N/A";
}
if (price.compareTo(BigDecimal.ZERO) == 0) {
return "0.00";
}
return String.format("%,.2f", price);
}
}
2
import java.io.;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.;
public class PositionCalculator {
// ポジションデータクラス
public static class PositionData {
private String ticker;
private String productName;
private int quantity;
private BigDecimal averageUnitPrice; // nullの場合はN/A
private BigDecimal realizedProfit; // 実現損益
private BigDecimal valuation; // 評価額(nullの場合はN/A)
private BigDecimal unrealizedProfit; // 評価損益(nullの場合はN/A)
public PositionData(String ticker, String productName, int quantity,
BigDecimal averageUnitPrice, BigDecimal realizedProfit,
BigDecimal valuation, BigDecimal unrealizedProfit) {
this.ticker = ticker;
this.productName = productName;
this.quantity = quantity;
this.averageUnitPrice = averageUnitPrice;
this.realizedProfit = realizedProfit;
this.valuation = valuation;
this.unrealizedProfit = unrealizedProfit;
}
public String getTicker() { return ticker; }
public String getProductName() { return productName; }
public int getQuantity() { return quantity; }
public BigDecimal getAverageUnitPrice() { return averageUnitPrice; }
public BigDecimal getRealizedProfit() { return realizedProfit; }
public BigDecimal getValuation() { return valuation; }
public BigDecimal getUnrealizedProfit() { return unrealizedProfit; }
}
// 全銘柄のポジションデータを計算
public static List<PositionData> calculateAllPositions() throws IOException {
// 取引データを時系列順に読み込む
List<TransactionRecord> transactions = loadTransactions();
// 銘柄マスターから銘柄名を取得
Map<String, String> tickerToNameMap = loadTickerToNameMap();
// 銘柄ごとのポジション状態を管理
Map<String, PositionState> positionStates = new HashMap<>();
// 取引を時系列順に処理
for (TransactionRecord transaction : transactions) {
String ticker = transaction.ticker;
PositionState state = positionStates.getOrDefault(ticker, new PositionState());
if ("B".equals(transaction.side)) {
// 買い取引:移動平均法で平均取得単価を更新
state.processBuy(transaction.quantity, transaction.price);
} else if ("S".equals(transaction.side)) {
// 売り取引:実現損益を計算
state.processSell(transaction.quantity, transaction.price);
}
positionStates.put(ticker, state);
}
// 時価情報を読み込む
Map<String, Double> marketPriceMap = MarketPriceReader.loadMarketPrices();
// ポジションデータを作成
List<PositionData> positions = new ArrayList<>();
for (Map.Entry<String, PositionState> entry : positionStates.entrySet()) {
String ticker = entry.getKey();
PositionState state = entry.getValue();
// 保有数量が0でない銘柄のみを対象
if (state.quantity != 0) {
String productName = tickerToNameMap.getOrDefault(ticker, "不明");
// 平均取得単価(保有数量が0の場合は0またはN/A)
BigDecimal avgPrice = null;
if (state.quantity > 0 && state.totalCost.compareTo(BigDecimal.ZERO) > 0) {
avgPrice = state.totalCost.divide(
BigDecimal.valueOf(state.quantity), 2, RoundingMode.HALF_UP);
} else if (state.quantity == 0) {
// クローズされたポジションは0
avgPrice = BigDecimal.ZERO;
}
// 時価を取得
Double marketPrice = marketPriceMap.get(ticker);
// 評価額と評価損益
BigDecimal valuation = null;
BigDecimal unrealizedProfit = null;
if (marketPrice != null && state.quantity > 0) {
BigDecimal marketPriceBD = BigDecimal.valueOf(marketPrice);
valuation = marketPriceBD.multiply(BigDecimal.valueOf(state.quantity))
.setScale(2, RoundingMode.HALF_UP);
if (avgPrice != null && avgPrice.compareTo(BigDecimal.ZERO) > 0) {
unrealizedProfit = marketPriceBD.subtract(avgPrice)
.multiply(BigDecimal.valueOf(state.quantity))
.setScale(2, RoundingMode.HALF_UP);
}
}
positions.add(new PositionData(ticker, productName, state.quantity,
avgPrice, state.realizedProfit, valuation, unrealizedProfit));
}
}
// 銘柄コードの昇順でソート
Collections.sort(positions, (p1, p2) -> p1.getTicker().compareTo(p2.getTicker()));
return positions;
}
// 取引データを時系列順に読み込む
private static List<TransactionRecord> loadTransactions() throws IOException {
List<TransactionRecord> transactions = new ArrayList<>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
try (BufferedReader reader = new BufferedReader(new FileReader("transactions.csv"))) {
String headerLine = reader.readLine(); // ヘッダー行をスキップ
if (headerLine == null) {
return transactions;
}
String line;
while ((line = reader.readLine()) != null) {
String[] fields = line.split(",");
if (fields.length >= 5) {
try {
LocalDateTime transactionDateTime = LocalDateTime.parse(fields[0].trim(), formatter);
String ticker = fields[1].trim();
String side = fields[2].trim();
int quantity = Integer.parseInt(fields[3].trim());
double price = Double.parseDouble(fields[4].trim());
transactions.add(new TransactionRecord(transactionDateTime, ticker, side, quantity, price));
} catch (Exception e) {
// パースエラーは無視
}
}
}
}
// 取引日時の昇順でソート
Collections.sort(transactions, (t1, t2) -> t1.transactionDateTime.compareTo(t2.transactionDateTime));
return transactions;
}
// 銘柄マスターから銘柄名を取得
private static Map<String, String> loadTickerToNameMap() {
Map<String, String> tickerToNameMap = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader("Masterdata.csv"))) {
reader.readLine(); // ヘッダー行をスキップ
String line;
while ((line = reader.readLine()) != null) {
String[] fields = line.split(",");
if (fields.length >= 2) {
String ticker = fields[0].trim();
String productName = fields[1].trim();
tickerToNameMap.put(ticker, productName);
}
}
} catch (IOException e) {
// エラーは無視
}
return tickerToNameMap;
}
// ポジション状態を管理する内部クラス
private static class PositionState {
int quantity = 0;
BigDecimal totalCost = BigDecimal.ZERO; // 総取得コスト
BigDecimal realizedProfit = BigDecimal.ZERO; // 実現損益
void processBuy(int buyQuantity, double buyPrice) {
BigDecimal buyPriceBD = BigDecimal.valueOf(buyPrice);
BigDecimal buyCost = buyPriceBD.multiply(BigDecimal.valueOf(buyQuantity));
// 移動平均法:総コストを更新
totalCost = totalCost.add(buyCost);
quantity += buyQuantity;
}
void processSell(int sellQuantity, double sellPrice) {
if (quantity == 0) {
return; // エラーケース(通常は発生しない)
}
// 現在の平均取得単価を計算
BigDecimal avgPrice = totalCost.divide(
BigDecimal.valueOf(quantity), 2, RoundingMode.HALF_UP);
// 実現損益を計算
BigDecimal sellPriceBD = BigDecimal.valueOf(sellPrice);
BigDecimal profitPerShare = sellPriceBD.subtract(avgPrice);
BigDecimal realizedProfitForThisSale = profitPerShare.multiply(
BigDecimal.valueOf(sellQuantity)).setScale(2, RoundingMode.HALF_UP);
realizedProfit = realizedProfit.add(realizedProfitForThisSale);
// 保有数量と総コストを更新
quantity -= sellQuantity;
if (quantity > 0) {
// 残りの数量に対する総コストを再計算
totalCost = avgPrice.multiply(BigDecimal.valueOf(quantity))
.setScale(2, RoundingMode.HALF_UP);
} else {
totalCost = BigDecimal.ZERO;
}
}
}
// 取引レコード
private static class TransactionRecord {
LocalDateTime transactionDateTime;
String ticker;
String side;
int quantity;
double price;
TransactionRecord(LocalDateTime transactionDateTime, String ticker,
String side, int quantity, double price) {
this.transactionDateTime = transactionDateTime;
this.ticker = ticker;
this.side = side;
this.quantity = quantity;
this.price = price;
}
}
}