Skip to content

Latest commit

 

History

History
1187 lines (734 loc) · 14.5 KB

deck.md

File metadata and controls

1187 lines (734 loc) · 14.5 KB

[fit] trading bot hero

@danielepolencic


inline 150%


once upon a time... ✨


fill


🌍 money transfer


have you ever noticed?


fill


fill


fill


fill


fill


fill


fill


fill


eur/gbp 0.88

gbp/eur 1.1364


send £1000 to 👩


£1 ➡️ €1.13640


💁 €1136.40


send the money back


€1 ➡️ £0.88


€1136.40 * 0.88


£1000.032


😱 £1000.032 😱


surely it's just transferwise


fill


£1 ➡️ €1.13612


BUY €1136.12

price gbp/eur 1.13612


€1 ➡️ £0.8802


SELL €1136.12

price eur/gbp 0.8802


£1000.012


😱 £1000.012 😱


this doesn't make any sense


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


🏊 liquidity 🚿


💸 just pennies 💸


fit


fit


BUY @1.25

SELL @0.8032


1.25 * 0.8032


😱 __£100__4 😱


2 transactions per day

20 days per month


2 * 20 * £4 = £1601


time to build something


✨ 3 wishes ✨


1. 👂


👂 to price changes


1.13612 * 0.88 = 0.99978 ❌

1.13612 * 0.882 = 1.00205 ✅


👂 to price changes

👂 to order changes


created

modified

partially matched

matched in full


👂 to price changes

👂 to order changes

👂 to balance changes


deposit 💰

withdraw 💰

orders matched in full


🕚🕘🕖

2. time travel


fit


⏪ market conditions

debugging & 🔍


3. 🛂 bulletproof testing


fit


"As part of its normal process, the UTP distributed test data and certain third parties improperly propagated the data."

  • NASDAQ


oops


mvp 1️⃣


scalable architecture

🐰MQ

mysql

^ ts 1.8


fit


fit


fit


fit


😩

overengineered


😩

sync state

^ syncing state means that I couldn't reboot the server easily ^ hard to test rabbitMQ. proxy-require modules ^ no need for multiple workers ^ hard to manage deployments


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


mvp 2️⃣


fit fit fit


𝒻(state, message) -> state


const message = {
  type: 'PRICE_TICK',
  price: 1.3508
};

const state = {
  currentPrice: 1.3502
};

function update(state, message) {
  switch(message.type) {

  case 'PRICE_TICK':
    return {
      ...state,
      currentPrice: message.price
    };

  default:
    return state;
  }
}

const newState = update(state, message);

console.log(newState); // {currentPrice: 1.3508};

fit


redux

leveldb

docker

^ ts 1.9


😩

crashes


😩

redux actions & orchestration

^ 1. leveldb is a 🃏 ^ 2. crashes


Bad ❌

function update(state, message) {
  switch(message.type) {

  case 'PRICE_TICK':

    /* SIDE EFFECT! */
    updateOrderWithPrice(message.price);

    return {
      ...state,
      currentPrice: message.price
    };

  default:
    return state;
  }
}

Good ✅

function getPrice(dispatch) {
  dispatch({type: 'PRICE_TICK'});

  updateOrderWithPrice(message.price).then(order => {
    dispatch({type: 'ORDER_UPDATED', order});
  });
}

Good ✅

function(state, action) {
  switch(action.type) {

  case 'PRICE_TICK':
    return {
      ...state,
      currentPrice: message.price
    };

  case 'ORDER_UPDATED':
    return {
      ...state,
      openOrder: message.order
    };
  }
}

flow and state are separated


redux saga

redux thunk

mobx + rxjs


mvp 3️⃣


𝒻(state, msg) -> [state, cmd]


function(state, message) {
  switch(message.type) {

    case 'PRICE_TICK':
      return [
        {...state, currentPrice: message.price},
        updateOrderWithPrice(message.price)
      ];
  }
}

updateOrderWithPrice(message.price)

function updateOrderWithPrice(price) {
  return {
    commandType: 'UPDATE_ORDER',
    price
  };
}

console.log(updateOrderWithPrice(1.2)) // {
                                       //   price: 1.2,
                                       //   commandType: 'UPDATE_ORDER'
                                       // }

fit


fit


fit


fit


free monad

(interpreter pattern)

^ ts 2.0, 2.1, 2.2 ^ redux-like state ^ redis


AST

const fetch = {
  commandType: 'FETCH',
  url: '/any_url'
};

const log = {
  commandType: 'LOG',
  value: 'Hello World!'
};

interpreter

function interpreter(commands) {
  commands.forEach(command => {
    switch(command.commandType) {

    case 'FETCH':
      request(command.url);

    case 'LOG':
      console.log(command.value);
    }

  });
}

no side effects


fit


[ state,

updateOrderWithPrice(1.2) ]


