예제 #1
0
    def _get_averages(self, market: Market) -> Tuple[Decimal, Decimal]:
        now = self._datetime_factory.now()

        long_average = self._candle_storage.mean(
            market.name, self._strategy_run.pair, CANDLE_STORAGE_FIELD_CLOSE,
            DateTimeInterval(now - self._long_average_interval, now))

        short_average = self._candle_storage.mean(
            market.name, self._strategy_run.pair, CANDLE_STORAGE_FIELD_CLOSE,
            DateTimeInterval(now - self._short_average_interval, now))

        return long_average, short_average
예제 #2
0
    def first_tick_initialize_strategy_data(self, markets: List[Market]) -> None:
        market = self.get_market(markets)

        current_time = self._datetime_factory.now()
        interval = DateTimeInterval(current_time - 4 * self._candle_size.get_as_time_delta(), current_time)

        candles = self._candle_storage.find_by(
            market_name=market.name,
            pair=self._strategy_run.pair,
            interval=interval,
            candle_size=self._candle_size
        )

        if len(candles) not in [4, 5]:
            raise SkipTickException(
                'Expected to get at 4 or 5 candles, but only {} given. Do you have enough data?'.format(len(candles))
            )

        if len(candles) == 5:  # First and last candle can be cut in half, we dont need the first half-candle.
            candles.pop(0)

        first_candle = create_initial_heikin_ashi_candle(candles[0])
        self._second_previous_candle = candle_to_heikin_ashi(candles[1], first_candle)
        self._first_previous_candle = candle_to_heikin_ashi(candles[2], self._second_previous_candle)
        self._current_unfinished_candle = candle_to_heikin_ashi(candles[3], self._first_previous_candle)
예제 #3
0
    def find_by(self) -> List[StrategyRun]:
        self._connection.begin()
        cursor = self._connection.cursor()
        cursor.execute('SELECT * FROM `strategy_runs` ORDER BY `run_at` DESC')

        result = []
        for row in cursor.fetchall():
            result.append(StrategyRun(
                UUID(row[0]),
                datetime.datetime.fromtimestamp(row[1], tz=datetime.timezone.utc),
                deserialize_pair(row[2]),
                deserialize_strategy_run_markets(json.loads(row[3])),
                row[4],
                json.loads(row[5]),
                DateTimeInterval(
                    datetime.datetime.fromtimestamp(row[6], tz=datetime.timezone.utc),
                    datetime.datetime.fromtimestamp(row[7], tz=datetime.timezone.utc) \
                        if row[7] is not None else None,
                ),
                row[8],
                row[9],
            ))
        cursor.close()
        self._connection.commit()

        return result
예제 #4
0
    def find_by(
        self,
        market_name: str,
        pair: Pair,
        interval: DateTimeInterval = DateTimeInterval(None, None),
        candle_size: CandleSize = CandleSize(CANDLE_SIZE_UNIT_MINUTE, 1)
    ) -> List[Candle]:
        parameters = {
            CANDLE_STORAGE_FIELD_MARKET: "= '{}'".format(market_name),
            CANDLE_STORAGE_FIELD_PAIR: "= '{}'".format(serialize_pair(pair)),
        }
        if interval.since is not None:
            parameters['"time" >'] = "'{}'".format(interval.since.isoformat())

        if interval.till is not None:
            parameters['"time" <'] = "'{}'".format(interval.till.isoformat())

        select = '*'
        if not candle_size.is_one_minute():
            select = 'FIRST("open") AS "open", MAX("high") AS "high", MIN("low") AS "low", LAST("close") AS "close"'

        sql = 'SELECT {} FROM "{}" WHERE '.format(select,
                                                  MEASUREMENT_CANDLES_NAME)
        where = []
        for key, value in parameters.items():
            where.append('{} {}'.format(key, value))
        sql += ' AND '.join(where)
        sql += self._get_group_by(candle_size)

        result: ResultSet = self._client.query(sql)
        data = list(result.get_points())

        return self._parse_db_result_into_candles(data, market_name, pair,
                                                  candle_size)
