package simplex.bn25.makino335926.trading.position;
import simplex.bn25.makino335926.trading.trade.Trade;
import java.math.BigDecimal;
import java.util.*;
/**
* クラス: PositionCalculator
* 取引(Trade)データをもとに、各銘柄のポジションを計算するクラス。
* 保有数量、平均取得単価、実現損益、評価額などを計算し、最終的なポジションリストを生成する。
*/
public class PositionCalculator {
/**
* メソッド: calculatePositions
* 与えられた取引データと時価データをもとに、銘柄ごとのポジションを計算。
*
* @param trades List<Trade> 取引データのリスト
* @param marketPrices Map<String, BigDecimal> 各銘柄コードに対応する時価情報
* @return List<Position> 計算済みのポジションリスト(銘柄コード順にソート済み)
*/
public static List<Position> calculatePositions(List<Trade> trades, Map<String, BigDecimal> marketPrices) {
// 銘柄コードをキーとするポジションデータを格納するマップ
Map<String, Position> positions = new HashMap<>();
// 1. 取引データを順に処理し、ポジションを更新
for (Trade trade : trades) {
// 現在処理中の取引の銘柄コードを取得
String ticker = trade.ticker();
// マップに該当銘柄のポジションがなければ、新規作成して追加
positions.putIfAbsent(ticker, new Position(ticker, trade.name()));
// 該当銘柄のポジションオブジェクトを取得
Position position = positions.get(ticker);
// 取引区分(BUYまたはSELL)に応じた処理を行う
if (trade.side().equals("BUY")) {
// 買い取引の場合、平均取得単価を更新
// 新規取得した単価(tradedUnitPrice)と数量(quantity)を加重平均計算に使用
position.updateAverageUnitPrice(trade.tradedUnitPrice(), trade.quantity());
} else if (trade.side().equals("SELL")) {
// 売り取引の場合、次の計算を行う:
// 1. 損益(P/L: Profit or Loss)を計算
// 売却単価(tradedUnitPrice) - 平均取得単価 × 売却数量(quantity)
BigDecimal pnl = trade.tradedUnitPrice().subtract(position.getAverageUnitPrice())
.multiply(BigDecimal.valueOf(trade.quantity()));
// 2. 実現損益を更新
position.addRealizedPnL(pnl);
// 3. 保有数量を減少
position.updateQuantity(-trade.quantity());
}
}
// 2. 全ての銘柄の評価額を計算
for (Position position : positions.values()) {
// マーケット価格を取得し、ポジションの評価額を設定
// 時価情報が存在しない場合は評価額は設定されない(nullのまま)
position.setValuation(marketPrices.get(position.getTicker()));
}
// 3. マップをリストに変換し、銘柄コード順にソート
List<Position> positionList = new ArrayList<>(positions.values());
positionList.sort(Comparator.comparing(Position::getTicker));
// 計算されたポジションリストを返す
return positionList;
}
}
コメント