Giter Club home page Giter Club logo

blankly's People

Contributors

algoq avatar arun-annamalai avatar aryan34 avatar aydingokce avatar bfan1256 avatar c0indev3l avatar emersondove avatar hawkinsjb1 avatar jaewoo5830 avatar jkalish14 avatar jliuu1 avatar jmoz avatar jordibeen avatar mehta-avi avatar n8henrie avatar qmatias avatar tylerv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blankly's Issues

Import blankly: ModuleNotFoundError: No module named 'blankly'

Description

It appears that pip installs blankly under the folder Blankly (capitalized).
However, the documentation and the internal python scripts reference blankly with a lower letter.

A quickfix is to rename the Blankly folder in site-packages to blankly.

Error (if applicable)

Traceback (most recent call last):
  File "C:\Users\....\blankly\script.py", line 1, in <module>
    import blankly
ModuleNotFoundError: No module named 'blankly'

Platform Info

  • Python version: 3.9.7
  • Platform: Windows 10

Feature request: Default to minimum possible resolution

Description

When trying to inter-operate the same strategy with various currencies, it'd be great to get a helper (a la trunc) to default to minimum possible resolution. For instance, BTC-USD resolution may be higher than SOL-USD.

Error

blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 0.01. You specified 60.405. Try using blankly.trunc(size, decimal_number) to match the exchange resolution.

Support for UK/EU equities trading

Description of Feature Request

  • Support for an exchange that supports equities/stocks/etfs (etc) for UK/EU clients. Like Alpaca does for US based clients.

Why is it important?

Currently it seems it's only possible to trade Crypto/Forex with Blankly if you're located outside the US.

How would you use the feature?

N/A

OPTIONAL: How would you implement this?

Connection to Interactive Brokers, maybe?

Would you like to Contribute?

  • Yes, I would love to contribute to this feature request ๐ŸŽ‰
  • No, but please keep me posted ๐ŸŽ‰

How to short with Binance ?

Hello,

I didn't find mentions about futures or shorting. Is it possible ?

I tried this

state.interface.limit_order(symbol, 'sell', price, quantity)
blankly.utils.exceptions.InvalidOrder: Not enough base currency. Available: 0.0. hold: 0. requested: 100

Order Submission with Strategy should use Order class instead of Local Interface

The Problem

In our strategy_usage_example.py, the current implementation utilizes the local interface as a method to submit orders. This method of submitting orders is unpythonic and isn't as fit for development (for example when running in a jupyter notebook, unless the interface is defined before the function, the price_event function will not compile). Instead, the implementation should utilize the Order class that is defined in strategy/order.py of which the strategy_base should evaluate the order and make the necessary purchase for the user.

The Fix

The fix should be to make sure that price_event returns a value (otherwise throw a ValueError) and translate the order into the necessary order types.

Sample Fix

import Blankly
from Blankly.strategy.strategy_base import Strategy, Order

prices = []


def price_event(price, currency_pair):
    prices.append(price)
    sma50 = Blankly.analysis.calculate_sma(price, window=10)
    if price > sma50:
        return Order('market', currency_pair, 'buy', 10)
    else:
        return Order('market', currency_pair, 'sell', 10)


if __name__ == "__main__":
    # Authenticate coinbase pro strategy
    coinbase_pro = Blankly.Coinbase_Pro()
    coinbase_strategy = Strategy(coinbase_pro)

    coinbase_strategy.add_price_event(price_event, currency_pair='BTC-USD', resolution='15m')
    coinbase_strategy.add_price_event(price_event, currency_pair='XLM-USD', resolution='15m')
    coinbase_strategy.add_price_event(price_event, currency_pair='COMP-USD', resolution='15m')

JSONDecodeError: Expecting value

Description

I'm trying the RSI code in colab and desktop pc, but I'm getting this error.

#RSI KODLARI
import blankly


def price_event(price, symbol, state: blankly.StrategyState):
    """ This function will give an updated price every 15 seconds from our definition below """
    state.variables['history'].append(price) # appends to the deque of historical prices
    rsi = blankly.indicators.rsi(state.variables['history'])
    if rsi[-1] < 30 and not state.variables['owns_position']:
        buy = int(state.interface.cash / price)
        state.interface.market_order(symbol, side='buy', size=buy)
        state.variables['owns_position'] = True
    elif rsi[-1] > 70 and state.variables['owns_position']:
        curr_value = int(state.interface.account[state.base_asset].available)
        state.interface.market_order(symbol, side='sell', size=curr_value)
        state.variables['owns_position'] = False

def init(symbol, state: blankly.StrategyState):
    # Download price data to give context to the algo
    state.variables['history'] = state.interface.history(symbol, to=150, return_as='deque')['close']
    state.variables['owns_position'] = False

if __name__ == "__main__":
    exchange = blankly.Binance(portfolio_name="ATD Binance")
    strategy = blankly.Strategy(exchange)
    strategy.add_price_event(price_event, symbol='ETH-BUSD', resolution='15m', init=init)

    # Start the strategy. This will begin each of the price event ticks
    # strategy.start()
    # Or backtest using this
    results = strategy.backtest(to='1y', initial_values={'BUSD': 1000})
    print(results)

Error (if applicable)

JSONDecodeError: Expecting value: line 10 column 5 (char 364)

blankly-rsi

Struggling with setting `risk_free_return_rate` in `backtest.json`

Description

I'm struggling with setting risk_free_return_rate and see it propagated when printing results from a backtest. Could you please help?

backtest.json

{
  "price_data": {
...
  },
  "settings": {
...
    "risk_free_return_rate": 0.01
  }
}

Output when print(results)

Blankly Metrics: 
Compound Annual Growth Rate (%):   661.0%
Cumulative Returns (%):            64.0%
Max Drawdown (%):                  8.0%
Variance (%):                      6.36%
Sortino Ratio:                     4.92
Sharpe Ratio:                      3.68
Calmar Ratio:                      19.29
Volatility:                        0.03
Value-at-Risk:                     654.94
Conditional Value-at-Risk:         24.68
Risk Free Return Rate:             0.0
Resampled Time:                    86400.0

Tested on last main branch installed using pip.

Permissive License

I am not sure if some in the financial services industry would be comfortable with a *GPL license. I think something like AL 2.0, MIT, BSD would be better for wider usage and adaptation. Is there a particular reason a *GPL license has been used?

  • Quantopian used AL 2.0
  • QuantConnect is using AL 2.0

KeylessExchange does not provide OHLCV data on bar event

Description of Feature Request

First of all, congratz on your work on Blankly. I've also used Freqtrade and Backtrader and even though they provide other capabilities that you don't, you rock on making this framework simple to use.

I'm developing a strategy using Artificial Neural Networks trained using Genetic Algorithms. In here, I need to run multiple back tests in order to calculate the fitness of each individual. For this, I'm using Blankly backtest where I need to initialize an Exchange. I was using Alpaca but during initialization and execution, it performs some HTTPS calls that slows down the training (even if they are just a few). I switched to KeyLess exchange but I noticed that it mocks the OHCLV data (reference).

Would be nice to have this Exchange (or maybe another exchange) to provide all the OHCLV data. Maybe one exchange that requires a directory with multiple CSV files, one per Symbol (maybe similar to the ones generated by the backtests on the price_cache folder).

Why is it important?

Maybe other people needs this feature to perform offline trainings and backtests using the full OHCLV data. Just as a silly example, I want to code while I'm flying without internet connectivity and be able to tests my strategy which uses volume indicators.

How would you use the feature?

I would like to provide a folder that will contains all the CSV files that can be consumed by this Exchange. For instance:

if __name__ == "__main__":
    exchange = blankly.CSVExchange(folder='price_caches', initial_account_values={'USD': 100000})
    strategy = blankly.Strategy(exchange)
    strategy.add_bar_event(self.bar_event, 'AAPL', resolution='1d', init=self.init)

    strategy.backtest(start_date="20/10/2021", end_date="01/03/2022")

In here, the folder might have the following files:

  • AAPL,1614793059,1646329059,86400.csv
  • TSLA,1634688000,1641168000,1800.csv

How would you implement this?
As mentioned, I'll probably do something quite similar to the KeylessExchange but instead of providing a file, I'll read each file on a folder looking for CSV files that follows the cached prices format and in their header have the high,low,volume,close,open,time. Then I'll somehow do a lazy load of those files (depending the symbols and date range that is needed)

Would you like to Contribute?

  • Yes, I would love to contribute to this feature request ๐ŸŽ‰
  • No, but please keep me posted ๐ŸŽ‰

Additional Information

symbol not found

Description

whatever I do, I can't get past the "symbol not found" error.

Standard RSI strategy

import blankly
from blankly import StrategyState


def price_event(price, symbol, state: StrategyState):
    """ This function will give an updated price every 15 seconds from our definition below """
    state.variables['history'].append(price)
    rsi = blankly.indicators.rsi(state.variables['history'])
    if rsi[-1] < 30:
        # Dollar cost average buy
        state.interface.market_order(symbol, side='buy', funds=10)
    elif rsi[-1] > 70:
        # Dollar cost average sell
        state.interface.market_order(symbol, side='sell', funds=10)


def init(symbol, state: StrategyState):
    # Download price data to give context to the algo
    state.variables['history'] = state.interface.history(symbol, start_date = "2021-08-01", end_date = "2021-08-28", return_as='list')['close']


if __name__ == "__main__":
    binance = blankly.Binance(portfolio_name="ATD Binance")
    binance_strategy = blankly.Strategy(binance)
    binance_strategy.add_price_event(price_event, symbol='ETH-USDT', resolution='1h', init=init)

    # Start the strategy. This will begin each of the price event ticks
    # binance_strategy.start()
    # Or backtest using this
    binance_strategy.backtest(start_date = "2021-08-01", end_date = "2021-08-28",
                              initial_values={'USDT': 100})

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": false,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD"
    }
  }
}

backtest.json

{
  "price_data": {
    "assets": [
      [ "ETH-USDT", 1546290000, 1630270800, 300.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 900.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 1800.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 3600.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 14400.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 21600.0 ],
      [ "ETH-USDT", 1546290000, 1630270800, 86400.0 ]
    ]
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": false
  }
}

Error in Pyzo Editor

Including: ETH-USDT,1546290000,1630270800,300.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,900.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,1800.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,3600.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,14400.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,21600.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,86400.0.csv in backtest.
Including: ETH-USDT,1627776000.0,1630108800.0,3600.0.csv in backtest.
Including: ETH-USDT,1598774943.8658986,1630310943.8658986,3600.0.csv in backtest.
No exact cache exists for ETH-USDT from 1630310996.5534158 to 1630311056.5534158 at 3600.0s resolution. Downloading...
already identified

