コード例 #1
0
 public TradingResult OnTradingResult(AccountInfo account, Ticker ticker, string text)
 {
     try {
         JObject obj   = JsonConvert.DeserializeObject <JObject>(text);
         JObject error = obj.Value <JObject>("error");
         if (error != null)
         {
             LogManager.Default.Error(this, "trade", error.Value <string>("message"));
             return(null);
         }
         TradingResult res = new TradingResult();
         res.OrderId     = obj.Value <string>("orderID");
         res.Date        = Convert.ToDateTime(obj.Value <string>("transactTime"));
         res.Amount      = FastValueConverter.Convert(obj.Value <string>("orderQty"));
         res.Type        = obj.Value <string>("side")[0] == 'B' ? OrderType.Buy : OrderType.Sell;
         res.Value       = FastValueConverter.Convert(obj.Value <string>("price"));
         res.OrderStatus = obj.Value <string>("ordStatus");
         res.Filled      = res.OrderStatus == "Filled";
         res.Total       = res.Amount;
         return(res);
     }
     catch (Exception e) {
         Telemetry.Default.TrackException(e);
         return(null);
     }
 }
コード例 #2
0
        public TradingResult OnSell(AccountInfo account, Ticker ticker, string result)
        {
            string        res = OnUuidResult(result);
            TradingResult t   = new TradingResult();

            t.Trades.Add(new TradeEntry()
            {
                Id = res
            });
            return(t);
        }
コード例 #3
0
        public async Task <TradingResult> TradeAsync()
        {
            TradingResult result = null;

            foreach (var traderRule in _traderRules)
            {
                result = await traderRule.ExecuteAsync(this);
            }

            return(result);
        }
コード例 #4
0
        protected virtual OpenPositionInfo OpenShortPosition(Ticker ticker, string mark, double value, double amount, bool allowTrailing, bool checkForMinValue, double trailingStopLossPc, double minProfitPc)
        {
            double spent = ticker.SpentInBaseCurrency(value, amount);

            if (1.05 * spent > GetMaxAllowedShortDeposit())
            {
                return(null);
            }
            if (checkForMinValue && spent < MinDepositForOpenPosition)
            {
                return(null);
            }
            TradingResult res = MarketSell(ticker, value, amount);

            if (res == null)
            {
                return(null);
            }
            OpenPositionInfo info = new OpenPositionInfo()
            {
                Ticker           = ticker,
                DataItemIndex    = StrategyData.Count - 1,
                Time             = DataProvider.CurrentTime,
                Type             = OrderType.Sell,
                Spent            = spent + CalcFee(res.Total),
                AllowTrailing    = allowTrailing,
                StopLossPercent  = trailingStopLossPc,
                OpenValue        = res.Value,
                OpenAmount       = res.Amount,
                Amount           = res.Amount,
                Mark             = mark,
                AllowHistory     = (DataProvider is SimulationStrategyDataProvider),
                Total            = res.Total,
                MinProfitPercent = minProfitPc,
                CloseValue       = value * (1 + minProfitPc * 0.01),
            };

            info.UpdateCurrentValue(DataProvider.CurrentTime, res.Value);

            OpenedOrders.Add(info);
            OrdersHistory.Add(info);

            OnOpenShortPosition(info);
            UpdateMaxAllowedShortDeposit(-info.Spent);

            IOpenedPositionsProvider provider = (IOpenedPositionsProvider)StrategyData.Last();

            provider.OpenedPositions.Add(info);
            provider.AddMark(mark);

            Save();
            return(info);
        }
コード例 #5
0
        private static void ClientShowErrorNotification(
            TradingStationLot lot,
            TradingResult error,
            bool isPlayerBuying)
        {
            var message = error.GetDescription();

            if (error == TradingResult.ErrorNotEnoughMoneyOnPlayer)
            {
                // calculate how many coins are needed
                var  character    = Client.Characters.CurrentPlayerCharacter;
                long deficitShiny = 0,
                     deficitPenny = 0;

                if (lot.PriceCoinShiny > 0)
                {
                    deficitShiny = lot.PriceCoinShiny
                                   - character.CountItemsOfType(ProtoItemCoinShiny.Value);
                }

                if (lot.PriceCoinPenny > 0)
                {
                    deficitPenny = lot.PriceCoinPenny
                                   - character.CountItemsOfType(ProtoItemCoinPenny.Value);
                }

                if (deficitShiny > 0 ||
                    deficitPenny > 0)
                {
                    if (deficitShiny > 0)
                    {
                        // ReSharper disable once CanExtractXamlLocalizableStringCSharp
                        message += "[br]"
                                   + string.Format(NotificationNeedMoreShinyCoins, deficitShiny);
                    }

                    if (deficitPenny > 0)
                    {
                        // ReSharper disable once CanExtractXamlLocalizableStringCSharp
                        message += "[br]"
                                   + string.Format(NotificationNeedMorePennyCoins, deficitPenny);
                    }
                }
            }

            NotificationSystem.ClientShowNotification(
                isPlayerBuying
                    ? NotiticationCannotBuy_Title
                    : NotiticationCannotSell_Title,
                message,
                NotificationColor.Bad,
                icon: lot.ProtoItem.Icon);
        }
