VectorBT Pro - Custom Simulator 3: CandleStick Strategy + StopLoss + TakeProfit + Partial Profits
Importing The Dependencies
import vectorbtpro as vbt
import numpy as np
import pandas as pd
from numba import njit
import talib
import datetime as dt
import time
from collections import namedtuple
import itertools
import math
from vectorbtpro.records.nb import col_map_nb
from vectorbtpro.portfolio import nb as pf_nb, enums as pf_enums
import plotly.io as pio
from numba import njit
Strategy Rules π
In addition to the previous rules , the only additional rule here is to close 50 % of the position when the price hits the first take profit
tp1
(RRR of 1) moving the stop loss to breakeven (entry_price
)
The tracking dictionary here does exactly what it says , it will keep track of the
entry_price
,stop_loss
,tp1
(take profit 1) ,tp2
(take profit 2) for bothlong
π© andshort
π₯ positions.
π Indexing into a dictionary to update / retrieve values that will subsequently be used in the trading logic will be undoubtly be computationally slow however it is very easy to understand.
β‘The dictionary variables can be unpacked and be used independently to increase the speed.
tracking_dict = {'long' : {'entry_price': None,'stop_loss': None,'tp1': None,'tp2': None},
'short': {'entry_price': None,'stop_loss': None,'tp1': None,'tp2': None}}
When
tp1
(first take profit) the tracking dictionary (tracking_dict
) must be updated to reflect this change (set toNone
).
In conjunction to this when
tp1
is hit thestop_loss
should be moved to breakeven (stop_loss
price is set toentry_price
)
When the
stop_loss
ortp2
is hit every variable in thetracking_dict
is set toNone
to reflect that we are no longer in that position.
Custom Simulator
def custom_simulator_candlestick_partial_profs(open_ , high_ , low_ , close_ , IsBullArray, init_cash = 10000):
order_records = np.empty((2663,1), dtype = vbt.pf_enums.order_dt)
order_counts = np.full(1,0, dtype = np.int_)
tracking_dict = {'long' : {'entry_price': None,'stop_loss': None,'tp1': None,'tp2': None},
'short': {'entry_price': None,'stop_loss': None,'tp1': None,'tp2': None}}
exec_state = vbt.pf_enums.ExecState(
cash = float(init_cash),
position = 0.0,
debt = 0.0,
locked_cash = 0.0,
free_cash = float(init_cash),
val_price = np.nan,
value = np.nan)
for i in range(len(close_)):
price_area = vbt.pf_enums.PriceArea(open = open_[i],
high = high_[i],
low = low_[i],
close = close_[i])
value_price = price_area.close
value = exec_state.cash + (exec_state.position * value_price)
current_price = price_area.close
exec_state = vbt.pf_enums.ExecState(
cash = exec_state.cash,
position = exec_state.position,
debt = exec_state.debt,
locked_cash = exec_state.locked_cash,
free_cash = exec_state.free_cash,
val_price = value_price,
value = value)
if IsBullArray[i] and exec_state.position == 0:
long_stop_loss = price_area.low #Long Stop loss
long_entry_price = price_area.close #Long Entry Price
long_tp1 = price_area.close + ( 1 * (price_area.close - long_stop_loss) ) #1RRR
long_tp2 = price_area.close + ( 2 * (price_area.close - long_stop_loss) ) #2RRR
tracking_dict['long']['entry_price'] = long_entry_price
tracking_dict['long']['stop_loss'] = long_stop_loss
tracking_dict['long']['tp1'] = long_tp1
tracking_dict['long']['tp2'] = long_tp2
order_result , exec_state = enter_position(
execution_state_ = exec_state,
price_area_ = price_area,
percent_risk_ = 1,
group_ = 0, column_= 0, bar_ = i,
direction = 'Buy',
order_records_= order_records,
order_counts_ = order_counts)
elif not IsBullArray[i] and exec_state.position == 0:
short_stop_loss = price_area.high #Short Stop Loss
short_entry_price = price_area.close #Short Entry Price
short_tp1 = price_area.close - ( 1 * (price_area.high - short_stop_loss) ) #1RRR
short_tp2 = price_area.close - ( 2 * (price_area.high - short_stop_loss) ) #2RRR
tracking_dict['short']['entry_price'] = short_entry_price
tracking_dict['short']['stop_loss'] = short_stop_loss
tracking_dict['short']['tp1'] = short_tp1
tracking_dict['short']['tp2'] = short_tp2
order_result , exec_state = enter_position(
execution_state_ = exec_state,
price_area_ = price_area,
percent_risk_ = 1,
group_ = 0, column_= 0, bar_ = i,
direction = 'Sell',
order_records_= order_records,
order_counts_ = order_counts)
elif not IsBullArray[i] and exec_state.position > 0:
tracking_dict['long']['entry_price'] = None
tracking_dict['long']['stop_loss'] = None
tracking_dict['long']['tp1'] = None
tracking_dict['long']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
elif IsBullArray[i] and exec_state.position < 0:
tracking_dict['short']['entry_price'] = None
tracking_dict['short']['stop_loss'] = None
tracking_dict['short']['tp1'] = None
tracking_dict['short']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
else:
if exec_state.position > 0: #In Long
entry_price = tracking_dict['long']['entry_price']
if (price_area.low <= long_stop_loss):
#Long Stop Loss Has Been Hit
tracking_dict['long']['entry_price'] = None
tracking_dict['long']['stop_loss'] = None
tracking_dict['long']['tp1'] = None
tracking_dict['long']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
elif (price_area.high >= tracking_dict['long']['tp1']):
# First Long Take Profit Has Been Hit
# Move The Stop Loss To Break Even (Entry Price) & Close 50% of The Position
tracking_dict['long']['stop_loss'] = entry_price #SL Moved To Entry Price (BreakEven)
tracking_dict['tp1'] = None #take profit 1 has been hit
order_result, exec_state = close_partial_pos(
execution_state_ = exec_state,
price_area_ = price_area,
closing_percent = 50,
group_ = 0 , column_ = 0, bar_ = None,
update_value_ = False, order_records_ = None,
order_counts_ = None, log_records_ = None,
log_counts_ = None)
elif (price_area.high >= tracking_dict['long']['tp2']):
# Second Take Profit Has Been Hit
tracking_dict['long']['stop_loss'] = None
tracking_dict['long']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
else:
continue
elif exec_state.position < 0:
entry_price = tracking_dict['short']['entry_price']
if (price_area.high >= tracking_dict['short']['stop_loss']):
#Short Stop Loss Has Been Hit
tracking_dict['short']['entry_price'] = None
tracking_dict['short']['stop_loss'] = None
tracking_dict['short']['tp1'] = None
tracking_dict['short']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
elif (price_area.low <= tracking_dict['short']['tp1']):
# Frist Take Profit Has Been Hit
tracking_dict['stop_loss'] = entry_price #SL Moved To Entry Price (BreakEven)
tracking_dict['tp1'] = None
order_result, exec_state = close_partial_pos(
execution_state_ = exec_state,
price_area_ = price_area,
closing_percent = 50,
group_ = 0 , column_ = 0, bar_ = None,
update_value_ = False, order_records_ = None,
order_counts_ = None, log_records_ = None,
log_counts_ = None)
elif (price_area.low <= tracking_dict['short']['tp2']):
# Second Take Profit Has Been Hit
tracking_dict['short']['entry_price'] = None
tracking_dict['short']['stop_loss'] = None
tracking_dict['short']['tp1'] = None
tracking_dict['short']['tp2'] = None
order_result , exec_state = close_full_position(
execution_state_ = exec_state,
price_area_ = price_area,
group_ = 0, column_= 0, bar_ = i,
order_records_= order_records,
order_counts_ = order_counts)
else:
continue
return vbt.nb.repartition_nb(order_records, order_counts)
Key Points / Summary π‘
As you can see there is not a whole lot of differences when looking at partial profits and moving stop loss to breakeven.
These are just simple examples to demonstrate how one can do these things that are quite common amongst most strategies.
You can use this code as a point of reference and get creative π¨ when you are coding your own strategies β‘