Initializing...
1630098000.0
Traceback (most recent call last):
  File "C:\Blankly\RSI-strategy.py", line 30, in <module>
    binance_strategy.backtest(start_date = "2021-08-01", end_date = "2021-08-28",
  File "c:\python39\lib\site-packages\blankly\strategy\strategy_base.py", line 473, in backtest
    results = self.backtesting_controller.run()
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 397, in run
    available_dict, no_trade_dict = self.format_account_data(self.initial_time)
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 221, in format_account_data
    true_account[i] = self.interface.get_account(i)
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 298, in get_account
    raise KeyError("Symbol not found.")
KeyError: 'Symbol not found.'

Error in CMD

C:\Blankly>RSI-strategy.py
Including: ETH-USDT,1546290000,1630270800,300.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,900.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,1800.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,3600.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,14400.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,21600.0.csv in backtest.
Including: ETH-USDT,1546290000,1630270800,86400.0.csv in backtest.
Including: ETH-USDT,1627776000.0,1630108800.0,3600.0.csv in backtest.

Initializing...
1630098000.0
Traceback (most recent call last):
  File "C:\Python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 296, in get_account
    return trade_local.get_account(symbol)
  File "C:\Python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\local_account\trade_local.py", line 125, in get_account
    return copy.deepcopy(utils.AttributeDict(local_account.account[asset_id]))
KeyError: 'USD'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Blankly\RSI-strategy.py", line 30, in <module>
    binance_strategy.backtest(start_date = "2021-08-01", end_date = "2021-08-28",
  File "C:\Python39\lib\site-packages\blankly\strategy\strategy_base.py", line 473, in backtest
    results = self.backtesting_controller.run()
  File "C:\Python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 397, in run
    available_dict, no_trade_dict = self.format_account_data(self.initial_time)
  File "C:\Python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 221, in format_account_data
    true_account[i] = self.interface.get_account(i)
  File "C:\Python39\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 298, in get_account
    raise KeyError("Symbol not found.")
KeyError: 'Symbol not found.'

Platform Info

  • Python version: 3.9.6
  • Platform: Win10

Try toggling the 'use_sandbox' setting in your settings.json or check if the keys were input correctly into your keys.json.

Description

I am trying to run sample code from docs using Alpaca paper trading account:

from blankly import Strategy, StrategyState, Interface
from blankly import Alpaca
from blankly.indicators import rsi


def init(symbol, state: StrategyState):
    # Download price data to give context to the algo
    state.variables['history'] = state.interface.history(symbol, to='1y', return_as='list')['close']

def price_event(price, symbol, state: StrategyState):
    # we'll come back to this soon
    print("New price event: " + str(price) + ".")
    pass


alpaca = Alpaca()
s = Strategy(alpaca)
s.add_price_event(price_event, 'MSFT', resolution='1m', init=init)
s.start()

but I a getting an error below.

settings.json

{
    "settings": {
      "account_update_time": 5000,
      "use_sandbox": false,
      "use_sandbox_websockets": false,
      "websocket_buffer_size": 10000,
      "test_connectivity_on_auth": true,
  
      "coinbase_pro": {
        "cash": "USD"
      },
      "binance": {
        "cash": "USDT",
        "binance_tld": "us"
      },
      "alpaca": {
        "websocket_stream": "iex",
        "cash": "USD"
      }
    }
  }

Error (if applicable)

INFO: No portfolio name to load specified, defaulting to the first in the file: (another cool portfolio). This is fine if there is only one portfolio in use.
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\alpaca_trade_api\rest.py", line 208, in _one_request
    resp.raise_for_status()
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\requests\models.py", line 953, in raise_for_status  
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://api.alpaca.markets/v2/account        

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\alpaca\alpaca_interface.py", line 48, in init_exchange
    account_info = self.calls.get_account()
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\alpaca_trade_api\rest.py", line 246, in get_account
    resp = self.get('/account')
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\alpaca_trade_api\rest.py", line 224, in get
    return self._request('GET', path, data)
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\alpaca_trade_api\rest.py", line 187, in _request
    return self._one_request(method, url, opts, retry)
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\alpaca_trade_api\rest.py", line 216, in _one_request
    raise APIError(error, http_error)
alpaca_trade_api.rest.APIError: request is not authorized

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Mislav\Documents\blankly_projects\intro.py", line 16, in <module>
    alpaca = Alpaca()
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\alpaca\alpaca.py", line 26, in __init__
    Exchange.__init__(self, 'alpaca', portfolio_name, keys_path, settings_path)
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\exchange.py", line 41, in __init__
    self.calls, self.interface = self.__direct_calls_factory.create(self.__type, self.__auth, preferences_path)
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\direct_calls_factory.py", line 57, in create
    return calls, AlpacaInterface(calls, preferences_path)
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\alpaca\alpaca_interface.py", line 43, in __init__
    super().__init__('alpaca', authenticated_API, preferences_path, valid_resolutions=[60, 60*5, 60*15, 60*60*24])
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\exchange_interface.py", line 49, in __init__
    self.init_exchange()
  File "C:\ProgramData\Anaconda3\envs\blankly\lib\site-packages\blankly\exchanges\interfaces\alpaca\alpaca_interface.py", line 50, in init_exchange
    raise APIException(e.__str__() + ". Are you trying to use your normal exchange keys "
blankly.utils.exceptions.APIException: request is not authorized. Are you trying to use your normal exchange keys while in sandbox mode?
Try toggling the 'use_sandbox' setting in your settings.json or check if the keys were input correctly into your keys.json.

Platform Info

  • Python version: 3.9
  • Platform: Windows

Struggling on building from source

Description

I'm trying to build de package from source using python3 setup.py install but I'm getting some problems....
First it did not find Numpy on my virtual environment, so I installed it. Then it could not find Cython. Installed it again.

Now it gives me an error message when running the setup.

Traceback (most recent call last):
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 152, in save_modules
    yield saved
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 193, in setup_context
    yield
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 254, in run_setup
    _execfile(setup_script, ns)
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 43, in _execfile
    exec(code, globals, locals)
  File "/var/folders/q8/6drbgp5d02zg2z91trgq193c0000gn/T/easy_install-vjxyib74/pandas-1.4.0/setup.py", line 651, in <module>
  File "/var/folders/q8/6drbgp5d02zg2z91trgq193c0000gn/T/easy_install-vjxyib74/pandas-1.4.0/setup.py", line 424, in maybe_cythonize
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/Cython/Build/Dependencies.py", line 1079, in cythonize
    pool = multiprocessing.Pool(
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 119, in Pool
    return Pool(processes, initializer, initargs, maxtasksperchild,
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/pool.py", line 212, in __init__
    self._repopulate_pool()
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/pool.py", line 303, in _repopulate_pool
    return self._repopulate_pool_static(self._ctx, self.Process,
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/pool.py", line 326, in _repopulate_pool_static
    w.start()
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/context.py", line 284, in _Popen
    return Popen(process_obj)
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 32, in __init__
    super().__init__(process_obj)
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_fork.py", line 19, in __init__
    self._launch(process_obj)
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/popen_spawn_posix.py", line 61, in _launch
    with open(parent_w, 'wb', closefd=False) as f:
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 421, in _open
    if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
  File "/Users/vitoreller/Desktop/Projects/open-repos/Blankly/venv/lib/python3.9/site-packages/setuptools/sandbox.py", line 432, in _ok
    realpath = os.path.normcase(os.path.realpath(path))
  File "/usr/local/Cellar/[email protected]/3.9.7_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/posixpath.py", line 391, in realpath
    filename = os.fspath(filename)
TypeError: expected str, bytes or os.PathLike object, not int

Platform Info

  • Python version: 3.9.1
  • Platform: MacOS

Additional context
From my experiences on other open-source repositories, I would say that you need a requirements.txt file on root, so that users can install it in an easier way when trying to build from source.

Btw, I want to build from source so I can see if I can contribute to your repository! Really loved it!

Run Signals without Need for notify.json

Description

When I'm running a Signal similar to the example RSI Screener, it requires me to have a notify.json even if I don't actually want to send an email with a local SMTP server. Is there a way to not require this and simply output the formatted results to the terminal?

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true,
      "use_yfinance": true
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

Error (if applicable)

Traceback (most recent call last):
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/utils/utils.py", line 153, in load
    preferences = load_json_file(self.__default_path)
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/utils/utils.py", line 100, in load_json_file
    f = open(override_path, )
FileNotFoundError: [Errno 2] No such file or directory: './notify.json'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/weigfan/code/blankly/blankly-package/examples/rsi_screener.py", line 25, in <module>
    signal.notify()
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/frameworks/signal/signal.py", line 148, in notify
    blankly.reporter.email(use_str)
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/deployment/reporter_headers.py", line 172, in email
    self.__send_email(email)
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/deployment/reporter_headers.py", line 148, in __send_email
    notify_preferences = load_notify_preferences()
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/utils/utils.py", line 200, in load_notify_preferences
    return notify_settings.load(override_path)
  File "/opt/anaconda3/envs/blankly/lib/python3.9/site-packages/blankly/utils/utils.py", line 155, in load
    raise FileNotFoundError(self.__not_found_err)
FileNotFoundError: To send emails locally, make sure a notify.json file is placed in the same folder as the project working directory. This is not necessary when deployed live on blankly cloud.

Platform Info

  • Python version: 3.9.7
  • Platform: Mac OS

Binance - KeyError: 'maxNumOrders'

Description

The code throws an exception - KeyError: 'maxNumOrders' when running backtests on Binance.

I think I have found out the root cause of the issue - Binance might have changed their response file. The code worked after I modified as below. There are other places in the file where filter is fetched in the code using indexes; I feel they need to be modified to fetch data based on the filterType.

binance_interface.py

       # max_orders = int(filters[6]["maxNumOrders"])
        max_orders = int(next(filter for filter in filters if filter["filterType"] == "MAX_NUM_ORDERS")["maxNumOrders"])

Response

[{'filterType': 'PRICE_FILTER', 'minPrice': '0.01000000', 'maxPrice': '1000000.00000000', 'tickSize': '0.01000000'}, 
{'filterType': 'PERCENT_PRICE', 'multiplierUp': '5', 'multiplierDown': '0.2', 'avgPriceMins': 5}, 
{'filterType': 'LOT_SIZE', 'minQty': '0.00001000', 'maxQty': '9000.00000000', 'stepSize': '0.00001000'}, 
{'filterType': 'MIN_NOTIONAL', 'minNotional': '10.00000000', 'applyToMarket': True, 'avgPriceMins': 5}, 
{'filterType': 'ICEBERG_PARTS', 'limit': 10}, 
{'filterType': 'MARKET_LOT_SIZE', 'minQty': '0.00000000', 'maxQty': '148.34869255', 'stepSize': '0.00000000'}, 
{'filterType': 'TRAILING_DELTA', 'minTrailingAboveDelta': 10, 'maxTrailingAboveDelta': 2000, 'minTrailingBelowDelta': 10, 'maxTrailingBelowDelta': 2000}, 
{'filterType': 'MAX_NUM_ORDERS', 'maxNumOrders': 200}, 
{'filterType': 'MAX_NUM_ALGO_ORDERS', 'maxNumAlgoOrders': 5}]

Error

Traceback (most recent call last):
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 771, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/examples/golden_cross.py", line 36, in price_event
    interface.market_order(symbol, 'buy', int(interface.cash/price))
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/paper_trade_interface.py", line 359, in market_order
    market_limits = self.get_order_filter(symbol)
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/paper_trade_interface.py", line 640, in get_order_filter
    self.get_order_filter_cache[symbol] = self.calls.get_order_filter(symbol)
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/binance/binance_interface.py", line 776, in get_order_filter
    max_orders = int(filters[6]["maxNumOrders"])
KeyError: 'maxNumOrders'

Platform Info

  • Python version: 3.9
  • Platform: MacOS on Apple Silicon

`cancel_order` does not move value from hold to available

Description

When using Binance interface in backtesting, calling cancel_order on a sell limit order does not move the value of the asset from hold to available.

settings.json

settings.json here

backtest.json (if applicable)

backtest.json here

Error (if applicable)

Error here

Platform Info

  • Python version:
  • Platform:

Additional context
Add any other context about the problem here.

Is there an OCO order in the framework?

I want to implement OCO orders for Binance but it's not found in the document. Should I implement it manually?

To build it manually, I plan to:

  • Place 3 orders when my strategy decides to take a position.
    • A market order for buy/long or sell/short.
    • A limit order for stop loss.
    • a stop limit order for take profit.
  • Monitor the status of the limit order and stop limit order. If one of them is achieved, cancel the other.

Is there a better way to do this?

Price data cache improvement

Currently, the cached data needs to be networked. Can we configure to turn off networking and use local default data. Because of the country firewall limit. Don't want to keep VPN on

Oh, sorry, maybe networking is to get trade fees? I don't know much about the code here.

Binance Api Connection Error

When i try my test binance api it is work correctly but my real binance account doesnt work! I use my api in wunderbit and other services.
Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) on Windows (64 bits).

GetInfo.py

import blankly
exchange = blankly.Binance(portfolio_name="Blankly Binance")
interface = exchange.get_interface()  # Use the getter
print(interface.get_account())

keys.json

{
    "binance": {
        "Blankly Binance": {
            "API_KEY": "y*********************************************************************",
            "API_SECRET": "yx*********************************************************************"
        },
        "ATD Test": {
            "API_KEY": "x*********************************************************************",
            "API_SECRET": "xx*********************************************************************"
        }
    }
}

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD"
    }
  }
}

Error

>>> (executing file "GetInfo.py")
Traceback (most recent call last):
  File "C:\Blankly\GetInfo.py", line 2, in <module>
    exchange = blankly.Binance(portfolio_name="Blankly Binance")
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\binance\binance.py", line 27, in __init__
    Exchange.__init__(self, "binance", portfolio_name, keys_path, settings_path)
  File "c:\python39\lib\site-packages\blankly\exchanges\exchange.py", line 41, in __init__
    self.calls, self.interface = self.__direct_calls_factory.create(self.__type, self.__auth, preferences_path)
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\direct_calls_factory.py", line 51, in create
    return calls, BinanceInterface(exchange_name, calls)
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\binance\binance_interface.py", line 34, in __init__
    super().__init__(exchange_name, authenticated_API)
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\exchange_interface.py", line 43, in __init__
    self.init_exchange()
  File "c:\python39\lib\site-packages\blankly\exchanges\interfaces\binance\binance_interface.py", line 40, in init_exchange
    raise exceptions.APIException("Invalid API Key, IP, or permissions for action - are you trying "
blankly.utils.exceptions.APIException: Invalid API Key, IP, or permissions for action - are you trying to use your normal exchange keys while in sandbox mode?

[Praise] Can't wait for Blankly Platform!

Not a bug, not a feature request - just a quick note saying that I can't wait for Blankly Platform early access and wanted to applaud the entire team for their outstanding work! Keep it up!

About backtest output data

It has nothing to do with the number of indicators, mainly to discuss data related issues.

Blankly Metrics:
cagr: 0.05496190946315571
cum_returns: 0.10125999999999999
sortino: 0.015965089446707815
sharpe: 0.041373845807941685
calmar: 5.896585739465047e-50
volatility: 0.6150518619211591
variance: 0.37828879285268463
var: 0.06339999993087986
cvar: 41.08448129311799
max_drawdown: -4.315592692015137e+47

There are two problems at present.

  1. Numbers are confusing. Should we use appropriate units or precision? Such as:
cagr: 5.496% (using a full name may be better.  Compound Annual Growth Rate)
cum_returns: 10.126%

the jeese report as a reference

 Total Closed Trades             |                                221
 Total Net Profit                |            1,699,245.56 (1699.25%)
 Starting => Finishing Balance   |            100,000 => 1,799,245.56
 Total Open Trades               |                                  0
 Open PL                         |                                  0
 Total Paid Fees                 |                         331,480.93
 Max Drawdown                    |                            -22.42%
 Annual Return                   |                             80.09%
 Expectancy                      |                   7,688.89 (7.69%)
 Avg Win | Avg Loss              |                 31,021.9 | 8,951.7
 Ratio Avg Win / Avg Loss        |                               3.47
 Percent Profitable              |                                42%
 Longs | Shorts                  |                          60% | 40%
 Avg Holding Time                | 3.0 days, 22.0 hours, 50.0 minutes
 Winning Trades Avg Holding Time |  6.0 days, 14.0 hours, 9.0 minutes
 Losing Trades Avg Holding Time  |   2.0 days, 1.0 hour, 41.0 minutes
 Sharpe Ratio                    |                               1.88
 Calmar Ratio                    |                               3.57
 Sortino Ratio                   |                               3.51
 Omega Ratio                     |                               1.49
 Winning Streak                  |                                  5
 Losing Streak                   |                                 10
 Largest Winning Trade           |                         205,575.89
 Largest Losing Trade            |                         -50,827.92
 Total Winning Trades            |                                 92
 Total Losing Trades             |                                129
  1. There seems to be a problem with the calculation of the max drawdown.
max_drawdown: -4.315592692015137e+47

Binance api connection problem

Hello,
The following error pops up when I try to test one of the examples with the Binance interface:

INFO: No portfolio name to load specified, defaulting to the first in the file: (Pompom). This is fine if there is only one portfolio in use. Invalid API Key, IP, or permissions for action - are you trying to use your normal exchange keys while in sandbox mode? Try toggling the 'use_sandbox' setting in your settings.json or check if the keys were input correctly into your keys.json.
Any idea what it could be?

My api keys appear to be correct, because I get my account information using the following code (python-binance package):

from binance.client import Client

client = Client(keys['binance']["project"]["API_KEY"],
                keys['binance']["project"]["API_SECRET"],
                )

print(client.get_account())

If I look at the trace, it is on the self.calls.get_account() call, where the code breaks.
My settings file is as follows:

  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true, (I also tried it with this as false, but then it suggests to put it to true)
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

Thanks in advance!

numpy.ndarray size changed, may indicate binary incompatibility.

Description

I'm trying to work with google colab, but I get the error "ValueError: numpy.ndarray size changed, may indicate binary incompatibility. Expected 88 from C header, got 80 from PyObject".

!pip install --upgrade numpy==1.19.2
I tried the numpy version with the above, still didn't work.
But it's very interesting, I got it working yesterday after repeating it a few times.
colab

Platform Info

Additional context
I'll open a separate thread for windows :(
blankly.utils.exceptions.APIException: Invalid API Key, IP, or permissions for action - are you trying to use your normal exchange keys while in sandbox mode?

Third party signal support

Description of Feature Request

Support third party signal. Currently everything is soley based on market data

Why is it important?

Adds more variety of trading strategies like integrating with newsfeed, sentiment scores, etc.

How would you use the feature?

example:

add_price_event(data_handler, symbol, ...)

OPTIONAL: How would you implement this?

Give us some of your initial thoughts for implementing this.

Would you like to Contribute?

  • [ x] Yes, I would love to contribute to this feature request ๐ŸŽ‰
  • No, but please keep me posted ๐ŸŽ‰

Additional Information
Add any other context about the feature request here including what other packages have done, linked issues, and more.

Coinbase Pro backtest not running

Description

  1. INFO: "enable_shorting" not specified in preferences, defaulting to: "True"
    Where do I specify it to be "false" or "true"?
  2. KeyError: 'maker_fee_rate'
    API keys look right but not sure. Am seeing the following object for "fees" retrieved from CoinbasePro:
    image

My Code:

coinbase = CoinbasePro()
s = Strategy(coinbase)
s.add_price_event(price_event, 'BTC-USD', resolution='30m', init=init)
result = s.backtest(initial_values={'USD': 10000}, to='2y')

settings.json

    "settings": {
      "account_update_time": 5000,
      "use_sandbox": false,
      "use_sandbox_websockets": false,
      "websocket_buffer_size": 10000,
      "test_connectivity_on_auth": true,
  
      "coinbase_pro": {
        "cash": "USD"
      },
      "binance": {
        "cash": "USDT",
        "binance_tld": "us"
      },
      "alpaca": {
        "websocket_stream": "iex",
        "cash": "USD"
      },
      "oanda": {
        "cash": "USD"
      }
    }
  }

backtest.json (if applicable)

backtest.json here

Error (if applicable)

Traceback (most recent call last):
  File "H:\ProgramData\Anaconda3\lib\runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "H:\ProgramData\Anaconda3\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "c:\Users\xxxxxxxxxx\.vscode\extensions\ms-python.python-2021.12.1559732655\pythonFiles\lib\python\debugpy\__main__.py", line 45, in <module>
    cli.main()
  File "c:\Users\xxxxxxxxxx\.vscode\extensions\ms-python.python-2021.12.1559732655\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 444, in main
    run()
  File "c:\Users\xxxxxxxxxx\.vscode\extensions\ms-python.python-2021.12.1559732655\pythonFiles\lib\python\debugpy/..\debugpy\server\cli.py", line 285, in run_file
    runpy.run_path(target_as_str, run_name=compat.force_str("__main__"))
  File "H:\ProgramData\Anaconda3\lib\runpy.py", line 265, in run_path
    return _run_module_code(code, init_globals, run_name,
  File "H:\ProgramData\Anaconda3\lib\runpy.py", line 97, in _run_module_code
    _run_code(code, mod_globals, init_globals,
  File "H:\ProgramData\Anaconda3\lib\runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "c:\git\crypto-test\src\macd\macd.py", line 57, in <module>
    s = Strategy(coinbase)
  File "C:\Users\xxxxxxxxxx\AppData\Roaming\Python\Python38\site-packages\blankly\frameworks\strategy\strategy_base.py", line 64, in __init__
    self.__paper_trade_exchange = blankly.PaperTrade(self.__exchange)
  File "C:\Users\xxxxxxxxxx\AppData\Roaming\Python\Python38\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade.py", line 28, in __init__
    self.interface = PaperTradeInterface(authenticated_exchange.get_interface(),
  File "C:\Users\xxxxxxxxxx\AppData\Roaming\Python\Python38\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 56, in __init__
    ExchangeInterface.__init__(self, derived_interface.get_exchange_type(), derived_interface)
  File "C:\Users\xxxxxxxxxx\AppData\Roaming\Python\Python38\site-packages\blankly\exchanges\interfaces\exchange_interface.py", line 135, in __init__
    self.init_exchange()
  File "C:\Users\xxxxxxxxxx\AppData\Roaming\Python\Python38\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 131, in init_exchange
    "maker_fee_rate": fees['maker_fee_rate'],
KeyError: 'maker_fee_rate'

Platform Info

  • Python version: Python 3.8.8 64-bit ("base", conda)
  • Platform: Windows 10

Additional context
Add any other context about the problem here.

`state.interface.history` should return history at state time during backtest

Description

state.interface.history should return history at state time during backtest.
At the moment, when called in an init function at the beginning of the backtest, it retrieves history relative to wall time.

Example:

The backtest goes from 2020-01-01 to 2020-12-31. When retrieving 5 days of history I'd expect it to get 2019-12-26 to 2019-12-31.

Instead it gets the last 5 days relative to the day when it is run, e.g., 2021-10-24 - 2021-10-29.

As a workaround I'm using something like this now:

def get_historic_candles_at_state_time(state, symbol, ncandles, resolution):
    current_time = state.time
    start = current_time - ncandles * time_interval_to_seconds(resolution)
    end = current_time
    history = state.interface.history(
        symbol, start_date=start, end_date=end, resolution=resolution
    )
    return history

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": false,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "EUR",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD"
    }
  }
}

backtest.json (if applicable)

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": false,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./data/price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": false
  }

Platform Info

  • Python version: 3.9
  • Platform: Ubuntu 21.10

Example scripts error

Description

As in the title. Provided examples throw errors during execution
Tested files: bot.py (generated by 'blankly init' command), examples/rsi.py, examples/mlp_model.py

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": true,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,
    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD"
    }
  }
}