コード例 #6
0
        private void RaiseOnTrade(TradingResult trade)
        {
            TradeEventHandler handler = (TradeEventHandler)Events[onTrade];

            if (handler != null)
            {
                handler(this, new TradeEventArgs()
                {
                    Trade = trade
                });
            }
        }
コード例 #7
0
        private static void ClientShowErrorNotification(
            TradingStationLot lot,
            TradingResult error,
            bool isPlayerBuying)
        {
            var message = error.GetDescription();

            if (error == TradingResult.ErrorNotEnoughMoneyOnPlayer)
            {
                // calculate how many coins are needed
                long shinyNotEnough = 0, pennyNotEnough = 0;
                if (lot.PriceCoinShiny > 0)
                {
                    shinyNotEnough = lot.PriceCoinShiny
                                     - Client.Characters.CurrentPlayerCharacter.CountItemsOfType(
                        ProtoItemCoinShiny.Value);
                }

                if (lot.PriceCoinPenny > 0)
                {
                    pennyNotEnough = lot.PriceCoinPenny
                                     - Client.Characters.CurrentPlayerCharacter.CountItemsOfType(
                        ProtoItemCoinPenny.Value);
                }

                if (shinyNotEnough > 0 ||
                    pennyNotEnough > 0)
                {
                    if (shinyNotEnough > 0)
                    {
                        message += Environment.NewLine
                                   + string.Format(NotificationNeedMoreShinyCoins, shinyNotEnough);
                    }

                    if (pennyNotEnough > 0)
                    {
                        message += Environment.NewLine
                                   + string.Format(NotificationNeedMorePennyCoins, pennyNotEnough);
                    }
                }
            }

            NotificationSystem.ClientShowNotification(
                isPlayerBuying
                    ? NotiticationCannotBuy_Title
                    : NotiticationCannotSell_Title,
                message,
                NotificationColor.Bad,
                icon: lot.ProtoItem.Icon);
        }
コード例 #8
0
        protected TradingResult AddDemoTradingResult(Ticker ticker, double rate, double amount, OrderType type)
        {
            TradingResult res = new TradingResult()
            {
                Amount = amount, Type = type, Date = DateTime.Now, OrderId = "demo", Total = rate * amount
            };

            res.Value = rate;
            res.Trades.Add(new TradeEntry()
            {
                Ticker = ticker, Amount = amount, Date = DateTime.Now, Id = "demo", Rate = rate, Total = rate * amount, Type = type
            });
            return(res);
        }
コード例 #9
0
        protected virtual OpenPositionInfo OpenLongPosition(string mark, double value, double amount, bool allowTrailing, bool checkForMinValue, double trailingStopLossPc, double minProfitPc)
        {
            if (1.05 * value * amount > MaxAllowedDeposit)
            {
                return(null);
            }
            if (checkForMinValue && value * amount < MinDepositForOpenPosition)
            {
                return(null);
            }
            TradingResult res = MarketBuy(value, amount);

            if (res == null)
            {
                return(null);
            }
            OpenPositionInfo info = new OpenPositionInfo()
            {
                DataItemIndex    = StrategyData.Count - 1,
                Time             = DataProvider.CurrentTime,
                Type             = OrderType.Buy,
                Spent            = res.Total + CalcFee(res.Total),
                AllowTrailing    = allowTrailing,
                StopLossPercent  = trailingStopLossPc,
                OpenValue        = res.Value,
                OpenAmount       = res.Amount,
                Amount           = res.Amount,
                Mark             = mark,
                AllowHistory     = (DataProvider is SimulationStrategyDataProvider),
                Total            = res.Total,
                MinProfitPercent = minProfitPc,
                CloseValue       = value * (1 + minProfitPc * 0.01),
            };

            info.UpdateCurrentValue(DataProvider.CurrentTime, res.Value);

            OpenedOrders.Add(info);
            OrdersHistory.Add(info);

            OnOpenLongPosition(info);
            MaxAllowedDeposit -= info.Spent;

            IOpenedPositionsProvider provider = (CombinedStrategyDataItem)StrategyData.Last();

            provider.OpenedPositions.Add(info);
            provider.AddMark(mark);

            Save();
            return(info);
        }