예제 #5
0
def test_mean(influx_database: InfluxDBClient, expected_mean: int,
              minute_interval: Tuple[int, int]):
    storage = CandleInnoDbStorage(influx_database)
    storage.write_candles(
        [_create_dummy_candle(10, 8000),
         _create_dummy_candle(20, 8300)])
    interval = DateTimeInterval(
        datetime.datetime(2017,
                          7,
                          2,
                          0,
                          minute_interval[0],
                          0,
                          tzinfo=datetime.timezone.utc),
        datetime.datetime(2017,
                          7,
                          2,
                          0,
                          minute_interval[1],
                          0,
                          tzinfo=datetime.timezone.utc))
    mean = storage.mean(DUMMY_MARKET, BTC_USD_PAIR, CANDLE_STORAGE_FIELD_CLOSE,
                        interval)

    assert Decimal(expected_mean) == mean
예제 #6
0
 def mean(
     self,
     market: str,
     pair: Pair,
     field: str,
     interval: DateTimeInterval = DateTimeInterval(None, None)
 ) -> Decimal:
     pass
예제 #7
0
 def find_by(
     self,
     market_name: str,
     pair: Pair,
     interval: DateTimeInterval = DateTimeInterval(None, None),
     candle_size: CandleSize = CandleSize(CANDLE_SIZE_UNIT_MINUTE, 1)
 ) -> List[Candle]:
     raise NotImplementedError()
예제 #8
0
 def mean(
     self,
     market: str,
     pair: Pair,
     field: str,
     interval: DateTimeInterval = DateTimeInterval(None, None)
 ) -> Decimal:
     raise NotImplementedError()
예제 #9
0
def test_mean_no_data_raise_exception(influx_database: InfluxDBClient):
    """We want to raise exception to prevent invalid signal by dropping some price to 0."""
    storage = CandleInnoDbStorage(influx_database)
    interval = DateTimeInterval(
        datetime.datetime(2017, 7, 2, 0, 0, 0, tzinfo=datetime.timezone.utc),
        datetime.datetime(2017, 7, 2, 0, 30, 0, tzinfo=datetime.timezone.utc))
    with pytest.raises(NoCandlesForMarketInStorageException):
        storage.mean(DUMMY_MARKET, BTC_USD_PAIR, CANDLE_STORAGE_FIELD_CLOSE,
                     interval)
예제 #10
0
def test_immutability():
    date0 = datetime.datetime(2017,
                              12,
                              31,
                              0,
                              0,
                              0,
                              tzinfo=datetime.timezone.utc)
    date1 = datetime.datetime(2018,
                              1,
                              1,
                              0,
                              0,
                              0,
                              tzinfo=datetime.timezone.utc)
    date2 = datetime.datetime(2018,
                              1,
                              2,
                              0,
                              0,
                              0,
                              tzinfo=datetime.timezone.utc)
    date3 = datetime.datetime(2018,
                              1,
                              3,
                              0,
                              0,
                              0,
                              tzinfo=datetime.timezone.utc)

    interval = DateTimeInterval(date1, date2)
    assert str(
        interval) == '[2018-01-01T00:00:00+00:00, 2018-01-02T00:00:00+00:00]'

    new_till_interval = interval.with_till(date3)
    assert str(new_till_interval
               ) == '[2018-01-01T00:00:00+00:00, 2018-01-03T00:00:00+00:00]'
    assert interval != new_till_interval

    new_since_interval = interval.with_since(date0)
    assert str(new_since_interval
               ) == '[2017-12-31T00:00:00+00:00, 2018-01-02T00:00:00+00:00]'
    assert interval != new_since_interval
예제 #11
0
 def find_by(self,
             market_name: str,
             pair: Pair,
             status: Union[str, None] = None,
             direction: Union[str, None] = None,
             interval: DateTimeInterval = DateTimeInterval(None, None),
             strategy_run_id: Union[str, None] = None) -> List[Order]:
     return self._order_storage.find_by(market_name, pair, status,
                                        direction, interval,
                                        strategy_run_id)
