Skip to content

Commit

Permalink
Add fills report (#1289)
Browse files Browse the repository at this point in the history
  • Loading branch information
r3k4mn14r authored Oct 20, 2023
1 parent bf30479 commit 8232c5e
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 0 deletions.
32 changes: 32 additions & 0 deletions nautilus_trader/analysis/reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from nautilus_trader.accounting.accounts.base import Account
from nautilus_trader.core.datetime import unix_nanos_to_dt
from nautilus_trader.model.events import AccountState
from nautilus_trader.model.events import OrderFilled
from nautilus_trader.model.orders import Order
from nautilus_trader.model.position import Position

Expand Down Expand Up @@ -80,6 +81,37 @@ def generate_order_fills_report(orders: list[Order]) -> pd.DataFrame:

return report

@staticmethod
def generate_fills_report(orders: list[Order]) -> pd.DataFrame:
"""
Generate a fills report.
Parameters
----------
orders : list[Order]
The orders for the report.
Returns
-------
pd.DataFrame
"""
if not orders:
return pd.DataFrame()

fills = [
OrderFilled.to_dict(e) for o in orders for e in o.events if isinstance(e, OrderFilled)
]
if not fills:
return pd.DataFrame()

report = pd.DataFrame(data=fills).set_index("client_order_id").sort_index()
report["ts_event"] = [unix_nanos_to_dt(ts_last or 0) for ts_last in report["ts_event"]]
report["ts_init"] = [unix_nanos_to_dt(ts_init) for ts_init in report["ts_init"]]
del report["type"]

return report

@staticmethod
def generate_positions_report(positions: list[Position]) -> pd.DataFrame:
"""
Expand Down
64 changes: 64 additions & 0 deletions tests/unit_tests/analysis/test_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from nautilus_trader.model.identifiers import AccountId
from nautilus_trader.model.identifiers import PositionId
from nautilus_trader.model.identifiers import StrategyId
from nautilus_trader.model.identifiers import TradeId
from nautilus_trader.model.identifiers import TraderId
from nautilus_trader.model.identifiers import Venue
from nautilus_trader.model.objects import AccountBalance
Expand Down Expand Up @@ -98,6 +99,13 @@ def test_generate_orders_fills_report_with_no_order_returns_emtpy_dataframe(self
# Assert
assert report.empty

def test_generate_fills_report_with_no_fills_returns_emtpy_dataframe(self):
# Arrange, Act
report = ReportProvider.generate_fills_report([])

# Assert
assert report.empty

def test_generate_positions_report_with_no_positions_returns_emtpy_dataframe(self):
# Arrange, Act
report = ReportProvider.generate_positions_report([])
Expand Down Expand Up @@ -229,6 +237,62 @@ def test_generate_order_fills_report(self):
assert report.iloc[1]["filled_qty"] == "500000"
assert report.iloc[1]["avg_px"] == "0.80011"

def test_generate_fills_report(self):
# Arrange
order1 = self.order_factory.limit(
AUDUSD_SIM.id,
OrderSide.BUY,
Quantity.from_int(1_500_000),
Price.from_str("0.80010"),
)

order1.apply(TestEventStubs.order_submitted(order1))
order1.apply(TestEventStubs.order_accepted(order1))

partially_filled1 = TestEventStubs.order_filled(
order1,
trade_id=TradeId("E-19700101-0000-000-001-1"),
instrument=AUDUSD_SIM,
position_id=PositionId("P-1"),
strategy_id=StrategyId("S-1"),
last_qty=Quantity.from_int(1_000_000),
last_px=Price.from_str("0.80011"),
)

partially_filled2 = TestEventStubs.order_filled(
order1,
trade_id=TradeId("E-19700101-0000-000-001-2"),
instrument=AUDUSD_SIM,
position_id=PositionId("P-1"),
strategy_id=StrategyId("S-1"),
last_qty=Quantity.from_int(500_000),
last_px=Price.from_str("0.80011"),
)

order1.apply(partially_filled1)
order1.apply(partially_filled2)

orders = [order1]

# Act
report = ReportProvider.generate_fills_report(orders)

# Assert
assert len(report) == 2
assert report.index.name == "client_order_id"
assert report.index[0] == order1.client_order_id.value
assert report.iloc[0]["instrument_id"] == "AUD/USD.SIM"
assert report.iloc[0]["order_side"] == "BUY"
assert report.iloc[0]["order_type"] == "LIMIT"
assert report.iloc[0]["last_qty"] == "1000000"
assert report.iloc[0]["last_px"] == "0.80011"
assert report.index[1] == order1.client_order_id.value
assert report.iloc[1]["instrument_id"] == "AUD/USD.SIM"
assert report.iloc[1]["order_side"] == "BUY"
assert report.iloc[1]["order_type"] == "LIMIT"
assert report.iloc[1]["last_qty"] == "500000"
assert report.iloc[1]["last_px"] == "0.80011"

def test_generate_positions_report(self):
# Arrange
order1 = self.order_factory.market(
Expand Down

0 comments on commit 8232c5e

Please sign in to comment.