コード例 #10
0
        protected void Sell()
        {
            OrderBookEntry e   = GetAvailableToSell(SellLevel);
            TradingResult  res = MarketSell(e.Value, e.Amount);

            if (res == null)
            {
                return;
            }

            SoldTotal            += res.Amount;
            Earned               += res.Total - CalcFee(res.Total);
            MaxActualBuyDeposit  += res.Total;
            MaxActualSellDeposit -= res.Amount + CalcFee(res.Total);
        }
コード例 #11
0
        protected void Buy()
        {
            OrderBookEntry e   = GetAvailableToBuy(BuyLevel);
            TradingResult  res = MarketBuy(e.Value, e.Amount);

            if (res == null)
            {
                return;
            }

            BoughtTotal          += res.Total;
            Earned               -= res.Total + CalcFee(res.Total);
            MaxActualBuyDeposit  -= res.Total + CalcFee(res.Total);
            MaxActualSellDeposit += res.Amount;
        }
コード例 #12
0
        protected override TradingResult OnTradeResultCore(AccountInfo account, Ticker ticker, JObject res)
        {
            TradingResult tr = new TradingResult();

            tr.Ticker       = ticker;
            tr.OrderId      = res.Value <string>("orderId");
            tr.Amount       = Convert.ToDouble(res.Value <string>("executedQty"));
            tr.Value        = Convert.ToDouble(res.Value <string>("price"));
            tr.Type         = res.Value <string>("side")[0] == 'B'? OrderType.Buy: OrderType.Sell;
            tr.PositionSide = String2PositionSide(res.Value <string>("positionSide"));
            tr.Total        = tr.Amount * tr.Value;
            tr.OrderStatus  = res.Value <string>("status");
            tr.Filled       = res.Value <bool>("closePosition") == true;
            return(tr);
        }
コード例 #13
0
        protected void CloseShortPosition(OpenPositionInfo info, TradingResult res)
        {
            if (res == null)
            {
                return;
            }

            double earned = res.Total - CalcFee(res.Total);

            MaxAllowedShortDeposit += earned;

            info.UpdateCurrentValue(DataProvider.CurrentTime, res.Value);
            info.Earned    += earned;
            info.Amount    -= res.Amount;
            info.Total     -= res.Total;
            info.CloseValue = res.Value;

            //CombinedStrategyDataItem item = (CombinedStrategyDataItem)StrategyData.FirstOrDefault(i => ((CombinedStrategyDataItem)i).Time == info.CandlestickTime);
            //if(item != null) {
            //    item.Closed = true;
            //    item.CloseLength = ((CombinedStrategyDataItem)StrategyData.Last()).Index - item.Index;
            //}
            IOpenedPositionsProvider last = (IOpenedPositionsProvider)StrategyData.Last();
            double fee      = 0.075 / 100.0;
            double profit   = ((1 / info.CloseValue) - (1 / info.OpenValue)) * res.Amount;
            double openFee  = fee * (1 / info.OpenValue) * res.Amount;  // info.Ticker.Total(info.OpenValue, res.Amount);
            double closeFee = fee * (1 / info.CloseValue) * res.Amount; // info.Ticker.Total(info.CloseValue, res.Amount);

            profit -= openFee + closeFee;
            profit *= Short.UsdTicker.OrderBook.Bids[0].Value;
            Earned += profit;
            if (info.Amount < 0.000001)
            {
                OpenedOrders.Remove(info);
                last.ClosedPositions.Add(info);
                info.CloseTime = DataProvider.CurrentTime;
            }
            //last.ClosedOrder = true;
            //last.Value = Ticker.OrderBook.Bids[0].Value;
            //if(item != null)
            //    item.Profit = profit;
            //last.AddMark("Close " + info.Mark);
        }
コード例 #14
0
        protected virtual TradingResult PlaceAsk(double rate, double amount)
        {
            TradingResult res = null;

            if (!DemoMode)
            {
                res = Ticker.Sell(rate, amount);
                if (res == null)
                {
                    Log(LogType.Error, "", rate, amount, StrategyOperation.LimitSell);
                }
                return(res);
            }
            else
            {
                res = AddDemoTradingResult(rate, amount, OrderType.Sell);
            }
            Log(LogType.Error, "", rate, amount, StrategyOperation.LimitSell);
            return(res);
        }