backtest.json

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": false,
    "risk_free_return_rate": 0.0
  }
}

Error

rsi.py

Traceback (most recent call last):
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 692, in run
    raise e
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 685, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/home/jakub/PycharmProjects/bot/blankly/rsi.py", line 7, in price_event
    rsi = blankly.indicators.rsi(state.variables['history'])
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/indicators/oscillators.py", line 33, in rsi
    rsi_values = ti.rsi(data, period)
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/tulipy/__init__.py", line 1003, in rsi
    return lib.rsi([real], [period])
  File "tulipy/lib/__init__.pyx", line 105, in tulipy.lib._Indicator.__call__
tulipy.lib.InvalidOptionError

mlp_model.py

Traceback (most recent call last):
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 692, in run
    raise e
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 685, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/home/jakub/PycharmProjects/bot/blankly/mlp_model.py", line 38, in price_event
    rsi_values = rsi(variables['history'], period=14).reshape(-1, 1)
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/indicators/oscillators.py", line 33, in rsi
    rsi_values = ti.rsi(data, period)
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/tulipy/__init__.py", line 1003, in rsi
    return lib.rsi([real], [period])
  File "tulipy/lib/__init__.pyx", line 105, in tulipy.lib._Indicator.__call__
tulipy.lib.InvalidOptionError

