Skip to content

Commit

Permalink
upd
Browse files Browse the repository at this point in the history
  • Loading branch information
m5l14i11 committed May 1, 2024
1 parent b2cfffc commit af6855e
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 53 deletions.
120 changes: 91 additions & 29 deletions core/models/position.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,75 @@
class Position:
signal: Signal
side: PositionSide
size: float
entry_price: float
risk_strategy: AbstractPositionRiskStrategy
take_profit_strategy: AbstractPositionTakeProfitStrategy
orders: Tuple[Order] = ()
closed: bool = False
stop_loss_price: float = field(default_factory=lambda: 0.0000001)
take_profit_price: float = field(default_factory=lambda: 0.0000001)
fee: float = field(default_factory=lambda: 0)
open_timestamp: float = field(default_factory=lambda: 0)
closed_timestamp: float = field(default_factory=lambda: 0)
last_modified: float = field(default_factory=lambda: datetime.now().timestamp())
exit_price: float = field(default_factory=lambda: 0.0000001)

@property
def trade_time(self) -> int:
return abs(int(self.closed_timestamp - self.open_timestamp))

@property
def closed(self) -> bool:
closed_orders = [
order.size for order in self.orders if order.status == OrderStatus.CLOSED
]
closed_size = sum(closed_orders)

failed_orders = [
order for order in self.orders if order.status == OrderStatus.FAILED
]

pending_orders = [
order for order in self.orders if order.status == OrderStatus.PENDING
]

if not closed_orders:
return False

return closed_size >= self.filled_size or len(failed_orders) == len(
pending_orders
)

@property
def adj_count(self) -> int:
executed_orders = [
order for order in self.orders if order.status == OrderStatus.EXECUTED
]
return max(
0,
len(executed_orders) - 1,
)

@property
def pending_size(self) -> int:
pending_orders = [
order.size for order in self.orders if order.status == OrderStatus.PENDING
]

return sum(pending_orders)

@property
def pending_price(self) -> int:
pending_orders = [
order.price for order in self.orders if order.status == OrderStatus.PENDING
]

return sum(pending_orders) / len(pending_orders) if pending_orders else 0.0

@property
def filled_size(self) -> int:
executed_orders = [
order.size for order in self.orders if order.status == OrderStatus.EXECUTED
]

return sum(executed_orders)

@property
def pnl(self) -> float:
pnl = 0.0
Expand All @@ -44,7 +95,36 @@ def pnl(self) -> float:

factor = -1 if self.side == PositionSide.SHORT else 1

return factor * (self.exit_price - self.entry_price) * self.size
return factor * (self.exit_price - self.entry_price) * self.filled_size

@property
def fee(self) -> float:
executed_orders = [
order.fee for order in self.orders if order.status == OrderStatus.EXECUTED
]
open_fee = sum(executed_orders)

closed_orders = [
order.fee for order in self.orders if order.status == OrderStatus.CLOSED
]
closed_fee = sum(closed_orders)

return open_fee + closed_fee

@property
def entry_price(self) -> float:
executed_orders = [
order.price for order in self.orders if order.status == OrderStatus.EXECUTED
]
return sum(executed_orders) / len(executed_orders) if executed_orders else 0.0

@property
def exit_price(self) -> float:
closed_orders = [
order.price for order in self.orders if order.status == OrderStatus.CLOSED
]

return sum(closed_orders) / len(closed_orders) if closed_orders else 0.0

def add_order(self, order: Order) -> "Position":
if self.closed:
Expand All @@ -53,41 +133,22 @@ def add_order(self, order: Order) -> "Position":
last_modified = datetime.now().timestamp()
orders = (*self.orders, order)

if order.status == OrderStatus.PENDING:
return replace(self, orders=orders, last_modified=last_modified)

if order.status == OrderStatus.EXECUTED:
if order.status == OrderStatus.PENDING or order.status == OrderStatus.EXECUTED:
take_profit_price = self.take_profit_strategy.next(
self.side, order.price, self.stop_loss_price
)

return replace(
self,
orders=orders,
entry_price=order.price,
fee=order.fee,
size=order.size,
last_modified=last_modified,
take_profit_price=take_profit_price,
)

if order.status == OrderStatus.CLOSED:
return replace(
self,
closed=True,
orders=orders,
fee=self.fee + order.fee,
exit_price=order.price,
closed_timestamp=last_modified,
last_modified=last_modified,
)

if order.status == OrderStatus.FAILED:
if order.status == OrderStatus.CLOSED or order.status == OrderStatus.FAILED:
return replace(
self,
closed=True,
orders=orders,
exit_price=self.entry_price,
closed_timestamp=last_modified,
last_modified=last_modified,
)
Expand All @@ -111,7 +172,8 @@ def to_dict(self):
return {
"signal": self.signal.to_dict(),
"side": str(self.side),
"size": self.size,
"pending_size": self.pending_size,
"filled_size": self.filled_size,
"entry_price": self.entry_price,
"exit_price": self.exit_price,
"closed": self.closed,
Expand All @@ -123,4 +185,4 @@ def to_dict(self):
}

