main 695db65f8289 cached
10 files
19.0 KB
4.8k tokens
50 symbols
1 requests
Download .txt
Repository: lindomar-oliveira/backtrader-binance
Branch: main
Commit: 695db65f8289
Files: 10
Total size: 19.0 KB

Directory structure:
gitextract_x0l9znb4/

├── .gitignore
├── LICENSE
├── README.md
├── backtrader_binance/
│   ├── __init__.py
│   ├── binance_broker.py
│   ├── binance_feed.py
│   └── binance_store.py
├── examples/
│   └── live_trade.py
├── requirements.txt
└── setup.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
__pycache__/

.idea/
*.egg-info/

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Lindomar Oliveira

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# backtrader-binance

Create your strategy for the [Backtrader](https://www.backtrader.com), do the backtesting and you will also be ready for live trading on the exchange [Binance](https://www.binance.com/pt-PT/register?ref=D9K8QI13) with this integration.

Installation
============

pip install git+https://github.com/lindomar-oliveira/backtrader-binance.git@main

Usage
=====

See examples folder

Thanks
======

- backtrader: An incredible library...
- [python-binance](https://github.com/sammchardy/python-binance): For creating Binance API wrapper, shortening a lot of work.

License
=======

[MIT](https://choosealicense.com/licenses/mit)


================================================
FILE: backtrader_binance/__init__.py
================================================
from .binance_store import BinanceStore


================================================
FILE: backtrader_binance/binance_broker.py
================================================
import datetime as dt

from collections import defaultdict, deque
from math import copysign

from backtrader.broker import BrokerBase
from backtrader.order import Order, OrderBase
from backtrader.position import Position
from binance.enums import *


class BinanceOrder(OrderBase):
    def __init__(self, owner, data, exectype, binance_order):
        self.owner = owner
        self.data = data
        self.exectype = exectype
        self.ordtype = self.Buy if binance_order['side'] == SIDE_BUY else self.Sell
        
        # Market order price is zero
        if self.exectype == Order.Market:
            self.size = float(binance_order['executedQty'])
            self.price = sum(float(fill['price']) for fill in binance_order['fills']) / len(binance_order['fills'])  # Average price
        else:
            self.size = float(binance_order['origQty'])
            self.price = float(binance_order['price'])
        self.binance_order = binance_order
        
        super(BinanceOrder, self).__init__()
        self.accept()


class BinanceBroker(BrokerBase):
    _ORDER_TYPES = {
        Order.Limit: ORDER_TYPE_LIMIT,
        Order.Market: ORDER_TYPE_MARKET,
        Order.Stop: ORDER_TYPE_STOP_LOSS,
        Order.StopLimit: ORDER_TYPE_STOP_LOSS_LIMIT,
    }

    def __init__(self, store):
        super(BinanceBroker, self).__init__()

        self.notifs = deque()
        self.positions = defaultdict(Position)

        self.open_orders = list()
    
        self._store = store
        self._store.binance_socket.start_user_socket(self._handle_user_socket_message)

    def _execute_order(self, order, date, executed_size, executed_price):
        order.execute(
            date,
            executed_size,
            executed_price,
            0, 0.0, 0.0,
            0, 0.0, 0.0,
            0.0, 0.0,
            0, 0.0)
        pos = self.getposition(order.data, clone=False)
        pos.update(copysign(executed_size, order.size), executed_price)

    def _handle_user_socket_message(self, msg):
        """https://binance-docs.github.io/apidocs/spot/en/#payload-order-update"""
        if msg['e'] == 'executionReport':
            if msg['s'] == self._store.symbol:
                for o in self.open_orders:
                    if o.binance_order['orderId'] == msg['i']:
                        if msg['X'] in [ORDER_STATUS_FILLED, ORDER_STATUS_PARTIALLY_FILLED]:
                            date = dt.datetime.fromtimestamp(msg['T'] / 1000)
                            executed_size = float(msg['l'])
                            executed_price = float(msg['L'])
                            self._execute_order(o, dt, executed_size, executed_price)
                        self._set_order_status(o, msg['X'])

                        if o.status not in [Order.Accepted, Order.Partial]:
                            self.open_orders.remove(o)
                        self.notify(o)
        elif msg['e'] == 'error':
            raise msg
    
    def _set_order_status(self, order, binance_order_status):
        if binance_order_status == ORDER_STATUS_CANCELED:
            order.cancel()
        elif binance_order_status == ORDER_STATUS_EXPIRED:
            order.expire()
        elif binance_order_status == ORDER_STATUS_FILLED:
            order.completed()
        elif binance_order_status == ORDER_STATUS_PARTIALLY_FILLED:
            order.partial()
        elif binance_order_status == ORDER_STATUS_REJECTED:
            order.reject()

    def _submit(self, owner, data, side, exectype, size, price):
        type = self._ORDER_TYPES.get(exectype, ORDER_TYPE_MARKET)

        binance_order = self._store.create_order(side, type, size, price)
        order = BinanceOrder(owner, data, exectype, binance_order)
        if binance_order['status'] in [ORDER_STATUS_FILLED, ORDER_STATUS_PARTIALLY_FILLED]:
            self._execute_order(
                order,
                dt.datetime.fromtimestamp(binance_order['transactTime'] / 1000),
                float(binance_order['executedQty']),
                float(binance_order['price']))
        self._set_order_status(order, binance_order['status'])
        if order.status == Order.Accepted:
            self.open_orders.append(order)
        self.notify(order)
        return order

    def buy(self, owner, data, size, price=None, plimit=None,
            exectype=None, valid=None, tradeid=0, oco=None,
            trailamount=None, trailpercent=None,
            **kwargs):
        return self._submit(owner, data, SIDE_BUY, exectype, size, price)

    def cancel(self, order):
        order_id = order.binance_order['orderId']
        self._store.cancel_order(order_id)
        
    def format_price(self, value):
        return self._store.format_price(value)

    def get_asset_balance(self, asset):
        return self._store.get_asset_balance(asset)

    def getcash(self):
        self.cash = self._store._cash
        return self.cash

    def get_notification(self):
        if not self.notifs:
            return None

        return self.notifs.popleft()

    def getposition(self, data, clone=True):
        pos = self.positions[data._dataname]
        if clone:
            pos = pos.clone()
        return pos

    def getvalue(self, datas=None):
        self.value = self._store._value
        return self.value

    def notify(self, order):
        self.notifs.append(order)

    def sell(self, owner, data, size, price=None, plimit=None,
             exectype=None, valid=None, tradeid=0, oco=None,
             trailamount=None, trailpercent=None,
             **kwargs):
        return self._submit(owner, data, SIDE_SELL, exectype, size, price)


================================================
FILE: backtrader_binance/binance_feed.py
================================================
from collections import deque

import pandas as pd

from backtrader.dataseries import TimeFrame
from backtrader.feed import DataBase
from backtrader.utils import date2num


class BinanceData(DataBase):
    params = (
        ('drop_newest', True),
    )
    
    # States for the Finite State Machine in _load
    _ST_LIVE, _ST_HISTORBACK, _ST_OVER = range(3)

    def __init__(self, store, timeframe_in_minutes, start_date=None):
        self.timeframe_in_minutes = timeframe_in_minutes
        self.start_date = start_date

        self._store = store
        self._data = deque()

    def _handle_kline_socket_message(self, msg):
        """https://binance-docs.github.io/apidocs/spot/en/#kline-candlestick-streams"""
        if msg['e'] == 'kline':
            if msg['k']['x']:  # Is closed
                kline = self._parser_to_kline(msg['k']['t'], msg['k'])
                self._data.extend(kline.values.tolist())
        elif msg['e'] == 'error':
            raise msg

    def _load(self):
        if self._state == self._ST_OVER:
            return False
        elif self._state == self._ST_LIVE:
            return self._load_kline()
        elif self._state == self._ST_HISTORBACK:
            if self._load_kline():
                return True
            else:
                self._start_live()

    def _load_kline(self):
        try:
            kline = self._data.popleft()
        except IndexError:
            return None

        timestamp, open_, high, low, close, volume = kline

        self.lines.datetime[0] = date2num(timestamp)
        self.lines.open[0] = open_
        self.lines.high[0] = high
        self.lines.low[0] = low
        self.lines.close[0] = close
        self.lines.volume[0] = volume
        return True
    
    def _parser_dataframe(self, data):
        df = data.copy()
        df.columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume']
        df['timestamp'] = df['timestamp'].values.astype(dtype='datetime64[ms]')
        df['open'] = df['open'].values.astype(float)
        df['high'] = df['high'].values.astype(float)
        df['low'] = df['low'].values.astype(float)
        df['close'] = df['close'].values.astype(float)
        df['volume'] = df['volume'].values.astype(float)
        # df.set_index('timestamp', inplace=True)
        return df
    
    def _parser_to_kline(self, timestamp, kline):
        df = pd.DataFrame([[timestamp, kline['o'], kline['h'],
                            kline['l'], kline['c'], kline['v']]])
        return self._parser_dataframe(df)
    
    def _start_live(self):
        self._state = self._ST_LIVE
        self.put_notification(self.LIVE)
            
        self._store.binance_socket.start_kline_socket(
            self._handle_kline_socket_message,
            self.symbol_info['symbol'],
            self.interval)
        
    def haslivedata(self):
        return self._state == self._ST_LIVE and self._data

    def islive(self):
        return True
        
    def start(self):
        DataBase.start(self)

        self.interval = self._store.get_interval(TimeFrame.Minutes, self.timeframe_in_minutes)
        if self.interval is None:
            self._state = self._ST_OVER
            self.put_notification(self.NOTSUPPORTED_TF)
            return
        
        self.symbol_info = self._store.get_symbol_info(self._store.symbol)
        if self.symbol_info is None:
            self._state = self._ST_OVER
            self.put_notification(self.NOTSUBSCRIBED)
            return

        if self.start_date:
            self._state = self._ST_HISTORBACK
            self.put_notification(self.DELAYED)

            klines = self._store.binance.get_historical_klines(
                self.symbol_info['symbol'],
                self.interval,
                self.start_date.strftime('%d %b %Y %H:%M:%S'))

            if self.p.drop_newest:
                klines.pop()
            
            df = pd.DataFrame(klines)
            df.drop(df.columns[[6, 7, 8, 9, 10, 11]], axis=1, inplace=True)  # Remove unnecessary columns
            df = self._parser_dataframe(df)
            self._data.extend(df.values.tolist())            
        else:
            self._start_live()


================================================
FILE: backtrader_binance/binance_store.py
================================================
import time

from functools import wraps
from math import floor

from backtrader.dataseries import TimeFrame
from binance import Client, ThreadedWebsocketManager
from binance.enums import *
from binance.exceptions import BinanceAPIException
from requests.exceptions import ConnectTimeout, ConnectionError

from .binance_broker import BinanceBroker
from .binance_feed import BinanceData


class BinanceStore(object):
    _GRANULARITIES = {
        (TimeFrame.Minutes, 1): KLINE_INTERVAL_1MINUTE,
        (TimeFrame.Minutes, 3): KLINE_INTERVAL_3MINUTE,
        (TimeFrame.Minutes, 5): KLINE_INTERVAL_5MINUTE,
        (TimeFrame.Minutes, 15): KLINE_INTERVAL_15MINUTE,
        (TimeFrame.Minutes, 30): KLINE_INTERVAL_30MINUTE,
        (TimeFrame.Minutes, 60): KLINE_INTERVAL_1HOUR,
        (TimeFrame.Minutes, 120): KLINE_INTERVAL_2HOUR,
        (TimeFrame.Minutes, 240): KLINE_INTERVAL_4HOUR,
        (TimeFrame.Minutes, 360): KLINE_INTERVAL_6HOUR,
        (TimeFrame.Minutes, 480): KLINE_INTERVAL_8HOUR,
        (TimeFrame.Minutes, 720): KLINE_INTERVAL_12HOUR,
        (TimeFrame.Days, 1): KLINE_INTERVAL_1DAY,
        (TimeFrame.Days, 3): KLINE_INTERVAL_3DAY,
        (TimeFrame.Weeks, 1): KLINE_INTERVAL_1WEEK,
        (TimeFrame.Months, 1): KLINE_INTERVAL_1MONTH,
    }

    def __init__(self, api_key, api_secret, coin_refer, coin_target, testnet=False, retries=5):
        self.binance = Client(api_key, api_secret, testnet=testnet)
        self.binance_socket = ThreadedWebsocketManager(api_key, api_secret, testnet=testnet)
        self.binance_socket.daemon = True
        self.binance_socket.start()
        self.coin_refer = coin_refer
        self.coin_target = coin_target
        self.symbol = coin_refer + coin_target
        self.retries = retries

        self._cash = 0
        self._value = 0
        self.get_balance()

        self._step_size = None
        self._tick_size = None
        self.get_filters()

        self._broker = BinanceBroker(store=self)
        self._data = None
        
    def _format_value(self, value, step):
        precision = step.find('1') - 1
        if precision > 0:
            return '{:0.0{}f}'.format(value, precision)
        return floor(int(value))
        
    def retry(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            for attempt in range(1, self.retries + 1):
                time.sleep(60 / 1200) # API Rate Limit
                try:
                    return func(self, *args, **kwargs)
                except (BinanceAPIException, ConnectTimeout, ConnectionError) as err:
                    if isinstance(err, BinanceAPIException) and err.code == -1021:
                        # Recalculate timestamp offset between local and Binance's server
                        res = self.binance.get_server_time()
                        self.binance.timestamp_offset = res['serverTime'] - int(time.time() * 1000)
                    
                    if attempt == self.retries:
                        raise
        return wrapper

    @retry
    def cancel_open_orders(self):
        orders = self.binance.get_open_orders(symbol=self.symbol)
        if len(orders) > 0:
            self.binance._request_api('delete', 'openOrders', signed=True, data={ 'symbol': self.symbol })

    @retry
    def cancel_order(self, order_id):
        try:
            self.binance.cancel_order(symbol=self.symbol, orderId=order_id)
        except BinanceAPIException as api_err:
            if api_err.code == -2011:  # Order filled
                return
            else:
                raise api_err
        except Exception as err:
            raise err
    
    @retry
    def create_order(self, side, type, size, price):
        params = dict()
        if type in [ORDER_TYPE_LIMIT, ORDER_TYPE_STOP_LOSS_LIMIT]:
            params.update({
                'timeInForce': TIME_IN_FORCE_GTC
            })
        if type != ORDER_TYPE_MARKET:
            params.update({
                'price': self.format_price(price)
            })

        return self.binance.create_order(
            symbol=self.symbol,
            side=side,
            type=type,
            quantity=self.format_quantity(size),
            **params)

    def format_price(self, price):
        return self._format_value(price, self._tick_size)
    
    def format_quantity(self, size):
        return self._format_value(size, self._step_size)

    @retry
    def get_asset_balance(self, asset):
        balance = self.binance.get_asset_balance(asset)
        return float(balance['free']), float(balance['locked'])

    def get_balance(self):
        free, locked = self.get_asset_balance(self.coin_target)
        self._cash = free
        self._value = free + locked

    def getbroker(self):
        return self._broker

    def getdata(self, timeframe_in_minutes, start_date=None):
        if not self._data:
            self._data = BinanceData(store=self, timeframe_in_minutes=timeframe_in_minutes, start_date=start_date)
        return self._data
        
    def get_filters(self):
        symbol_info = self.get_symbol_info(self.symbol)
        for f in symbol_info['filters']:
            if f['filterType'] == 'LOT_SIZE':
                self._step_size = f['stepSize']
            elif f['filterType'] == 'PRICE_FILTER':
                self._tick_size = f['tickSize']

    def get_interval(self, timeframe, compression):
        return self._GRANULARITIES.get((timeframe, compression))

    @retry
    def get_symbol_info(self, symbol):
        return self.binance.get_symbol_info(symbol)

    def stop_socket(self):
        self.binance_socket.stop()
        self.binance_socket.join(5)


================================================
FILE: examples/live_trade.py
================================================
import datetime as dt

import backtrader as bt

from backtrader_binance import BinanceStore


class RSIStrategy(bt.Strategy):
    def __init__(self):
        self.rsi = bt.indicators.RSI(period=14)  # RSI indicator

    def next(self):
        print('Open: {}, High: {}, Low: {}, Close: {}'.format(
            self.data.open[0],
            self.data.high[0],
            self.data.low[0],
            self.data.close[0]))
        print('RSI: {}'.format(self.rsi[0]))

        if not self.position:
            if self.rsi < 30:  # Enter long
                self.buy()
        else:
            if self.rsi > 70:
                self.sell()  # Close long position
    
    def notify_order(self, order):
        print(order)

if __name__ == '__main__':
    cerebro = bt.Cerebro(quicknotify=True)

    store = BinanceStore(
        api_key='YOUR_BINANCE_KEY',
        api_secret='YOUR_BINANCE_SECRET',
        coin_refer='BTC',
        coin_target='USDT',
        testnet=True)
    broker = store.getbroker()
    cerebro.setbroker(broker)

    from_date = dt.datetime.utcnow() - dt.timedelta(minutes=5*16)
    data = store.getdata(
        timeframe_in_minutes=5,
        start_date=from_date)

    cerebro.addstrategy(RSIStrategy)
    cerebro.adddata(data)
    cerebro.run()


================================================
FILE: requirements.txt
================================================
backtrader==1.9.76.123
matplotlib==3.2.2
pandas==1.2.4
python-binance==1.0.12

================================================
FILE: setup.py
================================================
import os
from setuptools import setup

with open(os.path.join('README.md')) as desc:
    LONG_DESCRIPTION = desc.read()

with open(os.path.join('requirements.txt')) as reqs:
    REQUIREMENTS = reqs.readlines()

setup(
    name='backtrader-binance',
    version='1.0.0',
    description='Binance API integration with backtrader',
    long_description=LONG_DESCRIPTION,
    long_description_content_type='text/markdown',
    url='https://github.com/lindomar-oliveira/backtrader-binance',
    author='Lindomar Oliveira',
    author_email='lindomar.souza1999@gmail.com',
    license='MIT',
    packages=['backtrader_binance'],
    python_requires='>=3.7',
    keywords='backtrader,binance,bitcoin,bot,crypto,trading',
    install_requires=REQUIREMENTS
)
Download .txt
gitextract_x0l9znb4/

├── .gitignore
├── LICENSE
├── README.md
├── backtrader_binance/
│   ├── __init__.py
│   ├── binance_broker.py
│   ├── binance_feed.py
│   └── binance_store.py
├── examples/
│   └── live_trade.py
├── requirements.txt
└── setup.py
Download .txt
SYMBOL INDEX (50 symbols across 4 files)

FILE: backtrader_binance/binance_broker.py
  class BinanceOrder (line 12) | class BinanceOrder(OrderBase):
    method __init__ (line 13) | def __init__(self, owner, data, exectype, binance_order):
  class BinanceBroker (line 32) | class BinanceBroker(BrokerBase):
    method __init__ (line 40) | def __init__(self, store):
    method _execute_order (line 51) | def _execute_order(self, order, date, executed_size, executed_price):
    method _handle_user_socket_message (line 63) | def _handle_user_socket_message(self, msg):
    method _set_order_status (line 82) | def _set_order_status(self, order, binance_order_status):
    method _submit (line 94) | def _submit(self, owner, data, side, exectype, size, price):
    method buy (line 111) | def buy(self, owner, data, size, price=None, plimit=None,
    method cancel (line 117) | def cancel(self, order):
    method format_price (line 121) | def format_price(self, value):
    method get_asset_balance (line 124) | def get_asset_balance(self, asset):
    method getcash (line 127) | def getcash(self):
    method get_notification (line 131) | def get_notification(self):
    method getposition (line 137) | def getposition(self, data, clone=True):
    method getvalue (line 143) | def getvalue(self, datas=None):
    method notify (line 147) | def notify(self, order):
    method sell (line 150) | def sell(self, owner, data, size, price=None, plimit=None,

FILE: backtrader_binance/binance_feed.py
  class BinanceData (line 10) | class BinanceData(DataBase):
    method __init__ (line 18) | def __init__(self, store, timeframe_in_minutes, start_date=None):
    method _handle_kline_socket_message (line 25) | def _handle_kline_socket_message(self, msg):
    method _load (line 34) | def _load(self):
    method _load_kline (line 45) | def _load_kline(self):
    method _parser_dataframe (line 61) | def _parser_dataframe(self, data):
    method _parser_to_kline (line 73) | def _parser_to_kline(self, timestamp, kline):
    method _start_live (line 78) | def _start_live(self):
    method haslivedata (line 87) | def haslivedata(self):
    method islive (line 90) | def islive(self):
    method start (line 93) | def start(self):

FILE: backtrader_binance/binance_store.py
  class BinanceStore (line 16) | class BinanceStore(object):
    method __init__ (line 35) | def __init__(self, api_key, api_secret, coin_refer, coin_target, testn...
    method _format_value (line 56) | def _format_value(self, value, step):
    method retry (line 62) | def retry(func):
    method cancel_open_orders (line 80) | def cancel_open_orders(self):
    method cancel_order (line 86) | def cancel_order(self, order_id):
    method create_order (line 98) | def create_order(self, side, type, size, price):
    method format_price (line 116) | def format_price(self, price):
    method format_quantity (line 119) | def format_quantity(self, size):
    method get_asset_balance (line 123) | def get_asset_balance(self, asset):
    method get_balance (line 127) | def get_balance(self):
    method getbroker (line 132) | def getbroker(self):
    method getdata (line 135) | def getdata(self, timeframe_in_minutes, start_date=None):
    method get_filters (line 140) | def get_filters(self):
    method get_interval (line 148) | def get_interval(self, timeframe, compression):
    method get_symbol_info (line 152) | def get_symbol_info(self, symbol):
    method stop_socket (line 155) | def stop_socket(self):

FILE: examples/live_trade.py
  class RSIStrategy (line 8) | class RSIStrategy(bt.Strategy):
    method __init__ (line 9) | def __init__(self):
    method next (line 12) | def next(self):
    method notify_order (line 27) | def notify_order(self, order):
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (21K chars).
[
  {
    "path": ".gitignore",
    "chars": 32,
    "preview": "__pycache__/\n\n.idea/\n*.egg-info/"
  },
  {
    "path": "LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2021 Lindomar Oliveira\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "README.md",
    "chars": 647,
    "preview": "# backtrader-binance\n\nCreate your strategy for the [Backtrader](https://www.backtrader.com), do the backtesting and you "
  },
  {
    "path": "backtrader_binance/__init__.py",
    "chars": 40,
    "preview": "from .binance_store import BinanceStore\n"
  },
  {
    "path": "backtrader_binance/binance_broker.py",
    "chars": 5663,
    "preview": "import datetime as dt\n\nfrom collections import defaultdict, deque\nfrom math import copysign\n\nfrom backtrader.broker impo"
  },
  {
    "path": "backtrader_binance/binance_feed.py",
    "chars": 4211,
    "preview": "from collections import deque\n\nimport pandas as pd\n\nfrom backtrader.dataseries import TimeFrame\nfrom backtrader.feed imp"
  },
  {
    "path": "backtrader_binance/binance_store.py",
    "chars": 5669,
    "preview": "import time\n\nfrom functools import wraps\nfrom math import floor\n\nfrom backtrader.dataseries import TimeFrame\nfrom binanc"
  },
  {
    "path": "examples/live_trade.py",
    "chars": 1277,
    "preview": "import datetime as dt\n\nimport backtrader as bt\n\nfrom backtrader_binance import BinanceStore\n\n\nclass RSIStrategy(bt.Strat"
  },
  {
    "path": "requirements.txt",
    "chars": 77,
    "preview": "backtrader==1.9.76.123\nmatplotlib==3.2.2\npandas==1.2.4\npython-binance==1.0.12"
  },
  {
    "path": "setup.py",
    "chars": 751,
    "preview": "import os\nfrom setuptools import setup\n\nwith open(os.path.join('README.md')) as desc:\n    LONG_DESCRIPTION = desc.read()"
  }
]

About this extraction

This page contains the full source code of the lindomar-oliveira/backtrader-binance GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (19.0 KB), approximately 4.8k tokens, and a symbol index with 50 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!