/*
 * Copyright (c) 2006-2009 OrangeSignal.com All rights reserved.
 */

package jp.sourceforge.orangesignal.trading.stats;

import java.util.LinkedList;

import jp.sourceforge.orangesignal.trading.Position;

/**
 * <p>勝ちトレード及び負けトレードに関するパフォーマンス統計情報の基底クラスを提供します。</p>
 * 
 * @author 杉澤 浩二
 */
public abstract class WinLossStats extends AbstractStats {

	private static final long serialVersionUID = 1L;

	/**
	 * <p>デフォルトコンストラクタです。</p>
	 * <p>このコンストラクタはサブクラスを実装する開発者向けに提供しています。</p>
	 */
	protected WinLossStats() {}

	/**
	 * ポジション情報のリストと初期資金を指定してこのクラクを構築するコンストラクタです。
	 * 
	 * @param positions ポジション情報のリスト
	 * @param initialCapital 初期資金
	 * @param discard トレード情報のリストを破棄するかどうか
	 */
	public WinLossStats(final LinkedList<Position> positions, final double initialCapital, final boolean discard) {
		statistics(positions, initialCapital, discard);
	}

	@Override
	protected void statistics(final LinkedList<Position> positions, final double initialCapital, final boolean discard) {
		super.statistics(positions, initialCapital, discard);

		final AbstractStats parent = this;
		winStats = new AbstractStats(positions, initialCapital, discard) {
			private static final long serialVersionUID = 1L;
			@Override protected boolean isStatistics(final Position p) {
				return parent.isStatistics(p) && p.getNetProfit() > 0.0;
			}
		};
		lossStats = new AbstractStats(positions, initialCapital, discard) {
			private static final long serialVersionUID = 1L;
			@Override protected boolean isStatistics(final Position p) {
				return parent.isStatistics(p) && p.getNetProfit() <= 0.0;
			}
		};
	}

	// ----------------------------------------------------------------------

	/**
	 * 勝ちトレードのパフォーマンス統計情報を保持します。
	 */
	protected AbstractStats winStats;

	/**
	 * 勝ちトレードのパフォーマンス統計情報を返します。
	 * 
	 * @return 勝ちトレードのパフォーマンス統計情報
	 */
	public AbstractStats getWinStats() { return winStats; }

	/**
	 * 負けトレードのパフォーマンス統計情報を保持します。
	 */
	protected AbstractStats lossStats;

	/**
	 * 負けトレードのパフォーマンス統計情報を返します。
	 * 
	 * @return 負けトレードのパフォーマンス統計情報
	 */
	public AbstractStats getLossStats() { return lossStats; }

	// ----------------------------------------------------------------------

	/**
	 * 最大利益期間(勝ち最大期間)
	 * 
	 * @return 最大利益期間
	 */
	public int getMaxWinPeriod() { return winStats.getMaxHold(); }

	/**
	 * 最大損失期間
	 * 
	 * @return 最大損失期間
	 */
	public int getMaxLossPeriod() { return lossStats.getMaxHold(); }

	/**
	 * 平均利益期間
	 * 
	 * @return 平均利益期間
	 */
	public double getAverageWinPeriod() { return winStats.getAverageHold(); }

	/**
	 * 平均損失期間
	 * 
	 * @return 平均損失期間
	 */
	public double getAverageLossPeriod() { return lossStats.getAverageHold(); }

	// ----------------------------------------------------------------------

	/**
	 * 利益を返します。
	 * 
	 * @return 利益
	 */
	public double getGrossProfit() { return winStats.getNetProfit(); }

	/**
	 * 利益率を返します。
	 * 
	 * @return 利益率
	 */
	public double getPercentGrossProfit() { return getPercent(getGrossProfit()); }

	/**
	 * 損失を返します。
	 * 
	 * @return 損失
	 */
	public double getGrossLoss() { return lossStats.getNetProfit(); }

	/**
	 * 損失率を返します。
	 * 
	 * @return 損失率
	 */
	public double getPercentGrossLoss() { return getPercent(getGrossLoss()); }

	/**
	 * 調整損益を返します。
	 * 
	 * @return 調整損益
	 */
	public double getAdjustedNetProfit() { return getAdjustedGrossProfit() + getAdjustedGrossLoss(); }

	/**
	 * 調整損益率を返します。
	 * 
	 * @return 調整損益率
	 */
	public double getPercentAdjustedNetProfit() { return getPercent(getAdjustedNetProfit()); }

	/**
	 * 調整利益を返します。
	 * 
	 * @return 調整利益
	 */
	public double getAdjustedGrossProfit() {
		final int count = winStats.getCount();
		return (count - Math.sqrt(count)) * winStats.getAverageNetProfit();
	}

	/**
	 * 調整利益率を返します。
	 * 
	 * @return 調整利益率
	 */
	public double getPercentAdjustedGrossProfit() { return getPercent(getAdjustedGrossProfit()); }

