package jp.sf.orangesignal.trading;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import jp.sf.orangesignal.csv.CsvConfig;
import jp.sf.orangesignal.csv.handlers.ColumnNameMappingBeanListHandler;
import jp.sf.orangesignal.ta.data.CsvDataConfig;
import jp.sf.orangesignal.ta.data.CsvPriceLoader;
import jp.sf.orangesignal.ta.data.model.Price;
import jp.sf.orangesignal.trading.commission.FreeCommission;
import jp.sf.orangesignal.trading.data.Dataset;
import jp.sf.orangesignal.trading.data.FileDatasetLoader;
import jp.sf.orangesignal.trading.order.NextOpenOrder;

import org.junit.BeforeClass;
import org.junit.Test;

public class VirtualTraderTest {

	private static final String SYMBOL = "n225";

	private static Dataset dataset;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		final CsvDataConfig csvDataConfig = new CsvDataConfig();
		csvDataConfig.setPath("src/test/resources/data");
		csvDataConfig.setEncoding("UTF-8");

		final CsvConfig csvConfig = new CsvConfig();
		csvConfig.setIgnoreEmptyLines(true);
		csvDataConfig.setCsvConfig(csvConfig);

		final CsvPriceLoader loader = new CsvPriceLoader();
		loader.setConfig(csvDataConfig);
		loader.setHandler(
			new ColumnNameMappingBeanListHandler<Price>(Price.class)
				.addColumn("日付", "date", new SimpleDateFormat("yyyy/MM/dd"))
				.addColumn("始値", "open")
				.addColumn("高値", "high")
				.addColumn("安値", "low")
				.addColumn("終値", "close")
				.addColumn("出来高", "volume")
		);

