Пример #1
0
        public static void GetBasePnl(IList <IPosition> positions, int barNum, double f, double futNominal,
                                      out CashPnlUsd cashPnlUsd, out CashPnlBtc cashPnlBtc)
        {
            double pnlBtc  = 0;
            double pnlUsd  = 0;
            double cashBtc = 0;
            double cashUsd = 0;
            int    len     = positions.Count;

            for (int j = 0; j < len; j++)
            {
                IPosition pos = positions[j];
                // Пока что State лучше не трогать
                //if (pos.PositionState == PositionState.HaveError)
                {
                    int    sign  = pos.IsLong ? 1 : -1;
                    double qty   = Math.Abs(pos.Shares);
                    double begPx = pos.GetBalancePrice(barNum);
                    // PROD-6085 - PnL(в битках) = (Позиция в лотах) * (номинал 10 долларов) * (1/BegPx - 1/EndPx)
                    // На обычном рынке знак "минус" стоит в честь того, что при покупке инструмента наличные средства уменьшаются,
                    // а прибыль вычисляется пропорционально величине (EndPx - BegPx).
                    // Но с биткойном все наоборот: из НАЧАЛЬНОЙ цены вычисляют конечную (1/BegPx - 1/EndPx)
                    // {Это нужно, чтобы на росте фьючерса получать прибыль}
                    // Поэтому требуется еще одно инвертирование знака.
                    cashBtc += sign * qty * futNominal / begPx;
                    pnlBtc  -= sign * qty * futNominal / f;

                    //// TODO: Учет комиссии
                    //cashBtc -= pos.EntryCommission;

                    if (!pos.IsActiveForBar(barNum))
                    {
                        // Знак "ПЛЮС" стоит в честь того, что при ЗАКРЫТИИ ЛОНГА наличные средства УВЕЛИЧИВАЮТСЯ
                        // Но еще помним про инвертирование на Дерибите
                        cashBtc -= sign * qty * futNominal / pos.ExitPrice;
                        pnlBtc  += sign * qty * futNominal / f;

                        //// TODO: Учет комиссии
                        //cashBtc -= pos.ExitCommission;
                    }
                }
            } // End for (int j = 0; j < len; j++)

            // Также насколько понял описание на сайте Дерибит, упрощается конвертация в доллары
            // https://www.deribit.com/main#/pages/docs/futures   (section 4: Example)
            // При этом для перевода в баксы используется КОНЕЧНЫЙ КУРС EndPx!
            cashUsd = cashBtc * f;
            pnlUsd  = pnlBtc * f;

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);
        }
Пример #2
0
        /// <summary>
        /// Получить финансовые параметры опционной позиции (один опцион)
        /// </summary>
        /// <param name="positions">список закрытых и открытых позиций</param>
        /// <param name="curBar">номер рабочего бара</param>
        /// <param name="f">текущая цена БА</param>
        /// <param name="k">страйк</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="sigma">волатильность</param>
        /// <param name="r">процентная ставка</param>
        /// <param name="isCall">put-false; call-true</param>
        /// <param name="cash">денежные затраты на формирование позы (могут быть отрицательными)</param>
        /// <param name="pnl">текущая цена позиции</param>
        public static void GetOptPnl(IList <IPosition> positions, int curBar,
                                     double f, double k, double dT, double sigma, double r, bool isCall, double btcUsdInd,
                                     out CashPnlUsd cashPnlUsd, out CashPnlBtc cashPnlBtc)
        {
            if (positions.Count == 0)
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return;
            }

            {
                var msg = $"Как получился отрицательный курс BTC/USD? btcUsdInd:{btcUsdInd}";
                Contract.Assert(DoubleUtil.IsPositive(btcUsdInd), msg);
                if (!DoubleUtil.IsPositive(btcUsdInd))
                {
                    throw new ArgumentException(msg, nameof(btcUsdInd));
                }
            }

            double pnlBtc  = 0;
            double pnlUsd  = 0;
            double cashBtc = 0;
            double cashUsd = 0;

            foreach (IPosition pos in positions)
            {
                int    sign = pos.IsLong ? 1 : -1;
                double qty  = Math.Abs(pos.Shares);
                // Знак "минус" стоит в честь того, что при покупке инструмента наличные средства уменьшаются
                double locCashBtcEntry = sign * pos.GetBalancePrice(curBar) * qty;
                cashBtc -= locCashBtcEntry;
                cashUsd -= locCashBtcEntry * btcUsdInd;
                double optPxUsd       = FinMath.GetOptionPrice(f, k, dT, sigma, r, isCall);
                double locPnlUsdEntry = sign * optPxUsd * qty;
                pnlUsd += locPnlUsdEntry;
                pnlBtc += locPnlUsdEntry / btcUsdInd;

                // Учет комиссии (комиссия в битках по идее)
                cashBtc -= pos.EntryCommission;
                cashUsd -= pos.EntryCommission * btcUsdInd;

                if (!pos.IsActiveForBar(curBar))
                {
                    // Знак "ПЛЮС" стоит в честь того, что при ЗАКРЫТИИ ЛОНГА наличные средства УВЕЛИЧИВАЮТСЯ
                    double locCashBtcExit = sign * pos.ExitPrice * qty;
                    cashBtc += locCashBtcExit;
                    cashUsd += locCashBtcExit * btcUsdInd;
                    double locPnlUsdExit = sign * optPxUsd * qty;
                    pnlUsd -= locPnlUsdExit;
                    pnlBtc -= locPnlUsdExit / btcUsdInd;

                    // Учет комиссии (комиссия в битках по идее)
                    cashBtc -= pos.ExitCommission;
                    cashUsd -= pos.ExitCommission * btcUsdInd;
                }
            } // End foreach (IPosition pos in positions)

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);
        }
