Firetron's Hedged DCA Dipper

stable
By Firetron in Trading Bots Published August 2020 👁 6,240 views 💬 18 comments ★ Staff Pick

Description

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.

F
Firetron almost 6 years ago

Named "Dipper" because if you set the Expansion Trigger to 0% or less then it only "buys the dips".

F
Firetron almost 6 years ago

Decent setting for BTC:
Expand By: 0.01
Expansion Trigger: -5
Interval: 5 minutes
Profit Trigger: 2

F
Firetron almost 6 years ago

Better setting for BTC:

Expand By: 0.01
Expansion Trigger: -5
Interval: 4 hour
Profit Trigger: 5

B
beamtradingx over 5 years ago

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)

F
Firetron over 5 years ago

Does the exchange support hedge and have it enabled?

B
beamtradingx over 5 years ago

Yes the test was against a Binance futures account with hedge enabled.

F
Firetron over 5 years ago

That's where I run it. Have you tried again more recently?

B
beamtradingx over 5 years ago

Tried again and its now working!

B
beamtradingx over 5 years ago

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.

F
Firetron over 5 years ago

That is expected depending on the settings and how trending the market is.

G
guarguaser over 4 years ago

But this bot doesn't work with Binace spot?

F
Firetron over 4 years ago

Correct. It is called hedged because it only works in Hedge mode, which is not compatible with spot.

F
fj2 over 4 years ago

Hi, how can I add the dependency scripts?

F
Firetron over 4 years ago

To learn the basics of how to use the Haas Trade Server's HaasScript Editor, please join the Discord group, or read the documentation.

R
Rahul420 over 4 years ago

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.

F
Firetron over 2 years ago

You need to copy all the CC to your account before using the script.

R
ryan over 2 years ago

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

F
Firetron over 2 years ago

Which exchange and market?