So! Ich habe mein VBA Makro gebugfixt und erweitert.
Zunächst muss die Pionex CSV am besten mittels MS Excel CSV-Import-Assistenten in Excel importiert werden. Das Worksheet habe ich „Tabelle1“ genannt und referenziere diesen Namen auch im Makro.
Das Makro kann dann über das Menü > Entwicklertools oder mit Alt+F8 ausgewählt werden. ich selbst habe mit über Menü > Entwicklertools einen Button ins Worksheet „Tabelle1“ gepackt und dem das Makro „GridBotMengenMatchingImport_AllBots“ zugewiesen. Der Lauf kann eine Weile dauern, bei mir waren es mehr als 92.000 Einträge, sodass es einige Minuten dauerte. Nachdem das Makro fertig ist, gebe ich auch eine MsgBox mit einer kleinen Zusammenfassung aus, u.a. auch, wie lange der Übertrag dauerte. Die Zieltabelle liegt dann im Worksheet „Sheet1“ und wird automatisch angelegt, sollte es noch nicht existieren.
Anschließend ein Rechtsklick auf den Tab für das Worksheet „Sheet1“ > Verschieben oder kopieren… > ganz unten: „Kopie erstellen“ auswählen > ganz oben: „Zur Mappe:“ > (neue Arbeitsmappe). Dann wird ein separate Excel Datei/Mappe erstellt, die ihr abspeichern und in Blockpit hochladen könnt.
Das Makro führt eine Mengenberechnung durch und kennzeichnet die Bot-Trades als Derivative Profit bzw. Derivative Loss und der Bot-Start wird auch erkannt. Als Kriterium nehme ich derzeit die im Vergleich zu den folgenden Bot-Trades erheblich größere executed_qty aus der importierten CSV. Mglw. müsste geprüft werden, ob das als Unterscheidungskriterium wirklich ausreichend ist, bspw. falls es mehrere Bots für ein gleiches Asset-Paar gibt. In der Spalte Label wird der Bot-Start nicht als Derivative Profit/Loss sondern als Trade eingetragen.
Sub GridBotMengenMatchingImport_AllBots()
Const maxTestZeilen As Long = 0 ' > 0, um Test-Lauf zu aktivieren
Dim testModusAktiv As Boolean: testModusAktiv = (maxTestZeilen > 0)
Dim wsQ As Worksheet, wsZ As Worksheet
Dim letzteQ As Long, zielStart As Long, ziel As Long
Dim i As Long, j As Long
Dim dictBUY As Object, dictSELL As Object
Dim buyList As Collection, sellList As Collection
Dim key As Variant, entryBUY As Variant, entrySELL As Variant
Dim iBUY As Long, iSELL As Long, totalBUYAmount As Double, totalBUYValue As Double
Dim remainingSELLAmount As Double, sellPrice As Double
Dim label As String, kommentar As String
Dim profitCount As Long, lossCount As Long, unmatchedBUYs As Long
Dim buyCountTotal As Long, sellCountTotal As Long, otherCountTotal As Long
Dim writtenCount As Long, strategyType As String
Dim startZeit As Double, laufzeit As Double
Dim hh As Long, mm As Long, ss As Long, formatZeit As String
Dim originalDezimal As String, originalTausender As String
Dim usedBUYIndexes As Object: Set usedBUYIndexes = CreateObject("Scripting.Dictionary")
Set wsQ = Worksheets("Tabelle1")
On Error Resume Next
Set wsZ = Worksheets("Sheet1")
On Error GoTo 0
If wsZ Is Nothing Then
Set wsZ = Worksheets.Add
wsZ.Name = "Sheet1"
MsgBox "Ziel-Sheet 'Sheet1' wurde automatisch erstellt.", vbInformation
wsZ.Range("A1:K1").Value = Array( _
"Date (UTC)", "Integration Name", "Label", "Outgoing Asset", "Outgoing Amount", _
"Incoming Asset", "Incoming Amount", "Fee Asset (optional)", "Fee Amount (optional)", _
"Comment (optional)", "Trx. ID (optional)")
End If
With wsZ
Dim letzteZielzeile As Long
letzteZielzeile = .Cells(.Rows.count, 1).End(xlUp).Row
If letzteZielzeile > 1 Then .Rows("2:" & letzteZielzeile).ClearContents
.Columns("A").NumberFormat = "dd.mm.yyyy hh:mm"
End With
letzteQ = wsQ.Cells(wsQ.Rows.count, 1).End(xlUp).Row
zielStart = wsZ.Cells(wsZ.Rows.count, 1).End(xlUp).Row + 1
ziel = zielStart
startZeit = Timer
Set dictBUY = CreateObject("Scripting.Dictionary")
Set dictSELL = CreateObject("Scripting.Dictionary")
With Application
originalDezimal = .DecimalSeparator
originalTausender = .ThousandsSeparator
.UseSystemSeparators = False
.DecimalSeparator = "."
.ThousandsSeparator = ""
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
For i = 2 To letzteQ
strategyType = UCase(wsQ.Cells(i, 11).Text)
If InStr(strategyType, "GRID") > 0 Then
Dim symbol As String, side As String
symbol = UCase(Trim(wsQ.Cells(i, 7).Text))
side = UCase(wsQ.Cells(i, 6).Text)
If side = "BUY" Then
If IstBotStart(wsQ, i) Then
Call SchreibeZeile(wsQ, wsZ, i, ziel, "Trade", "Bot-Start: Initialkauf")
ziel = ziel + 1
writtenCount = writtenCount + 1
otherCountTotal = otherCountTotal + 1
If testModusAktiv And ziel >= maxTestZeilen + zielStart Then GoTo EndeMatching
Else
If Not dictBUY.exists(symbol) Then dictBUY.Add symbol, New Collection
dictBUY(symbol).Add Array(i, wsQ.Cells(i, 3).Value, wsQ.Cells(i, 4).Value)
buyCountTotal = buyCountTotal + 1
End If
ElseIf side = "SELL" Then
If Not dictSELL.exists(symbol) Then dictSELL.Add symbol, New Collection
dictSELL(symbol).Add Array(i, wsQ.Cells(i, 3).Value, wsQ.Cells(i, 4).Value)
sellCountTotal = sellCountTotal + 1
End If
Else
Call SchreibeZeile(wsQ, wsZ, i, ziel, "Trade", "Direktübertrag")
ziel = ziel + 1
writtenCount = writtenCount + 1
otherCountTotal = otherCountTotal + 1
If testModusAktiv And ziel >= maxTestZeilen + zielStart Then GoTo EndeMatching
End If
Next i
For Each key In dictSELL.Keys
Set sellList = dictSELL(key)
If dictBUY.exists(key) Then Set buyList = dictBUY(key) Else Set buyList = New Collection
For i = 1 To sellList.count
entrySELL = sellList(i)
iSELL = entrySELL(0)
remainingSELLAmount = entrySELL(1)
sellPrice = entrySELL(2)
totalBUYAmount = 0
totalBUYValue = 0
j = buyList.count
Do While j >= 1 And remainingSELLAmount > 0
entryBUY = buyList(j)
iBUY = entryBUY(0)
Dim amtBUY As Double: amtBUY = entryBUY(1)
Dim prcBUY As Double: prcBUY = entryBUY(2)
Dim usedAmount As Double
If amtBUY <= remainingSELLAmount Then
usedAmount = amtBUY
buyList.Remove j
Else
usedAmount = remainingSELLAmount
entryBUY(1) = amtBUY - remainingSELLAmount
buyList.Remove j
buyList.Add entryBUY
End If
If Not usedBUYIndexes.exists(iBUY) Then
Call SchreibeZeile(wsQ, wsZ, iBUY, ziel, "Derivative Profit", "BUY zur Deckung verwendet")
ziel = ziel + 1
writtenCount = writtenCount + 1
usedBUYIndexes.Add iBUY, True
If testModusAktiv And ziel >= maxTestZeilen + zielStart Then GoTo EndeMatching
End If
totalBUYAmount = totalBUYAmount + usedAmount
totalBUYValue = totalBUYValue + usedAmount * prcBUY
remainingSELLAmount = remainingSELLAmount - usedAmount
j = j - 1
Loop
If totalBUYAmount > 0 Then
Dim avgBUYPrice As Double: avgBUYPrice = totalBUYValue / totalBUYAmount
If sellPrice > avgBUYPrice Then
label = "Derivative Profit": profitCount = profitCount + 1
Else
label = "Derivative Loss": lossCount = lossCount + 1
End If
If remainingSELLAmount > 0 Then
label = "Trade"
kommentar = "SELL nur teilweise gedeckt – BUYs aus 2024 reichen nicht vollständig"
Else
kommentar = ""
End If
Else
label = "Trade"
kommentar = "SELL nicht gedeckt in 2024 – Deckung ggf. außerhalb Betrachtung"
End If
Call SchreibeZeile(wsQ, wsZ, iSELL, ziel, label, kommentar)
ziel = ziel + 1
writtenCount = writtenCount + 1
If testModusAktiv And ziel >= maxTestZeilen + zielStart Then GoTo EndeMatching
Next i
Next key
For Each key In dictBUY.Keys
Set buyList = dictBUY(key)
For i = 1 To buyList.count
iBUY = buyList(i)(0)
If Not usedBUYIndexes.exists(iBUY) Then
Call SchreibeZeile(wsQ, wsZ, iBUY, ziel, "Derivative Profit", "Nicht verbrauchte BUY-Trx")
ziel = ziel + 1
writtenCount = writtenCount + 1
unmatchedBUYs = unmatchedBUYs + 1
If testModusAktiv And ziel >= maxTestZeilen + zielStart Then GoTo EndeMatching
End If
Next i
Next key
EndeMatching:
If Not testModusAktiv Then
With wsZ
.Range("A1:K" & .Cells(.Rows.count, 1).End(xlUp).Row).Sort _
Key1:=.Range("A2"), Order1:=xlAscending, Header:=xlYes
End With
End If
With Application
.UseSystemSeparators = True
.DecimalSeparator = originalDezimal
.ThousandsSeparator = originalTausender
.Calculation = xlCalculationAutomatic
.ScreenUpdating = True
End With
laufzeit = Timer - startZeit
hh = Int(laufzeit / 3600)
mm = Int((laufzeit - hh * 3600) / 60)
ss = Int(laufzeit - hh * 3600 - mm * 60)
formatZeit = Format(hh, "00") & ":" & Format(mm, "00") & ":" & Format(ss, "00")
Call GridBotStatistikAlsSheet(letzteQ, buyCountTotal, sellCountTotal, _
otherCountTotal, profitCount, lossCount, unmatchedBUYs, writtenCount, formatZeit)
MsgBox IIf(testModusAktiv, "[TESTMODUS AKTIV]" & vbCrLf & vbCrLf, "") & _
"[OK] Mengen-Matching abgeschlossen in " & formatZeit & vbCrLf & vbCrLf & _
"[#] Ursprungseinträge: " & letzteQ - 1 & vbCrLf & _
"[+] BUYs (GRID): " & buyCountTotal & vbCrLf & _
"[-] SELLs (GRID): " & sellCountTotal & vbCrLf & _
"[>] Andere Zeilen: " & otherCountTotal & vbCrLf & vbCrLf & _
"[?] Profitable SELLs: " & profitCount & vbCrLf & _
"[?] Verlustreiche SELLs: " & lossCount & vbCrLf & _
"[~] Nicht verwendete BUYs: " & unmatchedBUYs & vbCrLf & vbCrLf & _
"[=] Gesamt-Zielzeilen: " & writtenCount, vbInformation, "Grid Bot Import"
End Sub
Sub SchreibeZeile(wsQ As Worksheet, wsZ As Worksheet, idx As Long, ziel As Long, label As String, kommentar As String)
Dim symbol As String, baseAsset As String, quoteAsset As String
Dim side As String, feeAsset As String
Dim datumswert As Variant
Dim amount As Double, executedQty As Double, fee As Double
symbol = Trim(wsQ.Cells(idx, 7).Value)
If InStr(symbol, "_") > 0 Then
baseAsset = Split(symbol, "_")(0)
quoteAsset = Split(symbol, "_")(1)
Else
baseAsset = "-"
quoteAsset = "-"
End If
side = UCase(Trim(wsQ.Cells(idx, 6).Value))
datumswert = wsQ.Cells(idx, 1).Value
feeAsset = Trim(wsQ.Cells(idx, 10).Value)
With Application
.UseSystemSeparators = False
.DecimalSeparator = "."
.ThousandsSeparator = ""
End With
executedQty = Val(Replace(wsQ.Cells(idx, 2).Text, ",", "."))
amount = Val(Replace(wsQ.Cells(idx, 3).Text, ",", "."))
fee = Val(Replace(wsQ.Cells(idx, 9).Text, ",", "."))
With wsZ
.Cells(ziel, 5).NumberFormat = "[$-en-US]0.000000" ' Outgoing Amount
.Cells(ziel, 7).NumberFormat = "[$-en-US]0.000000" ' Incoming Amount
.Cells(ziel, 9).NumberFormat = "[$-en-US]0.000000" ' Fee Amount
.Cells(ziel, 1).Value = datumswert
.Cells(ziel, 1).NumberFormat = "dd.mm.yyyy hh:mm"
.Cells(ziel, 2).Value = "PionexFutures"
.Cells(ziel, 10).Value = kommentar
.Cells(ziel, 11).Value = ""
Select Case label
Case "Derivative Profit"
.Cells(ziel, 3).Value = "Derivative Profit"
.Cells(ziel, 4).ClearContents ' Outgoing Asset
.Cells(ziel, 5).ClearContents ' Outgoing Amount
.Cells(ziel, 6).Value = baseAsset ' Incoming Asset
.Cells(ziel, 7).Value = executedQty ' Incoming Amount
Case "Derivative Loss"
.Cells(ziel, 3).Value = "Derivative Loss"
.Cells(ziel, 4).Value = quoteAsset ' Outgoing Asset
.Cells(ziel, 5).Value = amount ' Outgoing Amount
.Cells(ziel, 6).ClearContents ' Incoming Asset
.Cells(ziel, 7).ClearContents ' Incoming Amount
Case Else ' Trade
.Cells(ziel, 3).Value = "Trade"
If side = "BUY" Then
.Cells(ziel, 4).Value = quoteAsset
.Cells(ziel, 5).Value = amount
.Cells(ziel, 6).Value = baseAsset
.Cells(ziel, 7).Value = executedQty
ElseIf side = "SELL" Then
.Cells(ziel, 4).Value = baseAsset
.Cells(ziel, 5).Value = executedQty
.Cells(ziel, 6).Value = quoteAsset
.Cells(ziel, 7).Value = amount
Else
.Range("D" & ziel & ":G" & ziel).Value = "-"
End If
End Select
.Cells(ziel, 8).Value = feeAsset
.Cells(ziel, 9).Value = fee
Dim r As Range: Set r = .Range("A" & ziel & ":K" & ziel)
Select Case .Cells(ziel, 3).Value
Case "Derivative Profit": r.Interior.Color = RGB(230, 255, 230)
Case "Derivative Loss": r.Interior.Color = RGB(255, 230, 230)
Case "Trade"
Select Case kommentar
Case "SELL nur teilweise gedeckt – BUYs aus 2024 reichen nicht vollständig": r.Interior.Color = RGB(255, 255, 200)
Case "SELL nicht gedeckt in 2024 – Deckung ggf. außerhalb Betrachtung": r.Interior.Color = RGB(230, 230, 255)
Case "Nicht verbrauchte BUY-Trx": r.Interior.Color = RGB(240, 240, 240)
Case "Direktübertrag": r.Interior.Color = RGB(224, 224, 224)
End Select
End Select
End With
Application.UseSystemSeparators = True
End Sub
Function IstBotStart(ws As Worksheet, idx As Long) As Boolean
Dim qty As Double, strategyType As String, symbol As String
qty = ws.Cells(idx, 2).Value
strategyType = UCase(ws.Cells(idx, 11).Text)
symbol = UCase(ws.Cells(idx, 7).Text)
If InStr(strategyType, "GRID") = 0 Then Exit Function
Dim avgQty As Double, count As Long, nextQty As Double
Dim i As Long
For i = idx + 1 To ws.Cells(ws.Rows.count, 1).End(xlUp).Row
If UCase(ws.Cells(i, 7).Text) = symbol And InStr(UCase(ws.Cells(i, 11).Text), "GRID") > 0 Then
nextQty = ws.Cells(i, 2).Value
If nextQty > 0 Then
avgQty = avgQty + nextQty
count = count + 1
If count >= 5 Then Exit For
End If
End If
Next i
If count = 0 Then Exit Function
avgQty = avgQty / count
IstBotStart = (qty >= avgQty * 10)
End Function
Sub GridBotStatistikAlsSheet(totalQ As Long, buys As Long, sells As Long, _
others As Long, profit As Long, loss As Long, unmatched As Long, totalZiel As Long, laufzeitText As String)
Dim wsS As Worksheet
On Error Resume Next: Application.DisplayAlerts = False: Worksheets("GridBot_Statistik").Delete: Application.DisplayAlerts = True: On Error GoTo 0
Set wsS = Worksheets.Add: wsS.Name = "GridBot_Statistik"
With wsS
.Cells(1, 1).Value = "GridBot-Matching Statistik"
.Cells(2, 1).Value = "Bearbeitungsdauer": .Cells(2, 2).Value = laufzeitText
.Cells(3, 1).Value = "Ursprungseinträge": .Cells(3, 2).Value = totalQ - 1
.Cells(4, 1).Value = "BUYs (GRID)": .Cells(4, 2).Value = buys
.Cells(5, 1).Value = "SELLs (GRID)": .Cells(5, 2).Value = sells
.Cells(6, 1).Value = "Andere Zeilen": .Cells(6, 2).Value = others
.Cells(7, 1).Value = "Profitable SELLs": .Cells(7, 2).Value = profit
.Cells(8, 1).Value = "Verlustreiche SELLs": .Cells(8, 2).Value = loss
.Cells(9, 1).Value = "Nicht verwendete BUYs": .Cells(9, 2).Value = unmatched
.Cells(10, 1).Value = "Gesamt-Zielzeilen": .Cells(10, 2).Value = totalZiel
.Columns("A:B").AutoFit
End With
End Sub