예제 #12
0
 def _find_last_candle_for_subscription(
         self, candle_storage: CandleStorage,
         subscription: LastCandleSubscription) -> Candle:
     since = self._datetime_factory.now(
     ) - SAFETY_MULTIPLIER * subscription.candle_size.get_as_time_delta()
     interval = DateTimeInterval(since, None)
     candles = candle_storage.find_by(market_name=subscription.market_name,
                                      pair=subscription.pair,
                                      interval=interval,
                                      candle_size=subscription.candle_size)
     assert candles != [], 'Its expected to found candles after getting candle-added event. But none found.'
     return candles[-1]
예제 #13
0
    def export_to_file(self,
                       filename: str,
                       market_name: str,
                       pair: Pair,
                       interval: DateTimeInterval = DateTimeInterval(
                           None, None)):
        candles = self._candle_storage.find_by(market_name=market_name,
                                               pair=pair,
                                               interval=interval)
        data = serialize_candles(candles)

        with open(filename, 'w') as outfile:
            json.dump(data, outfile)
def test_save_strategy_run_and_find_it(
        mysql_connection: MySQLdb.Connection) -> None:
    storage = StrategyRunStorage(mysql_connection)
    strategy_run_to_save = StrategyRun(
        UUID('637f46a2-d008-48ba-9899-322abb56b425'), DUMMY_DATE,
        Pair('USD', 'BTC'),
        [StrategyRunMarket('yolo_market', 'yolo_plugin_name', {'foo': 'BAR'})],
        'strategy_dummy_name', {'gandalf': 'Gandalf the Gray'},
        DateTimeInterval(DUMMY_DATE,
                         None), 'candle_dummy_storage', 'order_dummy_storage')
    storage.insert(strategy_run_to_save)
    strategy_runs = storage.find_by()
    assert len(strategy_runs) == 1
    assert str(strategy_runs[0].strategy_run_id) == str(
        strategy_run_to_save.strategy_run_id)
예제 #15
0
def export_orders(
    ctx: Context,
    market_name,
    pair: Tuple[str, str],
    interval: Tuple[str, str],
    output_file: str,
    order_storage: str
) -> None:
    storage = di_container.order_storage_plugins.get_order_storage(order_storage)
    pair_obj = Pair(pair[0], pair[1])
    interval_obj = DateTimeInterval(
        dateutil.parser.parse(interval[0]).replace(tzinfo=datetime.timezone.utc),
        dateutil.parser.parse(interval[1]).replace(tzinfo=datetime.timezone.utc)
    )
    exporter = OrderExporter(storage)
    exporter.export_to_file(output_file, market_name, pair_obj, interval_obj)
예제 #16
0
def run_strategy(
    ctx: Context,
    strategy_name: str,
    pair: Tuple[str, str],
    market_names: Tuple[str],
    configuration_file: Union[str, None],
    candle_storage: str,
    order_storage: str,
    market_plugin: str
) -> None:
    pair_obj = Pair(pair[0], pair[1])
    strategy_configuration: Dict = {}
    if configuration_file is not None:
        strategy_configuration = load_configuration_from_file(configuration_file)

    strategy_run_at = di_container.datetime_factory.now()

    strategy_run = StrategyRun(
        uuid.uuid4(),
        strategy_run_at,
        pair_obj,
        [StrategyRunMarket(market_plugin, market_name, {}) for market_name in market_names],
        strategy_name,
        strategy_configuration,
        DateTimeInterval(strategy_run_at, None),
        candle_storage,
        order_storage
    )
    di_container.strategy_run_storage.insert(strategy_run)
    di_container.event_emitter.emit_new_strategy_run(strategy_run)

    try:
        di_container.strategy_standard_runner.run(strategy_run)

    except StrategyNotProvidedByAnyPluginException as e:
        _terminate_strategy_run(strategy_run)
        print_error_and_terminate(str(e))

    except ForEndUserException as e:
        _terminate_strategy_run(strategy_run)
        print_error_and_terminate(str(e))

    except KeyboardInterrupt:
        _terminate_strategy_run(strategy_run)
        pass
