Skip to content

Instantly share code, notes, and snippets.

@austin362667
Created March 21, 2023 08:05
Show Gist options
  • Save austin362667/da7bfc90765b53fb896b920068829be3 to your computer and use it in GitHub Desktop.
Save austin362667/da7bfc90765b53fb896b920068829be3 to your computer and use it in GitHub Desktop.
// Copyright 2023 Optiver Asia Pacific Pty. Ltd.
//
// This file is part of Ready Trader Go.
//
// Ready Trader Go is free software: you can redistribute it and/or
// modify it under the terms of the GNU Affero General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// Ready Trader Go 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
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with Ready Trader Go. If not, see
// <https://www.gnu.org/licenses/>.
#include <cmath>
#include <array>
#include <boost/asio/io_context.hpp>
#include <ready_trader_go/logging.h>
#include "autotrader.h"
using namespace ReadyTraderGo;
RTG_INLINE_GLOBAL_LOGGER_WITH_CHANNEL(LG_AT, "AUTO")
constexpr int LOT_SIZE = 100;
constexpr int HALF_SPREAD = 400;
constexpr int POSITION_LIMIT = 100;
constexpr int TICK_SIZE_IN_CENTS = 100;
constexpr int MIN_BID_NEARST_TICK = (MINIMUM_BID + TICK_SIZE_IN_CENTS) / TICK_SIZE_IN_CENTS * TICK_SIZE_IN_CENTS;
constexpr int MAX_ASK_NEAREST_TICK = MAXIMUM_ASK / TICK_SIZE_IN_CENTS * TICK_SIZE_IN_CENTS;
AutoTrader::AutoTrader(boost::asio::io_context& context) : BaseAutoTrader(context)
{
}
void AutoTrader::DisconnectHandler()
{
BaseAutoTrader::DisconnectHandler();
RLOG(LG_AT, LogLevel::LL_INFO) << "execution connection lost";
}
void AutoTrader::ErrorMessageHandler(unsigned long clientOrderId,
const std::string& errorMessage)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "error with order " << clientOrderId << ": " << errorMessage;
if (clientOrderId != 0 && ((mAsks.count(clientOrderId) == 1) || (mBids.count(clientOrderId) == 1)))
{
OrderStatusMessageHandler(clientOrderId, 0, 0, 0);
}
}
void AutoTrader::HedgeFilledMessageHandler(unsigned long clientOrderId,
unsigned long price,
unsigned long volume)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "hedge order " << clientOrderId << " filled for " << volume
<< " lots at $" << price << " average price in cents";
}
void AutoTrader::OrderBookMessageHandler(Instrument instrument,
unsigned long sequenceNumber,
const std::array<unsigned long, TOP_LEVEL_COUNT>& askPrices,
const std::array<unsigned long, TOP_LEVEL_COUNT>& askVolumes,
const std::array<unsigned long, TOP_LEVEL_COUNT>& bidPrices,
const std::array<unsigned long, TOP_LEVEL_COUNT>& bidVolumes)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "order book received for " << instrument << " instrument"
<< ": ask prices: " << askPrices[0]
<< "; ask volumes: " << askVolumes[0]
<< "; bid prices: " << bidPrices[0]
<< "; bid volumes: " << bidVolumes[0];
if (instrument == Instrument::FUTURE)
{
// unsigned long priceAdjustment = - (mPosition / LOT_SIZE) * TICK_SIZE_IN_CENTS;
unsigned long newAskPrice = (askPrices[0] != 0) ? askPrices[0] + HALF_SPREAD : 0 ; // + priceAdjustment : 0;
unsigned long newBidPrice = (bidPrices[0] != 0) ? bidPrices[0] - HALF_SPREAD : 0; // + priceAdjustment : 0;
if (mAskId != 0 && newAskPrice != 0 && newAskPrice != mAskPrice)
{
SendCancelOrder(mAskId);
mAskId = 0;
}
if (mBidId != 0 && newBidPrice != 0 && newBidPrice != mBidPrice)
{
SendCancelOrder(mBidId);
mBidId = 0;
}
if (mAskId == 0 && newAskPrice != 0 && mPosition > -1*POSITION_LIMIT)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "Ask Side, Position: " << mPosition;
mAskId = mNextMessageId++;
mAskPrice = newAskPrice;
SendInsertOrder(mAskId, Side::SELL, newAskPrice, LOT_SIZE*(1+std::fmin(POSITION_LIMIT, mPosition)/POSITION_LIMIT), Lifespan::GOOD_FOR_DAY);
mAsks.emplace(mAskId);
}
if (mBidId == 0 && newBidPrice != 0 && mPosition < POSITION_LIMIT)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "Bid Side, Position: " << mPosition;
mBidId = mNextMessageId++;
mBidPrice = newBidPrice;
SendInsertOrder(mBidId, Side::BUY, newBidPrice, LOT_SIZE*(1-std::fmin(POSITION_LIMIT, mPosition)/POSITION_LIMIT), Lifespan::GOOD_FOR_DAY);
mBids.emplace(mBidId);
}
}
}
void AutoTrader::OrderFilledMessageHandler(unsigned long clientOrderId,
unsigned long price,
unsigned long volume)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "order " << clientOrderId << " filled for " << volume
<< " lots at $" << price << " cents";
if (mAsks.count(clientOrderId) == 1)
{
mPosition -= (long)volume;
SendHedgeOrder(mNextMessageId++, Side::BUY, MAX_ASK_NEAREST_TICK, volume);
}
else if (mBids.count(clientOrderId) == 1)
{
mPosition += (long)volume;
SendHedgeOrder(mNextMessageId++, Side::SELL, MIN_BID_NEARST_TICK, volume);
}
}
void AutoTrader::OrderStatusMessageHandler(unsigned long clientOrderId,
unsigned long fillVolume,
unsigned long remainingVolume,
signed long fees)
{
if (remainingVolume == 0)
{
if (clientOrderId == mAskId)
{
mAskId = 0;
}
else if (clientOrderId == mBidId)
{
mBidId = 0;
}
mAsks.erase(clientOrderId);
mBids.erase(clientOrderId);
}
}
void AutoTrader::TradeTicksMessageHandler(Instrument instrument,
unsigned long sequenceNumber,
const std::array<unsigned long, TOP_LEVEL_COUNT>& askPrices,
const std::array<unsigned long, TOP_LEVEL_COUNT>& askVolumes,
const std::array<unsigned long, TOP_LEVEL_COUNT>& bidPrices,
const std::array<unsigned long, TOP_LEVEL_COUNT>& bidVolumes)
{
RLOG(LG_AT, LogLevel::LL_INFO) << "trade ticks received for " << instrument << " instrument"
<< ": ask prices: " << askPrices[0]
<< "; ask volumes: " << askVolumes[0]
<< "; bid prices: " << bidPrices[0]
<< "; bid volumes: " << bidVolumes[0];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment