package singlejartest;

/*
 * Author:Lawrence Reid
 * Version 1.0.2
 * Creation Date: 10.4.2012
 * Last edited: 14:4.2012
 * Description: 
 * This is a small strategy that I wrote to make the ICHIMOKU indicator more understandable. The calculateIndicator method is not very well 
 * documented in my opinion and the best way to see how it works is through examples. Many people in the forum seem to think that the values
 * returned by the calculate indicator method are incorrect because the values on the chart seem different. This is why I used the 
 * calculateIndicator with the FlatFilter as a parameter. Ensure that this is setup the same as your JForex Client.
 * 
 * Change log:
 * 1.0.1 Created strategy and tried to get multiple bars returned and displayed in the console
 * 1.0.2 
 */
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;

import com.dukascopy.api.*;
import com.dukascopy.api.indicators.*;
import com.dukascopy.api.IIndicators.AppliedPrice;

@RequiresFullAccess
public class TestIchimokuCalc implements IStrategy {
	final String version = "1.0.2";
	private int TENKAN = 0;
	private int KIJUN = 1;
	private int CHINKOU = 2;
	private int SENOKU_A = 3;
	private int SENOKU_B = 4;
	private String indName = "ICHIMOKU";
	private static String[] pivotLevelNames = { "P", "R1", "S1", "R2", "S2",
			"R3", "S3" };

	@Configurable("Instrument")
	private Instrument selectedInstrument = Instrument.EURUSD;
	@Configurable("Tenkan sen")
	private int tenkanPeriod = 9;
	@Configurable("Kijun sen")
	private int kijunPeriod = 26;
	@Configurable("Senko span")
	private int senkouPeriod = 52;
	@Configurable("period")
	public Period selectedPeriod = Period.ONE_MIN;
	@Configurable("AppliedPrice")
	public AppliedPrice appliedPrice = AppliedPrice.CLOSE;
	@Configurable("OfferSide")
	public OfferSide selectedOfferSide = OfferSide.BID;
	@Configurable("Flat Filter")
	private Filter indFilter = Filter.ALL_FLATS;
	@Configurable("ICHI Bars to return from last completed bar")
	private int barCount = 7;;

	private IContext context;
	private IConsole console;
	private IHistory history;
	private IIndicators indicators;

	public void onStart(IContext context) throws JFException {
		this.console = context.getConsole();
		this.history = context.getHistory();
		this.indicators = context.getIndicators();
		this.context = context;

		this.console = context.getConsole();
		this.indicators = context.getIndicators();

		IIndicator indicator = indicators.getIndicator(indName);

		// 1 - input related parameters - we make arrays of 1 element since we
		// have 1 input
		AppliedPrice[] inputTypes = new AppliedPrice[] { appliedPrice };
		OfferSide[] offerSides = new OfferSide[] { selectedOfferSide };

		// 2 - optional inputs - we have three of them - Jaw Time Period, Teeth
		// Time Period, Lips Time Period. Use default values.
		Object[] optParams = new Object[] { tenkanPeriod, senkouPeriod,
				senkouPeriod };

		try {
			int shift = 1;
			Object[] outputArray = indicators.calculateIndicator(
					selectedInstrument, selectedPeriod, offerSides, indName,
					inputTypes, optParams, shift);
			System.out
					.println("Output for calculateIndicator without Filter parameter for last completed candle");
			println(outputArray);

		} catch (JFException e) {
			e.printStackTrace();
		}

		try {

			Object[] outputArray = indicators
					.calculateIndicator(
							selectedInstrument,
							selectedPeriod,
							new OfferSide[] { selectedOfferSide },
							"ICHIMOKU",
							new IIndicators.AppliedPrice[] { IIndicators.AppliedPrice.CLOSE },
							optParams,
							indFilter,
							barCount,
							this.history
									.getPreviousBarStart(
											selectedPeriod,
											this.history
													.getTimeOfLastTick(selectedInstrument)),
							0);
			System.out
					.println("Output for calculateIndicator with Filter parameter for last "
							+ (String.format("%d", barCount))
							+ " completed candles");
			println(outputArray);
			printf("Output for getIchimokuValue with Filter parameter for last completed candle");
			printf("TENKAN: %.5f ",
					getIchimokuValue(outputArray, barCount - 1, TENKAN));
			printf("KIJUN: %.5f ",
					getIchimokuValue(outputArray, barCount - 1, KIJUN));
			printf("CHINKOU: %.5f ",
					getIchimokuValue(outputArray, barCount - 1, CHINKOU));
			printf("SENOKU_A: %.5f ",
					getIchimokuValue(outputArray, barCount - 1, SENOKU_A));
			printf("SENOKU_B: %.5f ",
					getIchimokuValue(outputArray, barCount - 1, SENOKU_B));
			printf("\n");

		} catch (JFException e) {
			e.printStackTrace();
		}
		/*
		 * try {
		 * 
		 * Object[] outputArray = indicators .calculateIndicator(
		 * selectedInstrument, selectedPeriod, new OfferSide[] {
		 * selectedOfferSide }, "PIVOT", new IIndicators.AppliedPrice[] {
		 * IIndicators.AppliedPrice.CLOSE }, new Object[] { 0 }, indFilter,
		 * barCount, this.history .getPreviousBarStart( selectedPeriod,
		 * this.history .getTimeOfLastTick(selectedInstrument)), 0); System.out
		 * .
		 * println("Output for calculateIndicator with Filter parameter for last "
		 * + (String.format("%d", barCount)) + " completed candles");
		 * print(outputArray);
		 * 
		 * } catch (JFException e) { e.printStackTrace(); }
		 */
		boolean showHistoricalLevels = true;
		double[] pivotDaily = indicators.pivot(selectedInstrument,
				Period.DAILY, OfferSide.BID, 7, showHistoricalLevels, 0);
		double[] pivotWeekly = indicators.pivot(selectedInstrument,
				Period.WEEKLY, OfferSide.BID, 8, showHistoricalLevels, 0);
		double[] pivotMonthly = indicators.pivot(selectedInstrument,
				Period.MONTHLY, OfferSide.BID, 9, showHistoricalLevels, 0);

		println("pivotMonthly " + arrayToString(pivotMonthly));
		println("pivotWeekly  " + arrayToString(pivotWeekly));
		println("pivotDaily   " + arrayToString(pivotDaily));

		IIndicator pivotIndicator = indicators.getIndicator("PIVOT");

		printIndicatorInfos(pivotIndicator);

		try {
			Object[] PIVOT = indicators.calculateIndicator(selectedInstrument,
					selectedPeriod, new OfferSide[] { selectedOfferSide,
							selectedOfferSide }, "PIVOT",
					new IIndicators.AppliedPrice[] {
							IIndicators.AppliedPrice.CLOSE,
							IIndicators.AppliedPrice.CLOSE }, new Object[] { 7,
							showHistoricalLevels }, 1);
			println(PIVOT);

			Object[] outputArray = indicators
					.calculateIndicator(
							selectedInstrument,
							selectedPeriod,
							new OfferSide[] { selectedOfferSide,
									selectedOfferSide },
							"PIVOT",
							new IIndicators.AppliedPrice[] {
									IIndicators.AppliedPrice.CLOSE,
									IIndicators.AppliedPrice.CLOSE },
							new Object[] { 7/* Daily */, showHistoricalLevels },
							indFilter,
							barCount,
							this.history
									.getPreviousBarStart(
											selectedPeriod,
											this.history
													.getTimeOfLastTick(selectedInstrument)),
							0);

			println(outputArray);

		} catch (JFException e) {
			e.printStackTrace();
		}

		this.context.stop();
	}

	public static String arrayToString(double[] arr) {
		String str = "";
		for (int r = 0; r < arr.length; r++) {
			str += " " + pivotLevelNames[r] + ": "
					+ (new DecimalFormat("0.000000")).format(arr[r]) + "; ";
		}
		return str;
	}

	private double getIchimokuValue(Object[] objectArray, int nBar, int ichiName) {
		try {
			return (((double[]) objectArray[ichiName])[nBar]);
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println(e.toString());

		} catch (Exception e) {

			System.out.println(e.toString());
		}
		return Double.NaN;

	}

	private void printf(String format, Object... args) {
		console.getOut().print(String.format(format, args));
	}

	/**************** debug print functions ***********************/
	private void println(Object... o) {
		print(o);
		console.getOut().println();
	}

