Skip to content

Commit

Permalink
Merge pull request #682 from ExchangeUnion/persist-maker-trades
Browse files Browse the repository at this point in the history
Persist trades from maker order swaps
  • Loading branch information
sangaman committed Dec 27, 2018
2 parents 82b4226 + 36f3c0d commit 5a539f5
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 115 deletions.
11 changes: 8 additions & 3 deletions lib/db/models/Trade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { db } from '../../types';

export default (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.TradeAttributes> = {
makerOrderId: { type: DataTypes.STRING, primaryKey: true },
takerOrderId: { type: DataTypes.STRING, primaryKey: true },
makerOrderId: { type: DataTypes.STRING, allowNull: false },
takerOrderId: { type: DataTypes.STRING, allowNull: true },
rHash: { type: DataTypes.STRING, allowNull: true },
quantity: { type: DataTypes.DECIMAL(8), allowNull: false },
};

Expand All @@ -25,7 +26,11 @@ export default (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes)
models.Trade.belongsTo(models.Order, {
as: 'takerOrder',
foreignKey: 'takerOrderId',
constraints: true,
constraints: false,
});
models.Trade.belongsTo(models.SwapDeal, {
foreignKey: 'rHash',
constraints: false,
});
};

Expand Down
18 changes: 12 additions & 6 deletions lib/orderbook/OrderBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Models } from '../db/DB';
import Swaps from '../swaps/Swaps';
import { SwapRole, SwapFailureReason, SwapPhase } from '../types/enums';
import { CurrencyInstance, PairInstance, CurrencyFactory } from '../types/db';
import { Pair, OrderIdentifier, OwnOrder, OrderPortion, OwnLimitOrder, PeerOrder } from '../types/orders';
import { Pair, OrderIdentifier, OwnOrder, OrderPortion, OwnLimitOrder, PeerOrder, Order } from '../types/orders';
import { PlaceOrderEvent, PlaceOrderEventCase, PlaceOrderResult } from '../types/orderBook';
import { SwapRequestPacket, SwapFailedPacket } from '../p2p/packets';
import { SwapResult, SwapDeal } from 'lib/swaps/types';
Expand Down Expand Up @@ -96,7 +96,7 @@ class OrderBook extends EventEmitter {

private bindSwaps = () => {
if (this.swaps) {
this.swaps.on('swap.paid', (swapResult) => {
this.swaps.on('swap.paid', async (swapResult) => {
if (swapResult.role === SwapRole.Maker) {
const { orderId, pairId, quantity, peerPubKey } = swapResult;

Expand All @@ -105,6 +105,7 @@ class OrderBook extends EventEmitter {

this.removeOwnOrder(orderId, pairId, quantity, peerPubKey);
this.emit('ownOrder.swapped', { pairId, quantity, id: orderId });
await this.persistTrade(swapResult.quantity, this.getOwnOrder(swapResult.orderId, swapResult.pairId), undefined, swapResult.rHash);
}
});
this.swaps.on('swap.failed', (deal) => {
Expand Down Expand Up @@ -375,7 +376,7 @@ class OrderBook extends EventEmitter {
try {
const swapResult = await this.swaps!.executeSwap(maker, taker);
this.emit('peerOrder.filled', maker);
await this.persistTrade(swapResult.quantity, maker, taker);
await this.persistTrade(swapResult.quantity, maker, taker, swapResult.rHash);
return swapResult;
} catch (err) {
this.emit('peerOrder.invalidation', maker);
Expand All @@ -401,12 +402,17 @@ class OrderBook extends EventEmitter {
return true;
}

private persistTrade = async (quantity: number, makerOrder: orders.PeerOrder | orders.OwnOrder, takerOrder: orders.OwnOrder | orders.PeerOrder) => {
await Promise.all([this.repository.addOrderIfNotExists(makerOrder), this.repository.addOrderIfNotExists(takerOrder)]);
private persistTrade = async (quantity: number, makerOrder: Order, takerOrder?: OwnOrder, rHash?: string) => {
const addOrderPromises = [this.repository.addOrderIfNotExists(makerOrder)];
if (takerOrder) {
addOrderPromises.push(this.repository.addOrderIfNotExists(takerOrder));
}
await Promise.all(addOrderPromises);
await this.repository.addTrade({
quantity,
rHash,
makerOrderId: makerOrder.id,
takerOrderId: takerOrder.id,
takerOrderId: takerOrder ? takerOrder.id : undefined,
});
}

Expand Down
11 changes: 7 additions & 4 deletions lib/orderbook/OrderBookRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,23 @@ class OrderbookRepository {
}

/**
* Only persist order if it doesn't exists.
* Adds an order to the database if it doesn't already exist.
* @param order order to persist
* @returns the created order instance, or undefined if it already existed
*/
public addOrderIfNotExists = async (order: db.OrderFactory) => {
const count = await this.models.Order.count({
where: { id: order.id },
});
if (count === 0) {
await this.models.Order.create(order);
return this.models.Order.create(order);
} else {
return undefined;
}
}

public addTrade = async (trade: db.TradeFactory) => {
await this.models.Trade.create(trade);
public addTrade = (trade: db.TradeFactory) => {
return this.models.Trade.create(trade);
}
}

Expand Down
6 changes: 4 additions & 2 deletions lib/types/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ export type OrderInstance = OrderAttributes & Sequelize.Instance<OrderAttributes
export type TradeFactory = {
/** The order id of the maker order involved in this trade. */
makerOrderId: string,
/** The order id of the taker order involved in this trade. */
takerOrderId: string,
/** The order id of the taker order involved in this trade, if applicable. */
takerOrderId?: string,
/** The rHash of the swap that filled this trade, if applicable. */
rHash?: string,
/** The quantity transacted in this trade. */
quantity: number,
};
Expand Down
31 changes: 1 addition & 30 deletions test/integration/OrderBook.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,12 @@ import OrderBookRepository from '../../lib/orderbook/OrderBookRepository';
import Logger, { Level } from '../../lib/Logger';
import { orders } from '../../lib/types';
import { SwapClients } from '../../lib/types/enums';
import { ms } from '../../lib/utils/utils';
import { createOwnOrder } from '../utils';

const PAIR_ID = 'LTC/BTC';
const currencies = PAIR_ID.split('/');
const loggers = Logger.createLoggers(Level.Warn);

const createOwnOrder = (price: number, quantity: number, isBuy: boolean, createdAt = ms()): orders.OwnOrder => ({
price,
quantity,
isBuy,
createdAt,
initialQuantity: quantity,
id: uuidv1(),
localId: uuidv1(),
pairId: PAIR_ID,
hold: 0,
});

const createPeerOrder = (
price: number,
quantity: number,
isBuy: boolean,
createdAt = ms(),
peerPubKey = '029a96c975d301c1c8787fcb4647b5be65a3b8d8a70153ff72e3eac73759e5e345',
): orders.PeerOrder => ({
quantity,
price,
isBuy,
createdAt,
peerPubKey,
initialQuantity: quantity,
id: uuidv1(),
pairId: PAIR_ID,
});

const initValues = async (db: DB) => {
const orderBookRepository = new OrderBookRepository(loggers.orderbook, db.models);

Expand Down
64 changes: 25 additions & 39 deletions test/unit/DB.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import chai, { expect } from 'chai';
import uuidv1 from 'uuid/v1';
import DB from '../../lib/db/DB';
import OrderBookRepository from '../../lib/orderbook/OrderBookRepository';
import Logger, { Level } from '../../lib/Logger';
import { SwapClients, SwapRole, SwapState, SwapPhase } from '../../lib/types/enums';
import { ms } from '../../lib/utils/utils';
import SwapRepository from '../../lib/swaps/SwapRepository';
import chaiAsPromised = require('chai-as-promised');
import { OwnOrder } from '../../lib/types/orders';
import { SwapDeal } from '../../lib/swaps/types';
import P2PRepository from '../../lib/p2p/P2PRepository';
import { createOwnOrder } from '../utils';
import { TradeFactory } from '../../lib/types/db';

chai.use(chaiAsPromised);

Expand All @@ -20,19 +19,9 @@ const price = 0.005;
const quantity = 0.1;
const peerPubKey = '03029c6a4d80c91da9e40529ec41c93b17cc9d7956b59c7d8334b0318d4a86aef8';
const rHash = '62c8bbef4587cff4286246e63044dc3e454b5693fb5ebd0171b7e58644bfafe2';
const orderId = uuidv1();

const order: OwnOrder = {
price,
quantity,
isBuy: true,
createdAt: ms(),
initialQuantity: quantity,
id: orderId,
localId: uuidv1(),
pairId: PAIR_ID,
hold: 0,
};
const order = createOwnOrder(price, quantity, true);
const orderId = order.id;

const deal: SwapDeal = {
quantity,
Expand Down Expand Up @@ -112,9 +101,17 @@ describe('Database', () => {
await expect(db.models.Order.count()).to.eventually.equal(1);
});

it('should add a swap for the order', async () => {
it('should add a swap and a trade for the order', async () => {
await orderBookRepo.addOrderIfNotExists(order);
const { rHash } = deal;
const trade: TradeFactory = { rHash, quantity: deal.quantity!, makerOrderId: order.id };
await orderBookRepo.addTrade(trade);
await swapRepo.addSwapDeal(deal);
await expect(db.models.SwapDeal.count()).to.eventually.equal(1);

const swapInstance = await db.models.SwapDeal.findOne({ where: { rHash } });
expect(swapInstance!.orderId).to.equal(order.id);
const tradeInstance = await db.models.Trade.findOne({ where: { rHash } });
expect(tradeInstance!.makerOrderId).to.equal(order.id);
});

it('should get a swap along with the order for the swap', async () => {
Expand All @@ -132,28 +129,8 @@ describe('Database', () => {
});

it('should add market orders and have their price in db be null', async () => {
const buyMarketOrder: OwnOrder = {
quantity,
price: Number.MAX_VALUE,
isBuy: true,
createdAt: ms(),
initialQuantity: quantity,
id: uuidv1(),
localId: uuidv1(),
pairId: PAIR_ID,
hold: 0,
};
const sellMarketOrder: OwnOrder = {
quantity,
price: 0,
isBuy: true,
createdAt: ms(),
initialQuantity: quantity,
id: uuidv1(),
localId: uuidv1(),
pairId: PAIR_ID,
hold: 0,
};
const buyMarketOrder = createOwnOrder(Number.MAX_VALUE, quantity, true);
const sellMarketOrder = createOwnOrder(0, quantity, true);
await orderBookRepo.addOrderIfNotExists(buyMarketOrder);
await orderBookRepo.addOrderIfNotExists(sellMarketOrder);
const buyOrder = (await db.models.Order.findById(buyMarketOrder.id))!;
Expand All @@ -164,6 +141,15 @@ describe('Database', () => {
expect(sellOrder.price).to.be.null;
});

it('should add two own orders and a trade between them', async () => {
const tradeQuantity = 0.1;
const maker = createOwnOrder(price, tradeQuantity, true);
const taker = createOwnOrder(price, tradeQuantity, false);
await Promise.all([orderBookRepo.addOrderIfNotExists(maker), orderBookRepo.addOrderIfNotExists(taker)]);
const trade: TradeFactory = { quantity: tradeQuantity, makerOrderId: maker.id, takerOrderId: taker.id };
await orderBookRepo.addTrade(trade);
});

after(async () => {
await db.close();
});
Expand Down
32 changes: 1 addition & 31 deletions test/unit/TradingPair.spec.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,13 @@
import { expect } from 'chai';
import uuidv1 from 'uuid/v1';
import Logger, { Level } from '../../lib/Logger';
import TradingPair from '../../lib/orderbook/TradingPair';
import { orders } from '../../lib/types';
import { OrderingDirection } from '../../lib/types/enums';
import { ms } from '../../lib/utils/utils';
import { createOwnOrder, createPeerOrder } from '../utils';

const PAIR_ID = 'LTC/BTC';
const loggers = Logger.createLoggers(Level.Warn);

const createOwnOrder = (price: number, quantity: number, isBuy: boolean, createdAt = ms()): orders.OwnOrder => ({
price,
quantity,
isBuy,
createdAt,
initialQuantity: quantity,
id: uuidv1(),
localId: uuidv1(),
pairId: PAIR_ID,
hold: 0,
});

const createPeerOrder = (
price: number,
quantity: number,
isBuy: boolean,
createdAt = ms(),
peerPubKey = '029a96c975d301c1c8787fcb4647b5be65a3b8d8a70153ff72e3eac73759e5e345',
): orders.PeerOrder => ({
quantity,
price,
isBuy,
createdAt,
peerPubKey,
initialQuantity: quantity,
id: uuidv1(),
pairId: PAIR_ID,
});

let tp: TradingPair;

const isEmpty = (tp: TradingPair) => {
Expand Down
33 changes: 33 additions & 0 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import net from 'net';
import { SinonSpy } from 'sinon';
import { ms } from '../lib/utils/utils';
import { PeerOrder, OwnOrder } from '../lib/types/orders';
import uuidv1 from 'uuid';

/**
* Discovers and returns a dynamically assigned, unused port available for testing.
Expand Down Expand Up @@ -37,3 +40,33 @@ export const waitForSpy = (spy: SinonSpy, property = 'called') => {
}
});
};

export const createOwnOrder = (price: number, quantity: number, isBuy: boolean, createdAt = ms(), pairId = 'LTC/BTC'): OwnOrder => ({
price,
quantity,
isBuy,
pairId,
createdAt,
initialQuantity: quantity,
id: uuidv1(),
localId: uuidv1(),
hold: 0,
});

export const createPeerOrder = (
price: number,
quantity: number,
isBuy: boolean,
createdAt = ms(),
peerPubKey = '029a96c975d301c1c8787fcb4647b5be65a3b8d8a70153ff72e3eac73759e5e345',
pairId = 'LTC/BTC',
): PeerOrder => ({
quantity,
price,
isBuy,
pairId,
createdAt,
peerPubKey,
initialQuantity: quantity,
id: uuidv1(),
});

0 comments on commit 5a539f5

Please sign in to comment.