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

package jp.sourceforge.orangesignal.trading.strategy;

import java.util.Date;
import java.util.LinkedList;

import jp.sourceforge.orangesignal.ta.candle.Candlestick;
import jp.sourceforge.orangesignal.ta.dataset.StandardDataset;
import jp.sourceforge.orangesignal.trading.MarketPositionType;
import jp.sourceforge.orangesignal.trading.Position;
import jp.sourceforge.orangesignal.trading.Trader;
import jp.sourceforge.orangesignal.trading.commission.Commission;
import jp.sourceforge.orangesignal.trading.order.CurrentCloseOrder;
import jp.sourceforge.orangesignal.trading.order.NextCloseOrder;
import jp.sourceforge.orangesignal.trading.order.NextLimitOrder;
import jp.sourceforge.orangesignal.trading.order.NextOpenOrder;
import jp.sourceforge.orangesignal.trading.order.NextStopOrder;
import jp.sourceforge.orangesignal.trading.order.Order;
import jp.sourceforge.orangesignal.trading.order.OrderTiming;
import jp.sourceforge.orangesignal.trading.order.OrderType;

/**
 * ストラテジーの基底クラスを提供します。
 * 
 * @author 杉澤 浩二
 */
public abstract class AbstractStrategy implements Strategy {

	/**
	 * デフォルトコンストラクタです。
	 */
	public AbstractStrategy() {}

	// ----------------------------------------------------------------------
	// ストラテジープロパティ

	/**
	 * 売買管理オブジェクトを保持します。
	 */
	private Trader trader;

	/**
	 * 売買管理オブジェクトを返します。
	 * 
	 * @return 売買管理オブジェクト
	 */
	public final Trader getTrader() { return trader; }

	@Override public final void setTrader(final Trader trader) { this.trader = trader; }

	/**
	 * デフォルトの注文方法を保持します。
	 */
	private OrderTiming defaultOrderTiming = OrderTiming.NEXT_CLOSE;

	/**
	 * デフォルトの注文方法を返します。
	 * 
	 * @return デフォルトの注文方法
	 */
	public final OrderTiming getDefaultOrderTiming() { return defaultOrderTiming; }

	@Override public final void setDefaultOrderTiming(final OrderTiming defaultOrderTiming) { this.defaultOrderTiming = defaultOrderTiming; }

	/**
	 * シンボルを保持します。
	 */
	private String symbol;

	/**
	 * シンボルを返します。
	 * 
	 * @return シンボル
	 */
	public final String getSymbol() { return symbol; }

	@Override public final void setSymbol(final String symbol) { this.symbol = symbol; }

	/**
	 * データセットを保持します。
	 */
	private StandardDataset dataset;

	/**
	 * データセットを返します。
	 * 
	 * @return データセット
	 */
	public final StandardDataset getDataset() { return dataset; }

	@Override public final void setDataset(final StandardDataset dataset) { this.dataset = dataset; }

	/**
	 * 開始データのインデックスを保持します。
	 */
	private int startDataIndex;

	/**
	 * 開始データのインデックスを返します。
	 * 
	 * @return 開始データのインデックス
	 */
	public final int getStartDataIndex() { return startDataIndex; }

	@Override public final void setStartDataIndex(final int index) { this.startDataIndex = index; }

	/**
	 * 終了データのインデックスを保持します。
	 */
	private int endDataIndex;

	/**
	 * 終了データのインデックスを返します。
	 * 
	 * @return 終了データのインデックス
	 */
	public final int getEndDataIndex() { return endDataIndex; }

	@Override public final void setEndDataIndex(final int index) { this.endDataIndex = index; }

	/**
	 * 当日データのインデックスを保持します。
	 */
	private int currentDataIndex;

	/**
	 * 当日データのインデックスを返します。
	 * 
	 * @return 当日データのインデックス
	 */
	public final int getCurrentDataIndex() { return currentDataIndex; }

	@Override public final void setCurrentDataIndex(final int index) { this.currentDataIndex = index; }

	/**
	 * 当日データの日時を保持します。
	 */
	private Date currentDate;

	/**
	 * 当日データの日時を返します。
	 * 
	 * @return 当日データの日時
	 */
	public final Date getDate() { return currentDate; }

	@Override public final void setDate(final Date date) { this.currentDate = date; }

	/**
	 * <p>ストラテジーを初期化します。</p>
	 * デフォルトの実装は何も行いません。
	 */
	@Override
	public void init() {}

	/**
	 * <p>ストラテジーを終了します。</p>
	 * デフォルトの実装は何も行いません。
	 */
	@Override
	public void terminate() {}

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

	/**
	 * 当日始値を返します。
	 * 
	 * @return 当日始値
	 */
	public final Number getOpen() { return dataset.getOpen(currentDataIndex); }

	/**
	 * 当日高値を返します。
	 * 
	 * @return 当日高値
	 */
	public final Number getHigh() { return dataset.getHigh(currentDataIndex); }

	/**
	 * 当日安値を返します。
	 * 
	 * @return 当日安値
	 */
	public final Number getLow() { return dataset.getLow(currentDataIndex); }

