#include "sierrachart.h"


SCSFExport scsf_TradeManagementByStudy(SCStudyInterfaceRef sc)
{
	// Setup References
	SCInputRef Input_Enabled                  = sc.Input[0];
	SCInputRef Input_OrderType                = sc.Input[1];
	SCInputRef Input_ControlSubgraphRef       = sc.Input[2];
	SCInputRef Input_AdjustmentTiming         = sc.Input[3]; 
	SCInputRef Input_AdjustmentFrequency      = sc.Input[4];
	SCInputRef Input_TrailingOffset              = sc.Input[5];
	SCInputRef Input_ACSButtonNumber			= sc.Input[6];
	SCInputRef Input_PositionType             = sc.Input[7];
	SCInputRef Input_Logging                  = sc.Input[8];
	SCInputRef Input_OnlyModifyInOneDirection = sc.Input[9];
	SCInputRef Input_Version = sc.Input[10];
	SCInputRef Input_AllowZeroValues = sc.Input[11];
	SCInputRef Input_AllowNegativeValues= sc.Input[12];
	SCInputRef Input_AllowCustomPropertiesForControlBarButton = sc.Input[13];

	if (sc.SetDefaults)	
	{
		sc.GraphName = "Trade Management by Study";
		sc.AutoLoop = 0;//We are using Manual looping for efficiency since historical data is not relevant with this study
		sc.GraphRegion = 0;
		sc.CalculationPrecedence = LOW_PREC_LEVEL;
		
		Input_Enabled.Name = "Enabled";
		Input_Enabled.SetYesNo(0);

		Input_OrderType.Name = "Order Type To Manage";
		Input_OrderType.SetCustomInputStrings("Stops;Targets");
		Input_OrderType.SetCustomInputIndex(0);

		Input_ControlSubgraphRef.Name = "Controlling Subgraph Reference";
		Input_ControlSubgraphRef.SetStudySubgraphValues(0, 0);

		Input_AdjustmentTiming.Name = "Order Adjustment Timing";
		Input_AdjustmentTiming.SetCustomInputStrings("Every N Seconds;On Bar Close");
		Input_AdjustmentTiming.SetCustomInputIndex(1);

		Input_AdjustmentFrequency.Name = "Adjustment Frequency (in Seconds)";
		Input_AdjustmentFrequency.SetInt(5);

		Input_TrailingOffset.Name = "Trailing Offset (in Ticks)";
		Input_TrailingOffset.SetInt(2);

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

		Input_PositionType.  Name = "Position Type";
		Input_PositionType.SetCustomInputStrings("All Positions;Long Only;Short Only");
		Input_PositionType.SetCustomInputIndex(0);

		Input_Logging.Name = "Detailed Logging (for debugging)";
		Input_Logging.SetYesNo(0);

		Input_OnlyModifyInOneDirection.Name = "Only Modify Stops In One Direction";
		Input_OnlyModifyInOneDirection.SetYesNo(false);

		Input_Version.SetInt(2);

		Input_AllowZeroValues.Name = "Allow Zero Values";
		Input_AllowZeroValues.SetYesNo(false);

		Input_AllowNegativeValues.Name = "Allow Negative Values";
		Input_AllowNegativeValues.SetYesNo(false);

		Input_AllowCustomPropertiesForControlBarButton.Name = "Allow Custom 'Properties' for Control Bar Button";
		Input_AllowCustomPropertiesForControlBarButton.SetYesNo(false);

		// Do not receive mouse pointer events when ACS Tool Control Bar button is pressed in/enabled
		sc.ReceivePointerEvents = false;

		//sc.CancelAllOrdersOnEntries = false;


		return;
	}



	//References to persistent variables
	SCDateTime& LastAdjustmentDateTime = sc.GetPersistentSCDateTime(1);

	int& MenuID = sc.GetPersistentInt(1);

	int & LastLogMessageIdentifier =sc.GetPersistentInt(2);

	/****************************************
	* First, handle mouse events
	*****************************************/

	if (sc.IsFullRecalculation)//Is full recalculation
	{
		// set ACS Tool Control Bar tool tip
		sc.SetCustomStudyControlBarButtonHoverText(Input_ACSButtonNumber.GetInt(), "Enable/Disable Trade Management by Study");

		// set Custom Study Control Bar button text if the input to allow custom properties is not true. Otherwise, rely upon what the user sets.
		if (!Input_AllowCustomPropertiesForControlBarButton.GetYesNo())
			sc.SetCustomStudyControlBarButtonText(Input_ACSButtonNumber.GetInt(), "TMS");

		sc.SetCustomStudyControlBarButtonEnable(Input_ACSButtonNumber.GetInt(), Input_Enabled.GetBoolean() );

		LastLogMessageIdentifier = 0;
	}

	// Wait for an event to enable study if not already enabled
	if (sc.MenuEventID == Input_ACSButtonNumber.GetInt())
	{
		const int ButtonState = (sc.PointerEventType == SC_ACS_BUTTON_ON) ? 1 : 0;
		Input_Enabled.SetYesNo(ButtonState);
	}

	/****************************************
	* next, execute the order management
	*****************************************/

	// If study is not enabled, exit without doing anything
	if (Input_Enabled.GetYesNo() == 0)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 1)
		{
			LastLogMessageIdentifier = 1;
			sc.AddMessageToLog("Study not enabled.", 0);
		}

		return;
	}
	

	//For safety we must never do any order management while historical data is being downloaded.
	if (sc.ChartIsDownloadingHistoricalData(sc.ChartNumber))
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 2)
		{
			LastLogMessageIdentifier = 2;
			sc.AddMessageToLog("No order adjustments performed because historical data is being downloaded for chart.", 0);
		}
		return;
	}

	sc.SendOrdersToTradeService = !sc.GlobalTradeSimulationIsOn;

	// Flat state is a good time to reset LastAdjustmentDateTime
	s_SCPositionData PositionData;
	sc.GetTradePosition (PositionData);

	if (PositionData.PositionQuantity == 0)
	{
		LastAdjustmentDateTime = 0.0;

		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 3)
		{
			LastLogMessageIdentifier = 3;
			sc.AddMessageToLog("Position does not exist.", 0);
		}
		
		return;
	}

	if (Input_PositionType.GetIndex() == 1 && PositionData.PositionQuantity < 0)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 4)
		{
			LastLogMessageIdentifier = 4;
			SCString  LogText("Position Type input is set to 'Long Only' and Position is Short. Position: ");
			SCString QuantityString;
			sc.OrderQuantityToString(PositionData.PositionQuantity, QuantityString);
			LogText += QuantityString;
			sc.AddMessageToLog(LogText, 0);
		}

		return;
	}

	if (Input_PositionType.GetIndex() == 2 && PositionData.PositionQuantity > 0)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 5)
		{
			LastLogMessageIdentifier = 5;
			SCString  LogText("Position Type input is set to 'Short Only' and Position is Long. Position: ");
			SCString QuantityString;
			sc.OrderQuantityToString(PositionData.PositionQuantity, QuantityString);
			LogText += QuantityString;
			sc.AddMessageToLog(LogText, 0);
		}

		return;
	}

	// If the timing is every N seconds, check that we have surpassed the set frequency
	if (Input_AdjustmentTiming.GetInt() == 0 && (sc.LatestDateTimeForLastBar - LastAdjustmentDateTime) < Input_AdjustmentFrequency.GetFloat() * SECONDS)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 6)
		{
			LastLogMessageIdentifier = 6;
			sc.AddMessageToLog("Time interval has not elapsed yet for 'Every N Seconds'.", 0);
		}
		return;		
	}


	//  Update the LastAdjustmentDateTime

	LastAdjustmentDateTime = sc.LatestDateTimeForLastBar;

	// Retrieve the controlling subgraph array
	SCFloatArray ControllingSubgraph;
	sc.GetStudyArrayUsingID(Input_ControlSubgraphRef.GetStudyID(), Input_ControlSubgraphRef.GetSubgraphIndex(), ControllingSubgraph);



	// Based on Input settings, retrieve nearest stop/target order
	s_SCTradeOrder ExistingTargetOrStopOrder;
	int Result;

	if (Input_OrderType.IntValue == 0)
		Result = sc.GetNearestStopOrder(ExistingTargetOrStopOrder);
	else
		Result = sc.GetNearestTargetOrder(ExistingTargetOrStopOrder);

	if (Result == 0)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 7)
		{
			LastLogMessageIdentifier = 7;
			sc.AddMessageToLog("There is no Stop/Target Attached Order found.", 0);
		}
		return;
	}
	
	// only process open orders
	if (ExistingTargetOrStopOrder.OrderStatusCode != SCT_OSC_OPEN
		&& ExistingTargetOrStopOrder.OrderStatusCode != SCT_OSC_HELD)
	{	
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 8)
		{
			LastLogMessageIdentifier = 8;
			sc.AddMessageToLog("The found Stop/Target order is not in an Open/Held state.", 0);
		}
		return;
	}
	
	// Modify the order
	int BarIndex = sc.ArraySize - 1;
	
	if(Input_AdjustmentTiming.GetIndex() == 1 )
		BarIndex = sc.ArraySize - 2;


	if (!Input_AllowZeroValues.GetYesNo() && ControllingSubgraph[BarIndex]== 0.0)
	{	
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 9)
		{
			LastLogMessageIdentifier = 9;
			sc.AddMessageToLog("The 'Controlling Subgraph Reference' value is 0 at the last bar.", 0);
		}
		return;
	}


	if (!Input_AllowNegativeValues.GetYesNo() && ControllingSubgraph[BarIndex] < 0)
	{	
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 15)
		{
			LastLogMessageIdentifier = 15;
			sc.AddMessageToLog("The 'Controlling Subgraph Reference' value is < 0 at the last bar.", 0);
		}
		return;
	}


	double NewPrice = 0.0;
	
	if (ExistingTargetOrStopOrder.BuySell == BSE_SELL)
	{
		NewPrice = ControllingSubgraph[BarIndex] - Input_TrailingOffset.GetInt() * sc.TickSize;
	}
	else if (ExistingTargetOrStopOrder.BuySell == BSE_BUY)
	{
		NewPrice = ControllingSubgraph[BarIndex] + Input_TrailingOffset.GetInt() * sc.TickSize;
	}
	else
		return;//Should never happen so no need for error logging.

	//Round the price to an actual order price.
	NewPrice= sc.RoundToTickSize(NewPrice);

	//If the price has not changed, then do not modify.
	if (sc.FormattedEvaluateUsingDoubles(ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat, EQUAL_OPERATOR, NewPrice, sc.BaseGraphValueFormat ) 
		|| sc.FormattedEvaluateUsingDoubles(ExistingTargetOrStopOrder.LastModifyPrice1, sc.BaseGraphValueFormat, EQUAL_OPERATOR, NewPrice, sc.BaseGraphValueFormat)
		)
	{	
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 10)
		{
			LastLogMessageIdentifier = 10;

			SCString LogMessage;
			LogMessage.Format("There is not a new price to modify the order to. Existing order: Price1=%f, LastModifyPrice1 =%f. New study subgraph price with offset=%f.", ExistingTargetOrStopOrder.Price1, ExistingTargetOrStopOrder.LastModifyPrice1, NewPrice);

			sc.AddMessageToLog(LogMessage, 0);
		}
		return;
	}

	// If modify stop price is in wrong direction, then do not modify.
	if (Input_OrderType.IntValue == 0 //Stops
		&&  Input_OnlyModifyInOneDirection.GetYesNo() &&
		((PositionData.PositionQuantity < 0 && sc.FormattedEvaluateUsingDoubles(NewPrice, sc.BaseGraphValueFormat, GREATER_OPERATOR, ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat)) ||
		(PositionData.PositionQuantity > 0 && sc.FormattedEvaluateUsingDoubles(NewPrice, sc.BaseGraphValueFormat, LESS_OPERATOR, ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat)) )	)
	{
		if (Input_Logging.GetYesNo() && LastLogMessageIdentifier != 14)
		{
			LastLogMessageIdentifier = 14;
			sc.AddMessageToLog("Modify stop price is in wrong direction.", 0);
		}
		return;
	}
	
	s_SCNewOrder ModifyOrder;
	ModifyOrder.InternalOrderID = ExistingTargetOrStopOrder.InternalOrderID;
	ModifyOrder.Price1 = NewPrice;

	Result = sc.ModifyOrder(ModifyOrder);

	if (Result == 1 && Input_Logging.GetYesNo() && LastLogMessageIdentifier != 11)
	{
		LastLogMessageIdentifier = 11;
		SCString  LogText;
		LogText.Format("The Stop/Target order has been modified. Internal Order ID: %d. New Price: %f", ExistingTargetOrStopOrder.InternalOrderID, NewPrice);

		sc.AddMessageToLog( LogText , 0);
	}
	else if (Result == SCT_SKIPPED_AUTO_TRADING_DISABLED && Input_Logging.GetYesNo() && LastLogMessageIdentifier != 13)
	{
		LastLogMessageIdentifier = 13;
		SCString  LogText;
		LogText.Format("'Trade >> Auto Trading Enabled' is disabled.");

		sc.AddMessageToLog( LogText , 0);
	}
	else if (Result != 1 && Input_Logging.GetYesNo() && LastLogMessageIdentifier != 12)
	{
		LastLogMessageIdentifier = 12;
		SCString  LogText;
		LogText.Format("There was an error modifying the Stop/Target order. Internal Order ID: %d. New Price: %f. Error Code %d.", ExistingTargetOrStopOrder.InternalOrderID, NewPrice, Result);

		sc.AddMessageToLog( LogText , 0);
	}


}


