0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

11/25

Last updated at Posted at 2025-11-24

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

}

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?