/* * Legal Notice * * This document and associated source code (the "Work") is a part of a * benchmark specification maintained by the TPC. * * The TPC reserves all right, title, and interest to the Work as provided * under U.S. and international laws, including without limitation all patent * and trademark rights therein. * * No Warranty * * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION * CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE * AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER * WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY, * INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, * DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR * PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF * WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE. * ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, * QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT * WITH REGARD TO THE WORK. * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO * ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE * COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS * OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT, * INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY, * OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT * RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD * ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES. * * Contributors * - Sergey Vasilevskiy, Doug Johnson */ /****************************************************************************** * Description: Implementation of the MEESecurity class. * See MEESecurity.h for a description. ******************************************************************************/ #include #include "main/MEESecurity.h" using namespace TPCE; // Period of security price change (in seconds) // e.g. when the price will repeat. // const int iSecPricePeriod = 900; // 15 minutes // Mean delay between Submission and Completion times // const double fMeanCompletionTimeDelay = 1.0; // Delay added to the clipped MEE Completion delay // to simulate SUT-to-MEE and MEE-to-SUT processing delays. // const double fCompletionSUTDelay = 1.0; // seconds /* * Constructor. * Initializes the class using RNG and constants. * * PARAMETERS: * none. * * RETURNS: * not applicable. */ CMEESecurity::CMEESecurity() : m_rnd(RNGSeedBaseMEESecurity), m_fRangeLow(fMinSecPrice), m_fRangeHigh(fMaxSecPrice), m_fRange(fMaxSecPrice - fMinSecPrice), m_iPeriod(iSecPricePeriod), m_TradingTimeSoFar(0), m_pBaseTime(NULL), m_pCurrentTime(NULL) { } /* * Initialize before the first use. * Separated from constructor in order to have default (no-parameters) * constructor. * * PARAMETERS: * IN Index - unique security index to * generate a unique starting price IN TradeTimeSoFar - point * where we last left off on the price curve IN pBaseTime - wall clock time * corresponding to the initial time for all securities IN pCurrentTime - * current time for the security (determines current price) IN * fMeanInTheMoneySubmissionDelay - Mean delay between Pending and Submission * times for an immediatelly triggered (in-the-money) limit order. * * RETURNS: * none */ void CMEESecurity::Init(INT32 TradingTimeSoFar, // for picking up where we last // left off on the price curve CDateTime *pBaseTime, CDateTime *pCurrentTime, // Mean delay between Pending and Submission times // for an immediatelly triggered (in-the-money) limit // order. // // The actual delay is randomly calculated in the range // [0.5 * Mean .. 1.5 * Mean] // double fMeanInTheMoneySubmissionDelay) { m_TradingTimeSoFar = TradingTimeSoFar; m_pBaseTime = pBaseTime; m_pCurrentTime = pCurrentTime; m_fMeanInTheMoneySubmissionDelay = fMeanInTheMoneySubmissionDelay; // Reset the RNG seed so that loading in multiple EGenLoader instances // results in the same database as running just one EGenLoader. // m_rnd.SetSeed(RNGSeedBaseMEESecurity); } /* * Calculate the "unique" starting offset * in the price curve based on the security ID (0-based) * 0 corresponds to m_fRangeLow price, * m_fPeriod/2 corresponds to m_fRangeHigh price, * m_fPeriod corresponds again to m_fRangeLow price * * PARAMETERS: * IN SecurityIndex - unique security index to generate a unique * starting price * * RETURNS: * time from which to calculate initial price */ inline double CMEESecurity::InitialTime(TIdent SecurityIndex) { INT32 MsPerPeriod = iSecPricePeriod * MsPerSecond; TIdent SecurityFactor = SecurityIndex * 556237 + 253791; TIdent TradingFactor = (TIdent)m_TradingTimeSoFar * MsPerSecond; // Cast to avoid truncation return (((TradingFactor + SecurityFactor) % MsPerPeriod) / MsPerSecondDivisor); } /* * Negative exponential distribution. * * PARAMETERS: * IN fMean - mean value of the distribution * * RETURNS: * random value according to the negative * exponential distribution with the given mean. */ inline double CMEESecurity::NegExp(double fMean) { return RoundToNearestNsec(m_rnd.RndDoubleNegExp(fMean)); } /* * Calculate current price for the security identified by its index (0-based). * * PARAMETERS: * IN SecurityIndex - unique identifier for the security. * * RETURNS: * price at this point in time given with integer number of cents. */ CMoney CMEESecurity::GetCurrentPrice(TIdent SecurityIndex) { return (CalculatePrice(SecurityIndex, *m_pCurrentTime - *m_pBaseTime)); } /* * Return minimum price on the price curve for any security. * * PARAMETERS: * none. * * RETURNS: * minimum price given with integer number of cents. */ CMoney CMEESecurity::GetMinPrice(void) { return (m_fRangeLow); } /* * Return maximum price on the price curve for any security. * * PARAMETERS: * none. * * RETURNS: * maximum price given with integer number of cents. */ CMoney CMEESecurity::GetMaxPrice(void) { return (m_fRangeHigh); } /* * Calculate price at a certain point in time. * * PARAMETERS: * IN SecurityIndex - unique security index to generate a unique * starting price IN fTime - seconds from initial time * * RETURNS: * price according to the triangular function * that will be achived at the given time */ CMoney CMEESecurity::CalculatePrice(TIdent SecurityIndex, double fTime) { double fPeriodTime = (fTime + InitialTime(SecurityIndex)) / (double)m_iPeriod; double fTimeWithinPeriod = (fPeriodTime - (int)fPeriodTime) * (double)m_iPeriod; double fPricePosition; // 0..1 corresponding to m_fRangeLow..m_fRangeHigh CMoney PriceCents; if (fTimeWithinPeriod < m_iPeriod / 2) { fPricePosition = fTimeWithinPeriod / (m_iPeriod / 2); } else { fPricePosition = (m_iPeriod - fTimeWithinPeriod) / (m_iPeriod / 2); } PriceCents = m_fRangeLow + m_fRange * fPricePosition; return PriceCents; } /* * Calculate time required to move between certain prices * with certain initial direction of price change. * * PARAMETERS: * IN fStartPrice - price at the start of the time interval * IN fEndPrice - price at the end of the time interval * IN iStartDirection - direction (up or down) on the price curve at * the start of the time interval * * RETURNS: * seconds required to move from the start price to the end price */ double CMEESecurity::CalculateTime(CMoney fStartPrice, CMoney fEndPrice, int iStartDirection) { int iHalfPeriod = m_iPeriod / 2; // Distance on the price curve from StartPrice to EndPrice (in dollars) // CMoney fDistance; // Amount of time (in seconds) needed to move $1 on the price curve. // In half a period the price moves over the entire price range. // double fSpeed = iHalfPeriod / m_fRange.DollarAmount(); if (fEndPrice > fStartPrice) { if (iStartDirection > 0) { fDistance = fEndPrice - fStartPrice; } else { fDistance = (fStartPrice - m_fRangeLow) + (fEndPrice - m_fRangeLow); } } else { if (iStartDirection > 0) { fDistance = (m_fRangeHigh - fStartPrice) + (m_fRangeHigh - fEndPrice); } else { fDistance = fStartPrice - fEndPrice; } } return fDistance.DollarAmount() * fSpeed; } /* * Calculate triggering time for limit orders. * * PARAMETERS: * IN SecurityIndex - unique security index to generate a unique * starting price IN fPendingTime - pending time of the order, in seconds * from time 0 IN fLimitPrice - limit price of the order IN TradeType - * order trade type * * RETURNS: * the expected submission time */ double CMEESecurity::GetSubmissionTime(TIdent SecurityIndex, double fPendingTime, // in seconds from time 0 CMoney fLimitPrice, eTradeTypeID TradeType) { CMoney fPriceAtPendingTime = CalculatePrice(SecurityIndex, fPendingTime); int iDirectionAtPendingTime; double fSubmissionTimeFromPending; // Submission - Pending time difference // Check if the order is already in the money // e.g. if the current price is less than the buy price // or the current price is more than the sell price. // if (((TradeType == eLimitBuy || TradeType == eStopLoss) && fPriceAtPendingTime <= fLimitPrice) || ((TradeType == eLimitSell) && fPriceAtPendingTime >= fLimitPrice)) { // Order is in-the-money. Trigger immediatelly. // fSubmissionTimeFromPending = m_rnd.RndDoubleIncrRange(0.5 * m_fMeanInTheMoneySubmissionDelay, 1.5 * m_fMeanInTheMoneySubmissionDelay, 0.001); } else { if ((int)(fPendingTime + InitialTime(SecurityIndex)) % m_iPeriod < m_iPeriod / 2) { // In the first half of the period => price is going up // iDirectionAtPendingTime = 1; } else { // In the second half of the period => price is going down // iDirectionAtPendingTime = -1; } fSubmissionTimeFromPending = CalculateTime(fPriceAtPendingTime, fLimitPrice, iDirectionAtPendingTime); } return fPendingTime + fSubmissionTimeFromPending; } /* * Return the expected completion time and the completion price. * Completion time is between 0 and 5 seconds * with 1 sec mean. * * Used to calculate completion time for * both limit (first must get submission time) * and market orders. * * Equivalent of MEE function sequence * 'receive trade' then 'complete the trade request'. * * PARAMETERS: * IN SecurityIndex - unique security index to generate a * unique starting price IN fSubmissionTime - time when the order was * submitted, in seconds from time 0 OUT pCompletionPrice - completion price * of the order * * RETURNS: * the approximated completion time for the trade * */ double CMEESecurity::GetCompletionTime(TIdent SecurityIndex, double fSubmissionTime, // in seconds from time 0 CMoney *pCompletionPrice // out ) { double fCompletionDelay = NegExp(fMeanCompletionTimeDelay); // Clip at 5 seconds to prevent rare, but really long delays // if (fCompletionDelay > 5.0) { fCompletionDelay = 5.0; } if (pCompletionPrice != NULL) { *pCompletionPrice = CalculatePrice(SecurityIndex, fSubmissionTime + fCompletionDelay); } return fSubmissionTime + fCompletionDelay + fCompletionSUTDelay; }