예제 #17
0
 def mean(
     self,
     market_name: str,
     pair: Pair,
     field: str,
     interval: DateTimeInterval = DateTimeInterval(None, None)
 ) -> Decimal:
     sql = '''
         SELECT MEAN("{}") AS "field_mean" 
         FROM "{}" WHERE "time" > '{}' AND "time" < '{}' AND "pair"='{}' AND "market"='{}' 
         GROUP BY "{}"
     '''.format(field, MEASUREMENT_CANDLES_NAME, interval.since.isoformat(),
                interval.till.isoformat(), serialize_pair(pair),
                market_name, field)
     result: ResultSet = self._client.query(sql)
     self._validate_result_has_some_data(market_name, result)
     mean = list(result.items()[0][1])[0]['field_mean']
     return Decimal(mean)
예제 #18
0
    def get_interval_for_datetime(self,
                                  time: datetime.datetime) -> DateTimeInterval:
        time = time.replace(second=0)

        unit_range = UNIT_RANGES[self.unit]

        current_value = self._get_date_value_by_unit(time)
        number_of_buckets = unit_range / self.size
        bucket_number = int(math.floor(current_value / self.size))

        if bucket_number > number_of_buckets:
            raise ValueError(
                'Bucket {} is higher than max number of buckets [{} ')

        value = bucket_number * self.size
        since = self._set_value_by_unit(time, value)
        till = since + self.get_as_time_delta()

        return DateTimeInterval(since, till)
예제 #19
0
def create_strategy_to_test(candle_storage: CandleStorage,
                            order_storage: OrderStorage,
                            emitter_mock: EventEmitter,
                            datetime_factory: DateTimeFactory):
    return DoubleCrossoverStrategy(
        candle_storage,
        OrderFacade(order_storage, create_portfolio_snapshot_mock(),
                    emitter_mock), datetime_factory,
        StrategyRun(
            STRATEGY_RUN_ID,
            datetime.datetime(2017,
                              12,
                              2,
                              14,
                              0,
                              0,
                              tzinfo=datetime.timezone.utc), BTC_USD_PAIR, [],
            '', {
                'long_average_interval': 60 * 60,
                'short_average_interval': 15 * 60,
            }, DateTimeInterval(None, None), '', ''))
예제 #20
0
def test_serialize_strategy() -> None:
    strategy_run_market = StrategyRun(
        UUID('8b3213c8-2c07-4283-8197-a9babfcc1ec8'),
        DUMMY_DATE,
        Pair('USD', 'BTC'),
        [],
        'strategy_xyz',
        {},
        DateTimeInterval(),
        'c',
        'o'
    )
    assert serialize_strategy_run(strategy_run_market) == {
        'candle_storage_name': 'c',
        'interval': {'since': None, 'till': None},
        'markets': [],
        'order_storage_name': 'o',
        'pair': 'USD_BTC',
        'run_at': '2017-11-26T10:11:12+00:00',
        'strategy_configuration': {},
        'strategy_name': 'strategy_xyz',
        'strategy_run_id': '8b3213c8-2c07-4283-8197-a9babfcc1ec8',
    }
예제 #21
0
    def _tick(self, markets: List[Market]) -> None:

        market = self.get_market(markets)
        current_time = self._datetime_factory.now()
        interval = DateTimeInterval(current_time - 2 * self._candle_size.get_as_time_delta(), current_time)

        candles = self._candle_storage.find_by(
            market_name=market.name,
            pair=self._strategy_run.pair,
            interval=interval,
            candle_size=self._candle_size
        )

        if len(candles) not in [2, 3]:
            raise SkipTickException(
                'Expected to get at 2 or 3 candles, but only {} given. Do you have enough data?'.format(len(candles))
            )

        if len(candles) == 3:  # First and last candle can be cut in half, we don't need the first half-candle.
            candles.pop(0)

        self.update_trend()

        if candles[0].time == self._current_unfinished_candle.time:
            self._second_previous_candle = self._first_previous_candle
            self._first_previous_candle = candle_to_heikin_ashi(candles[0], self._first_previous_candle)
            self._current_unfinished_candle = candle_to_heikin_ashi(candles[1], self._first_previous_candle)

            self.log_tick()

            try:
                self.check_for_buy_or_sell(market)
            except NotEnoughBalanceToPerformOrderException as e:
                # Intentionally, this strategy does not need state of order,
                # just ignores buy/sell and waits for next signal.
                logger.warning(str(e))