bot.py

Traceback (most recent call last):
  File "/home/jakub/PycharmProjects/bot/blankly2/bot.py", line 39, in <module>
    results = strategy.backtest(to='1y', initial_values={'USDT': 10000})
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/frameworks/strategy/strategy_base.py", line 495, in backtest
    results = self.backtesting_controller.run()
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 543, in run
    self.interface.receive_price(k, v[use_price].iloc[0])
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/pandas/core/indexing.py", line 879, in __getitem__
    return self._getitem_axis(maybe_callable, axis=axis)
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/pandas/core/indexing.py", line 1496, in _getitem_axis
    self._validate_integer(key, axis)
  File "/home/jakub/anaconda3/envs/cbot/lib/python3.9/site-packages/pandas/core/indexing.py", line 1437, in _validate_integer
    raise IndexError("single positional indexer is out-of-bounds")
IndexError: single positional indexer is out-of-bounds

Platform Info

  • Python version: 3.9 Anaconda based
  • Platform: Ubuntu 20.04.3 LTS

Additional context
Library installed as proposed in https://github.com/Blankly-Finance/Blankly#quickstart

All example scripts and json files were modified to use sandbox account and API for Binance, also USD was changed to USDT in symbols and portfolio.

TypeError: cannot concatenate object of type '<class 'numpy.float64'>'

Description

A clear and concise description of what the bug is.

TypeError: cannot concatenate object of type '<class 'numpy.float64'>'; only Series and DataFrame objs are valid

My first time looking into Blankly.

My process is basically:

$ python3 -m venv .venv
$ source ./.venv/bin/activate
$ python3 -m pip install --upgrade blankly
$ blankly init
$ python3 example.py

where example.py is the C&Pd code from https://docs.blankly.finance/examples/golden-cross/:

from blankly import Alpaca, Interface, Strategy, StrategyState
from blankly.indicators import sma


def init(symbol, state: StrategyState):
    interface: Interface = state.interface
    resolution: float = state.resolution
    variables = state.variables
    # initialize the historical data
    variables["history"] = interface.history(symbol, 800, resolution)["close"]
    variables["has_bought"] = False


def price_event(price, symbol, state: StrategyState):
    interface: Interface = state.interface
    # allow the resolution to be any resolution: 15m, 30m, 1d, etc.
    resolution: float = state.resolution
    variables = state.variables

    variables["history"].append(price)

    sma50 = sma(variables["history"], period=50)
    sma200 = sma(variables["history"], period=200)
    diff = sma200 - sma50
    slope_sma50 = (
        sma50[-1] - sma50[-5]
    ) / 5  # get the slope of the last 5 SMA50 Data Points
    prev_diff = diff[-2]
    curr_diff = diff[-1]
    is_cross_up = slope_sma50 > 0 and curr_diff >= 0 and prev_diff < 0
    is_cross_down = slope_sma50 < 0 and curr_diff <= 0 and prev_diff > 0
    # comparing prev diff with current diff will show a cross
    if is_cross_up and not variables["has_bought"]:
        interface.market_order("buy", symbol, int(interface.cash / price))
        variables["has_bought"] = True
    elif is_cross_down and variables["has_bought"]:
        interface.market_order(
            "sell", symbol, int(interface.account[symbol].available)
        )
        variables["has_bought"] = False


alpaca = Alpaca()
s = Strategy(alpaca)
s.add_price_event(price_event, "MSFT", resolution="1d", init=init)
s.backtest(initial_values={"USD": 10000}, to="2y")

In response, I get numerous rows of the error:

TypeError: cannot concatenate object of type '<class 'numpy.float64'>'; only Series and DataFrame objs are valid

The plot then opens in my browser and is flat -- looks like no data.

My only change to settings.json is setting use_sandbox to true for alpaca paper trading.

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,
    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD"
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

Backtest.json is unchanged from default:

backtest.json (if applicable)

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USD",
    "ignore_user_exceptions": true,
    "risk_free_return_rate": 0.0
  }
}

Error (if applicable)

TypeError: cannot concatenate object of type '<class 'numpy.float64'>'; only Series and DataFrame objs are valid

Platform Info

  • Python version: python3.10.1
  • Platform: MacOS, Apple Silicon, Mojave 12.1

Code often freezes when paper trading

Hi!
Around 50% of the time when starting a bot it will freeze up when using the broker interface and wait for thread lock. When killing the script i get

Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 1281, in _shutdown
    t.join()
  File "/usr/lib/python3.7/threading.py", line 1032, in join
    self._wait_for_tstate_lock()
  File "/usr/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
    elif lock.acquire(block, timeout):
KeyboardInterrupt

restarting the script often solves the problem.

Document needs correction

https://docs.blankly.finance/core/strategy

Because of this API interface.market_order change.

There are several areas that need to be changed, and the following is one of them.

from blankly import Strategy, StrategyState, Alpaca
import blankly


def init(symbol: str, state: StrategyState):
    variables = state.variables  # grab the variables dictionary
    # initialize any variables here
    variables['has_bought'] = True
    # get 50 data points at specific resolution of event
    variables['history'] = state.interface.history(symbol, 50, state.resolution)['close'].to_list()


def price_event(price, symbol, state: StrategyState):
    """ This buys and sells whenever the boolean is true or false """
    interface: blankly.Interface = state.interface
    state.variables['history'].append(price)  # add new price to history
    # buy the symbol using available cash
    if not state.variables['has_bought']:
        interface.market_order(symbol, 'buy', blankly.trunc(interface.cash, 2))
        state.variables['has_bought'] = True
    else:
        interface.market_order(symbol, 'sell', blankly.trunc(interface.account[state.base_asset]['available'], 2))
        state.variables['has_bought'] = False


a = Alpaca()
s = Strategy(a)
s.add_price_event(price_event, 'MSFT', resolution='15m', init=init)

s.start()

interface.market_order(symbol, 'buy', blankly.trunc(interface.cash, 2)) --> interface.market_order(symbol, 'buy', blankly.trunc(interface.cash / price, 2))

[Feature] Arbitrage Events within an Exchange

Description

One of the main use cases of trading models is for arbitrage events that rely on the simultaneous reading of two data streams. Say for example that you want to trade Coke (NYSE:KO) and Pepsi (NYSE:PEP), we want to be able to read from both and generate a synthetic price from each.

What would this look like?

I would think that within a Strategy we could add an add_arbitrage_event(event_handler, <ticker_1>, <ticker_2>, resolution) that uses the same interface to get the price data simultaneously.

Full Use Case Example

from blankly import Alpaca, Strategy

def mean_reversion(price_symbol_1, price_symbol_2, state):
    ratio = price_symbol_2 / price_symbol_1
    if ratio > 1:
        # do something 
    else:
        # do something else

a = Alpaca()
s = Strategy(a)
s.add_arbitrage_event(mean_reversion, 'MSFT', 'AAPL', '15m')
s.backtest(to='2y')

Oanda Pycharm Forex errors

Description

Great job. Keep getting errors while using example rsi files.

RSI.py

from blankly import Oanda, Strategy, StrategyState
#from blankly.utils import trunc
from blankly.indicators import rsi

def init(symbol, state: StrategyState):
state.variables['history'] = state.interface.history(symbol, 150, resolution=state.resolution, return_as='deque')['close']
state.variables['own_position'] = False

