#include "sierrachart.h"
//#include "scstudyfunctions.h"



SCDLLName("RVH")

#define isBull(N) (sc.Close[N] > sc.Open[N])
#define isBear(N) (!( sc.Close[N] > sc.Open[N]))


#define RoundToMinTick(n) (std::ceil(n * 1.0f/sc.TickSize)*sc.TickSize)




SCSFExport scsf_OppoSingOrders(SCStudyInterfaceRef sc)
{
	
	
	SCSubgraphRef Subgraph_AvgBarSize = sc.Subgraph[0]; 
	

	
	if (sc.SetDefaults)
	{
		sc.GraphName = "Opposing Orders";

		sc.AutoLoop = true;
		sc.GraphRegion = 0;
		sc.ValueFormat= VALUEFORMAT_INHERITED;
		
		Subgraph_AvgBarSize.Name = "Avg Bar Size";
		Subgraph_AvgBarSize.DrawStyle = DRAWSTYLE_SUBGRAPH_NAME_AND_VALUE_LABELS_ONLY;
		
	
		
		


			
		
		//Any of the following variables can also be set outside and below the sc.SetDefaults code block

		sc.AllowMultipleEntriesInSameDirection = true; 
		sc.MaximumPositionAllowed = 10;
		sc.SupportReversals = false;

		// This is false by default. Orders will go to the simulation system always.
		sc.SendOrdersToTradeService = false;

		sc.AllowOppositeEntryWithOpposingPositionOrOrders = true;
		sc.SupportAttachedOrdersForTrading = true;

		// This line can be within sc.SetDefaults or outside of it.
		//sc.TradeWindowConfigFileName = "Test_2.twconfig";

		sc.CancelAllOrdersOnEntriesAndReversals= false;
		sc.AllowEntryWithWorkingOrders = true;
		sc.CancelAllWorkingOrdersOnExit = true;

		// Only 1 trade for each Order Action type is allowed per bar.
		sc.AllowOnlyOneTradePerBar = false; 

		//This needs to be set to true when a trading study uses trading functions.
		sc.MaintainTradeStatisticsAndTradesData = true;
		
		

		return;
	}

	
	SCString msg;
	
	int n = sc.Index;
	
	float avgBarSize;
	
	/* if (sc.GetBarHasClosedStatus() == BHCS_BAR_HAS_NOT_CLOSED)
		return; */

	//START OF TRADING CODE
	s_SCPositionData PositionData;
	sc.GetTradePosition(PositionData);
	
	sc.SupportTradingScaleIn = true; 

	sc.SupportTradingScaleOut = true; 
	

	
	
	//If a bar has closed, close any unfilled parent orders. Parent orders are created at close of a bar, and allowed till the close of next bar to fill.
	//If they are still unfilled, cancel the order. Next see whether we have an existing position - If no position, create a new long or short position depending
	//on whether the current bar is a bull or bear bar. If position exists, update stop of existing position as necessary, and scale in to the existing position. 
	//If position is opposite, then do the following - 
	//update stop of existing position to low - tick or high + tick depending on if existing position is long or short.
	//scale in to existing position at the close of current bar - this is in case the low or high of the bar is never taken out. Possible when there is a tail
	//Create an opposite order at the low-tick or high+tick in case it gets triggered.
	
	//Problems I am seeing - Things start out fine, but usually Sierra Chart will hang randomly around close of a bar ( The time remaining counter will usually show 10 or 9 sec left).
	//Sometimes the current position line on chart disappears and comes back randomly.
	

	
	if (sc.GetBarHasClosedStatus() == BHCS_BAR_HAS_CLOSED)
	{
		
		
		//Look for Unfilled Parent Order, and Cancel them. Parent Orders should only be held for one bar.This is in the absence of a Good_Till_DateTime TIF.
		
		s_SCTradeOrder order;
		int orderIndex = 0;

		while (sc.GetOrderByIndex(orderIndex, order) != SCTRADING_ORDER_ERROR)
		{
			//if parent Order id ==0, we know its a parent. Check if its status is still open.
			if ( order.ParentInternalOrderID == 0 &&
					order.OrderStatusCode == SCT_OSC_OPEN)
			{
				 int Result = sc.CancelOrder(order.InternalOrderID);
				
			}
			
			++orderIndex;
		}
		
		
		SCFloatArrayRef barSize = sc.Subgraph[0].Arrays[0];	
  

		// Calculate bar sizes: abs(high - low)
		for (int i = 0; i < sc.ArraySize; ++i)
		{
			barSize[i] = std::fabs(sc.High[i] - sc.Low[i]);
		}
		
		
		
		
		//Use the average of bars till the prior bar. This will be used for initial target, and stop loss.
		sc.SimpleMovAvg(barSize, Subgraph_AvgBarSize, 10);
		avgBarSize = Subgraph_AvgBarSize[n-1];

	
		float iLongStop, iShortStop;
	
	

	
			
		if (isBull(n) )
		{
			iLongStop = sc.Low[n] - RoundToMinTick(2*avgBarSize);
			if ( PositionData.PositionQuantity ==0  && !PositionData.WorkingOrdersExist) //Start a new long position
			{
				// Create an s_SCNewOrder object. 
				s_SCNewOrder NewOrder;
				NewOrder.OrderQuantity = 1;
				NewOrder.OrderType = SCT_ORDERTYPE_STOP;
				//NewOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//NewOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				NewOrder.Price1 = sc.High[n] + sc.TickSize;
				
				NewOrder.Target1Price =NewOrder.Price1 + RoundToMinTick(avgBarSize) ;
				NewOrder.Stop1Price = sc.Low[n]- RoundToMinTick(2*avgBarSize);
				
				msg.Format("New order fill: Price = %f, Prior Quantity = %u", NewOrder.Price1, PositionData.PositionQuantity);
				sc.AddMessageToLog(msg, 1);
				
				int Result = static_cast<int>(sc.BuyEntry(NewOrder));
				
		
				
			}
			else if (PositionData.PositionQuantity >0 && !PositionData.NonAttachedWorkingOrdersExist) //add to existing long position
			{
				
				//First see if the stop of existing orders - 2x avgBarSize has to be adjusted before scaling in.
				
				s_SCTradeOrder order;
				int orderIndex = 0;

				while (sc.GetOrderByIndex(orderIndex, order) != SCTRADING_ORDER_ERROR)
				{
					
					if ( order.ParentInternalOrderID == 0 &&
						order.OrderStatusCode == SCT_OSC_FILLED)
					{
						
						
						
						s_SCTradeOrder stopOrder;
						if (sc.GetNearestStopWorkingAttachedOrder(stopOrder))
						{						
						
							// You can now modify this stop order
							s_SCNewOrder modify;
							modify.InternalOrderID = stopOrder.InternalOrderID;
							if (iLongStop > stopOrder.Price1)
							{
								
							
								modify.Price1 =  iLongStop ; //New Stop will be max of the existing stop, and 2x avgBarSize below current bar low.
								sc.ModifyOrder(modify);
								msg.Format("Long Position Stop update: Price = %f, Prior Quantity = %u", modify.Price1, PositionData.PositionQuantity);
								sc.AddMessageToLog(msg, 1);
							}
						}
						
						
					}
					++orderIndex;
					
				}
				
				
				//Assumption is the Scale in is ON, so we do not need to specify target and stop price, and this should add to the existing attached Orders
				s_SCNewOrder scaleInOrder;
				scaleInOrder.OrderQuantity = 1;
				scaleInOrder.OrderType = SCT_ORDERTYPE_STOP;
				//scaleInOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				scaleInOrder.Price1	 = sc.Close[n];
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//scaleInOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				
				
				msg.Format("Long Position Scalein: Price = %f, Prior Quantity = %u", scaleInOrder.Price1, PositionData.PositionQuantity);
				sc.AddMessageToLog(msg, 1);
				
				
				int Result = static_cast<int>(sc.BuyEntry(scaleInOrder));
				
			}
			else if (!PositionData.NonAttachedWorkingOrdersExist) //Existing Short position. Update stop and also prepare new Scale in Short position, as well as a Long order above high of bar.
			{
				s_SCTradeOrder order;
				int orderIndex = 0;

				//First see if stop of existing attached order needs to be updated.
				
				while (sc.GetOrderByIndex(orderIndex, order) != SCTRADING_ORDER_ERROR)
				{
					if ( order.ParentInternalOrderID == 0 &&
						order.OrderStatusCode == SCT_OSC_FILLED)
					{
						// You now have a filled parent order. Find attached Stop Order
						
						s_SCTradeOrder stopOrder;
						if (sc.GetNearestStopWorkingAttachedOrder(stopOrder))
						{	
							// You can now modify this stop order
							s_SCNewOrder modify;
							modify.InternalOrderID = stopOrder.InternalOrderID;
							modify.Price1 = sc.High[n]+ sc.TickSize;
							sc.ModifyOrder(modify);
							
							msg.Format("Short Position Stop update: Price = %f, Prior Quantity = %u", modify.Price1, PositionData.PositionQuantity);
							sc.AddMessageToLog(msg, 1);
							
						}
						
					}
					
				}
				
				//Next create a scale in Short Order
				int Result;
				
				s_SCNewOrder scaleInOrder;
				scaleInOrder.OrderQuantity = 1;
				scaleInOrder.OrderType = SCT_ORDERTYPE_STOP;
				//scaleInOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				scaleInOrder.Price1	 = sc.Close[n];
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//scaleInOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				
				msg.Format("Short Position Scalein: Price = %f, Prior Quantity = %u", scaleInOrder.Price1, PositionData.PositionQuantity);
				sc.AddMessageToLog(msg, 1);
				
				Result = static_cast<int>(sc.SellEntry(scaleInOrder));
				
				
				
				//Next create a new Buy Order. This order will be cancelled at the end of the next bar if still not filled.
				// Create an s_SCNewOrder object. 
				s_SCNewOrder NewOrder;
				NewOrder.OrderQuantity = 1;
				NewOrder.OrderType = SCT_ORDERTYPE_STOP;
				//NewOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//NewOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				NewOrder.Price1 = sc.High[n] + sc.TickSize;	//In this case, simplifying this to be a buy Stop order above the high of the bar.
				
				NewOrder.Target1Price =NewOrder.Price1 + RoundToMinTick(avgBarSize) ;
				NewOrder.Stop1Price = sc.Low[n]- RoundToMinTick(2*avgBarSize);
				
				msg.Format("Opposite Long Order: Price = %f, Prior Quantity = %u", NewOrder.Price1 , PositionData.PositionQuantity);
				sc.AddMessageToLog(msg, 1);
				
				Result = static_cast<int>(sc.BuyEntry(NewOrder));

			}	
			
		}

	

		
		if (isBear(n) )
		{
		
			iShortStop = sc.High[n] + RoundToMinTick( 2 * avgBarSize );
			
			if ( PositionData.PositionQuantity ==0  && !PositionData.WorkingOrdersExist) //Start a new short position
			{
				// Create an s_SCNewOrder object. 
				s_SCNewOrder NewOrder;
				NewOrder.OrderQuantity = 1;
				NewOrder.OrderType = SCT_ORDERTYPE_STOP;
				//NewOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//NewOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				NewOrder.Price1 = sc.Low[n] - sc.TickSize;
				
				NewOrder.Target1Price =NewOrder.Price1 - RoundToMinTick(avgBarSize) ;
				NewOrder.Stop1Price = sc.High[n] + RoundToMinTick(2*avgBarSize);
				
				int Result = static_cast<int>(sc.SellEntry(NewOrder));
				

				
			}
			else if (PositionData.PositionQuantity < 0 && !PositionData.NonAttachedWorkingOrdersExist) //add to existing short position
			{
				
				//First see if the stop of existing attached orders has to be adjusted before scaling in. May need to wrap the scale in behavior with an Input field.
				
				s_SCTradeOrder order;
				int orderIndex = 0;

				while (sc.GetOrderByIndex(orderIndex, order) != SCTRADING_ORDER_ERROR)
				{
					
					if ( order.ParentInternalOrderID == 0 &&
						order.OrderStatusCode == SCT_OSC_FILLED)
					{
						
						
						
						s_SCTradeOrder stopOrder;
						if (sc.GetNearestStopWorkingAttachedOrder(stopOrder))
						{						
						
							// You can now modify this stop order
							s_SCNewOrder modify;
							modify.InternalOrderID = stopOrder.InternalOrderID;
							if ( iShortStop <  stopOrder.Price1)
							{
								modify.Price1 = iShortStop ; //New Stop will be min of the existing stop, and 2x avgBarSize above current bar high.
								sc.ModifyOrder(modify);
							}
						}
						
						
					}
					++orderIndex;
					
				}
				
				
				//Assumption is the Scale in is ON, so we do not need to specify target and stop price, and this should add to the existing attached Orders
				s_SCNewOrder scaleInOrder;
				scaleInOrder.OrderQuantity = 1;
				scaleInOrder.OrderType = SCT_ORDERTYPE_STOP;
				//scaleInOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				scaleInOrder.Price1	 = sc.Close[n];
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//scaleInOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				
				
				
				
				
				int Result = static_cast<int>(sc.SellEntry(scaleInOrder));
				
			}
			else if (!PositionData.NonAttachedWorkingOrdersExist) //Current Long position. Update stop and also prepare new Scale in Long position, and a new Short order below low of the bear bar.
			{
				s_SCTradeOrder order;
				int orderIndex = 0;

				while (sc.GetOrderByIndex(orderIndex, order) != SCTRADING_ORDER_ERROR)
				{
					if ( order.ParentInternalOrderID == 0 &&
						order.OrderStatusCode == SCT_OSC_FILLED)
					{
						// You now have a filled parent order. Find attached Stop Order
						
						s_SCTradeOrder stopOrder;
						if (sc.GetNearestStopWorkingAttachedOrder(stopOrder))
						{	
							// You can now modify this stop order
							s_SCNewOrder modify;
							modify.InternalOrderID = stopOrder.InternalOrderID;
							modify.Price1 = sc.Low[n]- sc.TickSize;
							sc.ModifyOrder(modify);
							
						}
						
					}
					
				}
				
				//Next create a scale in Long Order
				int Result;
				s_SCNewOrder scaleInOrder;
				scaleInOrder.OrderQuantity = 1;
				scaleInOrder.OrderType = SCT_ORDERTYPE_STOP;
				//scaleInOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				scaleInOrder.Price1	 = sc.Close[n];
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//scaleInOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				
				Result = static_cast<int>(sc.BuyEntry(scaleInOrder));
				
				//Next create a new Short Order, below thw low of the bear bar.
				// Create an s_SCNewOrder object. 
				s_SCNewOrder NewOrder;
				NewOrder.OrderQuantity = 1;
				NewOrder.OrderType = SCT_ORDERTYPE_STOP;
				//NewOrder.TimeInForce = TIF_GOOD_TILL_DATE_TIME;
				
				//SCDateTime barDuration = sc.BaseDateTimeIn[n] - sc.BaseDateTimeIn[n - 1];
				//NewOrder.GoodTillDateTime = sc.BaseDateTimeIn[n+1] + barDuration;
				NewOrder.Price1 = sc.Low[n] - sc.TickSize;	//In this case, simplifying this to be a buy Stop order above the high of the bar.
				
				NewOrder.Target1Price =NewOrder.Price1 - RoundToMinTick(avgBarSize) ;
				NewOrder.Stop1Price = sc.High[n] +  RoundToMinTick(2*avgBarSize);
				
				Result = static_cast<int>(sc.SellEntry(NewOrder));
				
		
			}	
		
		}
	}	
}
	
