Skip to content

Commit

Permalink
V1.1
Browse files Browse the repository at this point in the history
First working version.
  • Loading branch information
beastlybeast committed Nov 28, 2017
1 parent ae59778 commit 6f80876
Show file tree
Hide file tree
Showing 2 changed files with 291 additions and 0 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,23 @@
# SignificantTrades
A more useful version of the Recent Trades pane on Bitmex.

![screenshot](https://i.imgur.com/F7LpL8J.png)

## About
Bitmex's Recent Trades pane has a terrible signal-to-noise ratio because of bot activity and the fact that market orders are not batched by order but by matched price, usually resulting in many lines for a single 50k order.

This script uses Bitmex's public trades websocket and does a few things:

1. It collapses all orders with the same timestamp into one line.
2. It allows you to filter out orders less than a particular size.
3. It highlights orders that exceed a certain size.

The end result is a Recent Trades pane you can watch without getting a seizure, and which can be a useful tool to see when aggressive orders are coming in.

## How To Use
Just open the HTML file and type in the symbol name (e.g. XBTUSD) and minimum order threshold (e.g. 5000).

## Support Further Development
I'm not a developer, I'm a trader, and I'm paying contractors to build and improve this tool. If you find it useful, please consider sending a donation to support further development. Please also send your ideas to make this even more useful.

BTC (segwit) address: bc1q32ncgq5aaffz6l5vxrvfejfwdm9jhqdc3qvk5x
270 changes: 270 additions & 0 deletions SignificantTrades.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Bitmex - v1.1</title>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/[email protected]/babel.min.js"></script>
</head>
<body>
<div id="root"></div>

<style>
.streamingTable
{
background-color: rgba(0, 0, 0, 0);
border-collapse: collapse;
border-spacing: 0;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
}
.streamingTable .streamingListRow td
{
padding: 5px;
white-space: nowrap;
border-left: 1px solid #ddd;
text-align: right;
}
.streamingListRow
{
border-top: 1px solid #bbb;
}

.streamingTable .Buy
{
color: #4aa165;
}
.streamingTable tr.Dark.Buy
{
color: white;
background-color: #4aa165;
}
.streamingTable .Sell
{
color: #d16547;
}
.streamingTable tr.Dark.Sell
{
color: white;
background-color: #d16547;
}

.streamingListRow:nth-child(odd) {
background: rgba(195,195,195,0.25);
}
.streamingListRow:nth-child(even) {
background: #fff;;
}

.symbolInput
{
width: 120px;
margin-right: 5px;
}
.priceInput
{
width: 170px;
margin-right: 5px;
}
</style>

<script type="text/babel">

function formatAMPM(date) {
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getMinutes();
var ampm = hours >= 12 ? 'pm' : 'am';
hours = hours % 12;
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? '0'+minutes : minutes;
seconds = seconds < 10 ? '0'+seconds : seconds;
var strTime = hours + ':' + minutes + ':' + seconds + ' ' + ampm;
return strTime;
}

function average(arr, priceFilter) {
var sumSizeArr = {}, sumPriceArr= {}, results = [], timestamp, side;
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
var key = item.timestamp + item.side;
if (!(key in sumSizeArr)) {
sumSizeArr[key] = {};
sumSizeArr[key].sizeSum = item.size;
}
sumSizeArr[key].sizeSum += arr[i].size;
}

for (var i = 0; i < arr.length; i++) {
var item = arr[i];
var key = item.timestamp + item.side;
var sizeItem = sumSizeArr[key];
if (!(key in sumPriceArr)) {
sumPriceArr[key] = {};
sumPriceArr[key].symbol = item.symbol;
sumPriceArr[key].priceSum = item.price * item.size / sizeItem.sizeSum;
sumPriceArr[key].sizeSum = item.size;
sumPriceArr[key].timestamp = formatAMPM(new Date(item.timestamp));
sumPriceArr[key].side = item.side;
}
sumPriceArr[key].priceSum += item.price * item.size / sizeItem.sizeSum;
sumPriceArr[key].sizeSum += item.size;
}

for(key in sumPriceArr) {
var item = sumPriceArr[key];
if (item.sizeSum >= priceFilter)
{
results.push({
symbol: item.symbol,
price:item.priceSum.toFixed(2),
size: item.sizeSum,
timestamp: item.timestamp,
side: item.side
});
}
}
return results;
}

class TableRow extends React.Component {
render() {
const {data} = this.props;
const row = data.map((data, i) =>
<tr className={"streamingListRow " + (data.size > 100000 ? 'Dark ' : '') + (data.side == "Sell" ? 'Sell' : 'Buy')} key={i}>
<td width="90">{data.price}</td>
<td width="80">{data.size > 999 ? (data.size/1000).toFixed(0) + 'k' : data.size}</td>
<td width="100">{data.timestamp}</td>
</tr>
);
return (
<tbody>{row}</tbody>
);
}
}

class Table extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<table className="streamingTable">
<TableRow data={this.props.data} />
</table>
);
}
}

class ShowData extends React.Component {
constructor(props) {
super(props);
this.state = {data: []};
this.instance = this;
}

initWebSocketClient(symbol, price) {
if (symbol == null)
{
return null
}
var context = this.instance
var streamingData = null;
var priceFilter = isNaN(price) ? 0 : price;
if (context.socket != null)
{
context.socket.close();
context.socket = null;
}
context.socket = new WebSocket("wss://www.bitmex.com/realtime?subscribe=trade:" + symbol.toUpperCase());
context.socket.onmessage = function(event) {
let obj = JSON.parse(event.data);
if (obj.data == null)
{
return;
}
let data = average(obj.data, priceFilter);
if (streamingData == null)
{
streamingData =data;
}
else
{
if (streamingData.length > 100)
{
streamingData.splice(streamingData.length - 20);
}
streamingData = data.concat(streamingData);
}

context.setStreamData({data: streamingData});
};
}

setStreamData(data){
this.setState(data);
}

render() {
return (
<div>
<Table data={this.state.data}/>
</div>);
}
}

class SetSymbolView extends React.Component {
constructor() {
super();
this.onSymbol = this.onSymbol.bind(this);
this.onKeyPress = this.onKeyPress.bind(this);
}

onKeyPress(e) {
if (e.key === 'Enter') {
this.onSymbol();
}
}

onSymbol() {
this.dataComponent.initWebSocketClient(this.refs.symbol.value, parseInt(this.refs.minPrice.value));
}

render() {
return (
<div>
<input
ref="symbol"
type="text"
name="symbolBox"
className="symbolInput"
placeholder="Enter symbol here..."
onKeyPress={this.onKeyPress}
/>
<input
ref="minPrice"
type="number"
name="minPriceBox"
className="priceInput"
placeholder="Min order size threshold..."
min="0"
onKeyPress={this.onKeyPress}
/>
<button onClick={this.onSymbol}>Submit</button>
<p/>
<div>
<ShowData ref={instance => { this.dataComponent = instance; }}/>
</div>
</div>
);
}
}

ReactDOM.render(<SetSymbolView />, document.getElementById('root'));



</script>
</body>
</html>

0 comments on commit 6f80876

Please sign in to comment.