def price_event(price, symbol, state: StrategyState):
state.variables['history'].append(price)
rsi_output = rsi(state.variables['history'])
print(rsi_output)
if rsi_output[-1] < 30 and not state.variables['own_position']:
qty = int(state.interface.cash / price)
state.interface.market_order(symbol, side='buy', size=qty)
state.variables['own_position'] = True
elif rsi_output[-1] > 70 and state.variables['own_position']:
qty = int(state.interface.account[symbol].available)
state.interface.market_order(symbol, side='sell', size=qty)
state.variables['own_position'] = False

exchange = Oanda()
s = Strategy(exchange)
s.add_price_event(price_event, symbol='EUR-USD', resolution='15m', init=init)
results = s.backtest(to='1M')
print(results)

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

backtest.json (if applicable)

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USD",
    "ignore_user_exceptions": true,
    "risk_free_return_rate": 0.0
  }
}

Error (if applicable)

#Error with int
Traceback (most recent call last):
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 769, in run
    'state_object'])
  File "C:/../blankly/rsi_bot_old.py", line 20, in price_event
    qty = int(state.interface.account[symbol].available)
KeyError: 'EUR-USD'

#Error with trunc with 2 decimals
Traceback (most recent call last):
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 769, in run
    'state_object'])
  File "C:/../blankly/rsi_bot_old.py", line 17, in price_event
    state.interface.market_order(symbol, side='buy', size=qty)
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 378, in market_order
    ". Try using blankly.trunc(size, decimal_number) to match the exchange resolution.")
blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 1. You specified 88357.95. Try using blankly.trunc(size, decimal_number) to match the exchange resolution.
Traceback (most recent call last):
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 769, in run
    'state_object'])
  File "C:/../blankly/rsi_bot_old.py", line 17, in price_event
    state.interface.market_order(symbol, side='buy', size=qty)
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 378, in market_order
    ". Try using blankly.trunc(size, decimal_number) to match the exchange resolution.")
blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 1. You specified 88361.07. Try using blankly.trunc(size, decimal_number) to match the exchange resolution.
Traceback (most recent call last):
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 769, in run
    'state_object'])
  File "C:/../blankly/rsi_bot_old.py", line 17, in price_event
    state.interface.market_order(symbol, side='buy', size=qty)
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 378, in market_order
    ". Try using blankly.trunc(size, decimal_number) to match the exchange resolution.")
blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 1. You specified 88390.75. Try using blankly.trunc(size, decimal_number) to match the exchange resolution.
Traceback (most recent call last):
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 769, in run
    'state_object'])
  File "C:/../blankly/rsi_bot_old.py", line 17, in price_event
    state.interface.market_order(symbol, side='buy', size=qty)
  File "C:\..\blankly\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\paper_trade_interface.py", line 378, in market_order
    ". Try using blankly.trunc(size, decimal_number) to match the exchange resolution.")
blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 1. You specified 88397.79. Try using blankly.trunc(size, decimal_number) to match the exchange resolution.
...

C:\..\blankly\venv\lib\site-packages\blankly\metrics\portfolio.py:55: RuntimeWarning: invalid value encountered in double_scalars
  return mean / std
C:\..\blankly\venv\lib\site-packages\blankly\metrics\portfolio.py:60: RuntimeWarning: invalid value encountered in double_scalars
  return return_series.mean() * n / abs(max_drawdown(return_series))

Platform Info

  • Python version: 3.7
  • Platform: PyCharm Community Edition 2021.1.3

Additional context

InvalidOrder: Not enough base currency. Available: 0.02037. hold: 0. requested: 0.02038.

Description

I was backtesting my custom logic (exchange=Binance) but I keep getting the below error frequently. I have seen this error in the example code also. It works fine for some of resolution/duration. It seems to be a floating point/rounding/truncate issue.

Could you please take a look?

Error

Traceback (most recent call last):
  File "/Users/rs/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 771, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/Users/rs/Desktop/ML/GitHub/blankly/examples/rolling_window_breakout.py", line 84, in price_event
    interface.market_order(symbol, 'sell', state.interface.account[state.base_asset].available)
  File "/Users/rs/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/paper_trade_interface.py", line 391, in market_order
    self.local_account.test_trade(symbol, side, qty, price, market_limits['market_order']["quote_increment"],
  File "/Users/rs/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/local_account/trade_local.py", line 192, in test_trade
    raise InvalidOrder("Not enough base currency. Available: " +
blankly.utils.exceptions.InvalidOrder: Not enough base currency. Available: 0.02037. hold: 0. requested: 0.02038.

Code

import blankly
from blankly import Strategy, StrategyState, Interface
from blankly import Binance
from blankly.indicators import sma
#import math

my_stop_loss = 0
my_target = 0

my_winning_trades =0
my_losing_trades = 0

my_stop_loss_percent = 1
my_target_percent = 1.5


def init(symbol, state: StrategyState):
    interface: Interface = state.interface
    resolution = state.resolution
    variables = state.variables
    # initialize the historical data
    variables['history'] = interface.history(symbol, 25, resolution, end_date=state.time, return_as='deque')['close']
    variables['owns_position'] = False


def price_event(price, symbol, state: StrategyState):
    interface: Interface = state.interface
    variables = state.variables
    window_max_price = max(variables['history'])
    window_min_price = min(variables['history'])

    action = 'Hold'
    global my_stop_loss
    global my_target
    global my_winning_trades
    global my_losing_trades

    if window_max_price > price > window_min_price:
        action = 'Hold'

    elif price > window_max_price:
        action = 'Buy'

    elif price < window_min_price:
        action = 'Sell'

    # trailing stoploss and target

    if my_stop_loss < price*(100-my_stop_loss_percent)/100:
        my_stop_loss = price*(100-my_stop_loss_percent)/100
        my_target = my_target+ 0.5 * price*(my_target_percent) / 100


    #print(f'{action} --- price:{price},    max:{window_max_price},    min:{window_min_price}')
    variables['history'].append(price)

    # comparing prev diff with current  diff will show a cross
    if action == 'Buy' and not variables['owns_position']:
        interface.market_order(symbol, 'buy', blankly.trunc(interface.cash / price, 5))
        variables['owns_position'] = True
        my_stop_loss = price * (100-my_stop_loss_percent)/100
        my_target = price * (100+my_target_percent)/100
        print(f'My Buy:{price}')
    # Stop Loss Handling
    # elif price < my_stop_loss and variables['owns_position']:
    #     interface.market_order(symbol, 'sell', state.interface.account[state.base_asset].available)
    #     variables['owns_position'] = False
    #     my_stop_loss = 0


    #elif action == 'Sell' and variables['owns_position']:
    elif (price > my_target or price < my_stop_loss) and variables['owns_position']:
        if(price < my_stop_loss):
            print(f'SL Hit:{price}  Target{my_target}   SL {my_stop_loss}')
            my_losing_trades +=1
        elif (price > my_target):
            my_winning_trades +=1
            print(f'Target Hit:{price}  Target{my_target}   SL {my_stop_loss}')
        # use strategy.base_asset if on CoinbasePro or Binance
        # interface.market_order(symbol, 'sell', int(interface.account[symbol].available))
        interface.market_order(symbol, 'sell', state.interface.account[state.base_asset].available)

        variables['owns_position'] = False
        my_stop_loss = 0
        my_target = 0

    # sma200 = sma(variables['history'], period=200)
    # # match up dimensions
    # sma50 = sma(variables['history'], period=50)[-len(sma200):]
    # diff = sma200 - sma50
    # slope_sma50 = (sma50[-1] - sma50[-5]) / 5  # get the slope of the last 5 SMA50 Data Points
    # prev_diff = diff[-2]
    # curr_diff = diff[-1]
    # is_cross_up = slope_sma50 > 0 and curr_diff >= 0 and prev_diff < 0
    # is_cross_down = slope_sma50 < 0 and curr_diff <= 0 and prev_diff > 0
    #
    # # comparing prev diff with current  diff will show a cross
    # if is_cross_up and not variables['owns_position']:
    #     interface.market_order(symbol, 'buy', blankly.trunc(interface.cash/price,5))
    #     variables['owns_position'] = True
    # elif is_cross_down and variables['owns_position']:
    #     # use strategy.base_asset if on CoinbasePro or Binance
    #     # interface.market_order(symbol, 'sell', int(interface.account[symbol].available))
    #     interface.market_order(symbol, 'sell', state.interface.account[state.base_asset].available)

    #     variables['owns_position'] = False

if __name__ == "__main__":
    ex = Binance()
    s = Strategy(ex)
    s.add_price_event(price_event, 'BTC-USDT', resolution='30m', init=init)
    # s.add_price_event(price_event, 'GME', resolution='1h', init=init)
    results = s.backtest(initial_values={'USDT': 1000}, to='3mo')
    print(results)
    print(f'Win Count:{my_winning_trades}, Loss Count:{my_losing_trades}')
    # Or just run it directly on the exchange
    # s.start()

settings.json

{
  "settings": {
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true,
      "use_yfinance": false
    },
    "oanda": {
      "cash": "USD"
    },
    "keyless": {
      "cash": "USD"
    },
    "kucoin": {
      "cash": "USDT"
    },
    "ftx": {
      "cash": "USD"
    }
  }
}

backtest.json

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": true,
    "risk_free_return_rate": 0.0,
    "benchmark_symbol" : null
  }
}

