-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
37 changed files
with
2,270 additions
and
1,522 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import django | ||
from django.db.models import Sum | ||
from datetime import datetime, timedelta | ||
from os import environ | ||
from pandas import DataFrame, concat | ||
environ['DJANGO_SETTINGS_MODULE'] = 'lndg.settings' | ||
django.setup() | ||
from gui.models import Forwards, Channels, LocalSettings, FailedHTLCs | ||
|
||
def main(channels): | ||
channels_df = DataFrame.from_records(channels.values()) | ||
filter_1day = datetime.now() - timedelta(days=1) | ||
filter_7day = datetime.now() - timedelta(days=7) | ||
if channels_df.shape[0] > 0: | ||
if LocalSettings.objects.filter(key='AF-MaxRate').exists(): | ||
max_rate = int(LocalSettings.objects.filter(key='AF-MaxRate')[0].value) | ||
else: | ||
LocalSettings(key='AF-MaxRate', value='2500').save() | ||
max_rate = 2500 | ||
if LocalSettings.objects.filter(key='AF-MinRate').exists(): | ||
min_rate = int(LocalSettings.objects.filter(key='AF-MinRate')[0].value) | ||
else: | ||
LocalSettings(key='AF-MinRate', value='0').save() | ||
min_rate = 0 | ||
if LocalSettings.objects.filter(key='AF-Increment').exists(): | ||
increment = int(LocalSettings.objects.filter(key='AF-Increment')[0].value) | ||
else: | ||
LocalSettings(key='AF-Increment', value='5').save() | ||
increment = 5 | ||
if LocalSettings.objects.filter(key='AF-Multiplier').exists(): | ||
multiplier = int(LocalSettings.objects.filter(key='AF-Multiplier')[0].value) | ||
else: | ||
LocalSettings(key='AF-Multiplier', value='5').save() | ||
multiplier = 5 | ||
if LocalSettings.objects.filter(key='AF-FailedHTLCs').exists(): | ||
failed_htlc_limit = int(LocalSettings.objects.filter(key='AF-FailedHTLCs')[0].value) | ||
else: | ||
LocalSettings(key='AF-FailedHTLCs', value='25').save() | ||
failed_htlc_limit = 25 | ||
if LocalSettings.objects.filter(key='AF-UpdateHours').exists(): | ||
update_hours = int(LocalSettings.objects.filter(key='AF-UpdateHours').get().value) | ||
else: | ||
LocalSettings(key='AF-UpdateHours', value='24').save() | ||
update_hours = 24 | ||
if LocalSettings.objects.filter(key='AF-LowLiqLimit').exists(): | ||
lowliq_limit = int(LocalSettings.objects.filter(key='AF-LowLiqLimit').get().value) | ||
else: | ||
LocalSettings(key='AF-LowLiqLimit', value='5').save() | ||
lowliq_limit = 15 | ||
if LocalSettings.objects.filter(key='AF-ExcessLimit').exists(): | ||
excess_limit = int(LocalSettings.objects.filter(key='AF-ExcessLimit').get().value) | ||
else: | ||
LocalSettings(key='AF-ExcessLimit', value='95').save() | ||
excess_limit = 90 | ||
if lowliq_limit >= excess_limit: | ||
print('Invalid thresholds detected, using defaults...') | ||
lowliq_limit = 5 | ||
excess_limit = 95 | ||
forwards = Forwards.objects.filter(forward_date__gte=filter_7day, amt_out_msat__gte=1000000) | ||
if forwards.exists(): | ||
forwards_df_in_7d_sum = DataFrame.from_records(forwards.values('chan_id_in').annotate(amt_out_msat=Sum('amt_out_msat'), fee=Sum('fee')), 'chan_id_in') | ||
forwards_df_out_7d_sum = DataFrame.from_records(forwards.values('chan_id_out').annotate(amt_out_msat=Sum('amt_out_msat'), fee=Sum('fee')), 'chan_id_out') | ||
else: | ||
forwards_df_in_7d_sum = DataFrame() | ||
forwards_df_out_7d_sum = DataFrame() | ||
channels_df['amt_routed_in_7day'] = channels_df.apply(lambda row: int(forwards_df_in_7d_sum.loc[row.chan_id].amt_out_msat/1000) if (forwards_df_in_7d_sum.index == row.chan_id).any() else 0, axis=1) | ||
channels_df['amt_routed_out_7day'] = channels_df.apply(lambda row: int(forwards_df_out_7d_sum.loc[row.chan_id].amt_out_msat/1000) if (forwards_df_out_7d_sum.index == row.chan_id).any() else 0, axis=1) | ||
channels_df['net_routed_7day'] = channels_df.apply(lambda row: round((row['amt_routed_out_7day']-row['amt_routed_in_7day'])/row['capacity'], 1), axis=1) | ||
channels_df['local_balance'] = channels_df.apply(lambda row: row.local_balance + row.pending_outbound, axis=1) | ||
channels_df['remote_balance'] = channels_df.apply(lambda row: row.remote_balance + row.pending_inbound, axis=1) | ||
channels_df['in_percent'] = channels_df.apply(lambda row: int(round((row['remote_balance']/row['capacity'])*100, 0)), axis=1) | ||
channels_df['out_percent'] = channels_df.apply(lambda row: int(round((row['local_balance']/row['capacity'])*100, 0)), axis=1) | ||
channels_df['eligible'] = channels_df.apply(lambda row: (datetime.now()-row['fees_updated']).total_seconds() > (update_hours*3600), axis=1) | ||
|
||
# Low Liquidity | ||
lowliq_df = channels_df[channels_df['out_percent'] <= lowliq_limit].copy() | ||
failed_htlc_df = DataFrame.from_records(FailedHTLCs.objects.exclude(wire_failure=99).filter(timestamp__gte=filter_1day).order_by('-id').values()) | ||
if failed_htlc_df.shape[0] > 0: | ||
failed_htlc_df = failed_htlc_df[(failed_htlc_df['wire_failure']==15) & (failed_htlc_df['failure_detail']==6) & (failed_htlc_df['amount']>failed_htlc_df['chan_out_liq']+failed_htlc_df['chan_out_pending'])] | ||
lowliq_df['failed_out_1day'] = 0 if failed_htlc_df.empty else lowliq_df.apply(lambda row: len(failed_htlc_df[failed_htlc_df['chan_id_out']==row.chan_id]), axis=1) | ||
# INCREASE IF (failed htlc > threshhold) && (flow in == 0) | ||
lowliq_df['new_rate'] = lowliq_df.apply(lambda row: row['local_fee_rate']+(5*multiplier) if row['failed_out_1day']>failed_htlc_limit and row['amt_routed_in_7day'] == 0 else row['local_fee_rate'], axis=1) | ||
|
||
# Balanced Liquidity | ||
balanced_df = channels_df[(channels_df['out_percent'] > lowliq_limit) & (channels_df['out_percent'] < excess_limit)].copy() | ||
# IF NO FLOW THEN DECREASE FEE AND IF HIGH FLOW THEN SLOWLY INCREASE FEE | ||
balanced_df['new_rate'] = balanced_df.apply(lambda row: row['local_fee_rate']+((2*multiplier)*(1+(row['net_routed_7day']/row['capacity']))) if row['net_routed_7day'] > row['capacity'] else row['local_fee_rate'], axis=1) | ||
balanced_df['new_rate'] = balanced_df.apply(lambda row: row['local_fee_rate']-(3*multiplier) if (row['amt_routed_in_7day']+row['amt_routed_out_7day']) == 0 else row['local_fee_rate'], axis=1) | ||
|
||
# Excess Liquidity | ||
excess_df = channels_df[channels_df['out_percent'] >= excess_limit].copy() | ||
excess_df['revenue_7day'] = excess_df.apply(lambda row: int(forwards_df_out_7d_sum.loc[row.chan_id].fee) if forwards_df_out_7d_sum.empty == False and (forwards_df_out_7d_sum.index == row.chan_id).any() else 0, axis=1) | ||
excess_df['revenue_assist_7day'] = excess_df.apply(lambda row: int(forwards_df_in_7d_sum.loc[row.chan_id].fee) if forwards_df_in_7d_sum.empty == False and (forwards_df_in_7d_sum.index == row.chan_id).any() else 0, axis=1) | ||
# DECREASE IF (assisting channel or stagnant liq) | ||
excess_df['new_rate'] = excess_df.apply(lambda row: row['local_fee_rate']-(5*multiplier) if row['net_routed_7day'] < 0 and row['revenue_assist_7day'] > (row['revenue_7day']*10) else row['local_fee_rate'], axis=1) | ||
excess_df['new_rate'] = excess_df.apply(lambda row: row['local_fee_rate']-(5*multiplier) if (row['amt_routed_in_7day']+row['amt_routed_out_7day']) == 0 else row['local_fee_rate'], axis=1) | ||
|
||
#Merge back results | ||
result_df = concat([lowliq_df, balanced_df, excess_df]) | ||
result_df['new_rate'] = result_df.apply(lambda row: int(round(row['new_rate']/increment, 0)*increment), axis=1) | ||
result_df['new_rate'] = result_df.apply(lambda row: max_rate if max_rate < row['new_rate'] else row['new_rate'], axis=1) | ||
result_df['new_rate'] = result_df.apply(lambda row: min_rate if min_rate > row['new_rate'] else row['new_rate'], axis=1) | ||
result_df['adjustment'] = result_df.apply(lambda row: int(row['new_rate']-row['local_fee_rate']), axis=1) | ||
return result_df | ||
else: | ||
return DataFrame() | ||
|
||
|
||
if __name__ == '__main__': | ||
print(main(Channels.objects.filter(is_open=True))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
from django.db import migrations, models | ||
from gui.lnd_deps import lightning_pb2 as ln | ||
from gui.lnd_deps import lightning_pb2_grpc as lnrpc | ||
from gui.lnd_deps.lnd_connect import lnd_connect | ||
|
||
def migrate_update_channels(apps, schedma_editor): | ||
try: | ||
stub = lnrpc.LightningStub(lnd_connect()) | ||
lnd_channels = stub.ListChannels(ln.ListChannelsRequest()).channels | ||
db_channels = apps.get_model('gui', 'channels') | ||
for channel in lnd_channels: | ||
if db_channels.objects.filter(chan_id=channel.chan_id).exists(): | ||
db_channel = db_channels.objects.filter(chan_id=channel.chan_id)[0] | ||
db_channel.push_amt = channel.push_amount_sat | ||
db_channel.close_address = channel.close_address | ||
db_channel.save() | ||
except Exception as e: | ||
print('Unable to get current channel data:', str(e)) | ||
|
||
def revert_update_channels(apps, schedma_editor): | ||
pass | ||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('gui', '0035_histfailedhtlc'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='peers', | ||
name='ping_time', | ||
field=models.BigIntegerField(default=0), | ||
), | ||
migrations.AddField( | ||
model_name='channels', | ||
name='notes', | ||
field=models.TextField(blank=True, default=''), | ||
), | ||
migrations.AddField( | ||
model_name='channels', | ||
name='close_address', | ||
field=models.CharField(default='', max_length=100), | ||
preserve_default=False, | ||
), | ||
migrations.AddField( | ||
model_name='channels', | ||
name='push_amt', | ||
field=models.BigIntegerField(default=0), | ||
preserve_default=False, | ||
), | ||
migrations.RunPython(migrate_update_channels, revert_update_channels), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.