public Alert ExitAlertCreate(Bar exitBar, Position position, double stopOrLimitPrice, string signalName,
			Direction direction, MarketLimitStop exitMarketLimitStop) {

			this.checkThrowEntryBarIsValid(exitBar);
			this.checkThrowPositionToCloseIsValid(position);

			double priceScriptOrStreaming = stopOrLimitPrice;
			OrderSpreadSide orderSpreadSide = OrderSpreadSide.Unknown;
			if (exitMarketLimitStop == MarketLimitStop.Market) {
				priceScriptOrStreaming = this.getStreamingPriceForMarketOrder(exitMarketLimitStop, direction, out orderSpreadSide);
			}

			PositionLongShort longShortFromDirection = MarketConverter.LongShortFromDirection(direction);
			double exitPriceScript = exitBar.ParentBars.SymbolInfo.RoundAlertPriceToPriceLevel(
				priceScriptOrStreaming, true, longShortFromDirection, exitMarketLimitStop);

			Alert alert = new Alert(exitBar, position.Shares, exitPriceScript, signalName,
				direction, exitMarketLimitStop, orderSpreadSide,
				//this.executor.Script,
				this.executor.Strategy);
			alert.AbsorbFromExecutor(executor);
			alert.PositionAffected = position;
			// moved to CallbackAlertFilled - we can exit by TP or SL - and position has no clue which Alert was filled!!!
			//position.ExitCopyFromAlert(alert);
			alert.PositionAffected.ExitAlertAttach(alert);

			return alert;
		}
		internal void PositionsMasterOpenNewAdd(Position positionOpening) {
			if (positionOpening.EntryFilledBarIndex == -1) {
				string msg = "ENTRY_BAR_NEGATIVE_CAN_NOT_STORE_POSITION_IN_PositionsMasterByEntryBar"
					+ " Strategy[" + this.executor.Strategy.ToString() + "] EntryBar=-1 for position[" + positionOpening + "]";
				#if DEBUG
				Debugger.Break();
				#endif
				//throw new Exception(msg);
				this.executor.PopupException(msg);
				return;
			}
			//if (positionOpening.EntryFilledBarIndex < 10) { int a = 1; }
			lock (this.PositionsMasterByEntryBarLock) {
				//if (PositionsMasterContainsIdenticalPosition(position)) return;
				//position.SernoInMasterList = this.PositionsMaster.Count;

				List<Position> sameBarPositions = null;
				if (this.PositionsMasterByEntryBar.ContainsKey(positionOpening.EntryFilledBarIndex) == false) {
					sameBarPositions = new List<Position>();
					this.PositionsMasterByEntryBar.Add(positionOpening.EntryFilledBarIndex, sameBarPositions);
				} else {
					sameBarPositions = this.PositionsMasterByEntryBar[positionOpening.EntryFilledBarIndex];
					//foreach (Position each in sameBarPositions) {
					//	if (position == each) {int a = 1;}
					//	if (position.Equals(each)) { int b = 2; }
					//}
					bool brokerDupe = sameBarPositions.Contains(positionOpening); // see Position.Equals()
					if (brokerDupe) {
						string msg = "positionOpening already added everywhere; create a dupe-filtering! " + positionOpening;
						this.executor.PopupException(msg);
						return;
					}
				}
				positionOpening.SernoAbs = ++this.positionSernoAbs;
				sameBarPositions.Add(positionOpening);

				if (this.PositionsMaster.Contains(positionOpening)) {
					string msg = "position is already in PositionsMaster, won't add a dupe; create a dupe-filtering!";
					this.executor.PopupException(msg);
				} else {
					this.PositionsMaster.Add(positionOpening);
				}

				if (this.PositionsOpenedAfterExec.Contains(positionOpening)) {
					string msg = "position is already in PositionsOpenedAfterExec, won't add a dupe; create a dupe-filtering!";
					this.executor.PopupException(msg);
				} else {
					this.PositionsOpenedAfterExec.Add(positionOpening);
				}

				if (this.PositionsOpenNow.Contains(positionOpening)) {
					string msg = "position is already in PositionsOpenNow, won't add a dupe; create a dupe-filtering!";
					this.executor.PopupException(msg);
				} else {
					this.PositionsOpenNow.Add(positionOpening);
				}
			}
		}
		public List<Alert> CreateStopLossAndTakeProfitAlertsFromPositionPrototype(Position position) {
			if (position.IsEntryFilled == false) {
				string msg = "I can not place SL and TP for an unopened position; alert[" + position.EntryAlert + "]";
				throw new Exception(msg);
			}
			Alert SlPlaced = this.CreateStopLossFromPositionPrototype(position);
			Alert TpPlaced = this.CreateTakeProfitFromPositionPrototype(position);
			return new List<Alert>() { SlPlaced, TpPlaced };
		}
		private void checkThrowPositionToCloseIsValid(Position position) {
			if (position == null) {
				string msg = "position=null, can't close it!";
				throw new Exception(msg);
			}
			if (position.NoExitBarOrStreaming == false) {
				string msg = "position.Active=false, can't close it!";
				throw new Exception(msg);
			}
		}
		private bool checkPendingEntryPositionAlreadyPlaced(Position entryPosition) {
			if (entryPosition.EntryAlert == null) return false;
			foreach (Alert alert in executor.ExecutionDataSnapshot.AlertsPending) {
				Position pos = alert.PositionAffected;
				if (pos == null) continue;
				if (pos.EntryAlert == null) continue;
				if (pos.EntryAlert == entryPosition.EntryAlert) return true;
				if (pos.EntryAlert.IsIdenticalForOrdersPending(entryPosition.EntryAlert)) return true;
			}
			return false;
		}
		public override void SelectPosition(Position position) {
			this.ActivateParentForm();
			if (position == null) {
				string msg = "DONT_PASS_NULL_POSITION_TO_CHART_SHADOW ChartControl.SelectPosition()";
				Assembler.PopupException(msg);
				#if DEBUG
				Debugger.Break();
				#endif
				return;
			}
			int bar = (position.ExitAlert != null)
				? position.ExitAlert.PlacedBar.ParentBarsIndex
				: position.EntryAlert.PlacedBar.ParentBarsIndex;
			this.scrollToBarSafely(bar);
		}
		public Alert CreateStopLossFromPositionPrototype(Position position) {
			//if (this.executor.checkPrototypeAlreadyPlaced(position)) return;
			PositionPrototype proto = position.Prototype;
			if (proto.StopLossNegativeOffset == 0) {
				string msg = "What should Activator do with proto.StopLossNegativeOffset=0?";
				throw new Exception(msg);
			}

			if (proto.StopLossNegativeOffset == 0) {
				string msg = this.ReasonWhyNewStopLossOffsetDoesntMakeSense(position, proto.StopLossNegativeOffset);
				if (String.IsNullOrEmpty(msg) == false) {
					string msg2 = "What should Activator do with sense-less proto.StopLossNegativeOffset[" + proto.StopLossNegativeOffset + "], ";
					throw new Exception(msg2, new Exception(msg));
				}
			}

			MarketLimitStop simpleStopIfActivationZero = (proto.StopLossActivationNegativeOffset == 0) ? MarketLimitStop.Stop : MarketLimitStop.StopLimit;

			Alert alertStopLoss = executor.SellOrCoverAlertCreateDontRegister (
				executor.Bars.BarStreaming,
				position, proto.PriceStopLoss,
				"protoStopLossExit:" + proto.StopLossActivationNegativeOffset
					+ "@" + proto.StopLossNegativeOffset + " for " + position.EntrySignal,
				MarketConverter.ExitDirectionFromLongShort(proto.LongShort),
				simpleStopIfActivationZero);
			if (alertStopLoss == null) {
				string msg = "alertStopLoss should NOT be null";
				throw new Exception(msg);
			}
			alertStopLoss.PriceStopLimitActivation = 0;
			if (proto.StopLossActivationNegativeOffset < 0) alertStopLoss.PriceStopLimitActivation = proto.PriceStopLossActivation;
			if (proto.StopLossAlertForAnnihilation != null && this.executor.Backtester.IsBacktestingNow == false) {
				string msg = "CLEANUP: I was trying to catch MoveStopLoss::if(proto.StopLossAlertForAnnihilation==null)"
					+ " so I thought there is a new prototype assigned to a position,"
					+ " since we never put null directly proto.StopLossAlertForAnnihilation";
				throw new Exception(msg);
			}

			proto.StopLossAlertForAnnihilation = alertStopLoss;
			return alertStopLoss;
		}
		public override void OnPositionOpenedPrototypeSlTpPlacedCallback(Position positionOpenedProto) {
			PositionPrototype proto = positionOpenedProto.Prototype;
			if (proto == null) return;

			double currentStopLossNegativeOffset = proto.StopLossNegativeOffset;
			double newStopLossNegativeOffset = currentStopLossNegativeOffset - 20;
			//string msg = base.Executor.PositionPrototypeActivator.ReasonWhyNewStopLossOffsetDoesntMakeSense(positionOpenedProto, newStopLossNegativeOffset);
			//if (String.IsNullOrEmpty(msg)) {
				base.Executor.PositionPrototypeActivator.StopLossNewNegativeOffsetUpdateActivate(positionOpenedProto, newStopLossNegativeOffset);
			//} else {
			//	base.Executor.PopupException(new Exception("WONT_UPDATE_STOPLOSS: " + msg));
			//}

			double newTakeProfitPositiveOffset = proto.TakeProfitPositiveOffset + 50;
			//msg = base.Executor.PositionPrototypeActivator.ReasonWhyNewTakeProfitOffsetDoesntMakeSense(positionOpenedProto, newTakeProfitPositiveOffset);
			//if (String.IsNullOrEmpty(msg)) {
				base.Executor.PositionPrototypeActivator.TakeProfitNewPositiveOffsetUpdateActivate(positionOpenedProto, newTakeProfitPositiveOffset);
			//} else {
			//	base.Executor.PopupException(new Exception("WONT_UPDATE_TAKEPROFIT: " + msg));
			//}
		}
		public AlertArrow(Position position, bool arrowIsForPositionEntry) {
			this.Position = position;
			this.ArrowIsForPositionEntry = arrowIsForPositionEntry;
			this.Ytransient = 0;
			this.XBarMiddle = 99;
			//this.Location = new Point(0, 0);
		}
		public Alert SellAtLimit(Bar bar, Position position, double limitPrice, string signalName = "SOLD_AT_LIMIT") {
			return this.Executor.SellOrCoverAlertCreateRegister(bar, position, limitPrice, signalName, Direction.Sell, MarketLimitStop.Limit);
		}
		public Alert ExitAtMarket(Bar bar, Position position, string signalName = "EXITED_AT_MARKET") {
			if (position.PositionLongShort == PositionLongShort.Long) {
				return this.SellAtMarket(bar, position, signalName);
			} else {
				return this.CoverAtMarket(bar, position, signalName);
			}
		}
		public string ReasonWhyNewTakeProfitOffsetDoesntMakeSense(Position position, double newTakeProfitPositiveOffset, bool internalCallee = false) {
			PositionPrototype proto = position.Prototype;
			if (position.Symbol != proto.Symbol) {
				string msg1 = "NotYetImplemented: your script changes TakeProfitOffset for a proto.Symbol[" + proto.Symbol + "]!=position.Symbol[" + position.Symbol + "]";
				throw new Exception(msg1);
			}
			Direction dir = MarketConverter.EntryDirectionFromLongShort(proto.LongShort);
			if (position.EntryAlert.Direction != dir) {
				string msg1 = "NotYetImplemented: Crazy check here";
				throw new Exception(msg1);
			}
			Quote quote = executor.DataSource.StreamingProvider.StreamingDataSnapshot.LastQuoteGetForSymbol(proto.Symbol);
			double priceBestBidAsk = executor.DataSource.StreamingProvider.StreamingDataSnapshot.BidOrAskFor(proto.Symbol, proto.LongShort);
			double newTakeProfitPrice = proto.OffsetToPrice(newTakeProfitPositiveOffset);
			bool willBeExecutedImmediately = false;
			string ident = "TakeProfit{old[" + proto.PriceTakeProfit + "]:new[" + newTakeProfitPrice + "]}";
			string msg = "";
			switch (dir) {
				case Direction.Buy:
					if (newTakeProfitPrice < priceBestBidAsk) {
						willBeExecutedImmediately = true;
						msg = "will be filled immediately: newLongTakeProfitPrice[" + newTakeProfitPrice + "] < Ask[" + priceBestBidAsk + "] "
							+ ident + " //position[" + position + "]";
					}
					break;
				case Direction.Short:
					if (newTakeProfitPrice > priceBestBidAsk) {
						willBeExecutedImmediately = true;
						msg = "will be filled immediately: newShortTakeProfitPrice[" + newTakeProfitPrice + "] > Bid[" + priceBestBidAsk + "] "
							+ ident + " //position[" + position + "]";
					}
					break;
				default:
					msg = "I refuse to change TakeProfit when position.EntryAlert.Direction=["
						+ position.EntryAlert.Direction + "] - must be Buy or Sell only!!!";
					break;
			}
			if (internalCallee == true) {
				msg += " (Script is strongly recommented to check TP price first so we don't pass unexpected position closures to the Market)";
			}
			return msg;
		}
		public Alert ExitAtClose(Bar bar, Position position, string signalName = "EXITED_AT_CLOSE") {
			if (position.PositionLongShort == PositionLongShort.Long) {
				return this.SellAtClose(bar, position, signalName);
			} else {
				return this.CoverAtClose(bar, position, signalName);
			}
		}
		public List<Alert> PositionCloseImmediately(Position position, string signalName) {
			this.ExitAtMarket(this.Bars.BarStreaming, position, signalName);
			// BETTER WOULD BE KILL PREVIOUS PENDING ALERT FROM A CALBACK AFTER MARKET EXIT ORDER GETS FILLED, IT'S UNRELIABLE EXIT IF WE KILL IT HERE
			// LOOK AT EMERGENCY CLASSES, SOLUTION MIGHT BE THERE ALREADY
			return this.PositionKillExitAlert(position, signalName);
		}
		public string ReasonWhyNewStopLossOffsetDoesntMakeSense(Position position, double newStopLossNegativeOffset, bool internalCallee = false) {
			PositionPrototype proto = position.Prototype;
			if (position.Symbol != proto.Symbol) {
				string msg1 = "NotYetImplemented: your script changes StopLossOffset for a proto.Symbol[" + proto.Symbol + "]!=position.Symbol[" + position.Symbol + "]";
				throw new Exception(msg1);
			}
			Direction dir = MarketConverter.EntryDirectionFromLongShort(proto.LongShort);
			if (position.EntryAlert.Direction != dir) {
				string msg1 = "NotYetImplemented: Crazy check here";
				throw new Exception(msg1);
			}

			string msg = "";
			double priceBestBidAsk = executor.DataSource.StreamingProvider.StreamingDataSnapshot.BidOrAskFor(proto.Symbol, proto.LongShort);
			double newStopLossPrice = proto.OffsetToPrice(newStopLossNegativeOffset);
			switch (proto.StopLossAlertForAnnihilation.MarketLimitStop) {
				#region StopLimits are considered NYI; mess v1 implementation
				case MarketLimitStop.StopLimit:
					double newActivationOffset = proto.CalcActivationOffsetForNewClosing(newStopLossNegativeOffset);
					//double lastPrice = executor.DataSource.StreamingProvider.StreamingDataSnapshot.LastQuoteGetPriceForMarketOrder(proto.Symbol);
					//Quote quote = executor.DataSource.StreamingProvider.StreamingDataSnapshot.LastQuoteGetForSymbol(proto.Symbol);
					double newActivationPrice = proto.OffsetToPrice(newActivationOffset);
					bool willBeExecutedImmediately = false;
					string ident = "StopLoss{old[" + proto.PriceStopLoss + "]:new[" + newStopLossPrice + "]}"
								 + " Activation{old[" + proto.PriceStopLossActivation + "]:new[" + newActivationPrice + "]}";
					switch (dir) {
						case Direction.Buy:
							if (newActivationPrice > priceBestBidAsk) {
								willBeExecutedImmediately = true;
								msg = "newActivationPrice[" + newActivationPrice + "] > Bid[" + priceBestBidAsk + "] " + ident
									+ " your SLactivation goes above current price so the StopLoss will be activated/filled immediately..."
									+ " //position[" + position + "]";
							}
							break;
						case Direction.Short:
							if (newActivationPrice < priceBestBidAsk) {
								willBeExecutedImmediately = true;
								msg = "newActivationPrice[" + newActivationPrice + "] < Ask[" + priceBestBidAsk + "] " + ident
									+ " your SLactivation goes below current price so the StopLoss will be activated/filled immediately..."
									+ " //position[" + position + "]";
							}
							break;
						default:
							msg = "I refuse to change StopLoss when position.EntryAlert.Direction=["
								+ position.EntryAlert.Direction + "] - must be Buy or Short only!!!";
							break;
					}
					break;
				#endregion
				case MarketLimitStop.Stop:
					string ident2 = "PureStopLoss{old[" + proto.PriceStopLoss + "]:new[" + newStopLossPrice + "]}";
					switch (proto.StopLossAlertForAnnihilation.Direction) {
						case Direction.Sell:
							double ask = executor.DataSource.StreamingProvider.StreamingDataSnapshot.BestAskGetForMarketOrder(proto.Symbol);
							if (newStopLossPrice > ask) {
								msg = "NEW_STOP_PRICE_BELOW_ASK_WILL_BE_REJECTED_BY_MARKET"
									+ " newStopLossPrice[" + newStopLossPrice + "] < Ask[" + ask + "] " + ident2
									+ " //position[" + position + "]";
							}
							break;
						case Direction.Cover:
							double bid = executor.DataSource.StreamingProvider.StreamingDataSnapshot.BestBidGetForMarketOrder(proto.Symbol);
							if (newStopLossPrice > bid) {
								msg = "NEW_STOP_PRICE_ABOVE_BID_WILL_BE_REJECTED_BY_MARKET"
									+ " newStopLossPrice[" + newStopLossPrice + "] > Bid[" + bid + "] " + ident2
									+ " //position[" + position + "]";
							}
							break;
						default:
							msg = "PROTOTYPE_BASED_STOP_LOSS_MODIFICATION_REFUSED_MUSTBE_SELL_OR_COVER"
								+ ";  position.ExitA.Direction=[" + position.ExitAlert.Direction + "]";
							break;
					}
					break;
				default:
					msg = "STOP_LOSS_ALERT_TYPE_NOT_SUPPORTED [" + proto.StopLossAlertForAnnihilation.MarketLimitStop + "]";
					throw new Exception(msg);
			}
			if (internalCallee == true) {
				msg += " (Script is strongly recommented to check SL price first so we don't pass unexpected position closures to the Market)";
			}
			return msg;
		}
		public override void OnPositionClosedCallback(Position positionClosed) {
			//Debugger.Break();
		}
		public Alert CoverAtClose(Bar bar, Position position, string signalName = "COVERED_AT_CLOSE") {
			return this.Executor.SellOrCoverAlertCreateRegister(bar, position, 0, signalName, Direction.Cover, MarketLimitStop.AtClose);
		}
		public bool AnnihilateCounterpartyForClosedPosition(Position position) {
			bool killed = false;
			if (position.IsExitFilled == false) {
				string msg = "I refuse to AnnihilateCounterpartyForClosedPosition: position.IsExitFilled=false for position=[" + position + "]";
				throw new Exception(msg);
			}
			if (position.Prototype == null) {
				string msg = "I refuse to AnnihilateCounterpartyForClosedPosition: position.Prototype=null";
				throw new Exception(msg);
			}
			if (position.IsExitFilledByPrototypedTakeProfit) {
				if (position.Prototype.StopLossAlertForAnnihilation == null) {
					string msg = "FAILED_ANNIHILATE_STOPLOSS Prototype.StopLossAlertForAnnihilation=null for position[" + position + "]";
					throw new Exception(msg);
				}
				killed = executor.AnnihilateCounterpartyAlertDispatched(position.Prototype.StopLossAlertForAnnihilation);
				if (executor.Backtester.IsBacktestingNow == false) {
					string msg = "killed[" + killed + "] counterParty [" + position.Prototype.StopLossAlertForAnnihilation + "]";
					this.appendMessageToTakeProfitOrder(position, msg);
				}
				if (killed == false) {
					string msg = "position.ClosedByTakeProfit but StopLoss wasn't annihilated"
						+ " or DUPE annihilation of a previously annihilated StopLoss";
					throw new Exception(msg);
				}
			}
			if (position.IsExitFilledByPrototypedStopLoss) {
				if (position.Prototype.TakeProfitAlertForAnnihilation == null) {
					string msg = "FAILED_ANNIHILATE_TAKEPROFIT Prototype.TakeProfitAlertForAnnihilation=null for position[" + position + "]";
					throw new Exception(msg);
				}
				killed = executor.AnnihilateCounterpartyAlertDispatched(position.Prototype.TakeProfitAlertForAnnihilation);
				if (executor.Backtester.IsBacktestingNow == false) {
					string msg = "killed[" + killed + "] counterParty [" + position.Prototype.TakeProfitAlertForAnnihilation + "]";
					this.appendMessageToStopLossOrder(position, msg);
				}
				if (killed == false) {
					string msg = "position.ClosedByStopLoss but TakeProfit wasn't annihilated"
						+ " or DUPE annihilation of a previously annihilated TakeProfit";
					throw new Exception(msg);
				}
			}
			return killed;
		}
		void appendMessageToTakeProfitOrder(Position position, string msgOrder) {
			if (position.Prototype.TakeProfitAlertForAnnihilation == null) {
				string msg = "Can't AppendMessageToTakeProfitOrder: position.Prototype.TakeProfitAlertForAnnihilation=null";
				throw new Exception(msg);
			}
			if (position.Prototype.TakeProfitAlertForAnnihilation.OrderFollowed == null) {
				string msg = "Can't AppendMessageToTakeProfitOrder: position.Prototype.TakeProfitAlertForAnnihilation.OrderFollowed=null";
				throw new Exception(msg);
			}
			position.Prototype.TakeProfitAlertForAnnihilation.OrderFollowed.AppendMessage(msgOrder);
		}
		public Alert CreateTakeProfitFromPositionPrototype(Position position) {
			PositionPrototype proto = position.Prototype;
			if (proto.TakeProfitPositiveOffset == 0) {
				string msg = "What should Activator do with proto.StopLossNegativeOffset=0?";
				throw new Exception(msg);
			}

			Alert alertTakeProfit = executor.SellOrCoverAlertCreateDontRegister (
				executor.Bars.BarStreaming,
				position, proto.PriceTakeProfit,
				"protoTakeProfitExit:" + proto.TakeProfitPositiveOffset
					+ "@" + proto.PriceTakeProfit + " for " + position.EntrySignal,
				MarketConverter.ExitDirectionFromLongShort(position.Prototype.LongShort),
				MarketLimitStop.Limit);
			if (alertTakeProfit == null) {
				string msg = "alertTakeProfit should NOT be null";
				throw new Exception(msg);
			}
			proto.TakeProfitAlertForAnnihilation = alertTakeProfit;
			return alertTakeProfit;
		}
		public bool Equals(Position otherPosition) {
			bool headerIsTheSame = this.PositionLongShort == otherPosition.PositionLongShort
				&& this.Symbol == otherPosition.Symbol
				&& this.Shares == otherPosition.Shares;
			if (headerIsTheSame == false) return false;

			bool entryIsTheSame = this.EntryMarketLimitStop == otherPosition.EntryMarketLimitStop
				&& this.EntryFilledBarIndex == otherPosition.EntryFilledBarIndex
				&& this.EntryFilledPrice == otherPosition.EntryFilledPrice
				&& this.EntrySignal == otherPosition.EntrySignal;
			if (entryIsTheSame == false) return false;

			// what did I want to say by this??
			//if (this.ExitBar == -1) return true;
			//if (otherPosition.ExitBar == -1) return true;

			bool bothNaNs = double.IsNaN(this.ExitFilledPrice) && double.IsNaN(otherPosition.ExitFilledPrice);
			if (bothNaNs) {
				bool exitIsTheSameNaNs = this.EntryMarketLimitStop == otherPosition.EntryMarketLimitStop
					&& this.ExitFilledBarIndex == otherPosition.ExitFilledBarIndex
					&& this.ExitSignal == otherPosition.ExitSignal;
				return exitIsTheSameNaNs;
			}
			bool exitIsTheSame = this.EntryMarketLimitStop == otherPosition.EntryMarketLimitStop
				&& this.ExitFilledBarIndex == otherPosition.ExitFilledBarIndex
				&& this.ExitFilledPrice == otherPosition.ExitFilledPrice
				&& this.ExitSignal == otherPosition.ExitSignal;
			//bool alwaysDifferent = this.SernoInMasterList == position.SernoInMasterList;
			return exitIsTheSame;
		}
		public List<Alert> PositionKillExitAlert(Position position, string signalName) {
			List<Alert> alertsSubmittedToKill = new List<Alert>();
			if (position.IsEntryFilled == false) {
				Debugger.Break();
				return alertsSubmittedToKill;
			}
			if (null == position.ExitAlert) {	// for prototyped position, position.ExitAlert contains TakeProfit 
				Debugger.Break();
				return alertsSubmittedToKill;
			}
			if (string.IsNullOrEmpty(signalName)) signalName = "PositionCloseImmediately()";
			if (position.Prototype != null) {
				alertsSubmittedToKill = this.PositionPrototypeKillWhateverIsPending(position.Prototype, signalName);
				return alertsSubmittedToKill;
			}
			this.AlertKillPending(position.ExitAlert);
			alertsSubmittedToKill.Add(position.ExitAlert);
			return alertsSubmittedToKill;
		}
		public override void OnPositionOpenedCallback(Position positionOpened) {
			// NEVER_INVOKED_SINCE_I_USE_POSITION_PROTOTYPES_ONLY no direct BuyAt* or SellAt*
			Debugger.Break();
		}
		public Alert SellAtStop(Bar bar, Position position, double stopPrice, string signalName = "SOLD_AT_STOP") {
			return this.Executor.SellOrCoverAlertCreateRegister(bar, position, stopPrice, signalName, Direction.Sell, MarketLimitStop.Stop);
		}
		public void TakeProfitNewPositiveOffsetUpdateActivate(Position position, double newTakeProfitPositiveOffset) {
			PositionPrototype proto = position.Prototype;
			if (proto == null) {
				string msg = "TakeProfitNewPositiveOffsetUpdateActivate() can't update TakeProfit for a position.Prototype=null: position=[" + position + "]";
				throw new Exception(msg);
			}
			this.checkThrowNewTakeProfitOffsetMakesSense(position, newTakeProfitPositiveOffset);
			//proto.checkOffsetsThrowBeforeAbsorbing(newTakeProfitPositiveOffset, proto.StopLossNegativeOffset, proto.StopLossActivationNegativeOffset);
			if (executor.Backtester.IsBacktestingNow) {
				// NOPE!!! I WANT ALL ALERTS TO STAY IN HISTORY!!! just move TP in Alert, change Prices immediately; no orderKill/newOrder for TakeProfit (Alerts don't have OrdersFollowed)
				// PositionPrototypeActivator.checkThrowNewPriceMakesSense() made sure Alert!=null
				// TP_WILL_BE_KILLED proto.TakeProfitAlertForAnnihilation.PriceScript = proto.OffsetToPrice(newTakeProfitPositiveOffset);
				proto.SetNewTakeProfitOffset(newTakeProfitPositiveOffset);
				// TODO: don't forget about backtesting and MarketSim (implement OppMover for offline)
				executor.MarketSimStreaming.SimulateTakeProfitMoved(proto.TakeProfitAlertForAnnihilation);
			} else {
				executor.DataSource.BrokerProvider.MoveTakeProfitOrderProcessorInvoker(proto, newTakeProfitPositiveOffset);
			}
		}
		public Alert ExitAtStop(Bar bar, Position position, double price, string signalName = "EXITED_AT_STOP") {
			if (position.PositionLongShort == PositionLongShort.Long) {
				return this.SellAtStop(bar, position, price, signalName);
			} else {
				return this.CoverAtStop(bar, position, price, signalName);
			}
		}
		public PositionEventArgs(Execution.Position position) {
			this.Position = position;
		}
		void checkThrowNewTakeProfitOffsetMakesSense(Position position, double newTakeProfitPositiveOffset) {
			string msg = this.ReasonWhyNewTakeProfitOffsetDoesntMakeSense(position, newTakeProfitPositiveOffset);
			if (String.IsNullOrEmpty(msg) == false) throw new Exception(msg);
		}
		public void StopLossNewNegativeOffsetUpdateActivate(Position position, double newStopLossNegativeOffset) {
			string msig = " StopLossNewNegativeOffsetUpdateActivate(position[" + position + "], newStopLossNegativeOffset[" + newStopLossNegativeOffset + "])";
			PositionPrototype proto = position.Prototype;
			if (proto == null) {
				string msg = "StopLossNewNegativeOffsetUpdateActivate() can't update StopLoss for a position.Prototype=null: position=[" + position + "]";
				throw new Exception(msg);
			}
			this.checkThrowNewStopLossOffsetMakesSense(position, newStopLossNegativeOffset);
			double newActivationOffset = proto.CalcActivationOffsetForNewClosing(newStopLossNegativeOffset);
			switch (proto.StopLossAlertForAnnihilation.MarketLimitStop) {
				case MarketLimitStop.StopLimit:
					#region StopLimit are considered NYI in Backtester.QuoteGenerator; switched to Stops since they do SL job perfectly;
					//proto.checkOffsetsThrowBeforeAbsorbing(proto.TakeProfitPositiveOffset, newStopLossNegativeOffset, newActivationOffset);
					if (executor.Backtester.IsBacktestingNow) {
						// NOPE!!! I WANT ALL ALERTS TO STAY IN HISTORY!!! just move SL in Alert, change Prices immediately; no orderKill/newOrder for StopLoss (Alerts don't have OrdersFollowed)
						// PositionPrototypeActivator.checkThrowNewPriceMakesSense() made sure Alert!=null
						// SL_WILL_BE_KILLED proto.StopLossAlertForAnnihilation.PriceScript = proto.OffsetToPrice(newStopLossNegativeOffset);
						// SL_WILL_BE_KILLED proto.StopLossAlertForAnnihilation.PriceStopLimitActivation = proto.OffsetToPrice(newActivationOffset);
						proto.SetNewStopLossOffsets(newStopLossNegativeOffset, newActivationOffset);
						// TODO: don't forget about backtesting and MarketSim (implement OppMover for offline)
						executor.MarketSimStreaming.SimulateStopLossMoved(proto.StopLossAlertForAnnihilation);
					} else {
						executor.DataSource.BrokerProvider.MoveStopLossOrderProcessorInvoker(proto, newActivationOffset, newStopLossNegativeOffset);
					}
					break;
					#endregion
				case MarketLimitStop.Stop:
					if (executor.Backtester.IsBacktestingNow) {
						proto.SetNewStopLossOffsets(newStopLossNegativeOffset, 0);
						// TODO: don't forget about backtesting and MarketSim (implement OppMover for offline)
						executor.MarketSimStreaming.SimulateStopLossMoved(proto.StopLossAlertForAnnihilation);
					} else {
						executor.DataSource.BrokerProvider.MoveStopLossOrderProcessorInvoker(proto, 0, newStopLossNegativeOffset);
					}
					break;
				default:
					string msg = "UNSUPPORTED_STOP_LOSS_CANT_MOVE [" + proto.StopLossAlertForAnnihilation.MarketLimitStop + "] must be Stop or StopLimit(weak support now, almost NYI)";
					throw new Exception(msg + msig);
			}
		}
		public Alert SellAtMarket(Bar bar, Position position, string signalName = "SOLD_AT_MARKET") {
			return this.Executor.SellOrCoverAlertCreateRegister(bar, position, 0, signalName, Direction.Sell, MarketLimitStop.Market);
		}