MCOV

Open Source Monte Carlo Option Valuer
Home
Contact
Download
Releases
Installation
Read-Me
License

Monte Carlo
Strategy Objects
C++ Classes

C++ Files
mcovaluer.h
mcovaluer.cxx
mcov.cxx

mcovaluer.cxx

C++ Function Bodies for MCOV Software

This is the mcovaluer.cxx file that comes with a MCOV distribution (without the license section)...

// ================================================================
//  Implementation of functions declared in  mcovaluer.h
//
//  See  mcovaluer.h  for more information.
//
//  See copyright notice and license at end of this file.
//
// ----------------------------------------------------------------

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
#include "mcovaluer.h"

using namespace std;

// ================================================================
//  mcoOption  -  Option on an underlying financial instrument
//
// ----------------------------------------------------------------
//  Construct an mcoOption

mcoOption::mcoOption (char put_call,
                      double strike_price, long days_to_expiry)
    : m_put_call(put_call),
      m_strike_price(strike_price),
      m_days_to_expiry(days_to_expiry) { 

  m_iday_expiry = -1; 

}


// ----------------------------------------------------------------
//  Initialize the option and return true if it can be used
//  with the passed dayset.
//  Return true if this option's attributes are reasonable.
//
//  This function is called by an mcoValuer function after the
//  history is read but before any projection is done.

bool  mcoOption::initialize (mcoDaySet *dayset) {

  mcoErr *e = mcoErr::instance();

  if (e->is_err()) return false;

  if (!dayset) {
    e->err("mcoOption::initialize called with null dayset");
    return false;
  }

  if (m_put_call == 'p') {
    m_put_call = 'P';
  } else if (m_put_call == 'c') {
    m_put_call = 'C';
  } else if (m_put_call != 'P' && m_put_call != 'C') {
    e->err("put_call must be P or C");
    return false;
  }

  if (m_strike_price <= 0.0 || m_strike_price > 100000.0) {
    e->err("Bad strike-price: ");
    e->append (m_strike_price);
    return false;
  }

  if (m_days_to_expiry < 1 || m_days_to_expiry > 500) {
    e->err("Bad Days-to-expiry value: ");
    e->append (m_days_to_expiry);
    return false;
  }

  if (m_days_to_expiry > dayset->max_proj()) {
    e->err("Days-to-expiry is too high: ");
    e->append (m_days_to_expiry);
    return false;
  }

  set_iday_expiry (dayset->iday_hist_last() + m_days_to_expiry);

}

// ================================================================
//  mcoValuer  -  Object to determine value of an option
//
// ----------------------------------------------------------------
//  Construct an mcoValuer

mcoValuer::mcoValuer (const char *hist_file,
                      mcoSellStratBase *ss, mcoPDayStratBase *ps,
                      mcoDayFactoryBase *df,
                      long max_hist, long max_proj) {

  mcoErr *e = mcoErr::instance();  // There may be a singleton...
  if (e) delete e;
  e = mcoErr::instance();  // Create a fresh one.

  if (df) {
    m_day_factory = df;
    m_owns_day_factory = false;
  } else {
    m_day_factory = new mcoDayFactory;
    if (!m_day_factory) {
      e->err("ERROR allocating day-factory");
      cleanup();
      return;
    }
    m_owns_day_factory = true;
  }

  m_dayset = new mcoDaySet (hist_file, m_day_factory,
                            max_hist, max_proj);
  if (! m_dayset) {
    if (!e->is_err()) e->err("ERROR creating dayset");
    cleanup();
    return;
  }

  if (ss) {
    m_sell_strategy = ss;
    ss->set_dayset(m_dayset);
    m_owns_sell_strategy = false;
  } else {
    m_sell_strategy = new mcoSellStrategy1 (m_dayset);
    if (!m_sell_strategy) {
      e->err("ERROR allocating sell-strategy");
      cleanup();
      return;
    }
    m_owns_sell_strategy = true;
  }

  if (ps) {
    m_pday_strategy = ps;
    ps->set_dayset(m_dayset);
    m_owns_pday_strategy = false;
  } else {
    m_pday_strategy = new mcoPDayStrategy1 (m_dayset);
    if (!m_pday_strategy) {
      e->err("ERROR allocating sell-strategy");
      cleanup();
      return;
    }
    m_owns_pday_strategy = true;
  }

}

