using NinjaTrader.Cbi; using NinjaTrader.Data; using NinjaTrader.Gui; using NinjaTrader.Gui.Tools; using NinjaTrader.NinjaScript.DrawingTools; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Windows; using System.Windows.Media; using System.Xml.Serialization; using System.Security.Cryptography; using System.Text; using System.Runtime.InteropServices; using System.Net.Http; namespace NinjaTrader.NinjaScript.Strategies { [CategoryOrder(Setup, 0)] [CategoryOrder(GlobalSettings, 1)] [CategoryOrder(TimeWindowSettings, 2)] [CategoryOrder(PositionSettings, 3)] [CategoryOrder(EntrySettings, 4)] [CategoryOrder(ScaleInSettings, 5)] [CategoryOrder(FilterSettings, 6)] [CategoryOrder(ExitSettings, 7)] [CategoryOrder(MartingaleSettings, 8)] public class AlexORB : Strategy { #region Constants private const string SystemVersion = " V1.4"; private const string StrategyName = "Special ORB Bot - Vincere Trading"; private const string Setup = "Setup"; private const string GlobalSettings = "Global Settings"; private const string PositionSettings = "Position Settings"; private const string EntrySettings = "Entry Settings"; private const string ScaleInSettings = "Scale In Settings"; private const string FilterSettings = "Filter Settings"; private const string ExitSettings = "Exit Settings"; private const string TimeWindowSettings = "Time Settings"; private const string MartingaleSettings = "Martingale Settings"; public override string DisplayName { get { return StrategyName + SystemVersion; } } #endregion #region System Information Structure [DllImport("kernel32.dll")] private static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo); [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_INFO { public ushort wProcessorArchitecture; public ushort wReserved; public uint dwPageSize; public IntPtr lpMinimumApplicationAddress; public IntPtr lpMaximumApplicationAddress; public IntPtr dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public ushort wProcessorLevel; public ushort wProcessorRevision; } #endregion #region License Properties private bool isValidated = false; private static Dictionary licenseCache = new Dictionary(); private const int CACHE_DURATION_HOURS = 24; private string cachedHardwareId = null; private bool ValidateLicense() { if (string.IsNullOrEmpty(LicenseKey)) return false; var hwid = GetHardwareId(); if (string.IsNullOrEmpty(hwid)) return false; // Check cache if (licenseCache.ContainsKey(LicenseKey)) { var cacheTime = licenseCache[LicenseKey]; if (DateTime.Now.Subtract(cacheTime).TotalHours < CACHE_DURATION_HOURS) return true; licenseCache.Remove(LicenseKey); } try { using (var client = new System.Net.Http.HttpClient()) { const string apiKey = "A6FHGMmhg3PXodjW1XFmYx7Tg5cQgQlEETvTckfIQ1o"; client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); // Send HWID in metadata var hwidMetadata = $"{{\"metadata\": {{\"HWID\": \"{hwid}\"}}}}"; var content = new StringContent(hwidMetadata, System.Text.Encoding.UTF8, "application/json"); // Validate license with Whop API var requestUrl = $"https://api.whop.com/api/v2/memberships/{LicenseKey}/validate_license"; var response = client.PostAsync(requestUrl, content).Result.Content.ReadAsStringAsync().Result; // Check if license is valid bool isValid = (response.Contains("\"status\":\"active\"") || response.Contains("\"status\":\"trialing\"") || response.Contains("\"status\":\"completed\"")) && response.Contains("\"valid\":true"); if (isValid) { licenseCache[LicenseKey] = DateTime.Now; isValidated = true; return true; } return false; } } catch (Exception) { // Offline grace period check if (licenseCache.ContainsKey(LicenseKey)) { var cacheTime = licenseCache[LicenseKey]; if (DateTime.Now.Subtract(cacheTime).TotalHours < (CACHE_DURATION_HOURS * 2)) return true; } return false; } } private string GetHardwareId() { if (!string.IsNullOrEmpty(cachedHardwareId)) return cachedHardwareId; try { SYSTEM_INFO sysInfo = new SYSTEM_INFO(); GetSystemInfo(out sysInfo); var hwProfile = new StringBuilder(); hwProfile.Append(sysInfo.dwNumberOfProcessors); hwProfile.Append(sysInfo.dwProcessorType); hwProfile.Append(sysInfo.wProcessorArchitecture); hwProfile.Append(sysInfo.wProcessorLevel); hwProfile.Append(sysInfo.wProcessorRevision); // Hash the hardware profile using (var sha256 = SHA256.Create()) { var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hwProfile.ToString())); cachedHardwareId = BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); return cachedHardwareId; } } catch (Exception) { return null; } } private void StopStrategy() { if (State != State.SetDefaults && State != State.Terminated) { Log("Strategy stopping due to invalid license.", LogLevel.Error); IsEnabled = false; State = State.Terminated; } } #endregion #region Parameters [NinjaScriptProperty] [Display(Name = "License Key", Description = "Enter your Whop license key", Order = 1, GroupName = "Setup")] public string LicenseKey { get; set; } [NinjaScriptProperty] [Display(Name = "Backtest", GroupName = GlobalSettings, Order = 25)] public bool Backtest { get; set; } [NinjaScriptProperty] [Display(Name = "Pos Size 1", GroupName = PositionSettings, Order = 5)] public int PosSize1 { get; set; } [NinjaScriptProperty] [Display(Name = "Pos Size 2", GroupName = PositionSettings, Order = 6)] public int PosSize2 { get; set; } [NinjaScriptProperty] [Display(Name = "Pos Size 3", GroupName = PositionSettings, Order = 7)] public int PosSize3 { get; set; } [NinjaScriptProperty] [Display(Name = "Trade Window", GroupName = TimeWindowSettings, Order = 13)] public bool TradeWindow1IsOn { get; set; } [PropertyEditor("NinjaTrader.Gui.Tools.AutoCloseTimeEditorKey")] [NinjaScriptProperty] [Display(Name = "Trade Start 1", GroupName = TimeWindowSettings, Order = 14)] public DateTime TradeStart1 { get; set; } [PropertyEditor("NinjaTrader.Gui.Tools.AutoCloseTimeEditorKey")] [NinjaScriptProperty] [Display(Name = "Trade End 1", GroupName = TimeWindowSettings, Order = 15)] public DateTime TradeEnd1 { get; set; } public enum Trade { Both, Long, Short } [PropertyEditor("NinjaTrader.Gui.Tools.AutoCloseTimeEditorKey")] [NinjaScriptProperty] [Display(Name = "Opening Range Start", GroupName = EntrySettings, Order = 12)] public DateTime RangeStart { get; set; } [PropertyEditor("NinjaTrader.Gui.Tools.AutoCloseTimeEditorKey")] [NinjaScriptProperty] [Display(Name = "Opening Range End", GroupName = EntrySettings, Order = 13)] public DateTime RangeEnd { get; set; } [Display(Name = "Range Is Overnight", Order = 24, GroupName = EntrySettings)] public bool RangeIsOvernight { get; set; } [NinjaScriptProperty] [Display(Name = "Trade Direction", GroupName = EntrySettings, Order = 17)] public Trade MyTradeDirection { get; set; } [NinjaScriptProperty] [Display(Name = "Entry Order Tick Offset", GroupName = EntrySettings, Order = 18)] public int EntryOrderTickOffset { get; set; } [NinjaScriptProperty] [Display(Name = "Stop Loss Ticks", GroupName = ExitSettings, Order = 15)] public double StopLossTicks { get; set; } [NinjaScriptProperty] [Display(Name = "Profit Target 1 Ticks", GroupName = ExitSettings, Order = 16)] public double ProfitTargetTicks1 { get; set; } [NinjaScriptProperty] [Display(Name = "Profit Target 2 Ticks", GroupName = ExitSettings, Order = 17)] public double ProfitTargetTicks2 { get; set; } [NinjaScriptProperty] [Display(Name = "Profit Target 3 Ticks", GroupName = ExitSettings, Order = 18)] public double ProfitTargetTicks3 { get; set; } [NinjaScriptProperty] [Display(Name = "BreakEven", GroupName = ExitSettings, Order = 19)] public bool BreakEvenIsOn { get; set; } [NinjaScriptProperty] [Display(Name = "BreakEven Offset Ticks", GroupName = ExitSettings, Order = 21)] public double BreakEvenOffset { get; set; } [NinjaScriptProperty] [Display(Name = "BreakEven After Ticks", GroupName = ExitSettings, Order = 22)] public double BreakEvenAfterTicks { get; set; } [NinjaScriptProperty] [Display(Name = "Trail", GroupName = ExitSettings, Order = 23)] public bool TrailIsOn { get; set; } [NinjaScriptProperty] [Display(Name = "Trail By Ticks", GroupName = ExitSettings, Order = 24)] public int TrailByTicks { get; set; } [NinjaScriptProperty] [Display(Name = "Start Trail After Ticks", GroupName = ExitSettings, Order = 25)] public int StartTrailAfterTicks { get; set; } [NinjaScriptProperty] [Display(Name = "Trail Frequency", GroupName = ExitSettings, Order = 26)] public int TrailFrequency { get; set; } [NinjaScriptProperty] [Display(Name = "Martingale", GroupName = MartingaleSettings, Order = 27)] public bool Martingale { get; set; } [NinjaScriptProperty] [Display(Name = "Multiplier", GroupName = MartingaleSettings, Order = 28)] public int MartingaleMultiplier { get; set; } [NinjaScriptProperty] [Display(Name = "Max Martingales", GroupName = MartingaleSettings, Order = 29)] public int MaxMartingales { get; set; } #endregion #region Values [Browsable(false)] [XmlIgnore()] public Series RangeHigh { get { return Values[0]; } } [Browsable(false)] [XmlIgnore()] public Series RangeLow { get { return Values[1]; } } #endregion #region Variables private SolidColorBrush _bgColor; private bool _orderIgnored; private bool _entryOrdersPlaced; private double _entryPrice; private int _posSize1, _posSize2, _posSize3; private bool _beApplied; private Order _exitMarketOrder; private Order _entryOrder; private Order _scaleInOrder; private Order _exitStopOrder; private Order _entryOrderShort; private Order _entryOrderLong; private double _stop; private int _scaleInCount; private const string ExitLongProfitName1 = "PT1-Long"; private const string ExitShortProfitName1 = "PT1-Short"; private const string ExitLongProfitName2 = "PT2-Long"; private const string ExitShortProfitName2 = "PT2-Short"; private const string ExitLongProfitName3 = "PT3-Long"; private const string ExitShortProfitName3 = "PT3-Short"; private Order _exitProfitOrder1; private Order _exitProfitOrder2; private Order _exitProfitOrder3; private double _profit1; private double _profit2; private double _profit3; private double _rangeHigh; private double _rangeLow; private bool _reset; private bool _rangeIsInitialized; private bool _clearOnClose; private bool _stopTradingForSession; private int _martinCounter; private List _positions = new List(); #endregion #region Initialization protected override void OnStateChange() { if (State == State.SetDefaults) { LicenseKey = string.Empty; SetDefaults(); SetParameters(); InitPlots(); } else if (State == State.Configure) { if (!Backtest) // Only validate for live trading { if (!ValidateLicense()) { Log("Invalid license key.", LogLevel.Error); StopStrategy(); return; } } ClearOutputWindow(); SuperImpose(); _bgColor = new SolidColorBrush(Colors.Green) { Opacity = 0.25 }; _bgColor.Freeze(); _posSize1 = PosSize1; _posSize2 = PosSize2; _posSize3 = PosSize3; } else if (State == State.DataLoaded) { InitIndicators(); } else if (State == State.Terminated) { // Cleanup } else if (State == State.Realtime) { if (!Backtest && !ValidateLicense()) { Log("License validation failed. Strategy stopped.", LogLevel.Error); StopStrategy(); return; } } } private void InitPlots() { var high = new Stroke(Brushes.Orchid, DashStyleHelper.Dash, 3); var low = new Stroke(Brushes.Orchid, DashStyleHelper.Dash, 3); AddPlot(high, PlotStyle.Hash, "Range High"); AddPlot(low, PlotStyle.Hash, "Range Low"); } private void SuperImpose() { if (Calculate == Calculate.OnEachTick || Calculate == Calculate.OnPriceChange) Calculate = Calculate.OnBarClose; if (IsExitOnSessionCloseStrategy) IsExitOnSessionCloseStrategy = false; TraceOrders = false; } private void SetDefaults() { Name = StrategyName + SystemVersion; Calculate = Calculate.OnBarClose; IsExitOnSessionCloseStrategy = false; IsUnmanaged = true; BarsRequiredToTrade = 0; IsInstantiatedOnEachOptimizationIteration = true; RealtimeErrorHandling = RealtimeErrorHandling.StopCancelCloseIgnoreRejects; ShowTransparentPlotsInDataBox = true; } private void SetParameters() { PosSize1 = 1; PosSize2 = 2; PosSize3 = 3; StopLossTicks = 20; ProfitTargetTicks1 = 30; ProfitTargetTicks2 = 60; ProfitTargetTicks3 = 90; TrailByTicks = 30; TrailIsOn = true; BreakEvenIsOn = true; TradeStart1 = new DateTime(2020, 01, 01, 9, 30, 0); TradeEnd1 = new DateTime(2020, 01, 01, 16, 30, 0); StartTrailAfterTicks = 60; BreakEvenAfterTicks = 30; EntryOrderTickOffset = 1; TrailFrequency = 10; RangeStart = new DateTime(2020, 01, 01, 9, 30, 0); RangeEnd = new DateTime(2020, 01, 01, 9, 45, 0); MaxMartingales = 5; MartingaleMultiplier = 2; } private void InitIndicators() { } #endregion #region Trading Logic protected override void OnBarUpdate() { if (CurrentBar < 1) return; if (!ValidateLicense() && !Backtest) { Log("License validation failed. Strategy stopped.", LogLevel.Error); _stopTradingForSession = true; StopStrategy(); return; } SetRange(); ColorInsideTradeWindow(TradeStart1, TradeEnd1, TradeWindow1IsOn); Reset(); if (State == State.Historical && !Backtest) return; if (_stopTradingForSession) return; EntryModule(); ExitModule(); } private void SetRange() { try { if (IsInsideRangeSetWindow()) { if (_reset) { _reset = false; _rangeHigh = 0; _rangeLow = 0; _rangeIsInitialized = true; } if (High[0] > _rangeHigh) _rangeHigh = High[0]; if (_rangeLow == 0) _rangeLow = Low[0]; if (Low[0] < _rangeLow) _rangeLow = Low[0]; PlotBrushes[0][0] = Brushes.Blue; PlotBrushes[1][0] = Brushes.Blue; } else { PlotBrushes[0][0] = Brushes.MediumVioletRed; PlotBrushes[1][0] = Brushes.MediumVioletRed; _reset = true; } if (_rangeLow == 0 || _rangeHigh == 0) { PlotBrushes[1][0] = Brushes.Transparent; PlotBrushes[0][0] = Brushes.Transparent; } RangeHigh[0] = _rangeHigh; RangeLow[0] = _rangeLow; } catch (Exception e) { Print("Crash inside set range method. " + e); } } private bool IsInsideRangeSetWindow() { var timeNow = ToTime(Time[0]); var timeStart = ToTime(RangeStart); var timeEnd = ToTime(RangeEnd); if (!RangeIsOvernight) { if (timeNow >= timeStart && timeNow <= timeEnd) { return true; } } if (RangeIsOvernight) { if (timeNow >= timeStart || timeNow <= timeEnd) { return true; } } return false; } private void Reset() { if (Time[0].Date != Time[1].Date) { _stopTradingForSession = false; if (Position.MarketPosition == MarketPosition.Flat) _positions.Clear(); if (Position.MarketPosition != MarketPosition.Flat) _clearOnClose = true; } } private void EntryModule() { if (Position.MarketPosition != MarketPosition.Flat) { _entryOrdersPlaced = false; return; } if (!FiltersAreOk()) { CancelEntryOrders(); _entryOrdersPlaced = false; return; } if (_entryOrdersPlaced) return; PlaceStopMarketEntryOrder(true, RangeHigh[0] + TickSize * EntryOrderTickOffset); PlaceStopMarketEntryOrder(false, RangeLow[0] - TickSize * EntryOrderTickOffset); _entryOrdersPlaced = true; } private bool FiltersAreOk() { return IsInsideTradeWindow() && !IsInsideRangeSetWindow() && CloseInsideRange() && RangeIsOk(); } private bool CloseInsideRange() { return Close[0] < RangeHigh[0] && Close[0] > RangeLow[0]; } private bool RangeIsOk() { return RangeHigh[0] > 0 && RangeLow[0] > 0 && !double.IsNaN(RangeHigh[0]) && !double.IsNaN(RangeLow[0]) && _rangeIsInitialized; } private void CancelEntryOrders() { if (_entryOrderLong != null) CancelOrder(_entryOrderLong); if (_entryOrderShort != null) CancelOrder(_entryOrderShort); } private void ColorInsideTradeWindow(DateTime start, DateTime end, bool isOn) { if (!isOn) return; var timeNow = ToTime(Time[0]); if (start < end) { if (timeNow >= ToTime(start) && timeNow <= ToTime(end)) { BackBrush = _bgColor; } } else if (start > end) { if (timeNow >= ToTime(start) || timeNow <= ToTime(end)) { BackBrush = _bgColor; } } } private bool IsInsideTradeWindow() { if (!TradeWindow1IsOn) return true; var timeNow = ToTime(Time[0]); if (TradeStart1 < TradeEnd1) { if (timeNow >= ToTime(TradeStart1) && timeNow <= ToTime(TradeEnd1)) { return true; } } else if (TradeStart1 > TradeEnd1) { if (timeNow >= ToTime(TradeStart1) || timeNow <= ToTime(TradeEnd1)) { return true; } } return false; } #endregion #region Exits private void ExitModule() { if (Position.MarketPosition == MarketPosition.Flat) return; if (Position.MarketPosition == MarketPosition.Long) BreakEven(High[0]); if (Position.MarketPosition == MarketPosition.Short) BreakEven(Low[0]); Trail(Close[0]); } private void BreakEven(double price) { if (!BreakEvenIsOn) return; if (Position.MarketPosition == MarketPosition.Flat) { _beApplied = false; return; } if (_beApplied) return; if (Position.MarketPosition == MarketPosition.Long) { if (price > _entryPrice + BreakEvenAfterTicks * TickSize) { var newStop = _entryPrice + BreakEvenOffset * TickSize; if (newStop > _stop) { if (_exitStopOrder != null) { _stop = newStop; ChangeStopExitOrder(); _beApplied = true; } } } } if (Position.MarketPosition == MarketPosition.Short) { if (price < _entryPrice - BreakEvenAfterTicks * TickSize) { var newStop = _entryPrice - BreakEvenOffset * TickSize; if (newStop < _stop) { if (_exitStopOrder != null) { _stop = newStop; ChangeStopExitOrder(); _beApplied = true; } } } } } private void Trail(double price) { if (!TrailIsOn) return; if (Position.MarketPosition == MarketPosition.Long && price >= _entryPrice + StartTrailAfterTicks * TickSize) { var newStop = price - TrailByTicks * TickSize; if (newStop > _stop + TrailFrequency * TickSize) { _stop = newStop; ChangeStopExitOrder(); } } if (Position.MarketPosition == MarketPosition.Short && price <= _entryPrice - StartTrailAfterTicks * TickSize) { var newStop = price + TrailByTicks * TickSize; if (newStop < _stop - TrailFrequency * TickSize) { _stop = newStop; ChangeStopExitOrder(); } } } private void ExitPositions() { if (Position.MarketPosition == MarketPosition.Long) { PlaceMarketExitOrder(true); } if (Position.MarketPosition == MarketPosition.Short) { PlaceMarketExitOrder(false); } } #endregion #region Order Management private const string EnterLongName = "Enter Long"; private const string EnterShortName = "Enter Short"; private const string ScaleInLongName = "Scale In Long"; private const string ScaleInShortName = "Scale In Short"; private const string ExitLongStopName = "Stop Long"; private const string ExitShortStopName = "Stop Short"; private const string MarketCloseLong = "Exit Long Market"; private const string MarketCloseShort = "Exit Short Market"; private void PlaceStopMarketEntryOrder(bool isLong, double price) { var posSize = _posSize1 + _posSize2 + _posSize3; if (MyTradeDirection == Trade.Both) { if (isLong) { _entryOrderLong = SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.StopMarket, posSize, 0, price, "", EnterLongName); } if (!isLong) { _entryOrderShort = SubmitOrderUnmanaged(0, OrderAction.SellShort, OrderType.StopMarket, posSize, 0, price, "", EnterShortName); } } if (MyTradeDirection == Trade.Long) { if (isLong) { _entryOrderLong = SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.StopMarket, posSize, 0, price, "", EnterLongName); } } if (MyTradeDirection == Trade.Short) { if (!isLong) { _entryOrderShort = SubmitOrderUnmanaged(0, OrderAction.SellShort, OrderType.StopMarket, posSize, 0, price, "", EnterShortName); } } } private void PlaceMarketExitOrder(bool isLong) { if (isLong) { _exitMarketOrder = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, Position.Quantity, 0, 0, "", MarketCloseLong); } else if (!isLong) { _exitMarketOrder = SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Market, Position.Quantity, 0, 0, "", MarketCloseShort); } } private void ChangeStopExitOrder() { ChangeOrder(_exitStopOrder, Position.Quantity, 0, _stop); } private void PlaceStopExitOrders(bool isStopLong) { if (isStopLong) { _exitStopOrder = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.StopMarket, Position.Quantity, 0, _stop, "", ExitLongStopName); } if (!isStopLong) { _exitStopOrder = SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.StopMarket, Position.Quantity, 0, _stop, "", ExitShortStopName); } } private void PlaceLimitProfitExitOrder1(bool isProfitOrderLong) { if (isProfitOrderLong) { _exitProfitOrder1 = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, _posSize1, _profit1, 0, "", ExitLongProfitName1); } if (!isProfitOrderLong) { _exitProfitOrder1 = SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Limit, _posSize1, _profit1, 0, "", ExitShortProfitName1); } } private void PlaceLimitProfitExitOrder2(bool isProfitOrderLong) { if (isProfitOrderLong) { _exitProfitOrder2 = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, _posSize2, _profit2, 0, "", ExitLongProfitName2); } if (!isProfitOrderLong) { _exitProfitOrder2 = SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Limit, _posSize2, _profit2, 0, "", ExitShortProfitName2); } } private void PlaceLimitProfitExitOrder3(bool isProfitOrderLong) { if (isProfitOrderLong) { _exitProfitOrder3 = SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Limit, _posSize3, _profit3, 0, "", ExitLongProfitName3); } if (!isProfitOrderLong) { _exitProfitOrder3 = SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Limit, _posSize3, _profit3, 0, "", ExitShortProfitName3); } } protected override void OnOrderTrace(DateTime timestamp, string message) { if (message.Contains("Ignored")) { _orderIgnored = true; } } protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time) { if (!IsFilled(execution.Order)) return; ManageOrders(execution); } private bool IsFilled(Order order) { if (order.OrderState == OrderState.Filled) return true; if (order.OrderState == OrderState.PartFilled) { if (order.Filled == order.Quantity) { return true; } } return false; } private void ManageOrders(Execution ex) { var orderName = ex.Order.Name; if (orderName == EnterLongName || orderName == EnterShortName) { _entryOrdersPlaced = false; _entryPrice = ex.Order.AverageFillPrice; _beApplied = false; var stopSize = StopLossTicks * TickSize; var profitSize1 = ProfitTargetTicks1 * TickSize; var profitSize2 = ProfitTargetTicks2 * TickSize; var profitSize3 = ProfitTargetTicks3 * TickSize; if (orderName == EnterLongName) { if (_entryOrderShort != null) CancelOrder(_entryOrderShort); _stop = ex.Order.AverageFillPrice - stopSize; _profit1 = ex.Order.AverageFillPrice + profitSize1; _profit2 = ex.Order.AverageFillPrice + profitSize2; _profit3 = ex.Order.AverageFillPrice + profitSize3; PlaceStopExitOrders(true); PlaceLimitProfitExitOrder1(true); if (PosSize2 > 0) PlaceLimitProfitExitOrder2(true); if (PosSize3 > 0) PlaceLimitProfitExitOrder3(true); CreatePosition(true, ex.Order); } if (orderName == EnterShortName) { if (_entryOrderLong != null) CancelOrder(_entryOrderLong); _stop = ex.Order.AverageFillPrice + stopSize; _profit1 = ex.Order.AverageFillPrice - profitSize1; _profit2 = ex.Order.AverageFillPrice - profitSize2; _profit3 = ex.Order.AverageFillPrice - profitSize3; PlaceStopExitOrders(false); PlaceLimitProfitExitOrder1(false); if (PosSize2 > 0) PlaceLimitProfitExitOrder2(false); if (PosSize3 > 0) PlaceLimitProfitExitOrder3(false); CreatePosition(false, ex.Order); } } if (orderName == ExitLongStopName || orderName == ExitShortStopName) { if (_exitProfitOrder1 != null) CancelOrder(_exitProfitOrder1); if (_exitProfitOrder2 != null) CancelOrder(_exitProfitOrder2); if (_exitProfitOrder3 != null) CancelOrder(_exitProfitOrder3); ScaleOrCloseOut(ex.Order); } if (orderName == ExitLongProfitName1 || orderName == ExitShortProfitName1) { if (PosSize2 == 0 && PosSize3 == 0) { if (_exitStopOrder != null) CancelOrder(_exitStopOrder); } else { ChangeStopExitOrder(); } ScaleOrCloseOut(ex.Order); } if (orderName == ExitLongProfitName2 || orderName == ExitShortProfitName2) { if (PosSize3 == 0) { if (_exitStopOrder != null) CancelOrder(_exitStopOrder); } else { ChangeStopExitOrder(); } ScaleOrCloseOut(ex.Order); } if (orderName == ExitLongProfitName3 || orderName == ExitShortProfitName3) { if (_exitStopOrder != null) CancelOrder(_exitStopOrder); ScaleOrCloseOut(ex.Order); } if (orderName == MarketCloseLong || orderName == MarketCloseShort) { if (_exitStopOrder != null) CancelOrder(_exitStopOrder); if (_exitProfitOrder1 != null) CancelOrder(_exitProfitOrder1); if (_exitProfitOrder2 != null) CancelOrder(_exitProfitOrder2); if (_exitProfitOrder3 != null) CancelOrder(_exitProfitOrder3); ScaleOrCloseOut(ex.Order); } } #endregion #region Virtual Trades // Remove duplicate field declaration // private bool _stopTradingForSession; // This line was causing the duplicate definition class MyPosition { public List Trades { get; set; } public bool IsOpen { get; set; } public bool IsLong { get; set; } public double Pnl { get; set; } } class MyTrade { public double EntryPrice { get; set; } public double ExitPrice { get; set; } public int Qnt { get; set; } public int ClosedQnt { get; set; } public double Pnl { get; set; } public bool IsClosed { get; set; } } private void CreatePosition(bool isLong, Order order) { var pos = new MyPosition() { Trades = new List(), IsOpen = true, IsLong = isLong }; pos.Trades.Add(new MyTrade() { EntryPrice = order.AverageFillPrice, Qnt = order.Quantity }); _positions.Add(pos); } private void AddToPosition(Order order) { _positions[_positions.Count - 1].Trades.Add(new MyTrade() { EntryPrice = order.AverageFillPrice, Qnt = order.Quantity }); } private void ScaleOrCloseOut(Order order) { var pos = _positions[_positions.Count - 1]; var needToClose = order.Quantity; var closed = 0; double tradePnl = 0; foreach (var trade in pos.Trades) { if (trade.IsClosed) continue; if (trade.Qnt == needToClose) { trade.IsClosed = true; trade.ClosedQnt = trade.Qnt; needToClose = 0; if (pos.IsLong) { trade.Pnl = (order.AverageFillPrice - trade.EntryPrice) * trade.Qnt * Instrument.MasterInstrument.PointValue; } if (!pos.IsLong) { trade.Pnl = (trade.EntryPrice - order.AverageFillPrice) * trade.Qnt * Instrument.MasterInstrument.PointValue; } } else if (trade.Qnt < needToClose) { trade.IsClosed = true; trade.ClosedQnt = trade.Qnt; needToClose -= trade.Qnt; if (pos.IsLong) { trade.Pnl = (order.AverageFillPrice - trade.EntryPrice) * trade.Qnt * Instrument.MasterInstrument.PointValue; } if (!pos.IsLong) { trade.Pnl = (trade.EntryPrice - order.AverageFillPrice) * trade.Qnt * Instrument.MasterInstrument.PointValue; } } else { var tradeNotClosedQnt = trade.Qnt - trade.ClosedQnt; var extraPnl = 0d; if (tradeNotClosedQnt == needToClose) { if (pos.IsLong) { extraPnl = (order.AverageFillPrice - trade.EntryPrice) * needToClose * Instrument.MasterInstrument.PointValue; } if (!pos.IsLong) { extraPnl = (trade.EntryPrice - order.AverageFillPrice) * needToClose * Instrument.MasterInstrument.PointValue; } trade.Pnl += extraPnl; trade.IsClosed = true; trade.ClosedQnt = trade.Qnt; } else if (tradeNotClosedQnt < needToClose) { if (pos.IsLong) { extraPnl = (order.AverageFillPrice - trade.EntryPrice) * tradeNotClosedQnt * Instrument.MasterInstrument.PointValue; } if (!pos.IsLong) { extraPnl = (trade.EntryPrice - order.AverageFillPrice) * tradeNotClosedQnt * Instrument.MasterInstrument.PointValue; } trade.Pnl += extraPnl; trade.IsClosed = true; trade.ClosedQnt = trade.Qnt; needToClose -= tradeNotClosedQnt; } else if (tradeNotClosedQnt > needToClose) { if (pos.IsLong) { extraPnl = (order.AverageFillPrice - trade.EntryPrice) * needToClose * Instrument.MasterInstrument.PointValue; } if (!pos.IsLong) { extraPnl = (trade.EntryPrice - order.AverageFillPrice) * needToClose * Instrument.MasterInstrument.PointValue; } trade.Pnl += extraPnl; trade.ClosedQnt += needToClose; needToClose = 0; } } } var positionPnl = pos.Trades.Sum(x => x.Pnl); if (!pos.Trades.All(x => x.IsClosed)) return; pos.Pnl = positionPnl; pos.IsOpen = false; if (Martingale) { if (positionPnl >= 0) { _posSize1 = PosSize1; _posSize2 = PosSize2; _posSize3 = PosSize3; _martinCounter = 0; } else { if (_martinCounter < MaxMartingales) { _posSize1 = _posSize1 * MartingaleMultiplier; _posSize2 = _posSize2 * MartingaleMultiplier; _posSize3 = _posSize3 * MartingaleMultiplier; _martinCounter++; } } } ClearOnClose(); } private void ClearOnClose() { if (_clearOnClose) { _positions.Clear(); _clearOnClose = false; } } #endregion } }