Skip to content

Commit

Permalink
upd
Browse files Browse the repository at this point in the history
  • Loading branch information
m5l14i11 committed Sep 19, 2024
1 parent a975307 commit 1a9101a
Show file tree
Hide file tree
Showing 28 changed files with 131 additions and 178 deletions.
2 changes: 1 addition & 1 deletion core/commands/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from core.events.base import EventMeta
from core.models.broker import MarginMode, PositionMode
from core.models.position import Position
from core.models.entity.position import Position
from core.models.symbol import Symbol

from .base import Command, CommandGroup
Expand Down
2 changes: 1 addition & 1 deletion core/events/position.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field

from core.models.position import Position
from core.models.entity.position import Position

from .base import Event, EventGroup, EventMeta

Expand Down
2 changes: 1 addition & 1 deletion core/events/risk.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field

from core.models.position import Position
from core.models.entity.position import Position

from .base import Event, EventGroup, EventMeta

Expand Down
2 changes: 1 addition & 1 deletion core/events/signal.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field

from core.models.signal import Signal
from core.models.entity.signal import Signal

from .base import Event, EventGroup, EventMeta

Expand Down
2 changes: 1 addition & 1 deletion core/interfaces/abstract_exchange.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from abc import ABC, abstractmethod

from core.models.broker import MarginMode, PositionMode
from core.models.entity.position import PositionSide
from core.models.lookback import Lookback
from core.models.position import PositionSide
from core.models.symbol import Symbol
from core.models.timeframe import Timeframe

Expand Down
2 changes: 1 addition & 1 deletion core/interfaces/abstract_order_size_strategy.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from abc import abstractmethod

from core.interfaces.abstract_event_manager import AbstractEventManager
from core.models.signal import Signal
from core.models.entity.signal import Signal


class AbstractOrderSizeStrategy(AbstractEventManager):
Expand Down
4 changes: 2 additions & 2 deletions core/interfaces/abstract_position_factory.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from abc import ABC, abstractmethod

from core.models.position import Position
from core.models.entity.position import Position
from core.models.entity.signal import Signal
from core.models.risk_type import SignalRiskType
from core.models.signal import Signal
from core.models.ta import TechAnalysis


Expand Down
2 changes: 1 addition & 1 deletion core/interfaces/abstract_position_risk_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List, Tuple

from core.models.entity.ohlcv import OHLCV
from core.models.signal import SignalSide
from core.models.entity.signal import SignalSide


class AbstractPositionRiskStrategy(ABC):
Expand Down
17 changes: 12 additions & 5 deletions core/models/entity/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,18 @@ def from_list(cls, values: List[Any]) -> "Entity":
return cls.from_dict(data)

def __str__(self) -> str:
field_dict = {
key: value
for key, value in self.to_dict().items()
if not key.startswith("_")
}
def filter_private_fields(data):
if isinstance(data, dict):
return {
key: filter_private_fields(value)
for key, value in data.items()
if not key.startswith("_")
}
elif isinstance(data, list):
return [filter_private_fields(item) for item in data]
return data

field_dict = filter_private_fields(self.to_dict())

def format_value(value):
if isinstance(value, dict):
Expand Down
44 changes: 10 additions & 34 deletions core/models/position.py → core/models/entity/position.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import logging
import uuid
from dataclasses import dataclass, field, replace
from dataclasses import field, replace
from datetime import datetime
from typing import List, Optional, Tuple

import numpy as np

from .entity.ohlcv import OHLCV
from .entity.order import Order
from .order_type import OrderStatus
from core.models.order_type import OrderStatus
from core.models.risk_type import PositionRiskType, SessionRiskType, SignalRiskType
from core.models.side import PositionSide, SignalSide
from core.models.ta import TechAnalysis

from ._base import Entity
from .ohlcv import OHLCV
from .order import Order
from .position_risk import PositionRisk
from .profit_target import ProfitTarget
from .risk_type import PositionRiskType, SessionRiskType, SignalRiskType
from .side import PositionSide, SignalSide
from .signal import Signal
from .signal_risk import SignalRisk
from .ta import TechAnalysis

logger = logging.getLogger(__name__)

DEFAULT_TARGET_IDX = 2
LATENCY_GAP_THRESHOLD = 1.8