fit


fit


fit


fit


fit


1. elm

2. redux-loop

3. redux-effects


state + logic 👭

easier testing

interpreter


😩

monads are hard


mvp 3️⃣.1️⃣


actors

^ ts 2.3, 2.4 ^ redux-like state ^ redis


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fit


fully decoupled effects and business logic

200 LoC


if a 🌲 falls in a forest

does it make a sound?


testing


1. data driven


updateOrder

updateOrder(order: Order, price: number): State

enum State {
  UPDATE,
  DO_NOTHING,
  CANCEL
}

fit


fit


fit


unit testing

it('should update the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

unit testing

it('should update the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

it('should cancel the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.22;
  expect(updateOrder(order, currentTickPrice)).toEqual(CANCEL);
});

unit testing

it('should update the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

it('should do nothing', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.12;
  expect(updateOrder(order, currentTickPrice)).toEqual(DO_NOTHING);
});

it('should cancel the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.22;
  expect(updateOrder(order, currentTickPrice)).toEqual(CANCEL);
});

it('should update the order');
it('should do nothing');
it('should cancel the order');

it('should update the order');
it('should do nothing');
it('should cancel the order');

it('should NOT update the order');
it('should NOT do nothing');
it('should NOT cancel the order');

it('should update the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

it('should do nothing', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.12;
  expect(updateOrder(order, currentTickPrice)).toEqual(DO_NOTHING);
});

it('should cancel the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.22;
  expect(updateOrder(order, currentTickPrice)).toEqual(CANCEL);
});

it('should NOT update the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

it('should NOT do nothing', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.12;
  expect(updateOrder(order, currentTickPrice)).toEqual(DO_NOTHING);
});

it('should NOT cancel the order', () => {
  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.22;
  expect(updateOrder(order, currentTickPrice)).toEqual(CANCEL);
});

duplication 👯

duplication 👯


unit testing

it('should update the order', () => {

  const order = {id: 1, price: 1.12, active: true};
  const currentTickPrice = 1.11;

  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

unit testing

it('should update the order', () => {
  const order = {id: 1, price: 1.12, active: true}; // CHANGE ME
  const currentTickPrice = 1.11; // CHANGE ME
  expect(updateOrder(order, currentTickPrice)).toEqual(UPDATE);
});

110%


unit testing

test(updateOrder, () => {
  given({id: 1, price: 1.11, active: true}, 1.12).expect(UPDATE);
  given({id: 1, price: 1.12, active: true}, 1.12).expect(DO_NOTHING);
  given({id: 1, price: 1.12, active: true}, 1.22).expect(CANCEL);
});

unit testing

test(updateOrder, () => {
  given({id: 1, price: 1.11, active: true}, 1.12).expect(UPDATE);
  given({id: 1, price: 1.12, active: true}, 1.12).expect(DO_NOTHING);
  given({id: 1, price: 1.12, active: true}, 1.22).expect(CANCEL);

  given({id: 1, price: 1.11, active: false}, 1.12).expect(DO_NOTHING);
  given({id: 1, price: 1.12, active: false}, 1.12).expect(DO_NOTHING);
  given({id: 1, price: 1.12, active: false}, 1.22).expect(DO_NOTHING);
});

2. sound 🎵


orders

const order = {
  id: 1,
  price: 1.12
};

orders with state

const order = {
  id: 1,
  price: 1.12,
  active: true // new field
};

old tests still pass

/(search|replace)/gi

unpredictable


typescript


orders

interace IOrder = {
  id: number
  price: number
};

const order: IOrder = {
  id: 1,
  price: 1.12
};

orders with state

interace IOrder = {
  id: number
  price: number
  active: boolean
};

const order: IOrder = { // ERROR! `active` is missing
  id: 1,
  price: 1.12
};

3. integration


110%


fit


fit


very hard to setup

harder to maintain

time consuming


but…


𝒻(state, msg) -> [state, cmd]

+

snapshot testing


const messages = [
  {type: 'TICK', price: 1.12},
  {type: 'TICK', price: 1.13},
  {type: 'SUBMITTED', price: 1.13},
  {type: 'COMPLETED', id: 1},
];

const initialState = {
  openOrders: []
};

const finalState = messages.reduce((state, message) => {
  return Update(state, message);
});

expect(finalState).toMatchSnapshot();

fit


show me the 💵!


fit


fit


fit


fit


2️⃣✖️ initial investment


957 transactions,

3 motnhs later…


fit


lessons learned


1️⃣ js is awesome


proper type system

transpilers

functional, CQRS, etc.


2️⃣ trading is coding


not only python/r/c++

no MBA/Phd

no special HW or SW



3️⃣ build once, run everywhere


bitcoin

stock

forex


hungry for more?


[fit] thanks

@danielepolencic

Footnotes

  1. best case scenario