def __str__(self):
return f"Position(signal={self.signal}, side={self.side}, size={self.size}, entry_price={self.entry_price}, exit_price={self.exit_price}, take_profit_price={self.take_profit_price}, stop_loss_price={self.stop_loss_price}, trade_time={self.trade_time}, closed={self.closed})"
return f"Position(signal={self.signal}, side={self.side}, pending_size={self.pending_size}, filled_size={self.filled_size}, entry_price={self.entry_price}, exit_price={self.exit_price}, take_profit_price={self.take_profit_price}, stop_loss_price={self.stop_loss_price}, trade_time={self.trade_time}, closed={self.closed})"
28 changes: 16 additions & 12 deletions executor/_paper_order_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,10 @@ async def _execute_order(self, event: PositionInitialized):

logger.debug(f"New Position: {current_position}")

size = current_position.size
size = current_position.pending_size
side = current_position.side
fill_price = await self._determine_fill_price(
side, current_position.entry_price
)
price = current_position.pending_price
fill_price = await self._determine_fill_price(side, price)

if (
side == PositionSide.LONG and current_position.stop_loss_price > fill_price
Expand All @@ -82,8 +81,8 @@ async def _execute_order(self, event: PositionInitialized):
order = Order(
status=OrderStatus.FAILED,
type=OrderType.PAPER,
price=fill_price,
size=size,
price=0,
size=0,
)
else:
order = Order(
Expand All @@ -108,12 +107,17 @@ async def _adjust_position(self, event: RiskAdjustRequested):

logger.debug(f"To Adjust Position: {current_position}")

total_value = (current_position.size * current_position.entry_price) + (
current_position.size * event.adjust_price
total_value = (current_position.filled_size * current_position.entry_price) + (
current_position.filled_size * event.adjust_price
)

size = current_position.size + current_position.size
fill_price = total_value / size
size = round(
1.3 * current_position.filled_size,
current_position.signal.symbol.position_precision,
)
fill_price = round(
total_value / size, current_position.signal.symbol.price_precision
)

if (
current_position.side == PositionSide.LONG
Expand All @@ -135,7 +139,7 @@ async def _adjust_position(self, event: RiskAdjustRequested):

current_position = current_position.add_order(order)

logger.info(f"Adjusted Position: {current_position}")
logger.debug(f"Adjusted Position: {current_position}")

await self.tell(BrokerPositionAdjusted(current_position))

Expand All @@ -148,7 +152,7 @@ async def _close_position(self, event: PositionCloseRequested):
current_position.side, event.exit_price
)
price = self._calculate_closing_price(current_position, fill_price)
size = current_position.size
size = current_position.filled_size

order = Order(
status=OrderStatus.CLOSED,
Expand Down
17 changes: 11 additions & 6 deletions position/_position_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
AbstractPositionTakeProfitStrategy,
)
from core.models.ohlcv import OHLCV
from core.models.order import Order, OrderStatus
from core.models.position import Position
from core.models.side import PositionSide
from core.models.signal import Signal, SignalSide
Expand Down Expand Up @@ -36,20 +37,24 @@ async def create_position(
PositionSide.LONG if signal.side == SignalSide.BUY else PositionSide.SHORT
)

position_size = await self.position_size_strategy.calculate(
order_size = await self.position_size_strategy.calculate(
signal, entry_price, stop_loss_price
)

adjusted_position_size = max(position_size, symbol.min_position_size)
rounded_position_size = round(adjusted_position_size, symbol.position_precision)
adjusted_order_size = max(order_size, symbol.min_position_size)
rounded_order_size = round(adjusted_order_size, symbol.position_precision)

return Position(
order = Order(
status=OrderStatus.PENDING, price=entry_price, size=rounded_order_size
)

position = Position(
signal,
position_side,
rounded_position_size,
entry_price,
self.risk_strategy,
self.take_profit_strategy,
open_timestamp=ohlcv.timestamp,
stop_loss_price=stop_loss_price,
)

return position.add_order(order)
11 changes: 5 additions & 6 deletions sor/_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,14 @@ async def open_position(self, command: OpenPosition):
logger.info(f"Try to open position: {position}")

symbol = position.signal.symbol
position_size = position.size
position_size = position.pending_size
stop_loss = position.stop_loss_price
entry_price = position.pending_price

if self.exchange.fetch_position(symbol, position.side):
logging.info("Position already exists")
return

stop_loss = position.stop_loss_price
entry_price = position.entry_price

distance_to_stop_loss = abs(entry_price - stop_loss)

min_size = symbol.min_position_size
Expand Down Expand Up @@ -190,7 +189,7 @@ async def adjust_position(self, command: AdjustPosition):
logger.info(f"Try to adjust position: {position}")

symbol = position.signal.symbol
position_size = position.size
position_size = position.filled_size
stop_loss = position.stop_loss_price
entry_price = command.adjust_price

Expand Down Expand Up @@ -279,7 +278,7 @@ async def close_position(self, command: ClosePosition):
logging.info("Position is not existed")
return

position_size = position.size
position_size = position.filled_size
position_side = position.side
exit_price = command.exit_price

Expand Down

0 comments on commit af6855e

Please sign in to comment.