36 KiB
36 KiB
MTF analysis¶
In [ ]:
from vectorbtpro import * # whats_imported() vbt.settings.set_theme("dark")
Data¶
In [ ]:
# h1_data = vbt.BinanceData.pull( # "BTCUSDT", # start="2020-01-01 UTC", # end="2021-01-01 UTC", # timeframe="1h" # ) # h1_data.to_hdf()
In [ ]:
h1_data = vbt.HDFData.pull('BinanceData.h5')
In [ ]:
h1_data.wrapper.index
In [ ]:
h1_resampler = h1_data.wrapper.get_resampler("1h") h1_resampler.index_difference(reverse=True)
In [ ]:
h1_data.wrapper.columns
In [ ]:
h1_ohlcv_data = h1_data[["Open", "High", "Low", "Close", "Volume"]]
In [ ]:
h4_ohlcv = h1_ohlcv_data.get().resample("4h").agg({ "Open": "first", "High": "max", "Low": "min", "Close": "last", "Volume": "sum" }) h4_ohlcv
In [ ]:
print(h1_ohlcv_data.get().iloc[:4])
In [ ]:
print(h4_ohlcv.iloc[[0]])
In [ ]:
print(vbt.prettify(vbt.BinanceData.feature_config))
In [ ]:
h1_data.use_feature_config_of(vbt.BinanceData) h4_data = h1_data.resample("4h") d1_data = h1_data.resample("1d")
In [ ]:
print(d1_data.get().iloc[[0, -1]])
In [ ]:
print(vbt.BinanceData.pull( "BTCUSDT", start="2020-01-01 UTC", end="2021-01-01 UTC", timeframe="1d" ).get().iloc[[0, -1]])
Alignment¶
Pandas¶
In [ ]:
h1_close = h1_data.get("Close") h4_close = h4_data.get("Close")
In [ ]:
h1_close.iloc[:4]
In [ ]:
h4_close.iloc[:1]
In [ ]:
h1_h4_ratio = h1_close / h4_close h1_h4_ratio.iloc[:4]
In [ ]:
h4_close_shifted = h4_close.shift() h1_h4_ratio = h1_close / h4_close_shifted h1_h4_ratio.iloc[:8]
In [ ]:
h1_h4_ratio.shift(-1).iloc[:8]
In [ ]:
h4_h1_close = h4_close.shift(1).resample("1h").last().shift(-1).ffill() h4_h1_close.iloc[:8]
In [ ]:
fig = h1_close.rename("H1").iloc[:16].vbt.plot() h4_h1_close.rename("H4_H1").iloc[:16].vbt.plot(fig=fig).show_svg()
In [ ]:
h1_h4_ratio = h1_close / h4_h1_close h1_h4_ratio
In [ ]:
h1_open = h1_data.get("Open") h4_open = h4_data.get("Open") h1_open.iloc[:8]
In [ ]:
h4_h1_open = h4_open.resample("1h").first().ffill() h4_h1_open.iloc[:8]
VBT¶
In [ ]:
h4_close.vbt.realign_closing("1h")
In [ ]:
h4_open.vbt.realign_opening("1h")
Resampler¶
In [ ]:
h4_h1_resampler = h4_close.vbt.wrapper.get_resampler("1h") h4_h1_resampler.source_index
In [ ]:
h4_h1_resampler.target_index
In [ ]:
h4_h1_resampler.source_freq
In [ ]:
h4_h1_resampler.target_freq
In [ ]:
pd_resampler = h4_close.resample("1h") vbt.Resampler.from_pd_resampler(pd_resampler)
In [ ]:
resampler = vbt.Resampler.from_date_range( source_index=h4_close.index, source_freq="4h", start="2020-01-01 10:00:00", end="2020-01-01 22:00:00", freq="1h", )
In [ ]:
h4_close.vbt.realign_closing(resampler)
Custom index¶
In [ ]:
target_index = pd.Index([ "2020-01-01", "2020-02-01", "2020-03-01", "2020-04-01", "2020-05-01", "2020-06-01", "2020-07-01", "2020-08-01", "2020-09-01", "2020-10-01", "2020-11-01", "2020-12-01", "2021-01-01" ]) resampler = vbt.Resampler(h4_close.index, target_index, target_freq=False) h4_close.vbt.realign_closing(resampler)
In [ ]:
h4_close[h4_close.index < "2020-09-01"].iloc[-1]
In [ ]:
h4_open.vbt.realign_opening(resampler)
In [ ]:
h4_open[h4_open.index <= "2020-08-01"].iloc[-1]
In [ ]:
target_index = pd.Index([ "2020-01-01", "2020-02-01", ]) resampler = vbt.Resampler(h4_close.index, target_index, target_freq=False) h4_close.vbt.realign_closing(resampler)
In [ ]:
resampler = vbt.Resampler(h4_close.index, target_index, target_freq="30d") h4_close.vbt.realign_closing(resampler)
In [ ]:
h4_open.vbt.realign("2020-06-07 12:15:00")
In [ ]:
h4_close.vbt.realign( "2020-06-07 12:15:00", source_rbound=True )
In [ ]:
h4_high = h4_data.get("High") h4_high.vbt.realign( target_index, source_rbound=True )
In [ ]:
h4_high.index[h4_high.index < "2020-02-01"][-1]
In [ ]:
h4_high.vbt.realign( target_index, source_rbound=True, target_rbound=True )
In [ ]:
resampler = vbt.Resampler(h4_high.index, target_index) resampler.target_rbound_index
In [ ]:
resampler = vbt.Resampler( h4_high.index, target_index, target_freq=pd.offsets.MonthBegin(1)) resampler.target_rbound_index
In [ ]:
h4_high.vbt.realign( resampler.replace( target_index=resampler.target_rbound_index, target_freq=False ), wrap_kwargs=dict(index=target_index) )
In [ ]:
h4_high.vbt.realign( target_index, freq=pd.offsets.MonthBegin(1), target_rbound="pandas" )
In [ ]:
h4_high[h4_high.index < "2020-03-01"].resample(vbt.offset("M")).last()
Numeric index¶
In [ ]:
resampler = vbt.Resampler( source_index=np.arange(len(h4_high)), target_index=np.arange(len(h4_high))[::6], source_freq=1, target_freq=6 ) h4_high.vbt.realign( resampler, source_rbound=True, target_rbound=True )
Forward filling¶
In [ ]:
min5_index = vbt.date_range(start="2020", freq="5min", periods=3) min1_index = vbt.date_range(start="2020", freq="1min", periods=15) min5_mask = pd.Series(False, index=min5_index) min5_mask.iloc[0] = True min5_mask.iloc[2] = True resampler = vbt.Resampler(min5_index, min1_index) min1_mask = min5_mask.vbt.realign_closing(resampler) min1_mask
In [ ]:
min1_mask = min5_mask.vbt.realign_closing(resampler, ffill=False) min1_mask
In [ ]:
min1_mask = min1_mask.fillna(False).astype(bool) min1_mask
Indicators¶
In [ ]:
h4_sma = vbt.talib("SMA").run(h4_data.get("Close"), skipna=True).real d1_sma = vbt.talib("SMA").run(d1_data.get("Close"), skipna=True).real h4_sma = h4_sma.ffill() d1_sma = d1_sma.ffill()
In [ ]:
resampler = vbt.Resampler( d1_sma.index, h4_sma.index, source_freq="1d", target_freq="4h" ) d1_h4_sma = d1_sma.vbt.realign_closing(resampler)
In [ ]:
d1_sma["2020-12-30":]
In [ ]:
d1_h4_sma["2020-12-30":]
In [ ]:
entries = h4_sma.vbt.crossed_above(d1_h4_sma) exits = h4_sma.vbt.crossed_below(d1_h4_sma) def plot_date_range(date_range): fig = h4_sma[date_range].rename("H4").vbt.plot() d1_h4_sma[date_range].rename("D1_H4").vbt.plot(fig=fig) entries[date_range].rename("Entry").vbt.signals.plot_as_entries( y=h4_sma[date_range], fig=fig) exits[date_range].rename("Exit").vbt.signals.plot_as_exits( y=h4_sma[date_range], fig=fig) return fig plot_date_range(slice("2020-02-01", "2020-03-01")).show_svg()
In [ ]:
d1_open_sma = vbt.talib("SMA").run( d1_data.get("Open"), skipna=True ).real d1_open_sma = d1_open_sma.ffill() d1_h4_open_sma = d1_open_sma.vbt.realign( resampler, source_rbound=False, target_rbound=True, )
In [ ]:
d1_open_sma["2020-12-30":]
In [ ]:
d1_h4_open_sma["2020-12-30":]
In [ ]:
def generate_bandwidths(freqs): bandwidths = [] for freq in freqs: close = h1_data.resample(freq).get("Close") bbands = vbt.talib("BBANDS").run(close, skipna=True) upperband = bbands.upperband.ffill() middleband = bbands.middleband.ffill() lowerband = bbands.lowerband.ffill() bandwidth = (upperband - lowerband) / middleband bandwidths.append(bandwidth.vbt.realign_closing("1h")) df = pd.concat(bandwidths, axis=1, keys=pd.Index(freqs, name="timeframe")) return df.ffill() bandwidths = generate_bandwidths(["1h", "4h", "1d", "7d"]) print(bandwidths)
In [ ]:
bandwidths.loc[:, ::-1].vbt.ts_heatmap().show_svg()
In [ ]:
>>> bbands = vbt.talib("BBANDS").run( ... h1_data.get("Close"), ... skipna=True, ... timeframe=["1h", "4h", "1d", "7d"], ... broadcast_kwargs=dict(wrapper_kwargs=dict(freq="1h")) ... ) >>> bandwidth = (bbands.upperband - bbands.lowerband) / bbands.middleband >>> print(bandwidths)
Testing¶
In [ ]:
def generate_signals(data, freq, fast_window, slow_window): open_price = data.get("Open").resample(freq).first() fast_sma = vbt.talib("SMA")\ .run( open_price, fast_window, skipna=True, short_name="fast_sma" )\ .real.ffill()\ .vbt.realign(data.wrapper.index) slow_sma = vbt.talib("SMA")\ .run( open_price, slow_window, skipna=True, short_name="slow_sma" )\ .real.ffill()\ .vbt.realign(data.wrapper.index) entries = fast_sma.vbt.crossed_above(slow_sma) exits = fast_sma.vbt.crossed_below(slow_sma) return entries, exits fast_window = [10, 20] slow_window = [20, 30] h1_entries, h1_exits = generate_signals(h1_data, "1h", fast_window, slow_window) h4_entries, h4_exits = generate_signals(h1_data, "4h", fast_window, slow_window) d1_entries, d1_exits = generate_signals(h1_data, "1d", fast_window, slow_window) entries = pd.concat( (h1_entries, h4_entries, d1_entries), axis=1, keys=pd.Index(["1h", "4h", "1d"], name="timeframe") ) exits = pd.concat( (h1_exits, h4_exits, d1_exits), axis=1, keys=pd.Index(["1h", "4h", "1d"], name="timeframe") )
In [ ]:
(entries.astype(int) - exits.astype(int))\ .resample("1d").sum()\ .vbt.ts_heatmap( trace_kwargs=dict( colorscale=["#ef553b", "rgba(0, 0, 0, 0)", "#17becf"], colorbar=dict( tickvals=[-1, 0, 1], ticktext=["Exit", "", "Entry"] ) ) ).show_svg()
In [ ]:
pf = vbt.Portfolio.from_signals( h1_data, entries, exits, sl_stop=0.1, freq="1h" ) pf.orders.count()
In [ ]:
pf.sharpe_ratio
Aggregation¶
In [ ]:
ms_data = h1_data.resample("M") ms_data.get("Low") / ms_data.get("High") - 1
In [ ]:
h1_high = h1_data.get("High") h1_low = h1_data.get("Low") ms_high = h1_high.resample(vbt.offset("M")).max() ms_low = h1_low.resample(vbt.offset("M")).min() ms_low / ms_high - 1
In [ ]:
ms_high = h1_high.vbt.resample_apply("M", vbt.nb.max_reduce_nb) ms_low = h1_low.vbt.resample_apply("M", vbt.nb.min_reduce_nb) ms_low / ms_high - 1
Custom index¶
Using target index¶
In [ ]:
target_index = pd.Index([ "2020-01-01", "2020-02-01", ]) h1_high.vbt.resample_to_index( target_index, vbt.nb.max_reduce_nb )
In [ ]:
target_rbound_index = vbt.Resampler.get_rbound_index( target_index, pd.offsets.MonthBegin(1) ) h1_high.vbt.resample_to_index( target_index.append(target_rbound_index[[-1]]), vbt.nb.max_reduce_nb ).iloc[:-1]
In [ ]:
h1_high[:"2020-03-01"].resample(vbt.offset("M")).max().iloc[:-1]
Using group-by¶
In [ ]:
pd_resampler = h1_high.resample(vbt.offset("M")) ms_high = h1_high.vbt.groupby_apply(pd_resampler, vbt.nb.max_reduce_nb) ms_low = h1_low.vbt.groupby_apply(pd_resampler, vbt.nb.min_reduce_nb) ms_low / ms_high - 1
In [ ]:
target_lbound_index = pd.Index([ "2020-01-01", "2020-02-01", ]) target_rbound_index = pd.Index([ "2020-02-01", "2020-03-01", ]) h1_high.vbt.resample_between_bounds( target_lbound_index, target_rbound_index, vbt.nb.max_reduce_nb )
In [ ]:
h1_high.vbt.resample_between_bounds( "2020-01-01", vbt.date_range("2020-01-02", "2021-01-01", freq="M", inclusive="both"), vbt.nb.max_reduce_nb )
In [ ]:
h1_high.expanding().max().resample(vbt.offset("M")).max()
Meta methods¶
In [ ]:
@njit def mdd_nb(from_i, to_i, col, high, low): highest = np.nanmax(high[from_i:to_i, col]) lowest = np.nanmin(low[from_i:to_i, col]) return lowest / highest - 1 vbt.pd_acc.resample_apply( "M", mdd_nb, vbt.Rep("high"), vbt.Rep("low"), broadcast_named_args=dict( high=h1_high, low=h1_low ) )
In [ ]:
h1_high.iloc[0:744]
In [ ]:
h1_low.iloc[0:744].min() / h1_high.iloc[0:744].max() - 1
In [ ]:
>>> target_lbound_index = vbt.date_range("2020-01-01", "2020-12-01", freq="M", tz="UTC", inclusive="both") >>> target_rbound_index = vbt.date_range("2020-02-01", "2021-01-01", freq="M", tz="UTC", inclusive="both") >>> vbt.pd_acc.resample_between_bounds( ... target_lbound_index, ... target_rbound_index, ... mdd_nb, ... vbt.Rep("high"), ... vbt.Rep("low"), ... broadcast_named_args=dict( ... high=h1_high, ... low=h1_low ... ) ... )
Numba¶
In [ ]:
>>> from vectorbtpro.base.resampling.nb import map_bounds_to_source_ranges_nb >>> range_starts, range_ends = map_bounds_to_source_ranges_nb( ... source_index=h1_high.index.values, ... target_lbound_index=target_lbound_index.values, ... target_rbound_index=target_rbound_index.values, ... closed_lbound=True, ... closed_rbound=False, ... ) >>> np.column_stack((range_starts, range_ends))
In [ ]:
>>> ms_mdd_arr = vbt.nb.reduce_index_ranges_meta_nb( ... 1, ... range_starts, ... range_ends, ... mdd_nb, ... vbt.to_2d_array(h1_high), ... vbt.to_2d_array(h1_low) ... ) >>> ms_mdd_arr
In [ ]:
>>> pd.Series(ms_mdd_arr[:, 0], index=target_lbound_index)
Caveats¶
In [ ]:
h4_close_2d = h4_close.iloc[:12] h4_close_2d
In [ ]:
h4_close_2d.resample("1d").last()
In [ ]:
h5_close = h1_close.resample("5h").last() h5_close_2d = h5_close.iloc[:10] h5_close_2d
In [ ]:
h5_close_2d.resample("1d").last()
In [ ]:
vbt.timedelta("1d") % vbt.timedelta("1h")
In [ ]:
vbt.timedelta("1d") % vbt.timedelta("4h")
In [ ]:
vbt.timedelta("1d") % vbt.timedelta("5h")
In [ ]:
h5_close_time = h5_close_2d.index.shift() - pd.Timedelta(nanoseconds=1) h5_close_time.name = "Close time" h5_close_2d.index = h5_close_time h5_close_2d
In [ ]:
h5_close_2d.resample("1d").last()
Portfolio¶
In [ ]:
fast_sma = vbt.talib("SMA").run(h1_close, timeperiod=vbt.Default(10)) slow_sma = vbt.talib("SMA").run(h1_close, timeperiod=vbt.Default(20)) entries = fast_sma.real_crossed_above(slow_sma.real) exits = fast_sma.real_crossed_below(slow_sma.real) pf = vbt.Portfolio.from_signals(h1_close, entries, exits) pf.plot().show_svg()
In [ ]:
ms_pf = pf.resample("M") ms_pf.plot().show_svg()
In [ ]:
pf.total_return
In [ ]:
ms_pf.total_return
In [ ]:
(1 + pf.returns).resample(vbt.offset("M")).apply(lambda x: x.prod() - 1)
In [ ]:
ms_pf.returns
In [ ]:
ms_pf.trades.pnl.to_pd(reduce_func_nb="sum")
In [ ]: