StrategyGeneratorLatest / bt_strategies.py
JuanFuriaz's picture
Upload folder using huggingface_hub
63bad2b verified
Raw
History Blame Contribute Delete
16.8 kB
import backtrader as bt
import pandas as pd
class SmaCross(bt.Strategy):
"""
Simple moving average crossover strategy.
Buy when fast SMA crosses above slow SMA.
Sell when fast SMA crosses below slow SMA.
User prompt: "Go long when the 10-period SMA crosses above the 100-period SMA,
and exit when the 10-period SMA crosses below the 100-period SMA."
Call: cerebro.addstrategy(SmaCross, pfast=10, pslow=100)
"""
params = dict(pfast=10, pslow=100)
def __init__(self):
self.sma_fast = bt.ind.SMA(period=self.p.pfast)
self.sma_slow = bt.ind.SMA(period=self.p.pslow)
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
def next(self):
if not self.position:
if self.crossover > 0: # Golden cross
self.buy()
elif self.crossover < 0: # Death cross
self.close()
class TrendMomentumLongStrategy(bt.Strategy):
"""
Multi-indicator strategy with trend following and momentum
Long entries
"""
params = dict(
sma_fast=5,# 20
sma_slow=30,# 50
rsi_period=14,
rsi_upper=90, # 70
rsi_lower=30,
atr_period=14,
atr_multiplier=2.0
)
def __init__(self):
# Trend indicators
self.sma_fast = bt.ind.SMA(period=self.p.sma_fast)
self.sma_slow = bt.ind.SMA(period=self.p.sma_slow)
self.trend = self.sma_fast - self.sma_slow
# Momentum indicator
self.rsi = bt.ind.RSI(period=self.p.rsi_period)
# Volatility for position sizing
self.atr = bt.ind.ATR(period=self.p.atr_period)
# Crossovers focall_liner entry signals
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
def next(self):
# Calculate position size based on volatility (1% risk per trade)
if self.atr[0] > 0:
risk_amount = self.broker.getvalue() * 0.01
size = risk_amount / (self.atr[0] * self.p.atr_multiplier)
size = int(size)
else:
size = 100 # Default size
# Entry conditions: Golden cross + RSI not overbought
if not self.position:
if (self.crossover > 0 and
self.rsi < self.p.rsi_upper and
self.trend > 0):
self.buy(size=size)
# Exit conditions: Death cross OR RSI overbought
elif self.position:
if (self.crossover < 0 or
self.rsi > self.p.rsi_upper or
self.trend < 0):
self.close()
class TrendMomentumShortStrategy(bt.Strategy):
"""
Multi-indicator strategy with trend following and momentum
Short entries only
"""
params = dict(
sma_fast=5, # Fast SMA period
sma_slow=30, # Slow SMA period
rsi_period=14, # RSI period
rsi_upper=70, # Overbought threshold
rsi_lower=10, # Oversold threshold
atr_period=14, # ATR period for volatility-based sizing
atr_multiplier=2.0
)
def __init__(self):
# Trend indicators
self.sma_fast = bt.ind.SMA(period=self.p.sma_fast)
self.sma_slow = bt.ind.SMA(period=self.p.sma_slow)
self.trend = self.sma_fast - self.sma_slow
# Momentum indicator
self.rsi = bt.ind.RSI(period=self.p.rsi_period)
# Volatility indicator
self.atr = bt.ind.ATR(period=self.p.atr_period)
# Crossover signal
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
def next(self):
# Volatility-based position sizing (1% risk per trade)
if self.atr[0] > 0:
risk_amount = self.broker.getvalue() * 0.01
size = risk_amount / (self.atr[0] * self.p.atr_multiplier)
size = int(size)
else:
size = 100 # Fallback default
# Entry conditions: Death cross + RSI not oversold
if not self.position:
if (self.crossover < 0 and
self.rsi > self.p.rsi_lower and
self.trend < 0):
self.sell(size=size)
# Exit conditions: Golden cross OR RSI oversold
elif self.position:
if (self.crossover > 0 or
self.rsi < self.p.rsi_lower or
self.trend > 0):
self.close()
# Go long when the 10-period SMA crosses above the 100-period SMA, and exit when the 10-period SMA crosses below the 100-period SMA.
# With logging
class SmaCrossExtended(bt.Strategy):
'''
Follow two moving average lines on a stock chart to decide when to buy and sell.
'''
params = dict(pfast=10, pslow=100)
def __init__(self):
self.sma1 = bt.ind.SMA(period=self.p.pfast)
self.sma2 = bt.ind.SMA(period=self.p.pslow)
self.crossover = bt.ind.CrossOver(self.sma1, self.sma2)
# Initialize order tracking
self.order = None
def log(self, txt, dt=None):
"""Logging function for this strategy"""
dt = dt or self.datas[0].datetime.date(0)
print(f'{dt.isoformat()}: {txt}')
def notify_order(self, order):
"""Called when order status changes"""
if order.status in [order.Submitted, order.Accepted]:
# Order submitted/accepted - nothing to do
return
# Order completed
if order.status in [order.Completed]:
if order.isbuy():
self.log(f'BUY EXECUTED - Price: {order.executed.price:.2f}, '
f'Cost: {order.executed.value:.2f}, '
f'Comm: {order.executed.comm:.2f}, '
f'Size: {order.executed.size}')
else:
self.log(f'SELL EXECUTED - Price: {order.executed.price:.2f}, '
f'Cost: {order.executed.value:.2f}, '
f'Comm: {order.executed.comm:.2f}, '
f'Size: {order.executed.size}')
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Reset order
self.order = None
def notify_trade(self, trade):
"""Called when a trade is closed"""
if not trade.isclosed:
return
self.log(f'TRADE CLOSED - PnL: {trade.pnl:.2f}, PnL Net: {trade.pnlcomm:.2f}')
def next(self):
# Check if we have a pending order
if self.order:
return
if not self.position: # not in market
if self.crossover > 0: # Golden cross
self.log('BUY SIGNAL DETECTED')
self.order = self.buy()
else: # in market
if self.crossover < 0: # Death cross
self.log('SELL SIGNAL DETECTED')
self.order = self.close()
import backtrader as bt
class TrendMomentumLongStrategyTS(bt.Strategy):
"""
Multi-indicator strategy with trend following and momentum
Long entries with take profit and stop loss
"""
params = dict(
sma_fast=5,
sma_slow=30,
rsi_period=14,
rsi_upper=90,
rsi_lower=30,
atr_period=14,
atr_multiplier=2.0,
stop_loss_pct=0.05, # 5% stop loss
take_profit_pct=0.10 # 10% take profit
)
def __init__(self):
# Trend indicators
self.sma_fast = bt.ind.SMA(period=self.p.sma_fast)
self.sma_slow = bt.ind.SMA(period=self.p.sma_slow)
self.trend = self.sma_fast - self.sma_slow
# Momentum indicator
self.rsi = bt.ind.RSI(period=self.p.rsi_period)
# Volatility for position sizing
self.atr = bt.ind.ATR(period=self.p.atr_period)
# Crossovers for entry signals
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
# Track entry price for stop loss and take profit
self.entry_price = None
def next(self):
# Calculate position size based on volatility (1% risk per trade)
if self.atr[0] > 0:
risk_amount = self.broker.getvalue() * 0.01
size = risk_amount / (self.atr[0] * self.p.atr_multiplier)
size = int(size)
else:
size = 100 # Default size
# Entry conditions: Golden cross + RSI not overbought
if not self.position:
if (self.crossover > 0 and
self.rsi < self.p.rsi_upper and
self.trend > 0):
self.buy(size=size)
self.entry_price = self.data.close[0] # Track entry price
# Exit conditions: Death cross OR RSI overbought OR stop loss/take profit
elif self.position:
current_price = self.data.close[0]
# Calculate stop loss and take profit levels
stop_loss_price = self.entry_price * (1 - self.p.stop_loss_pct)
take_profit_price = self.entry_price * (1 + self.p.take_profit_pct)
# Check exit conditions
if (self.crossover < 0 or
self.rsi > self.p.rsi_upper or
self.trend < 0 or
current_price <= stop_loss_price or
current_price >= take_profit_price):
self.close()
self.entry_price = None # Reset entry price
class TrendMomentumShortStrategyTS(bt.Strategy):
"""
Multi-indicator strategy with trend following and momentum
Short entries only with take profit and stop loss
"""
params = dict(
sma_fast=5, # Fast SMA period
sma_slow=30, # Slow SMA period
rsi_period=14, # RSI period
rsi_upper=70, # Overbought threshold
rsi_lower=10, # Oversold threshold
atr_period=14, # ATR period for volatility-based sizing
atr_multiplier=2.0,
stop_loss_pct=0.05, # 5% stop loss
take_profit_pct=0.10 # 10% take profit
)
def __init__(self):
# Trend indicators
self.sma_fast = bt.ind.SMA(period=self.p.sma_fast)
self.sma_slow = bt.ind.SMA(period=self.p.sma_slow)
self.trend = self.sma_fast - self.sma_slow
# Momentum indicator
self.rsi = bt.ind.RSI(period=self.p.rsi_period)
# Volatility indicator
self.atr = bt.ind.ATR(period=self.p.atr_period)
# Crossover signal
self.crossover = bt.ind.CrossOver(self.sma_fast, self.sma_slow)
# Entry price for stop loss and take profit
self.entry_price = None
def next(self):
# Volatility-based position sizing (1% risk per trade)
if self.atr[0] > 0:
risk_amount = self.broker.getvalue() * 0.01
size = risk_amount / (self.atr[0] * self.p.atr_multiplier)
size = int(size)
else:
size = 100 # Fallback default
# Entry conditions: Death cross + RSI not oversold
if not self.position:
if (self.crossover < 0 and
self.rsi > self.p.rsi_lower and
self.trend < 0):
self.sell(size=size)
self.entry_price = self.data.close[0] # Track entry price
# Exit conditions: Golden cross OR RSI oversold OR stop loss/take profit
elif self.position:
current_price = self.data.close[0]
# Calculate stop loss and take profit levels
# For short positions:
# - Stop loss triggers when price goes UP (price > entry * (1 + stop_loss_pct))
# - Take profit triggers when price goes DOWN (price < entry * (1 - take_profit_pct))
stop_loss_price = self.entry_price * (1 + self.p.stop_loss_pct) # Stop loss above entry
take_profit_price = self.entry_price * (1 - self.p.take_profit_pct) # Take profit below entry
if (self.crossover > 0 or # Golden cross
self.rsi < self.p.rsi_lower or # RSI oversold
self.trend > 0 or # Trend turned positive
current_price >= stop_loss_price or # Stop loss hit (price went up)
current_price <= take_profit_price): # Take profit hit (price went down)
self.close()
self.entry_price = None # Reset entry price
class ScalpingBB(bt.Strategy):
"""
Scalping strategy using Bollinger Bands with RSI during high volatility periods.
Only trades during specific time windows for 1-minute.
"""
params = dict(
bb_period=20,
bb_dev=2.0,
rsi_period=14,
rsi_oversold=30,
rsi_overbought=70,
start_hour=9, # 9:00 AM
end_hour=16, # 4:00 PM
position_size=100
)
def __init__(self):
# Bollinger Bands indicator
self.bb = bt.ind.BollingerBands(
period=self.p.bb_period,
devfactor=self.p.bb_dev
)
# RSI indicator for confirmation
self.rsi = bt.ind.RSI(
period=self.p.rsi_period
)
# Track current time
self.current_time = None
def is_trading_hours(self):
"""Check if current time is within trading hours"""
if self.current_time is None:
return False
hour = self.current_time.hour
minute = self.current_time.minute
# Check if within 9:00 AM to 4:00 PM
if hour < self.p.start_hour or hour >= self.p.end_hour:
return False
return True
def next(self):
# Get current datetime
self.current_time = self.data.datetime.datetime()
# Only trade during specified hours
if not self.is_trading_hours():
if self.position:
self.close()
return
# Check for buy signal (price touches lower band, RSI oversold)
if self.data.close[0] <= self.bb.lines.bot[0] and self.rsi[0] <= self.p.rsi_oversold:
if not self.position:
self.buy(size=self.p.position_size)
# Check for sell signal (price touches upper band, RSI overbought)
elif self.data.close[0] >= self.bb.lines.top[0] and self.rsi[0] >= self.p.rsi_overbought:
if self.position:
self.sell(size=self.p.position_size)
# Exit if price returns to middle band
elif self.position:
if abs(self.data.close[0] - self.bb.lines.mid[0]) < (self.bb.lines.top[0] - self.bb.lines.mid[0]) * 0.3:
self.close()
class TripleMACross(bt.Strategy):
"""
Triple moving average crossover strategy with 10% position sizing.
- Entry: Fast SMA (5) crosses above Medium SMA (10) and Medium SMA is above Slow SMA (20)
- Exit: Fast SMA crosses below Medium SMA OR Medium SMA crosses below Slow SMA
- Position sizing: 10% of portfolio per trade
"""
params = (
('fast_period', 5),
('medium_period', 10),
('slow_period', 20),
)
def __init__(self):
# Three moving averages
self.sma_fast = bt.indicators.SMA(period=self.p.fast_period)
self.sma_medium = bt.indicators.SMA(period=self.p.medium_period)
self.sma_slow = bt.indicators.SMA(period=self.p.slow_period)
# Crossover indicators
self.cross_fast_medium = bt.indicators.CrossOver(self.sma_fast, self.sma_medium)
self.cross_medium_slow = bt.indicators.CrossOver(self.sma_medium, self.sma_slow)
# Track position for conditional logic
self.position_open = False
def next(self):
# Entry condition: Fast crosses above Medium AND Medium > Slow (no existing position)
if not self.position:
if self.cross_fast_medium > 0 and self.sma_medium[0] > self.sma_slow[0]:
self.buy(size=self.get_target_size()) # Use dynamic sizing
self.position_open = True
# Exit conditions: Fast crosses below Medium OR Medium crosses below Slow
elif self.position_open:
if self.cross_fast_medium < 0 or self.cross_medium_slow < 0:
self.close()
self.position_open = False
def get_target_size(self):
"""Calculate 10% of current portfolio value"""
return int((self.broker.getvalue() * 0.90) / self.data.close[0])