#include "sierrachart.h"

/*==========================================================================*/
SCSFExport scsf_TradingSystemBasedOnAlertCondition(SCStudyInterfaceRef sc)
{
	SCInputRef Input_Enabled = sc.Input[0];
	SCInputRef Input_NumBarsToCalculate = sc.Input[1];

	SCInputRef Input_DrawStyleOffsetType = sc.Input[2];
	SCInputRef Input_PercentageOrTicksOffset = sc.Input[3];

	SCInputRef Input_OrderActionOnAlert = sc.Input[4];

	SCInputRef Input_EvaluateOnBarCloseOnly = sc.Input[5];
	SCInputRef Input_SendTradeOrdersToTradeService = sc.Input[6];
	SCInputRef Input_MaximumPositionAllowed = sc.Input[7];
	SCInputRef Input_MaximumLongTradesPerDay = sc.Input[8];
	SCInputRef Input_MaximumShortTradesPerDay = sc.Input[9];
	SCInputRef Input_EnableDebuggingOutput = sc.Input[10];
	SCInputRef Input_CancelAllWorkingOrdersOnExit = sc.Input[11];

	SCInputRef Input_AllowTradingOnlyDuringTimeRange = sc.Input[12];
	SCInputRef Input_StartTimeForAllowedTimeRange = sc.Input[13];
	SCInputRef Input_EndTimeForAllowedTimeRange = sc.Input[14];
	SCInputRef Input_AllowOnlyOneTradePerBar = sc.Input[15];
	SCInputRef Input_Version = sc.Input[16];
	SCInputRef Input_SupportReversals = sc.Input[17];
	SCInputRef Input_AllowMultipleEntriesInSameDirection = sc.Input[18];
	SCInputRef Input_AllowEntryWithWorkingOrders = sc.Input[19];
	SCInputRef Input_ControlBarButtonNumberForEnableDisable = sc.Input[20];


	SCSubgraphRef BuyEntry = sc.Subgraph[0];
	SCSubgraphRef SellEntry = sc.Subgraph[1];

	if (sc.SetDefaults)
	{
		// Set the configuration and defaults

		sc.GraphName = "Trading System Based on Alert Condition";

		sc.StudyDescription = "";

		sc.AutoLoop = 0;  // manual looping
		sc.GraphRegion = 0;
		sc.CalculationPrecedence = LOW_PREC_LEVEL;

		Input_Enabled.Name = "Enabled";
		Input_Enabled.SetYesNo(false);

		Input_NumBarsToCalculate.Name = "Number of Bars to Calculate";
		Input_NumBarsToCalculate.SetInt(2000);
		Input_NumBarsToCalculate.SetIntLimits(1, MAX_STUDY_LENGTH);

		//Input_DrawStyleOffsetType.Name = "Draw Style Offset Type";
		//Input_DrawStyleOffsetType.SetCustomInputStrings("Percentage;Number of Ticks");

		//Input_PercentageOrTicksOffset.Name = "Percentage or Ticks Offset";
		//Input_PercentageOrTicksOffset.SetFloat(0);

		Input_OrderActionOnAlert.Name = "Order Action on Alert";
		Input_OrderActionOnAlert.SetCustomInputStrings("Buy Entry;Buy Exit;Sell Entry;Sell Exit");
		Input_OrderActionOnAlert.SetCustomInputIndex(0);

		Input_EvaluateOnBarCloseOnly.Name = "Evaluate on Bar Close Only";
		Input_EvaluateOnBarCloseOnly.SetYesNo(true);

		Input_SendTradeOrdersToTradeService.Name = "Send Trade Orders to Trade Service";
		Input_SendTradeOrdersToTradeService.SetYesNo(false);

		Input_MaximumPositionAllowed.Name = "Maximum Position Allowed";
		Input_MaximumPositionAllowed.SetInt(1);

		Input_MaximumLongTradesPerDay.Name = "Maximum Long Trades Per Day";
		Input_MaximumLongTradesPerDay.SetInt(2);

		Input_MaximumShortTradesPerDay.Name = "Maximum Short Trades Per Day";
		Input_MaximumShortTradesPerDay.SetInt(2);

		Input_CancelAllWorkingOrdersOnExit.Name = "Cancel All Working Orders On Exit";
		Input_CancelAllWorkingOrdersOnExit.SetYesNo(false);

		Input_EnableDebuggingOutput.Name = "Enable Debugging Output";
		Input_EnableDebuggingOutput.SetYesNo(false);

		Input_AllowTradingOnlyDuringTimeRange.Name = "Allow Trading Only During Time Range";
		Input_AllowTradingOnlyDuringTimeRange.SetYesNo(false);

		Input_StartTimeForAllowedTimeRange.Name = "Start Time For Allowed Time Range";
		Input_StartTimeForAllowedTimeRange.SetTime(HMS_TIME(0, 0, 0));

		Input_EndTimeForAllowedTimeRange.Name = "End Time For Allowed Time Range";
		Input_EndTimeForAllowedTimeRange.SetTime(HMS_TIME(23, 59, 59));

		Input_AllowOnlyOneTradePerBar.Name = "Allow Only One Trade per Bar";
		Input_AllowOnlyOneTradePerBar.SetYesNo(true);

		Input_SupportReversals.Name = "Support Reversals";
		Input_SupportReversals.SetYesNo(false);

		Input_AllowMultipleEntriesInSameDirection.Name = "Allow Multiple Entries In Same Direction"; 
		Input_AllowMultipleEntriesInSameDirection.SetYesNo(false);

		Input_AllowEntryWithWorkingOrders.Name = "Allow Entry With Working Orders";
		Input_AllowEntryWithWorkingOrders.SetYesNo(false);

		Input_ControlBarButtonNumberForEnableDisable.Name = "ACS Control Bar Button # for Enable/Disable";
		Input_ControlBarButtonNumberForEnableDisable.SetInt(1);
		Input_ControlBarButtonNumberForEnableDisable.SetIntLimits(1, MAX_ACS_BUTTONS);

		Input_Version.SetInt(2);

		BuyEntry.Name = "Buy";
		BuyEntry.DrawStyle = DRAWSTYLE_POINT_ON_HIGH;
		BuyEntry.LineWidth = 5;

		SellEntry.Name = "Sell";
		SellEntry.DrawStyle = DRAWSTYLE_POINT_ON_LOW;
		SellEntry.LineWidth = 5;

		sc.AllowMultipleEntriesInSameDirection = false;
		sc.SupportReversals = false;
		sc.AllowOppositeEntryWithOpposingPositionOrOrders = false;

		sc.SupportAttachedOrdersForTrading = false;
		sc.UseGUIAttachedOrderSetting = true;
		sc.CancelAllOrdersOnEntriesAndReversals = false;
		sc.AllowEntryWithWorkingOrders = false;

		// Only 1 trade for each Order Action type is allowed per bar.
		sc.AllowOnlyOneTradePerBar = true;

		//This should be set to true when a trading study uses trading functions.
		sc.MaintainTradeStatisticsAndTradesData = true;

		return;
	}

	if (Input_Version.GetInt() < 2)
	{
		Input_AllowOnlyOneTradePerBar.SetYesNo(true);
		Input_Version.SetInt(2);
	}

	sc.AllowOnlyOneTradePerBar = Input_AllowOnlyOneTradePerBar.GetYesNo();
	sc.SupportReversals = false;
	sc.MaximumPositionAllowed = Input_MaximumPositionAllowed.GetInt();
	sc.SendOrdersToTradeService = Input_SendTradeOrdersToTradeService.GetYesNo();
	sc.CancelAllWorkingOrdersOnExit = Input_CancelAllWorkingOrdersOnExit.GetYesNo();

	sc.SupportReversals = Input_SupportReversals.GetYesNo();
	sc.AllowMultipleEntriesInSameDirection = Input_AllowMultipleEntriesInSameDirection.GetYesNo();
	sc.AllowEntryWithWorkingOrders = Input_AllowEntryWithWorkingOrders.GetYesNo();

	if (sc.MenuEventID == Input_ControlBarButtonNumberForEnableDisable.GetInt())
	{
		const int ButtonState = (sc.PointerEventType == SC_ACS_BUTTON_ON) ? 1 : 0;
		Input_Enabled.SetYesNo(ButtonState);
	}


	if (!Input_Enabled.GetYesNo() || sc.LastCallToFunction)
		return;

	int& r_LastDebugMessageType = sc.GetPersistentInt(1);
	int& r_PriorFormulaState = sc.GetPersistentInt(2);

	if (sc.IsFullRecalculation)
	{
		r_LastDebugMessageType = 0;
		r_PriorFormulaState = 0;

		// set ACS Tool Control Bar tool tip
		sc.SetCustomStudyControlBarButtonHoverText(Input_ControlBarButtonNumberForEnableDisable.GetInt(), "Enable/Disable Trade System Based on Alert Condition");
		
		sc.SetCustomStudyControlBarButtonEnable(Input_ControlBarButtonNumberForEnableDisable.GetInt(), Input_Enabled.GetBoolean());
	}


	// Figure out the last bar to evaluate
	int LastIndex = sc.ArraySize - 1;
	if (Input_EvaluateOnBarCloseOnly.GetYesNo())
		--LastIndex;

	const int EarliestIndexToCalculate = LastIndex - Input_NumBarsToCalculate.GetInt() + 1;

	int CalculationStartIndex = sc.GetCalculationStartIndexForStudy();

	if (CalculationStartIndex < EarliestIndexToCalculate)
		CalculationStartIndex = EarliestIndexToCalculate;

	sc.EarliestUpdateSubgraphDataArrayIndex = CalculationStartIndex;

	s_SCPositionData PositionData;

	s_ACSTradeStatistics TradeStatistics;

	SCString TradeNotAllowedReason;

	bool IsTradeAllowed = !sc.IsFullRecalculation && !sc.DownloadingHistoricalData;
	if (Input_EnableDebuggingOutput.GetYesNo())
	{
		if (sc.IsFullRecalculation)
			TradeNotAllowedReason = "Is full recalculation";
		else if (sc.DownloadingHistoricalData)
			TradeNotAllowedReason = "Downloading historical data";
	}

	if (Input_AllowTradingOnlyDuringTimeRange.GetYesNo())
	{
		const SCDateTime  LastBarDateTime = sc.LatestDateTimeForLastBar;

		const int LastBarTimeInSeconds = LastBarDateTime.GetTimeInSeconds();

		bool LastIndexIsInAllowedTimeRange = false;

		if (Input_StartTimeForAllowedTimeRange.GetTime() <= Input_EndTimeForAllowedTimeRange.GetTime())
		{
			LastIndexIsInAllowedTimeRange = (LastBarTimeInSeconds >= Input_StartTimeForAllowedTimeRange.GetTime()
				&& LastBarTimeInSeconds <= Input_EndTimeForAllowedTimeRange.GetTime()
				);
		}
		else //Reversed times.
		{
			LastIndexIsInAllowedTimeRange = (LastBarTimeInSeconds >= Input_StartTimeForAllowedTimeRange.GetTime()
				|| LastBarTimeInSeconds <= Input_EndTimeForAllowedTimeRange.GetTime()
				);
		}

		if (!LastIndexIsInAllowedTimeRange)
		{
			IsTradeAllowed = false;
			TradeNotAllowedReason = "Not within allowed time range";
		}
	}

	bool ParseAndSetFormula = sc.IsFullRecalculation;

	// Evaluate each of the bars
	for (int BarIndex = CalculationStartIndex; BarIndex <= LastIndex; ++BarIndex)
	{

		int FormulaResult = sc.EvaluateAlertConditionFormulaAsBoolean(BarIndex, ParseAndSetFormula);
		ParseAndSetFormula = false;

		bool StateHasChanged = true;
		if (FormulaResult == r_PriorFormulaState )
		{
			TradeNotAllowedReason = "Formula state has not changed";
			StateHasChanged = false;
		}

		r_PriorFormulaState = FormulaResult;

		// Determine the result
		if (FormulaResult != 0) //Formula is true
		{
			if (IsTradeAllowed)
			{
				sc.GetTradePosition(PositionData);
				sc.GetTradeStatisticsForSymbol(0, 1, TradeStatistics);
			}

			if (Input_OrderActionOnAlert.GetIndex() == 0)// Buy entry
			{
				BuyEntry[BarIndex] = 1;
				SellEntry[BarIndex] = 0;

				if (IsTradeAllowed  && StateHasChanged)
				{
					if (PositionData.PositionQuantityWithAllWorkingOrders == 0
						|| Input_AllowMultipleEntriesInSameDirection.GetYesNo()
						|| Input_SupportReversals.GetYesNo())
					{
						if (TradeStatistics.TotalLongTrades < Input_MaximumLongTradesPerDay.GetInt())
						{
							s_SCNewOrder NewOrder;
							NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
							NewOrder.OrderType = SCT_ORDERTYPE_MARKET;

							int Result = (int)sc.BuyEntry(NewOrder);
							if (Result < 0)
								sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true);
						}
						else if (Input_EnableDebuggingOutput.GetYesNo())
						{
							sc.AddMessageToTradeServiceLog("Buy Entry signal ignored. Maximum Long Trades reached.", false, true);
						}
					}
					else if (Input_EnableDebuggingOutput.GetYesNo())
					{
						sc.AddMessageToTradeServiceLog("Buy Entry signal ignored. Position exists.", false, true);
					}
				}
				else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
				{
					SCString MessageText("Trading is not allowed. Reason: ");
					MessageText += TradeNotAllowedReason;
					sc.AddMessageToTradeServiceLog(MessageText, 0);
					r_LastDebugMessageType = 1;
				}
			}
			else if (Input_OrderActionOnAlert.GetIndex() == 1)// Buy exit
			{
				BuyEntry[BarIndex] = 0;
				SellEntry[BarIndex] = 1;

				if (IsTradeAllowed && StateHasChanged)
				{
					if (PositionData.PositionQuantity > 0)
					{
						s_SCNewOrder NewOrder;
						NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
						NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
						int Result = (int)sc.BuyExit(NewOrder);
						if (Result < 0)
							sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true);

					}
					else if (Input_EnableDebuggingOutput.GetYesNo())
					{
						sc.AddMessageToTradeServiceLog("Buy Exit signal ignored. Long position does not exist.", false, true);
					}
				}
				else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
				{
					SCString MessageText("Trading is not allowed. Reason: ");
					MessageText += TradeNotAllowedReason;
					sc.AddMessageToTradeServiceLog(MessageText, 0);
					r_LastDebugMessageType = 1;
				}
			}
			else if(Input_OrderActionOnAlert.GetIndex() == 2)// Sell entry
			{
				BuyEntry[BarIndex] = 0;
				SellEntry[BarIndex] = 1;

				if (IsTradeAllowed  && StateHasChanged)
				{
					if (PositionData.PositionQuantityWithAllWorkingOrders == 0
						|| Input_AllowMultipleEntriesInSameDirection.GetYesNo()
						|| Input_SupportReversals.GetYesNo())
					{
						if (TradeStatistics.TotalShortTrades < Input_MaximumShortTradesPerDay.GetInt())
						{
							s_SCNewOrder NewOrder;
							NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
							NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
							int Result = (int)sc.SellEntry(NewOrder);
							if (Result < 0)
								sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true);
						}
						else if (Input_EnableDebuggingOutput.GetYesNo())
						{
							sc.AddMessageToTradeServiceLog("Sell Entry signal ignored. Maximum Short Trades reached.", false, true);
						}
					}
					else if (Input_EnableDebuggingOutput.GetYesNo())
					{
						sc.AddMessageToTradeServiceLog("Sell Entry signal ignored. Position exists.", false, true);
					}
				}
				else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
				{
					SCString MessageText("Trading is not allowed. Reason: ");
					MessageText += TradeNotAllowedReason;
					sc.AddMessageToTradeServiceLog(MessageText, 0);
					r_LastDebugMessageType = 1;
				}
			}
			else if (Input_OrderActionOnAlert.GetIndex() == 3)// Sell exit
			{
				BuyEntry[BarIndex] = 1;
				SellEntry[BarIndex] = 0;

				if (IsTradeAllowed && StateHasChanged)
				{
					if (PositionData.PositionQuantity < 0)
					{
						s_SCNewOrder NewOrder;
						NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
						NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
						int Result = (int)sc.SellExit(NewOrder);
						if (Result < 0)
							sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true);

					}
					else if (Input_EnableDebuggingOutput.GetYesNo())
					{
						sc.AddMessageToTradeServiceLog("Sell Exit signal ignored. Short position does not exist.", false, true);
					}
				}
				else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
				{
					SCString MessageText("Trading is not allowed. Reason: ");
					MessageText += TradeNotAllowedReason;
					sc.AddMessageToTradeServiceLog(MessageText, 0);
					r_LastDebugMessageType = 1;
				}
			}

		}
		else
		{
			BuyEntry[BarIndex] = 0;
			SellEntry[BarIndex] = 0;
		}

		//  Reset the prior formula state to 0 if we did not allow trading in order to not prevent trading  the next time it is allowed if the state did not change.
		if (!IsTradeAllowed)
			r_PriorFormulaState = 0;

	}

	if (sc.IsFullRecalculation)
		r_LastDebugMessageType = 0;
}
