From 4cf3d55ab3581300d259c6809e8084c915696d81 Mon Sep 17 00:00:00 2001 From: Kyle Widmann Date: Sun, 12 Jan 2025 10:25:52 -0500 Subject: [PATCH 1/2] Updating to support arbitrary instrument name --- pytrade/broker.py | 4 ++-- pytrade/interfaces/broker.py | 4 ++-- pytrade/interfaces/client.py | 8 ++++---- pytrade/models/instruments.py | 20 ++++++++++--------- pytrade/models/order.py | 14 +++++++------- pytrade/strategy.py | 4 ++-- tests/unit/test_broker.py | 8 ++++---- tests/unit/test_indicator.py | 18 ++++++++--------- tests/unit/test_instrument_history.py | 6 +++--- tests/unit/test_strategy.py | 28 +++++++++++++-------------- 10 files changed, 58 insertions(+), 56 deletions(-) diff --git a/pytrade/broker.py b/pytrade/broker.py index 48dcda3..a8a4c21 100644 --- a/pytrade/broker.py +++ b/pytrade/broker.py @@ -2,7 +2,7 @@ from pytrade.interfaces.broker import IBroker from pytrade.interfaces.client import IClient -from pytrade.models.instruments import Candlestick, Granularity, Instrument +from pytrade.models.instruments import Candlestick, Granularity, FxInstrument from pytrade.models.order import OrderRequest @@ -35,7 +35,7 @@ def process_orders(self): def subscribe( self, - instrument: Instrument, + instrument: FxInstrument, granularity: Granularity, callback: Callable[[Candlestick], None], ): diff --git a/pytrade/interfaces/broker.py b/pytrade/interfaces/broker.py index c022716..d400a63 100644 --- a/pytrade/interfaces/broker.py +++ b/pytrade/interfaces/broker.py @@ -1,7 +1,7 @@ import abc from typing import Callable -from pytrade.models.instruments import Candlestick, Granularity, Instrument +from pytrade.models.instruments import Candlestick, Granularity, FxInstrument from pytrade.models.order import OrderRequest @@ -38,7 +38,7 @@ def order(self, order: OrderRequest): @abc.abstractmethod def subscribe( self, - instrument: Instrument, + instrument: FxInstrument, granularity: Granularity, callback: Callable[[Candlestick], None], ): diff --git a/pytrade/interfaces/client.py b/pytrade/interfaces/client.py index 0674e24..0d96061 100644 --- a/pytrade/interfaces/client.py +++ b/pytrade/interfaces/client.py @@ -3,7 +3,7 @@ from multimethod import multimethod -from pytrade.models.instruments import Candlestick, Granularity, Instrument +from pytrade.models.instruments import Candlestick, Granularity, FxInstrument from pytrade.models.order import MarketOrderRequest @@ -29,20 +29,20 @@ def order(self, order: MarketOrderRequest): @abc.abstractmethod def get_candles( - self, instrument: Instrument, granularity: Granularity, count: int + self, instrument: FxInstrument, granularity: Granularity, count: int ) -> list[Candlestick]: raise NotImplementedError() @abc.abstractmethod def get_candle( - self, instrument: Instrument, granularity: Granularity + self, instrument: FxInstrument, granularity: Granularity ) -> Candlestick: raise NotImplementedError() @abc.abstractmethod def subscribe( self, - instrument: Instrument, + instrument: FxInstrument, granularity: Granularity, callback: Callable[[Candlestick], None], ): diff --git a/pytrade/models/instruments.py b/pytrade/models/instruments.py index fdf06ab..417f5a0 100644 --- a/pytrade/models/instruments.py +++ b/pytrade/models/instruments.py @@ -16,6 +16,7 @@ class Granularity(Enum): M15 = "M15" H1 = "H1" H4 = "H4" + D1 = "D1" MINUTES_MAP = { @@ -24,10 +25,11 @@ class Granularity(Enum): Granularity.M15: 15, Granularity.H1: 60, Granularity.H4: 240, + Granularity.D1: 1440 } -class Instrument(Enum): +class FxInstrument(Enum): AUDJPY = "AUD/JPY" AUDNZD = "AUD/NZD" AUDUSD = "AUD/USD" @@ -50,12 +52,12 @@ class Instrument(Enum): USDZAR = "USD/ZAR" -instrument_lookup = {m.value: m for m in Instrument} +instrument_lookup = {m.value: m for m in FxInstrument} class CandleSubscription: - def __init__(self, instrument: Instrument, granularity: Granularity): + def __init__(self, instrument: FxInstrument, granularity: Granularity): self._instrument = instrument self._granularity = granularity @@ -64,7 +66,7 @@ def granularity(self) -> Granularity: return self._granularity @property - def instrument(self) -> Instrument: + def instrument(self) -> FxInstrument: return self._instrument def __hash__(self): @@ -96,7 +98,7 @@ class Candlestick: def __init__( self, - instrument: Instrument, + instrument: FxInstrument, granularity: Granularity, open: float, high: float, @@ -125,7 +127,7 @@ def to_dict(self): class TickData: - instrument: Instrument + instrument: FxInstrument timestamp: datetime bid: float ask: float @@ -161,7 +163,7 @@ def __init__( "Dataframe does not have a datetime index and does not have a 'Timestamp' column" ) self._max_size: Optional[int] = max_size - self.__instrument: Optional[Instrument] = None + self.__instrument: Optional[FxInstrument] = None self.__granularity: Optional[Granularity] = None self.__update_event = Event() @@ -216,7 +218,7 @@ def update(self, candlestick: Candlestick): class CandleData: def __init__(self, max_size=1000): - self._data: dict[tuple[Instrument, Granularity], InstrumentCandles] = {} + self._data: dict[tuple[FxInstrument, Granularity], InstrumentCandles] = {} self._max_size = max_size def __new__(cls, *args, **kwargs): @@ -225,7 +227,7 @@ def __new__(cls, *args, **kwargs): # Need to handle case where instantiatied and different max size is provided return cls.instance - def get(self, instrument: Instrument, granularity: Granularity): + def get(self, instrument: FxInstrument, granularity: Granularity): key = (instrument, granularity) instrument_candles: InstrumentCandles = self._data.get( (instrument, granularity), InstrumentCandles(max_size=self._max_size) diff --git a/pytrade/models/order.py b/pytrade/models/order.py index 5106f1c..0af0572 100644 --- a/pytrade/models/order.py +++ b/pytrade/models/order.py @@ -1,7 +1,7 @@ from enum import Enum from typing import Optional -from pytrade.models.instruments import Instrument +from pytrade.models.instruments import FxInstrument from pytrade.models.trade import Trade @@ -185,7 +185,7 @@ class OrderRequest(dict): def __init__( self, - instrument: Instrument, + instrument: FxInstrument, units: int, time_in_force: TimeInForce, take_profit_on_fill: Optional[float] = None, @@ -201,7 +201,7 @@ def __init__( # stop_loss_on_fill=stop_loss_on_fill, # trailing_stop_loss_on_fill=trailing_stop_loss_on_fill # ) - self._instrument: Instrument = instrument + self._instrument: FxInstrument = instrument self._units: int = units self._time_in_force: Optional[TimeInForce] = time_in_force self._take_profit_on_fill: Optional[float] = take_profit_on_fill @@ -209,7 +209,7 @@ def __init__( self._trailing_stop_loss_on_fill: Optional[float] = trailing_stop_loss_on_fill @property - def instrument(self) -> Instrument: + def instrument(self) -> FxInstrument: return self._instrument @property @@ -241,7 +241,7 @@ class MarketOrderRequest(OrderRequest): def __init__( self, - instrument: Instrument, + instrument: FxInstrument, units: int, time_in_force: TimeInForce = TimeInForce.FILL_OR_KILL, take_profit_on_fill: Optional[float] = None, @@ -272,7 +272,7 @@ class LimitOrderRequest(OrderRequest): def __init__( self, - instrument: Instrument, + instrument: FxInstrument, units: int, price: float, time_in_force: TimeInForce, @@ -295,7 +295,7 @@ class StopOrderRequest(OrderRequest): def __init__( self, - instrument: Instrument, + instrument: FxInstrument, units: int, price: float, time_in_force: TimeInForce, diff --git a/pytrade/strategy.py b/pytrade/strategy.py index 80787bf..c41d35a 100644 --- a/pytrade/strategy.py +++ b/pytrade/strategy.py @@ -8,7 +8,7 @@ Candlestick, CandleSubscription, Granularity, - Instrument, + FxInstrument, InstrumentCandles, ) @@ -74,7 +74,7 @@ async def next(self) -> None: self._pending_updates = self._required_updates.copy() def get_data( - self, instrument: Instrument, granularity: Granularity + self, instrument: FxInstrument, granularity: Granularity ) -> InstrumentCandles: return self._data_context.get(instrument, granularity) diff --git a/tests/unit/test_broker.py b/tests/unit/test_broker.py index d85c217..4f36d67 100644 --- a/tests/unit/test_broker.py +++ b/tests/unit/test_broker.py @@ -1,7 +1,7 @@ from unittest.mock import Mock from pytrade.broker import FxBroker -from pytrade.models.instruments import Instrument +from pytrade.models.instruments import FxInstrument from pytrade.models.order import OrderRequest, TimeInForce @@ -11,7 +11,7 @@ def test_buy_order(): assert len(broker._pending_orders) == 0 - broker.order(OrderRequest(Instrument.GBPUSD, 10, TimeInForce.GOOD_TILL_CANCELLED)) + broker.order(OrderRequest(FxInstrument.GBPUSD, 10, TimeInForce.GOOD_TILL_CANCELLED)) assert len(broker._pending_orders) == 1 @@ -22,7 +22,7 @@ def test_sell_order(): assert len(broker._pending_orders) == 0 - broker.order(OrderRequest(Instrument.GBPUSD, -10, TimeInForce.GOOD_TILL_CANCELLED)) + broker.order(OrderRequest(FxInstrument.GBPUSD, -10, TimeInForce.GOOD_TILL_CANCELLED)) assert len(broker._pending_orders) == 1 @@ -31,7 +31,7 @@ def test_process_orders(): client = Mock() broker = FxBroker(client) - broker.order(OrderRequest(Instrument.GBPUSD, 10, TimeInForce.GOOD_TILL_CANCELLED)) + broker.order(OrderRequest(FxInstrument.GBPUSD, 10, TimeInForce.GOOD_TILL_CANCELLED)) assert len(broker._pending_orders) == 1 diff --git a/tests/unit/test_indicator.py b/tests/unit/test_indicator.py index b26b1b4..2ed5370 100644 --- a/tests/unit/test_indicator.py +++ b/tests/unit/test_indicator.py @@ -8,13 +8,13 @@ MINUTES_MAP, Candlestick, Granularity, - Instrument, + FxInstrument, InstrumentCandles, ) def get_candles( - count: int, instrument: Instrument, granularity: Granularity, end_time: datetime + count: int, instrument: FxInstrument, granularity: Granularity, end_time: datetime ) -> list[Candlestick]: _delta = MINUTES_MAP[granularity] return [ @@ -65,7 +65,7 @@ def test_update(): test_series = pd.Series([0, 1, 0, 0, 1]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = BoolIndicator(data) for idx, candle in enumerate(candles): @@ -82,7 +82,7 @@ def test_primitive_equality(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = SquareIndicator(data) for idx, candle in enumerate(candles): @@ -99,7 +99,7 @@ def test_primitive_greater(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = StaticIndicator(data) for idx, candle in enumerate(candles): @@ -116,7 +116,7 @@ def test_primitive_less(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = StaticIndicator(data) for idx, candle in enumerate(candles): @@ -133,7 +133,7 @@ def test_indicator_equality(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = StaticIndicator(data) indicator2 = StaticIndicator(data) @@ -153,7 +153,7 @@ def test_indicator_greater(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = StaticIndicator(data) indicator2 = SubtractIndicator(data) @@ -173,7 +173,7 @@ def test_indicator_less(): test_series = pd.Series([0, 1, 2, 3, 4]) data = InstrumentCandles() candles = get_candles( - len(test_series), Instrument.EURUSD, Granularity.M1, datetime.now() + len(test_series), FxInstrument.EURUSD, Granularity.M1, datetime.now() ) indicator = StaticIndicator(data) indicator2 = AddIndicator(data) diff --git a/tests/unit/test_instrument_history.py b/tests/unit/test_instrument_history.py index f731005..935fe46 100644 --- a/tests/unit/test_instrument_history.py +++ b/tests/unit/test_instrument_history.py @@ -11,7 +11,7 @@ MINUTES_MAP, Candlestick, Granularity, - Instrument, + FxInstrument, InstrumentCandles, ) @@ -21,7 +21,7 @@ def get_candles(count: int, granularity: Granularity): _delta = MINUTES_MAP[granularity] return [ Candlestick( - Instrument.EURUSD, + FxInstrument.EURUSD, granularity, random.uniform(0, 10), random.uniform(0, 10), @@ -121,7 +121,7 @@ def test_update_wrong_instrument(): history = InstrumentCandles(max_size=10) first_candle = dummy_candles[0] second_candle = dummy_candles[1] - second_candle.instrument = Instrument.GBPUSD + second_candle.instrument = FxInstrument.GBPUSD assert first_candle.instrument != second_candle.instrument diff --git a/tests/unit/test_strategy.py b/tests/unit/test_strategy.py index 5cc2049..e2f17ba 100644 --- a/tests/unit/test_strategy.py +++ b/tests/unit/test_strategy.py @@ -12,31 +12,31 @@ Candlestick, CandleSubscription, Granularity, - Instrument, + FxInstrument, ) from pytrade.strategy import FxStrategy TEST_SUBCRIPTIONS = [ - CandleSubscription(Instrument.EURUSD, Granularity.M5), - CandleSubscription(Instrument.EURUSD, Granularity.M15), - CandleSubscription(Instrument.GBPUSD, Granularity.M5), - CandleSubscription(Instrument.GBPUSD, Granularity.M15), + CandleSubscription(FxInstrument.EURUSD, Granularity.M5), + CandleSubscription(FxInstrument.EURUSD, Granularity.M15), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M5), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M15), ] TEST_UPDATES = [ - CandleSubscription(Instrument.EURUSD, Granularity.M5), - CandleSubscription(Instrument.EURUSD, Granularity.M5), - CandleSubscription(Instrument.EURUSD, Granularity.M5), - CandleSubscription(Instrument.EURUSD, Granularity.M15), - CandleSubscription(Instrument.GBPUSD, Granularity.M5), - CandleSubscription(Instrument.GBPUSD, Granularity.M5), - CandleSubscription(Instrument.GBPUSD, Granularity.M5), - CandleSubscription(Instrument.GBPUSD, Granularity.M15), + CandleSubscription(FxInstrument.EURUSD, Granularity.M5), + CandleSubscription(FxInstrument.EURUSD, Granularity.M5), + CandleSubscription(FxInstrument.EURUSD, Granularity.M5), + CandleSubscription(FxInstrument.EURUSD, Granularity.M15), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M5), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M5), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M5), + CandleSubscription(FxInstrument.GBPUSD, Granularity.M15), ] def get_candles( - count: int, instrument: Instrument, granularity: Granularity, end_time: datetime + count: int, instrument: FxInstrument, granularity: Granularity, end_time: datetime ) -> list[Candlestick]: _delta = MINUTES_MAP[granularity] return [ From 0c1c2aaa9937d320470638a3c53ce6709ce676d8 Mon Sep 17 00:00:00 2001 From: Kyle Widmann Date: Sun, 12 Jan 2025 10:47:00 -0500 Subject: [PATCH 2/2] lint fixes --- pytrade/broker.py | 2 +- pytrade/interfaces/broker.py | 2 +- pytrade/interfaces/client.py | 2 +- pytrade/models/instruments.py | 2 +- pytrade/strategy.py | 2 +- tests/unit/test_broker.py | 4 +++- tests/unit/test_indicator.py | 2 +- tests/unit/test_instrument_history.py | 2 +- tests/unit/test_strategy.py | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pytrade/broker.py b/pytrade/broker.py index a8a4c21..8201153 100644 --- a/pytrade/broker.py +++ b/pytrade/broker.py @@ -2,7 +2,7 @@ from pytrade.interfaces.broker import IBroker from pytrade.interfaces.client import IClient -from pytrade.models.instruments import Candlestick, Granularity, FxInstrument +from pytrade.models.instruments import Candlestick, FxInstrument, Granularity from pytrade.models.order import OrderRequest diff --git a/pytrade/interfaces/broker.py b/pytrade/interfaces/broker.py index d400a63..4179fe9 100644 --- a/pytrade/interfaces/broker.py +++ b/pytrade/interfaces/broker.py @@ -1,7 +1,7 @@ import abc from typing import Callable -from pytrade.models.instruments import Candlestick, Granularity, FxInstrument +from pytrade.models.instruments import Candlestick, FxInstrument, Granularity from pytrade.models.order import OrderRequest diff --git a/pytrade/interfaces/client.py b/pytrade/interfaces/client.py index 0d96061..791cbd7 100644 --- a/pytrade/interfaces/client.py +++ b/pytrade/interfaces/client.py @@ -3,7 +3,7 @@ from multimethod import multimethod -from pytrade.models.instruments import Candlestick, Granularity, FxInstrument +from pytrade.models.instruments import Candlestick, FxInstrument, Granularity from pytrade.models.order import MarketOrderRequest diff --git a/pytrade/models/instruments.py b/pytrade/models/instruments.py index 417f5a0..38389cb 100644 --- a/pytrade/models/instruments.py +++ b/pytrade/models/instruments.py @@ -25,7 +25,7 @@ class Granularity(Enum): Granularity.M15: 15, Granularity.H1: 60, Granularity.H4: 240, - Granularity.D1: 1440 + Granularity.D1: 1440, } diff --git a/pytrade/strategy.py b/pytrade/strategy.py index c41d35a..6d3d993 100644 --- a/pytrade/strategy.py +++ b/pytrade/strategy.py @@ -7,8 +7,8 @@ CandleData, Candlestick, CandleSubscription, - Granularity, FxInstrument, + Granularity, InstrumentCandles, ) diff --git a/tests/unit/test_broker.py b/tests/unit/test_broker.py index 4f36d67..84d195a 100644 --- a/tests/unit/test_broker.py +++ b/tests/unit/test_broker.py @@ -22,7 +22,9 @@ def test_sell_order(): assert len(broker._pending_orders) == 0 - broker.order(OrderRequest(FxInstrument.GBPUSD, -10, TimeInForce.GOOD_TILL_CANCELLED)) + broker.order( + OrderRequest(FxInstrument.GBPUSD, -10, TimeInForce.GOOD_TILL_CANCELLED) + ) assert len(broker._pending_orders) == 1 diff --git a/tests/unit/test_indicator.py b/tests/unit/test_indicator.py index 2ed5370..56dab85 100644 --- a/tests/unit/test_indicator.py +++ b/tests/unit/test_indicator.py @@ -7,8 +7,8 @@ from pytrade.models.instruments import ( MINUTES_MAP, Candlestick, - Granularity, FxInstrument, + Granularity, InstrumentCandles, ) diff --git a/tests/unit/test_instrument_history.py b/tests/unit/test_instrument_history.py index 935fe46..87241fd 100644 --- a/tests/unit/test_instrument_history.py +++ b/tests/unit/test_instrument_history.py @@ -10,8 +10,8 @@ INDEX, MINUTES_MAP, Candlestick, - Granularity, FxInstrument, + Granularity, InstrumentCandles, ) diff --git a/tests/unit/test_strategy.py b/tests/unit/test_strategy.py index e2f17ba..a307e1c 100644 --- a/tests/unit/test_strategy.py +++ b/tests/unit/test_strategy.py @@ -11,8 +11,8 @@ CandleData, Candlestick, CandleSubscription, - Granularity, FxInstrument, + Granularity, ) from pytrade.strategy import FxStrategy