Как повысить производительность при расчете греков в DolphinDB? - PullRequest
2 голосов
/ 16 февраля 2020

Я использую DolphinDB для вычисления Греки , я написал это в векторизации, и производительность довольно хорошая. Но я не могу реализовать подразумеваемую волатильность в векторном виде, что делает производительность очень плохой. Как я могу улучшить производительность следующей реализации?

def GBlackScholes(future_price, strike, input_ttm, risk_rate, b_rate, input_vol, is_call) {
  ttm = input_ttm + 0.000000000000001;
  vol = input_vol + 0.000000000000001;

  d1 = (log(future_price/strike) + (b_rate + vol*vol/2) * ttm) / (vol * sqrt(ttm));
  d2 = d1 - vol * sqrt(ttm);

  if (is_call) {
    return future_price * exp((b_rate - risk_rate) * ttm) * cdfNormal(0, 1, d1) - strike * exp(-risk_rate*ttm) * cdfNormal(0, 1, d2);
  } else {
    return strike * exp(-risk_rate*ttm) * cdfNormal(0, 1, -d2) - future_price * exp((b_rate - risk_rate) * ttm) * cdfNormal(0, 1, -d1);
  }
}

def ImpliedVolatility(future_price, strike, ttm, risk_rate, b_rate, option_price, is_call) {
  high=5.0;
  low = 0.0;

  do {
    if (GBlackScholes(future_price, strike, ttm, risk_rate, b_rate, (high+low)/2, is_call) > option_price) {
      high = (high+low)/2;
    } else {
      low = (high + low) /2;
    }
  } while ((high-low) > 0.00001);

  return (high + low) /2;
}

Ответы [ 2 ]

3 голосов
/ 17 февраля 2020

Q : Как улучшить производительность следующей реализации?

Векторизованная обработка?

Это часть немного загадочная - простите старый квант, чтобы он не читал это достаточно хорошо - информация о том, какие параметры были предназначены, чтобы не быть скаляром, отсутствовала, поэтому анализ был основан на явно представленных фрагментах информации.

Отказ от ответственности :
Хотя я знаю, что DolphinDB не опубликовал троичный оператор (...)?(...):(...), который был бы доступен в API publi c, я чувствую, что идеи, изложенные ниже, ясны и Звук.

Боевая производительность?

Если серьезно говорить о производительности, сначала давайте рассмотрим и принципиально избежим повторных пересчетов, которые имели место в предложенном выше коде:

def GBlackScholes( future_price,
                   strike,
                   input_ttm,
                   risk_rate,
                   b_rate,
                   input_vol,               // <------------[VAR]:: <-- ( high+low )/2
                   is_call                 //
                   ) {                    //
    ttm = input_ttm + 0.000000000000001; //-do-while-(CONST)
    vol = input_vol + 0.000000000000001;//--do-while--------[VAR]
                                       //
    d1  = ( log( future_price )       //----do-while-(CONST)
          - log( strike )            //-----do-while-(CONST)
          + b_rate                  //------do-while-(CONST)
          + vol*vol/2              //-------do-while--------[VAR]
          * ttm                   //--------do-while-(CONST)
            ) / ( vol            //---------do-while--------[VAR]
                * sqrt( ttm )   //----------do-while-(CONST)
                  );           //
    d2  = ( d1                //------------do-while--------[VAR]
          - vol              //-------------do-while--------[VAR]
          * sqrt( ttm )     //--------------do-while-(CONST)
            );             //              ++---------------[VAR]
                          //               ||     .________________________________________________.
 // -----------[VAR]-?-( cdfNormal(--------vv-) * [                                                ]--do-while-(CONST)
    return ( is_call ? ( cdfNormal( 0, 1,  d1 ) * future_price * exp( ( b_rate - risk_rate ) * ttm )
                       - cdfNormal( 0, 1,  d2 ) * strike       * exp(           -risk_rate   * ttm )
                         )
                     : ( cdfNormal( 0, 1, -d2 ) * strike       * exp(           -risk_rate   * ttm )
                       - cdfNormal( 0, 1, -d1 ) * future_price * exp( ( b_rate - risk_rate ) * ttm )
                         )
             );
}

Чуть лучше сформулированная GBlackScholes_WHILEd() функция - это экономит ~ 22x float -OP (некоторые из которых довольно дорогие) для каждого * 10 31 * -L OOP:

def GBlackScholes_WHILEd( vol_,         // <--------------------------[VAR]:: ( high+low )/2 + 0.000000000000001;
                          V1,           // <--------------------------[VAR]:: vol_ * C3
                          is_call,
                          ttm_,         // do-while-(CONST)
                          C1, C2, C3,   // do-while-(CONST)
                          R1, R2        // do-while-(CONST)
                          ) {
  d1  = ( C1
        + C2
        * vol_*vol_ //--------------------do-while--------[VAR]
          )
        / V1;       //--------------------do-while--------[VAR]

  d2  = ( d1 //---------------------------do-while--------[VAR]
        - V1 //---------------------------do-while--------[VAR]
          );

  //    --[VAR]--- ? ( cdfNormal(------[VAR]) * <________________________________________________>--do-while-(CONST)
  return ( is_call ? ( cdfNormal( 0, 1,  d1 ) * R1
                     - cdfNormal( 0, 1,  d2 ) * R2
                       )
                   : ( cdfNormal( 0, 1, -d2 ) * R2
                     - cdfNormal( 0, 1, -d1 ) * R1
                       )
           );
}

Наконец:

Наиболее эффективно сформулированная функция ImpliedVolatility() позволяет избежать даже всех вызовов-подписей в -l oop вообще обработал, выполнил некоторую алгебру и остается на достижимом крае производительности:

