Momentum Entries with Martingale Averaging in Crypto Futures
Summary
This futures strategy enters a long position after a coin posts a sufficiently strong gain over an hourly or four-hour bar, while filtering out bars with a large upper wick. Once in a position, it tracks the highest price and exits after the position has gained enough and price has pulled back from that high. If price falls a set amount below the most recent entry, it adds to the position with successively larger orders, subject to a configured increase limit. The implementation also tracks average entry price, order state, minimum order value, and estimated fees.
The document supplies source code and parameter examples, but no backtest, live-trading results, or asset-selection evidence. The averaging rule increases exposure as price moves against the position, so the configured limit does not itself establish a safe maximum loss. The source also focuses on long entries and does not explain margin, liquidation, portfolio-level exposure, or how the strategy behaves across simultaneous positions.
Key ideas
- Long entries are triggered by strong hourly or four-hour gains, with a filter for large upper wicks.
- Profitable positions exit after a pullback from the highest price reached since entry.
- The strategy adds progressively larger orders after price falls far enough below the latest entry.
- The code tracks average price, position size, order status, and estimated trading fees.
- No backtest or evidence about drawdowns, liquidation risk, or live performance is provided.
Tags
Full text
# MartingleFutureStrategyV3
# MartingleFutureStrategyV3
1. 马丁策略.
币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant
币安合约邀请码:51bitquant
## 策略思路
1. 挑选1小时涨幅超过2.6%的币,或者4小涨幅超过4.6%的币, 然后入场
2. 利润超过1%,且最高价回调1%后平仓,当然你可以选择自己的参数
3. 如果入场后,没有利润,价格继续下跌。那么入场价格下跌5%后,采用马丁策略加仓。
## Source (MIT)
```python
from howtrader.app.cta_strategy import (
CtaTemplate,
StopOrder
)
from howtrader.trader.object import TickData, BarData, TradeData, OrderData
from howtrader.app.cta_strategy.engine import CtaEngine
from howtrader.trader.object import Status, Direction, Interval, ContractData, AccountData
from typing import Optional
from howtrader.trader.utility import BarGenerator
from decimal import Decimal
class MartingleFutureStrategyV3(CtaTemplate):
"""
1. 马丁策略.
币安邀请链接: https://www.binancezh.pro/cn/futures/ref/51bitquant
币安合约邀请码:51bitquant
## 策略思路
1. 挑选1小时涨幅超过2.6%的币,或者4小涨幅超过4.6%的币, 然后入场
2. 利润超过1%,且最高价回调1%后平仓,当然你可以选择自己的参数
3. 如果入场后,没有利润,价格继续下跌。那么入场价格下跌5%后,采用马丁策略加仓。
"""
author = "51bitquant"
# 策略的核心参数.
initial_trading_value = 200 # 首次开仓价值 1000USDT.
trading_value_multiplier = 2 # 加仓的比例.
max_increase_pos_count = 5 # 最大的加仓次数
hour_pump_pct = 0.026 # 小时的上涨百分比
four_hour_pump_pct = 0.046 # 四小时的上涨百分比.
high_close_change_pct = 0.03 # 最高价/收盘价 -1, 防止上引线过长.
increase_pos_when_dump_pct = 0.05 # 价格下跌 5%就继续加仓.
exit_profit_pct = 0.01 # 出场平仓百分比 1%
exit_pull_back_pct = 0.01 # 最高价回调超过1%,且利润超过1% 就出场.
trading_fee = 0.00075 # 交易手续费
# 变量
avg_price = 0.0 # 当前持仓的平均价格.
last_entry_price = 0.0 # 上一次入场的价格.
entry_highest_price = 0.0
current_pos = 0.0 # 当前的持仓的数量.
current_increase_pos_count = 0 # 当前的加仓的次数.
total_profit = 0 # 统计总的利润.
parameters = ["initial_trading_value", "trading_value_multiplier", "max_increase_pos_count",
"hour_pump_pct", "four_hour_pump_pct", "high_close_change_pct", "increase_pos_when_dump_pct",
"exit_profit_pct",
"exit_pull_back_pct", "trading_fee"]
variables = ["avg_price", "last_entry_price", "entry_highest_price", "current_pos", "current_increase_pos_count",
"total_profit"]
def __init__(self, cta_engine: CtaEngine, strategy_name, vt_symbol, setting):
""""""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.last_filled_order: Optional[OrderData] = None
self.tick: Optional[TickData] = None
self.contract: Optional[ContractData] = None
self.account: Optional[AccountData] = None
self.bg_1hour = BarGenerator(self.on_bar, 1, on_window_bar=self.on_1hour_bar, interval=Interval.HOUR) # 1hour
self.bg_4hour = BarGenerator(self.on_bar, 4, on_window_bar=self.on_4hour_bar, interval=Interval.HOUR) # 4hour
# self.cta_engine.event_engine.register(EVENT_ACCOUNT + 'BINANCE.币名称', self.process_acccount_event)
# self.cta_engine.event_engine.register(EVENT_ACCOUNT + "BINANCE.USDT", self.process_account_event)
self.buy_orders = [] # 买单id列表。
self.sell_orders = [] # 卖单id列表。
self.min_notional = 11 # 最小的交易金额.
def on_init(self):
"""
Callback when strategy is inited.
"""
self.write_log("策略初始化")
self.load_bar(3) # 加载3天的数据.
def on_start(self):
"""
Callback when strategy is started.
"""
self.write_log("策略启动")
def on_stop(self):
"""
Callback when strategy is stopped.
"""
self.write_log("策略停止")
# def process_account_event(self, event: Event):
# self.account: AccountData = event.data
# if self.account:
# print(
# f"self.account: available{self.account.available}, balance:{self.account.balance}, frozen: {self.account.frozen}")
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
if tick.bid_price_1 > 0 and tick.ask_price_1 > 0:
self.bg_1hour.update_tick(tick)
self.bg_4hour.update_tick(tick)
def on_bar(self, bar: BarData):
"""
Callback of new bar data update.
"""
if self.entry_highest_price > 0:
self.entry_highest_price = max(bar.high_price, self.entry_highest_price)
if self.current_pos * bar.close_price >= self.min_notional:
if len(self.sell_orders) <= 0 < self.avg_price:
# 有利润平仓的时候
# 清理掉其他买单.
profit_percent = bar.close_price / self.avg_price - 1
profit_pull_back_pct = self.entry_highest_price / bar.close_price - 1
if profit_percent >= self.exit_profit_pct and profit_pull_back_pct >= self.exit_pull_back_pct:
self.cancel_all()
orderids = self.short(Decimal(bar.close_price), Decimal(abs(self.current_pos)))
self.sell_orders.extend(orderids)
if len(self.buy_orders) <= 0:
# 考虑加仓的条件: 1) 当前有仓位,且仓位值要大于11USDTyi以上,2)加仓的次数小于最大的加仓次数,3)当前的价格比上次入场的价格跌了一定的百分比。
dump_down_pct = self.last_entry_price / bar.close_price - 1
if self.current_increase_pos_count <= self.max_increase_pos_count and dump_down_pct >= self.increase_pos_when_dump_pct:
# ** 表示的是乘方.
self.cancel_all() # 清理其他卖单.
increase_pos_value = self.initial_trading_value * self.trading_value_multiplier ** self.current_increase_pos_count
price = bar.close_price
vol = increase_pos_value / price
orderids = self.buy(Decimal(price), Decimal(vol))
self.buy_orders.extend(orderids)
self.bg_1hour.update_bar(bar)
self.bg_4hour.update_bar(bar)
self.put_event()
def on_1hour_bar(self, bar: BarData):
close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少.
high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线
# 回调一定比例的时候.
if self.current_pos * bar.close_price < self.min_notional:
# 每次下单要大于等于10USDT, 为了简单设置11USDT.
if close_change_pct >= self.hour_pump_pct and high_change_pct < self.high_close_change_pct and len(
self.buy_orders) == 0:
# 这里没有仓位.
# 重置当前的数据.
self.cancel_all()
self.current_increase_pos_count = 0
self.avg_price = 0
self.entry_highest_price = 0.0
price = bar.close_price
vol = self.initial_trading_value / price
orderids = self.buy(Decimal(price), Decimal(vol))
self.buy_orders.extend(orderids) # 以及已经下单的orderids.
def on_4hour_bar(self, bar: BarData):
close_change_pct = bar.close_price / bar.open_price - 1 # 收盘价涨了多少.
high_change_pct = bar.high_price / bar.close_price - 1 # 计算上引线
# 回调一定比例的时候.
if self.current_pos * bar.close_price < self.min_notional:
# 每次下单要大于等于10USDT, 为了简单设置11USDT.
if close_change_pct >= self.four_hour_pump_pct and high_change_pct < self.high_close_change_pct and len(
self.buy_orders) == 0:
# 这里没有仓位.
# 重置当前的数据.
self.cancel_all()
self.current_increase_pos_count = 0
self.avg_price = 0
self.entry_highest_price = 0.0
price = bar.close_price
vol = self.initial_trading_value / price
orderids = self.buy(Decimal(price), Decimal(vol))
self.buy_orders.extend(orderids) # 以及已经下单的orderids.
def on_order(self, order: OrderData):
"""
Callback of new order data update.
"""
if order.status == Status.ALLTRADED:
if order.direction == Direction.LONG:
# 买单成交.
self.current_increase_pos_count += 1
self.last_entry_price = float(order.price) # 记录上一次成绩的价格.
self.entry_highest_price = float(order.price)
if not order.is_active():
if order.vt_orderid in self.sell_orders:
self.sell_orders.remove(order.vt_orderid)
elif order.vt_orderid in self.buy_orders:
self.buy_orders.remove(order.vt_orderid)
self.put_event() # 更新UI使用.
def on_trade(self, trade: TradeData):
"""
Callback of new trade data update.
"""
if trade.direction == Direction.LONG:
total = self.avg_price * self.current_pos + float(trade.price) * float(trade.volume)
self.current_pos += float(trade.volume)
self.avg_price = total / self.current_pos
elif trade.direction == Direction.SHORT:
self.current_pos -= float(trade.volume)
# 计算统计下总体的利润.
profit = (float(trade.price) - self.avg_price) * float(trade.volume)
total_fee = float(trade.volume) * float(trade.price) * 2 * self.trading_fee
self.total_profit += profit - total_fee
self.put_event()
def on_stop_order(self, stop_order: StopOrder):
"""
Callback of stop order update.
"""
pass
```Shown in full with attribution under the source's licence. Licence: MIT
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.