252 lines
8.7 KiB
C++
252 lines
8.7 KiB
C++
/*
|
|
* 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
|
|
* - Doug Johnson
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* Description: Implementation of the MEETickerTape class.
|
|
* See MEETickerTape.h for a description.
|
|
******************************************************************************/
|
|
|
|
#include "main/MEETickerTape.h"
|
|
#include "main/StatusTypeIDs.h"
|
|
|
|
using namespace TPCE;
|
|
|
|
const int CMEETickerTape::LIMIT_TRIGGER_TRADE_QTY = 375;
|
|
const int CMEETickerTape::RANDOM_TRADE_QTY_1 = 325;
|
|
const int CMEETickerTape::RANDOM_TRADE_QTY_2 = 425;
|
|
|
|
RNGSEED CMEETickerTape::GetRNGSeed(void) {
|
|
return (m_rnd.GetSeed());
|
|
}
|
|
|
|
void CMEETickerTape::SetRNGSeed(RNGSEED RNGSeed) {
|
|
m_rnd.SetSeed(RNGSeed);
|
|
}
|
|
|
|
void CMEETickerTape::Initialize(void) {
|
|
// Set up status and trade types for Market-Feed input
|
|
//
|
|
// Submitted
|
|
strncpy(m_TxnInput.StatusAndTradeType.status_submitted, m_StatusType[eSubmitted].ST_ID_CSTR(),
|
|
sizeof(m_TxnInput.StatusAndTradeType.status_submitted));
|
|
// Limit-Buy
|
|
strncpy(m_TxnInput.StatusAndTradeType.type_limit_buy, m_TradeType[eLimitBuy].TT_ID_CSTR(),
|
|
sizeof(m_TxnInput.StatusAndTradeType.type_limit_buy));
|
|
// Limit-Sell
|
|
strncpy(m_TxnInput.StatusAndTradeType.type_limit_sell, m_TradeType[eLimitSell].TT_ID_CSTR(),
|
|
sizeof(m_TxnInput.StatusAndTradeType.type_limit_sell));
|
|
// Stop-Loss
|
|
strncpy(m_TxnInput.StatusAndTradeType.type_stop_loss, m_TradeType[eStopLoss].TT_ID_CSTR(),
|
|
sizeof(m_TxnInput.StatusAndTradeType.type_stop_loss));
|
|
}
|
|
|
|
// Constructor - use default RNG seed
|
|
CMEETickerTape::CMEETickerTape(CMEESUTInterface *pSUT, CMEEPriceBoard *pPriceBoard, CDateTime *pBaseTime,
|
|
CDateTime *pCurrentTime, const DataFileManager &dfm)
|
|
: m_pSUT(pSUT), m_pPriceBoard(pPriceBoard), m_BatchIndex(0), m_BatchDuplicates(0), m_rnd(RNGSeedBaseMEETickerTape),
|
|
m_Enabled(true), m_pBaseTime(pBaseTime), m_pCurrentTime(pCurrentTime), m_StatusType(dfm.StatusTypeDataFile()),
|
|
m_TradeType(dfm.TradeTypeDataFile()) {
|
|
Initialize();
|
|
}
|
|
|
|
// Constructor - RNG seed provided
|
|
CMEETickerTape::CMEETickerTape(CMEESUTInterface *pSUT, CMEEPriceBoard *pPriceBoard, CDateTime *pBaseTime,
|
|
CDateTime *pCurrentTime, RNGSEED RNGSeed, const DataFileManager &dfm)
|
|
: m_pSUT(pSUT), m_pPriceBoard(pPriceBoard), m_BatchIndex(0), m_BatchDuplicates(0), m_rnd(RNGSeed), m_Enabled(true),
|
|
m_pBaseTime(pBaseTime), m_pCurrentTime(pCurrentTime), m_StatusType(dfm.StatusTypeDataFile()),
|
|
m_TradeType(dfm.TradeTypeDataFile()) {
|
|
Initialize();
|
|
}
|
|
|
|
CMEETickerTape::~CMEETickerTape(void) {
|
|
}
|
|
|
|
bool CMEETickerTape::DisableTicker(void) {
|
|
m_Enabled = false;
|
|
return (!m_Enabled);
|
|
}
|
|
|
|
bool CMEETickerTape::EnableTicker(void) {
|
|
m_Enabled = true;
|
|
return (m_Enabled);
|
|
}
|
|
|
|
void CMEETickerTape::AddEntry(PTickerEntry pTickerEntry) {
|
|
if (m_Enabled) {
|
|
AddToBatch(pTickerEntry);
|
|
AddArtificialEntries();
|
|
}
|
|
}
|
|
|
|
void CMEETickerTape::PostLimitOrder(PTradeRequest pTradeRequest) {
|
|
eTradeTypeID eTradeType;
|
|
double CurrentPrice = -1.0;
|
|
PTickerEntry pNewEntry = new TTickerEntry;
|
|
|
|
eTradeType = ConvertTradeTypeIdToEnum(pTradeRequest->trade_type_id);
|
|
|
|
pNewEntry->price_quote = pTradeRequest->price_quote;
|
|
strncpy(pNewEntry->symbol, pTradeRequest->symbol, sizeof(pNewEntry->symbol));
|
|
pNewEntry->trade_qty = LIMIT_TRIGGER_TRADE_QTY;
|
|
|
|
CurrentPrice = m_pPriceBoard->GetCurrentPrice(pTradeRequest->symbol).DollarAmount();
|
|
|
|
if (((eTradeType == eLimitBuy || eTradeType == eStopLoss) && CurrentPrice <= pTradeRequest->price_quote) ||
|
|
((eTradeType == eLimitSell) && CurrentPrice >= pTradeRequest->price_quote)) {
|
|
// Limit Order is in-the-money.
|
|
pNewEntry->price_quote = CurrentPrice;
|
|
// Make sure everything is up to date.
|
|
m_LimitOrderTimers.ProcessExpiredTimers();
|
|
// Now post the incoming entry.
|
|
m_InTheMoneyLimitOrderQ.push(pNewEntry);
|
|
} else {
|
|
// Limit Order is not in-the-money.
|
|
pNewEntry->price_quote = pTradeRequest->price_quote;
|
|
double TriggerTimeDelay;
|
|
double fCurrentTime = *m_pCurrentTime - *m_pBaseTime;
|
|
|
|
// GetSubmissionTime returns a value relative to time 0, so we
|
|
// need to substract off the value for the current time to get
|
|
// the delay time relative to now.
|
|
TriggerTimeDelay =
|
|
m_pPriceBoard->GetSubmissionTime(pNewEntry->symbol, fCurrentTime, pNewEntry->price_quote, eTradeType) -
|
|
fCurrentTime;
|
|
m_LimitOrderTimers.StartTimer(TriggerTimeDelay, this, &CMEETickerTape::AddLimitTrigger, pNewEntry);
|
|
}
|
|
}
|
|
|
|
void CMEETickerTape::AddLimitTrigger(PTickerEntry pTickerEntry) {
|
|
m_InTheMoneyLimitOrderQ.push(pTickerEntry);
|
|
}
|
|
|
|
void CMEETickerTape::AddArtificialEntries(void) {
|
|
TIdent SecurityIndex;
|
|
TTickerEntry TickerEntry;
|
|
int TotalEntryCount = 0;
|
|
static const int PaddingLimit =
|
|
(max_feed_len / 10) - 1; // NOTE: 10 here represents the ratio of TR to MF transactions
|
|
static const int PaddingLimitForAll = PaddingLimit; // MAX (trigger+artificial) entries
|
|
static const int PaddingLimitForTriggers = PaddingLimit; // MAX (triggered) entries
|
|
|
|
while (TotalEntryCount < PaddingLimitForTriggers && !m_InTheMoneyLimitOrderQ.empty()) {
|
|
PTickerEntry pEntry = m_InTheMoneyLimitOrderQ.front();
|
|
AddToBatch(pEntry);
|
|
delete pEntry;
|
|
m_InTheMoneyLimitOrderQ.pop();
|
|
TotalEntryCount++;
|
|
}
|
|
|
|
while (TotalEntryCount < PaddingLimitForAll) {
|
|
TickerEntry.trade_qty = (m_rnd.RndPercent(50)) ? RANDOM_TRADE_QTY_1 : RANDOM_TRADE_QTY_2;
|
|
|
|
SecurityIndex = m_rnd.RndInt64Range(0, m_pPriceBoard->m_iNumberOfSecurities - 1);
|
|
TickerEntry.price_quote = (m_pPriceBoard->GetCurrentPrice(SecurityIndex)).DollarAmount();
|
|
m_pPriceBoard->GetSymbol(SecurityIndex, TickerEntry.symbol, static_cast<INT32>(sizeof(TickerEntry.symbol)));
|
|
|
|
AddToBatch(&TickerEntry);
|
|
TotalEntryCount++;
|
|
}
|
|
}
|
|
|
|
void CMEETickerTape::AddToBatch(PTickerEntry pTickerEntry) {
|
|
// Check to see if this symbol already exists in the batch
|
|
for (int i = 0; i < m_BatchIndex; i++) {
|
|
if (strncmp(pTickerEntry->symbol, m_TxnInput.Entries[i].symbol, cSYMBOL_len) == 0) {
|
|
m_BatchDuplicates++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add the ticker to the batch
|
|
m_TxnInput.Entries[m_BatchIndex++] = *pTickerEntry;
|
|
|
|
// Buffer is full, time for Market-Feed.
|
|
if (max_feed_len == m_BatchIndex) {
|
|
m_TxnInput.unique_symbols = (max_feed_len - m_BatchDuplicates);
|
|
m_pSUT->MarketFeed(&m_TxnInput);
|
|
m_BatchIndex = 0;
|
|
m_BatchDuplicates = 0;
|
|
}
|
|
}
|
|
|
|
eTradeTypeID CMEETickerTape::ConvertTradeTypeIdToEnum(char *pTradeType) {
|
|
// Convert character trade type to enumeration
|
|
switch (pTradeType[0]) {
|
|
case 'T':
|
|
switch (pTradeType[1]) {
|
|
case 'L':
|
|
switch (pTradeType[2]) {
|
|
case 'B':
|
|
return (eLimitBuy);
|
|
case 'S':
|
|
return (eLimitSell);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 'M':
|
|
switch (pTradeType[2]) {
|
|
case 'B':
|
|
return (eMarketBuy);
|
|
case 'S':
|
|
return (eMarketSell);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 'S':
|
|
switch (pTradeType[2]) {
|
|
case 'L':
|
|
return (eStopLoss);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Throw exception - should never get here
|
|
assert(false); // this should never happen
|
|
|
|
return eMarketBuy; // silence compiler warning about not all control paths
|
|
// returning a value
|
|
}
|