/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*!
 Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl
 Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl
 Copyright (C) 2004 Ferdinando Ametrano

 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/

 QuantLib is free software: you can redistribute it and/or modify it
 under the terms of the QuantLib license.  You should have received a
 copy of the license along with this program; if not, please email
 <quantlib-dev@lists.sf.net>. The license is also available online at
 <http://quantlib.org/license.shtml>.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

/*  This example shows how to set up a Term Structure and then price a simple
    swap.
*/

// the only header you need to use QuantLib
#include "stdafx.h"
#include <windows.h>
#include <ql/quantlib.hpp>

#ifdef BOOST_MSVC
/* Uncomment the following lines to unmask floating-point
   exceptions. Warning: unpredictable results can arise...

   See http://www.wilmott.com/messageview.cfm?catid=10&threadid=9481
   Is there anyone with a definitive word about this?
*/
// #include <float.h>
// namespace { unsigned int u = _controlfp(_EM_INEXACT, _MCW_EM); }
#endif

#include <boost/timer.hpp>
#include <iostream>
#include <iomanip>

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)
namespace QuantLib {

    Integer sessionId() { return 0; }

}
#endif

double swaprate(LPVOID threadparam)
{
	boost::timer timer;
	std::cout << "thread " << *((int*)threadparam) << " started" << std::endl;
	std::cout.flush();

	/*********************
	***  MARKET DATA  ***
	*********************/

	Calendar calendar = TARGET();
	Date settlementDate(12, February, 2014);
	// must be a business day
	settlementDate = calendar.adjust(settlementDate);

	Integer fixingDays = 2;
	Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);
	// nothing to do with Date::todaysDate
	Settings::instance().evaluationDate() = todaysDate;


	todaysDate = Settings::instance().evaluationDate();
	std::cout << "Today: " << todaysDate.weekday()
		<< ", " << todaysDate << std::endl;

	std::cout << "Settlement date: " << settlementDate.weekday()
		<< ", " << settlementDate << std::endl;

	// deposits
	Rate d1wQuote = 0.0382;
	Rate d1mQuote = 0.0372;
	Rate d3mQuote = 0.0363;
	Rate d6mQuote = 0.0353;
	Rate d9mQuote = 0.0348;
	Rate d1yQuote = 0.0345;
	// FRAs
	Rate fra3x6Quote = 0.037125;
	Rate fra6x9Quote = 0.037125;
	Rate fra6x12Quote = 0.037125;
	// swaps
	Rate s2yQuote = 0.037125;
	Rate s3yQuote = 0.0398;
	Rate s5yQuote = 0.0443;
	Rate s10yQuote = 0.05165;
	Rate s15yQuote = 0.055175;


	/********************
	***    QUOTES    ***
	********************/

	// SimpleQuote stores a value which can be manually changed;
	// other Quote subclasses could read the value from a database
	// or some kind of data feed.

	// deposits
	boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote));
	boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote));
	boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote));
	boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote));
	boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote));
	boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote));
	// FRAs
	boost::shared_ptr<Quote> fra3x6Rate(new SimpleQuote(fra3x6Quote));
	boost::shared_ptr<Quote> fra6x9Rate(new SimpleQuote(fra6x9Quote));
	boost::shared_ptr<Quote> fra6x12Rate(new SimpleQuote(fra6x12Quote));
	// swaps
	boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote));
	boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote));
	boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote));
	boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote));
	boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote));


	/*********************
	***  RATE HELPERS ***
	*********************/

	// RateHelpers are built from the above quotes together with
	// other instrument dependant infos.  Quotes are passed in
	// relinkable handles which could be relinked to some other
	// data source later.

	// deposits
	DayCounter depositDayCounter = Actual360();

	boost::shared_ptr<RateHelper> d1w(new DepositRateHelper(
		Handle<Quote>(d1wRate),
		1 * Weeks, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> d1m(new DepositRateHelper(
		Handle<Quote>(d1mRate),
		1 * Months, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> d3m(new DepositRateHelper(
		Handle<Quote>(d3mRate),
		3 * Months, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> d6m(new DepositRateHelper(
		Handle<Quote>(d6mRate),
		6 * Months, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> d9m(new DepositRateHelper(
		Handle<Quote>(d9mRate),
		9 * Months, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> d1y(new DepositRateHelper(
		Handle<Quote>(d1yRate),
		1 * Years, fixingDays,
		calendar, ModifiedFollowing,
		true, depositDayCounter));


	// setup FRAs
	boost::shared_ptr<RateHelper> fra3x6(new FraRateHelper(
		Handle<Quote>(fra3x6Rate),
		3, 6, fixingDays, calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> fra6x9(new FraRateHelper(
		Handle<Quote>(fra6x9Rate),
		6, 9, fixingDays, calendar, ModifiedFollowing,
		true, depositDayCounter));
	boost::shared_ptr<RateHelper> fra6x12(new FraRateHelper(
		Handle<Quote>(fra6x12Rate),
		6, 12, fixingDays, calendar, ModifiedFollowing,
		true, depositDayCounter));

	// setup swaps
	Frequency swFixedLegFrequency = Annual;
	BusinessDayConvention swFixedLegConvention = Unadjusted;
	DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European);
	boost::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M);

	boost::shared_ptr<RateHelper> s2y(new SwapRateHelper(
		Handle<Quote>(s2yRate), 2 * Years,
		calendar, swFixedLegFrequency,
		swFixedLegConvention, swFixedLegDayCounter,
		swFloatingLegIndex));
	boost::shared_ptr<RateHelper> s3y(new SwapRateHelper(
		Handle<Quote>(s3yRate), 3 * Years,
		calendar, swFixedLegFrequency,
		swFixedLegConvention, swFixedLegDayCounter,
		swFloatingLegIndex));
	boost::shared_ptr<RateHelper> s5y(new SwapRateHelper(
		Handle<Quote>(s5yRate), 5 * Years,
		calendar, swFixedLegFrequency,
		swFixedLegConvention, swFixedLegDayCounter,
		swFloatingLegIndex));
	boost::shared_ptr<RateHelper> s10y(new SwapRateHelper(
		Handle<Quote>(s10yRate), 10 * Years,
		calendar, swFixedLegFrequency,
		swFixedLegConvention, swFixedLegDayCounter,
		swFloatingLegIndex));
	boost::shared_ptr<RateHelper> s15y(new SwapRateHelper(
		Handle<Quote>(s15yRate), 15 * Years,
		calendar, swFixedLegFrequency,
		swFixedLegConvention, swFixedLegDayCounter,
		swFloatingLegIndex));


	/*********************
	**  CURVE BUILDING **
	*********************/

	// Any DayCounter would be fine.
	// ActualActual::ISDA ensures that 30 years is 30.0
	DayCounter termStructureDayCounter =
		ActualActual(ActualActual::ISDA);

	double tolerance = 1.0e-15;

	// A depo-FRA-swap curve
	std::vector<boost::shared_ptr<RateHelper> > depoFRASwapInstruments;
	depoFRASwapInstruments.push_back(d1w);
	depoFRASwapInstruments.push_back(d1m);
	depoFRASwapInstruments.push_back(d3m);
	depoFRASwapInstruments.push_back(fra3x6);
	depoFRASwapInstruments.push_back(fra6x9);
	depoFRASwapInstruments.push_back(fra6x12);
	depoFRASwapInstruments.push_back(s2y);
	depoFRASwapInstruments.push_back(s3y);
	depoFRASwapInstruments.push_back(s5y);
	depoFRASwapInstruments.push_back(s10y);
	depoFRASwapInstruments.push_back(s15y);
	boost::shared_ptr<YieldTermStructure> depoFRASwapTermStructure(
		new PiecewiseYieldCurve<Discount, LogLinear>(
		settlementDate, depoFRASwapInstruments,
		termStructureDayCounter,
		tolerance));

	// Term structures that will be used for pricing:
	// the one used for discounting cash flows
	RelinkableHandle<YieldTermStructure> discountingTermStructure;
	// the one used for forward rate forecasting
	RelinkableHandle<YieldTermStructure> forecastingTermStructure;

	/*********************
	* SWAPS TO BE PRICED *
	**********************/

	// constant nominal 1,000,000 Euro
	Real nominal = 1000000.0;
	// fixed leg
	Frequency fixedLegFrequency = Annual;
	BusinessDayConvention fixedLegConvention = Unadjusted;
	BusinessDayConvention floatingLegConvention = ModifiedFollowing;
	DayCounter fixedLegDayCounter = Thirty360(Thirty360::European);
	Rate fixedRate = 0.04;
	DayCounter floatingLegDayCounter = Actual360();

	// floating leg
	Frequency floatingLegFrequency = Semiannual;
	boost::shared_ptr<IborIndex> euriborIndex(
		new Euribor6M(forecastingTermStructure));
	Spread spread = 0.0;

	Integer lenghtInYears = 5;
	VanillaSwap::Type swapType = VanillaSwap::Payer;

	Date maturity = settlementDate + lenghtInYears*Years;
	Schedule fixedSchedule(settlementDate, maturity,
							Period(fixedLegFrequency),
							calendar, fixedLegConvention,
							fixedLegConvention,
							DateGeneration::Forward, false);
	Schedule floatSchedule(settlementDate, maturity,
							Period(floatingLegFrequency),
							calendar, floatingLegConvention,
							floatingLegConvention,
							DateGeneration::Forward, false);
	VanillaSwap spot5YearSwap(swapType, nominal,
								fixedSchedule, fixedRate, fixedLegDayCounter,
								floatSchedule, euriborIndex, spread,
								floatingLegDayCounter);

	Real NPV;
	Rate fairRate;
	Spread fairSpread;

	boost::shared_ptr<PricingEngine> swapEngine(
		new DiscountingSwapEngine(discountingTermStructure));

	spot5YearSwap.setPricingEngine(swapEngine);

	// Of course, you're not forced to really use different curves
	forecastingTermStructure.linkTo(depoFRASwapTermStructure);
	discountingTermStructure.linkTo(depoFRASwapTermStructure);

	NPV = spot5YearSwap.NPV();
	fairSpread = spot5YearSwap.fairSpread();
	fairRate = spot5YearSwap.fairRate();

	return fairRate;
}

int main(int, char* []) {

    try {
		DWORD id;
		int numthreads;
		HANDLE *threads;
		
		std::cout << "Number of threads: ";
		std::cin >> numthreads;

		if (numthreads == 1) {
			for (int i = 0; i < 1000; i++) {
				std::cout << swaprate((LPVOID)&i);
			}
		} else {
			threads = new HANDLE[numthreads];

			for (int i = 0; i < numthreads; i++) {
				threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&swaprate, (LPVOID)&i, 0, &id);
				if (threads[i] == NULL) {
					std::cout << "Error creating thread" << std::endl;
					exit(-1);
				}
			}

			WaitForMultipleObjects(numthreads, threads, TRUE, INFINITE);

			delete threads;
		}

		std::cout << "all done";

    } catch (std::exception& e) {
        std::cerr << e.what() << std::endl;
        return 1;
    } catch (...) {
        std::cerr << "unknown error" << std::endl;
        return 1;
    }
}