Skip to content

Commit

Permalink
Merge pull request #8 from nkaz001/partial_fill
Browse files Browse the repository at this point in the history
Partial fill
  • Loading branch information
nkaz001 authored Mar 31, 2023
2 parents 9e4265c + 5be4b2f commit 900b426
Show file tree
Hide file tree
Showing 9 changed files with 631 additions and 69 deletions.
19 changes: 13 additions & 6 deletions hftbacktest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
from .reader import COL_EVENT, COL_EXCH_TIMESTAMP, COL_LOCAL_TIMESTAMP, COL_SIDE, COL_PRICE, COL_QTY, \
DEPTH_EVENT, DEPTH_CLEAR_EVENT, DEPTH_SNAPSHOT_EVENT, TRADE_EVENT, DataReader, Cache
from .order import BUY, SELL, NONE, NEW, EXPIRED, FILLED, CANCELED, GTC, GTX, Order, OrderBus
from .backtest import SingleInstHftBacktest
from .backtest import SingleAssetHftBacktest
from .data import validate_data, correct_local_timestamp, correct_exch_timestamp, correct
from .proc.local import Local
from .proc.exchange import NoPartialFillExch
from .proc.nopartialfillexchange import NoPartialFillExchange
from .proc.partialfillexchange import PartialFillExchange
from .marketdepth import MarketDepth
from .state import State
from .models.latencies import FeedLatency, ConstantLatency, ForwardFeedLatency, BackwardFeedLatency, IntpOrderLatency
Expand All @@ -21,13 +22,14 @@
'NONE', 'NEW', 'EXPIRED', 'FILLED', 'CANCELED',
'GTC', 'GTX',
'Order', 'HftBacktest',
'NoPartialFillExchange', 'PartialFillExchange',
'ConstantLatency', 'FeedLatency', 'ForwardFeedLatency', 'BackwardFeedLatency', 'IntpOrderLatency',
'Linear', 'Inverse',
'RiskAverseQueueModel', 'LogProbQueueModel', 'IdentityProbQueueModel', 'SquareProbQueueModel',
'Stat',
'validate_data', 'correct_local_timestamp', 'correct_exch_timestamp', 'correct',)

__version__ = '1.3.1'
__version__ = '1.4.0'


def HftBacktest(
Expand All @@ -43,7 +45,8 @@ def HftBacktest(
start_position=0,
start_balance=0,
start_fee=0,
trade_list_size=0
trade_list_size=0,
exchange_model=None
):
cache = Cache()

Expand Down Expand Up @@ -160,7 +163,11 @@ def HftBacktest(
order_latency,
trade_list_size
)
exch = NoPartialFillExch(

if exchange_model is None:
exchange_model = NoPartialFillExchange

exch = exchange_model(
exch_reader,
exch_to_local_orders,
local_to_exch_orders,
Expand All @@ -170,4 +177,4 @@ def HftBacktest(
queue_model
)

return SingleInstHftBacktest(local, exch)
return SingleAssetHftBacktest(local, exch)
29 changes: 17 additions & 12 deletions hftbacktest/assettype.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
from numba import int64
from numba.experimental import jitclass


@jitclass
class _Linear:
def __init__(self):
pass
class LinearAsset:
contract_size: int64

def __init__(self, contract_size=1):
self.contract_size = contract_size

def amount(self, exec_price, qty):
return exec_price * qty
return self.contract_size * exec_price * qty

def equity(self, price, balance, position, fee):
return balance + position * price - fee
return balance + self.contract_size * position * price - fee


@jitclass
class _Inverse:
def __init__(self):
pass
class InverseAsset:
contract_size: int64

def __init__(self, contract_size=1):
self.contract_size = contract_size

def amount(self, exec_price, qty):
return qty / exec_price
return self.contract_size * qty / exec_price

def equity(self, price, balance, position, fee):
return -balance - position / price - fee
return -balance - self.contract_size * position / price - fee


Linear = _Linear()
Inverse = _Inverse()
Linear = LinearAsset()
Inverse = InverseAsset()
16 changes: 9 additions & 7 deletions hftbacktest/backtest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from numba import int64, boolean, typeof
from numba.experimental import jitclass

from . import BUY
from .order import LIMIT, SELL
from .reader import WAIT_ORDER_RESPONSE_NONE, COL_LOCAL_TIMESTAMP, UNTIL_END_OF_DATA


class SingleInstHftBacktest_:
class SingleAssetHftBacktest_:
def __init__(self, local, exch):
self.local = local
self.exch = exch
Expand Down Expand Up @@ -109,15 +111,15 @@ def last_trades(self):
def local_timestamp(self):
return self.current_timestamp

def submit_buy_order(self, order_id, price, qty, time_in_force, wait=False):
self.local.submit_buy_order(order_id, price, qty, time_in_force, self.current_timestamp)
def submit_buy_order(self, order_id, price, qty, time_in_force, order_type=LIMIT, wait=False):
self.local.submit_order(order_id, BUY, price, qty, order_type, time_in_force, self.current_timestamp)

if wait:
return self.goto(UNTIL_END_OF_DATA, wait_order_response=order_id)
return True

def submit_sell_order(self, order_id, price, qty, time_in_force, wait=False):
self.local.submit_sell_order(order_id, price, qty, time_in_force, self.current_timestamp)
def submit_sell_order(self, order_id, price, qty, time_in_force, order_type=LIMIT, wait=False):
self.local.submit_order(order_id, SELL, price, qty, order_type, time_in_force, self.current_timestamp)

if wait:
return self.goto(UNTIL_END_OF_DATA, wait_order_response=order_id)
Expand Down Expand Up @@ -202,11 +204,11 @@ def goto(self, timestamp, wait_order_response=WAIT_ORDER_RESPONSE_NONE):
return True


def SingleInstHftBacktest(local, exch):
def SingleAssetHftBacktest(local, exch):
jitted = jitclass(spec=[
('run', boolean),
('current_timestamp', int64),
('local', typeof(local)),
('exch', typeof(exch)),
])(SingleInstHftBacktest_)
])(SingleAssetHftBacktest_)
return jitted(local, exch)
33 changes: 27 additions & 6 deletions hftbacktest/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,21 @@
EXPIRED = 2
FILLED = 3
CANCELED = 4
PARTIALLY_FILLED = 5

GTC = 0 # Good 'till cancel
GTX = 1 # Post only
FOK = 2 # Fill or kill
IOC = 3 # Immediate or cancel

LIMIT = 0
MARKET = 1


@jitclass
class Order:
qty: float64
leaves_qty: float64
price_tick: int64
tick_size: float64
side: int8
Expand All @@ -30,9 +37,11 @@ class Order:
local_timestamp: int64
req: int8
exec_price_tick: int64
exec_qty: float64
order_id: int64
q: float64[:]
limit: boolean
maker: boolean
order_type: int8

def __init__(
self,
Expand All @@ -41,9 +50,11 @@ def __init__(
tick_size,
qty,
side,
time_in_force
time_in_force,
order_type
):
self.qty = qty
self.leaves_qty = qty
self.price_tick = price_tick
self.tick_size = tick_size
self.side = side
Expand All @@ -53,9 +64,16 @@ def __init__(
self.local_timestamp = 0
self.req = NONE
self.exec_price_tick = 0
self.exec_qty = 0.0
self.order_id = order_id
self.q = np.zeros(2, float64)
self.limit = False
self.maker = False
self.order_type = order_type

@property
def limit(self):
# compatibility <= 1.3
return self.maker

@property
def price(self):
Expand All @@ -76,16 +94,19 @@ def copy(self):
self.tick_size,
self.qty,
self.side,
self.time_in_force
self.time_in_force,
self.order_type
)
order.leaves_qty = self.leaves_qty
order.exch_timestamp = self.exch_timestamp
order.status = self.status
order.local_timestamp = self.local_timestamp
order.req = self.req
order.exec_price_tick = self.exec_price_tick
order.exec_qty = self.exec_qty
order.order_id = self.order_id
order.q = self.q
order.limit = self.limit
order.q[:] = self.q[:]
order.maker = self.maker
return order


Expand Down
15 changes: 3 additions & 12 deletions hftbacktest/proc/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from numba.experimental import jitclass

from .proc import Proc, proc_spec
from ..order import BUY, SELL, NEW, CANCELED, FILLED, EXPIRED, NONE, Order
from ..order import BUY, SELL, NEW, CANCELED, FILLED, EXPIRED, NONE, Order, LIMIT
from ..reader import COL_EVENT, COL_LOCAL_TIMESTAMP, COL_SIDE, COL_PRICE, COL_QTY, DEPTH_CLEAR_EVENT, DEPTH_EVENT, \
DEPTH_SNAPSHOT_EVENT, TRADE_EVENT, USER_DEFINED_EVENT

Expand Down Expand Up @@ -78,18 +78,9 @@ def _process_data(self, row):
self.user_data[i] = row[:]
return 0

def submit_buy_order(self, order_id, price, qty, time_in_force, current_timestamp):
def submit_order(self, order_id, side, price, qty, order_type, time_in_force, current_timestamp):
price_tick = round(price / self.depth.tick_size)
order = Order(order_id, price_tick, self.depth.tick_size, qty, BUY, time_in_force)
order.req = NEW
exch_recv_timestamp = current_timestamp + self.order_latency.entry(current_timestamp, order, self)

self.orders[order.order_id] = order
self.orders_to.append(order.copy(), exch_recv_timestamp)

def submit_sell_order(self, order_id, price, qty, time_in_force, current_timestamp):
price_tick = round(price / self.depth.tick_size)
order = Order(order_id, price_tick, self.depth.tick_size, qty, SELL, time_in_force)
order = Order(order_id, price_tick, self.depth.tick_size, qty, side, time_in_force, order_type)
order.req = NEW
exch_recv_timestamp = current_timestamp + self.order_latency.entry(current_timestamp, order, self)

Expand Down
Loading

0 comments on commit 900b426

Please sign in to comment.