		final FileDatasetLoader fileDatasetLoader = new FileDatasetLoader();
		fileDatasetLoader.setLoader(loader);
		dataset = fileDatasetLoader.load(SYMBOL);
	}

	@Test
	public void testVirtualTrader() {
		new VirtualTrader();
		assertTrue(true);
	}

	@Test
	public void testVirtualTraderTimeSeriesDatasetDouble() {
		new VirtualTrader(dataset, 1000000);
		assertTrue(true);
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetDoubleIllegalArgumentException() {
		new VirtualTrader(null, 1000000);
	}

	@Test
	public void testVirtualTraderTimeSeriesDatasetVirtualAccount() {
		new VirtualTrader(dataset, new VirtualAccount(1000000));
		assertTrue(true);
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountIllegalArgumentExceptionByDataset() {
		new VirtualTrader(null, new VirtualAccount(1000000));
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountIllegalArgumentExceptionByAccount() {
		new VirtualTrader(dataset, null);
	}

	@Test
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountCommission() {
		new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission());
		assertTrue(true);
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountCommissionIllegalArgumentExceptionByDataset() {
		new VirtualTrader(null, new VirtualAccount(1000000), new FreeCommission());
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountCommissionIllegalArgumentExceptionByAccount() {
		new VirtualTrader(dataset, null, new FreeCommission());
	}

	@Test(expected = IllegalArgumentException.class)
	public void testVirtualTraderTimeSeriesDatasetVirtualAccountCommissionIllegalArgumentExceptionByCommission() {
		new VirtualTrader(dataset, new VirtualAccount(1000000), null);
	}

	@Test
	public void testReset() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		trader.setAccount(new VirtualAccount(1000000));

		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2, 1);
		datasetMap.put(SYMBOL, dataset);
		trader.setDatasetMap(datasetMap);

		final Calendar calendar = Calendar.getInstance(Locale.JAPAN);
		calendar.set(1998, Calendar.JANUARY, 30);
		trader.buy(new NextOpenOrder(SYMBOL, "L1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 2);
		trader.sell(new NextOpenOrder(SYMBOL, "S1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 3);
		trader.buy(new NextOpenOrder(SYMBOL, "L2", calendar.getTime(), 0, null, null));

		assertTrue(trader.getAccount().getCash() != 1000000D);
		assertTrue(trader.getMarketPositionType(SYMBOL) != MarketPositionType.FLAT);
		assertNotNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getPositions().size() > 0);

		trader.reset();

		assertTrue(trader.getAccount().getCash() == 1000000D);
		assertTrue(trader.getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
		assertNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getPositions().isEmpty());
	}

	@Test
	public void testGetCommission() {
		assertNull(new VirtualTrader().getCommission());
		assertNotNull(new VirtualTrader(dataset, new VirtualAccount(1000000)).getCommission());
		assertNotNull(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getCommission());
	}

	@Test
	public void testSetCommission() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		assertNotNull(trader.getCommission());
	}

	@Test
	public void testGetSlippage() {
		assertTrue(new VirtualTrader().getSlippage() == 0D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getSlippage() == 0D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getSlippage() == 0D);
	}

	@Test
	public void testSetSlippage() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setSlippage(100);
		assertTrue(trader.getSlippage() == 100D);
	}

	@Test
	public void testGetAccount() {
		assertNull(new VirtualTrader().getAccount());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getAccount().getCash() == 1000000D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getAccount().getCash() == 1000000D);
	}

	@Test
	public void testSetAccount() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setAccount(new VirtualAccount(1000000));
		assertTrue(trader.getAccount().getCash() == 1000000D);
	}

	@Test
	public void testGetInitialCapital() {
		assertTrue(new VirtualTrader().getInitialCapital() == 0D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getInitialCapital() == 1000000D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getInitialCapital() == 1000000D);
	}

	@Test
	public void testGetPositionLimit() {
		assertTrue(new VirtualTrader().getPositionLimit() == 0D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositionLimit() == 0D);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositionLimit() == 0D);
	}

	@Test
	public void testSetPositionLimit() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setPositionLimit(10);
		assertTrue(trader.getPositionLimit() == 10);
	}

	@Test
	public void testGetDefaultQuantity() {
		assertTrue(new VirtualTrader().getDefaultQuantity() == 1);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getDefaultQuantity() == 1);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getDefaultQuantity() == 1);
	}

	@Test
	public void testSetDefaultQuantity() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setDefaultQuantity(10);
		assertTrue(trader.getDefaultQuantity() == 10);
	}

	@Test
	public void testGetTradeType() {
		assertTrue(new VirtualTrader().getTradeType() == TradeType.LONG_AND_SHORT_AND_REVERSE);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getTradeType() == TradeType.LONG_AND_SHORT_AND_REVERSE);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getTradeType() == TradeType.LONG_AND_SHORT_AND_REVERSE);
	}

	@Test
	public void testSetTradeType() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setTradeType(TradeType.LONG_AND_SHORT);
		assertTrue(trader.getTradeType() == TradeType.LONG_AND_SHORT);
	}

	@Test
	public void testSetDatasetMap() {
		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2);
		datasetMap.put(SYMBOL, dataset);
		final VirtualTrader trader = new VirtualTrader();
		trader.setDatasetMap(datasetMap);
		assertTrue(true);
	}

	@Test
	public void testSetDataset() {
		new VirtualTrader().setDataset(SYMBOL, dataset);
		assertTrue(true);
	}

	@Test
	public void testGetMarketPositionType() {
		assertTrue(new VirtualTrader().getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
	}

	@Test
	public void testGetCurrentPosition() {
		assertNull(new VirtualTrader().getCurrentPosition(SYMBOL));
		assertNull(new VirtualTrader(dataset, new VirtualAccount(1000000)).getCurrentPosition(SYMBOL));
		assertNull(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getCurrentPosition(SYMBOL));
	}

	@Test
	public void testGetCurrentPositions() {
		assertTrue(new VirtualTrader().getCurrentPositions(SYMBOL).isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getCurrentPositions(SYMBOL).isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getCurrentPositions(SYMBOL).isEmpty());
	}

	@Test
	public void testGetPositions() {
		assertTrue(new VirtualTrader().getPositions().isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositions().isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositions().isEmpty());
	}

	@Test
	public void testGetPositionsBySymbol() {
		assertTrue(new VirtualTrader().getPositionsBySymbol(SYMBOL).isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositionsBySymbol(SYMBOL).isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositionsBySymbol(SYMBOL).isEmpty());
	}

	@Test
	public void testGetPositionsByEntryLabel() {
		assertTrue(new VirtualTrader().getPositionsByEntryLabel("MovingAverage2LineCrossLongEntry").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositionsByEntryLabel("MovingAverage2LineCrossLongEntry").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositionsByEntryLabel("MovingAverage2LineCrossLongEntry").isEmpty());
	}

	@Test
	public void testGetPositionsByExitLabel() {
		assertTrue(new VirtualTrader().getPositionsByExitLabel("InactivityStop").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositionsByExitLabel("InactivityStop").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositionsByExitLabel("InactivityStop").isEmpty());
	}

	@Test
	public void testGetPositionsByLabel() {
		assertTrue(new VirtualTrader().getPositionsByLabel("L1").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000)).getPositionsByLabel("L1").isEmpty());
		assertTrue(new VirtualTrader(dataset, new VirtualAccount(1000000), new FreeCommission()).getPositionsByLabel("L1").isEmpty());
	}

	@Test
	public void testBuy() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		trader.setAccount(new VirtualAccount(1000000));

		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2, 1);
		datasetMap.put(SYMBOL, dataset);
		trader.setDatasetMap(datasetMap);

		final Calendar calendar = Calendar.getInstance(Locale.JAPAN);
		calendar.set(1998, Calendar.JANUARY, 30);
		trader.buy(new NextOpenOrder(SYMBOL, "L1", calendar.getTime(), 0, null, null));

		assertTrue(trader.getMarketPositionType(SYMBOL) == MarketPositionType.LONG);
		assertNotNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getCurrentPositions(SYMBOL).size() == 1);
		assertTrue(trader.getPositions().isEmpty());
	}

	@Test
	public void testSellShort() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		trader.setAccount(new VirtualAccount(1000000));

		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2, 1);
		datasetMap.put(SYMBOL, dataset);
		trader.setDatasetMap(datasetMap);

		final Calendar calendar = Calendar.getInstance(Locale.JAPAN);
		calendar.set(1998, Calendar.JANUARY, 30);
		trader.sellShort(new NextOpenOrder(SYMBOL, "S1", calendar.getTime(), 0, null, null));

		assertTrue(trader.getMarketPositionType(SYMBOL) == MarketPositionType.SHORT);
		assertNotNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getCurrentPositions(SYMBOL).size() == 1);
		assertTrue(trader.getPositions().isEmpty());
	}

	@Test
	public void testSell() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		trader.setAccount(new VirtualAccount(1000000));

		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2, 1);
		datasetMap.put(SYMBOL, dataset);
		trader.setDatasetMap(datasetMap);

		final Calendar calendar = Calendar.getInstance(Locale.JAPAN);
		calendar.set(1998, Calendar.JANUARY, 30);
		trader.buy(new NextOpenOrder(SYMBOL, "L1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 2);
		trader.sell(new NextOpenOrder(SYMBOL, "S1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 3);
		trader.buy(new NextOpenOrder(SYMBOL, "L2", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 4);
		trader.sell(new NextOpenOrder(SYMBOL, "S2", calendar.getTime(), 0, null, null));

		assertTrue(trader.getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
		assertNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getCurrentPositions(SYMBOL).isEmpty());
		assertTrue(trader.getPositions().size() == 2);
		assertTrue(trader.getPositionsBySymbol(SYMBOL).size() == 2);
		assertTrue(trader.getPositionsByEntryLabel("L1").size() == 1);
		assertTrue(trader.getPositionsByEntryLabel("L2").size() == 1);
		assertTrue(trader.getPositionsByExitLabel("S1").size() == 1);
		assertTrue(trader.getPositionsByExitLabel("S2").size() == 1);
		assertTrue(trader.getPositionsByLabel("L1").size() == 1);
		assertTrue(trader.getPositionsByLabel("L2").size() == 1);
		assertTrue(trader.getPositionsByLabel("S1").size() == 1);
		assertTrue(trader.getPositionsByLabel("S2").size() == 1);
	}

	@Test
	public void testBuyToCover() {
		final VirtualTrader trader = new VirtualTrader();
		trader.setCommission(new FreeCommission());
		trader.setAccount(new VirtualAccount(1000000));

		final Map<String, Dataset> datasetMap = new HashMap<String, Dataset>(2, 1);
		datasetMap.put(SYMBOL, dataset);
		trader.setDatasetMap(datasetMap);

		final Calendar calendar = Calendar.getInstance(Locale.JAPAN);
		calendar.set(1998, Calendar.JANUARY, 30);
		trader.sellShort(new NextOpenOrder(SYMBOL, "S1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 2);
		trader.buyToCover(new NextOpenOrder(SYMBOL, "L1", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 3);
		trader.sellShort(new NextOpenOrder(SYMBOL, "S2", calendar.getTime(), 0, null, null));
		calendar.set(1998, Calendar.FEBRUARY, 4);
		trader.buyToCover(new NextOpenOrder(SYMBOL, "L2", calendar.getTime(), 0, null, null));

		assertTrue(trader.getMarketPositionType(SYMBOL) == MarketPositionType.FLAT);
		assertNull(trader.getCurrentPosition(SYMBOL));
		assertTrue(trader.getCurrentPositions(SYMBOL).isEmpty());
		assertTrue(trader.getPositions().size() == 2);
		assertTrue(trader.getPositionsBySymbol(SYMBOL).size() == 2);
		assertTrue(trader.getPositionsByEntryLabel("S1").size() == 1);
		assertTrue(trader.getPositionsByEntryLabel("S2").size() == 1);
		assertTrue(trader.getPositionsByExitLabel("L1").size() == 1);
		assertTrue(trader.getPositionsByExitLabel("L2").size() == 1);
		assertTrue(trader.getPositionsByLabel("S1").size() == 1);
		assertTrue(trader.getPositionsByLabel("S2").size() == 1);
		assertTrue(trader.getPositionsByLabel("L1").size() == 1);
		assertTrue(trader.getPositionsByLabel("L2").size() == 1);
	}

}