コード例 #15
0
        protected virtual TradingResult MarketSell(double rate, double amount)
        {
            TradingResult res = null;

            if (!DemoMode)
            {
                res = Ticker.Sell(rate, amount);
                if (res == null)
                {
                    Log(LogType.Error, "", rate, amount, StrategyOperation.MarketSell);
                }
                return(res);
            }
            else
            {
                res = AddDemoTradingResult(rate, amount, OrderType.Sell);
            }
            TradeHistory.Add(res);
            Log(LogType.Success, "", rate, amount, StrategyOperation.MarketSell);
            return(res);
        }
コード例 #16
0
        protected TradingResult OnTradeResult(AccountInfo account, Ticker ticker, byte[] data)
        {
            if (data == null)
            {
                return(null);
            }
            string  text = Encoding.UTF8.GetString(data);
            JObject res  = JsonConvert.DeserializeObject <JObject>(text);

            if (res.Value <string>("code") != null)
            {
                LogManager.Default.Error(this, "trade", text);
                return(null);
            }

            TradingResult tr = new TradingResult();

            tr.OrderId     = res.Value <string>("orderId");
            tr.Amount      = Convert.ToDouble(res.Value <string>("executedQty"));
            tr.Value       = Convert.ToDouble(res.Value <string>("price"));
            tr.Total       = tr.Amount * tr.Value;
            tr.OrderStatus = res.Value <string>("status");
            tr.Filled      = tr.OrderStatus == "FILLED";
            JArray fills = res.Value <JArray>("fills");

            if (fills == null)
            {
                return(tr);
            }
            foreach (JObject item in fills)
            {
                TradeEntry e = new TradeEntry();
                e.Amount   = Convert.ToDouble(item.Value <string>("qty"));
                e.Rate     = Convert.ToDouble(item.Value <string>("price"));
                e.Fee      = Convert.ToDouble(item.Value <string>("comission"));
                e.FeeAsset = item.Value <string>("commissionAsset");
                tr.Trades.Add(e);
            }
            return(tr);
        }
コード例 #17
0
        protected void CloseLongPosition(OpenPositionInfo info, TradingResult res)
        {
            if (res == null)
            {
                return;
            }

            double earned = res.Total - CalcFee(res.Total);

            MaxAllowedDeposit += earned;

            info.UpdateCurrentValue(DataProvider.CurrentTime, res.Value);
            info.Earned    += earned;
            info.Amount    -= res.Amount;
            info.Total     -= res.Total;
            info.CloseValue = res.Value;

            //StatisticalArbitrageHistoryItem item = (StatisticalArbitrageHistoryItem)StrategyData.FirstOrDefault(i => ((StatisticalArbitrageHistoryItem)i).Time == info.CandlestickTime);
            //if(item != null) {
            //    item.Closed = true;
            //    item.CloseLength = ((CombinedStrategyDataItem)StrategyData.Last()).Index - item.Index;
            //}
            IOpenedPositionsProvider last = StrategyData.Last() as IOpenedPositionsProvider;

            if (info.Amount < 0.000001)
            {
                OpenedOrders.Remove(info);
                last.ClosedPositions.Add(info);
                info.CloseTime = DataProvider.CurrentTime;
                Earned        += info.Earned - info.Spent;
            }
            //last.ClosedOrder = true;
            //last.Value = Ticker.OrderBook.Bids[0].Value;
            //if(item != null)
            //    item.Profit = earned - info.Spent;
            //last.AddMark("Close " + info.Mark);
        }
コード例 #18
0
        protected override void CloseLongPosition(OpenPositionInfo info)
        {
            TradingResult res = MarketSell(Ticker.OrderBook.Bids[0].Value, info.Amount);

            if (res != null)
            {
                double earned = res.Total - CalcFee(res.Total);
                MaxAllowedDeposit += earned;
                info.UpdateCurrentValue(DataProvider.CurrentTime, res.Value);
                info.Earned    += earned;
                info.Amount    -= res.Amount;
                info.Total     -= res.Total;
                info.CloseValue = res.Value;
                CombinedStrategyDataItem item = (CombinedStrategyDataItem)StrategyData.FirstOrDefault(i => ((CombinedStrategyDataItem)i).Time == info.CandlestickTime);
                if (item != null)
                {
                    item.Closed      = true;
                    item.CloseLength = ((CombinedStrategyDataItem)StrategyData.Last()).Index - item.Index;
                }
                CombinedStrategyDataItem last = (CombinedStrategyDataItem)StrategyData.Last();
                if (info.Amount < 0.000001)
                {
                    OpenedOrders.Remove(info);
                    last.ClosedPositions.Add(info);
                    info.CloseTime = DataProvider.CurrentTime;
                    Earned        += info.Earned - info.Spent;
                }
                last.ClosedOrder = true;
                last.Value       = Ticker.OrderBook.Bids[0].Value;
                if (item != null)
                {
                    item.Profit = earned - info.Spent;
                }
                last.AddMark("Close " + info.Mark);
            }
        }