// ----------------------------------------------------------------
//  Destruct an mcoValuer

mcoValuer::~mcoValuer (void) {

  cleanup();

}
  
// ----------------------------------------------------------------
//  Cleanup the mcoValuer so it can be deleted.

void mcoValuer::cleanup (void) {

  if (m_pday_strategy && m_owns_pday_strategy) {
    delete m_pday_strategy;
  }

  if (m_sell_strategy && m_owns_sell_strategy) {
    delete m_sell_strategy;
  }

  if (m_day_factory && m_owns_day_factory) {
    delete m_day_factory;
  }

  if (m_dayset) delete m_dayset;

  mcoErr *e = mcoErr::instance();
  if (e) delete e;

}

// ----------------------------------------------------------------
//  Determine the value of an option.

double mcoValuer::option_value (mcoOption *opt, long num_tries) {

  mcoErr *e = mcoErr::instance();

  if (e->is_err()) return -9999999.99;

  if ( ! opt->initialize(m_dayset) ) return -9999999.99;

  if (num_tries < 1 || num_tries > 100000) {
    e->err("Number of tries must be between 1 and 100000");
    return -9999999.99;
  }

  double ave_tot = 0.0;
  double ave_num = 0.0;
  double ave_val = 0.0;

  for (long itry=0; itryis_err()) return -9999999.99;

    ave_tot += do_try(opt);
    ave_num += 1.0;

  }    // end of loop through tries

  if (ave_num > 0.0) ave_val = ave_tot / ave_num;

  return ave_val;

}

// ----------------------------------------------------------------
//  Do a try and return the value of the option.

double mcoValuer::do_try (mcoOption *opt) {

  mcoErr *e = mcoErr::instance();

  double opt_val = 0.0;

  m_dayset->reset_projection();

  for (long i=0; idays_to_expiry(); i++) {

      // Identify day to use as prototype for price movement
    long iday_proto = m_pday_strategy->prototype_iday();

    if (e->is_err()) return -9999999.99;

      // Apply prototype's action to day being projected
    m_pday_strategy->apply_prototype(iday_proto);

    if (e->is_err()) return -9999999.99;

      // Decide if option should be sold this day
    double price_if_sold = m_sell_strategy->price_if_sold(opt);
    if (price_if_sold > 0.0) {         // option was sold
      opt_val = m_sell_strategy->option_bid(opt, price_if_sold);
      break;
    }    // end of block if option is sold

    if (e->is_err()) return -9999999.99;

  }    // end of loop through days until option expires.

  return opt_val;

}

// ================================================================
//  mcoDayBase  -  Base class for day in underlying instrument.
//
// ----------------------------------------------------------------
//  Note that this day is set (called by set() functions)

void mcoDayBase::set_is_set (bool sis) {

  m_is_set = sis;

  if (sis) m_parent->note_day_is_set(m_iday);

}

// ================================================================
//  mcoDay  -  A day for the underlying instrument.
//
// ----------------------------------------------------------------
//  Return a pointer to a specified day, downcast to this type.

mcoDay * mcoDay::day (long iday) {

  return ( (mcoDay*) parent()->day(iday) );

}

// ----------------------------------------------------------------
//  Calculate m_close_chg1

void  mcoDay::calc_close_chg1 (void) {

  long prev_iday = iday() - 1;
  if (prev_iday < 0) return;   // no previous day

  double prev_close  = day(prev_iday)->price_close();
  double prev_daynum = day(prev_iday)->daynum();

  if (daynum() != prev_daynum+1) return;  // days not continuous

  if (prev_close >= 0.0) {
    m_close_chg1 = m_price_close / prev_close;
  }

}

// ----------------------------------------------------------------
//  Set the values from separate fundamental values.

void mcoDay::set (long daynum, double prc_close) {

  set_daynum(daynum);
  m_price_close = prc_close;
  calc_close_chg1();

  set_is_set(true);

}

// ----------------------------------------------------------------
//  Set the values from a non-comment record from an xxx.mcod file