Platform Info

  • Python version: 3.10
  • Platform: MacOS (M1)

Additional context
Add any other context about the problem here.

[suggestion] check python version in during install

I've been banging my head on a wall on why does backtesting show an error upon finishing:

/blankly/exchanges/interfaces/paper_trade/backtest_controller.py
AttributeError: 'Figure' object has no attribute 'step'

Upgrading to python 3.10.0 solved the issue, I was using 3.8.11.

Maybe this can be checked during installation for convnivence?

Thank you

M1 Mac support?

Description

Unable to pip3 install blankly on an M1 MacBook Big Sur.

Error (if applicable)

Collecting scikit-learn
  Downloading scikit-learn-1.0.1.tar.gz (6.6 MB)
     |โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆ| 6.6 MB 3.8 MB/s 
  Installing build dependencies ... error
  ERROR: Command errored out with exit status 1:
   command: /Library/Frameworks/Python.framework/Versions/3.9/bin/python3.9 /private/var/folders/t9/2fb8pd6s41b0d41lk81pk_700000gn/T/pip-standalone-pip-ata6s47j/__env_pip__.zip/pip install --ignore-installed --no-user --prefix /private/var/folders/t9/2fb8pd6s41b0d41lk81pk_700000gn/T/pip-build-env-gp_u1yzv/overlay --no-warn-script-location --no-binary :none: --only-binary :none: -i https://pypi.org/simple -- setuptools wheel 'Cython>=0.28.5' 'oldest-supported-numpy; python_version!='"'"'3.7'"'"' or platform_machine=='"'"'aarch64'"'"' or platform_system=='"'"'AIX'"'"' or platform_python_implementation == '"'"'PyPy'"'"'' 'numpy==1.14.6; python_version=='"'"'3.7'"'"' and platform_machine!='"'"'aarch64'"'"' and platform_system!='"'"'AIX'"'"' and platform_python_implementation != '"'"'PyPy'"'"'' 'scipy>=1.1.0'
       cwd: None
  Complete output (55279 lines):
  Ignoring numpy: markers 'python_version == "3.7" and platform_machine != "aarch64" and platform_system != "AIX" and platform_python_implementation != "PyPy"' don't match your environment

also

clang: error: the clang compiler does not support 'faltivec', please use -maltivec and include altivec.h explicitly

Platform Info

  • Python version: 3.9.9
  • Platform: MacOS M1

How do backtests account for fees when projecting growth?

It would make no sense to keep selling and buying an instrument if price fluctuations are smaller than the trading fees. Do backtests use the functionality built into MarketOrder and LimitOrder? Do we need to explicitly implement fee impact analysis in our price events?

Binance: Issue in Golden Cross example

Description

While backtesting golden_cross.py example in BInance with asset = BTC-USTD, the code was able buy but not sell.

The code only worked when I modified the below line (as given in rsi.py).

        interface.market_order(symbol, 'sell', int(interface.account[symbol].available))

to

        interface.market_order(symbol, 'sell', state.interface.account[state.base_asset].available)

Could you please check why this is happening?

settings.json

{
  "settings": {
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true,
      "use_yfinance": false
    },
    "oanda": {
      "cash": "USD"
    },
    "keyless": {
      "cash": "USD"
    },
    "kucoin": {
      "cash": "USDT"
    },
    "ftx": {
      "cash": "USD"
    }
  }
}

backtest.json

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": true,
    "risk_free_return_rate": 0.0,
    "benchmark_symbol" : null
  }
}

Error (got the error multiple times - showing two)

Progress: [#####-----] 47.6% Traceback (most recent call last):
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 771, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/Users/rs/Desktop/ML/GitHub/blankly/examples/golden_cross.py", line 40, in price_event
    interface.market_order(symbol, 'sell', int(interface.account[symbol].available))
KeyError: 'BTC-USDT'


Progress: [######----] 55.89% Traceback (most recent call last):
  File "/Users/rs/Desktop/ML/GitHub/blankly/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 771, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "/Users/rohitsathyanathan/Desktop/ML/GitHub/blankly/examples/golden_cross.py", line 40, in price_event
    interface.market_order(symbol, 'sell', int(interface.account[symbol].available))
KeyError: 'BTC-USDT'
Progress: [##########] 100% Done...

Platform Info

  • Python version: 3.10
  • Platform: MacOS (M1)

TypeError: ufunc 'isnan' not supported for the input types

Description

When I try the RSI code on the Google Colab Jupyter, I get the following error.

Error on Colab

TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
INFO: "use_yfinance" not specified in preferences, defaulting to: "False"
No cached data found for ETH-USDT from: 1642515960 to 1642524144 at a resolution of 3600 seconds.

Initializing...

Backtesting...
Progress: [#---------] 14.87% Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 706, in run
    'state_object'])
  File "<ipython-input-3-1733380c8989>", line 10, in price_event
    state.interface.market_order(symbol, side='buy', size=buy)
  File "/usr/local/lib/python3.7/dist-packages/blankly/exchanges/interfaces/paper_trade/paper_trade_interface.py", line 363, in market_order
    raise InvalidOrder(f"Size is too small. Minimum is: {min_size}. You requested {size}.")
blankly.utils.exceptions.InvalidOrder: Size is too small. Minimum is: 0.0001. You requested 0.
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 706, in run
    'state_object'])
  File "<ipython-input-3-1733380c8989>", line 10, in price_event
    state.interface.market_order(symbol, side='buy', size=buy)
  File "/usr/local/lib/python3.7/dist-packages/blankly/exchanges/interfaces/paper_trade/paper_trade_interface.py", line 363, in market_order
    raise InvalidOrder(f"Size is too small. Minimum is: {min_size}. You requested {size}.")
blankly.utils.exceptions.InvalidOrder: Size is too small. Minimum is: 0.0001. You requested 0.
Traceback (most recent call last):
...
/usr/local/lib/python3.7/dist-packages/blankly/metrics/portfolio.py:55: RuntimeWarning: invalid value encountered in double_scalars
  return mean / std
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-1733380c8989> in <module>()
     30   # strategy.start()
     31   # Or backtest using this
---> 32   results = strategy.backtest(to='1y', initial_values={'USDT': 10000})
     33   print(results)

1 frames
/usr/local/lib/python3.7/dist-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py in run(self)
    908         # Lastly remove Nan values in the metrics
    909         for i in metrics_indicators:
--> 910             if np.isnan(metrics_indicators[i]):
    911                 metrics_indicators[i] = None
    912 

TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

chrome_0J9966GLO4

When I try the RSI code on the Windows desktop, I get the following error.

Error on Windows Desktop

c:\python38\lib\site-packages\blankly\metrics\portfolio.py:60: RuntimeWarning: invalid value encountered in double_scalars
  return return_series.mean() * n / abs(max_drawdown(return_series))

pyzo_dTWgsPQ6Cc

ModuleNotFoundError: No module named 'models'

Description

Hello, it seems that the work continues at full speed. An update is required for the line below.

examples/custom_model.py

....
from models import OrderPricingModel, OrderDecisionModel
....

Platform Info

  • Python version: 3.8.10
  • Platform: Windows (64 bits)

How to remove a subscribed ticker?

Description of Feature Request

We can add listener to a ticker stream via

strategy.add_price_event(price_event, symbol='BTC-USD', resolution='1d', init=init)

How to remove a subscribed ticker?

Why is it important?

This would enabled dynamic ticker selection in our strategy

How would you use the feature?

strategy.delete_price_event()

or

strategy.all_subscribed_events.delete(id)

OPTIONAL: How would you implement this?

Give us some of your initial thoughts for implementing this.

Would you like to Contribute?

  • [ x] Yes, I would love to contribute to this feature request ๐ŸŽ‰
  • No, but please keep me posted ๐ŸŽ‰

Additional Information
Add any other context about the feature request here including what other packages have done, linked issues, and more.

Shorting stock not developed?

Hi,

Thank you for the very useful package!
I'm trying to backtest a strategy that involves shorting a stock/crypto. This means I make a call like:
interface.market_order('MSFT', 'sell', 1)
in a price_event function.

This results in an error message such as:
*** blankly.utils.exceptions.InvalidOrder: Not enough base currency. Available: 0.0. hold: 0. requested: 1.

