Firetron's Hedged DCA Dipper
stableDescription
Runs a long and a short each doing dollar cost averaging strategy.
Expands positions on an interval and exits whenever there is a profit.
Only expands a position when its profit is less than a trigger.
Custom Command Dependencies:
Firetron’s CurrentVsEntry
Firetron’s FormatRoundedPercent
Firetron’s FormatRoundedQuoteCurrency
Firetron’s ReportMaxRiskPoint
Firetron’s ReportOpenPositions
Firetron’s TrueOnInterval
HaasScript
-- ============================================================================
-- Firetron's Hedged DCA Dipper
--
-- Runs a long and a short each doing dollar cost averaging strategy.
-- Expands positions on an interval and exits whenever there is a profit.
-- Only expands a position when its profit is less than a trigger.
--
-- Custom Command Dependencies:
-- Firetron's CurrentVsEntry
-- Firetron's FormatRoundedPercent
-- Firetron's FormatRoundedQuoteCurrency
-- Firetron's ReportMaxRiskPoint
-- Firetron's ReportOpenPositions
-- Firetron's TrueOnInterval
--
-- Discord: @FiretronP75
-- ============================================================================
-- ========================================================
-- Configuration
-- ========================================================
EnableHighSpeedUpdates()
HideOrderSettings()
HideTradeAmountSettings()
-- ========================================================
-- Variables
-- ========================================================
local logHRule = '------------------------------------------------------------';
-- ------------------------------------
-- Enumerations
-- ------------------------------------
local booleanOption = {
no = 'No',
yes = 'Yes',
}
local ProfitType = {
marketChange = 'Market Change',
positionROI = 'Position ROI',
}
-- ------------------------------------
-- Positions
-- ------------------------------------
local longPositionId = Load('longPositionId', '')
local shortPositionId = Load('shortPositionId', '')
-- ========================================================
-- Inputs
-- ========================================================
local group = ''
local label = ''
local tooltip = ''
-- ------------------------------------
-- Testing
-- ------------------------------------
local fee = Fee()
local isVerbose = booleanOption.no
local verbosityInterval = 60
group = ' Testing'
label = 'Fee Percentage'
tooltip = 'For backtests and simulated trading.'
fee = Input(label, fee, tooltip, group)
label = 'Verbose Logging'
tooltip = 'Log extra details. Will run slower.'
isVerbose = InputOptions(label, isVerbose, booleanOption, tooltip, group)
label = 'Verbose Logging Interval'
tooltip = 'Time between verbose logging.'
verbosityInterval = InputInterval(label, verbosityInterval, tooltip, group)
-- ------------------------------------
-- Main
-- ------------------------------------
local expandBy = MinimumTradeAmount()
local expansionTrigger = -5
local interval = 360
local profitTrigger = 5
local profitType = ProfitType.positionROI
group = 'Main'
label = 'Expand By'
tooltip = 'Number of contracts to expand by each interval.'
expandBy = Input(label, expandBy, tooltip, group)
label = 'Expansion Trigger'
tooltip = 'Only expand when percent profit is below this. Ignored if set higher than Profit Trigger.'
expansionTrigger = Input(label, expansionTrigger, tooltip, group)
label = 'Interval'
tooltip = 'Time between position expansions.'
interval = InputInterval(label, interval, tooltip, group)
label = 'Profit Trigger'
tooltip = 'Percent profit that will trigger an exit.'
profitTrigger = Input(label, profitTrigger, tooltip, group)
label = 'Profit Type'
tooltip = 'Calculate profit using either position ROI or simple change in market price. In other words, you are specifying whether or not your profit trigger is taking leverage into account or not.'
profitType = InputOptions(label, profitType, ProfitType, tooltip, group)
-- ========================================================
-- Functions
-- ========================================================
-- ------------------------------------
-- Debug
-- ------------------------------------
function GetDebugInfo ()
if isVerbose == booleanOption.no then
return {
isVerbose = false,
isVerboseInterval = false,
}
end
local currentPrice = CurrentPrice()
if not CC_TrueOnInterval(verbosityInterval) then
return {
baseCurrency = BaseCurrency(),
currentAsk = CC_FormatRoundedQuoteCurrency(currentPrice.ask),
currentBid = CC_FormatRoundedQuoteCurrency(currentPrice.bid),
isVerbose = true,
isVerboseInterval = false,
}
end
return {
baseCurrency = BaseCurrency(),
currentAsk = CC_FormatRoundedQuoteCurrency(currentPrice.ask),
currentBid = CC_FormatRoundedQuoteCurrency(currentPrice.bid),
isVerbose = true,
isVerboseInterval = true,
}
end
-- ----------------
function ResetSafeLog ()
Save('lastLog', '')
end
-- ----------------
function SafeLog (text)
if text == Load('lastLog', '') then return end
Log(text)
Save('lastLog', text)
end
-- ----------------
function SafeLogWarning (text)
if text == Load('lastLog', '') then return end
LogWarning(text)
Save('lastLog', text)
end
-- ------------------------------------
-- Exiting
-- ------------------------------------
function Exit ()
local debugInfo = GetDebugInfo()
ExitCheckLong( debugInfo)
ExitCheckShort(debugInfo)
end
-- ----------------
function ExitCheckLong (debugInfo)
local longProfit
if longPositionId != '' then
longProfit = profitType == ProfitType.positionROI and GetPositionROI(longPositionId) or CC_CurrentVsEntry(longPositionId)
end
if longPositionId == '' then
if debugInfo.isVerboseInterval then
SafeLog(logHRule)
SafeLog('No long position.')
end
elseif longProfit >= profitTrigger then
if debugInfo.isVerbose then
SafeLog(logHRule)
end
ExitLong()
if debugInfo.isVerbose then ResetSafeLog() end
elseif debugInfo.isVerboseInterval then
SafeLog(logHRule)
SafeLog('Not exiting long.')
SafeLog('Long profit is '..CC_FormatRoundedPercent(longProfit))
SafeLog(debugInfo.baseCurrency..' bid is @ '..debugInfo.currentBid)
end
end
-- ----------------
function ExitCheckShort (debugInfo)
local shortProfit
if shortPositionId != '' then
shortProfit = profitType == ProfitType.positionROI and GetPositionROI(shortPositionId) or CC_CurrentVsEntry(shortPositionId)
end
if shortPositionId == '' then
if debugInfo.isVerboseInterval then
SafeLog(logHRule)
SafeLog('No short position.')
end
elseif shortProfit >= profitTrigger then
if debugInfo.isVerbose then
SafeLog(logHRule)
end
ExitShort()
if debugInfo.isVerbose then ResetSafeLog() end
elseif debugInfo.isVerboseInterval then
SafeLog(logHRule)
SafeLog('Not exiting short.')
SafeLog('Short profit is '..CC_FormatRoundedPercent(shortProfit))
SafeLog(debugInfo.baseCurrency..' ask is @ '..debugInfo.currentAsk)
end
end
-- ----------------
function ExitLong ()
local parameters = {
positionId = longPositionId,
type = MarketOrderType,
}
SafeLogWarning('Taking profit on long.')
PlaceExitPositionOrder(parameters);
Save('longPositionId', '')
end
-- ----------------
function ExitShort ()
local parameters = {
positionId = shortPositionId,
type = MarketOrderType,
}
SafeLogWarning('Taking profit on short.')
PlaceExitPositionOrder(parameters);
Save('shortPositionId', '')
end
-- ------------------------------------
-- Going
-- ------------------------------------
function Go ()
local debugInfo = GetDebugInfo()
if debugInfo.isVerbose then
SafeLog(logHRule)
end
GoCheckLong( debugInfo)
GoCheckShort(debugInfo)
end
-- ----------------
function GoCheckLong (debugInfo)
local longProfit
if longPositionId != '' then
longProfit = CC_CurrentVsEntry(longPositionId)
end
if longPositionId == '' then
longPositionId = NewGuid()
Save('longPositionId', longPositionId)
GoLong()
if debugInfo.isVerbose then ResetSafeLog() end
elseif longProfit <= expansionTrigger then
GoLong()
if debugInfo.isVerbose then ResetSafeLog() end
elseif debugInfo.isVerboseInterval then
SafeLog('Not going long.')
SafeLog('Long profit is '..CC_FormatRoundedPercent(longProfit))
SafeLog(debugInfo.baseCurrency..' bid is @ '..debugInfo.currentBid)
end
end
-- ----------------
function GoCheckShort (debugInfo)
local shortProfit
if shortPositionId != '' then
shortProfit = CC_CurrentVsEntry(shortPositionId)
end
if shortPositionId == '' then
shortPositionId = NewGuid()
Save('shortPositionId', shortPositionId)
GoShort()
if debugInfo.isVerbose then ResetSafeLog() end
elseif shortProfit <= expansionTrigger then
GoShort()
if debugInfo.isVerbose then ResetSafeLog() end
elseif debugInfo.isVerboseInterval then
SafeLog('Not going short.')
SafeLog('Short profit is '..CC_FormatRoundedPercent(shortProfit))
SafeLog(debugInfo.baseCurrency..' ask is @ '..debugInfo.currentAsk)
end
end
-- ----------------
function GoLong ()
local parameters = {
type = MarketOrderType,
positionId = longPositionId,
}
PlaceGoLongOrder(1, expandBy, parameters)
end
-- ----------------
function GoShort ()
local parameters = {
type = MarketOrderType,
positionId = shortPositionId,
}
PlaceGoShortOrder(1, expandBy, parameters)
end
-- ========================================================
-- Execution
-- ========================================================
SetFee(fee)
CC_ReportOpenPositions()
CC_ReportMaxRiskPoint()
Exit()
OptimizedForInterval(interval, Go)
18 Comments
Sign in to leave a comment.
Named "Dipper" because if you set the Expansion Trigger to 0% or less then it only "buys the dips".
Decent setting for BTC:
Expand By: 0.01
Expansion Trigger: -5
Interval: 5 minutes
Profit Trigger: 2
Better setting for BTC:
Expand By: 0.01
Expansion Trigger: -5
Interval: 4 hour
Profit Trigger: 5
I get this error right after starting it:
32. 18 Jan 2021 11:10:45 ERROR: Blocking order. . Insufficient funds. Requires 1 contract(s). Wallet only has 0 contract(s)
Does the exchange support hedge and have it enabled?
Yes the test was against a Binance futures account with hedge enabled.
That's where I run it. Have you tried again more recently?
Tried again and its now working!
Ok its working however I see that the bot seems to lose awareness of open positions and it starts to open more and more new positions on both sides seemingly not closing some positions. If you would like a screenshot please let me know and I can chat you on discord.
That is expected depending on the settings and how trending the market is.
But this bot doesn't work with Binace spot?
Correct. It is called hedged because it only works in Hedge mode, which is not compatible with spot.
Hi, how can I add the dependency scripts?
To learn the basics of how to use the Haas Trade Server's HaasScript Editor, please join the Discord group, or read the documentation.
Hi,
I am a new user. I get the following error messages when I run the script. I am not sure what it's referencing to.
26 Feb 2022 14:23:00 ERROR: The script is invalid.18. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_ReportMaxRiskPoint17. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_ReportOpenPositions16. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedPercent15. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_CurrentVsEntry14. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedPercent13. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_CurrentVsEntry12. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedPercent11. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_CurrentVsEntry10. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedPercent9. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_CurrentVsEntry8. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedQuoteCurrency7. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedQuoteCurrency6. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedQuoteCurrency5. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_FormatRoundedQuoteCurrency4. 26 Feb 2022 14:23:00 ERROR: Unknown references: CC_TrueOnInterval3. 26 Feb 2022 14:23:00 WARNING: No order handling command detected. Use the command in the Order Handling category to check for open, cancel or filled orders.
You need to copy all the CC to your account before using the script.
Hey @firetron, very cool script. What would be the recommended wallet balance for position sizes of 1000 contracts? I'm just testing it now on simulation and ran some back tests, but my current account is limited for back test period? Also historically, what was the biggest drawdown you've seen considering its almost delta neutral most of the time. Thank you for you time, best regards. Ryan
Which exchange and market?