	/**
	 * 調整損失を返します。
	 * 
	 * @return 調整損失
	 */
	public double getAdjustedGrossLoss() {
		final int count = lossStats.getCount();
		return (count + Math.sqrt(count)) * lossStats.getAverageNetProfit();
	}

	/**
	 * 調整損失率を返します。
	 * 
	 * @return 調整損失率
	 */
	public double getPercentAdjustedGrossLoss() { return getPercent(getAdjustedGrossLoss()); }

	/**
	 * 平均利益を返します。
	 * 
	 * @return 平均利益
	 */
	public double getAverageGrossProfit() { return winStats.getAverageNetProfit(); }

	/**
	 * 平均損失を返します。
	 * 
	 * @return 平均損失
	 */
	public double getAverageGrossLoss() { return lossStats.getAverageNetProfit(); }

	/**
	 * 損益レシオ(ペイオフレシオ)を返します。
	 * 
	 * @return 損益レシオ(ペイオフレシオ)
	 */
	public double getPayoffRatio() {
		final double profit = getAverageGrossProfit();
		final double loss = getAverageGrossLoss();
		if (profit == 0.0 || loss == 0.0)
			return 0.0;
		return profit / Math.abs(loss);
	}

	// ----------------------------------------------------------------------
	// プロフィットファクター

	/**
	 * プロフィットファクターを返します。
	 * 
	 * @return プロフィットファクター
	 */
	public double getProfitFactor() {
		if (getGrossProfit() == 0.0 || getGrossLoss() == 0.0)
			return 0.0;
		return Math.abs(getGrossProfit() / getGrossLoss());
	}

	/**
	 * 調整プロフィットファクターを返します。
	 * 
	 * @return 調整プロフィットファクター
	 */
	public double getAdjustedProfitFactor() {
		final double profit = getAdjustedGrossProfit();
		final double loss = getAdjustedGrossLoss();
		if (profit == 0.0 || loss == 0.0)
			return 0.0;
		return Math.abs(profit / loss);
	}

	// ----------------------------------------------------------------------

	/**
	 * <p>勝率を返します。</p>
	 * 
	 * @return 勝率
	 */
	public double getPercentProfitable() { return getProfitable(); }

	/**
	 * <p>勝率を返します。</p>
	 * 
	 * @return 勝率
	 */
	public double getProfitable() {
		final int sum = getWinTrades() + getLossTrades();
		if (getWinTrades() == 0 || sum == 0)
			return 0.0;
		return (double) getWinTrades() / (double) sum;
	}

	/**
	 * 期待値を返します。
	 * <pre>
	 * 期待値＝平均利益×勝率－abs(平均損失)×負率
	 * </pre>
	 * 
	 * @return 期待値
	 */
	public double getExpectation() {
		// 勝ちトレードの平均利益×勝率＋負けトレードの平均損失×(1－勝率)
		final double profitable = getProfitable();
		return getAverageGrossProfit() * profitable + getAverageGrossLoss() * (1.0 - profitable);
	}

	/**
	 * ケリー値を返します。
	 * 
	 * @return ケリー値
	 */
	public double getKelly() {
		final double payoff = getPayoffRatio();
		final double num = ((payoff + 1.0) * getProfitable() - 1.0);
		if (payoff == 0.0 || num == 0.0)
			return 0.0;
		return num / payoff;
	}

	// ----------------------------------------------------------------------

	/**
	 * <p>トレード数を返します。</p>
	 * 
	 * @return トレード数
	 */
	public int getTrades() { return getCount(); }

	/**
	 * <p>勝ちトレード数を返します。</p>
	 * 
	 * @return 勝ちトレード数
	 */
	public int getWinTrades() { return winStats.getCount(); }

	/**
	 * <p>勝ちトレード率を返します。</p>
	 * 
	 * @return 勝ちトレード率
	 */
	public double getPercentWinTrades() {
		if (getWinTrades() == 0 || getTrades() == 0)
			return 0.0;
		return (double) getWinTrades() / (double) getTrades();
	}

	/**
	 * <p>負けトレード数を返します。</p>
	 * 
	 * @return 負けトレード数
	 */
	public int getLossTrades() { return lossStats.getCount(); }

	/**
	 * <p>負けトレード率を返します。</p>
	 * 
	 * @return 負けトレード率
	 */
	public double getPercentLossTrades() {
		if (getLossTrades() == 0 || getTrades() == 0)
			return 0.0;
		return (double) getLossTrades() / (double) getTrades();
	}

	// ----------------------------------------------------------------------

	/**
	 * <p>最大連続勝ちトレード数を返します。</p>
	 * 
	 * @return 最大連続勝ちトレード数
	 */
	public int getMaxConsecutiveWinner() { return winStats.getMaxConsecutiveCount(); }

	/**
	 * <p>最大連続負けトレード数を返します。</p>
	 * 
	 * @return 最大連続負けトレード数
	 */
	public int getMaxConsecutiveLoser() { return lossStats.getMaxConsecutiveCount(); }

	// ----------------------------------------------------------------------

}