	/**
	 * 当日終値を返します。
	 * 
	 * @return 当日終値
	 */
	public final Number getClose() { return dataset.getClose(currentDataIndex); }

	/**
	 * 当日出来高を返します。
	 * 
	 * @return 当日出来高
	 */
	public final Number getVolume() { return dataset.getVolume(currentDataIndex); }

	/**
	 * 当日ローソク足情報を返します。
	 * 
	 * @return 当日ローソク足情報
	 */
	public final Candlestick getCandlestick() { return dataset.getCandlestick(currentDataIndex); }

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

	/**
	 * <p>エントリー中の最後のポジションを返します。</p>
	 * <p>エントリー中のポジションがない場合(フラットな状態)は <code>null</code> を返します。</p>
	 * 
	 * @return エントリー中の最後のポジション。又は <code>null</code>
	 */
	public final Position getCurrentPosition() { return trader.getCurrentPosition(symbol); }

	/**
	 * エントリー中のポジションのリストを返します。
	 * 
	 * @return エントリー中のポジションのリスト
	 */
	public final LinkedList<Position> getCurrentPositions() { return trader.getCurrentPositions(symbol); }

	/**
	 * エントリー中のポジション数を返します。
	 * 
	 * @return エントリー中のポジション数
	 */
	public final int getCurrentEntries() { return getCurrentPositions().size(); }

	/**
	 * エントリー日時を返します。
	 * 
	 * @return エントリー日時
	 */
	public final Date getEntryDate() {
		final Position position = getCurrentPosition();
		if (position == null)
			return null;
		return position.getEntryDate();
	}

	/**
	 * エントリー価格を返します。
	 * 
	 * @return エントリー価格
	 */
	public final double getEntryPrice() {
		final Position position = getCurrentPosition();
		if (position == null)
			return 0;
		return position.getEntryPrice();
	}

	/**
	 * ポジション状態の種類を返します。
	 * 
	 * @return ポジション状態の種類
	 */
	public final MarketPositionType getMarketPositionType() { return trader.getMarketPositionType(symbol); }

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

	/**
	 * 手数料情報を返します。
	 * 
	 * @return 手数料情報
	 */
	public Commission getCommission() { return trader.getCommission(); }

	/**
	 * <p>ストラテジーの名前を返します。</p>
	 * デフォルトの実装は {@link Class#getSimpleName()} で返されるクラス名をストラテジー名として返します。
	 * 
	 * @return ストラテジーの名前
	 */
	@Override public String getStrategyName() { return this.getClass().getSimpleName(); }

	// ----------------------------------------------------------------------
	// オーダー

	/**
	 * 指定された注文情報で買い注文を発行します。
	 * 
	 * @param order 注文情報
	 */
	protected final void buy(final Order order) { trader.buy(order); }

	/**
	 * 指定された注文情報で空売り注文を発行します。
	 * 
	 * @param order 注文情報
	 */
	protected final void sellShort(final Order order) { trader.sellShort(order); }

	/**
	 * 指定された注文情報で売り注文を発行します。
	 * 
	 * @param order 注文情報
	 */
	protected final void sell(final Order order) { trader.sell(order); }

	/**
	 * 指定された注文情報で買戻し注文を発行します。
	 * 
	 * @param order 注文情報
	 */
	protected final void buyToCover(final Order order) { trader.buyToCover(order); }

	/**
	 * 指定された注文方法から注文情報を構築して返します。
	 * 
	 * @param label ラベル
	 * @param quantity 数量 (デフォルト数量で注文する場合は <code>0</code> を指定)
	 * @param timing 注文方法の種類 (必須)
	 * @param price 指定価格 (指値/逆指値時必須)
	 * @param findId 決済注文ID
	 * @param findLabel 決済注文ラベル
	 * @return 注文情報
	 * @throws NullPointerException 必須パラメーターに <code>null</code> を指定した場合
	 * @throws IllegalArgumentException パラメーターが不正な場合
	 */
	protected final Order createOrder(
			final String label,
			final int quantity,
			final OrderTiming timing,
			final double price,
			final Integer findId,
			final String findLabel)
	{
		if (price == 0 && timing.getOrderType() != OrderType.MARKET)
			throw new IllegalArgumentException();
		if (price != 0 && timing.getOrderType() == OrderType.MARKET)
			throw new IllegalArgumentException();

		switch (timing) {
			case CURRENT_CLOSE:
				return new CurrentCloseOrder(symbol, label, currentDate, quantity, findId, findLabel);
			case NEXT_OPEN:
				return new NextOpenOrder(symbol, label, currentDate, quantity, findId, findLabel);
			case NEXT_CLOSE:
				return new NextCloseOrder(symbol, label, currentDate, quantity, findId, findLabel);
			case NEXT_LIMIT:
				return new NextLimitOrder(symbol, label, currentDate, price, quantity, findId, findLabel);
			case NEXT_STOP:
				return new NextStopOrder(symbol, label, currentDate, price, quantity, findId, findLabel);
			default:
				throw new IllegalArgumentException("Unknown OrderTiming");
		}
	}

}