Пример #3
0
        /// <summary>
        /// Получить финансовые параметры опционной позиции (колы и путы на одном страйке суммарно)
        /// </summary>
        /// <param name="smileInfo">улыбка</param>
        /// <param name="strike">страйк</param>
        /// <param name="putBarCount">количество баров для пута</param>
        /// <param name="callBarCount">количество баров для кола</param>
        /// <param name="putPositions">позиции пута</param>
        /// <param name="callPositions">позиции кола</param>
        /// <param name="f">текущая цена БА</param>
        /// <param name="dT">время до экспирации</param>
        /// <param name="cash">денежные затраты на формирование позы (могут быть отрицательными)</param>
        /// <param name="pnl">текущая цена позиции</param>
        /// <returns>true, если всё посчитано без ошибок</returns>
        public static bool TryGetPairPnl(SmileInfo smileInfo, double strike,
                                         int putBarCount, int callBarCount,
                                         IList <IPosition> putPositions, IList <IPosition> callPositions,
                                         double f, double dT, double btcUsdInd,
                                         out CashPnlUsd cashPnlUsd, out CashPnlBtc cashPnlBtc)
        {
            if ((putPositions.Count <= 0) && (callPositions.Count <= 0))
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return(true);
            }

            double?sigma = null;

            if (smileInfo != null)
            {
                double tmp;
                if (smileInfo.ContinuousFunction.TryGetValue(strike, out tmp))
                {
                    sigma = tmp;
                }
            }

            if ((sigma == null) || (!DoubleUtil.IsPositive(sigma.Value)))
            {
                cashPnlUsd = new CashPnlUsd();
                cashPnlBtc = new CashPnlBtc();
                return(false);
            }

            double pnlBtc  = 0;
            double pnlUsd  = 0;
            double cashBtc = 0;
            double cashUsd = 0;

            if (putPositions.Count > 0)
            {
                CashPnlUsd putUsd;
                CashPnlBtc putBtc;
                GetOptPnl(putPositions, putBarCount - 1,
                          f, strike, dT, sigma.Value, 0.0, false, btcUsdInd,
                          out putUsd, out putBtc);
                pnlUsd  += putUsd.PnlUsd;
                cashUsd += putUsd.CashUsd;
                pnlBtc  += putBtc.PnlBtc;
                cashBtc += putBtc.CashBtc;
            }

            if (callPositions.Count > 0)
            {
                CashPnlUsd callUsd;
                CashPnlBtc callBtc;
                GetOptPnl(callPositions, callBarCount - 1,
                          f, strike, dT, sigma.Value, 0.0, true, btcUsdInd,
                          out callUsd, out callBtc);
                pnlUsd  += callUsd.PnlUsd;
                cashUsd += callUsd.CashUsd;
                pnlBtc  += callBtc.PnlBtc;
                cashBtc += callBtc.CashBtc;
            }

            cashPnlUsd = new CashPnlUsd(cashUsd, pnlUsd);
            cashPnlBtc = new CashPnlBtc(cashBtc, pnlBtc);

            return(true);
        }