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

package jp.sourceforge.orangesignal.trading.stats;

import java.io.Serializable;
import java.util.LinkedList;

import jp.sourceforge.orangesignal.trading.Position;

/**
 * <p>パフォーマンス統計情報の基底クラスを提供します。</p>
 * 
 * @author 杉澤 浩二
 */
public abstract class AbstractStats implements Serializable {

	private static final long serialVersionUID = 1L;

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

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

	/**
	 * <p>指定されたポジション情報のリストを解析して、このクラスの統計情報を設定します。</p>
	 * <p>実装は、ポジション毎に {@link #isStatistics(Position)} を呼出して統計対象のポジションであるかどうか確認します。</p>
	 * 
	 * @param positions ポジション情報のリスト
	 * @param initialCapital 初期資金
	 * @param discard トレード情報のリストを破棄するかどうか
	 */
	protected void statistics(final LinkedList<Position> positions, final double initialCapital, final boolean discard) {
		double equity = initialCapital;
		double maxEquity = equity;
		int consecutiveCount = 0;	// 連続回数

		for (final Position p : positions) {
			if (!isStatistics(p)) {
				consecutiveCount = 0;
				continue;
			}

			// 回数
			this.count++;
			// 連続回数/最大連続回数
			consecutiveCount++;
			this.maxConsecutiveCount = Math.max(this.maxConsecutiveCount, consecutiveCount);

			// 合計保有期間/最大保有期間
			final int hold = p.getHold();
			this.hold += hold;
			this.maxHold = Math.max(this.maxHold, hold);

			// 売上
			this.grossSales += p.getGrossSales();
			// 手数料
			this.commission += p.getCommission();
			// スリッページ
			this.slippage += p.getSlippage();

			final double netProfit = p.getNetProfit();

			// 損益
			this.netProfit += netProfit;
			// 最大利益
			this.maxGrossProfit = Math.max(this.maxGrossProfit, netProfit);
			// 最大損失
			this.maxGrossLoss = Math.min(this.maxGrossLoss, netProfit);

			final Trade trade = new Trade(p);
			trade.cumNetProfit = this.netProfit;
			equity += netProfit;
			trade.equity = equity;
			maxEquity = Math.max(maxEquity, equity);
			trade.drawdown = equity - maxEquity;
			this.maxDrawdown = Math.min(this.maxDrawdown, trade.drawdown);
			trade.maxEquity = maxEquity;
			trade.maxDrawdown = maxDrawdown;
			this.tradeList.add(trade);
		}

		this.initialCapital = initialCapital;
		this.endingCapital = tradeList.isEmpty() ? initialCapital : tradeList.getLast().equity;

		if (discard) {
			tradeList.clear();
			tradeList = null;
		}
	}

	/**
	 * <p>指定されたポジション情報が、このクラスの統計情報の対象であるかどうかを返します。</p>
	 * 
	 * @param position ポジション情報
	 * @return 指定されたポジション情報が、統計情報の対象である場合は <code>true</code> それ以外の場合は <code>false</code>
	 */
	protected abstract boolean isStatistics(Position position);

	// ----------------------------------------------------------------------
	// トレードリスト

	/**
	 * トレードのリストを保持します。
	 */
	protected LinkedList<Trade> tradeList = new LinkedList<Trade>();

	/**
	 * トレードのリストを返します。
	 * 
	 * @return トレードのリスト
	 */
	public LinkedList<Trade> getTradeList() { return tradeList; }

	@Deprecated
	public double getBuyAndHoldNetProfit() {
		if (tradeList == null || tradeList.isEmpty())
			return 0;
		final Trade first = tradeList.getFirst();
		final Trade last = tradeList.getLast();
		final double profit = last.getExitAmount() - first.getEntryAmount();
		final double commission = first.getEntryCommission() + last.getExitCommission();
		final double slippage = first.getEntrySlippage() + last.getExitSlippage();
		return profit - (commission + slippage);
	}

	// ----------------------------------------------------------------------
	// 資産

	/**
	 * 初期資金を保持します。
	 */
	protected double initialCapital;

	/**
	 * 初期資金を返します。
	 * 
	 * @return 初期資金
	 */
	public double getInitialCapital() { return initialCapital; }

	/**
	 * 最終資金を保持します。
	 */
	protected double endingCapital;

	/**
	 * 最終資金を返します。
	 * 
	 * @return 最終資金
	 */
	public double getEndingCapital() { return endingCapital; }

	/**
	 * <p>指定された価格に対しての百分率を返します。</p>
	 * 
	 * @param amount 価格
	 * @return 指定された価格に対しての百分率
	 */
	public double getPercent(final double amount) {
		if (amount == 0.0 || initialCapital == 0.0)
			return 0.0;
		return amount / initialCapital;
	}

	// ----------------------------------------------------------------------
	// 回数/期間

	/**
	 * 回数を保持します。
	 */
	protected int count;

	/**
	 * 回数を返します。
	 * 
	 * @return 対象ポジションの発生回数
	 */
	public int getCount() { return count; }

	/**
	 * <p>回数における指定された値の平均値を返します。</p>
	 * 
	 * @param value 値
	 * @return 平均値
	 */
	public double getAverage(final double value) {
		if (value == 0.0 || count == 0)
			return 0.0;
		return value / count;
	}

	// ----------------------------------------------------------------------
	// 最大連続回数

	/**
	 * 最大連続回数を保持します。
	 */
	protected int maxConsecutiveCount;

	/**
	 * 最大連続回数を返します。
	 * 
	 * @return 最大連続回数
	 */
	public int getMaxConsecutiveCount() { return maxConsecutiveCount; }

	// ----------------------------------------------------------------------
	// 保有期間

	/**
	 * 保有期間を保持します。
	 */
	protected int hold;

	/**
	 * 保有期間を返します。
	 * 
	 * @return 保有期間
	 */
	public int getHold() { return hold; }

	/**
	 * 最大保有期間を保持します。
	 */
	protected int maxHold;

	/**
	 * 最大保有期間を返します。
	 * 
	 * @return 最大保有期間
	 */
	public int getMaxHold() { return maxHold; }

	/**
	 * 平均保有期間を返します。
	 * 
	 * @return 平均保有期間
	 */
	public double getAverageHold() { return getAverage(hold); }

	// ----------------------------------------------------------------------
	// 売上/手数料/スリッページ

	/**
	 * 売上を保持します。
	 */
	protected double grossSales;

	/**
	 * 売上を返します。
	 * 
	 * @return 売上
	 */
	public double getGrossSales() { return grossSales; }

	/**
	 * シャープレシオを返します。
	 * 
	 * @return シャープレシオ
	 */
	public double getSharpeRatio() {
		final double sum = initialCapital + grossSales;
		if (sum == 0.0 || initialCapital == 0.0)
			return 0.0;
		return sum / initialCapital;
	}

	/**
	 * 手数料を保持します。
	 */
	protected double commission;

	/**
	 * 手数料を返します。
	 * 
	 * @return 手数料
	 */
	public double getCommission() { return commission; }

	/**
	 * 手数料率を返します。
	 * 
	 * @return 手数料率
	 */
	public double getPercentCommission() { return getPercent(getCommission()); }

	/**
	 * スリッページを保持します。
	 */
	protected double slippage;

	/**
	 * スリッページを返します。
	 * 
	 * @return スリッページ
	 */
	public double getSlippage() { return slippage; }

	// ----------------------------------------------------------------------
	// 損益

	/**
	 * 損益を保持します。
	 */
	protected double netProfit;

	/**
	 * 損益を返します。
	 * 
	 * @return 損益
	 */
	public double getNetProfit() { return netProfit; }

	/**
	 * 損益率を返します。
	 * 
	 * @return 損益率
	 */
	public double getPercentNetProfit() { return getPercent(netProfit); }

	/**
	 * 平均損益を返します。
	 * 
	 * @return 平均損益
	 */
	public double getAverageNetProfit() { return getAverage(netProfit); }

	/**
	 * 平均損益率を返します。
	 * 
	 * @return 平均損益率
	 */
	public double getPercentAverageNetProfit() { return getPercent(getAverageNetProfit()); }

	// ----------------------------------------------------------------------
	// 利益

	/**
	 * 最大利益を保持します。
	 */
	protected double maxGrossProfit;

	/**
	 * 最大利益を返します。
	 * 
	 * @return 最大利益
	 */
	public double getMaxGrossProfit() { return maxGrossProfit; }

	/**
	 * 最大利益率を返します。
	 * 
	 * @return 最大利益率
	 */
	public double getPercentMaxGrossProfit() { return getPercent(maxGrossProfit); }

	// ----------------------------------------------------------------------
	// 損失

	/**
	 * 最大損失を保持します。
	 */
	protected double maxGrossLoss;

	/**
	 * 最大損失を返します。
	 * 
	 * @return 最大損失
	 */
	public double getMaxGrossLoss() { return maxGrossLoss; }

	/**
	 * 最大損失率を返します。
	 * 
	 * @return 最大損失率
	 */
	public double getPercentMaxGrossLoss() { return getPercent(maxGrossLoss); }

	// ----------------------------------------------------------------------
	// ドローダウン

	/**
	 * 最大ドローダウンを保持します。
	 */
	protected double maxDrawdown;

	/**
	 * 最大ドローダウンを返します。
	 * 
	 * @return 最大ドローダウン
	 */
	public double getMaxDrawdown() { return maxDrawdown; }

	/**
	 * 最大ドローダウン率を返します。
	 * 
	 * @return 最大ドローダウン率
	 */
	public double getPercentMaxDrawdown() { return getPercent(maxDrawdown); }

}