bool  mcoDay::set (const string& str) {

  mcoErr *e = mcoErr::instance();

  istringstream ist (str);
  long a_daynum;
  double a_price_close;

  ist >> a_daynum >> a_price_close;

  if (a_daynum < -20000 || a_daynum > 20000) {
    e->err("ERROR: daynum must be between -20000 and 20000 : ");
    e->append (a_daynum);
    return false;
  }

  if (a_price_close < 0.01 || a_price_close > 10000.0) {
    e->err("Error in price_close in history: ");
    e->append(a_price_close);
    e->append(" (daynum: ");
    e->append(a_daynum);
    e->append(")");
    return false;
  }

  set (a_daynum, a_price_close);

  return true;

}

// ================================================================
//  mcoDaySet  -  Set of days - history and projection
//
// ----------------------------------------------------------------
//  Construct a dayset.

mcoDaySet::mcoDaySet (const char *hist_file,
                      mcoDayFactoryBase *df,
                      long max_hist_days, long max_proj_days) 
            : m_max_hist(max_hist_days), 
              m_max_proj(max_proj_days) {

  m_num_hist = m_num_proj = 0;
  m_day = 0;
  m_history_is_set = false;

  mcoErr *e = mcoErr::instance();

  if (!hist_file) {
    e->err("Error: no history file specified");
    cleanup();
    return;
  }

  if (max_hist_days < 2 || max_hist_days > 100000) {
    e->err("max_hist_days must be between 2 and 100000.");
    cleanup();
    return;
  }

  if (max_proj_days < 1 || max_proj_days > 1000) {
    e->err("max_proj_days must be between 1 and 1000.");
    cleanup();
    return;
  }

  m_day = new (mcoDayBase*) [max_day()];
  if (!m_day) {
    e->err("ERROR allocating new day array");
    cleanup();
    return;
  }
  for (long i=0; iis_err()) {
      e->err("Error: History file: ");
      e->append (hist_file);
      e->append (" has fewer than two days");
    }
    cleanup();
    return;
  }

  m_history_is_set = true;

  long daynum = m_day[iday_hist_last()]->daynum();

  long iday_last = iday_proj_first() + max_proj() - 1;
  for (long iday=iday_proj_first(); iday<=iday_last; iday++) {
    m_day[iday] = df->new_day(this,iday);
    if (!m_day[iday]) {
      if (!e->is_err()) {
        e->err("ERROR allocating creating new day");
      }
      cleanup();
      return;
    }
    m_day[iday]->set_daynum(++daynum);
  }

}

// ----------------------------------------------------------------
//  Destruct a dayset.

mcoDaySet::~mcoDaySet (void) {

  cleanup();

}


// ----------------------------------------------------------------
//  Note that the indicated day has been set.

void mcoDaySet::note_day_is_set (long iday) {

  if ( iday < num_set() ) {
    mcoErr *e = mcoErr::instance();
    e->err("Bug: Days set out of order");
    return;
  }

  if (m_history_is_set) {            // projected day
    if ( (iday+1) > num_set() ) {
       m_num_proj = (iday+1) - m_num_hist;
    }
  } else {                           // history day
    if ( (iday+1) > m_num_hist) m_num_hist = iday + 1;
  }

}


// ----------------------------------------------------------------
//  Cleanup a dayset so that it can be deleted.