@dataclass(frozen=True)
@Entity
class Position:
initial_size: float
signal: Signal
Expand Down Expand Up @@ -436,29 +438,3 @@ def _average_size(orders: List[Order]) -> float:
def _average_price(orders: List[Order]) -> float:
total_price = sum(order.price for order in orders)
return total_price / len(orders) if orders else 0.0

def to_dict(self):
return {
"signal": self.signal.to_dict(),
"signal_risk": self.signal_risk.to_dict(),
"position_risk": self.position_risk.to_dict(),
"curr_target": self.curr_target,
"side": str(self.side),
"size": self.size,
"entry_price": self.entry_price,
"exit_price": self.exit_price,
"closed": self.closed,
"valid": self.is_valid,
"pnl": self.pnl,
"fee": self.fee,
"take_profit": self.take_profit,
"stop_loss": self.stop_loss,
"trade_time": self.trade_time,
"break_even": self.has_break_even,
}

def __str__(self):
return f"signal={self.signal}, signal_risk={self.signal_risk.type}, position_risk={self.position_risk.type}, open_ohlcv={self.signal_bar}, close_ohlcv={self.risk_bar}, side={self.side}, size={self.size}, entry_price={self.entry_price}, exit_price={self.exit_price}, tp={self.take_profit}, sl={self.stop_loss}, pnl={self.pnl}, trade_time={self.trade_time}, closed={self.closed}, valid={self.is_valid}, break_even={self.has_break_even}"

def __repr__(self):
return f"Position({self})"
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dataclasses import dataclass, field, replace
from dataclasses import field, replace
from typing import List, Tuple

import numpy as np
Expand All @@ -13,10 +13,12 @@
)
from sklearn.preprocessing import MinMaxScaler, StandardScaler

from .entity.ohlcv import OHLCV
from .risk_type import PositionRiskType
from .side import PositionSide
from .ta import TechAnalysis
from core.models.risk_type import PositionRiskType
from core.models.side import PositionSide
from core.models.ta import TechAnalysis

from ._base import Entity
from .ohlcv import OHLCV

TIME_THRESHOLD = 10000
LOOKBACK = 6
Expand Down Expand Up @@ -126,23 +128,31 @@ def _ats(closes: List[float], atr: List[float]) -> List[float]:
OHLCV_SIZE = 200


@dataclass(frozen=True)
@Entity
class PositionRisk(TaMixin):
model: SGDRegressor
scaler: StandardScaler
ohlcv: List[OHLCV] = field(default_factory=list)
_model: SGDRegressor
_scaler: StandardScaler
_ohlcv: List[OHLCV] = field(default_factory=list)
type: PositionRiskType = PositionRiskType.NONE
trail_factor: float = field(default_factory=lambda: np.random.uniform(1.1, 1.8))

@property
def curr_bar(self):
return self.ohlcv[-1]
return self._ohlcv[-1]

@property
def time_points(self):
return [o.timestamp for o in self._ohlcv]

@property
def session(self):
return self._ohlcv

def update_model(self):
if len(self.ohlcv) < 3:
if len(self._ohlcv) < 3:
return

last_ohlcv = self.ohlcv[-3:]
last_ohlcv = self._ohlcv[-3:]

close = [ohlcv.close for ohlcv in last_ohlcv]
high = [ohlcv.high for ohlcv in last_ohlcv]
Expand Down Expand Up @@ -184,23 +194,23 @@ def update_model(self):
)
target = np.array([close[-1]])

features_scaled = self.scaler.transform(features)
features_scaled = self._scaler.transform(features)

self.model.partial_fit(features_scaled, target)
self._model.partial_fit(features_scaled, target)

def forecast(self, steps: int = 3):
if len(self.ohlcv) < 1:
if len(self._ohlcv) < 1:
return []

self.update_model()

last_ohlcv = self.ohlcv[-1]
last_ohlcv = self._ohlcv[-1]

last_hlcc4 = (last_ohlcv.high + last_ohlcv.low + 2 * last_ohlcv.close) / 4.0
last_true_range = max(
last_ohlcv.high - last_ohlcv.low,
abs(last_ohlcv.high - self.ohlcv[-2].close),
abs(last_ohlcv.low - self.ohlcv[-2].close),
abs(last_ohlcv.high - self._ohlcv[-2].close),
abs(last_ohlcv.low - self._ohlcv[-2].close),
)