예제 #22
0
    def find_by(self,
                market_name: str,
                pair: Pair,
                status: Union[str, None] = None,
                direction: Union[str, None] = None,
                interval: DateTimeInterval = DateTimeInterval(None, None),
                strategy_run_id: Union[str, None] = None) -> List[Order]:
        assert status in POSSIBLE_ORDER_STATUSES or status is None, 'Invalid status: "{}"'.format(
            status)

        parameters = {
            ORDER_FIELD_MARKET: "= '{}'".format(market_name),
            ORDER_FIELD_PAIR: "= '{}'".format(serialize_pair(pair)),
        }

        if status is not None:
            parameters[ORDER_FIELD_STATUS] = "= '{}'".format(status)
        if direction is not None:
            parameters[ORDER_FIELD_DIRECTION] = "= '{}'".format(direction)
        if interval.since is not None:
            parameters['"time" >'] = "'{}'".format(interval.since.isoformat())
        if interval.till is not None:
            parameters['"time" <'] = "'{}'".format(interval.till.isoformat())
        if strategy_run_id is not None:
            parameters[ORDER_FIELD_STRATEGY_RUN_ID] = "= '{}'".format(
                strategy_run_id)

        sql = 'SELECT * FROM "{}" WHERE '.format(self._measurement_name)
        where = []
        for key, value in parameters.items():
            where.append('{} {}'.format(key, value))
        sql += ' AND '.join(where)
        result: ResultSet = self._client.query(sql)
        data = list(result.get_points())

        return [self._create_order_from_serialized(row) for row in data]