void mcoDaySet::cleanup (void) {

  if (m_day) {

    for (long i=0; ierr("Error opening history file ");
    e->append (hist_file);
    return false;
  }

  long iday = -1;   // index into m_day

  string str;
  char  buf[501];

  while (true) {    // records in history file...

    if (e->is_err()) return false;

    ifs.getline(buf,500);  // read a line into the char-array.

    if (ifs.eof()) break;

    if (buf[0] == '#') continue;

    str = buf;   // Move C-style string into C++ string

    if (m_num_hist >= max_hist()) {
      e->err("Error: History file has too many days");
      return false;
    }

    iday++;

    m_day[iday] = df->new_day(this,iday);

    if (!m_day[iday]) {
      e->err("Error creating new day");
      return false;
    }

    if ( ! m_day[iday]->set(str) ) return false;

  }    // end of loop through records in history file

  return true;  // success

}


// ----------------------------------------------------------------
//  Reset the projection - the projected days

void mcoDaySet::reset_projection (void) {

  if ( num_proj() <= 0 ) return;

  for (long iday = iday_proj_first(); 
            iday <= iday_proj_last(); iday++) {
    day(iday)->reset();
  }

  m_num_proj = 0;

}


// ================================================================
//  mcoSellStrategy1  -  deciding when to sell and at what price
//
// ----------------------------------------------------------------
//  Decide whether the option was sold during the last projected
//  day.  If it was, return the price of the underlying instrument
//  at the time of the option sale; otherwise return 0.0.

double mcoSellStrategy1::price_if_sold (mcoOption *opt) {

  if (dayset()->num_proj() >= opt->days_to_expiry() ) {

    long iday_last = dayset()->iday_proj_last();
    double prc = day(iday_last)->price_close();

    if (opt->put_call() == 'P') {
      if (prc < opt->strike_price()) return prc;
    } else {                        // option is a call
      if (prc > opt->strike_price()) return prc;
    }

  }

  return 0.0;

}

// ----------------------------------------------------------------
//  Estimate the time-value component of the bid for the option
//  at the time the option is sold.  Since, in this strategy,
//  we sell the option just before expiry, time-value is zero.

double mcoSellStrategy1::rem_time_value (mcoOption *opt,
                                         double price) {

  return 0.0;

}

// ----------------------------------------------------------------
//  Estimate the bid (the price someone is willing to pay)
//  for the option which was sold when the underlying instrument
//  was selling for 'price'.

double mcoSellStrategy1::option_bid (mcoOption *opt,
                                     double price) {

  double val = 0.0;

  val += rem_time_value (opt, price);
  double strike = opt->strike_price();

  if (opt->put_call() == 'P') {  // Option is a Put
    if (price < strike) val += (strike - price);
  } else {
    if (price > strike) val += (price - strike);
  }

  val *= 0.99;   // apply bid/ask spread

  return val;

}

// ================================================================
//  mcoPDayStrategy1  -  pick/apply a prototype day
//
//  This is a simple implementation of the strategy.
//
// ----------------------------------------------------------------
//  Identify a day to use as a prototype for the next day 
//  to be projected.
//
//  In this simple implementation of this strategy:
//    pick a day of history (after the first) at random.

long mcoPDayStrategy1::prototype_iday (void) {

  long n = dayset()->num_hist() - 2;
  long iday = mcoRand::randnum(n) + 1; // 1(2nd) to end.
  return iday;

}


// ----------------------------------------------------------------
//  Apply a prototype day to the next day to be projected.

void mcoPDayStrategy1::apply_prototype (long iday_proto) {

  long iday_prev = dayset()->num_set() - 1;  // last day set
  long daynum_prev = day(iday_prev)->daynum();

  long iday_proj = iday_prev + 1;
  long daynum_proj = daynum_prev + 1;

  double close_prev = day(iday_prev)->price_close();
  double close_chg1 = day(iday_proto)->close_chg1();
  if (close_chg1 <= 0.0) close_chg1 = 1.0;

    // do the projection of the closing-price...
  double close_proj = close_prev * close_chg1;

  day(iday_proj)->set(daynum_proj, close_proj);
  
}


// ================================================================
//  mcoErr  -  Error status/message - a singleton.
//
// ----------------------------------------------------------------
//  Definition of static data member.

mcoErr * mcoErr::m_instance = 0;

// ================================================================
//  mcoRand  -  Random number generator - a singleton.
//
// ----------------------------------------------------------------
//  Definition of static data member.

bool  mcoRand::m_is_setup = false;


// ----------------------------------------------------------------
//  Return a random number between zero and maxnum.

long mcoRand::randnum (long max_num) {

  if (!mcoRand::m_is_setup) {
    time_t tt;
    struct tm *ptm;
    time (&tt);
    ptm = localtime(&tt);
    int seed = ptm->tm_hour * 10000 +
               ptm->tm_min  *   100 +
               ptm->tm_sec;
    srand(seed);
    mcoRand::m_is_setup = true;
    srand(seed);
  } 

  return int((double(max_num)*rand())/(RAND_MAX+1.0));

}

// ================================================================