def test_close_sell(): now = arrow.utcnow() epic = Epic() tick = Tick(bid=33.3, ask=34.5, datetime=now) epic.on_new_tick(tick) trade = epic.open_trade( direction=TradeDirection.SELL, quantity=7, ref="MY_TRADE", meta={"my_trade_data": "data"}, ) # close 3 @ 45.8 tick = Tick(bid=44.2, ask=45.8, datetime=now) epic.on_new_tick(tick) epic.close_trade( trade=trade, quantity=3, ref="MY_CLOSE_ID", meta={"data": "data"}, ) assert trade.closed_quantities == 3 assert trade.opened_quantities == 4 assert trade.closed_result_avg == round(33.3 - 45.8, 2) assert trade.closed_result == round(33.3 - 45.8, 2) * 3 assert trade.opened_result_avg == round(33.3 - 45.8, 2) assert trade.opened_result == round(33.3 - 45.8, 2) * 4 assert trade.result_avg == round(33.3 - 45.8, 2) assert trade.result == round(33.3 - 45.8, 2) * 7 assert trade.closed is False # close 4 @ 12.4 tick = Tick(bid=11.1, ask=12.4, datetime=now) epic.on_new_tick(tick) epic.close_trade( trade=trade, quantity=4, ) assert trade.closed_quantities == 7 assert trade.opened_quantities == 0 assert trade.closed_result_avg == round( (round(33.3 - 45.8, 2) * 3 + round(33.3 - 12.4, 2) * 4) / 7, 2 ) assert trade.closed_result == round(33.3 - 45.8, 2) * 3 + round(33.3 - 12.4, 2) * 4 assert trade.opened_result_avg == 0 assert trade.opened_result == 0 assert trade.result_avg == round( (round(33.3 - 45.8, 2) * 3 + round(33.3 - 12.4, 2) * 4) / 7, 2 ) assert trade.result == round(33.3 - 45.8, 2) * 3 + round(33.3 - 12.4, 2) * 4 assert trade.closed is True
def test_close_buy(): now = arrow.utcnow() epic = Epic() tick = Tick(bid=64.6, ask=68.2, datetime=now) epic.on_new_tick(tick) trade = epic.open_trade( direction=TradeDirection.BUY, quantity=6, ref="MY_TRADE", meta={"my_trade_data": "data"}, ) # close 4 @ 54.3 tick = Tick(bid=54.3, ask=55.6, datetime=now) epic.on_new_tick(tick) epic.close_trade( trade=trade, quantity=4, ref="MY_CLOSE_ID", meta={"data": "data"}, ) assert trade.closed_quantities == 4 assert trade.opened_quantities == 2 assert trade.closed_result_avg == round(54.3 - 68.2, 2) assert trade.closed_result == round(54.3 - 68.2, 2) * 4 assert trade.opened_result_avg == round(54.3 - 68.2, 2) assert trade.opened_result == round(54.3 - 68.2, 2) * 2 assert trade.result_avg == round(54.3 - 68.2, 2) assert trade.result == round(54.3 - 68.2, 2) * 6 assert trade.closed is False # close 2 @ 72.3 tick = Tick(bid=72.3, ask=73.4, datetime=now) epic.on_new_tick(tick) epic.close_trade( trade=trade, quantity=2, ) assert trade.closed_quantities == 6 assert trade.opened_quantities == 0 assert trade.closed_result_avg == round( (round(54.3 - 68.2, 2) * 4 + round(72.3 - 68.2, 2) * 2) / 6, 2 ) assert trade.closed_result == round(54.3 - 68.2, 2) * 4 + round(72.3 - 68.2, 2) * 2 assert trade.opened_result_avg == 0 assert trade.opened_result == 0 assert trade.result_avg == round( (round(54.3 - 68.2, 2) * 4 + round(72.3 - 68.2, 2) * 2) / 6, 2 ) assert trade.result == round(54.3 - 68.2, 2) * 4 + round(72.3 - 68.2, 2) * 2 assert trade.closed is True
def test_init_buy(): now = arrow.utcnow() epic = Epic() tick = Tick(bid=54.3, ask=68.9, datetime=now) epic.on_new_tick(tick) trade = epic.open_trade( direction=TradeDirection.BUY, quantity=3, ref="MY_TRADE", meta={"my_trade_data": "data"}, ) assert trade.direction == TradeDirection.BUY assert trade.open_quantity == 3 # for a BUY, open value is the input ask assert trade.open_value == 68.9 # current represents the current market value to close this trade assert trade.current_close_value == 54.3 assert trade.datetime == now assert trade.closes == [] # max and min result are the opened result assert trade.max_result == round(54.3 - 68.9, 2) * 3 assert trade.min_result == round(54.3 - 68.9, 2) * 3 # by default a trade status in confirmed assert trade.status == TransactionStatus.CONFIRMED assert trade.ref == "MY_TRADE" assert trade.meta == {"my_trade_data": "data"}
def test_trade_provider_custom(): # GIVEN an instance of our custom trade provider attached to an Epic trade_provider = MyTradeProvider() epic = Epic(ref="MY_EPIC", trade_provider=trade_provider) # Add a tick to the epic tick = Tick(datetime=arrow.utcnow(), bid=99, ask=101) epic.on_new_tick(tick) # WHEN I create a new trade add open it with the Trade Provider trade = epic.open_trade(direction=TradeDirection.SELL, quantity=2) # THEN a new trade is created assert len(epic.trade_provider.trades) == 1 # THEN the trade attribute were updated by the trade provider trade_in_provider = epic.trade_provider.trades[0] assert trade_in_provider.status == TransactionStatus.PENDING assert trade_in_provider.meta["provider_id"] == 123 # WHEN I close the trade epic.close_trade(trade=trade, quantity=1) # THEN a close of one quantity was created on the opened trade assert len(trade_in_provider.closes) == 1 # THEN the trade close attributes where set trade_close_in_provider = trade_in_provider.closes[0] assert trade_close_in_provider.status == TransactionStatus.REQUIRED assert trade_close_in_provider.meta["provider_close_id"] == 123
def test_trade_provider_backtest(): # GIVEN an instance of our custom trade provider attached to an Epic epic = Epic(ref="MY_EPIC") # Add a tick to the epic tick = Tick(datetime=arrow.utcnow(), bid=99, ask=101) epic.on_new_tick(tick) # WHEN I create a new trade add open it with the Trade Provider trade = epic.open_trade(direction=TradeDirection.SELL, quantity=2) # THEN a new trade is created assert len(epic.trade_provider.trades) == 1 # THEN the trade status is automatically set to CONFIRMED trade_in_provider = epic.trade_provider.trades[0] assert trade_in_provider.status == TransactionStatus.CONFIRMED # WHEN I close the trade epic.close_trade(trade=trade, quantity=1) # THEN a close of one quantity was created on the opened trade assert len(trade_in_provider.closes) == 1 # THEN the trade close attributes where set trade_close_in_provider = trade_in_provider.closes[0] assert trade_close_in_provider.status == TransactionStatus.CONFIRMED
def test_simple_moving_average(): # GIVEN A FrameSet in UT1 minutes ut1mn = FrameSet(ref="UT1MN", unit=Unit.MINUTE, unit_quantity=1) # GIVEN a SMA with a max number of periods of 10 sma = SimpleMovingAverage(ref="SMA", max_periods=10) ut1mn.add_indicator(sma) # GIVEN an Epic holding the UT4MN FrameSet epic = Epic(ref="MY_EPIC_CODE") epic.add_frame_set(ut1mn) # GIVEN an instance of my provider provider = MyTickProvider(epics=[epic]) # WHEN I run the tick provider provider.run() # THEN the SMA on the last 3 periods is properly calculated sma3 = epic.get_indicator_value( frame_set_ref="UT1MN", indicator_ref="SMA", ).get_value(periods=3) assert sma3 == round((9.4 + 14.7 + 11.8) / 3, 2) # WHEN I update the epic with a new tick, the SMA is updated new_tick = Tick(datetime=epic.last_tick.datetime, bid=15.6, ask=17.6) epic.on_new_tick(new_tick) # THEN the SMA on the last 3 periods is properly updated sma3 = epic.get_indicator_value( frame_set_ref="UT1MN", indicator_ref="SMA", ).get_value(periods=3) assert sma3 == round((9.4 + 14.7 + 16.6) / 3, 2)
def test_trade(): # GIVEN an Epic epic = Epic(ref="MY_EPIC_CODE") # GIVEN a tick added to this epic (to set the epic last value to 100) tick1 = Tick( datetime=arrow.get("2020-01-01 12:34:56"), bid=99, ask=101, ) epic.on_new_tick(tick1) # WHEN I open a BUY Trade with a quantity of 3 from the current epic value. trade = epic.open_trade( direction=TradeDirection.BUY, quantity=3, ) # THEN the trade result is the current spread assert trade.result == -6 # WHEN a new tick is received by the epic tick2 = Tick( datetime=arrow.get("2020-01-01 12:34:57"), bid=109, ask=111, ) epic.on_new_tick(tick2) # THEN the trade result is updated assert trade.result == 24 # WHEN I close 2 quantities on this trade epic.close_trade(trade=trade, quantity=2) assert trade.closed_quantities == 2 # WHEN a new tick is received by the epic tick3 = Tick( datetime=arrow.get("2020-01-01 12:34:57"), bid=89, ask=91, ) epic.on_new_tick(tick3) # THEN the trade result is updated and take account that one quantity is closed. assert trade.result == 4
def test_value(): tick = Tick( datetime.utcnow().replace(tzinfo=timezone.utc), bid=49, ask=50, ) assert tick.value == 49.5
def test_value(): tick = Tick( datetime.utcnow().replace(tzinfo=timezone.utc), bid=99, ask=101, ) assert tick.spread == 2
def test_arrow_datetime(): now = arrow.utcnow() tick = Tick( datetime=now, bid=49, ask=50, ) assert tick.datetime == now
def test_python_datetime(): now = datetime.utcnow().replace(tzinfo=timezone.utc) tick = Tick( datetime=now, bid=49, ask=50, ) assert tick.datetime == arrow.get(now)
def test_basic_strategy(): epic = Epic(ref="MY_EPIC_CODE") # GIVEN a instance of MyStrategy on an Epic strategy = MyStrategy() epic.add_strategy(strategy) # WHEN a tick of value 100 is received tick = Tick(arrow.utcnow(), 99, 101) epic.on_new_tick(tick) # THEN a trade is created assert len(epic.trade_provider.opened_trades) == 1 # WHEN a tick of value != 100 is received tick = Tick(arrow.utcnow(), 101, 103) epic.on_new_tick(tick) # THEN the previously opened trade is closed assert len(epic.trade_provider.opened_trades) == 0
def run(self): for i in range(10): # create a new tick new_tick = Tick( datetime=datetime.utcnow().replace(tzinfo=timezone.utc), bid=(i - 0.5), ask=(i + 0.5), ) # find epic to attach the tick to tick_epic = self.get_epic_by_ref("MY_EPIC_CODE") # attach tick to epic tick_epic.on_new_tick(new_tick)
def test_tick(): now_tokyo = arrow.now("Asia/Tokyo") tick = Tick(datetime=now_tokyo, bid=99, ask=101, meta={"my_data": "data"}) assert tick.bid == 99 assert tick.ask == 101 assert tick.datetime == now_tokyo assert tick.meta == {"my_data": "data"} assert tick.value == 100.0 # value represents the mean between bid and ask assert tick.spread == 2.0 # spread is the difference between bid and ask assert tick.datetime_utc == now_tokyo.to("UTC")
def run(self): # Generates 9 ticks for i in range(10): # create a new tick new_tick = Tick( datetime=arrow.utcnow(), bid=(i - 0.5), ask=(i + 0.5), ) # find epic to attach the tick to tick_epic = self.get_epic_by_ref("MY_EPIC_CODE") # dispatch tick to epic tick_epic.on_new_tick(new_tick)
def run(self): current_datetime = arrow.utcnow() i = 0 while i < 10: tick = Tick( bid=i - 0.2, ask=i + 0.2, datetime=current_datetime, ) self.get_epic_by_ref("MY_EPIC").on_new_tick(tick) current_datetime = current_datetime + timedelta(minutes=1) i += 0.01
def test_asdict(): now = arrow.utcnow() tick = Tick( now, bid=49, ask=50, ) assert tick.asdict() == { "ask": 50.0, "bid": 49.0, "datetime": now, "spread": 1.0, "value": 49.5, } assert tick.asdict(datetime_to_str=True) == { "ask": 50.0, "bid": 49.0, "datetime": now.strftime("%Y-%m-%d %H:%M:%S"), "spread": 1.0, "value": 49.5, }
def run(self): # Generate 8 ticks (1 tick every minute from 2020-01-01 00:00:12) dt = arrow.get("2020-01-01 00:00:00") for i in [12, 13.4, 8.2, 10, 12.6, 9.4, 14.7, 11.8]: # create a new tick new_tick = Tick( datetime=dt, bid=(i - 0.5), ask=(i + 0.5), ) # find epic to attach the tick to tick_epic = self.get_epic_by_ref("MY_EPIC_CODE") # dispatch tick to epic tick_epic.on_new_tick(new_tick) dt = dt.shift(minutes=1)
def run(self): # Generate 10 ticks (1 tick every minute from 2020-01-01 00:00:12) dt = arrow.get("2020-01-01 00:00:12") for i in range(10): dt = dt.shift(minutes=1) # create a new tick new_tick = Tick( datetime=dt, bid=(i - 0.5), ask=(i + 0.5), ) # find epic to attach the tick to tick_epic = self.get_epic_by_ref("MY_EPIC_CODE") # dispatch tick to epic tick_epic.on_new_tick(new_tick)
def test_create_epic(): # GIVEN an epic timezoned on Paris(France) holding a max of 3 ticks in memory epic = Epic(timezone="Europe/Paris") # WHEN I add a tick to this epic in UTC tick = Tick( datetime=arrow.utcnow(), bid=100, ask=101, ) epic.on_new_tick(tick) # THEN the tick is set as the Epic last tick assert epic.last_tick == tick # THEN the tick date was converted from UTC to the Epic timezone (Europe/Paris) assert tick.datetime.tzinfo == tz.gettz(epic.timezone)
def test_init_sell(): now = arrow.utcnow() epic = Epic() tick = Tick(bid=34, ask=36, datetime=now) epic.on_new_tick(tick) trade = epic.open_trade( direction=TradeDirection.SELL, quantity=7, ) assert trade.direction == TradeDirection.SELL assert trade.open_quantity == 7 # for a SELL, open value is the input bid assert trade.open_value == 34 # current represents the current market value to close this trade assert trade.current_close_value == 36 assert trade.datetime == now assert trade.closes == [] # max and min result are the opened result assert trade.max_result == (34 - 36) * 7 assert trade.min_result == (34 - 36) * 7
def test_convert_to_string(): now = arrow.utcnow() tick = Tick(datetime=now, bid=999, ask=1001) assert (str(tick) == f"{now.strftime('%Y-%m-%d %H:%M:%S')} : 1000.0 " f"(bid: 999.0, ask: 1001.0, spread: 2.0)")
def test_indicator_market_open_gaps(): my_indicator = BaseIndicator(ref="MY_IND", value_class=BaseIndicatorValue, market_open_only=True) daily_frameset = FrameSet(ref="UT1D", unit=Unit.DAY, unit_quantity=1) daily_frameset.add_indicator(my_indicator) # GIVEN an epic open between 9 and 18 on Tuesday and Thursday epic = Epic(open_time=time(9, 0), close_time=time(18, 0), trade_days=[1, 3]) epic.add_frame_set(daily_frameset) # Add ticks on Monday: Market is closed, the indicator value stays to None tick1 = Tick(datetime=arrow.get("2020-01-06 08:00:00"), bid=999, ask=1001) epic.on_new_tick(tick1) last_frame = epic.frame_sets["UT1D"].current assert last_frame.period_start == arrow.get("2020-01-06 00:00:00") assert last_frame.indicators["MY_IND"] is None tick2 = Tick(datetime=arrow.get("2020-01-06 10:00:00"), bid=999, ask=1001) epic.on_new_tick(tick2) assert last_frame.indicators["MY_IND"] is None tick3 = Tick(datetime=arrow.get("2020-01-06 19:00:00"), bid=999, ask=1001) epic.on_new_tick(tick3) assert last_frame.indicators["MY_IND"] is None # Add ticks on Tuesday: Market is opened, the indicator value is updated tick4 = Tick(datetime=arrow.get("2020-01-07 08:00:00"), bid=999, ask=1001) epic.on_new_tick(tick4) last_frame = epic.frame_sets["UT1D"].current assert last_frame.period_start == arrow.get("2020-01-07 00:00:00") assert last_frame.indicators["MY_IND"] is None tick5 = Tick(datetime=arrow.get("2020-01-07 10:00:00"), bid=999, ask=1001) epic.on_new_tick(tick5) assert last_frame.indicators["MY_IND"].first_tick == tick5 tick6 = Tick(datetime=arrow.get("2020-01-07 11:00:00"), bid=1005, ask=1007) epic.on_new_tick(tick6) assert last_frame.indicators["MY_IND"].high_tick == tick6 tick7 = Tick(datetime=arrow.get("2020-01-07 12:00:00"), bid=997, ask=999) epic.on_new_tick(tick7) assert last_frame.indicators["MY_IND"].low_tick == tick7 tick8 = Tick(datetime=arrow.get("2020-01-07 17:00:00"), bid=999, ask=1001) epic.on_new_tick(tick8) assert last_frame.indicators["MY_IND"].last_tick == tick8 tick9 = Tick(datetime=arrow.get("2020-01-07 19:00:00"), bid=999, ask=1001) epic.on_new_tick(tick9) assert last_frame.indicators["MY_IND"].first_tick == tick5 assert last_frame.indicators["MY_IND"].high_tick == tick6 assert last_frame.indicators["MY_IND"].low_tick == tick7 assert last_frame.indicators["MY_IND"].last_tick == tick8 # Add ticks on Wednesday: Market is closed, the indicator value stays to None tick10 = Tick(datetime=arrow.get("2020-01-08 08:00:00"), bid=999, ask=1001) epic.on_new_tick(tick10) last_frame = epic.frame_sets["UT1D"].current assert last_frame.period_start == arrow.get("2020-01-08 00:00:00") assert last_frame.indicators["MY_IND"] is None assert last_frame.previous_frame.indicators["MY_IND"].last_tick == tick8 tick11 = Tick(datetime=arrow.get("2020-01-08 12:00:00"), bid=999, ask=1001) epic.on_new_tick(tick11) assert last_frame.indicators["MY_IND"] is None tick12 = Tick(datetime=arrow.get("2020-01-08 19:00:00"), bid=999, ask=1001) epic.on_new_tick(tick12) assert last_frame.indicators["MY_IND"] is None # Add ticks on Thursday: Market is opened, the indicator value is updated tick13 = Tick(datetime=arrow.get("2020-01-09 08:00:00"), bid=999, ask=1001) epic.on_new_tick(tick13) last_frame = epic.frame_sets["UT1D"].current assert last_frame.period_start == arrow.get("2020-01-09 00:00:00") assert last_frame.indicators["MY_IND"] is None tick14 = Tick(datetime=arrow.get("2020-01-09 10:00:00"), bid=999, ask=1001) epic.on_new_tick(tick14) assert last_frame.indicators["MY_IND"].first_tick == tick14 assert last_frame.indicators["MY_IND"].previous.last_tick == tick8