-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Live trading support #81
Comments
Afaik, there were no prior efforts of getting real, live trading in. I guess |
after furthur study, I think practical approch is to mirror backtesting.py to a realtrading.py file (not disturbing original backtesting.py), still share Strategy class from backtesting.py. Backtest class, _broker, position need rewrite in new file, order/trade not sure as of now, probably keep as mirror of trade serverside data. No obvious barrier seen now for the real trade implementation(very broker API specific) :-) |
I envision live trading by way of a separate class, in Python type notation, say: from typing import Type, Union, Literal, NoReturn
LiveTrade(strategy: Type[Strategy],
period: str,
broker: Union[Literal["ib", "oanda", "ccxt"], Broker],
**kwargs) -> NoReturn The idea is we can reuse the same The common broker API is sure in the domain of its implementer. 😄 |
@kernc This needs some support, there has to be an abstract base class for live trading so that people can implement the Brokers of their choice as they like and ccxt library( for Crypto ) support as an example. cc @ttfreeman Tagging you in case you are interested. |
@arunavo4 Yes, definitely interested, though need to research on the best way to tackle this prior to diving in. I like @kernc 's suggestion to have a separate class (LiveTrade) but we might also need another class (LiveData) that would bridge the live data from broker to LiveTrade class.
|
Data acquisition can surely be handled by the chosen broker (i.e. |
practically my current process to live trading is rewrite strategy ( slight modification - thanks to the backtesting's clear structure and pandas compatible data carrier) , to fit in live broker/data source. Building a BT compatible live trading framework is not a small project - considering BT is single equity, single datafeed( though resample applicable) , while trading is diversified. Now I use backtesting.py to verify my alorithm( or subset of algo) which works pefectly.Then write trading in a flexible way. |
I'd recommend keeping data feeds and brokers separate responsibilities. That way theoretically one could subscribe to any data source then execute trades with the broker of their choice without being tightly coupled to anything. Definitely agree that having a path for people to BYOB (broker) is the way to go. Then strategy.buy() or strategy.sell() is executed on their broker of choice, even if it is a backtest broker. |
Has anyone started work on this (that's shareable)? |
@crazy25000 Have started working on it. but it's very integrated into my project. The gist is that we can easily (maybe not too easy) Make a class Similar to Backtest. Which can have Corresponding Broker Classes like CCXT/ Oanda/ Alpaca. |
Waiting |
For those who are interested I found a workaround to generate trading signals with backetsting.py. I use a custom strategy class that extends the standard one:
This does not require any modification to the backtesting.py source code. |
@arunavo4 could you share how your LiveTrade Class looks like? |
Any updates on this? I would love to see this feature added. |
Hi everyone, I am probably late to the party but I wanted to share that I am implementing similar ideas. I do use ccxt as broker for live trading and I managed to implement a Strategy object in my framework which extends the backtesting.py Strategy. My Strategy has a run_live() and a run_backtest() the latter calls BackTesting with self. The tricky part and not so elegant perhaps is that both frameworks have models such as Order and Position which aren’t really compatible. I refrained from going down the way of implementing a Broker extension of _Broker because it has clearly a backtesting design and purpose at the moment. It would be great if Broker would be more “open” to subclassing and alternative implementations. I’ll keep you posted on the progress if someone is interested. |
I'm about to do the same thing, can you share a gist of how you went about takcling it? Just some partial code would be great. |
Hey @hakunin perhaps we can join forces on this, provided @kernc will provide support for merging later on. Here are some of the issues which would require a Backtesting.by redesign, as of now I am solving them with real-time override (not nice):
While I am far from being done, and I have to confess I have considered multiple times to give up with the integration, I am still trying and hoping that things will evolve towards a more open framework approach, which probably won't bother the final users, but would definitely allow the Backtesting.py to become part of many trading solutions out there 😉 I will keep you posted on the progress and as soon as I will have an end-to-end working sample I will share it with you. |
Live trading is a must have feature I think and I am really interested in which files or where to add those modifications. I have multiple indicators and added the bool in the custom strategy class. I've made similar modifications but I got an error and trying to solve this: |
I have write a draft version based on comments above, from backtesting.live import LiveTrade, LiveMarketOhlcv
bt = Backtest(df_partial, SmaCross, commission=.002,
exclusive_orders=True)
live_trade = LiveTrade(bt) For workaround here, I clone a mirror strategy and a mirror data instance. Below are some script codes modified from the example in Readme.md: def run_with_live_new_ohlcv(live_trade: LiveTrade):
# select data from external (iteration not run currently)
...
live_trade.on_bar(LiveMarketOhlcv(...))
live_trade.run_next()
bt = Backtest(df_partial, SmaCross, commission=.002,
exclusive_orders=True)
live_trade = LiveTrade(bt)
live_trade.init()
live_trade.run(to_end=True)
...
run_with_live_new_ohlcv(live_trade) You can controll I haven't test detailed in my version, but just comaparsion of |
I have developed a plugin for live trading, but it's made up of homemade code and contains a lot of customizations. Therefore, I cannot release it yet. However, the basic logic is similar to that of backtrader's store. To begin, you will need to create a new class called "LiveTrading" that will replace "Backtest." This class will trigger the "strategy.next()" and "strategy.init()" functions every time new data is received. (I'll explain why the "strategy.init()" function is necessary later on.) Next, you will need to create a "DataFeed" class to fetch the data via ccxt/api/websocket. You can set it up however you like. After that, create a new broker based on the broker's sdk/api. Since I trade in cryptocurrency, I created one based on ccxt. I also created a paper trading broker for testing purposes. In "backtesting.py," all indicators are calculated when "Backtest" is initialized within the "strategy.init()" function. Therefore, it is necessary to recalculate the indicator each time new data is received. However, the current logic of "backtesting.py" will dump the indicator function, making it impossible to recalculate. A lazy solution would be to re-call the "strategy.init()" function. However, if you have other states inside "strategy.init()" (which is very common), you will lose the state each time you call "strategy.init()." To address this, I created another abstract method for the "Strategy" class that is dedicated to state initialization, and ensured that it is called only once. With the "DataFeed"/ "LiveTrading" (replacing "Backtest")/ "Broker," you can easily build your own robot with just a few modifications to your strategy code (making it less likely to introduce new bugs after modification). |
My approach into using the backtesting.py together with live trading is a bit different:
bt = Backtest(_df,
bot.strategy,
cash=self.cash,
commission=self.commission,
exclusive_orders=True,
trade_on_close=True,
size_second=self.size_second
) The last param size_second is of my own adding to know if we want to use fixed amount or legacy behavior. # update the size, calculating it from last_price if size_second
size_orig = size
size = size / self.last_price if self._size_second else float(size)
To get my signal next couple of things is done:
# Snippet from the Strategy (SuperTrend):
def next_signal(self):
signal = 'None'
if crossover(self.data.Close, self.st):
# self.buy(size=self.position_size) # Was 0.99
signal = 'buy'
elif crossover(self.st, self.data.Close):
# self.position.close()
signal = 'sell'
return signal
# From the main
strategy=Backtest(df,
bot.strategy,
cash=self.cash,
commission=self.commission,
exclusive_orders=True)
# Note that the cash/commission/exclusive_orders are not relevant as I only want strategy signal
# The settings are for the strategy params, example:
# settings = {'atr_timeperiod': 0.09, 'atr_multiplier': 0.55, 'atr_method': True, 'position_size': 100}
strategy.run(**settings)
signal = strategy._results._strategy.next_signal() For the compute_stats it gets a bit more complicated. ...
# My own local copy and the imports, had to change __init__.py, added _Broker to allow its import
from common.backtesting import Trade, _Broker
from common.backtesting._stats import compute_stats
# Note: self._trades is a list()
if trade_status == 'Bought':
# Keep track of trading for compute stats
broker = _Broker(data=self._data, cash=self.cash, commission=2 * self.trade_commission, margin=1,
trade_on_close=True, hedging=False, exclusive_orders=True, size_second=True,
index=self._data.index)
self._trade = Trade(broker=broker, size=executed_qty, entry_price=avg_price, entry_bar=self.candle_cnt - 1)
elif trade_status == 'Sold':
with suppress(AttributeError):
# Updating the _trade exit_price with avg_price from Market Order from Binance
self._trade._replace(exit_price=avg_price, exit_bar=self.candle_cnt-1)
self._trades += self._trade,
# And Finally
equity = pd.Series(self._data.equity).bfill().fillna(self.cash).values
stats = compute_stats(
trades=self._trades, # broker.closed_trades,
equity=equity,
ohlc_data=self._data,
risk_free_rate=0.0,
strategy_instance=None # strategy,
)
# Then cleaning the protected element
stats_clean = {k: v for k, v in stats.items() if not str(k).startswith('_')} Quite cool, the backtesting.py. |
who can integration live trade to Strategy |
Expected Behavior
I am trying to bind the realtrade into this frame work. Of course replacements of Backtest , _Broker , and datafeeding need rewrite. I got them (data API, broker API) on hand and had some ideas to plant in.
But I want to hear from @kernc you on your advices/thought first before start, esp. if there are already a reference ~
Actual Behavior
Steps to Reproduce
Additional info
The text was updated successfully, but these errors were encountered: