using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Domain.Entities.Paper;
///
/// 계좌 일별 스냅샷 — (AccountID, TradeDate) UNIQUE. 좌수 NAV·수익률·MDD 를 배치(M2)가 산출/upsert 한다 (d4 §③).
/// 리더보드 기간 수익률은 스냅샷 nav 로 조회한다.
///
public class PaperDailySnapshot
{
[ForeignKey(nameof(AccountID))]
public virtual PaperAccount Account { get; private set; } = null!;
[Key]
public int ID { get; private set; }
public int AccountID { get; private set; }
/// 거래일
public DateOnly TradeDate { get; private set; }
/// 자유 + 예약 토큰
public decimal Token { get; private set; }
/// 포지션 평가액
public decimal PositionsValue { get; private set; }
/// 순자산 = Token + PositionsValue
public decimal Equity { get; private set; }
/// 좌당 순자산 (18,8) = Equity / Units
public decimal UnitNav { get; private set; }
/// 일간 수익률 Bp
public int DailyReturnBp { get; private set; }
/// 누적 수익률 Bp = (nav - 1) × 10000
public int CumReturnBp { get; private set; }
/// 최고 순자산 (MDD 산출용)
public decimal PeakEquity { get; private set; }
/// 최대 낙폭 Bp
public int MddBp { get; private set; }
/// 누적 체결 수 (리더보드 등재 요건)
public int FillCountCum { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
private PaperDailySnapshot() { }
public static PaperDailySnapshot Create(
int accountID,
DateOnly tradeDate,
decimal token,
decimal positionsValue,
decimal equity,
decimal unitNav,
int dailyReturnBp,
int cumReturnBp,
decimal peakEquity,
int mddBp,
int fillCountCum
) {
if (accountID <= 0)
{
throw new ArgumentOutOfRangeException(nameof(accountID));
}
return new PaperDailySnapshot
{
AccountID = accountID,
TradeDate = tradeDate,
Token = token,
PositionsValue = positionsValue,
Equity = equity,
UnitNav = unitNav,
DailyReturnBp = dailyReturnBp,
CumReturnBp = cumReturnBp,
PeakEquity = peakEquity,
MddBp = mddBp,
FillCountCum = fillCountCum
};
}
/// 동일 (AccountID, TradeDate) 재실행 시 값 갱신 (upsert, 배치 멱등).
public void Update(
decimal token,
decimal positionsValue,
decimal equity,
decimal unitNav,
int dailyReturnBp,
int cumReturnBp,
decimal peakEquity,
int mddBp,
int fillCountCum
) {
Token = token;
PositionsValue = positionsValue;
Equity = equity;
UnitNav = unitNav;
DailyReturnBp = dailyReturnBp;
CumReturnBp = cumReturnBp;
PeakEquity = peakEquity;
MddBp = mddBp;
FillCountCum = fillCountCum;
}
}