	private void print(Object... o) {
		for (Object ob : o) {
			if (ob instanceof Double) {
				print(toStr((Double) ob)+ " ");
			} else if (ob instanceof double[]) {
				println((double[]) ob);
			} else if (ob instanceof double[]) {
				print((double[][]) ob);
			} else if (ob instanceof Long) {
				print(toStr((Long) ob));
			} else if (ob instanceof IBar) {
				println((IBar) ob);
			} else {
				print((ob) + " ");
			}
		}
	}

	private void println(Object o) {
		console.getOut().println(o);
	}

	private void print(Object o) {
		console.getOut().print(o);
	}

	private void println(double d) {
		println(toStr(d));
	}

	private void print(double d) {
		print(toStr(d));
	}

	private void println(double[] arr) {
		println(toStr(arr));
	}

	private void print(double[] arr) {
		print(toStr(arr));
	}

	private void println(double[][] arr) {
		println(toStr(arr));
	}

	private void print(double[][] arr) {
		print(toStr(arr));
	}

	private void printIndicatorInfos(IIndicator ind) {
		for (int i = 0; i < ind.getIndicatorInfo().getNumberOfInputs(); i++) {
			println(ind.getIndicatorInfo().getName() + " Input "
					+ ind.getInputParameterInfo(i).getName() + " "
					+ ind.getInputParameterInfo(i).getType());
		}
		for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOptionalInputs(); i++) {
			println(ind.getIndicatorInfo().getName() + " Opt Input "
					+ ind.getOptInputParameterInfo(i).getName() + " "
					+ ind.getOptInputParameterInfo(i).getType());
		}
		for (int i = 0; i < ind.getIndicatorInfo().getNumberOfOutputs(); i++) {
			println(ind.getIndicatorInfo().getName() + " Output "
					+ ind.getOutputParameterInfo(i).getName() + " "
					+ ind.getOutputParameterInfo(i).getType());
		}
		console.getOut().println();
	}

	public String toStr(double[] arr) {
		String str = "";
		for (int r = 0; r < arr.length; r++) {
			str += "[" + r + "] "
					+ (new DecimalFormat("#.000000")).format(arr[r]) + "; ";
		}
		return str;
	}

	public String toStr(double[][] arr) {
		String str = "";
		if (arr == null) {
			return "null";
		}
		for (int r = 0; r < arr.length; r++) {
			for (int c = 0; c < arr[r].length; c++) {
				str += "[" + r + "][" + c + "] "
						+ (new DecimalFormat("#.000000")).format(arr[r][c]);
			}
			str += "; ";
		}
		return str;
	}

	public String toStr(double d) {
		return (new DecimalFormat("#.000000")).format(d);
	}

	public String toStr(long time) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") {
			{
				setTimeZone(TimeZone.getTimeZone("GMT"));
			}
		};
		return sdf.format(time);
	}

	private void printTime(Long time) {
		console.getOut().println(toStr(time));
	}

	private void print(IBar bar) {
		console.getOut().println(
				toStr(bar.getTime()) + "  O:" + bar.getOpen() + " C:"
						+ bar.getClose() + " H:" + bar.getHigh() + " L:"
						+ bar.getLow());
	}

	private void print(List<IBar> bars) {
		console.getOut().println("<<<");
		for (IBar bar : bars) {
			print(bar);
		}
		console.getOut().println("-");
	}

	public void onAccount(IAccount account) throws JFException {
	}

	public void onMessage(IMessage message) throws JFException {
	}

	public void onStop() throws JFException {
	}

	public void onTick(Instrument instrument, ITick tick) throws JFException {
	}

	public void onBar(Instrument instrument, Period period, IBar askBar,
			IBar bidBar) throws JFException {
	}

	/*
	 * print("[0]Tenkan Sen=%.5f " , ((double[])ichiResult[0])[0]);
	 * print("[1]Ki-jun Sen=%.5f " , ((double[])ichiResult[1])[0]);
	 * print("[2]Chinkou Span=%.5f " , ((double[])ichiResult[2])[0]);
	 * print("[3]Senkou A=%.5f " , ((double[])ichiResult[3])[0]);
	 * print("[4]Senkou B=%.5f " , ((double[])ichiResult[4])[0]);
	 * console.getOut().println("");
	 */

}