Skip to content

Commit

Permalink
Updated equity tracking, postions liquidation on completion and annua…
Browse files Browse the repository at this point in the history
…lized_return calculation.

Fix liquidating position end
  • Loading branch information
anthonyb8 authored Mar 1, 2025
2 parents 7856f85 + 1bbcd08 commit b54da60
Show file tree
Hide file tree
Showing 13 changed files with 36 additions and 82 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ __pycache__/
/output
/dist

# Except for specific files
!he_zc_ohlcv-1h.bin

# Django
/db.sqlite3
/static
Expand Down
20 changes: 10 additions & 10 deletions midastrader/core/adapters/performance/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ def update_trades(self, event: TradeEvent) -> None:
trade_data (Trade): Trade object containing trade details.
"""
self.trades[event.trade_id] = event.trade
self.logger.debug(
f"\nTrade Updated:\n{event.trade.pretty_print(" ")}\n"
)
trade_str = event.trade.pretty_print(" ")
self.logger.debug(f"\nTrade Updated:\n{trade_str}\n")

def update_trade_commission(self, event: TradeCommissionEvent) -> None:
"""
Expand All @@ -65,9 +64,8 @@ def update_trade_commission(self, event: TradeCommissionEvent) -> None:
if event.trade_id in self.trades:
self.trades[event.trade_id].fees = event.commission
self.logger.debug(f"Commission Updated : {event.trade_id}")
self.logger.debug(
f"\nTrade Updated:\n{self.trades[event.trade_id].pretty_print(" ")}"
)
trade_str = self.trades[event.trade_id].pretty_print(" ")
self.logger.debug(f"\nTrade Updated:\n{trade_str}")
else:
self.logger.warning(
f"Trade ID {event.trade_id} not found for commission update."
Expand All @@ -82,7 +80,8 @@ def _output_trades(self) -> str:
"""
string = ""
for trade in self.trades.values():
string += f"{trade.pretty_print(" ")}\n"
trade_str = trade.pretty_print(" ")
string += f"{trade_str}\n"
return string

def _aggregate_trades(self) -> pd.DataFrame:
Expand Down Expand Up @@ -465,7 +464,7 @@ def update_equity(self, equity_details: EquityDetails) -> None:
Args:
equity_details (EquityDetails): The equity details to be logged.
"""
if equity_details not in self.equity_value:
if not self.equity_value or equity_details != self.equity_value[-1]:
self.equity_value.append(equity_details)
self.logger.debug(
f"\nEQUITY UPDATED: \n {self.equity_value[-1]}\n"
Expand Down Expand Up @@ -617,14 +616,15 @@ def calculate_equity_statistics(
raw_equity_curve = raw_equity_df["equity_value"].to_numpy()
daily_returns = self.daily_stats["period_return"].to_numpy()
period_returns = self.period_stats["period_return"].to_numpy()
annualized_return = round(((1 + daily_returns.mean()) ** 252) - 1, 4)

return {
"net_profit": float(Metrics.net_profit(raw_equity_curve)),
"beginning_equity": float(raw_equity_curve[0]),
"ending_equity": float(raw_equity_curve[-1]),
"total_return": float(Metrics.total_return(raw_equity_curve)),
"annualized_return": float(annualized_return),
"annualized_return": float(
Metrics.annualize_returns(daily_returns)
),
"daily_standard_deviation_percentage": float(
Metrics.standard_deviation(daily_returns)
),
Expand Down
11 changes: 6 additions & 5 deletions midastrader/core/adapters/portfolio/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ def _ouput_orders(self) -> str:
"""
string = ""
for permId, order in self.active_orders.items():
string += f"{permId}:\n{order.pretty_print(" ")} \n"
order_str = order.pretty_print(" ")
string += f"{permId}:\n{order_str} \n"
return string


Expand Down Expand Up @@ -181,7 +182,8 @@ def _output_positions(self) -> str:
"""
string = ""
for id, position in self.positions.items():
string += f"{id}:\n{position.pretty_print(" ")}\n"
position_str = position.pretty_print(" ")
string += f"{id}:\n{position_str}\n"
return string


Expand Down Expand Up @@ -235,9 +237,8 @@ def update_account_details(self, account_details: Account) -> None:
"""
self.account = account_details
self.logger.debug(
f"\nACCOUNT UPDATED: \n{self.account.pretty_print(" ")}"
)
account_str = self.account.pretty_print(" ")
self.logger.debug(f"\nACCOUNT UPDATED: \n{account_str}")

# Signal the orders have been updated atleast once
if not self.initial_data:
Expand Down
10 changes: 5 additions & 5 deletions midastrader/execution/adaptors/dummy/dummy_broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ def liquidate_positions(self) -> None:
current_price = mkt_data.pretty_price
position.market_price = current_price
position.calculate_liquidation_value()
quantity = position.quantity * -1

self.trade_id += 1
trade = Trade(
Expand All @@ -500,14 +501,13 @@ def liquidate_positions(self) -> None:
signal_id=self.last_trades[instrument_id].signal_id,
instrument=instrument_id,
security_type=symbol.security_type,
quantity=round(position.quantity * -1, 4),
quantity=round(quantity, 4),
avg_price=current_price * symbol.price_multiplier,
trade_value=round(
symbol.value(position.quantity, current_price), 2
),
trade_cost=symbol.cost(
position.quantity * -1, current_price
symbol.value(quantity, current_price),
2,
),
trade_cost=symbol.cost(quantity, current_price),
action=(
Action.SELL.value
if position.action == "BUY"
Expand Down
7 changes: 2 additions & 5 deletions midastrader/structs/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ def __post_init__(self):
if not isinstance(self.instrument, int):
raise TypeError("'instrument' field must be of type int.")
if not isinstance(self.order_type, OrderType):
raise TypeError(
"'order_type' field must be of type OrderType enum."
)
raise TypeError("'order_type' must be of type OrderType enum.")
if not isinstance(self.action, Action):
raise TypeError("'action' field must be of type Action enum.")
if not isinstance(self.signal_id, int):
Expand Down Expand Up @@ -120,8 +118,7 @@ def to_mbinary(self, ticker: str) -> mbinary.SignalInstructions:
ticker=ticker,
order_type=self.order_type.value,
action=self.action.value,
trade_id=self.signal_id,
leg_id=self.signal_id,
signal_id=self.signal_id,
weight=int(self.weight * PRICE_SCALE),
quantity=int(self.quantity * QUANTITY_SCALE),
limit_price=str(self.limit_price) if self.limit_price else "",
Expand Down
4 changes: 2 additions & 2 deletions midastrader/structs/trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def to_mbinary(self, ticker: str) -> mbinary.Trades:
"""
return mbinary.Trades(
trade_id=self.trade_id,
leg_id=self.signal_id,
signal_id=self.signal_id,
timestamp=self.timestamp,
ticker=ticker,
quantity=int(self.quantity * PRICE_SCALE),
Expand All @@ -136,7 +136,7 @@ def pretty_print(self, indent: str = "") -> str:
return (
f"{indent}Timestamp: {self.timestamp}\n"
f"{indent}Trade ID: {self.trade_id}\n"
f"{indent}Leg ID: {self.signal_id}\n"
f"{indent}Signal ID: {self.signal_id}\n"
f"{indent}Instrument: {self.instrument}\n"
f"{indent}Quantity: {self.quantity}\n"
f"{indent}Avg Price: {self.avg_price}\n"
Expand Down
Binary file added tests/integration/he_zc_ohlcv-1h.bin
Binary file not shown.
48 changes: 2 additions & 46 deletions tests/integration/strategy/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ output_path = "tests/integration/backtest/output/"

# Data Vendors
[vendor.historical]
url = "http://192.53.120.167:8080" #"http://127.0.0.1:8080"
url = "http://127.0.0.1:8080"
key = "your_database_key"
# data_file= "tests/integration/primer.bin"
# data_file="tests/integration/test_ohlcv-1h.bin"
data_file= "tests/integration/test_he.c.0_zc.c.0.bin"
data_file= "tests/integration/he_zc_ohlcv-1h.bin"

# [vendor.interactive_brokers]
# tick_interval = 5 # only matters for tick data
Expand Down Expand Up @@ -114,45 +112,3 @@ module = ""
class = ""



# # config.toml
# [general]
# # mode = "BACKTEST"
# session_id = 1001
# log_level = "INFO"
# log_output = "file"
# output_path = "tests/integration/backtest/output/"
# data_file= "tests/integration/he_zc_2024-09-01_2024-12-10_ohlcv-1h.bin"
#
# [database]
# url = "http://192.53.120.167:8080" #"http://127.0.0.1:8080"
# key = "your_database_key"
#
# [data_source]
# host="127.0.0.1"
# port="7497" #7496 for real account
# account_id="U4976268"
# client_id=1
#
# [broker]
# host="127.0.0.1"
# port="7497" # 7496 for real account
# account_id="U4976268"
# client_id=0
#
# [strategy.logic]
# module = "tests/integration/strategy/logic.py"
# class = "Cointegrationzscore"
#
# [strategy.parameters]
# strategy_name = "Cointegrationzscore"
# capital = 1000000
# data_type = "BAR"
# tick_interval = 5 # only matters for tick data
# schema = "ohlcv-1h"
# start = "2024-09-01"
# end = "2024-12-10"
# test_start = ""
# test_end = "2024-12-11" #2024-05-04 00:00:00"
# missing_values_strategy = "drop"
# risk_free_rate = 0.04
2 changes: 1 addition & 1 deletion tests/unit/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ output_path = "output/"
[vendor.historical]
url = "http://127.0.0.1:8080"
key = "your_database_key"
data_file= "tests/unit/test_HE.c.0_ZC.c.0__ohlcv-1h_2024-09-01_2024-12-31.bin"
data_file= "tests/unit/he_zc_ohlcv-1h.bin"

[vendor.interactive_brokers]
host="127.0.0.1"
Expand Down
8 changes: 2 additions & 6 deletions tests/unit/data/historical/test_data_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,7 @@ def setUp(self) -> None:

# Dataclient instance
self.bus = MessageBus()
kwargs = {
"data_file": "tests/unit/test_HE.c.0_ZC.c.0__ohlcv-1h_2024-09-01_2024-12-31.bin"
}
kwargs = {"data_file": "tests/unit/he_zc_ohlcv-1h.bin"}
self.adaptor = HistoricalAdaptor(
self.symbols_map,
self.bus,
Expand All @@ -117,9 +115,7 @@ def test_get_data_file(self):
symbols=self.symbols,
)

kwargs = {
"data_file": "tests/unit/test_HE.c.0_ZC.c.0__ohlcv-1h_2024-09-01_2024-12-31.bin"
}
kwargs = {"data_file": "tests/unit/he_zc_ohlcv-1h.bin"}
adaptor = HistoricalAdaptor(self.symbols_map, self.bus, **kwargs)

# Test
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/execution/dummy/test_dummy_broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ def test_liquidate_positions(self):
(
current_price
* hogs_position.quantity
* -1
* hogs_position.quantity_multiplier
* hogs_position.price_multiplier
),
Expand All @@ -523,7 +524,7 @@ def test_liquidate_positions(self):
security_type=SecurityType.STOCK,
quantity=round(aapl_position.quantity * -1, 4),
avg_price=float(current_price * 1),
trade_value=round(current_price * aapl_position.quantity, 2),
trade_value=round(current_price * aapl_position.quantity * -1, 2),
trade_cost=round(current_price * abs(aapl_position.quantity), 2),
action=Action.SELL.value,
fees=0.0,
Expand Down
Binary file added tests/unit/he_zc_ohlcv-1h.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion tests/unit/structs/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def test_type_checks(self):
)

with self.assertRaisesRegex(
TypeError, "'order_type' field must be of type OrderType enum."
TypeError, "'order_type' must be of type OrderType enum."
):
SignalInstruction(
instrument=self.instrument,
Expand Down

0 comments on commit b54da60

Please sign in to comment.