def test_subscription_can_be_found():
    storage = SubscriptionStorage()
    last_candle_subscription = LastCandleSubscription(
        '1',
        'foo_storage',
        'bar_market',
        Pair('USD', 'BTC'),
        CandleSize(CANDLE_SIZE_UNIT_MINUTE, 1)
    )
    storage.subscribe(last_candle_subscription)
    subscription_interval = DateTimeInterval(
        datetime.datetime(2018, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
        datetime.datetime(2018, 1, 2, 0, 0, 0, tzinfo=datetime.timezone.utc)
    )
    date_in_interval = datetime.datetime(2018, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
    date_outside_interval = datetime.datetime(2018, 1, 3, 0, 0, tzinfo=datetime.timezone.utc)
    new_order_subscription = NewOrderSubscription(
        '1',
        'foo_storage',
        'bar_market',
        Pair('USD', 'BTC'),
        subscription_interval
    )
    storage.subscribe(new_order_subscription)

    assert storage.find_subscriptions_for_event('unknown_event_name') == []

    candle_suitable_for_subscription = Candle(
        'bar_market',
        Pair('USD', 'BTC'),
        date_in_interval,
        Decimal('11000'),
        Decimal('11000'),
        Decimal('11000'),
        Decimal('11000')
    )

    assert storage.find_subscriptions_for_event(EVENT_LAST_CANDLE_UPDATED)[0] == last_candle_subscription
    assert storage.find_subscriptions_for_event(
        EVENT_LAST_CANDLE_UPDATED,
        {'storage': 'foo_storage', 'candle': serialize_candle(candle_suitable_for_subscription)}
    )[0] == last_candle_subscription
    assert storage.find_subscriptions_for_event(
        EVENT_LAST_CANDLE_UPDATED,
        {'storage': 'gandalf', 'candle': serialize_candle(candle_suitable_for_subscription)}
    ) == []
    assert storage.find_subscriptions_for_event(
        EVENT_LAST_CANDLE_UPDATED,
        {
            'storage': 'foo_storage',
            'candle': serialize_candle(
                Candle(
                    'bar_market',
                    Pair('OMG', 'WTF'),
                    date_in_interval,
                    Decimal('11000'),
                    Decimal('11000'),
                    Decimal('11000'),
                    Decimal('11000')
                )
            )
        }
    ) == []
    assert storage.find_subscriptions_for_event(
        EVENT_LAST_CANDLE_UPDATED,
        {
            'storage': 'foo_storage',
            'candle': serialize_candle(
                Candle(
                    'wtf_market',
                    Pair('USD', 'BTC'),
                    date_in_interval,
                    Decimal('11000'),
                    Decimal('11000'),
                    Decimal('11000'),
                    Decimal('11000')
                )
            )
        }
    ) == []

    order_suitable_for_subscription = _crate_serialized_order(Pair('USD', 'BTC'), 'bar_market', date_in_interval)

    assert storage.find_subscriptions_for_event(EVENT_NEW_ORDER)[0] == new_order_subscription
    assert storage.find_subscriptions_for_event(
        EVENT_NEW_ORDER,
        {'storage': 'foo_storage', 'order': order_suitable_for_subscription}
    )[0] == new_order_subscription
    assert storage.find_subscriptions_for_event(
        EVENT_NEW_ORDER,
        {'storage': 'gandalf', 'order': order_suitable_for_subscription}
    ) == []
    assert storage.find_subscriptions_for_event(
        EVENT_NEW_ORDER,
        {
            'storage': 'foo_storage',
            'order': _crate_serialized_order(Pair('USD', 'BTC'), 'bar_market', date_outside_interval)
        }
    ) == []
    assert storage.find_subscriptions_for_event(
        EVENT_NEW_ORDER,
        {
            'storage': 'foo_storage',
            'order': _crate_serialized_order(Pair('OMG', 'WTF'), 'bar_market', date_in_interval)
        }
    ) == []
    assert storage.find_subscriptions_for_event(
        EVENT_NEW_ORDER,
        {
            'storage': 'foo_storage',
            'order': _crate_serialized_order(Pair('USD', 'BTC'), 'wtf_market', date_in_interval)
        }
    ) == []

    storage.unsubscribe(EVENT_NEW_ORDER, '2')
    assert storage.find_subscriptions_for_event(EVENT_NEW_ORDER)[0] == new_order_subscription
    storage.unsubscribe(EVENT_NEW_ORDER, '1')
    assert storage.find_subscriptions_for_event(EVENT_NEW_ORDER) == []

    storage.unsubscribe(EVENT_LAST_CANDLE_UPDATED, '2')
    assert storage.find_subscriptions_for_event(EVENT_LAST_CANDLE_UPDATED)[0] == last_candle_subscription
    storage.unsubscribe(EVENT_LAST_CANDLE_UPDATED, '1')
    assert storage.find_subscriptions_for_event(EVENT_LAST_CANDLE_UPDATED) == []
예제 #24
0
    BTC_USD_PAIR, ORDER_TYPE_LIMIT, Decimal('1'), Decimal('8000'),
    'aaa-id-from-market', ORDER_STATUS_CLOSED,
    datetime.datetime(2017, 11, 26, 10, 11, 12, tzinfo=datetime.timezone.utc))
DUMMY_OPEN_ORDER = Order(
    UUID('16fd2706-8baf-433b-82eb-8c7fada847db'), STRATEGY_RUN_ID,
    DUMMY_MARKET_NAME, DIRECTION_BUY,
    datetime.datetime(2017, 11, 26, 10, 11, 12,
                      tzinfo=datetime.timezone.utc), BTC_USD_PAIR,
    ORDER_TYPE_LIMIT, Decimal('1'), Decimal('8000'), 'aaa-id-from-market')
STRATEGY_RUN = StrategyRun(
    STRATEGY_RUN_ID,
    datetime.datetime(2017, 11, 26, 10, 11, 12, tzinfo=datetime.timezone.utc),
    BTC_USD_PAIR, [], '', {
        'long_average_interval': 60 * 60,
        'short_average_interval': 15 * 60,
    }, DateTimeInterval(None, None), '', '')

CLOSED_ORDER_INFO = OrderMarketInfo(order=DUMMY_OPEN_ORDER,
                                    is_open=False,
                                    closed_at=datetime.datetime(
                                        2017,
                                        11,
                                        26,
                                        10,
                                        11,
                                        12,
                                        tzinfo=datetime.timezone.utc),
                                    quantity_remaining=Decimal('0'))

STILL_OPEN_ORDER_INFO = OrderMarketInfo(order=DUMMY_OPEN_ORDER,
                                        is_open=True,