I'd expect to be able to short on Alpaca, as doing a 'sell' market order (e.g. through their web console) with 0 position is successful.

Note: I've gotten the same results with

alpaca = Alpaca()
s = Strategy(alpaca)

and
coinbase = CoinbasePro()
crypto = Strategy(coinbase)

Is this intentional? Thanks so much!

Plotted returns and cumulative returns don't match

Description

Returns in results.metrics["Cumulative Returns (%)"] don't match charted returns in Bokehplot or Account Value (USD) in Account History

Platform Info

  • Using latest blankly package
  • Running backtest over 2d

Additional context
image
image
image

Incorrect syntax highlighting suggestions on default issue template

Description

A clear and concise description of what the bug is.

Your default issue template suggests wrongish syntax highlighting for the codeblocks, which is a little confusing:

For example:

## settings.json
```python
settings.json here
```

Should probably be:

## settings.json
```json
settings.json here
```

Similarly:

## backtest.json (if applicable)
```python
backtest.json here
```

Also

## Error (if applicable)
```python
Error here
```

Should probably be

## Error (if applicable)
```plaintext
Error here
```

Binance backtesting

Description

I have issues when backtesting with Binance api

settings.json

{
  "settings": {
    "use_sandbox": false,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "com"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true,
      "use_yfinance": false
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

backtest.json (if applicable)

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": true,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "USDT",
    "ignore_user_exceptions": true,
    "risk_free_return_rate": 0.0,
    "benchmark_symbol" : null
  }
}

Error (if applicable)

Traceback (most recent call last):
  File "C:\Users\me\PycharmProjects\bot-v2\venv\lib\site-packages\blankly\exchanges\interfaces\paper_trade\backtest_controller.py", line 771, in run
    self.price_events[0]['function'](self.interface.get_price(self.price_events[0]['asset_id']),
  File "C:\Users\me\PycharmProjects\bot-v2\stonks.py", line 20, in price_event
    quantity = int(state.interface.account[symbol].available)
KeyError: 'ALGO-USDT'

Platform Info

  • Python version: 3.9
  • Platform: Windows

Additional context
I copied the code in the youtube tutorial

Size resolution is too high error, can't buy fractional?

Description

Some problem with the order size. Seems like we can't buy fractional coins?

Of course it can easily happen that I misunderstood something, but then a more verbose error would be better

settings.json

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": false,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,
    "coinbase_pro": {
      "cash": "EUR"
    }
  }
}

backtest.json (if applicable)

{
  "price_data": {
    "assets": []
  },
  "settings": {
    "use_price": "close",
    "smooth_prices": false,
    "GUI_output": false,
    "show_tickers_with_zero_delta": false,
    "save_initial_account_value": true,
    "show_progress_during_backtest": true,
    "cache_location": "./price_caches",
    "continuous_caching": true,
    "resample_account_value_for_metrics": "1d",
    "quote_account_value_in": "EUR",
    "ignore_user_exceptions": false
  }
}

Error (if applicable)

blankly.utils.exceptions.InvalidOrder: Size resolution is too high, the highest resolution allowed for this symbol is: 1e-08. You specified 0.008585614459548448.

Traceback (most recent call last):
  File "bot.py", line 36, in <module>
    results = strategy.backtest(to="1y", initial_values={"EUR": 100})
  File "/Users/gabor.vecsei/anaconda3/lib/python3.8/site-packages/blankly/frameworks/strategy/strategy_base.py", line 493, in backtest
    results = self.backtesting_controller.run()
  File "/Users/gabor.vecsei/anaconda3/lib/python3.8/site-packages/blankly/exchanges/interfaces/paper_trade/backtest_controller.py", line 878, in run
    metrics_indicators['Compound Annual Growth Rate (%)'] = metrics.cagr(dataframes)
  File "/Users/gabor.vecsei/anaconda3/lib/python3.8/site-packages/blankly/exchanges/interfaces/paper_trade/metrics.py", line 26, in cagr
    return round(metrics.cagr(account_values['value'].iloc[0], account_values['value'].iloc[-1], years), 2) * 100
  File "/Users/gabor.vecsei/anaconda3/lib/python3.8/site-packages/blankly/metrics/portfolio.py", line 31, in cagr
    return (end_value / start_value) ** (1 / years) - 1
ZeroDivisionError: division by zero

Platform Info

  • Python version: 3.8.5
  • Platform: MacOS

This was the quick test code which produced the error:

import blankly


def price_event(price, symbol, state: blankly.StrategyState):
    state.variables["step_count"] += 1

    if state.variables["step_count"] == 1:
        buy = state.interface.cash / price
        state.interface.market_order(symbol, side="buy", size=buy)

    if state.variables["step_count"] == 30:
        curr_value = state.interface.account[symbol].available
        state.interface.market_order(symbol, side='sell', size=curr_value)


def init(symbol, state: blankly.StrategyState):
    # Download price data to give context to the algo
    state.variables["history"] = state.interface.history(symbol, to=150, return_as="deque")["close"]
    state.variables["owns_position"] = False
    state.variables["step_count"] = 0


if __name__ == "__main__":
    # Authenticate coinbase pro strategy
    exchange = blankly.CoinbasePro(portfolio_name="algotrade_test_portfolio")

    # Use our strategy helper on coinbase pro
    strategy = blankly.Strategy(exchange)

    # Run the price event function every time we check for a new price - by default that is 15 seconds
    strategy.add_price_event(price_event, symbol="BTC-EUR", resolution="1d", init=init)

    # Start the strategy. This will begin each of the price event ticks
    # strategy.start()
    # Or backtest using this
    results = strategy.backtest(to="1y", initial_values={"EUR": 100})
    print(results)

Alpaca paper trading not working

Hi!

I'm trying to paper trade an algorithm with Alpaca. When I fill an order with blankly, it prints Paper Trading... in the terminal, but if i log into my Alpaca account, nothing has happened.

In keys.json I added the API keys to my Alpaca paper account.

my settings are

{
  "settings": {
    "account_update_time": 5000,
    "use_sandbox": true,
    "use_sandbox_websockets": false,
    "websocket_buffer_size": 10000,
    "test_connectivity_on_auth": true,

    "coinbase_pro": {
      "cash": "USD"
    },
    "binance": {
      "cash": "USDT",
      "binance_tld": "us"
    },
    "alpaca": {
      "websocket_stream": "iex",
      "cash": "USD",
      "enable_shorting": true
    },
    "oanda": {
      "cash": "USD"
    }
  }
}

Thanks.

Backtest crashing depending on time-frame

Description

Running a backtest of 1 month with 60 seconds resolution strategy.backtest(to="1M", ...) on Binance XRP-USDT pair crashes when calculating RSI.
24h, 48h and 96h works fine but going up to 168h/1w causes same crash.

Error (if applicable)

  File "F:\Trunk\wine\blankly-test\models\RelativeStrengthIndexDecisionModel.py", line 16, in __call__
    rsi = blankly.indicators.rsi(state.variables["history"], self.period)
  File "C:\Users\ian\AppData\Local\Programs\Python\Python39\lib\site-packages\blankly\indicators\oscillators.py", line 33, in rsi
    rsi_values = ti.rsi(data, period)
  File "C:\Users\ian\AppData\Local\Programs\Python\Python39\lib\site-packages\tulipy\__init__.py", line 1003, in rsi
    return lib.rsi([real], [period])
  File "tulipy\lib\__init__.pyx", line 105, in tulipy.lib._Indicator.__call__
tulipy.lib.InvalidOptionError

Platform Info

  • Python version: 3.9.7 x86-64
  • Platform: Windows 10

Suggested/correct way of calculating stepSize for size truncating when stepSize is 1?

XRP-USDT pair on Binance' LOT_SIZE from https://api.binance.com/api/v1/exchangeInfo:

{
    "filterType": "LOT_SIZE",
    "minQty": "1.00000000",
    "maxQty": "9222449.00000000",
    "stepSize": "1.00000000"
},

Calculating amount of decimals like this:

filter = state.interface.get_order_filter(symbol)
increment = filter["market_order"]["base_increment"]
decimals = abs(Decimal(str(increment)).as_tuple().exponent() # I forget from where; but this is taken from the Blankly source

Decimals is 1 but should be 0 (?)

size = state.interface.cash / price
size = blankly.trunc(size, decimals)

When creating a market buy order Binance returns LOT_SIZE error.

My current solution is wrapping the market buy order in two try/except statements like this:

try:
    state.interface.market_order(symbol, "buy", size)
except:
    try:
        state.interface.market_order(symbol, "buy", blankly.trunc(size, decimals - 1))
    except Exception as e:
        print(e)

Is there a more proper/elegant way of doing this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.