def ImpliedVolatility( future_price,
                       strike,
                       ttm,
                       risk_rate,
                       b_rate,
                       option_price,
                       is_call
                       ) {
    high = 5.0;                                                 // IS THIS A UNIVERSALLY SAFE & TRUE SUPREME - i.e. SAFELY ABOVE ALL POSSIBLE OPTIONS ?
    low  = 0.0;

    ttm_ = ttm + 0.000000000000001;                             // do-while-(CONST)     1x fADD
    C1   = log(future_price ) - log( strike ) + b_rate;         // do-while-(CONST)     1x fADD  1x fDIV  1x fLOG  1x fNEG
    C2   =     ( ttm_ ) / 2;                                    // do-while-(CONST)              1x fDIV
    C3   = sqrt( ttm_ );                                        // do-while-(CONST)                       1x fSQRT
    R1   = future_price * exp( ( b_rate - risk_rate ) * ttm_ ); // do-while-(CONST)     1x fADD  2x fMUL  1x fEXP  1x fNEG
    R2   = strike       * exp(           -risk_rate   * ttm_ ); // do-while-(CONST)              2x fMUL  1x fEXP  1x fNEG
    U4   = C2   - ttm_;                                         // do-while-(CONST)     1x fADD                    1x fNEG
    U5   = ttm_ - C2;                                           // do-while-(CONST)     1x fADD                    1x fNEG
    U3inv= 1./ C3;                                              // do-while-(CONST)              1x fDIV

 // ------------------------------------------------------------// -----------------------------------------------------------------------------------------------
    if ( is_call ) {                                            // do-while-RE-TESTING: AVOIDED REPETITIVE per-loop COSTS of TESTING THE VERY THE SAME
                                                                // -----------------------------------------------------------------------------------------------
              do {
                    mid   = ( high + low ) / 2;       // cheapest  do-while per-loop-[VAR]-update
                    vol_  = mid + 0.000000000000001; //  cheapest  do-while per-loop-[VAR]-update
                    vol_2 = vol_ * vol_;            //   cheapest  do-while per-loop-[VAR]-update

                 /* ---------------------------------------------------------------------------------------------------------------------------------------------
                    HAS EVOLVED FROM THE ORIGINAL FORMULATION + AVOIDED REPETITIVE per-loop COSTS of 20+ expensive float OPs fully wasted,all in do-while-(CONST)
                                                              + AVOIDED REPETITIVE per-loop COSTS of all the CALL fun() STACK MANIPULATIONS AND RELATED OVERHEADS
                    ---------------------------------------------------------------------------------------------------------------------------------------------
                 */ 
                 // V4d1  =         ( C1 +   C2               * vol_2      ) / vol_ / C3;                            // [VAR]-dependent updates per loop
                 // V5d2  =         ( C1 + ( C2 - ttm_ )      * vol_2      ) / vol_ / C3;                            // [VAR]-dependent updates per loop
                 // Vmd2  =         (           ( ttm_ - C2 ) * vol_2 - C1 ) / vol_ / C3;                            // [VAR]-dependent updates per loop
                 // 
                 // V4d1  = U3inv * ( C1 +                 C2 * vol_2      ) / vol_;                                 // fMUL faster than fDIV + a few more fUtilityCONSTs
                 // V5d2  = U3inv * ( C1 +                 U4 * vol_2      ) / vol_;                                 // fMUL faster than fDIV + a few more fUtilityCONSTs
                 // Vmd2  = U3inv * (                      U5 * vol_2 - C1 ) / vol_;                                 // fMUL faster than fDIV + a few more fUtilityCONSTs
                 // 
                 // ---------------------------------------------------------------------------------------------------
                 // THIS AVOIDS RE-CALCULATION OF ALL do-while-(CONST)s BY THEIR RE-USE :
                 //
                 // if ( option_price < GBlackScholes_WHILEd( vol_,       // <----------[VAR] input_vol,             // GBlackScholes( future_price,
                 //                                           vol_ * C3,  // <----------[VAR] V1,                    //                strike,
                 //                                           is_call,    //                  is_call,               //                ttm,
                 //                                           ttm_,       // do-while-(CONST) ttm_,                  //                risk_rate,
                 //                                           C1, C2, C3, // do-while-(CONST) C1, C2, C3,            //                b_rate,
                 //                                           R1, R2      // do-while-(CONST) R1, R2                 //                mid,         // == (high+low)/2,
                 //                                           )           //                                         //                is_call
                 //      ) ...                                            //                                         //                )
                 //
                 // -------------------------------------------------------------------------------------------------- 
                 // EVEN BETTER :
                 //
                 // if ( option_price < ( is_call ? ( R1 * cdfNormal( 0, 1,  U3inv * ( C1 + C2 * vol_2      ) / vol_ )
                 //                                 - R2 * cdfNormal( 0, 1,  U3inv * ( C1 + U4 * vol_2      ) / vol_ )
                 //                                   )
                 //                               : ( R2 * cdfNormal( 0, 1,  U3inv * (      U5 * vol_2 - C1 ) / vol_ )
                 //                                 - R1 * cdfNormal( 0, 1, -U3inv * ( C1 + C2 * vol_2      ) / vol_ )
                 //                                   )
                 //                       )
                 // 
                 //     ) ...
                 // ________________________________( CALL-OPTIONs )__________________________________________________

                    if ( option_price < (           ( R1 * cdfNormal( 0, 1,  U3inv * ( C1 + C2 * vol_2      ) / vol_ )
                                                    - R2 * cdfNormal( 0, 1,  U3inv * ( C1 + U4 * vol_2      ) / vol_ )
                                                      )
                                          )
                         ) {  high = mid;                             // == (high+low)/2;  // LOWER    HI-SIDE BRACKET
                    } else {   low = mid;                             // == (high+low)/2;  // HEIGHTEN LO-SIDE BRACKET
                    }
              } while ( ( high - low ) > 0.00001 );
              return    ( high + low ) / 2; // ________________________________________________________________ JIT/RET
    } else {  
              do {  mid   = ( high + low ) / 2;       // cheapest  do-while per-loop-[VAR]-update
                    vol_  = mid + 0.000000000000001; //  cheapest  do-while per-loop-[VAR]-update
                    vol_2 = vol_ * vol_;            //   cheapest  do-while per-loop-[VAR]-update

                 // ________________________________( PUT-OPTIONs )___________________________________________________

                    if ( option_price < (           ( R2 * cdfNormal( 0, 1,  U3inv * (      U5 * vol_2 - C1 ) / vol_ )
                                                    - R1 * cdfNormal( 0, 1, -U3inv * ( C1 + C2 * vol_2      ) / vol_ )
                                                      )
                                          )
                         ) {  high = mid;                             // == (high+low)/2;  // LOWER    HI-SIDE BRACKET
                    } else {   low = mid;                             // == (high+low)/2;  // HEIGHTEN LO-SIDE BRACKET
                    }
              } while ( ( high - low ) > 0.00001 );
              return    ( high + low ) / 2; // ________________________________________________________________ JIT/RET
    }
}
2 голосов
/ 19 февраля 2020

В DolphinDB 1.01 введена своевременная компиляция (JIT). JIT идеально подходит для не векторизованных операций, таких как бинарный поиск для подразумеваемой волатильности.

Чтобы использовать JIT, просто добавьте нотацию @jit перед пользовательскими функциями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...