コード例 #19
0
        private void MakeTrade(OrderType type)
        {
            if (!ValidateChildrenCore())
            {
                XtraMessageBox.Show("Not all fields are filled!");
                return;
            }

            string validationError = string.Empty;

            if (type == OrderType.Buy)
            {
                validationError = Ticker.ValidateTrade(Settings.BuyPrice, Settings.BuyAmount);
            }
            else
            {
                validationError = Ticker.ValidateTrade(Settings.SellPrice, Settings.SellAmount);
            }
            if (!string.IsNullOrEmpty(validationError))
            {
                XtraMessageBox.Show("Error validating trade. Values will be corrected. Error was: " + validationError);

                double rate   = 0;
                double amount = 0;
                if (type == OrderType.Buy)
                {
                    rate   = Settings.BuyPrice;
                    amount = Settings.BuyAmount;
                }
                else
                {
                    rate   = Settings.SellPrice;
                    amount = Settings.SellAmount;
                }
                Ticker.CorrectTrade(ref rate, ref amount);
                if (type == OrderType.Buy)
                {
                    Settings.BuyPrice  = rate;
                    Settings.BuyAmount = amount;
                }
                else
                {
                    Settings.SellPrice  = rate;
                    Settings.SellAmount = amount;
                }
                return;
            }
            TradingResult res = null;

            if (type == OrderType.Buy)
            {
                res = Ticker.Buy(Settings.BuyPrice, Settings.BuyAmount);
                if (res == null)
                {
                    XtraMessageBox.Show("Error buying. " + LogManager.Default.Messages.Last().Description);
                    return;
                }
            }
            else
            {
                Settings.Enabled = false;
                res = Ticker.Sell(Settings.SellPrice, Settings.SellAmount);
                if (res == null)
                {
                    XtraMessageBox.Show("Error selling. " + LogManager.Default.Messages.Last().Description);
                    return;
                }
            }
            TradingResult = res;
            if (Settings.Enabled)
            {
                Settings.Date = DateTime.UtcNow;
                Ticker.Trailings.Add(Settings);
                Settings.Start();
                if (OperationsProvider != null)
                {
                    OperationsProvider.ShowTradingResult(Ticker);
                }
                Ticker.Save();

                XtraMessageBox.Show("Trailing added!");
            }
            RaiseOnTrade(res);
        }
コード例 #20
0
        private bool SharedValidateCanTrade(
            IStaticWorldObject tradingStation,
            TradingStationLot lot,
            TradingStationMode mode,
            ICharacter character,
            out TradingResult error)
        {
            var protoTradingStation = tradingStation.ProtoStaticWorldObject;

            var publicState = GetPublicState(tradingStation);

            if (publicState.Mode != mode)
            {
                throw new Exception("Trading station has different trading mode");
            }

            if (!protoTradingStation
                .SharedCanInteract(character, tradingStation, writeToLog: false))
            {
                error = TradingResult.ErrorCannotInteract;
                return(false);
            }

            var lots = publicState.Lots;

            var isLotFound = false;

            foreach (var otherLot in lots)
            {
                if (otherLot == lot)
                {
                    // found lot
                    isLotFound = true;
                    break;
                }
            }

            if (!isLotFound)
            {
                error = TradingResult.ErrorLotNotFound;
                return(false);
            }

            if (lot.State == TradingStationLotState.Disabled)
            {
                error = TradingResult.ErrorLotNotActive;
                return(false);
            }

            if (mode == TradingStationMode.StationBuying)
            {
                // ensure that the character has required item count to sell
                if (lot.LotQuantity > character.CountItemsOfType(lot.ProtoItem))
                {
                    error = TradingResult.ErrorNotEnoughItemsOnPlayer;
                    return(false);
                }

                if (IsServer)
                {
                    // ensure station has enough money to pay for these items
                    var tradingStationItemsContainer = GetPrivateState(tradingStation).StockItemsContainer;
                    if (!tradingStationItemsContainer.ContainsItemsOfType(
                            ProtoItemCoinPenny.Value,
                            requiredCount: lot.PriceCoinPenny) ||
                        !tradingStationItemsContainer.ContainsItemsOfType(
                            ProtoItemCoinShiny.Value,
                            requiredCount: lot.PriceCoinShiny))
                    {
                        error = TradingResult.ErrorNotEnoughMoneyOnStation;
                        return(false);
                    }
                }
            }
            else // if station selling
            {
                if (lot.CountAvailable == 0)
                {
                    error = TradingResult.ErrorNotEnoughItemsOnStation;
                    return(false);
                }

                // ensure player has enough money to pay for these items
                if (!character.ContainsItemsOfType(
                        ProtoItemCoinPenny.Value,
                        requiredCount: lot.PriceCoinPenny) ||
                    !character.ContainsItemsOfType(
                        ProtoItemCoinShiny.Value,
                        requiredCount: lot.PriceCoinShiny))
                {
                    error = TradingResult.ErrorNotEnoughMoneyOnPlayer;
                    return(false);
                }
            }

            // no error
            error = TradingResult.Success;
            return(true);
        }
コード例 #21
0
        private bool TryCloseDeals(double bottomBid, double topAsk, double spreadToClose)
        {
            if (spreadToClose > SpreadClosePosition)
            {
                return(false);
            }
            if (OpenedOrders.Count == 0)
            {
                return(false);
            }

            //StatisticalArbitrageOrderInfo order = GetOpenedOrderWithMaxSpread();
            foreach (StatisticalArbitrageOrderInfo order in OpenedPairs)
            {
                if (order.Spread < SpreadClosePosition)
                {
                    continue;
                }

                double actualLongCloseBid  = Long.GetActualBidByAmount(order.LongAmount);
                double actualShortCloseAsk = Short.GetActualAskByAmount(order.ShortAmount);

                double spread = actualShortCloseAsk - actualLongCloseBid;
                if (spread > SpreadClosePosition)
                {
                    continue;
                }

                double actualSellAmount = GetActualBottomBidAmount(actualLongCloseBid);
                double actualBuyAmount  = GetActualShortAskAmount(actualShortCloseAsk);

                double finalSellAmount = Math.Min(actualSellAmount, order.LongAmount);
                double koeffSell       = finalSellAmount / order.LongAmount;

                double finalBuyAmount = Math.Min(actualBuyAmount, order.ShortAmount);
                double koeffBuy       = finalBuyAmount / order.ShortAmount;
                double koeff          = Math.Min(koeffBuy, koeffSell);
                finalSellAmount *= koeff;
                finalBuyAmount  *= koeff;

                TradingResult closeLong  = MarketSell(Long, bottomBid, finalSellAmount);
                TradingResult closeShort = MarketBuy(Short, topAsk, finalBuyAmount);


                order.LongAmount  -= closeLong.Amount;
                order.ShortAmount -= closeShort.Amount;

                //double eInc = (1 / order.LongValue - 1 / order.ShortValue) * finalBuyAmount;
                //Earned += eInc;

                LastItem             = new StatisticalArbitrageHistoryItem();
                LastItem.Time        = DataProvider.CurrentTime;
                LastItem.Earned      = Earned;
                LastItem.LongAmount  = finalSellAmount;
                LastItem.LongAsk     = Long.OrderBook.LowestAsk;
                LastItem.LongBid     = bottomBid;
                LastItem.ShortAsk    = topAsk;
                LastItem.ShortBid    = Short.OrderBook.HighestBid;
                LastItem.ShortAmount = finalBuyAmount;
                LastItem.Close       = true;
                LastItem.Index       = StrategyData.Count;
                LastItem.Mark        = "CLOSE";
                LastItem.ClosedPositions.Add(order.LongPosition);
                LastItem.ClosedPositions.Add(order.ShortPosition);
                StrategyData.Add(LastItem);

                CloseLongPosition(order.LongPosition, closeLong);
                CloseShortPosition(order.ShortPosition, closeShort);

                if (order.LongAmount <= 0 && order.ShortAmount <= 0)
                {
                    OpenedPairs.Remove(order);
                    OnOrderClosed(LastItem);
                }
                else
                {
                    throw new Exception("Partial Close Not Implemented");
                }
            }

            return(true);
        }