// TestNewTrinomialTree.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <boost/timer.hpp>
#include <ql/quantlib.hpp>
#include "trinomialconvertibleengine.hpp"

#define LENGTH(a) (sizeof(a)/sizeof(a[0]))

using namespace QuantLib;
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{

	try {

		boost::timer timer;
		std::cout << std::endl;

		Option::Type type(Option::Put);
		Real underlying = 36.0;
		Real spreadRate = 0.005;

		Spread dividendYield = 0.02;
		Rate riskFreeRate = 0.06;
		Volatility volatility = 0.20;

		Integer settlementDays = 3;
		Integer length = 5;
		Real redemption = 100.0;
		Real conversionRatio = redemption/underlying; // at the money

		// set up dates/schedules
		Calendar calendar = TARGET();
		Date today = calendar.adjust(Date::todaysDate());

		Settings::instance().evaluationDate() = today;
		Date settlementDate = calendar.advance(today, settlementDays, Days);
		Date exerciseDate = calendar.advance(settlementDate, length, Years);
		Date issueDate = calendar.advance(exerciseDate, -length, Years);

		std::cout << "Settlement Date: " << settlementDate << std::endl;
		std::cout << "Issue Date: " << issueDate << std::endl;
		std::cout << "Exercise Date: " << exerciseDate << std::endl;

		BusinessDayConvention convention = ModifiedFollowing;

		Frequency frequency = Annual;

		Schedule schedule(issueDate,exerciseDate,Period(frequency),calendar,
			convention, convention, true, false);

		DividendSchedule dividends;
		CallabilitySchedule callability;

		std::vector<Real> coupons(1, 0.05);

		DayCounter bondDayCount = Thirty360();

		Integer callLength[] = { 2, 4 };  // Call dates, years 2, 4.
		Integer putLength[] = { 3 }; // Put dates year 3

		Real callPrices[] = { 101.5, 100.85 };
		Real putPrices[]= { 105.0 };

		// Load call schedules
		for (Size i=0; i<LENGTH(callLength); i++) {
			callability.push_back(
				boost::shared_ptr<Callability>(
				new SoftCallability(Callability::Price(
				callPrices[i],
				Callability::Price::Clean),
				schedule.date(callLength[i]),
				1.20)));
		}

		for (Size j=0; j<LENGTH(putLength); j++) {
			callability.push_back(
				boost::shared_ptr<Callability>(
				new Callability(Callability::Price(
				putPrices[j],
				Callability::Price::Clean),
				Callability::Put,
				schedule.date(putLength[j]))));
		}

		// Assume dividends are paid every 6 months.
		for (Date d = today + 6*Months; d < exerciseDate; d += 6*Months) {
			dividends.push_back(
				boost::shared_ptr<Dividend>(new FixedDividend(1.0, d)));
		}

		DayCounter dayCounter = Actual365Fixed();
		Time maturity = dayCounter.yearFraction(settlementDate,
			exerciseDate);

		std::cout << "option type = "  << type << std::endl;
		std::cout << "Time to maturity = "        << maturity
			<< std::endl;
		std::cout << "Underlying price = "        << underlying
			<< std::endl;
		std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate)
			<< std::endl;
		std::cout << "Dividend yield = " << io::rate(dividendYield)
			<< std::endl;
		std::cout << "Volatility = " << io::volatility(volatility)
			<< std::endl;
		std::cout << std::endl;

		std::string method;
		std::cout << std::endl ;

		// write column headings
		Size widths[] = { 35, 14, 14 };
		Size totalWidth = widths[0] + widths[1] + widths[2];
		std::string rule(totalWidth, '-'), dblrule(totalWidth, '=');

		std::cout << dblrule << std::endl;
		std::cout << "Tsiveriotis-Fernandes method" << std::endl;
		std::cout << dblrule << std::endl;
		std::cout << std::setw(widths[0]) << std::left << "Tree type"
			<< std::setw(widths[1]) << std::left << "European"
			<< std::setw(widths[1]) << std::left << "American"
			<< std::endl;
		std::cout << rule << std::endl;

		boost::shared_ptr<Exercise> exercise(
			new EuropeanExercise(exerciseDate));
		boost::shared_ptr<Exercise> amExercise(
			new AmericanExercise(settlementDate,
			exerciseDate));

		Handle<Quote> underlyingH(
			boost::shared_ptr<Quote>(new SimpleQuote(underlying)));

		Handle<YieldTermStructure> flatTermStructure(
			boost::shared_ptr<YieldTermStructure>(
			new FlatForward(settlementDate, riskFreeRate, dayCounter)));

		Handle<YieldTermStructure> flatDividendTS(
			boost::shared_ptr<YieldTermStructure>(
			new FlatForward(settlementDate, dividendYield, dayCounter)));

		Handle<BlackVolTermStructure> flatVolTS(
			boost::shared_ptr<BlackVolTermStructure>(
			new BlackConstantVol(settlementDate, volatility, dayCounter)));


		boost::shared_ptr<StochasticProcess> stochasticProcess(
			new BlackScholesMertonProcess(underlyingH,
			flatDividendTS,
			flatTermStructure,
			flatVolTS));

		Size timeSteps = 801;

		Handle<Quote> creditSpread(
			boost::shared_ptr<Quote>(new SimpleQuote(spreadRate)));

		boost::shared_ptr<Quote> rate(new SimpleQuote(riskFreeRate));

		Handle<YieldTermStructure> discountCurve(
			boost::shared_ptr<YieldTermStructure>(
			new FlatForward(today, Handle<Quote>(rate), dayCounter)));

		boost::shared_ptr<PricingEngine> engine(
			new TrinomialConvertibleEngine<RejebTree>(timeSteps));

		ConvertibleFixedCouponBond europeanBond(
			stochasticProcess, exercise, engine,
			conversionRatio, dividends, callability,
			creditSpread, issueDate, settlementDays,
			coupons, bondDayCount, schedule, redemption);

		ConvertibleFixedCouponBond americanBond(
			stochasticProcess, amExercise, engine,
			conversionRatio, dividends, callability,
			creditSpread, issueDate, settlementDays,
			coupons, bondDayCount, schedule, redemption);

		method = "Rejeb Tree";
		europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>(
			new TrinomialConvertibleEngine<RejebTree>(timeSteps)));
		americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>(
			new TrinomialConvertibleEngine<RejebTree>(timeSteps)));
		std::cout << std::setw(widths[0]) << std::left << method
			<< std::fixed
			<< std::setw(widths[1]) << std::left << europeanBond.NPV()
			<< std::setw(widths[2]) << std::left << americanBond.NPV()
			<< std::endl;

		method = "Kamrad-Ritchken";
		europeanBond.setPricingEngine(boost::shared_ptr<PricingEngine>(
			new TrinomialConvertibleEngine<KamradRitchken>(timeSteps)));
		americanBond.setPricingEngine(boost::shared_ptr<PricingEngine>(
			new TrinomialConvertibleEngine<KamradRitchken>(timeSteps)));
		std::cout << std::setw(widths[0]) << std::left << method
			<< std::fixed
			<< std::setw(widths[1]) << std::left << europeanBond.NPV()
			<< std::setw(widths[2]) << std::left << americanBond.NPV()
			<< std::endl;

		std::cout << dblrule << std::endl;

		Real seconds = timer.elapsed();
		Integer hours = int(seconds/3600);
		seconds -= hours * 3600;
		Integer minutes = int(seconds/60);
		seconds -= minutes * 60;
		std::cout << " \nRun completed in ";
		if (hours > 0)
			std::cout << hours << " h ";
		if (hours > 0 || minutes > 0)
			std::cout << minutes << " m ";
		std::cout << std::fixed << std::setprecision(0)
			<< seconds << " s\n" << std::endl;

		system("PAUSE");
		return 0;
	} catch (std::exception& e) {
		std::cout << e.what() << std::endl;

		system("PAUSE");
		return 1;
	} catch (...) {
		std::cout << "unknown error" << std::endl;

		system("PAUSE");
		return 1;
	}


	system("PAUSE");
	return 0;
}

