Skip to content

Commit

Permalink
Merge pull request #1050 from tradingstrategy-ai/partialtp-close-trade-2
Browse files Browse the repository at this point in the history
Fix partial tp and visualization
  • Loading branch information
hieuh25 authored Sep 30, 2024
2 parents ff9a2ce + 685dd19 commit 9469cfa
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 27 deletions.
2 changes: 1 addition & 1 deletion tests/backtest/test_grid_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def test_perform_grid_search_single_thread(
# Getting on Github:
# Obtained: 0.011546587485546267
# Expected: 0.011682534679563261 ± 1.2e-08
assert row["CAGR"] == pytest.approx(0.011536997940393867, rel=0.01)
assert row["CAGR"] == pytest.approx(0.06771955893113946)
assert row["Positions"] == 2

render_grid_search_result_table(table)
Expand Down
13 changes: 7 additions & 6 deletions tests/units_tests/test_market_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ def test_partial_take_profit(
)
assert len(opening_trades) == 1

# TODO: currently we don't support setting partial take profit for
# pending / planned positions
# Execute the position open
trader = UnitTestTrader(state)
trader.set_perfectly_executed(opening_trades[0])
assert opening_trades[0].is_success()

# Set the two stage take profit for the position in preparation
position = position_manager.get_current_position_for_pair(pair)
total_quantity = position.get_quantity(planned=True)
Expand Down Expand Up @@ -297,12 +304,6 @@ def test_partial_take_profit(
trigger = t.triggers[0]
assert trigger.price == pytest.approx(2500.0)

# Execute the position open
trader = UnitTestTrader(state)
trader.set_perfectly_executed(opening_trades[0])
assert opening_trades[0].is_success()
assert position.get_quantity() == pytest.approx(total_quantity)

# We are not within take profit level yet
assert position.last_token_price == pytest.approx(1823.4539999999997)

Expand Down
10 changes: 5 additions & 5 deletions tradeexecutor/cli/loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,10 +637,10 @@ def extract_long_short_stats_from_state(self, state) -> StatisticsTable:
return long_short_metrics_latest

def check_position_triggers(
self,
ts: datetime.datetime,
state: State,
universe: TradingStrategyUniverse
self,
ts: datetime.datetime,
state: State,
universe: TradingStrategyUniverse
) -> List[TradeExecution]:
"""Run stop loss/take profit/market limit price checks.
Expand Down Expand Up @@ -810,7 +810,7 @@ def run_backtest_trigger_checks(self,
# This gives value_at_open statitics for each position that are needed
# to calculate weights later.
update_statistics(
datetime.datetime.utcnow(),
ts,
state.stats,
state.portfolio,
self.execution_context.mode,
Expand Down
15 changes: 6 additions & 9 deletions tradeexecutor/state/position.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ def get_redeemed(self) -> Decimal:

def get_available_trading_quantity(
self,
include_pending: bool = False,
include_pending_trades: bool = False,
) -> Decimal:
"""Get token quantity still availble for the trades in this strategy cycle.
Expand All @@ -627,15 +627,12 @@ def get_available_trading_quantity(
This gives you remaining token balance, even if there are some earlier
sell orders that have not been executed yet.
"""

if include_pending:
planned = sum([
t.get_position_quantity()
for t in self.trades.values()
if t.is_planned()
])

if self.is_pending() or include_pending_trades:
planned = self.get_pending_quantity()
else:
# exclude partial tp trades
# this will be checked when stoploss is triggered
# so we need to exclude pending trades (e.g. partial tp trades)
planned = sum([
t.get_position_quantity()
for t in self.trades.values()
Expand Down
2 changes: 1 addition & 1 deletion tradeexecutor/statistics/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def update_statistics(
logger.info("Serialising long_short_metrics_latest")

stats.long_short_metrics_latest = long_short_metrics_latest

new_stats = calculate_statistics(clock, portfolio, execution_mode)
stats.portfolio.append(new_stats.portfolio)
for position_id, position_stats in new_stats.positions.items():
Expand Down
9 changes: 4 additions & 5 deletions tradeexecutor/strategy/pandas_trader/position_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ def adjust_position(
pair=pair,
quantity=Decimal(quantity_delta),
reserve=None,
assumed_price=price_structure.price,
assumed_price=price,
trade_type=TradeType.rebalance,
reserve_currency=self.reserve_currency,
reserve_currency_price=reserve_price,
Expand Down Expand Up @@ -1000,7 +1000,7 @@ def close_spot_position(

pair = position.pair

quantity = quantity or position.get_available_trading_quantity(include_pending=pending)
quantity = quantity or position.get_available_trading_quantity(include_pending_trades=pending)
if quantity:
assert quantity > 0, "Closing spot, quantity must be positive"

Expand Down Expand Up @@ -1161,7 +1161,7 @@ def close_position(
assert position.is_open(), f"Tried to close already closed position {position}"


quantity_left = position.get_available_trading_quantity(include_pending=pending)
quantity_left = position.get_available_trading_quantity(include_pending_trades=pending)

if quantity_left == 0:
# We have already generated closing trades for this position earlier?
Expand Down Expand Up @@ -2218,8 +2218,7 @@ def prepare_take_profit_trades(

flags = {TradeFlag.partial_take_profit, TradeFlag.reduce, TradeFlag.triggered}

# FIXME: temp hack to get market limit order working, fix this
if close_flag and not position.is_pending():
if close_flag:
per_level_trades = self.close_position(
position,
flags=flags,
Expand Down

0 comments on commit 9469cfa

Please sign in to comment.