public void ConsumeQuoteOfStreamingBar(Quote quote) { //Bar barLastFormed = quoteToReach.ParentStreamingBar; ExecutionDataSnapshot snap = this.backtester.Executor.ExecutionDataSnapshot; foreach (Indicator indicator in snap.Indicators.Values) { try { indicator.OnNewStreamingQuote(quote); } catch (Exception ex) { Debugger.Break(); } } if (snap.AlertsPending.Count > 0) { var dumped = snap.DumpPendingAlertsIntoPendingHistoryByBar(); if (dumped > 0) { //string msg = "here is at least one reason why dumping on fresh quoteToReach makes sense" // + " if we never reach this breakpoint the remove dump() from here" // + " but I don't see a need to invoke it since we dumped pendings already after OnNewBarCallback"; string msg = "DUMPED_BEFORE_SCRIPT_EXECUTION_ON_NEW_BAR_OR_QUOTE"; } int pendingCountPre = this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count; int pendingFilled = this.backtester.Executor.MarketSimStreaming.SimulatePendingFill(quote); int pendingCountNow = this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count; if (pendingCountNow != pendingCountPre - pendingFilled) { string msg = "NOT_ONLY it looks like AnnihilateCounterparty worked out!"; } if (pendingCountNow > 0) { string msg = "pending=[" + pendingCountNow + "], it must be prototype-induced 2 closing TP & SL"; } } //this.backtester.Executor.Script.OnNewQuoteCallback(quoteToReach); ReporterPokeUnit pokeUnit = this.backtester.Executor.ExecuteOnNewBarOrNewQuote(quote); }
public virtual int InjectQuotesToFillPendingAlerts(Quote quoteToReach) { int quotesInjected = 0; int pendingsToFillInitially = this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count; if (pendingsToFillInitially == 0) return quotesInjected; int iterationsLimit = 5; // hard to debug but I hate while(){} loops //for (Quote closestOnOurWay = this.generateClosestQuoteForEachPendingAlertOnOurWayTo(quoteToReach); // closestOnOurWay != null; // closestOnOurWay = this.generateClosestQuoteForEachPendingAlertOnOurWayTo(quoteToReach)) { Quote closestOnOurWay = this.generateClosestQuoteForEachPendingAlertOnOurWayTo(quoteToReach); while (closestOnOurWay != null) { quotesInjected++; closestOnOurWay.IntraBarSerno += quotesInjected; this.backtester.BacktestDataSource.BacktestStreamingProvider.PushQuoteReceived(closestOnOurWay); int pendingAfterInjected = this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count; if (pendingsToFillInitially != pendingAfterInjected) { string msg = "it looks like the quoteInjected triggered something"; //Debugger.Break(); } if (quotesInjected > iterationsLimit) { string msg = "InjectQuotesToFillPendingAlerts(): quotesInjected[" + quotesInjected + "] > iterationsLimit[" + iterationsLimit + "]" + " pendingNow[" + this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count + "]" + " quoteToReach[" + quoteToReach + "]"; //throw new Exception(msg); break; } if (this.backtester.BacktestAborted.WaitOne(0)) break; if (this.backtester.RequestingBacktestAbort.WaitOne(0)) break; closestOnOurWay = this.generateClosestQuoteForEachPendingAlertOnOurWayTo(quoteToReach); } return quotesInjected; }
protected void LastQuoteUpdate(Quote quote) { if (this.LastQuotesReceived[quote.Symbol] == quote) { string msg = "How come you update twice to the same quote?"; return; } this.LastQuotesReceived[quote.Symbol] = quote; }
public void GeneratedQuoteEnrichSymmetricallyAndPush(Quote quote) { if (this.SpreadModeler == null) { string msg = "Don't leave quoteToReach.Bid and quoteToReach.Ask uninitialized!!!"; throw new Exception(msg); } this.SpreadModeler.GenerateFillBidAskSymmetricallyFromLastPrice(quote); base.PushQuoteReceived(quote); }
public virtual void UpdateLastBidAskSnapFromQuote(Quote quote) { this.LastQuoteUpdate(quote); if (double.IsNaN(quote.Bid) || double.IsNaN(quote.Ask)) { if (false) throw new Exception("You seem to process Bars.LastBar with Partials=NaN"); return; } if (quote.Bid != 0 && quote.Ask != 0) { this.BestBidAskPutForSymbol(quote.Symbol, quote.Bid, quote.Ask); } }
public Quote DeriveIdenticalButFresh() { Quote identicalButFresh = new Quote(); identicalButFresh.Symbol = this.Symbol; identicalButFresh.SymbolClass = this.SymbolClass; identicalButFresh.Source = this.Source; identicalButFresh.ServerTime = this.ServerTime.AddMilliseconds(911); identicalButFresh.LocalTimeCreatedMillis = this.LocalTimeCreatedMillis.AddMilliseconds(911); identicalButFresh.PriceLastDeal = this.PriceLastDeal; identicalButFresh.Bid = this.Bid; identicalButFresh.Ask = this.Ask; identicalButFresh.Size = this.Size; identicalButFresh.IntraBarSerno = this.IntraBarSerno + Quote.IntraBarSernoShiftForGeneratedTowardsPendingFill; identicalButFresh.ParentStreamingBar = this.ParentStreamingBar; return identicalButFresh; }
protected Quote generateNewQuoteChildrenHelper(int intraBarSerno, string source, string symbol, DateTime serverTime, double price, int volume) { Quote quote = new Quote(); quote.Absno = ++this.QuoteAbsno; quote.ServerTime = serverTime; quote.IntraBarSerno = intraBarSerno; quote.Source = source; quote.Symbol = symbol; quote.SymbolClass = this.backtester.BarsOriginal.SymbolInfo.SymbolClass; quote.Size = volume; quote.PriceLastDeal = price; // moved to BacktestQuoteModeler //quote.Bid = quote.PriceLastDeal - 10; //quote.Ask = quote.PriceLastDeal + 10; return quote; }
public Quote BindStreamingBarForQuote(Quote quoteSernoEnrichedWithStreamingBarUnattached) { if (consumer.ConsumerBarsToAppendInto == null) { // StreamingSolidifier will attach the Bar itself return quoteSernoEnrichedWithStreamingBarUnattached; } // if (consumer.ConsumerBarsToAppendInto.BarStreaming != null) { // // first four quotes there is no BarStreaming?.../ // return quoteSernoEnrichedWithStreamingBarUnattached; // } //v1 consumer.ConsumerBarsToAppendInto.OverrideStreamingDOHLCVwith(quoteSernoEnrichedWithStreamingBarUnattached.ParentStreamingBar); consumer.ConsumerBarsToAppendInto.CreateNewOrAbsorbIntoStreaming(quoteSernoEnrichedWithStreamingBarUnattached.ParentStreamingBar); Quote quoteAttached = quoteSernoEnrichedWithStreamingBarUnattached.Clone(); quoteAttached.SetParentBar(consumer.ConsumerBarsToAppendInto.BarStreaming); return quoteAttached; }
public virtual Quote EnrichQuoteWithSernoUpdateStreamingBarCreateNewBar(Quote quoteClone) { if (quoteClone.PriceLastDeal == 0) { string msg = "quote.PriceLastDeal[" + quoteClone.PriceLastDeal + "] == 0;" + "what kind of quote is that?... (" + quoteClone + ")"; throw new Exception(msg); //return; } if (StreamingBar.Symbol != quoteClone.Symbol) { string msg = "StreamingBar.Symbol=[" + StreamingBar.Symbol + "]!=quote.Symbol[" + quoteClone.Symbol + "] (" + quoteClone + ")"; throw new Exception(msg); //return; } // included in if (quoteClone.ServerTime >= StreamingBar.DateTimeNextBarOpenUnconditional) !!! // on very first quote StreamingBar.DateTimeNextBarOpenUnconditional = DateTime.MinValue //SEE_BELOW if (StreamingBar.DateTimeOpen == DateTime.MinValue) //SEE_BELOW this.initStreamingBarResetIntraBarSerno(quoteClone.ServerTime, quoteClone.PriceLastDeal, quoteClone.Size); //SEE_BELOW } if (quoteClone.ServerTime >= StreamingBar.DateTimeNextBarOpenUnconditional) { LastBarFormedUnattached = StreamingBar.Clone(); //beware! on very first quote LastBarFormed.DateTimeOpen == DateTime.MinValue initStreamingBarResetIntraBarSerno(quoteClone.ServerTime, quoteClone.PriceLastDeal, quoteClone.Size); } else { if (Double.IsNaN(StreamingBar.Open) || StreamingBar.Open == 0.0) { throw new Exception("nonsense! we should've had StreamingBar already initialized with first quote of a bar"); //log.Warn("Initializing OHL as quote.PriceLastDeal[" + quoteClone.PriceLastDeal + "];" // + " following previous InitWithStreamingBarInsteadOfEmpty message" // + " (if absent then never initialized)"); //StreamingBar.Open = quoteClone.PriceLastDeal; //StreamingBar.High = quoteClone.PriceLastDeal; //StreamingBar.Low = quoteClone.PriceLastDeal; } if (quoteClone.PriceLastDeal > StreamingBar.High) StreamingBar.High = quoteClone.PriceLastDeal; if (quoteClone.PriceLastDeal < StreamingBar.Low) StreamingBar.Low = quoteClone.PriceLastDeal; StreamingBar.Close = quoteClone.PriceLastDeal; StreamingBar.Volume += quoteClone.Size; IntraBarSerno++; } quoteClone.IntraBarSerno = IntraBarSerno; quoteClone.SetParentBar(StreamingBar); return quoteClone; }
public override void OnNewQuoteOfStreamingBarCallback(Quote quote) { //double slowStreaming = this.MAslow.BarClosesProxied.StreamingValue; double slowStatic = this.MAslow.ClosesProxyEffective.LastStaticValue; DateTime slowStaticDate = this.MAslow.ClosesProxyEffective.LastStaticDate; if (this.Executor.Backtester.IsBacktestingNow == false) { Bar bar = quote.ParentStreamingBar; int barNo = bar.ParentBarsIndex; if (barNo == -1) return; DateTime lastStaticBarDateTime = bar.ParentBars.BarStaticLast.DateTimeOpen; DateTime streamingBarDateTime = bar.DateTimeOpen; Bar barNormalizedDateTimes = new Bar(bar.Symbol, bar.ScaleInterval, quote.ServerTime); DateTime thisBarDateTimeOpen = barNormalizedDateTimes.DateTimeOpen; int a = 1; } //log("OnNewQuoteCallback(): [" + quote.ToString() + "]"); string msg = "OnNewQuoteCallback(): [" + quote.ToString() + "]"; log("EnterEveryBar.cs now=[" + DateTime.Now.ToString("ddd dd-MMM-yyyy HH:mm:ss.fff" + "]: " + msg)); if (quote.IntraBarSerno == 0) { return; } }
public void PrintQuoteTimestampsOnStreamingButtonBeforeExecution(Quote quote) { if (quote == null) return; if (InvokeRequired) { base.BeginInvoke((MethodInvoker)delegate { this.PrintQuoteTimestampsOnStreamingButtonBeforeExecution(quote); }); return; } StringBuilder sb = new StringBuilder( "StreamingOn #" + quote.IntraBarSerno.ToString("000") + " " + quote.ServerTime.ToString("HH:mm:ss.fff")); bool quoteTimesDifferMoreThanOneMicroSecond = quote.ServerTime.ToString("HH:mm:ss.f") != quote.LocalTimeCreatedMillis.ToString("HH:mm:ss.f"); if (quoteTimesDifferMoreThanOneMicroSecond) { sb.Append(" :: " + quote.LocalTimeCreatedMillis.ToString("HH:mm:ss.fff")); } if (quote.HasParentBar) { TimeSpan timeLeft = (quote.ParentStreamingBar.DateTimeNextBarOpenUnconditional > quote.ServerTime) ? quote.ParentStreamingBar.DateTimeNextBarOpenUnconditional.Subtract(quote.ServerTime) : quote.ServerTime.Subtract(quote.ParentStreamingBar.DateTimeNextBarOpenUnconditional); string format = ":ss"; if (timeLeft.Minutes > 0) format = "mm:ss"; if (timeLeft.Hours > 0) format = "HH:mm:ss"; sb.Append(" " + new DateTime(timeLeft.Ticks).ToString(format)); } this.btnStreaming.Text = sb.ToString(); }
public void PushQuoteToConsumers(Quote quote2bClonedForEachConsumer) { if (String.IsNullOrEmpty(quote2bClonedForEachConsumer.Symbol)) { Assembler.PopupException("quote.Symbol[" + quote2bClonedForEachConsumer.Symbol + "] is null or empty, returning"); return; } if (quote2bClonedForEachConsumer.Symbol != this.Symbol) { Assembler.PopupException("quote.Symbol[" + quote2bClonedForEachConsumer.Symbol + "] != this.Symbol[" + this.Symbol + "], returning"); return; } Quote quoteSernoEnrichedWithUnboundStreamingBar = StreamingBarFactoryUnattached. EnrichQuoteWithSernoUpdateStreamingBarCreateNewBar(quote2bClonedForEachConsumer); if (quoteSernoEnrichedWithUnboundStreamingBar.IntraBarSerno == 0) { if (StreamingBarFactoryUnattached.LastBarFormedUnattached != null //&& Double.IsNaN(StreamingBarFactoryUnattached.LastBarFormedUnattached.Close) == true && StreamingBarFactoryUnattached.LastBarFormedUnattached.DateTimeOpen != DateTime.MinValue ) { lock (lockConsumersBar) { //try { this.bindNewStreamingBarAppendPokeConsumersStaticFormed(); //} catch (Exception e) { // string msg = "is this why barsSimulated.Count=6 while barOriginal=31/50??"; // throw new Exception(msg); //} } } else { string msg = "I won't push LastStaticBar(DateTime.MinValue, NaN*5) on first quote2bClonedForEachConsumer[" + quote2bClonedForEachConsumer + "]" + " because it has initialized LastStaticBar=StreamingBar.Clone()" + " for " + StreamingBarFactoryUnattached; //Assembler.PopupException(msg); } } lock (lockConsumersQuote) { this.bindStreamingBarForQuoteAndPushQuoteToConsumers(quoteSernoEnrichedWithUnboundStreamingBar.Clone()); } }
public virtual void OnNewQuoteOfStreamingBarCallback(Quote quoteNewArrived) { }
void IStreamingConsumer.ConsumeQuoteOfStreamingBar(Sq1.Core.DataTypes.Quote quote) { }
public virtual Quote EnrichQuoteWithSernoUpdateStreamingBarCreateNewBar(Quote quoteClone) { if (quoteClone.PriceLastDeal == 0) { string msg = "quote.PriceLastDeal[" + quoteClone.PriceLastDeal + "] == 0;" + "what kind of quote is that?... (" + quoteClone + ")"; throw new Exception(msg); //return; } if (this.StreamingBarUnattached.Symbol != quoteClone.Symbol) { string msg = "StreamingBar.Symbol=[" + this.StreamingBarUnattached.Symbol + "]!=quote.Symbol[" + quoteClone.Symbol + "] (" + quoteClone + ")"; throw new Exception(msg); //return; } // included in if (quoteClone.ServerTime >= StreamingBar.DateTimeNextBarOpenUnconditional) !!! // on very first quote StreamingBar.DateTimeNextBarOpenUnconditional = DateTime.MinValue //SEE_BELOW if (StreamingBar.DateTimeOpen == DateTime.MinValue) //SEE_BELOW this.initStreamingBarResetIntraBarSerno(quoteClone.ServerTime, quoteClone.PriceLastDeal, quoteClone.Size); //SEE_BELOW } if (quoteClone.ServerTime >= this.StreamingBarUnattached.DateTimeNextBarOpenUnconditional) { this.LastBarFormedUnattached = this.StreamingBarUnattached.Clone(); //beware! on very first quote LastBarFormed.DateTimeOpen == DateTime.MinValue this.initStreamingBarResetIntraBarSerno(quoteClone.ServerTime, quoteClone.PriceLastDeal, quoteClone.Size); // quoteClone.IntraBarSerno doesn't feel new Bar; can contain 100004 for generatedQuotes; // I only want to reset to 0 when it's attributed to a new Bar; it's unlikely to face a new bar here for generatedQuotes; if (this.IntraBarSerno != 0) { string msg = "STREAMING_JUST_INITED_TO_ZERO WHY_NOW_IT_IS_NOT?"; Debugger.Break(); } if (quoteClone.IntraBarSerno != 0) { if (quoteClone.IntraBarSerno >= Quote.IntraBarSernoShiftForGeneratedTowardsPendingFill) { string msg = "GENERATED_QUOTES_ARENT_SUPPOSED_TO_GO_TO_NEXT_BAR"; Debugger.Break(); } quoteClone.IntraBarSerno = this.IntraBarSerno; } if (this.IntraBarSerno >= Quote.IntraBarSernoShiftForGeneratedTowardsPendingFill) { string msg = "BAR_FACTORY_INTRABAR_SERNO_NEVER_GOES_TO_SYNTHETIC_ZONE"; Debugger.Break(); } } else { if (Double.IsNaN(this.StreamingBarUnattached.Open) || this.StreamingBarUnattached.Open == 0.0) { throw new Exception("nonsense! we should've had StreamingBar already initialized with first quote of a bar"); //log.Warn("Initializing OHL as quote.PriceLastDeal[" + quoteClone.PriceLastDeal + "];" // + " following previous InitWithStreamingBarInsteadOfEmpty message" // + " (if absent then never initialized)"); //StreamingBar.Open = quoteClone.PriceLastDeal; //StreamingBar.High = quoteClone.PriceLastDeal; //StreamingBar.Low = quoteClone.PriceLastDeal; } this.StreamingBarUnattached.MergeExpandHLCVforStreamingBarUnattached(quoteClone); this.IntraBarSerno++; } if (quoteClone.ParentStreamingBar != null) { string msg = "QUOTE_ALREADY_ENRICHED_WITH_PARENT_STREAMING_BAR; I think it's a pre- bindStreamingBarForQueue() atavism"; //Assembler.PopupException(msg); } else { quoteClone.SetParentBar(this.StreamingBarUnattached); } return quoteClone; }
public override void GenerateFillAskBasedOnBid(Quote quote, double bidPrice) { quote.Ask = bidPrice + this.constant; quote.Bid = bidPrice; }
public Quote generateClosestQuoteForEachPendingAlertOnOurWayTo(Quote quoteToReach) { if (this.backtester.Executor.ExecutionDataSnapshot.AlertsPending.Count == 0) { string msg = "it looks like no Pending alerts are left anymore"; return null; } Quote quotePrev = this.backtester.BacktestDataSource.BacktestStreamingProvider.StreamingDataSnapshot .LastQuoteGetForSymbol(quoteToReach.Symbol); // assuming both quotes generated using same SpreadModeler and their spreads are equal //v1 if (quoteToReach.Bid == quotePrev.Bid || quoteToReach.Ask == quotePrev.Ask) { if (quoteToReach.SameBidAsk(quotePrev)) { string msg = "WE_REACHED_QUOTE_TO_REACH_SAME_BIDASK_NOTHING_ELSE_TO_GENERATE"; return null; } if (quotePrev.ServerTime >= quoteToReach.ServerTime) { string msg = "WE_REACHED_QUOTE_TO_REACH_SAME_SERVERTIME_NOTHING_ELSE_TO_GENERATE"; return null; } bool scanningDown = quoteToReach.Bid < quotePrev.Bid; Quote quoteClosest = null; List<Alert> alertsPendingSafeCopy = new List<Alert>(this.backtester.Executor.ExecutionDataSnapshot.AlertsPending); foreach (Alert alert in alertsPendingSafeCopy) { // DONT_EXPECT_THEM_TO_BE_FILLED_YOU_SHOULD_FILL_ALL_RELEVANT_NOW //if (scanningDown) { // // while GEN'ing down, all BUY/COVER STOPs pending were already triggered & executed // bool executedAtAsk = alert.Direction == Direction.Buy || alert.Direction == Direction.Cover; // if (executedAtAsk && alert.MarketLimitStop == MarketLimitStop.Stop) continue; //} else { // // while GEN'ing up, all SHORT/SELL STOPs pending were already triggered & executed // bool executedAtBid = alert.Direction == Direction.Short || alert.Direction == Direction.Sell; // if (executedAtBid && alert.MarketLimitStop == MarketLimitStop.Stop) continue; //} Quote quoteThatWillFillAlert = this.modelQuoteThatCouldFillAlert(alert); if (quoteThatWillFillAlert == null) continue; if (scanningDown) { if (quoteThatWillFillAlert.Bid > quotePrev.Bid) { string msg = "IGNORING_QUOTE_HIGHER_THAN_PREVIOUS_WHILE_SCANNING_DOWN"; continue; } if (quoteThatWillFillAlert.Bid < quoteToReach.Bid) { string msg = "IGNORING_QUOTE_LOWER_THAN_TARGET_WHILE_SCANNING_DOWN"; continue; } } else { if (quoteThatWillFillAlert.Ask < quotePrev.Ask) { string msg = "IGNORING_QUOTE_LOWER_THAN_PREVIOUS_WHILE_SCANNING_UP"; continue; } if (quoteThatWillFillAlert.Ask > quoteToReach.Ask) { string msg = "IGNORING_QUOTE_HIGHER_THAN_TARGET_WHILE_SCANNING_UP"; continue; } } if (quoteClosest == null) { quoteClosest = quoteThatWillFillAlert; } else { if (scanningDown) { // while GEN'ing closest to the top, pick the highest possible // while scanning down, I'm looking for the quote with highest Ask that will trigger the closest pending if (quoteThatWillFillAlert.Bid < quoteClosest.Bid) continue; quoteClosest = quoteThatWillFillAlert; } else { // while GEN'ing closest to the bottom, pick the lowest possible if (quoteThatWillFillAlert.Ask > quoteClosest.Ask) continue; quoteClosest = quoteThatWillFillAlert; } } } if (quoteClosest == null) return quoteClosest; if (scanningDown) { if (quoteClosest.Bid > quotePrev.Bid) { string msg = "WHILE_SCANNING_DOWN_RETURNING_QUOTE_HIGHER_THAN_PREVIOUS_IS_WRONG"; } if (quoteClosest.Bid < quoteToReach.Bid) { string msg = "WHILE_SCANNING_DOWN_RETURNING_QUOTE_LOWER_THAN_TARGET_IS_WRONG"; } } else { if (quoteClosest.Ask < quotePrev.Ask) { string msg = "WHILE_SCANNING_UP_RETURNING_QUOTE_LOWER_THAN_PREVIOUS_IS_WRONG"; } if (quoteClosest.Ask > quoteToReach.Ask) { string msg = "WHILE_SCANNING_UP_RETURNING_QUOTE_HIGHER_THAN_TARGET_IS_WRONG"; } } //if (quotePrev.ParentStreamingBar.ParentBarsIndex == 185) { if (this.backtester.Executor.Bars.Count == 185) { //Debugger.Break(); int q = 1; } //I_WILL_SPOIL_STREAMING_BAR_IF_I_ATTACH_LIKE_THIS Quote quoteNextAttached = this.backtester.BacktestDataSource.BacktestStreamingProvider.(quoteToReach.Clone()); Quote ret = quotePrev.DeriveIdenticalButFresh(); ret.Bid = quoteClosest.Bid; ret.Ask = quoteClosest.Ask; ret.Size = quoteClosest.Size; ret.Source = quoteClosest.Source; return ret; }
public bool CheckEntryAlertWillBeFilledByQuote(Alert entryAlert, Quote quote, out double entryPriceOut, out double entrySlippageOutNotUsed) { // if entry is triggered, call position.EnterFinalize(entryPrice, entrySlippage, entryCommission); entryPriceOut = this.executor.AlignAlertPriceToPriceLevel(entryAlert.Bars, entryAlert.PriceScript, true, entryAlert.PositionLongShortFromDirection, entryAlert.MarketLimitStop); entrySlippageOutNotUsed = 0; //Direction directionPositionClose = MarketConverter.ExitDirectionFromLongShort(alert.PositionLongShortFromDirection); //double slippageLimit = this.executor.getSlippage( // alert.PriceScript, alert.Direction, 0, this.executor.IsStreaming, true); //double slippageNonLimit = this.executor.getSlippage( // alert.PriceScript, alert.Direction, 0, this.executor.IsStreaming, false); //double slippageMarketForClosingPosition = this.executor.getSlippage( // alert.PriceScript, directionPositionClose, 0, this.executor.IsStreaming, false); int barIndexTested = entryAlert.Bars.Count - 1; switch (entryAlert.MarketLimitStop) { case MarketLimitStop.Limit: switch (entryAlert.Direction) { case Direction.Buy: //dont check if (slippage >= 0) throw new Exception("BuyAtLimit: slippage[" + slippage + "] should be negative -"); //if (priceScriptAligned + slippageLimit < quoteToReach.Bid) return false; if (entryPriceOut < quote.Bid) return false; break; case Direction.Short: //dont check if (slippage <= 0) throw new Exception("ShortAtLimit: slippage[" + slippage + "] should be positive +"); //if (priceScriptAligned + slippageLimit > quoteToReach.Ask) return false; if (entryPriceOut > quote.Ask) return false; break; default: throw new Exception("NYI: direction[" + entryAlert.Direction + "] is not Long or Short"); } break; case MarketLimitStop.Stop: switch (entryAlert.Direction) { case Direction.Buy: if (entryPriceOut > quote.Ask) return false; //entrySlippageOutNotUsed = slippageNonLimit; //priceScriptAligned += entrySlippageOutNotUsed; break; case Direction.Short: if (entryPriceOut < quote.Bid) return false; //entrySlippageOutNotUsed = -slippageNonLimit; //priceScriptAligned += entrySlippageOutNotUsed; break; default: throw new Exception("NYI: direction[" + entryAlert.Direction + "] is not Long or Short"); } entryPriceOut = this.executor.AlignAlertPriceToPriceLevel(entryAlert.Bars, entryPriceOut, true, entryAlert.PositionLongShortFromDirection, entryAlert.MarketLimitStop); break; case MarketLimitStop.Market: case MarketLimitStop.AtClose: switch (entryAlert.Direction) { case Direction.Buy: if (entryPriceOut > quote.Ask) { entryPriceOut = quote.Ask; string msg = "BuyAtMarket/BuyAtClose(bar[" + barIndexTested + "], signalName[" + entryAlert.PositionAffected.EntrySignal + "]) " + "entryPrice[" + entryPriceOut + "] > := quoteToReach.Ask[" + barIndexTested + "]=[" + quote.Ask + "]" + " while basisPrice=[" + entryAlert.PositionAffected.LastQuoteForMarketOrStopLimitImplicitPrice + "]"; } //entrySlippageOutNotUsed = slippageNonLimit; //priceScriptAligned += entrySlippageOutNotUsed; break; case Direction.Short: if (entryAlert.PriceScript < quote.Bid) { entryPriceOut = quote.Bid; string msg = "ShortAtMarket/ShortAtClose(bar[" + barIndexTested + "], signalName[" + entryAlert.PositionAffected.EntrySignal + "]) " + "entryPrice[" + entryPriceOut + "] < := quoteToReach.Bid[" + barIndexTested + "]=[" + quote.Bid + "]" + " while basisPrice=[" + entryAlert.PositionAffected.LastQuoteForMarketOrStopLimitImplicitPrice + "]"; } //entrySlippageOutNotUsed = -slippageNonLimit; //priceScriptAligned += entrySlippageOutNotUsed; break; default: throw new Exception("CheckEntry() NYI direction[" + entryAlert.Direction + "] for [" + entryAlert + "]"); } break; default: throw new Exception("NYI: marketLimitStop[" + entryAlert.MarketLimitStop + "] is not Limit or Stop"); } entryPriceOut = this.executor.AlignAlertPriceToPriceLevel(entryAlert.Bars, entryPriceOut, true, entryAlert.PositionLongShortFromDirection, entryAlert.MarketLimitStop); if (entryPriceOut <= 0) { string msg = "entryPrice[" + entryPriceOut + "]<=0 what do you mean??? get Bars.LastBar.Close for Market..."; throw new Exception(msg); } // v1 BarStreaming with 1 quoteToReach (=> 0px height) won't contain any price you may check here; // v1 REMOVED because MarketLimitStop.Market haven't been filled immediately and behaved like stop/limit entries/exits; // v1 for Stop/Limit you already hit "return false" above; market should get whatever crazy bid/ask and get the fill at first attempt now //if (alert.Bars.BarStreaming.ContainsPrice(priceScriptAligned) == false) { // earlier version of same checkup as Position.FillEntryWith() // string msg = "QUOTE_UNFILLABLE_ON_BAR_STREAMING quoteToReach[" + quoteToReach + "] => priceScriptAligned[" // + priceScriptAligned + "] at alert.Bars.BarStreaming[" + alert.Bars.BarStreaming + "]"; // //throw new Exception(msg); // return false; //} // /v1 return true; }
public ReporterPokeUnit ExecuteOnNewBarOrNewQuote(Quote quote) { if (this.Strategy.Script == null) return null; ReporterPokeUnit pokeUnit = new ReporterPokeUnit(quote); this.ExecutionDataSnapshot.PreExecutionClear(); int alertsDumpedForStreamingBar = -1; if (quote != null) { try { this.Strategy.Script.OnNewQuoteOfStreamingBarCallback(quote); //alertsDumpedForStreamingBar = this.ExecutionDataSnapshot.DumpPendingAlertsIntoPendingHistoryByBar(); //if (alertsDumpedForStreamingBar > 0) { // string msg = "ITS OK HERE since prev quote has created prototype-based alerts" // + "I WANT DUMP TO BE VALID ONLY IN onNewBar case only!!!" // + " " + alertsDumpedForStreamingBar + " alerts Dumped for " + quote; //} } catch (Exception ex) { string msig = " Script[" + this.Strategy.Script.GetType().Name + "].OnNewQuoteCallback(" + quote + "): "; this.PopupException(ex.Message + msig, ex); } } else { try { this.Strategy.Script.OnBarStaticLastFormedWhileStreamingBarWithOneQuoteAlreadyAppendedCallback(this.Bars.BarStaticLast); } catch (Exception ex) { string msig = " Script[" + this.Strategy.Script.GetType().Name + "].OnNewBarCallback(" + quote + "): "; this.PopupException(ex.Message + msig, ex); } } string msg = "DONT_REMOVE_ALERT_SHOULD_LEAVE_ITS_TRAIL_DURING_LIFETIME_TO_PUT_UNFILLED_DOTS_ON_CHART"; alertsDumpedForStreamingBar = this.ExecutionDataSnapshot.DumpPendingAlertsIntoPendingHistoryByBar(); if (alertsDumpedForStreamingBar > 0) { msg += " DUMPED_AFTER_SCRIPT_EXECUTION_ON_NEW_BAR_OR_QUOTE"; } // what's updated after Exec: non-volatile, kept un-reset until executor.Initialize(): //this.ExecutionDataSnapshot.PositionsMasterByEntryBar (unique) //this.ExecutionDataSnapshot.PositionsMaster //this.PositionsOnlyActive //this.AlertsMaster //this.AlertsNewAfterExec // what's new for this iteration: volatile, cleared before next Exec): //this.AlertsNewAfterExec //this.ExecutionDataSnapshot.PositionsOpenedAfterExec //this.ExecutionDataSnapshot.PositionsClosedAfterExec bool willPlace = this.Backtester.IsBacktestingNow == false && this.OrderProcessor != null && this.IsAutoSubmitting; bool setStatusSubmitting = this.IsStreaming && this.IsAutoSubmitting; List<Alert> alertsNewAfterExecCopy = this.ExecutionDataSnapshot.AlertsNewAfterExecSafeCopy; if (alertsNewAfterExecCopy.Count > 0) { this.enrichAlertsWithQuoteCreated(alertsNewAfterExecCopy, quote); if (willPlace) { this.OrderProcessor.CreateOrdersSubmitToBrokerProviderInNewThreadGroups(alertsNewAfterExecCopy, setStatusSubmitting, true); } } if (this.Backtester.IsBacktestingNow && this.Backtester.WasBacktestAborted) return null; pokeUnit.AlertsNew = alertsNewAfterExecCopy; pokeUnit.PositionsOpened = this.ExecutionDataSnapshot.PositionsOpenedAfterExecSafeCopy; pokeUnit.PositionsClosed = this.ExecutionDataSnapshot.PositionsClosedAfterExecSafeCopy; return pokeUnit; }
void enrichAlertsWithQuoteCreated(List<Alert> alertsAfterStrategy, Quote quote) { if (quote == null) return; foreach (Alert alert in alertsAfterStrategy) { if (alert.PlacedBarIndex != this.Bars.Count - 1) { string msg = "skipping enriching: alertsNewAfterExec should contain only lastBar alerts" + "; got alertAfterStrategy.BarRelnoPlaced[" + alert.PlacedBarIndex + "]" + "!= this.Bars.Count[" + this.Bars.Count + "] for alert[" + alert + "]"; this.PopupException(msg); continue; } //log.Debug("quote!=null => setting for TradeHistory:" + // " 1) alert.BrokerQuoteDateTime=quote.ServerTime[" + quote.ServerTime + "]" + // " 2) alert.PriceDeposited=[" + priceDeposited + "] quote.DepositBuy[" + quote.FortsDepositBuy + "] quote.DepositSell[" + quote.FortsDepositSell + "] "); //if (alertAfterStrategy.DataRange == null) alertAfterStrategy.DataRange = this.BarDataRange; //alertAfterStrategy.PositionSize = this.PositionSize; alert.QuoteCreatedThisAlertServerTime = quote.ServerTime; alert.QuoteCreatedThisAlert = quote; } }
public override void GenerateFillBidAskSymmetrically(Quote quote, double medianPrice) { if (quote == null) return; quote.Ask = medianPrice + this.constant / 2; quote.Bid = medianPrice - this.constant / 2; }
public override void GenerateFillBidBasedOnAsk(Quote quote, double askPrice) { quote.Ask = askPrice; quote.Bid = askPrice - this.constant; }
public void ConsumeQuoteOfStreamingBar(Quote quote) { this.msigForNpExceptions = "ConsumeFreshQuote(): "; var barsSafe = this.Bars; var streamingSafe = this.StreamingProvider; var chartFormSafe = this.ChartForm; var executorSafe = this.Executor; //var rendererSafe = this.Renderer; try { streamingSafe.InitializeStreamingOHLCVfromStreamingProvider(ConsumerBarsToAppendInto); } catch (Exception e) { Assembler.PopupException("didn't merge with Partial, continuing", e); } //this.ChartForm.Chart.UpdatePartialAndHorizontalScrollMaximum(qd.BarFactory.CurrentBar); if (quote.ParentStreamingBar.ParentBarsIndex > quote.ParentStreamingBar.ParentBars.Count) { string msg = "should I add a bar into Chart.Bars?... NO !!! already added"; } //if (this.NewQuote != null) this.NewQuote(this, new QuoteEventArgs(quote)); //this.chartFormsManager.Executor.onNewQuoteEnqueueExecuteStrategyInNewThread(this, new QuoteEventArgs(quote)); // launch update in GUI thread chartFormSafe.PrintQuoteTimestampsOnStreamingButtonBeforeExecution(quote); // execute strategy in the thread of a StreamingProvider (DDE server for MockQuickProvider) executorSafe.ExecuteOnNewBarOrNewQuote(quote); // trigger GUI to repaint the chart with new positions and bid/ask lines chartFormSafe.ChartControl.InvalidateAllPanelsFolding(); //rendererSafe.DrawBidAskLines = true; }
public override void OnNewQuoteOfStreamingBarCallback(Quote quoteNewArrived) { this.placePrototypeOncePositionClosed(quoteNewArrived.ParentStreamingBar); }
public abstract void GenerateFillBidAskSymmetrically(Quote quote, double medianPrice);
Quote modelQuoteThatCouldFillAlert(Alert alert) { string err; Quote quoteModel = new Quote(); quoteModel.Source = "GENERATED_TO_FILL_" + alert.ToString(); quoteModel.Size = alert.Qty; double priceScriptAligned = this.backtester.Executor.AlignAlertPriceToPriceLevel(alert.Bars, alert.PriceScript, true, alert.PositionLongShortFromDirection, alert.MarketLimitStop); Quote quotePrev = this.backtester.BacktestDataSource.BacktestStreamingProvider.StreamingDataSnapshot .LastQuoteGetForSymbol(alert.Symbol); switch (alert.MarketLimitStop) { case MarketLimitStop.Limit: switch (alert.Direction) { case Direction.Buy: case Direction.Cover: if (priceScriptAligned > quotePrev.Ask) { err = "INVALID_PRICESCRIPT_FOR_LIMIT_BUY_MUST_NOT_BE_ABOVE_CURRENT_ASK"; return null; } this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillBidBasedOnAsk(quoteModel, priceScriptAligned); break; case Direction.Short: case Direction.Sell: if (priceScriptAligned < quotePrev.Bid) { err = "INVALID_PRICESCRIPT_FOR_LIMIT_SELL_MUST_NOT_BE_BELOW_CURRENT_BID"; return null; } this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillAskBasedOnBid(quoteModel, priceScriptAligned); break; default: throw new Exception("ALERT_LIMIT_DIRECTION_UNKNOWN direction[" + alert.Direction + "] is not Buy/Cover/Short/Sell modelQuoteThatCouldFillAlert()"); } break; case MarketLimitStop.Stop: switch (alert.Direction) { case Direction.Buy: case Direction.Cover: if (priceScriptAligned < quotePrev.Ask) { err = "INVALID_PRICESCRIPT_FOR_STOP_BUY_MUST_NOT_BE_BELOW_CURRENT_ASK"; return null; } this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillBidBasedOnAsk(quoteModel, priceScriptAligned); break; case Direction.Short: case Direction.Sell: if (priceScriptAligned > quotePrev.Bid) { err = "INVALID_PRICESCRIPT_FOR_STOP_SELL_MUST_NOT_BE_ABOVE_CURRENT_BID"; return null; } this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillAskBasedOnBid(quoteModel, priceScriptAligned); break; default: throw new Exception("ALERT_STOP_DIRECTION_UNKNOWN direction[" + alert.Direction + "] is not Buy/Cover/Short/Sell modelQuoteThatCouldFillAlert()"); } break; case MarketLimitStop.Market: switch (alert.Direction) { case Direction.Buy: case Direction.Cover: quoteModel.Ask = quotePrev.Ask; this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillBidBasedOnAsk(quoteModel, quotePrev.Ask); break; case Direction.Short: case Direction.Sell: quoteModel.Bid = quotePrev.Bid; this.backtester.BacktestDataSource.BacktestStreamingProvider.SpreadModeler .GenerateFillAskBasedOnBid(quoteModel, quotePrev.Bid); break; default: throw new Exception("ALERT_MARKET_DIRECTION_UNKNOWN direction[" + alert.Direction + "] is not Buy/Cover/Short/Sell modelQuoteThatCouldFillAlert()"); } break; case MarketLimitStop.StopLimit: // HACK one quote might satisfy SLactivation, the other one might fill it; time to introduce state of SL into Alert??? string msg = "STOP_LIMIT_QUOTE_FILLING_GENERATION_REQUIRES_TWO_STEPS_NYI" + "; pass SLActivation=0 to PositionPrototypeActivator so that it generates STOP instead of STOPLOSS which I can't generate yet"; //Debugger.Break(); break; case MarketLimitStop.AtClose: default: throw new Exception("ALERT_TYPE_UNKNOWN MarketLimitStop[" + alert.MarketLimitStop + "] is not Market/Limit/Stop modelQuoteThatCouldFillAlert()"); } return quoteModel; }
public abstract void GenerateFillAskBasedOnBid(Quote quote, double bidPrice);
public abstract void GenerateFillBidBasedOnAsk(Quote quote, double askPrice);
public void GenerateFillBidAskSymmetricallyFromLastPrice(Quote quote) { if (quote == null) return; this.GenerateFillBidAskSymmetrically(quote, quote.PriceLastDeal); }
public DateTime getThisDayClose(Quote quote) { return new DateTime(quote.ServerTime.Date.Year, quote.ServerTime.Date.Month, quote.ServerTime.Date.Day, this.MarketCloseServerTime.Hour, this.MarketCloseServerTime.Minute, this.MarketCloseServerTime.Second); }
public void CallbackAlertFilledMoveAroundInvokeScript(Alert alertFilled, Quote quote, int barFillRelno, double priceFill, double qtyFill, double slippageFill, double commissionFill) { string msig = " CallbackAlertFilledMoveAroundInvokeScript(" + alertFilled + ", " + quote + ")"; List<Alert> alertsNewAfterAlertFilled = new List<Alert>(); List<Position> positionsOpenedAfterAlertFilled = new List<Position>(); List<Position> positionsClosedAfterAlertFilled = new List<Position>(); //"Excuse me, what bar is it now?" I'm just guessing! does BrokerProvider knows to pass Bar here?... Bar barFill = (this.IsStreaming) ? alertFilled.Bars.BarStreamingCloneReadonly : alertFilled.Bars.BarStaticLast; if (barFillRelno != barFill.ParentBarsIndex) { string msg = "barFillRelno[" + barFillRelno + "] != barFill.ParentBarsIndex[" + barFill.ParentBarsIndex + "]; barFill=[" + barFill + "]"; Assembler.PopupException(msg); } if (priceFill == -1) { string msg = "won't set priceFill=-1 for alert [" + alertFilled + "]"; throw new Exception(msg); } if (alertFilled.PositionAffected == null) { string msg = "CallbackAlertFilled can't do its job: alert.PositionAffected=null for alert [" + alertFilled + "]"; throw new Exception(msg); } if (alertFilled.IsEntryAlert) { if (alertFilled.PositionAffected.EntryFilledBarIndex != -1) { string msg = "DUPE: CallbackAlertFilled can't do its job: alert.PositionAffected.EntryBar!=-1 for alert [" + alertFilled + "]"; throw new Exception(msg); } else { string msg = "initializing EntryBar=[" + barFill + "] on AlertFilled"; } } else { if (alertFilled.PositionAffected.ExitFilledBarIndex != -1) { string msg = "DUPE: CallbackAlertFilled can't do its job: alert.PositionAffected.ExitBar!=-1 for alert [" + alertFilled + "]"; throw new Exception(msg); return; } else { string msg = "initializing ExitBar=[" + barFill + "] on AlertFilled"; } } if (quote == null) { quote = this.DataSource.StreamingProvider.StreamingDataSnapshot.LastQuoteGetForSymbol(alertFilled.Symbol); // TODO: here quote will have NO_PARENT_BARS, since StreamingDataSnapshot contains anonymous quote; // I should keep per-timeframe / per-distributionChannel LastQuote to have ParentBar= different StreamingBar 's // bindStreamingBarForQuoteAndPushQuoteToConsumers(quoteSernoEnrichedWithUnboundStreamingBar.Clone()); } alertFilled.QuoteLastWhenThisAlertFilled = quote; try { alertFilled.FillPositionAffectedEntryOrExitRespectively(barFill, barFillRelno, priceFill, qtyFill, slippageFill, commissionFill); } catch (Exception ex) { string msg = "REMOVE_FILLED_FROM_PENDING? DONT_USE_Bar.ContainsPrice()?"; Assembler.PopupException(msg + msig, ex); //Debugger.Break(); } bool removed = this.ExecutionDataSnapshot.AlertsPendingRemove(alertFilled); if (removed == false) { Debugger.Break(); } if (alertFilled.IsEntryAlert) { // position has its parent alert in Position.ctor() //// REFACTORED_POSITION_HAS_AN_ALERT_AFTER_ALERTS_CONSTRUCTOR //alert.PositionAffected.EntryCopyFromAlert(alert); this.ExecutionDataSnapshot.PositionsMasterOpenNewAdd(alertFilled.PositionAffected); positionsOpenedAfterAlertFilled.Add(alertFilled.PositionAffected); } else { //// REFACTORED_POSITION_HAS_AN_ALERT_AFTER_ALERTS_CONSTRUCTOR we can exit by TP or SL - position doesn't have an ExitAlert assigned until Alert was filled!!! //alertFilled.PositionAffected.ExitAlertAttach(alertFilled); this.ExecutionDataSnapshot.MovePositionOpenToClosed(alertFilled.PositionAffected); positionsClosedAfterAlertFilled.Add(alertFilled.PositionAffected); } bool willPlace = this.Backtester.IsBacktestingNow == false && this.OrderProcessor != null && this.IsAutoSubmitting; bool setStatusSubmitting = this.IsStreaming && this.IsAutoSubmitting; PositionPrototype proto = alertFilled.PositionAffected.Prototype; if (proto != null) { // 0. once again, set ExitAlert to What was actually filled, because prototypeEntry created SL & TP, which were both written into ExitAlert; // so if we caught the Loss and SL was executed, position.ExitAlert will still contain TP if we don't set it here bool exitIsDifferent = alertFilled.PositionAffected.ExitAlert != null && alertFilled.PositionAffected.ExitAlert != alertFilled; if (alertFilled.IsExitAlert && exitIsDifferent) { alertFilled.PositionAffected.ExitAlertAttach(alertFilled); } // 1. alert.PositionAffected.Prototype.StopLossAlertForAnnihilation and TP will get assigned alertsNewAfterAlertFilled = this.PositionPrototypeActivator.AlertFilledCreateSlTpOrAnnihilateCounterparty(alertFilled); // quick check: there must be {SL+TP} OR Annihilator //this.BacktesterFacade.IsBacktestingNow == false && if (alertFilled.IsEntryAlert) { if (proto.StopLossAlertForAnnihilation == null) { string msg = "NONSENSE@Entry: proto.StopLossAlert is NULL???.."; throw new Exception(msg); } if (proto.TakeProfitAlertForAnnihilation == null) { string msg = "NONSENSE@Entry: proto.TakeProfitAlert is NULL???.."; throw new Exception(msg); } if (alertsNewAfterAlertFilled.Count == 0) { string msg = "NONSENSE@Entry: alertsNewSlAndTp.Count=0" + "; this.PositionPrototypeActivator.AlertFilledCreateSlTpOrAnnihilateCounterparty(alertFilled)" + " should return 2 alerts; I don't want to create new list from {proto.SL, proto.TP}"; throw new Exception(msg); } } if (alertFilled.IsExitAlert) { if (alertsNewAfterAlertFilled.Count > 0) { string msg = "NONSENSE@Exit: there must be no alerts (got " + alertsNewAfterAlertFilled.Count + "): killer works silently"; throw new Exception(msg); } } if (alertsNewAfterAlertFilled.Count > 0 && willPlace) { this.OrderProcessor.CreateOrdersSubmitToBrokerProviderInNewThreadGroups(alertsNewAfterAlertFilled, setStatusSubmitting, true); // 3. Script using proto might move SL and TP which require ORDERS to be moved, not NULLs int twoMinutes = 120000; if (alertFilled.IsEntryAlert) { // there must be SL.OrderFollowed!=null and TP.OrderFollowed!=null if (proto.StopLossAlertForAnnihilation.OrderFollowed == null) { string msg = "StopLossAlert.OrderFollowed is NULL!!! engaging ManualResetEvent.WaitOne()"; this.PopupException(msg); Stopwatch waitedForStopLossOrder = new Stopwatch(); waitedForStopLossOrder.Start(); proto.StopLossAlertForAnnihilation.MreOrderFollowedIsNotNull.WaitOne(twoMinutes); waitedForStopLossOrder.Stop(); msg = "waited " + waitedForStopLossOrder.ElapsedMilliseconds + "ms for StopLossAlert.OrderFollowed"; if (proto.StopLossAlertForAnnihilation.OrderFollowed == null) { msg += ": NO_SUCCESS still null!!!"; this.PopupException(msg); } else { proto.StopLossAlertForAnnihilation.OrderFollowed.AppendMessage(msg); this.PopupException(msg); } } else { string msg = "you are definitely crazy, StopLossAlert.OrderFollowed is a single-threaded assignment"; //this.ThrowPopup(new Exception(msg)); } if (proto.TakeProfitAlertForAnnihilation.OrderFollowed == null) { string msg = "TakeProfitAlert.OrderFollowed is NULL!!! engaging ManualResetEvent.WaitOne()"; this.PopupException(msg); Stopwatch waitedForTakeProfitOrder = new Stopwatch(); waitedForTakeProfitOrder.Start(); proto.TakeProfitAlertForAnnihilation.MreOrderFollowedIsNotNull.WaitOne(); waitedForTakeProfitOrder.Stop(); msg = "waited " + waitedForTakeProfitOrder.ElapsedMilliseconds + "ms for TakeProfitAlert.OrderFollowed"; if (proto.TakeProfitAlertForAnnihilation.OrderFollowed == null) { msg += ": NO_SUCCESS still null!!!"; this.PopupException(msg); } else { proto.TakeProfitAlertForAnnihilation.OrderFollowed.AppendMessage(msg); this.PopupException(msg); } } else { string msg = "you are definitely crazy, TakeProfitAlert.OrderFollowed is a single-threaded assignment"; //this.ThrowPopup(new Exception(msg)); } } } } if (this.Backtester.IsBacktestingNow == false) { ReporterPokeUnit pokeUnit = new ReporterPokeUnit(quote); pokeUnit.AlertsNew = alertsNewAfterAlertFilled; pokeUnit.PositionsOpened = positionsOpenedAfterAlertFilled; pokeUnit.PositionsClosed = positionsClosedAfterAlertFilled; this.PushPositionsOpenedClosedToReportersAsyncUnsafe(pokeUnit); } // 4. Script event will generate a StopLossMove PostponedHook this.invokeScriptEvents(alertFilled); // reasons for (alertsNewAfterExec.Count > 0) include: // 2.1. PrototypeActivator::AlertFilledPlaceSlTpOrAnnihilateCounterparty // 2.2. Script.OnAlertFilledCallback(alert) // 2.3. Script.OnPositionOpenedPrototypeSlTpPlacedCallback(alert.PositionAffected) // 2.4. Script.OnPositionClosedCallback(alert.PositionAffected) }