last_hlcc4_lagged_1 = last_hlcc4
Expand Down Expand Up @@ -229,9 +239,9 @@ def forecast(self, steps: int = 3):
]
)

X_scaled = self.scaler.transform(X)
X_scaled = self._scaler.transform(X)

forecast = self.model.predict(X_scaled)[0]
forecast = self._model.predict(X_scaled)[0]
predictions.append(forecast)

last_hlcc4_lagged_2 = last_hlcc4_lagged_1
Expand All @@ -242,16 +252,16 @@ def forecast(self, steps: int = 3):
last_true_range_lagged_1 = last_true_range
last_true_range = max(
forecast - last_ohlcv.close,
abs(forecast - self.ohlcv[-2].close),
abs(last_ohlcv.low - self.ohlcv[-2].close),
abs(forecast - self._ohlcv[-2].close),
abs(last_ohlcv.low - self._ohlcv[-2].close),
)

return predictions

def next(self, bar: OHLCV):
ohlcv = (self.ohlcv + [bar])[-OHLCV_SIZE:]
ohlcv = (self._ohlcv + [bar])[-OHLCV_SIZE:]
ohlcv.sort(key=lambda x: x.timestamp)
return replace(self, ohlcv=ohlcv)
return replace(self, _ohlcv=ohlcv)

def reset(self):
return replace(self, type=PositionRiskType.NONE)
Expand Down Expand Up @@ -300,7 +310,7 @@ def exit_price(self, side: PositionSide, tp: float, sl: float) -> "float":
return close

def sl_low(self, side: PositionSide, ta: TechAnalysis, sl: float) -> "float":
timestamps = np.array([candle.timestamp for candle in self.ohlcv])
timestamps = np.array(self.time_points)
ts_diff = np.diff(timestamps)

if ts_diff.sum() < TIME_THRESHOLD:
Expand Down Expand Up @@ -339,7 +349,7 @@ def sl_low(self, side: PositionSide, ta: TechAnalysis, sl: float) -> "float":
return sl

def tp_low(self, side: PositionSide, ta: TechAnalysis, tp: float) -> "float":
timestamps = np.array([candle.timestamp for candle in self.ohlcv])
timestamps = np.array(self.time_points)
ts_diff = np.diff(timestamps)

if ts_diff.sum() < TIME_THRESHOLD:
Expand Down Expand Up @@ -377,25 +387,27 @@ def tp_low(self, side: PositionSide, ta: TechAnalysis, tp: float) -> "float":
return tp

def sl_ats(self, side: PositionSide, ta: TechAnalysis, sl: float) -> "float":
timestamps = np.array([candle.timestamp for candle in self.ohlcv])
timestamps = np.array(self.time_points)
ts_diff = np.diff(timestamps)

if ts_diff.sum() < TIME_THRESHOLD:
return sl

max_lookback = max(len(timestamps), LOOKBACK)

close = np.array([candle.close for candle in self.ohlcv])
low = np.array([candle.low for candle in self.ohlcv])
high = np.array([candle.high for candle in self.ohlcv])
close = np.array([candle.close for candle in self._ohlcv])
low = np.array([candle.low for candle in self._ohlcv])
high = np.array([candle.high for candle in self._ohlcv])
volatility = np.array(ta.volatility.gkyz)[-max_lookback:]

min_length = min(len(close), len(volatility), len(high), len(low))

if min_length < 3:
return sl

anomaly = lambda series: (series - np.mean(series)) / np.std(series)
def anomaly(series):
return (series - np.mean(series)) / np.std(series)

close_anomaly = anomaly(close[-min_length:])

if abs(close_anomaly[-1]) > 2:
Expand Down Expand Up @@ -437,16 +449,3 @@ def sl_ats(self, side: PositionSide, ta: TechAnalysis, sl: float) -> "float":
return min(sl, min(atr_stop, adjusted_sl))

return sl

def to_dict(self):
return {
"type": self.type,
"trail_factor": self.trail_factor,
"ohlcv": self.curr_bar.to_dict(),
}

def __str__(self):
return f"type={self.type}, ohlcv={self.curr_bar}"

def __repr__(self):
return f"PositionRisk({self})"
Loading

0 comments on commit 1a9101a

Please sign in to comment.